Skip to content

Commit

Permalink
Add initial Game Client VM Terraform (#224)
Browse files Browse the repository at this point in the history
* Added iniital Game Client VM Terraform

* Additional changes to the Game Client VM setup

* Changed to download & install Moonlight/Sunshine Server

* Cleaned up VM app installs & added xpra

* Added xpra signing key for apt

* Added Game Client VM instructions

* Added more Game Client VM instructions

* Added Nvidia drivers and Chrome Remote Desktop

* Removed unneeded commented line

---------

Co-authored-by: Mark Mandel <[email protected]>
  • Loading branch information
abmarcum and markmandel authored Feb 21, 2024
1 parent ec767d1 commit aba5cd6
Show file tree
Hide file tree
Showing 7 changed files with 304 additions and 2 deletions.
24 changes: 23 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -317,8 +317,30 @@ gcloud compute addresses list --filter=name=frontend-service --format="value(add

JWT token can be obtained by accessing frontend api's ip address with '/login' path, such as "http://[IP_ADDRESS].sslip.io/login" and extracting it from the URL.

### Enable Cloud Linux VM for Game Client

#### Run the Game Launcher
You have the option to enable a GCP Linux VM for the Game Client. To have Terraform setup the VM, edit `terraform.tfvars` and set:

`enable_game_client_vm = true`

Then you will need to run Terraform to apply the changes to your environment:

```shell
terraform apply
```

Once Terraform has run and a few minutes have passed for all of the software packages to install, you can then connect to the VM using gcloud:

```shell
gcloud compute ssh game-client-vm
```

You can also connect to an X display using [Chrome Remote Desktop](https://remotedesktop.google.com/).
The first time you use the service, you will need to configure the Game Client VM by [Setting up via SSH](https://remotedesktop.google.com/headless)

You can click through the buttons Begin -> Next -> Authorize to get to the screen where you can then copy the commands for `Debian Linux`. Then you can paste these commands into the SSH terminal you have open to the Game Client VM. After the commands have been succesfully run, you can return to [Chrome Remote Desktop](https://remotedesktop.google.com/access) and click on the Game Client VM that should now be displayed.

### Run the Game Launcher

To run the game launcher, you will need to have [Go](https://go.dev/dl/) installed to run it, as well as the
[prerequisites for the Fyne Go Cross Platform UI library](https://developer.fyne.io/started/).
Expand Down
95 changes: 95 additions & 0 deletions infrastructure/game-client-startup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#!/bin/bash

# Script to install applications for the Game Client VM

export DEBIAN_FRONTEND=noninteractive

wget -O "/usr/share/keyrings/xpra.asc" https://xpra.org/xpra.asc
cd /etc/apt/sources.list.d
wget https://raw.githubusercontent.com/Xpra-org/xpra/master/packaging/repos/bookworm/xpra.sources
sudo apt update -y
cd -

curl -OL https://github.com/LizardByte/Sunshine/releases/download/v0.21.0/sunshine-debian-bookworm-amd64.deb
sudo apt install -f ./sunshine-debian-bookworm-amd64.deb -y
sudo apt install xfce4 xfce4-goodies tightvncserver dbus-x11 gcc make linux-headers-$(uname -r) software-properties-common mesa-utils golang libgl1-mesa-dev xorg-dev xpra -y

wget https://developer.download.nvidia.com/compute/cuda/12.3.1/local_installers/cuda_12.3.1_545.23.08_linux.run
sudo sh cuda_12.3.1_545.23.08_linux.run --silent

sudo apt update -y

#########
#
# Startup script to install Chrome remote desktop and a desktop environment.
#
# See environmental variables at then end of the script for configuration
#

function install_desktop_env {
PACKAGES="desktop-base xscreensaver dbus-x11"

if [[ "$INSTALL_XFCE" != "yes" && "$INSTALL_CINNAMON" != "yes" ]] ; then
# neither XFCE nor cinnamon specified; install both
INSTALL_XFCE=yes
INSTALL_CINNAMON=yes
fi

if [[ "$INSTALL_XFCE" = "yes" ]] ; then
PACKAGES="$PACKAGES xfce4"
echo "exec xfce4-session" > /etc/chrome-remote-desktop-session
[[ "$INSTALL_FULL_DESKTOP" = "yes" ]] && \
PACKAGES="$PACKAGES task-xfce-desktop"
fi

if [[ "$INSTALL_CINNAMON" = "yes" ]] ; then
PACKAGES="$PACKAGES cinnamon-core"
echo "exec cinnamon-session-cinnamon2d" > /etc/chrome-remote-desktop-session
[[ "$INSTALL_FULL_DESKTOP" = "yes" ]] && \
PACKAGES="$PACKAGES task-cinnamon-desktop"
fi

DEBIAN_FRONTEND=noninteractive \
apt-get install --assume-yes $PACKAGES $EXTRA_PACKAGES

systemctl disable lightdm.service
}

function download_and_install { # args URL FILENAME
curl -L -o "$2" "$1"
apt-get install --assume-yes --fix-broken "$2"
}

function is_installed { # args PACKAGE_NAME
dpkg-query --list "$1" | grep -q "^ii" 2>/dev/null
return $?
}

# Configure the following environmental variables as required:
INSTALL_XFCE=yes
INSTALL_CINNAMON=yes
INSTALL_CHROME=yes
INSTALL_FULL_DESKTOP=yes

# Any additional packages that should be installed on startup can be added here
EXTRA_PACKAGES="less bzip2 zip unzip tasksel wget"

apt-get update

! is_installed chrome-remote-desktop && \
download_and_install \
https://dl.google.com/linux/direct/chrome-remote-desktop_current_amd64.deb \
/tmp/chrome-remote-desktop_current_amd64.deb

install_desktop_env

[[ "$INSTALL_CHROME" = "yes" ]] && \
! is_installed google-chrome-stable && \
download_and_install \
https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb \
/tmp/google-chrome-stable_current_amd64.deb

echo "Chrome remote desktop installation completed"

# Delete instance startup script do it does not re-run after a boot
gcloud compute instances remove-metadata --keys startup-script --zone us-central1-a game-client-vm
134 changes: 134 additions & 0 deletions infrastructure/game-client.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# Copyright 2023 Google LLC All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


resource "google_service_account" "game_client_vm" {
count = var.enable_game_client_vm ? 1 : 0

project = var.project

account_id = "game-client-vm"
display_name = "Custom SA for Game Client VM"
}

resource "google_project_iam_member" "game_client_vm_compute_admin" {
count = var.enable_game_client_vm ? 1 : 0

project = var.project
role = "roles/compute.instanceAdmin.v1"
member = "serviceAccount:${google_service_account.game_client_vm[0].email}"
}

resource "google_project_iam_member" "game_client_vm_compute_is_sa" {
count = var.enable_game_client_vm ? 1 : 0

project = var.project
role = "roles/iam.serviceAccountUser"
member = "serviceAccount:${google_service_account.game_client_vm[0].email}"
}

resource "google_compute_address" "game_client_vm_static_ip" {
count = var.enable_game_client_vm ? 1 : 0

project = var.project
name = "game-client-vm-static-ip"
region = var.game_client_vm_region
}

data "google_compute_image" "game_client_vm_os" {
count = var.enable_game_client_vm ? 1 : 0

family = var.game_client_vm_os_family
project = var.game_client_vm_os_project
}

resource "google_compute_instance" "game_client_vm" {
count = var.enable_game_client_vm ? 1 : 0

project = var.project

name = "game-client-vm"
machine_type = var.game_client_vm_machine_type
zone = "${var.game_client_vm_region}-a"

tags = ["game-client-vm-ssh", "game-client-vm-vnc"]

scheduling {
on_host_maintenance = "TERMINATE"
}

boot_disk {
initialize_params {
size = var.game_client_vm_storage
image = data.google_compute_image.game_client_vm_os[0].self_link
}
}

// Local SSD disk
scratch_disk {
interface = "NVME"
}

network_interface {
subnetwork = google_compute_subnetwork.subnet["${var.game_client_vm_region}"].self_link
# network = google_compute_network.vpc.id

access_config {
// Ephemeral public IP
nat_ip = google_compute_address.game_client_vm_static_ip[0].address
}
}

metadata = {
serial-port-logging-enable = "TRUE"
}

metadata_startup_script = file("${path.root}/game-client-startup.sh")

service_account {
# Google recommends custom service accounts that have cloud-platform scope and permissions granted via IAM Roles.
email = google_service_account.game_client_vm[0].email
scopes = ["cloud-platform"]
}
}

resource "google_compute_firewall" "game-client-vm-ssh" {
project = var.project

name = "game-client-vm-ssh"
network = google_compute_network.vpc.id

allow {
protocol = "tcp"
ports = ["22"]
}

target_tags = ["game-client-vm-ssh"]
source_ranges = var.game_client_vm_allowed_cidr
}

resource "google_compute_firewall" "game-client-vm-vnc" {
project = var.project

name = "game-client-vm-vnc"
network = google_compute_network.vpc.id

allow {
protocol = "tcp"
ports = ["5901"]
}

target_tags = ["game-client-vm-vnc"]
source_ranges = var.game_client_vm_allowed_cidr
}
2 changes: 1 addition & 1 deletion infrastructure/game-server.tf
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,6 @@ resource "google_secret_manager_secret_iam_binding" "cloud_build_binding" {
secret_id = google_secret_manager_secret.secret_github_packages.id
role = "roles/secretmanager.secretAccessor"
members = [
"serviceAccount:cloudbuild-cicd@${var.project}.iam.gserviceaccount.com",
google_service_account.cloudbuild-sa.member
]
}
4 changes: 4 additions & 0 deletions infrastructure/services-gke.tf
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ resource "google_container_cluster" "services-gke" {
network = google_compute_network.vpc.name
subnetwork = google_compute_subnetwork.subnet[var.services_gke_config.location].name

gateway_api_config {
channel = "CHANNEL_STANDARD"
}

# See issue: https://github.com/hashicorp/terraform-provider-google/issues/10782
ip_allocation_policy {}

Expand Down
9 changes: 9 additions & 0 deletions infrastructure/terraform.tfvars.sample
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@
project = "PROJECT_ID"
resource_env_label = "demo-global-game"

# Game Client Configuration
enable_game_client_vm = false
game_client_vm_machine_type = "g2-standard-4"
game_client_vm_region = "us-central1" # MUST MATCH one of the below VPC regions
game_client_vm_storage = 300
game_client_vm_os_family = "debian-12"
game_client_vm_os_project = "debian-cloud"
game_client_vm_allowed_cidr = ["0.0.0.0/0"]

# Cloud Deploy Configuration
platform_directory = "../platform" # Relative to Terraform directory
services_directory = "../services" # Relative to Terraform directory
Expand Down
38 changes: 38 additions & 0 deletions infrastructure/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,41 @@ variable "github_pat" {
type = string
description = "A GitHub personal access token (classic) with at least repo scope"
}

### Game Client VM Variables

variable "enable_game_client_vm" {
type = bool
description = "Whether to create or not a Linux Game Client VM"
default = false
}

variable "game_client_vm_machine_type" {
type = string
description = "Game Client VM Machine Type"
}

variable "game_client_vm_allowed_cidr" {
type = list(any)
description = "Game Client VM Allowed CIDRs"
}

variable "game_client_vm_region" {
type = string
description = "Game Client VM Region"
}

variable "game_client_vm_storage" {
type = number
description = "Game Client VM Storage Size"
}

variable "game_client_vm_os_family" {
type = string
description = "Game Client VM OS Image Family"
}

variable "game_client_vm_os_project" {
type = string
description = "Game Client OS Image Project"
}

0 comments on commit aba5cd6

Please sign in to comment.