diff --git a/.gitignore b/.gitignore index 48ce7fc..d4f2461 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,9 @@ platform/agones/skaffold.yaml **/global-game-*/agones-system.yaml platform/open-match/base/kustomization.yaml +# Ignore Terraform generated liquibase files +infrastructure/schema/liquibase.properties + # Ignore go.work, go.work.sum (results of `go work`) go.work go.work.sum diff --git a/README.md b/README.md index efce571..3806690 100644 --- a/README.md +++ b/README.md @@ -13,10 +13,10 @@ arbitrary for any part not specified. Open the [Google OAuth consent screen](https://console.cloud.google.com/apis/credentials/consent) for your project, and create an "External" App, and allowlist any users you wish to be able to login to your deployment of this game. -Open the [Google Credentials](https://console.cloud.google.com/apis/credentials) screen for your project, and click +Open the [Google Credentials](https://console.cloud.google.com/apis/credentials) screen for your project, and click "+ CREATE CREDENTIALS", and create an "OAuth Client ID" of type "Web Application". -Leave this page open, as we'll need the Client ID and Client secret of the ID you just created shortly. +Leave this page open, as we'll need the Client ID and Client secret of the ID you just created shortly. ## Infrastructure and Services @@ -62,7 +62,7 @@ To do so, follow: [Accessing Unreal Engine source code on GitHub](https://www.un Once done, to pull down the [Unreal Development Containers](https://unrealcontainers.com/), you will also need to create [a personal access token (classic)](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token#creating-a-personal-access-token-classic) -with at least `read:packages` scope. +with at least `read:packages` scope. Leave the page open with this token, as we'll need it shortly. @@ -88,17 +88,17 @@ cp terraform.tfvars.sample terraform.tfvars You will need to now edit `terraform.tfvars` -* Update with the ID of your Google Cloud Project, +* Update with the ID of your Google Cloud Project, * Updated and with the Client ID and Client secret created in the above step. * Updated with the GitHub personal access token you created the above steps. -You can edit other variables in this file, but we recommend leaving the default values for your first run before +You can edit other variables in this file, but we recommend leaving the default values for your first run before experimenting. ### Provision the infrastructure. -> **Warning** -> This demo in its default state creates multiple Kubernetes clusters around the world, +> **Warning** +> This demo in its default state creates multiple Kubernetes clusters around the world, > Spanner instances, and more. Running this demo for an extended amount of time may incur significant costs. @@ -122,7 +122,7 @@ This should give you back an IP, such as `35.202.107.204`. 2. Click "+ ADD URI" under "Authorised redirect URIs" and add "http://[IP_ADDRESS].sslip.io/callback" 3. Click "Save". -Since OAuth needs a domain to authenticate against, we'll use [sslip.io](https://sslip.io) for development purposes. +Since OAuth needs a domain to authenticate against, we'll use [sslip.io](https://sslip.io) for development purposes. ### Deploy Platform Components Replace the` _RELEASE_NAME` substitution with a unique build name. Cloud Build will deploy @@ -136,10 +136,10 @@ cd $GAME_DEMO_HOME/platform/ gcloud builds submit --config=cloudbuild.yaml --substitutions=_RELEASE_NAME=rel-1 ``` -Navigate to the +Navigate to the [agones-deploy-pipeline](https://console.cloud.google.com/deploy/delivery-pipelines/us-central1/agones-deploy-pipeline) -delivery pipeline to review the rollout status. Cloud Build will create a Cloud Deploy release which automatically -deploys Agones the first game server cluster. Agones can be deployed to subsequent clusters by clicking on the +delivery pipeline to review the rollout status. Cloud Build will create a Cloud Deploy release which automatically +deploys Agones the first game server cluster. Agones can be deployed to subsequent clusters by clicking on the `promote` button within the Pipeline visualization or by running the following gcloud command: ```shell @@ -147,14 +147,25 @@ deploys Agones the first game server cluster. Agones can be deployed to subseque gcloud deploy releases promote --release=RELEASE_NAME --delivery-pipeline=agones-deploy-pipeline --region=us-central1 ``` -Continue the promotion until Agones has been deployed to all clusters. You can monitor the status of the deployment +Continue the promotion until Agones has been deployed to all clusters. You can monitor the status of the deployment through the Cloud Logging URL returned by the `gcloud builds` command as well as the Kubernetes Engine/Workloads panel in the GCP Console. Open Match rollout status can be viewed by navigating to the [global-game-open-match](https://console.cloud.google.com/deploy/delivery-pipelines/us-central1/global-game-open-match) delivery pipeline. Since open match is deployed onto a single services GKE cluster, deployments are automatically rolled out with no need for manual promotion. +## Deploy Cloud Spanner Schema + +To deploy the database schema, submit the following Cloud Build command: + +```shell +cd $GAME_DEMO_HOME/infrastructure/schema +gcloud builds submit --config=cloudbuild.yaml +``` + +This will deploy the schema migration using [Liquibase](https://www.liquibase.org/) and the [Cloud Spanner liquibase extension](https://github.com/cloudspannerecosystem/liquibase-spanner). + ## Install Game Backend Services -To install all the backend services, submit the following Cloud Build command, and replace the` _RELEASE_NAME` +To install all the backend services, submit the following Cloud Build command, and replace the` _RELEASE_NAME` substitution with a unique build name. ```shell @@ -170,7 +181,7 @@ This will: ## Dedicated Game Server -To build the Unreal dedicated game server image, run the following command, and replace the` _RELEASE_NAME` +To build the Unreal dedicated game server image, run the following command, and replace the` _RELEASE_NAME` substitution with a unique build name. ```shell @@ -204,7 +215,7 @@ To build the Game Client for your host machine, you will need: * [Unreal Engine 5.1.0](https://www.unrealengine.com/en-US/download) for your platform. -Open the [`game`](./game) folder in Unreal Engine. Once finished opening, you can run the game client directly within +Open the [`game`](./game) folder in Unreal Engine. Once finished opening, you can run the game client directly within the editor (Hit the ▶️ button), or we can package the project via: Platforms > {your host platform} > Package Project, and execute the resultant package. @@ -231,7 +242,7 @@ go run main.go ##### This project was made with a different version of the Unreal Engine. -If you hit this issue, it may be that you are building on a different host platform than the original. To solve, +If you hit this issue, it may be that you are building on a different host platform than the original. To solve, click: More Options > Convert in-place. The project should open as normal now. diff --git a/infrastructure/files/spanner/liquibase.properties.tpl b/infrastructure/files/spanner/liquibase.properties.tpl new file mode 100644 index 0000000..ca09dbe --- /dev/null +++ b/infrastructure/files/spanner/liquibase.properties.tpl @@ -0,0 +1,19 @@ +# 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. + +changeLogFile: /liquibase/changelog.yaml +url: jdbc:cloudspanner:/projects/${project_id}/instances/${instance_id}/databases/${database_id} +logLevel: 0 +liquibase.hub.mode=off +liquibase.includeSystemClasspath:true diff --git a/infrastructure/iam.tf b/infrastructure/iam.tf index ecf57f5..62258d4 100644 --- a/infrastructure/iam.tf +++ b/infrastructure/iam.tf @@ -39,6 +39,8 @@ resource "google_project_iam_member" "cloudbuild-sa-cloudbuild-roles" { "roles/container.admin", "roles/storage.admin", "roles/iam.serviceAccountUser", + "roles/run.developer", + "roles/spanner.databaseUser", "roles/gkehub.editor" ]) role = each.key diff --git a/infrastructure/schema/Dockerfile b/infrastructure/schema/Dockerfile new file mode 100644 index 0000000..3b09b4f --- /dev/null +++ b/infrastructure/schema/Dockerfile @@ -0,0 +1,20 @@ +# Copyright 2023 Google LLC +# +# 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 +# +# https:#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. + +FROM liquibase/liquibase:4.17 +COPY liquibase.properties /liquibase/liquibase.properties +COPY changelog.yaml /liquibase/changelog.yaml +COPY lib/liquibase-spanner-*.jar /liquibase/lib/. + +CMD ["liquibase", "update", "--defaultsFile=/liquibase/liquibase.properties"] diff --git a/infrastructure/schema/changelog.yaml b/infrastructure/schema/changelog.yaml new file mode 100644 index 0000000..da5e5a3 --- /dev/null +++ b/infrastructure/schema/changelog.yaml @@ -0,0 +1,86 @@ +# 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. + +databaseChangeLog: +- preConditions: + onFail: HALT + onError: HALT + +# CREATE TABLE players ( +# player_google_id STRING(MAX) NOT NULL, +# player_name STRING(64) NOT NULL, +# profile_image STRING(MAX) NOT NULL, +# region STRING(10) NOT NULL, +# skill_level int64 NOT NULL, +# tier STRING(1) NOT NULL, +# stats JSON, +# ) PRIMARY KEY(player_google_id); + +- changeSet: + id: create-players-table + author: dtest + changes: + - createTable: + tableName: players + columns: + - column: + name: player_google_id + type: STRING(MAX) + constraints: + primaryKey: true + - column: + name: player_name + type: STRING(MAX) + constraints: + nullable: false + - column: + name: profile_image + type: STRING(MAX) + constraints: + nullable: false + - column: + name: region + type: STRING(10) + constraints: + nullable: false + - column: + name: skill_level + type: BIGINT + constraints: + nullable: false + - column: + name: tier + type: STRING(1) + constraints: + nullable: false + - column: + name: stats + type: JSON + +# CREATE TABLE game_assets +# ( +# asset_uuid STRING(36) NOT NULL, +# asset_name STRING(MAX) NOT NULL, +# available_time TIMESTAMP NOT NULL +# )PRIMARY KEY (asset_uuid); + +# CREATE TABLE player_assets +# ( +# player_asset_uuid STRING(36) NOT NULL, +# player_google_id STRING(MAX) NOT NULL, +# asset_uuid STRING(36) NOT NULL, +# acquire_time TIMESTAMP NOT NULL DEFAULT (CURRENT_TIMESTAMP()), +# FOREIGN KEY (asset_uuid) REFERENCES game_assets (asset_uuid) +# ) PRIMARY KEY (player_google_id, player_asset_uuid), +# INTERLEAVE IN PARENT players ON DELETE CASCADE; diff --git a/infrastructure/schema/cloudbuild.yaml b/infrastructure/schema/cloudbuild.yaml new file mode 100644 index 0000000..f358f7e --- /dev/null +++ b/infrastructure/schema/cloudbuild.yaml @@ -0,0 +1,60 @@ +# 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. + +serviceAccount: projects/${PROJECT_ID}/serviceAccounts/cloudbuild-cicd@${PROJECT_ID}.iam.gserviceaccount.com +steps: + +# +# Building of schema image +# + + - name: gcr.io/cloud-builders/docker + id: schema image build + args: ["build", ".", "-t", "${_SCHEMA_IMAGE}"] + + - name: gcr.io/cloud-builders/docker + id: schema image push + args: ["push", "${_SCHEMA_IMAGE}"] + +# +# Create schema job +# + - name: "gcr.io/google.com/cloudsdktool/cloud-sdk:slim" + id: schema migrate create + entrypoint: gcloud + args: ["beta", "run", "jobs", "create", "${_RUN_JOB}", + "--image", "${_SCHEMA_IMAGE}", "--region", "${_REGION}"] + +# +# Running schema migration +# + - name: "gcr.io/google.com/cloudsdktool/cloud-sdk:slim" + id: schema migrate execute + entrypoint: gcloud + args: ["beta", "run", "jobs", "execute", "${_RUN_JOB}", + "--region", "${_REGION}", "--wait"] + +# artifacts: +# images: +# - ${_REGISTRY}/schema +substitutions: + _SCHEMA_IMAGE: ${_REGISTRY}/schema:${BUILD_ID} + _RUN_JOB: migrate-database-${BUILD_ID} + _REGISTRY: us-docker.pkg.dev/${PROJECT_ID}/global-game-images + _REGION: us-central1 + +options: + dynamic_substitutions: true + machineType: E2_HIGHCPU_8 + logging: CLOUD_LOGGING_ONLY diff --git a/infrastructure/schema/lib/liquibase-spanner-4.17.1-all.jar b/infrastructure/schema/lib/liquibase-spanner-4.17.1-all.jar new file mode 100644 index 0000000..44253f2 Binary files /dev/null and b/infrastructure/schema/lib/liquibase-spanner-4.17.1-all.jar differ diff --git a/infrastructure/schema/schema.sql b/infrastructure/schema/schema.sql deleted file mode 100644 index ef24514..0000000 --- a/infrastructure/schema/schema.sql +++ /dev/null @@ -1,41 +0,0 @@ --- Copyright 2023 Google LLC --- --- 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 --- --- https://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. --- - -CREATE TABLE players ( - player_google_id STRING(MAX) NOT NULL, - player_name STRING(64) NOT NULL, - profile_image STRING(MAX) NOT NULL, - region STRING(10) NOT NULL, - skill_level int64 NOT NULL, - tier STRING(1) NOT NULL, - stats JSON, -) PRIMARY KEY(player_google_id); - -CREATE TABLE game_assets -( - asset_uuid STRING(36) NOT NULL, - asset_name STRING(MAX) NOT NULL, - available_time TIMESTAMP NOT NULL -)PRIMARY KEY (asset_uuid); - -CREATE TABLE player_assets -( - player_asset_uuid STRING(36) NOT NULL, - player_google_id STRING(MAX) NOT NULL, - asset_uuid STRING(36) NOT NULL, - acquire_time TIMESTAMP NOT NULL DEFAULT (CURRENT_TIMESTAMP()), - FOREIGN KEY (asset_uuid) REFERENCES game_assets (asset_uuid) -) PRIMARY KEY (player_google_id, player_asset_uuid), - INTERLEAVE IN PARENT players ON DELETE CASCADE; diff --git a/infrastructure/spanner.tf b/infrastructure/spanner.tf index b015a99..c932498 100644 --- a/infrastructure/spanner.tf +++ b/infrastructure/spanner.tf @@ -55,9 +55,16 @@ resource "google_project_iam_member" "spanner-sa" { member = "serviceAccount:${google_service_account.spanner-sa.email}" } - - - +# Make liquibase.properties for schema management +resource "local_file" "liquibase-properties" { + content = templatefile( + "${path.module}/files/spanner/liquibase.properties.tpl", { + project_id = var.project + instance_id = google_spanner_instance.global-game-spanner.name + database_id = google_spanner_database.spanner-database.name + }) + filename = "${path.module}/${var.schema_directory}/liquibase.properties" +} # Make Config file for deploy with Cloud Deploy resource "local_file" "services-profile-config" { diff --git a/infrastructure/terraform.tfvars.sample b/infrastructure/terraform.tfvars.sample index e89e7df..51f56bb 100644 --- a/infrastructure/terraform.tfvars.sample +++ b/infrastructure/terraform.tfvars.sample @@ -21,6 +21,7 @@ resource_env_label = "demo-global-game" # Cloud Deploy Configuration platform_directory = "../platform" # Relative to Terraform directory services_directory = "../services" # Relative to Terraform directory +schema_directory = "../infrastructure/schema" # Relative to Terraform directory clouddeploy_config = { "location" : "us-central1" } diff --git a/infrastructure/variables.tf b/infrastructure/variables.tf index 9c67275..9538c28 100644 --- a/infrastructure/variables.tf +++ b/infrastructure/variables.tf @@ -83,6 +83,11 @@ variable "spanner_config" { description = "Configuration specs for Spanner" } +variable "schema_directory" { + type = string + description = "Schema directory where schema definition is found" +} + ### Services GKE Variables ### variable "services_gke_config" { diff --git a/services/profile/.gitignore b/services/profile/.gitignore index 17409b7..647cd31 100644 --- a/services/profile/.gitignore +++ b/services/profile/.gitignore @@ -1,3 +1,17 @@ +# 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. + config.yml test_data/*** vendor/*** diff --git a/services/profile/README.md b/services/profile/README.md index fedaa8e..144fea9 100644 --- a/services/profile/README.md +++ b/services/profile/README.md @@ -2,6 +2,122 @@ The Profile Service provides a REST API to interact with Cloud Spanner to manage Player Profiles. The service runs on GKE Autopilot. +## API + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
EndpointInputReturnDescription
GET /players/:player_id: None +
{
+"player_google_id": "[string]",
+"player_name": "[string]",
+"profile_image": "[string]",
+"region": "[string]"
+}
+
+ Retrieve information about the player +
GET /players/:player_id:/stats None +
{
+"player_google_id": "[string]",
+"stats": "[json]",
+"skill_level": [int64],
+"tier": "[string]" # currently unused
+}
+
+ Retrieve stats and skill info about the player +
POST /players +
{
+"player_google_id": "[string]",
+"player_name": "[string]",
+"profile_image": "default", # Currently unused
+"region": "[amer,eur,apac]"
+}
+
+
+                    "[player_google_id]"
+                
+
+ Create a new player +
PUT /players +
{
+"player_google_id": "[string]",
+"player_name": "[string]",
+"profile_image": "default", # Currently unused
+"region": "[amer,eur,apac]"
+}
+
+
{
+"player_google_id": "[string]",
+"player_name": "[string]",
+"profile_image": "[string]",
+"region": "[string]"
+}
+
+ Update player information +
PUT /players/:player_id:/stats +
{
+"won": [true, false],
+"score": [int64],
+"kills": [int64],
+"deaths": [int64]
+}
+
+
{
+"player_google_id": "[string]",
+"stats": "[json]",
+"skill_level": [int64],
+"tier": "[string]" # currently unused
+}
+
+ Update player stats +
+ ## Prerequisites Cloud Spanner must be set up using the infrastructure steps before this service will work. @@ -9,25 +125,7 @@ Local testing requires Docker to be installed. ## Schema management -This service uses the [wrench migration tool](https://github.com/cloudspannerecosystem/wrench) to perform Cloud Spanner schema migrations. - -Basic usage is as follows. - -- Create the database with the initial schema: - -``` -export SPANNER_PROJECT_ID=your-project-id -export SPANNER_INSTANCE_ID=your-instance-id -export SPANNER_DATABASE_ID=your-database-id - -wrench create --directory ../../infrastructure/schema -``` - -- Apply migrations - -``` -wrench migrate up --directory ../../infrastructure/schema -``` +This service uses the [Liquibase Spanner extension](https://github.com/cloudspannerecosystem/liquibase-spanner) to perform Cloud Spanner schema migrations. ## Local deployment