├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── solutionid_validator.sh └── workflows │ └── maintainer_workflows.yml ├── .gitignore ├── CHANGELOG_README.md ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── DETAILS.md ├── LICENSE ├── README.md ├── agones-openmatch-flow.png ├── agones-openmatch-multicluster.1.png ├── agones-openmatch-multicluster.2.png ├── agones-openmatch-multicluster.3.png ├── agones-openmatch-multicluster.4.png ├── agones-openmatch-multicluster.5.png ├── agones-openmatch-multicluster.final.png ├── integration ├── clients │ ├── allocation-client │ │ ├── README.md │ │ ├── allocation.go │ │ ├── go.mod │ │ └── go.sum │ ├── ncat │ │ ├── README.md │ │ ├── go.mod │ │ ├── go.sum │ │ ├── main.go │ │ └── ncat-sample.png │ └── stk │ │ ├── README.md │ │ ├── go.mod │ │ ├── go.sum │ │ └── main.go ├── director │ ├── Dockerfile │ ├── README.md │ ├── director.yaml │ ├── go.mod │ ├── go.sum │ ├── main.go │ └── profile.go ├── matchfunction │ ├── Dockerfile │ ├── README.md │ ├── go.mod │ ├── go.sum │ ├── main.go │ ├── matchfunction.go │ ├── matchfunction.yaml │ └── server.go └── ncat-server │ ├── Dockerfile │ ├── go.mod │ ├── go.sum │ └── main.go ├── manifests ├── agones-allocator-tls.yaml ├── agones-controller-cert.yaml ├── cluster-issuer.yaml ├── fleets │ ├── ncat │ │ ├── ncat-fleet1.yaml │ │ ├── ncat-fleet2.yaml │ │ ├── ncat-fleet3.yaml │ │ └── ncat-fleet4.yaml │ └── stk │ │ ├── supertux-kart-fleet1.yaml │ │ ├── supertux-kart-fleet2.yaml │ │ ├── supertux-kart-fleet3.yaml │ │ └── supertux-kart-fleet4.yaml ├── multicluster-allocation-1-to-2.yaml ├── multicluster-allocation-1.yaml ├── multicluster-allocation-2-to-1.yaml ├── multicluster-allocation-2.yaml └── open-match-tls-certmanager.cert.yaml ├── respawn-multicluster.6.png ├── scripts ├── configure-agones-tls.sh ├── configure-multicluster-allocation.sh ├── configure-open-match-ingress.sh ├── deploy-director.sh ├── deploy-mapping-configmap.sh ├── deploy-matchfunction.sh ├── deploy-ncat-fleets.sh ├── deploy-stk-fleets.sh ├── generate-agones-certs.sh ├── generate-tls-files.sh ├── install-linux-prerequisites.sh ├── namespace-finalizer.sh ├── set-allocator-ip.sh ├── test-agones-tls.sh ├── test-gameserver-allocation.sh └── test-gameserver-multicluster-allocation.sh ├── security.md └── terraform ├── cloudformation ├── buildspec.yml ├── cleanup-buildspec.yml └── main.yaml ├── cluster ├── backend.tf ├── main.tf ├── modules │ └── cluster │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf ├── outputs.tf ├── variables.tf └── versions.tf ├── extra-cluster ├── backend.tf ├── main.tf ├── outputs.tf ├── variables.tf └── versions.tf └── intra-cluster ├── backend.tf ├── helm_values └── agones-helm-values.yaml ├── main.tf ├── variables.tf └── versions.tf /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/solutionid_validator.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | #set -e 3 | 4 | echo "checking solution id $1" 5 | echo "grep -nr --exclude-dir='.github' "$1" ./.." 6 | result=$(grep -nr --exclude-dir='.github' "$1" ./..) 7 | if [ $? -eq 0 ] 8 | then 9 | echo "Solution ID $1 found\n" 10 | echo "$result" 11 | exit 0 12 | else 13 | echo "Solution ID $1 not found" 14 | exit 1 15 | fi 16 | 17 | export result 18 | -------------------------------------------------------------------------------- /.github/workflows/maintainer_workflows.yml: -------------------------------------------------------------------------------- 1 | # Workflows managed by aws-solutions-library-samples maintainers 2 | name: Maintainer Workflows 3 | on: 4 | # Triggers the workflow on push or pull request events but only for the "main" branch 5 | push: 6 | branches: [ "main" ] 7 | pull_request: 8 | branches: [ "main" ] 9 | types: [opened, reopened, edited] 10 | 11 | jobs: 12 | CheckSolutionId: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | - name: Run solutionid validator 17 | run: | 18 | chmod u+x ./.github/solutionid_validator.sh 19 | ./.github/solutionid_validator.sh ${{ vars.SOLUTIONID }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.crt 2 | *.cert 3 | *.key 4 | tempfile.json 5 | .DS_Store 6 | .idea 7 | .terraform* 8 | terraform.tfstate* 9 | go.work* -------------------------------------------------------------------------------- /CHANGELOG_README.md: -------------------------------------------------------------------------------- 1 | ## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | ## SPDX-License-Identifier: MIT-0 3 | 4 | # Description 5 | Running log of recent changes, on a git commit level. The purpose of this document is to provide more details about changes to the project without having overly verbose commit messages. 6 | 7 | # Change Log 8 | ## 10-11-2024 9 | **TLDR;** Wrapped up all SuperTuxKart changes so it now works with Arm-based instances. Running the game with AI racers has not been tested yet. 10 | 11 | - Updated the node affinity attributes of SuperTuxKart's manifest files to have the appropriate agones servers launched in the gameserver Managed Node Group. 12 | - Increased the minimum number of nodes for the Agones Managed Node Group to 3 for adequate storage (EBS volumes) for all required workloads. 13 | - Incorporated commands in `deploy-stk-fleets.sh` script to download the necessary files to build an arm-based version of SuperTuxKart for Agones. 14 | - Created an immutable ECR Repository for SuperTuxKart arm-based image to be stored and pulled when needed 15 | - Added pod anti-affinity for the agones controller to improve reliability 16 | - Configured helm values for agones helm chart to add node selector terms to appropriate pods launched by the helm chart 17 | - Updated README.md for SuperTuxKart example 18 | - Updated general README.md 19 | 20 | ## 9-2-2024 21 | **TLDR;** Modified ncat gameservers to have the ability to run with arm-based containers. Made ECR repos mutable. Added arm-arch node affinity to /integration/director/director.yaml and /integration/matchfunction/matchfunction.yaml. 22 | 23 | - Modified the /scripts/deploy-ncat-fleets.sh file to be able to build ncat servers for the linux/arm64 platform. The deployer can specify which architecture to use in the script by running the command found in the README.md file. 24 | - Set the agones-openmatch-ncat-server ECR repository to be mutable for the scripts/deploy-ncat-fleets.sh script to push the correct images to the repository. There was an error with the script trying to push an image to the repository when it was first created. The change permits the solution to upload an image index artifact to ECR. Otherwise, only the image gets uploaded. This does open things up for people to push editions of the same version/tag of an image in an image repository. 25 | - Added node affinity for matchfunction and director yaml files in integration/director and integration.matchfunction directories. The node affinity assigns the pods to x86-based nodes. 26 | - Updated the README.md file to have more efficient deletion commands and clearer instructions when choosing between arm-based and x86-based instance architectures. 27 | 28 | 29 | ## 8-27-2024 30 | **TLDR;** Improved the readability of the terraform/cluster section of the README.md file. Improved the markdown formatting of the CHANGELOG_README.md file. Removed functionality for deploying arm-based nodes for openmatch. Fluentbit now creates whatever CloudWatch Log Groups it needs upon deployment. Fixed the README.md file's instructions for fetching the load balancer arn. 31 | 32 | - Commented out CLI options to use arm-based instances for openmatch and agones-openmatch MNGs since openmatch containers work only on x86 architectures. 33 | - Commented out variables and if-else statements that enable using arm-based instances with openmatch. 34 | - Modified terraform/intra-cluster/main.tf to have fluentbit create CloudWatch log groups that it needs to successfuly run in the cluster. The log groups are made only if they don't already exist. 35 | - Modified open match load balancer fetch command to more accurately reflect the load balancer's correct name; it's not `open-match-frontend-loadbalancer`, it's `agones-gameservers-1-om-fe`. We could possibly change this in the future... 36 | - Also, it appears that the terraform/extra-cluster command in the README.md file still prompts the user for the load balancer arn even though the environment variable is set. All other variables are fine though... Fixed this typo (was missing a backslash) 37 | 38 | ``` 39 | # Initialize Terraform 40 | terraform -chdir=terraform/cluster init && 41 | # Create both clusters 42 | terraform -chdir=terraform/cluster apply -auto-approve \ 43 | -var="cluster_1_name=${CLUSTER1}" \ 44 | -var="cluster_1_region=${REGION1}" \ 45 | -var="cluster_1_cidr=${CIDR1}" \ 46 | -var="cluster_2_name=${CLUSTER2}" \ 47 | -var="cluster_2_region=${REGION2}" \ 48 | -var="cluster_2_cidr=${CIDR2}" \ 49 | -var="cluster_version=${VERSION}" \ 50 | -var="all_arm_based_instances_cluster_1"=false \ 51 | -var="all_arm_based_instances_cluster_2"=false \ 52 | -var="gameservers_arm_based_instances_cluster_1"=true \ 53 | -var="gameservers_arm_based_instances_cluster_2"=true \ 54 | -var="agones_system_arm_based_instances_cluster_1"=true \ 55 | -var="agones_system_arm_based_instances_cluster_2"=true \ 56 | -var="agones_metrics_arm_based_instances_cluster_1"=true \ 57 | -var="agones_metrics_arm_based_instances_cluster_2"=true 58 | ``` 59 | 60 | 61 | 62 | ## 8-13-2024 63 | **TLDR;** Added more flexibility for choosing which Managed Node Groups (MNGs) can have which architecture. The deployer can have 1, all, none, or any number in between of MNGs using an arm-based architecture. The deployer can also specify which MNG in which cluster should have which architecture type. 64 | 65 | - Added new variables to both of the terraform variables files int he core EKS Cluster (/terraform/cluster/variables.tf & /terraform/cluster/modules/cluster/variables.tf). Also added new variable parameters to the EKS modules listed in the main terraform template (/terraform/cluster/main.tf). These new variables enable deployers to specify which Managed Node Groups (MNGs) should have x86 or arm-based instances. There is also an option to have all MNGs in a cluster have either x86 or arm-based instances. 66 | - Modified the EKS Module terraform file (/terraform/cluster/modules/cluster/main.tf) so that the new variables impact what architecture the nodes in each MNG uses. 67 | - @sdpoueme and I had a discussion about whether to use booleans or a list of MNGs to have arm-based architectures. In the end, we decided on booleans over a list for the sake of intuitiveness. While a more verbose option, it is simpler and faster to implement. Plus, there is less error for typos accidentally attempting to configure non-existent MNGs. With boolean values, terraform will alert the deployer of any typos, stating that the given option does not exist or that the value passed into an option is an invalid boolean value. 68 | - Updated the README.md file to show a more up-to-date option list. 69 | - The new most verbose launch command for the core EKS Cluster through terraform with arm-based instances will be like so: 70 | ``` 71 | # Initialize Terraform 72 | terraform -chdir=terraform/cluster init && 73 | # Create both clusters 74 | terraform -chdir=terraform/cluster apply -auto-approve \ 75 | -var="cluster_1_name=${CLUSTER1}" \ 76 | -var="cluster_1_region=${REGION1}" \ 77 | -var="cluster_1_cidr=${CIDR1}" \ 78 | -var="cluster_2_name=${CLUSTER2}" \ 79 | -var="cluster_2_region=${REGION2}" \ 80 | -var="cluster_2_cidr=${CIDR2}" \ 81 | -var="cluster_version=${VERSION}" \ 82 | -var="all_arm_based_instances_cluster_1"=false \ 83 | -var="all_arm_based_instances_cluster_2"=false \ 84 | -var="gameservers_arm_based_instances_cluster_1"=true \ 85 | -var="gameservers_arm_based_instances_cluster_2"=true \ 86 | -var="agones_system_arm_based_instances_cluster_1"=true \ 87 | -var="agones_system_arm_based_instances_cluster_2"=true \ 88 | -var="agones_metrics_arm_based_instances_cluster_1"=true \ 89 | -var="agones_metrics_arm_based_instances_cluster_2"=true \ 90 | -var="open_match_arm_based_instances_cluster_1"=false \ 91 | -var="open_match_arm_based_instances_cluster_2"=false \ 92 | -var="agones_open_match_arm_based_instances_cluster_1"=false \ 93 | -var="agones_open_match_arm_based_instances_cluster_2"=false 94 | ``` 95 | 96 | 97 | 98 | ## 8-8-2024 99 | **TLDR;** Pass in a boolean option when creating the core cluster in the CLI to switch between x86-based and arm-based instances in the cluster's Managed Node Group. The deployer is responsible for ensuring that the non-default instance types in the terraform variables files have the architecture that matches the expectation of the array they are in. 100 | 101 | - Added new variables to both of the terraform variables files in the core EKS Cluster (/terraform/cluster/variables.tf & /terraform/cluster/modules/cluster/variables.tf). These new variables enable people to switch between using an array of x86-based instances and an array of arm-based instances for the nodes in the core EKS cluster. A boolean variable was added to the /terraform/cluster/variables.tf file to enable dynamic switching between x86-based instances and arm-based instances in the EKS Module terraform file (/terraform/cluster/modules/cluster/main.tf). 102 | - Modified the EKS Module terraform file (/terraform/cluster/modules/cluster/main.tf) so that based on the boolean option passed into the terraform cluster creation command, either an array of x86-based instance types or an array of arm-based instance types will be used for provisioning nodes in the main Managed Node Group of the core cluster. 103 | - @sdpoueme and I had a discussion about enforcing arm-based and x86-based instance types in the two arrays, but in the end it was decided that it is up to the deployer to ensure that the correct instance types are in their appropriate array. We will ensure that the defaults are always correct. 104 | - The new command for launching the core EKS Cluster through terraform with arm-based instances will be like so: 105 | ``` 106 | # Initialize Terraform 107 | terraform -chdir=terraform/cluster init && 108 | # Create both clusters 109 | terraform -chdir=terraform/cluster apply -auto-approve \ 110 | -var="cluster_1_name=${CLUSTER1}" \ 111 | -var="cluster_1_region=${REGION1}" \ 112 | -var="cluster_1_cidr=${CIDR1}" \ 113 | -var="cluster_2_name=${CLUSTER2}" \ 114 | -var="cluster_2_region=${REGION2}" \ 115 | -var="cluster_2_cidr=${CIDR2}" \ 116 | -var="cluster_version=${VERSION}" \ 117 | -var="arm_based_instances"=true 118 | ``` 119 | - Minor updates to the main README.md file to clarify installation and deployment instructions 120 | - In this commit, all instance and ami types in the core cluster (gameservers, agones, openmatch, etc.) are toggled between x86 and arm architectures through the one option in the cli command (arm_based_instances=true). So all instances and ami's are either arm-based or x86-based. -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | CODEOWNERS @aws-solutions-library-samples/maintainers 2 | /.github/workflows/maintainer_workflows.yml @aws-solutions-library-samples/maintainers 3 | /.github/solutionid_validator.sh @aws-solutions-library-samples/maintainers 4 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *main* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this 4 | software and associated documentation files (the "Software"), to deal in the Software 5 | without restriction, including without limitation the rights to use, copy, modify, 6 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 7 | permit persons to whom the Software is furnished to do so. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 10 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 11 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 12 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 13 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 14 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /agones-openmatch-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-game-server-hosting-using-agones-and-open-match-on-amazon-eks/19b9249d74a8c71e56c498362d5910033d59aa52/agones-openmatch-flow.png -------------------------------------------------------------------------------- /agones-openmatch-multicluster.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-game-server-hosting-using-agones-and-open-match-on-amazon-eks/19b9249d74a8c71e56c498362d5910033d59aa52/agones-openmatch-multicluster.1.png -------------------------------------------------------------------------------- /agones-openmatch-multicluster.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-game-server-hosting-using-agones-and-open-match-on-amazon-eks/19b9249d74a8c71e56c498362d5910033d59aa52/agones-openmatch-multicluster.2.png -------------------------------------------------------------------------------- /agones-openmatch-multicluster.3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-game-server-hosting-using-agones-and-open-match-on-amazon-eks/19b9249d74a8c71e56c498362d5910033d59aa52/agones-openmatch-multicluster.3.png -------------------------------------------------------------------------------- /agones-openmatch-multicluster.4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-game-server-hosting-using-agones-and-open-match-on-amazon-eks/19b9249d74a8c71e56c498362d5910033d59aa52/agones-openmatch-multicluster.4.png -------------------------------------------------------------------------------- /agones-openmatch-multicluster.5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-game-server-hosting-using-agones-and-open-match-on-amazon-eks/19b9249d74a8c71e56c498362d5910033d59aa52/agones-openmatch-multicluster.5.png -------------------------------------------------------------------------------- /agones-openmatch-multicluster.final.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-game-server-hosting-using-agones-and-open-match-on-amazon-eks/19b9249d74a8c71e56c498362d5910033d59aa52/agones-openmatch-multicluster.final.png -------------------------------------------------------------------------------- /integration/clients/allocation-client/README.md: -------------------------------------------------------------------------------- 1 | # Allocation module 2 | 3 | This module implements the communication of our game client with the Open Match Frontend service. It creates a ticket, submits a ticket request, wait until the ticket assignement is returned by the Frontend, and returns the connection string to its caller. It is used by the ncat sample client to connect to the ncat-server ([../ncat/](../ncat/)), and by the golang wrapper program used to integrate the SuperTuxKart client with Open Match functions ([../stk/](../stk/)). Please, refer to these folders and the page [Open Match - Game Frontend](https://open-match.dev/site/docs/guides/matchmaker/frontend/) for more information. -------------------------------------------------------------------------------- /integration/clients/allocation-client/allocation.go: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | package allocation 5 | 6 | import ( 7 | "context" 8 | "crypto/tls" 9 | "crypto/x509" 10 | "fmt" 11 | "log" 12 | "os" 13 | 14 | "github.com/google/uuid" 15 | "github.com/pkg/errors" 16 | "google.golang.org/grpc" 17 | "google.golang.org/grpc/credentials" 18 | "open-match.dev/open-match/pkg/pb" 19 | ) 20 | 21 | const GAME_MODE_SESSION = "mode.session" 22 | 23 | type MatchRequest struct { 24 | Ticket *pb.Ticket 25 | Tags []string 26 | StringArgs map[string]string 27 | DoubleArgs map[string]float64 28 | } 29 | 30 | type Player struct { 31 | UID string 32 | MatchRequest *MatchRequest 33 | } 34 | 35 | func createRemoteClusterDialOption(clientCert, clientKey, caCert []byte) (grpc.DialOption, error) { 36 | cert, err := tls.X509KeyPair(clientCert, clientKey) 37 | if err != nil { 38 | return nil, err 39 | } 40 | 41 | tlsConfig := &tls.Config{MinVersion: tls.VersionTLS13, Certificates: []tls.Certificate{cert}} 42 | if len(caCert) != 0 { 43 | tlsConfig.RootCAs = x509.NewCertPool() 44 | tlsConfig.ServerName = "open-match-evaluator" 45 | if !tlsConfig.RootCAs.AppendCertsFromPEM(caCert) { 46 | return nil, errors.New("only PEM format is accepted for server CA") 47 | } 48 | } 49 | 50 | return grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)), nil 51 | } 52 | 53 | func GetServerAssignment(omFrontendEndpoint string, region1 string, latencyRegion1 int, region2 string, latencyRegion2 int) string { 54 | log.Printf("Connecting to Open Match Frontend: " + omFrontendEndpoint) 55 | cert, err := os.ReadFile("public.cert") 56 | if err != nil { 57 | panic(err) 58 | } 59 | key, err := os.ReadFile("private.key") 60 | if err != nil { 61 | panic(err) 62 | } 63 | cacert, err := os.ReadFile("publicCA.cert") 64 | if err != nil { 65 | panic(err) 66 | } 67 | dialOpts, err := createRemoteClusterDialOption(cert, key, cacert) 68 | if err != nil { 69 | panic(err) 70 | } 71 | conn, err := grpc.Dial(omFrontendEndpoint, dialOpts) 72 | if err != nil { 73 | log.Fatalf("Failed to connect to Open Match Frontend, got %s", err.Error()) 74 | } 75 | 76 | feService := pb.NewFrontendServiceClient(conn) 77 | 78 | player := &Player{ 79 | UID: uuid.New().String(), 80 | MatchRequest: &MatchRequest{ 81 | Tags: []string{GAME_MODE_SESSION}, 82 | DoubleArgs: map[string]float64{ 83 | "latency-" + region1: float64(latencyRegion1), 84 | "latency-" + region2: float64(latencyRegion2), 85 | }, 86 | }} 87 | req := &pb.CreateTicketRequest{ 88 | Ticket: &pb.Ticket{ 89 | SearchFields: &pb.SearchFields{ 90 | Tags: player.MatchRequest.Tags, 91 | DoubleArgs: player.MatchRequest.DoubleArgs, 92 | }, 93 | }, 94 | } 95 | ticket, err := feService.CreateTicket(context.Background(), req) 96 | if err != nil { 97 | log.Fatalf("Error: %v", err) 98 | } 99 | log.Printf("Ticket ID: %s\n", ticket.Id) 100 | log.Printf("Waiting for ticket assignment") 101 | for { 102 | req := &pb.GetTicketRequest{ 103 | TicketId: ticket.Id, 104 | } 105 | ticket, err := feService.GetTicket(context.Background(), req) 106 | 107 | if err != nil { 108 | return fmt.Sprintf("Was not able to get a ticket, err: %s\n", err.Error()) 109 | } 110 | 111 | if ticket.Assignment != nil { 112 | log.Printf("Ticket assignment: %s\n", ticket.Assignment) 113 | log.Printf("Disconnecting from Open Match Frontend") 114 | 115 | defer conn.Close() 116 | return ticket.Assignment.String() 117 | } 118 | 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /integration/clients/allocation-client/go.mod: -------------------------------------------------------------------------------- 1 | module agones-openmatch/allocator 2 | 3 | go 1.21 4 | 5 | require ( 6 | github.com/google/uuid v1.3.0 7 | github.com/pkg/errors v0.9.1 8 | google.golang.org/grpc v1.57.1 9 | open-match.dev/open-match v1.8.0 10 | ) 11 | 12 | require ( 13 | github.com/golang/protobuf v1.5.3 // indirect 14 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.17.1 // indirect 15 | golang.org/x/net v0.33.0 // indirect 16 | golang.org/x/sys v0.28.0 // indirect 17 | golang.org/x/text v0.21.0 // indirect 18 | google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d // indirect 19 | google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d // indirect 20 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect 21 | google.golang.org/protobuf v1.33.0 // indirect 22 | ) 23 | -------------------------------------------------------------------------------- /integration/clients/allocation-client/go.sum: -------------------------------------------------------------------------------- 1 | github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= 2 | github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= 3 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 4 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= 5 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 6 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 7 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 8 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 9 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 10 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 11 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.17.1 h1:LSsiG61v9IzzxMkqEr6nrix4miJI62xlRjwT7BYD2SM= 12 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.17.1/go.mod h1:Hbb13e3/WtqQ8U5hLGkek9gJvBLasHuPFI0UEGfnQ10= 13 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 14 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 15 | golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= 16 | golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= 17 | golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= 18 | golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 19 | golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= 20 | golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= 21 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 22 | google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d h1:VBu5YqKPv6XiJ199exd8Br+Aetz+o08F+PLMnwJQHAY= 23 | google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= 24 | google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d h1:DoPTO70H+bcDXcd39vOqb2viZxgqeBeSGtZ55yZU4/Q= 25 | google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= 26 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4= 27 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= 28 | google.golang.org/grpc v1.57.1 h1:upNTNqv0ES+2ZOOqACwVtS3Il8M12/+Hz41RCPzAjQg= 29 | google.golang.org/grpc v1.57.1/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= 30 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 31 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 32 | google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= 33 | google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 34 | open-match.dev/open-match v1.8.0 h1:gk3nXv2PMA0FsvoeXt/YnGaw+RbPs4kdKb7PhfFDdZI= 35 | open-match.dev/open-match v1.8.0/go.mod h1:O0c6efyO6rrSSvHBiwFq8RHD6g8PbNDUcVVS3JHgB5k= 36 | -------------------------------------------------------------------------------- /integration/clients/ncat/README.md: -------------------------------------------------------------------------------- 1 | # ncat client 2 | 3 | This code implements a simple chat client that communicates with the [ncat-server](../../ncat-server/) game servers deployed to our clusters. It calls the [allocation](../allocation-client/) module that handles the communication with the Frontend, connects to the game server address returned, and runs a loop to exchange messages with the other clients connected to the ncat-server. 4 | ```bash 5 | Usage: 6 | player -frontend FrontendAddress:Port -latencyRegion1 int -latencyRegion2 int 7 | -frontend string 8 | Open Match Frontend Endpoint (default "localhost:50504") 9 | -latencyRegion1 int 10 | Latency to region 1 (default 100) 11 | -latencyRegion2 int 12 | Latency to region 2 (default 100) 13 | ``` 14 | ![](./ncat-sample.png)*Sample screen* 15 | 16 | Note: this code uses TLS to connect to the Open Match Frontend, it expects the files `public.cert`, `publicCA.cert`, and `private.key` in the same directory. Refer to the main [README.md](../../../README.md#test-the-ncat-server) for instructions on how to create the TLS files. -------------------------------------------------------------------------------- /integration/clients/ncat/go.mod: -------------------------------------------------------------------------------- 1 | module agones-openmatch/ncat-player 2 | 3 | go 1.21 4 | 5 | require agones-openmatch/allocation v0.0.0-00010101000000-000000000000 6 | 7 | require ( 8 | github.com/golang/protobuf v1.5.3 // indirect 9 | github.com/google/uuid v1.3.0 // indirect 10 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.17.1 // indirect 11 | github.com/pkg/errors v0.9.1 // indirect 12 | golang.org/x/net v0.23.0 // indirect 13 | golang.org/x/sys v0.18.0 // indirect 14 | golang.org/x/text v0.14.0 // indirect 15 | google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d // indirect 16 | google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d // indirect 17 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect 18 | google.golang.org/grpc v1.57.1 // indirect 19 | google.golang.org/protobuf v1.33.0 // indirect 20 | open-match.dev/open-match v1.8.0 // indirect 21 | ) 22 | 23 | replace agones-openmatch/allocation => ../allocation-client 24 | -------------------------------------------------------------------------------- /integration/clients/ncat/go.sum: -------------------------------------------------------------------------------- 1 | github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= 2 | github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= 3 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 4 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= 5 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 6 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 7 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 8 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 9 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 10 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 11 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.17.1 h1:LSsiG61v9IzzxMkqEr6nrix4miJI62xlRjwT7BYD2SM= 12 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.17.1/go.mod h1:Hbb13e3/WtqQ8U5hLGkek9gJvBLasHuPFI0UEGfnQ10= 13 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 14 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 15 | golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= 16 | golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= 17 | golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= 18 | golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 19 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= 20 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 21 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 22 | google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d h1:VBu5YqKPv6XiJ199exd8Br+Aetz+o08F+PLMnwJQHAY= 23 | google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= 24 | google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d h1:DoPTO70H+bcDXcd39vOqb2viZxgqeBeSGtZ55yZU4/Q= 25 | google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= 26 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4= 27 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= 28 | google.golang.org/grpc v1.57.1 h1:upNTNqv0ES+2ZOOqACwVtS3Il8M12/+Hz41RCPzAjQg= 29 | google.golang.org/grpc v1.57.1/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= 30 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 31 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 32 | google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= 33 | google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 34 | open-match.dev/open-match v1.8.0 h1:gk3nXv2PMA0FsvoeXt/YnGaw+RbPs4kdKb7PhfFDdZI= 35 | open-match.dev/open-match v1.8.0/go.mod h1:O0c6efyO6rrSSvHBiwFq8RHD6g8PbNDUcVVS3JHgB5k= 36 | -------------------------------------------------------------------------------- /integration/clients/ncat/main.go: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | package main 5 | 6 | import ( 7 | "agones-openmatch/allocation" 8 | "bufio" 9 | "flag" 10 | "fmt" 11 | "net" 12 | "os" 13 | "sync" 14 | 15 | "strings" 16 | ) 17 | 18 | const ( 19 | MSG_DISCONNECT = "Disconnected from the server.\n" 20 | CONN_TYPE = "tcp" 21 | ) 22 | 23 | var wg sync.WaitGroup 24 | 25 | func Read(conn net.Conn) { 26 | reader := bufio.NewReader(conn) 27 | for { 28 | str, err := reader.ReadString('\n') 29 | if err != nil { 30 | fmt.Print(MSG_DISCONNECT) 31 | wg.Done() 32 | return 33 | } 34 | fmt.Print(str) 35 | } 36 | } 37 | 38 | func Write(conn net.Conn) { 39 | reader := bufio.NewReader(os.Stdin) 40 | writer := bufio.NewWriter(conn) 41 | 42 | for { 43 | str, err := reader.ReadString('\n') 44 | if err != nil { 45 | fmt.Println(err) 46 | os.Exit(1) 47 | } 48 | 49 | _, err = writer.WriteString(str) 50 | if err != nil { 51 | fmt.Println(err) 52 | os.Exit(1) 53 | } 54 | err = writer.Flush() 55 | if err != nil { 56 | fmt.Println(err) 57 | os.Exit(1) 58 | } 59 | } 60 | } 61 | func ConnectGameServer(server string) { 62 | 63 | wg.Add(1) 64 | 65 | fmt.Printf("Connecting to ncat server") 66 | conn, err := net.Dial(CONN_TYPE, server) 67 | if err != nil { 68 | fmt.Println(err) 69 | } 70 | 71 | go Read(conn) 72 | go Write(conn) 73 | 74 | wg.Wait() 75 | } 76 | 77 | var omFrontendEndpoint, region1, region2 string 78 | var latencyRegion1, latencyRegion2 int 79 | 80 | func main() { 81 | flag.StringVar(&omFrontendEndpoint, "frontend", "localhost:50504", "Open Match Frontend Endpoint") 82 | flag.StringVar(®ion1, "region1", "us-east-1", "Region 1") 83 | flag.IntVar(&latencyRegion1, "latencyRegion1", 100, "Latency to region 1") 84 | flag.StringVar(®ion2, "region2", "us-east-2", "Region 2") 85 | flag.IntVar(&latencyRegion2, "latencyRegion2", 100, "Latency to region 2") 86 | flag.Usage = func() { 87 | fmt.Printf("Usage: \n") 88 | fmt.Printf("player -frontend FrontendAddress:Port -latencyRegion1 int -latencyRegion2 int\n") 89 | flag.PrintDefaults() 90 | } 91 | flag.Parse() 92 | serverPort := allocation.GetServerAssignment(omFrontendEndpoint, region1, latencyRegion1, region2, latencyRegion2) 93 | fmt.Println(serverPort) 94 | serverPort = strings.Replace(serverPort, "\"", "", -1) 95 | serverPort = strings.Replace(serverPort, "connection:", "", 1) 96 | ConnectGameServer(serverPort) 97 | } 98 | -------------------------------------------------------------------------------- /integration/clients/ncat/ncat-sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-game-server-hosting-using-agones-and-open-match-on-amazon-eks/19b9249d74a8c71e56c498362d5910033d59aa52/integration/clients/ncat/ncat-sample.png -------------------------------------------------------------------------------- /integration/clients/stk/README.md: -------------------------------------------------------------------------------- 1 | # SuperTuxKart client wrapper 2 | 3 | This code implements a wrapper to call the SuperTuxKart client with the server/port returned by the [allocation](../allocation-client/) module that handles the communication with the Open Match Frontend. 4 | 5 | To test this server, we need to download the client from https://github.com/supertuxkart/stk-code/releases and install it to our system, taking note of the location of the STK binary to use in our test. 6 | 7 | ## Deploy the game server fleets 8 | 9 | 1. Remove ncat servers 10 | If there were ncat servers deployed to the clusters following the [main instructions](../../../README.md#build-and-deploy-the-game-server-fleets), remove them from the clusters. 11 | ```bash 12 | kubectl config use-context $(kubectl config get-contexts -o=name | grep ${CLUSTER1}) 13 | kubectl delete fleets -n gameservers --all 14 | kubectl config use-context $(kubectl config get-contexts -o=name | grep ${CLUSTER2}) 15 | kubectl delete fleets -n gameservers --all 16 | ``` 17 | 18 | 2. Deploy STK servers 19 | For x86-based instances: 20 | ```bash 21 | sh scripts/deploy-stk-fleets.sh ${CLUSTER1} ${REGION1} ${CLUSTER2} ${REGION2} amd64 22 | ``` 23 | 24 | For arm-based instances: 25 | ```bash 26 | sh scripts/deploy-stk-fleets.sh ${CLUSTER1} ${REGION1} ${CLUSTER2} ${REGION2} arm64 27 | ``` 28 | 29 | ### Test the stk server 30 | 1. Go to the `integration/clients/stk` 31 | ```bash 32 | cd integration/clients/stk 33 | ``` 34 | 2. Get the TLS cert of the Frontend 35 | ```bash 36 | kubectl get secret open-match-tls-server -n open-match -o jsonpath="{.data.public\.cert}" | base64 -d > public.cert 37 | kubectl get secret open-match-tls-server -n open-match -o jsonpath="{.data.private\.key}" | base64 -d > private.key 38 | kubectl get secret open-match-tls-rootca -n open-match -o jsonpath="{.data.public\.cert}" | base64 -d > publicCA.cert 39 | ``` 40 | 3. Run the player client. Here we'll use the value of `global_accelerator_address` from the Terraform deployment. Remember to adjust our regions and use the location of the installed STK binary: 41 | ```bash 42 | REGION1=us-east-1 43 | REGION2=us-east-2 44 | go run main.go -frontend :50504 -region1 $REGION1 -latencyRegion1 10 -region2 $REGION2 -latencyRegion2 30 -path /path/to/stk/binary 45 | ``` 46 | 3. In three other terminal windows, type the commands from the steps **(1.)** and **(3.)** above. 47 | Be aware that this will run 4 instances of the SuperTuxKart client game (like we did with our terminal clients in the [ncat example](../../../README.md#test-the-ncat-server)), so it can be a bit demanding to run it in a single computer. One alternative is to clone this project on 3 other computers and run the steps above on them. From this point on, the server behavior should be the same as the observed in the ncat example. -------------------------------------------------------------------------------- /integration/clients/stk/go.mod: -------------------------------------------------------------------------------- 1 | module agones-openmatch/stk 2 | 3 | go 1.21 4 | 5 | require agones-openmatch/allocation v0.0.0-00010101000000-000000000000 6 | 7 | require ( 8 | github.com/golang/protobuf v1.5.3 // indirect 9 | github.com/google/uuid v1.3.0 // indirect 10 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.17.1 // indirect 11 | github.com/pkg/errors v0.9.1 // indirect 12 | golang.org/x/net v0.23.0 // indirect 13 | golang.org/x/sys v0.18.0 // indirect 14 | golang.org/x/text v0.14.0 // indirect 15 | google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d // indirect 16 | google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d // indirect 17 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect 18 | google.golang.org/grpc v1.57.1 // indirect 19 | google.golang.org/protobuf v1.33.0 // indirect 20 | open-match.dev/open-match v1.8.0 // indirect 21 | ) 22 | 23 | replace agones-openmatch/allocation => ../allocation-client -------------------------------------------------------------------------------- /integration/clients/stk/go.sum: -------------------------------------------------------------------------------- 1 | github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= 2 | github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= 3 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 4 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= 5 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 6 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 7 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 8 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 9 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 10 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 11 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.17.1 h1:LSsiG61v9IzzxMkqEr6nrix4miJI62xlRjwT7BYD2SM= 12 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.17.1/go.mod h1:Hbb13e3/WtqQ8U5hLGkek9gJvBLasHuPFI0UEGfnQ10= 13 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 14 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 15 | golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= 16 | golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= 17 | golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= 18 | golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 19 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= 20 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 21 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 22 | google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d h1:VBu5YqKPv6XiJ199exd8Br+Aetz+o08F+PLMnwJQHAY= 23 | google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= 24 | google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d h1:DoPTO70H+bcDXcd39vOqb2viZxgqeBeSGtZ55yZU4/Q= 25 | google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= 26 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4= 27 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= 28 | google.golang.org/grpc v1.57.1 h1:upNTNqv0ES+2ZOOqACwVtS3Il8M12/+Hz41RCPzAjQg= 29 | google.golang.org/grpc v1.57.1/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= 30 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 31 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 32 | google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= 33 | google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 34 | open-match.dev/open-match v1.8.0 h1:gk3nXv2PMA0FsvoeXt/YnGaw+RbPs4kdKb7PhfFDdZI= 35 | open-match.dev/open-match v1.8.0/go.mod h1:O0c6efyO6rrSSvHBiwFq8RHD6g8PbNDUcVVS3JHgB5k= -------------------------------------------------------------------------------- /integration/clients/stk/main.go: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | package main 5 | 6 | import ( 7 | "flag" 8 | "fmt" 9 | "os/exec" 10 | 11 | "agones-openmatch/allocation" 12 | "strings" 13 | ) 14 | 15 | var omFrontendEndpoint, stkPath, region1, region2 string 16 | var latencyRegion1, latencyRegion2 int 17 | 18 | func main() { 19 | 20 | flag.StringVar(&omFrontendEndpoint, "frontend", "localhost:50504", "Open Match Frontend Endpoint") 21 | flag.StringVar(&stkPath, "path", "supertuxkart", "SuperTuxKart binary path") 22 | flag.StringVar(®ion1, "region1", "us-east-1", "Region 1") 23 | flag.IntVar(&latencyRegion1, "latencyRegion1", 100, "Latency to region 1") 24 | flag.StringVar(®ion2, "region2", "us-east-2", "Region 2") 25 | flag.IntVar(&latencyRegion2, "latencyRegion2", 100, "Latency to region 2") 26 | flag.Usage = func() { 27 | fmt.Printf("Usage: \n") 28 | fmt.Printf("player -frontend FrontendAddress:Port -latencyRegion1 int -latencyRegion2 int -path /path/to/stk/binary\n") 29 | flag.PrintDefaults() 30 | } 31 | flag.Parse() 32 | serverPort := allocation.GetServerAssignment(omFrontendEndpoint, region1, latencyRegion1, region2, latencyRegion2) 33 | serverPort = strings.Replace(serverPort, "\"", "", -1) 34 | serverPort = strings.Replace(serverPort, "connection:", "", 1) 35 | fmt.Println(serverPort) 36 | // nosemgrep 37 | cmd := exec.Command(stkPath, "--owner-less", "--connect-now="+serverPort) 38 | if err := cmd.Run(); err != nil { 39 | fmt.Println("Error: ", err) 40 | } 41 | 42 | return 43 | } -------------------------------------------------------------------------------- /integration/director/Dockerfile: -------------------------------------------------------------------------------- 1 | ## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | ## SPDX-License-Identifier: MIT-0 3 | 4 | FROM public.ecr.aws/docker/library/golang:1.21.4-alpine3.18 as go 5 | RUN apk add git 6 | WORKDIR /app 7 | ENV GO111MODULE=on 8 | ENV GOPROXY=direct,https://proxy.golang.org 9 | 10 | COPY go.mod . 11 | COPY go.sum . 12 | RUN go mod download -x 13 | COPY *.go . 14 | RUN go build -o director . 15 | 16 | FROM public.ecr.aws/docker/library/alpine:3.18 17 | 18 | WORKDIR /app 19 | COPY --from=go /app/director /app/director 20 | USER 999 21 | HEALTHCHECK CMD ["true"] 22 | 23 | CMD ["./director"] 24 | -------------------------------------------------------------------------------- /integration/director/README.md: -------------------------------------------------------------------------------- 1 | ## Name 2 | Guidance for Game Server Hosting on Amazon EKS with Agones and Open Match - Director 3 | # Description 4 | This module implements a Director for OpenMatch. The Director fetches Matches from Open Match for a set of MatchProfiles. 5 | ## Current State ## 6 | The Director submit MatchProfiles with latency requirements to the [Match Function](../matchfunction), and after receiving the Match Proposals, it sends an allocation request to the Agones Allocator service. After receiving the allocattion from Agones, the Director returns the game server details to Open Match Frontend service, like in the logs below: 7 | ``` 8 | 2024/01/20 21:13:11 Generated 1 matches for profile profile_double_arg:"latency-us-east-2" max:49 min:25 9 | 2024/01/20 21:13:12 Gameserver: ec2-3-22-130-7.us-east-2.compute.amazonaws.com 10 | 2024/01/20 21:13:12 Port: 7851 11 | 2024/01/20 21:13:12 Assigned server ec2-3-22-130-7.us-east-2.compute.amazonaws.com:7851 to match profile-profile_double_arg:"latency-us-east-2" max:49 min:25-time-2024-01-20T21:13:11.24-0 12 | 13 | ``` 14 | ## Testing and monitoring 15 | The [Match Function](../matchfunction) receives profile requests from the Open-Match backend provided by the Director and fetches the pools from the Open-Match query service. The execution flow can be inspected through the logs of the director, mmf (in the agones-openmatch namespace) and query (in the open-match namespace) pods. 16 | -------------------------------------------------------------------------------- /integration/director/director.yaml: -------------------------------------------------------------------------------- 1 | ## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | ## SPDX-License-Identifier: MIT-0 3 | 4 | 5 | apiVersion: apps/v1 6 | kind: Deployment 7 | metadata: 8 | labels: 9 | app: agones-openmatch-director 10 | name: agones-openmatch-director 11 | spec: 12 | progressDeadlineSeconds: 600 13 | replicas: 1 14 | revisionHistoryLimit: 10 15 | selector: 16 | matchLabels: 17 | app: agones-openmatch-director 18 | strategy: 19 | rollingUpdate: 20 | maxSurge: 25% 21 | maxUnavailable: 25% 22 | type: RollingUpdate 23 | template: 24 | metadata: 25 | labels: 26 | app: agones-openmatch-director 27 | spec: 28 | containers: 29 | 30 | - args: 31 | - /app/director 32 | - -backendAddr 33 | - $(OPENMATCH_BACKEND_ADDR) 34 | - -backendPort 35 | - $(OPENMATCH_BACKEND_PORT) 36 | - -functionAddr 37 | - $(OPENMATCH_MATCH_FUNCTION_ADDR) 38 | - -functionPort 39 | - $(OPENMATCH_MATCH_FUNCTION_PORT) 40 | - -allocatorAddr 41 | - $(AGONES_ALLOCATOR_ADDR) 42 | - -allocatorPort 43 | - $(AGONES_ALLOCATOR_PORT) 44 | - -certFile 45 | - $(CRT_FILE) 46 | - -keyFile 47 | - $(KEY_FILE) 48 | - -caFile 49 | - $(CA_FILE) 50 | - -namespace 51 | - $(NAMESPACE) 52 | - -multicluster 53 | - -regions 54 | - $(REGIONS) 55 | - -ranges 56 | - $(RANGES) 57 | - -interval 58 | - $(INTERVAL) 59 | 60 | env: 61 | - name: OPENMATCH_BACKEND_ADDR 62 | value: open-match-backend.open-match.svc.cluster.local 63 | - name: OPENMATCH_BACKEND_PORT 64 | value: "50505" 65 | - name: OPENMATCH_MATCH_FUNCTION_ADDR 66 | value: agones-openmatch-mmf.agones-openmatch.svc.cluster.local 67 | - name: OPENMATCH_MATCH_FUNCTION_PORT 68 | value: "50502" 69 | - name: AGONES_ALLOCATOR_ADDR 70 | value: agones-allocator.agones-system.svc.cluster.local 71 | - name: AGONES_ALLOCATOR_PORT 72 | value: "443" 73 | - name: CRT_FILE 74 | value: tls.crt 75 | - name: KEY_FILE 76 | value: tls.key 77 | - name: CA_FILE 78 | value: ca.crt 79 | - name: NAMESPACE 80 | value: agones-system 81 | - name: REGIONS 82 | value: ${REGION1},${REGION2} 83 | - name: RANGES 84 | value: 0-24,25-49,50-74,74-99,100-9999 85 | - name: INTERVAL 86 | value: "5" 87 | 88 | image: ${REGISTRY}/agones-openmatch-director 89 | imagePullPolicy: Always 90 | securityContext: 91 | allowPrivilegeEscalation: false 92 | name: director 93 | resources: 94 | limits: 95 | cpu: "1" 96 | memory: 100Mi 97 | requests: 98 | cpu: 100m 99 | memory: 50Mi 100 | terminationMessagePath: /dev/termination-log 101 | terminationMessagePolicy: File 102 | volumeMounts: 103 | - mountPath: /app/agones-tls 104 | name: agones-tls-volume 105 | - mountPath: /app/openmatch-tls 106 | name: openmatch-tls-volume 107 | - mountPath: /app/global-accelerator-mapping 108 | name: global-accelerator-mapping 109 | hostname: agones-openmatch-director 110 | dnsPolicy: ClusterFirst 111 | restartPolicy: Always 112 | schedulerName: default-scheduler 113 | securityContext: {} 114 | terminationGracePeriodSeconds: 30 115 | affinity: 116 | nodeAffinity: 117 | requiredDuringSchedulingIgnoredDuringExecution: 118 | nodeSelectorTerms: 119 | - matchExpressions: 120 | - key: kubernetes.io/arch 121 | operator: In 122 | values: 123 | - amd64 124 | - key: openmatch 125 | operator: In 126 | values: 127 | - customization 128 | volumes: 129 | - configMap: 130 | defaultMode: 420 131 | name: allocator-tls 132 | name: agones-tls-volume 133 | - secret: 134 | defaultMode: 420 135 | secretName: open-match-tls-certmanager 136 | name: openmatch-tls-volume 137 | - configMap: 138 | defaultMode: 420 139 | name: global-accelerator-mapping 140 | name: global-accelerator-mapping 141 | 142 | -------------------------------------------------------------------------------- /integration/director/go.mod: -------------------------------------------------------------------------------- 1 | module agones-openmatch/director 2 | 3 | go 1.21 4 | 5 | require ( 6 | agones.dev/agones v1.36.0 7 | github.com/pkg/errors v0.9.1 8 | google.golang.org/grpc v1.57.1 9 | open-match.dev/open-match v1.8.0 10 | ) 11 | 12 | require ( 13 | github.com/golang/protobuf v1.5.3 // indirect 14 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.17.1 // indirect 15 | golang.org/x/net v0.23.0 // indirect 16 | golang.org/x/sys v0.18.0 // indirect 17 | golang.org/x/text v0.14.0 // indirect 18 | google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d // indirect 19 | google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d // indirect 20 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect 21 | google.golang.org/protobuf v1.33.0 // indirect 22 | ) 23 | -------------------------------------------------------------------------------- /integration/director/go.sum: -------------------------------------------------------------------------------- 1 | agones.dev/agones v1.36.0 h1:R28n+0bo0gbHflAPe4Fbd65Ui/y7StIoTppQiueRPh4= 2 | agones.dev/agones v1.36.0/go.mod h1:nSjtecthytiwgxu2GSDTPHXY6nyZB3cyBVSUBO6GHqo= 3 | github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= 4 | github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= 5 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 6 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= 7 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 8 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 9 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 10 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 11 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.17.1 h1:LSsiG61v9IzzxMkqEr6nrix4miJI62xlRjwT7BYD2SM= 12 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.17.1/go.mod h1:Hbb13e3/WtqQ8U5hLGkek9gJvBLasHuPFI0UEGfnQ10= 13 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 14 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 15 | golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= 16 | golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= 17 | golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= 18 | golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 19 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= 20 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 21 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 22 | google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d h1:VBu5YqKPv6XiJ199exd8Br+Aetz+o08F+PLMnwJQHAY= 23 | google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= 24 | google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d h1:DoPTO70H+bcDXcd39vOqb2viZxgqeBeSGtZ55yZU4/Q= 25 | google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= 26 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4= 27 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= 28 | google.golang.org/grpc v1.57.1 h1:upNTNqv0ES+2ZOOqACwVtS3Il8M12/+Hz41RCPzAjQg= 29 | google.golang.org/grpc v1.57.1/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= 30 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 31 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 32 | google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= 33 | google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 34 | open-match.dev/open-match v1.8.0 h1:gk3nXv2PMA0FsvoeXt/YnGaw+RbPs4kdKb7PhfFDdZI= 35 | open-match.dev/open-match v1.8.0/go.mod h1:O0c6efyO6rrSSvHBiwFq8RHD6g8PbNDUcVVS3JHgB5k= 36 | -------------------------------------------------------------------------------- /integration/director/main.go: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | package main 5 | 6 | import ( 7 | "compress/gzip" 8 | "context" 9 | "crypto/tls" 10 | "crypto/x509" 11 | "encoding/json" 12 | "flag" 13 | "fmt" 14 | "io" 15 | "log" 16 | "os" 17 | "regexp" 18 | "strconv" 19 | "strings" 20 | "sync" 21 | "time" 22 | 23 | pba "agones.dev/agones/pkg/allocation/go" 24 | "github.com/pkg/errors" 25 | "google.golang.org/grpc" 26 | "google.golang.org/grpc/credentials" 27 | "open-match.dev/open-match/pkg/pb" 28 | ) 29 | 30 | // This Director continuously polls Open Match for the Match 31 | // Profiles, submits the tickets to Agones Allocator service 32 | // and returns the allocation to the Frontend. 33 | 34 | var backendAddr, functionAddr, allocatorAddr, backendPort, certFile, keyFile, caFile, namespace, region, regions, ranges, regionPattern string 35 | var functionPort, allocatorPort, interval int 36 | var multicluster bool 37 | 38 | func main() { 39 | flag.StringVar(&backendAddr, "backendAddr", "open-match-backend.open-match.svc.cluster.local", "Open Match Backend Address") 40 | flag.StringVar(&backendPort, "backendPort", "50505", "Open Match backend Port") 41 | flag.StringVar(&functionAddr, "functionAddr", "agones-openmatch-mmf.agones-openmatch.svc.cluster.local", "Open Match Function Address") 42 | flag.IntVar(&functionPort, "functionPort", 50502, "Open Match Function Port") 43 | flag.StringVar(&allocatorAddr, "allocatorAddr", "agones-allocator.agones-system.svc.cluster.local", "Agones Allocator Address") 44 | flag.IntVar(&allocatorPort, "allocatorPort", 443, "Agones Allocator Port") 45 | flag.StringVar(&certFile, "certFile", "client.crt", "Certificate File") 46 | flag.StringVar(&keyFile, "keyFile", "client.key", "Key File") 47 | flag.StringVar(&caFile, "caFile", "ca.crt", "CA File") 48 | flag.BoolVar(&multicluster, "multicluster", false, "Multi-Cluster allocation") 49 | flag.StringVar(&namespace, "namespace", "default", "Game servers namespace") 50 | flag.StringVar(®ions, "regions", "us-east-1,us-east-2", "List of regions, separated by ','") 51 | flag.StringVar(&ranges, "ranges", "0-24,25-49,50-74,74-99,100-9999", "List of latency ranges, in the format min-max, separated by ','") 52 | flag.IntVar(&interval, "interval", 5, "Polling interval, in seconds") 53 | 54 | flag.Usage = func() { 55 | fmt.Printf("Usage: \n") 56 | fmt.Printf("director -backendAddr addr -backendPort PortNumber -functionAddr addr -functionPort PortNumber -allocatorAddr addr -allocatorPort PortNumber -interval seconds\n") 57 | flag.PrintDefaults() // prints default usage 58 | } 59 | flag.Parse() 60 | certFile = "./agones-tls/" + certFile 61 | keyFile = "./agones-tls/" + keyFile 62 | caFile = "./agones-tls/" + caFile 63 | regionsArr := strings.Split(regions, ",") 64 | rangesArr := strings.Split(ranges, ",") 65 | accelerator := make(map[string]string) 66 | // Regular expression for AWS regions 67 | regionPattern = `(us(-gov)?|af|ap|ca|cn|eu|il|me|sa)-(central|(north|south)?(east|west)?)-\d` 68 | 69 | log.Printf("Opening files") 70 | 71 | gzippedMapping1, err := os.Open("/app/global-accelerator-mapping/mapping1.gz") 72 | if err != nil { 73 | log.Fatal(err) 74 | } 75 | defer gzippedMapping1.Close() 76 | 77 | gzippedMapping2, err := os.Open("/app/global-accelerator-mapping/mapping2.gz") 78 | if err != nil { 79 | log.Fatal(err) 80 | } 81 | defer gzippedMapping2.Close() 82 | 83 | acceleratorFile1, err := os.ReadFile("/app/global-accelerator-mapping/accelerator1") 84 | if err != nil { 85 | log.Fatal(err) 86 | } 87 | accelerator[regionsArr[0]] = string(acceleratorFile1) 88 | acceleratorFile2, err := os.ReadFile("/app/global-accelerator-mapping/accelerator2") 89 | if err != nil { 90 | log.Fatal(err) 91 | } 92 | accelerator[regionsArr[1]] = string(acceleratorFile2) 93 | 94 | mapping1Reader, err := gzip.NewReader(gzippedMapping1) 95 | defer mapping1Reader.Close() 96 | 97 | mapping2Reader, err := gzip.NewReader(gzippedMapping2) 98 | defer mapping2Reader.Close() 99 | 100 | mapping1, err := io.ReadAll(mapping1Reader) 101 | if err != nil { 102 | panic(err) 103 | } 104 | mapping2, err := io.ReadAll(mapping2Reader) 105 | if err != nil { 106 | panic(err) 107 | } 108 | mappingJson := make(map[string]map[string]int) 109 | var mapping1Json, mapping2Json map[string]int 110 | err = json.Unmarshal(mapping1, &mapping1Json) 111 | if err != nil { 112 | log.Fatal("Error during Unmarshal() mapping1: ", err) 113 | } 114 | mappingJson[regionsArr[0]] = mapping1Json 115 | err = json.Unmarshal(mapping2, &mapping2Json) 116 | if err != nil { 117 | log.Fatal("Error during Unmarshal() mapping2: ", err) 118 | } 119 | mappingJson[regionsArr[1]] = mapping2Json 120 | err = json.Unmarshal(mapping2, &mapping2Json) 121 | if err != nil { 122 | log.Fatal("Error during Unmarshal() mapping2: ", err) 123 | } 124 | 125 | // Connect to Open Match Backend. 126 | 127 | beCert, err := os.ReadFile("./openmatch-tls/tls.crt") 128 | if err != nil { 129 | panic(err) 130 | } 131 | beKey, err := os.ReadFile("./openmatch-tls/tls.key") 132 | if err != nil { 133 | panic(err) 134 | } 135 | beCacert, err := os.ReadFile("./openmatch-tls/ca.crt") 136 | if err != nil { 137 | panic(err) 138 | } 139 | backendDialOpts, err := createRemoteClusterDialOption(beCert, beKey, beCacert) 140 | if err != nil { 141 | panic(err) 142 | } 143 | 144 | conn, err := grpc.Dial(backendAddr+":"+backendPort, backendDialOpts) 145 | 146 | if err != nil { 147 | log.Fatalf("Failed to connect to Open Match backend, got %s", err.Error()) 148 | } 149 | 150 | defer conn.Close() 151 | be := pb.NewBackendServiceClient(conn) 152 | 153 | // Generate the profiles to fetch matches for. 154 | profiles := generateProfiles(regionsArr, rangesArr) 155 | log.Printf("Fetching matches for %v profiles", len(profiles)) 156 | 157 | for range time.Tick(time.Second * time.Duration(interval)) { 158 | // Fetch matches for each profile 159 | var wg sync.WaitGroup 160 | for _, p := range profiles { 161 | wg.Add(1) 162 | go func(wg *sync.WaitGroup, p *pb.MatchProfile) { 163 | defer wg.Done() 164 | matches, err := fetch(be, p) 165 | if err != nil { 166 | log.Printf("Failed to fetch matches for profile %v, got %s", p.GetName(), err.Error()) 167 | return 168 | } 169 | if len(matches) > 0 { 170 | log.Printf("Generated %v matches for profile %v", len(matches), p.GetName()) 171 | } 172 | if err := assign(be, matches, mappingJson, accelerator); err != nil { 173 | log.Printf("Failed to assign servers to matches, got %s", err.Error()) 174 | return 175 | } 176 | }(&wg, p) 177 | } 178 | 179 | wg.Wait() 180 | } 181 | } 182 | 183 | func fetch(be pb.BackendServiceClient, p *pb.MatchProfile) ([]*pb.Match, error) { 184 | req := &pb.FetchMatchesRequest{ 185 | Config: &pb.FunctionConfig{ 186 | Host: functionAddr, 187 | Port: int32(functionPort), 188 | Type: pb.FunctionConfig_GRPC, 189 | }, 190 | Profile: p, 191 | } 192 | 193 | stream, err := be.FetchMatches(context.Background(), req) 194 | if err != nil { 195 | log.Println() 196 | return nil, err 197 | } 198 | 199 | var result []*pb.Match 200 | for { 201 | resp, err := stream.Recv() 202 | if err == io.EOF { 203 | break 204 | } 205 | 206 | if err != nil { 207 | return nil, err 208 | } 209 | 210 | result = append(result, resp.GetMatch()) 211 | } 212 | 213 | return result, nil 214 | } 215 | 216 | // Creates a grpc client dial option with TLS configuration. 217 | func createRemoteClusterDialOption(clientCert, clientKey, caCert []byte) (grpc.DialOption, error) { 218 | // Load client cert 219 | cert, err := tls.X509KeyPair(clientCert, clientKey) 220 | if err != nil { 221 | return nil, err 222 | } 223 | 224 | tlsConfig := &tls.Config{MinVersion: tls.VersionTLS13, Certificates: []tls.Certificate{cert}} 225 | if len(caCert) != 0 { 226 | tlsConfig.RootCAs = x509.NewCertPool() 227 | if !tlsConfig.RootCAs.AppendCertsFromPEM(caCert) { 228 | return nil, errors.New("only PEM format is accepted for server CA") 229 | } 230 | } 231 | 232 | return grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)), nil 233 | } 234 | 235 | // Get allocation from the Agones Allocator Service 236 | func getAllocation(matchId string) *pba.AllocationResponse { 237 | log.Printf("Requesting server allocation from Agones") 238 | endpoint := allocatorAddr + ":" + strconv.Itoa(allocatorPort) 239 | cert, err := os.ReadFile(certFile) 240 | if err != nil { 241 | panic(err) 242 | } 243 | key, err := os.ReadFile(keyFile) 244 | if err != nil { 245 | panic(err) 246 | } 247 | cacert, err := os.ReadFile(caFile) 248 | if err != nil { 249 | panic(err) 250 | } 251 | regexpPattern := regexp.MustCompile(regionPattern) 252 | region := regexpPattern.FindString(matchId) 253 | 254 | request := &pba.AllocationRequest{ 255 | Namespace: namespace, 256 | MultiClusterSetting: &pba.MultiClusterSetting{ 257 | Enabled: multicluster, 258 | }, 259 | GameServerSelectors: []*pba.GameServerSelector{ 260 | { 261 | MatchLabels: map[string]string{"region": region}, 262 | }, 263 | }, 264 | } 265 | allocatorDialOpts, err := createRemoteClusterDialOption(cert, key, cacert) 266 | if err != nil { 267 | panic(err) 268 | } 269 | conn, err := grpc.Dial(endpoint, allocatorDialOpts) 270 | if err != nil { 271 | panic(err) 272 | } 273 | defer conn.Close() 274 | 275 | grpcClient := pba.NewAllocationServiceClient(conn) 276 | response, err := grpcClient.Allocate(context.Background(), request) 277 | if err != nil { 278 | panic(err) 279 | } 280 | return response 281 | } 282 | 283 | func assign(be pb.BackendServiceClient, matches []*pb.Match, mappingJson map[string]map[string]int, accelerator map[string]string) error { 284 | for _, match := range matches { 285 | ticketIDs := []string{} 286 | for _, t := range match.GetTickets() { 287 | ticketIDs = append(ticketIDs, t.Id) 288 | } 289 | matchId := match.GetMatchId() 290 | regexpPattern := regexp.MustCompile(regionPattern) 291 | region = regexpPattern.FindString(matchId) 292 | 293 | allocation := getAllocation(matchId) 294 | log.Printf("Agones Allocator response: %s", allocation.String()) 295 | 296 | var gameServerAddress string 297 | for _, addr := range allocation.GetAddresses() { 298 | if addr.GetType() == "InternalIP" { 299 | gameServerAddress = addr.GetAddress() 300 | break 301 | } 302 | } 303 | log.Printf("Gameserver address: %s", gameServerAddress) 304 | var gameServerPort int32 305 | for _, p := range allocation.GetPorts() { 306 | if p.GetName() == "default" { 307 | gameServerPort = p.GetPort() 308 | break 309 | } 310 | } 311 | log.Printf("Port: %s", strconv.Itoa(int(gameServerPort))) 312 | internalIpPort := gameServerAddress + ":" + strconv.Itoa(int(gameServerPort)) 313 | log.Printf("internalIpPort: %s", internalIpPort) 314 | globalAcceleratorPort, _ := mappingJson[region][internalIpPort] 315 | conn := accelerator[region] + ":" + strconv.Itoa(globalAcceleratorPort) 316 | req := &pb.AssignTicketsRequest{ 317 | Assignments: []*pb.AssignmentGroup{ 318 | { 319 | TicketIds: ticketIDs, 320 | Assignment: &pb.Assignment{ 321 | Connection: conn, 322 | }, 323 | }, 324 | }, 325 | } 326 | 327 | if _, err := be.AssignTickets(context.Background(), req); err != nil { 328 | return fmt.Errorf("AssignTickets failed for match %v, got %w", match.GetMatchId(), err) 329 | } 330 | log.Printf("Assigned server %s to match %v", accelerator[region]+":"+strconv.Itoa(globalAcceleratorPort), match.GetMatchId()) 331 | } 332 | 333 | return nil 334 | } 335 | -------------------------------------------------------------------------------- /integration/director/profile.go: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | // nosemgrep 8 | "math/rand" 9 | "strconv" 10 | "strings" 11 | 12 | "open-match.dev/open-match/pkg/pb" 13 | ) 14 | 15 | // Generates profiles based on player latency for the Director. 16 | func generateProfiles(regions []string, ranges []string) []*pb.MatchProfile { 17 | var profiles []*pb.MatchProfile 18 | 19 | // We could add extra criteria for the profile, like continent name, region and skills, in this case we will use latency from the client to each region 20 | 21 | // continents := []string{"America", "Africa", "Asia", "Australia", "Europe"} 22 | // regions := []string{"us-east-1", "us-east-2", "us-west-1", "us-west-2"} 23 | 24 | // skill := []*pb.DoubleRangeFilter{ 25 | // {DoubleArg: "skill", Min: 0, Max: 10}, 26 | // {DoubleArg: "skill", Min: 10, Max: 100}, 27 | // {DoubleArg: "skill", Min: 100, Max: 1000}, 28 | // } 29 | 30 | latency := []*pb.DoubleRangeFilter{} 31 | 32 | for _, region := range regions { 33 | for _, rang := range ranges { 34 | lower, _ := strconv.ParseFloat(strings.Split(rang, "-")[0], 64) 35 | upper, _ := strconv.ParseFloat(strings.Split(rang, "-")[1], 64) 36 | latency = append(latency, &pb.DoubleRangeFilter{DoubleArg: "latency-" + region, Min: lower, Max: upper}) 37 | fmt.Printf("Region: %s Min: %f Max: %f\n", region, lower, upper) 38 | } 39 | } 40 | for _, regionLatency := range latency { 41 | profile := &pb.MatchProfile{ 42 | Name: fmt.Sprintf("profile_%v", regionLatency), 43 | Pools: []*pb.Pool{ 44 | { 45 | Name: "pool_mode_" + fmt.Sprintf("%v", regionLatency), 46 | TagPresentFilters: []*pb.TagPresentFilter{ 47 | {Tag: "mode.session"}, 48 | }, 49 | StringEqualsFilters: []*pb.StringEqualsFilter{ 50 | // Possible extra string criteria 51 | // {StringArg: "continent", Value: continent}, 52 | // {StringArg: "region", Value: region}, 53 | }, 54 | DoubleRangeFilters: []*pb.DoubleRangeFilter{ 55 | regionLatency, 56 | // Possible extra numerical criteria 57 | // DoubleRangeFilterFromSlice(skill), 58 | }, 59 | }, 60 | }, 61 | } 62 | 63 | profiles = append(profiles, profile) 64 | } 65 | return profiles 66 | } 67 | 68 | func TagFromStringSlice(tags []string) string { 69 | randomIndex := rand.Intn(len(tags)) 70 | 71 | return tags[randomIndex] 72 | } 73 | 74 | func DoubleRangeFilterFromSlice(tags []*pb.DoubleRangeFilter) *pb.DoubleRangeFilter { 75 | randomIndex := rand.Intn(len(tags)) 76 | 77 | return tags[randomIndex] 78 | } 79 | -------------------------------------------------------------------------------- /integration/matchfunction/Dockerfile: -------------------------------------------------------------------------------- 1 | ## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | ## SPDX-License-Identifier: MIT-0 3 | 4 | 5 | FROM public.ecr.aws/docker/library/golang:1.21.4-alpine3.18 AS builder 6 | RUN apk add --no-cache git 7 | WORKDIR /app 8 | 9 | # Set Go Proxy env variables 10 | ENV GOPROXY=https://goproxy.io,direct 11 | # Leverage caching by copying go.mod and go.sum first 12 | COPY go.mod go.sum ./ 13 | RUN go mod download 14 | 15 | # Copy the remaining files and build 16 | COPY . . 17 | RUN go build -o matchfunction 18 | 19 | # Final minimal image 20 | FROM public.ecr.aws/docker/library/alpine:3.18 21 | WORKDIR /app 22 | 23 | # Copy only the binary and necessary files 24 | COPY --from=builder /app/matchfunction /app/matchfunction 25 | COPY *.cert *.key ./ 26 | 27 | USER 999 28 | HEALTHCHECK CMD ["true"] 29 | 30 | CMD ["./matchfunction"] 31 | -------------------------------------------------------------------------------- /integration/matchfunction/README.md: -------------------------------------------------------------------------------- 1 | ## Name 2 | Guidance for Game Server Hosting on Amazon EKS with Agones and Open Match - Match Function 3 | # Description 4 | This module implements a matchmaking function for OpenMatch. 5 | ## Current State 6 | This MatchFunction receives MatchProfiles from the Director, based on latencies to 4 regions, get tickets from query service, sorts them by latency and creates matches with ajdacent latency players. 7 | ## Testing and monitoring 8 | The Match Function receives profile requests from the Open-Match backend provided by the [Director](../director/) and fetches the pools from the Open-Match query service. The execution flow can be inspected through the logs of the director, mmf (in the agones-openmatch namespace) and query (in the open-match namespace) pods. 9 | -------------------------------------------------------------------------------- /integration/matchfunction/go.mod: -------------------------------------------------------------------------------- 1 | module agones-openmatch/matchfunction 2 | 3 | go 1.21 4 | toolchain go1.24.1 5 | 6 | require ( 7 | github.com/pkg/errors v0.9.1 8 | google.golang.org/grpc v1.57.1 9 | google.golang.org/protobuf v1.33.0 10 | open-match.dev/open-match v1.8.0 11 | ) 12 | 13 | require ( 14 | github.com/golang/protobuf v1.5.3 // indirect 15 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.17.1 // indirect 16 | golang.org/x/net v0.38.0 // indirect 17 | golang.org/x/sys v0.31.0 // indirect 18 | golang.org/x/text v0.23.0 // indirect 19 | google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d // indirect 20 | google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d // indirect 21 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect 22 | ) 23 | -------------------------------------------------------------------------------- /integration/matchfunction/go.sum: -------------------------------------------------------------------------------- 1 | github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= 2 | github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= 3 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 4 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= 5 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 6 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 7 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 8 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 9 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.17.1 h1:LSsiG61v9IzzxMkqEr6nrix4miJI62xlRjwT7BYD2SM= 10 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.17.1/go.mod h1:Hbb13e3/WtqQ8U5hLGkek9gJvBLasHuPFI0UEGfnQ10= 11 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 12 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 13 | golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= 14 | golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= 15 | golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= 16 | golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 17 | golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= 18 | golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= 19 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 20 | google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d h1:VBu5YqKPv6XiJ199exd8Br+Aetz+o08F+PLMnwJQHAY= 21 | google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= 22 | google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d h1:DoPTO70H+bcDXcd39vOqb2viZxgqeBeSGtZ55yZU4/Q= 23 | google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= 24 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4= 25 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= 26 | google.golang.org/grpc v1.57.1 h1:upNTNqv0ES+2ZOOqACwVtS3Il8M12/+Hz41RCPzAjQg= 27 | google.golang.org/grpc v1.57.1/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= 28 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 29 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 30 | google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= 31 | google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 32 | open-match.dev/open-match v1.8.0 h1:gk3nXv2PMA0FsvoeXt/YnGaw+RbPs4kdKb7PhfFDdZI= 33 | open-match.dev/open-match v1.8.0/go.mod h1:O0c6efyO6rrSSvHBiwFq8RHD6g8PbNDUcVVS3JHgB5k= 34 | -------------------------------------------------------------------------------- /integration/matchfunction/main.go: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | package main 5 | 6 | import ( 7 | "flag" 8 | "fmt" 9 | "strconv" 10 | ) 11 | 12 | func main() { 13 | var queryServiceAddr string 14 | var queryServicePort, serverPort, players int 15 | flag.StringVar(&queryServiceAddr, "queryServiceAddr", "open-match-query.open-match.svc.cluster.local", "Open Match Query Service Address") 16 | flag.IntVar(&queryServicePort, "queryServicePort", 50503, "Open Match Query Service Port") 17 | flag.IntVar(&serverPort, "serverPort", 50502, "Matchmaking Function Service Port") 18 | flag.IntVar(&players, "players", 4, "Number of players per match") 19 | flag.Usage = func() { 20 | fmt.Printf("Usage: \n") 21 | fmt.Printf("players -queryServiceAddr addr -queryServicePort PortNumber -serverPort PortNumber -players NumPlayers\n") 22 | flag.PrintDefaults() 23 | } 24 | flag.Parse() 25 | TicketsPerPoolPerMatch = players 26 | Start(queryServiceAddr+":"+strconv.Itoa(queryServicePort), serverPort) 27 | } 28 | -------------------------------------------------------------------------------- /integration/matchfunction/matchfunction.go: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | "log" 9 | "regexp" 10 | "sort" 11 | "time" 12 | 13 | "google.golang.org/protobuf/types/known/anypb" 14 | "open-match.dev/open-match/pkg/matchfunction" 15 | "open-match.dev/open-match/pkg/pb" 16 | ) 17 | 18 | const ( 19 | matchName = "basic-matchfunction" 20 | ) 21 | 22 | var TicketsPerPoolPerMatch int 23 | 24 | func (s *MatchFunctionService) Run(req *pb.RunRequest, stream pb.MatchFunction_RunServer) error { 25 | poolTickets, err := matchfunction.QueryPools(stream.Context(), s.queryServiceClient, req.GetProfile().GetPools()) 26 | if err != nil { 27 | log.Printf("Failed to query tickets for the given pools, got %s", err.Error()) 28 | return err 29 | } 30 | 31 | proposals, err := makeMatches(req.GetProfile(), poolTickets) 32 | if err != nil { 33 | log.Printf("Failed to generate matches, got %s", err.Error()) 34 | return err 35 | } 36 | if len(proposals) > 0 { 37 | log.Printf("Generating proposals for function %v", req.GetProfile().GetName()) 38 | log.Printf("Streaming %d proposals to Open Match", len(proposals)) 39 | 40 | for _, proposal := range proposals { 41 | if err := stream.Send(&pb.RunResponse{Proposal: proposal}); err != nil { 42 | log.Printf("Failed to stream proposals to Open Match, got %s", err.Error()) 43 | return err 44 | } 45 | } 46 | } 47 | 48 | return nil 49 | } 50 | 51 | func makeMatches(p *pb.MatchProfile, poolTickets map[string][]*pb.Ticket) ([]*pb.Match, error) { 52 | var matches []*pb.Match 53 | count := 0 54 | unsortedMatchTickets := []*pb.Ticket{} 55 | for { 56 | insufficientTickets := false 57 | for pool, tickets := range poolTickets { 58 | if len(tickets) < TicketsPerPoolPerMatch { 59 | insufficientTickets = true 60 | break 61 | } 62 | 63 | unsortedMatchTickets = append(unsortedMatchTickets, tickets[0:TicketsPerPoolPerMatch]...) 64 | poolTickets[pool] = tickets[TicketsPerPoolPerMatch:] 65 | } 66 | 67 | if insufficientTickets { 68 | break 69 | } 70 | 71 | count++ 72 | } 73 | totalLatency := 0.0 74 | // Regular expression for AWS regions 75 | regionPattern := `(us(-gov)?|af|ap|ca|cn|eu|il|me|sa)-(central|(north|south)?(east|west)?)-\d` 76 | regexpPattern := regexp.MustCompile(regionPattern) 77 | if len(unsortedMatchTickets) > 0 { 78 | sort.Slice(unsortedMatchTickets, func(i, j int) bool { 79 | return unsortedMatchTickets[i].SearchFields.DoubleArgs["latency-"+unsortedMatchTickets[i].SearchFields.StringArgs["region"]] < unsortedMatchTickets[j].SearchFields.DoubleArgs["latency-"+unsortedMatchTickets[j].SearchFields.StringArgs["region"]] 80 | }) 81 | for matchIndex := 0; matchIndex < count; matchIndex++ { 82 | matchTickets := []*pb.Ticket{} 83 | for ticketIndex := 0; ticketIndex < TicketsPerPoolPerMatch; ticketIndex++ { 84 | log.Printf("MatchProfile name: %s: ", p.GetName()) 85 | currentTicket := unsortedMatchTickets[TicketsPerPoolPerMatch*matchIndex+ticketIndex] 86 | region := regexpPattern.FindString(p.GetName()) 87 | totalLatency = totalLatency + currentTicket.SearchFields.DoubleArgs["latency-"+region] 88 | matchTickets = append(matchTickets, currentTicket) 89 | } 90 | 91 | matchScore := 1000 / (totalLatency / float64(TicketsPerPoolPerMatch)) 92 | evaluationInput, err := anypb.New(&pb.DefaultEvaluationCriteria{ 93 | Score: matchScore, 94 | }) 95 | 96 | if err != nil { 97 | log.Printf("Failed to marshal DefaultEvaluationCriteria, got %v.", err) 98 | return nil, fmt.Errorf("Failed to marshal DefaultEvaluationCriteria, got %w", err) 99 | } 100 | 101 | matchId := fmt.Sprintf("profile-%v-time-%v-%v", p.GetName(), time.Now().Format("2006-01-02T15:04:05.00"), matchIndex) 102 | log.Printf("MatchId: %s: ", matchId) 103 | matches = append(matches, &pb.Match{ 104 | MatchId: matchId, 105 | MatchProfile: p.GetName(), 106 | MatchFunction: matchName, 107 | Tickets: matchTickets, 108 | Extensions: map[string]*anypb.Any{ 109 | "evaluation_input": evaluationInput, 110 | }, 111 | }) 112 | 113 | } 114 | } 115 | 116 | return matches, nil 117 | 118 | } 119 | -------------------------------------------------------------------------------- /integration/matchfunction/matchfunction.yaml: -------------------------------------------------------------------------------- 1 | ## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | ## SPDX-License-Identifier: MIT-0 3 | 4 | apiVersion: apps/v1 5 | kind: Deployment 6 | metadata: 7 | labels: 8 | app: agones-openmatch-mmf 9 | name: agones-openmatch-mmf 10 | spec: 11 | replicas: 1 12 | selector: 13 | matchLabels: 14 | app: agones-openmatch-mmf 15 | strategy: 16 | rollingUpdate: 17 | maxSurge: 25% 18 | maxUnavailable: 25% 19 | type: RollingUpdate 20 | template: 21 | metadata: 22 | labels: 23 | app: agones-openmatch-mmf 24 | spec: 25 | containers: 26 | - args: 27 | - /app/matchfunction 28 | - -queryServiceAddr 29 | - $(OPENMATCH_QUERY_SERVICE_ADDR) 30 | - -queryServicePort 31 | - $(OPENMATCH_QUERY_SERVICE_PORT) 32 | - -serverPort 33 | - $(OPENMATCH_MATCH_FUNCTION_PORT) 34 | - -players 35 | - $(NUM_PLAYERS) 36 | env: 37 | - name: OPENMATCH_MATCH_FUNCTION_PORT 38 | value: "50502" 39 | - name: OPENMATCH_QUERY_SERVICE_ADDR 40 | value: open-match-query.open-match.svc.cluster.local 41 | - name: OPENMATCH_QUERY_SERVICE_PORT 42 | value: "50503" 43 | - name: NUM_PLAYERS 44 | value: "4" 45 | image: ${REGISTRY}/agones-openmatch-mmf:latest 46 | imagePullPolicy: Always 47 | securityContext: 48 | allowPrivilegeEscalation: false 49 | name: mmf 50 | ports: 51 | - name: grpc 52 | containerPort: 50502 53 | protocol: TCP 54 | resources: 55 | limits: 56 | cpu: "1" 57 | memory: 100Mi 58 | requests: 59 | cpu: 100m 60 | memory: 50Mi 61 | terminationMessagePath: /dev/termination-log 62 | terminationMessagePolicy: File 63 | volumeMounts: 64 | - mountPath: /app/openmatch-tls 65 | name: openmatch-tls-volume 66 | dnsPolicy: ClusterFirst 67 | restartPolicy: Always 68 | schedulerName: default-scheduler 69 | securityContext: {} 70 | terminationGracePeriodSeconds: 30 71 | affinity: 72 | nodeAffinity: 73 | requiredDuringSchedulingIgnoredDuringExecution: 74 | nodeSelectorTerms: 75 | - matchExpressions: 76 | - key: kubernetes.io/arch 77 | operator: In 78 | values: 79 | - amd64 80 | - key: openmatch 81 | operator: In 82 | values: 83 | - customization 84 | volumes: 85 | - secret: 86 | defaultMode: 420 87 | secretName: open-match-tls-certmanager 88 | name: openmatch-tls-volume 89 | --- 90 | kind: Service 91 | apiVersion: v1 92 | metadata: 93 | name: agones-openmatch-mmf 94 | labels: 95 | app: agones-openmatch-mmf 96 | spec: 97 | selector: 98 | app: agones-openmatch-mmf 99 | type: ClusterIP 100 | ports: 101 | - name: grpc 102 | protocol: TCP 103 | port: 50502 104 | 105 | -------------------------------------------------------------------------------- /integration/matchfunction/server.go: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | package main 5 | 6 | import ( 7 | "crypto/tls" 8 | "crypto/x509" 9 | "fmt" 10 | "log" 11 | "net" 12 | "os" 13 | 14 | "github.com/pkg/errors" 15 | "google.golang.org/grpc" 16 | "google.golang.org/grpc/credentials" 17 | "open-match.dev/open-match/pkg/pb" 18 | ) 19 | 20 | type MatchFunctionService struct { 21 | grpc *grpc.Server 22 | queryServiceClient pb.QueryServiceClient 23 | port int 24 | } 25 | 26 | func Start(queryServiceAddr string, serverPort int) { 27 | omCert, err := os.ReadFile("./openmatch-tls/tls.crt") 28 | if err != nil { 29 | panic(err) 30 | } 31 | omKey, err := os.ReadFile("./openmatch-tls/tls.key") 32 | if err != nil { 33 | panic(err) 34 | } 35 | omCacert, err := os.ReadFile("./openmatch-tls/ca.crt") 36 | if err != nil { 37 | panic(err) 38 | } 39 | omDialOpts, err := createRemoteClusterDialOption(omCert, omKey, omCacert) 40 | if err != nil { 41 | panic(err) 42 | } 43 | conn, err := grpc.Dial(queryServiceAddr, omDialOpts) 44 | if err != nil { 45 | log.Fatalf("Failed to connect to Open Match, got %s", err.Error()) 46 | } 47 | defer conn.Close() 48 | 49 | mmfService := MatchFunctionService{ 50 | queryServiceClient: pb.NewQueryServiceClient(conn), 51 | } 52 | 53 | creds, err := credentials.NewServerTLSFromFile("./openmatch-tls/tls.crt", "./openmatch-tls/tls.key") 54 | if err != nil { 55 | log.Fatalf("Failed to setup TLS with local files, error: %s", err) 56 | } 57 | var opts []grpc.ServerOption = []grpc.ServerOption{grpc.Creds(creds)} 58 | server := grpc.NewServer(opts...) 59 | pb.RegisterMatchFunctionServer(server, &mmfService) 60 | ln, err := net.Listen("tcp", fmt.Sprintf(":%d", serverPort)) 61 | if err != nil { 62 | log.Fatalf("TCP net listener initialization failed for port %v, got %s", serverPort, err.Error()) 63 | } 64 | 65 | log.Printf("TCP net listener initialized for port %v", serverPort) 66 | err = server.Serve(ln) 67 | if err != nil { 68 | log.Fatalf("gRPC serve failed, got %s", err.Error()) 69 | } 70 | } 71 | 72 | func createRemoteClusterDialOption(clientCert, clientKey, caCert []byte) (grpc.DialOption, error) { 73 | cert, err := tls.X509KeyPair(clientCert, clientKey) 74 | if err != nil { 75 | return nil, err 76 | } 77 | 78 | tlsConfig := &tls.Config{MinVersion: tls.VersionTLS13, Certificates: []tls.Certificate{cert}} 79 | if len(caCert) != 0 { 80 | tlsConfig.RootCAs = x509.NewCertPool() 81 | if !tlsConfig.RootCAs.AppendCertsFromPEM(caCert) { 82 | return nil, errors.New("only PEM format is accepted for server CA") 83 | } 84 | } 85 | 86 | return grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)), nil 87 | } 88 | -------------------------------------------------------------------------------- /integration/ncat-server/Dockerfile: -------------------------------------------------------------------------------- 1 | ## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | ## SPDX-License-Identifier: MIT-0 3 | 4 | FROM public.ecr.aws/docker/library/golang:1.21.4-alpine3.18 AS go 5 | RUN apk add git 6 | WORKDIR /app 7 | ENV GO111MODULE=on 8 | ENV GOPROXY=direct,https://proxy.golang.org 9 | 10 | COPY go.mod . 11 | COPY go.sum . 12 | RUN go mod download -x 13 | COPY *.go . 14 | RUN go build -o ncat-server . 15 | 16 | 17 | FROM public.ecr.aws/docker/library/alpine:3.18 18 | RUN apk add --no-cache --update --verbose nmap-ncat 19 | WORKDIR /app 20 | COPY --from=go /app/ncat-server /app/ncat-server 21 | RUN chmod +x /app/ncat-server 22 | USER 999 23 | HEALTHCHECK CMD ["true"] 24 | ENTRYPOINT ["./ncat-server"] -------------------------------------------------------------------------------- /integration/ncat-server/go.mod: -------------------------------------------------------------------------------- 1 | module supertuxkart 2 | 3 | go 1.21 4 | toolchain go1.24.1 5 | 6 | require agones.dev/agones v1.36.0 7 | 8 | require ( 9 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 10 | github.com/golang/protobuf v1.5.3 // indirect 11 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.17.1 // indirect 12 | github.com/pkg/errors v0.9.1 // indirect 13 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect 14 | github.com/stretchr/testify v1.8.4 // indirect 15 | golang.org/x/net v0.38.0 // indirect 16 | golang.org/x/sys v0.31.0 // indirect 17 | golang.org/x/text v0.23.0 // indirect 18 | google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d // indirect 19 | google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d // indirect 20 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect 21 | google.golang.org/grpc v1.57.1 // indirect 22 | google.golang.org/protobuf v1.33.0 // indirect 23 | ) 24 | -------------------------------------------------------------------------------- /integration/ncat-server/go.sum: -------------------------------------------------------------------------------- 1 | agones.dev/agones v1.36.0 h1:R28n+0bo0gbHflAPe4Fbd65Ui/y7StIoTppQiueRPh4= 2 | agones.dev/agones v1.36.0/go.mod h1:nSjtecthytiwgxu2GSDTPHXY6nyZB3cyBVSUBO6GHqo= 3 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= 4 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= 6 | github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= 7 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 8 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= 9 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 10 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 11 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 12 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 13 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.17.1 h1:LSsiG61v9IzzxMkqEr6nrix4miJI62xlRjwT7BYD2SM= 14 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.17.1/go.mod h1:Hbb13e3/WtqQ8U5hLGkek9gJvBLasHuPFI0UEGfnQ10= 15 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 16 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 17 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= 18 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 19 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 20 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 21 | golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= 22 | golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= 23 | golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= 24 | golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 25 | golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= 26 | golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= 27 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 28 | google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d h1:VBu5YqKPv6XiJ199exd8Br+Aetz+o08F+PLMnwJQHAY= 29 | google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= 30 | google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d h1:DoPTO70H+bcDXcd39vOqb2viZxgqeBeSGtZ55yZU4/Q= 31 | google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= 32 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4= 33 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= 34 | google.golang.org/grpc v1.57.1 h1:upNTNqv0ES+2ZOOqACwVtS3Il8M12/+Hz41RCPzAjQg= 35 | google.golang.org/grpc v1.57.1/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= 36 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 37 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 38 | google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= 39 | google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 40 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 41 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 42 | -------------------------------------------------------------------------------- /integration/ncat-server/main.go: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | package main 5 | 6 | import ( 7 | "bufio" 8 | "flag" 9 | "fmt" 10 | "log" 11 | "os" 12 | "os/exec" 13 | "regexp" 14 | "time" 15 | 16 | sdk "agones.dev/agones/sdks/go" 17 | ) 18 | 19 | // main intercepts the output of ncat and uses it 20 | // to determine if the game server is ready or not. 21 | func main() { 22 | log.SetPrefix("[wrapper] ") 23 | port := flag.String("p", "", "ncat listening port") 24 | 25 | // Since player tracking is not on by default, it is behind this flag. 26 | // If it is off, still log messages about players, but don't actually call the player tracking functions. 27 | enablePlayerTracking := flag.Bool("player-tracking", false, "If true, player tracking will be enabled.") 28 | flag.Parse() 29 | 30 | log.Println("Connecting to Agones with the SDK") 31 | s, err := sdk.NewSDK() 32 | if err != nil { 33 | log.Fatalf("could not connect to SDK: %v", err) 34 | } 35 | 36 | if *enablePlayerTracking { 37 | if err = s.Alpha().SetPlayerCapacity(8); err != nil { 38 | log.Fatalf("could not set play count: %v", err) 39 | } 40 | } 41 | 42 | log.Println("Starting health checking") 43 | go doHealth(s) 44 | 45 | log.Println("Starting wrapper for ncat") 46 | log.Printf("ncat server running on port %s \n", *port) 47 | 48 | cmd := exec.Command("/usr/bin/ncat", "--chat", "--listen", "-p "+*port, "-vvv") // #nosec 49 | cmdReader, err := cmd.StderrPipe() 50 | if err != nil { 51 | fmt.Fprintln(os.Stderr, "Error creating StdoutPipe for Cmd", err) 52 | return 53 | } 54 | 55 | playersConnected := false 56 | scanner := bufio.NewScanner(cmdReader) 57 | go func() { 58 | for scanner.Scan() { 59 | 60 | str := scanner.Text() 61 | fmt.Println(str) 62 | 63 | action, player := handleLogLine(str) 64 | switch action { 65 | case "READY": 66 | log.Print("READY") 67 | if err := s.Ready(); err != nil { 68 | log.Fatal("failed to mark server ready") 69 | } 70 | case "PLAYERJOIN": 71 | playersConnected = true 72 | if player == nil { 73 | log.Print("could not determine player") 74 | break 75 | } 76 | if *enablePlayerTracking { 77 | result, err := s.Alpha().PlayerConnect(*player) 78 | if err != nil { 79 | log.Print(err) 80 | } else { 81 | log.Print(result) 82 | } 83 | } 84 | case "PLAYERLEAVE": 85 | if player == nil { 86 | log.Print("could not determine player") 87 | break 88 | } 89 | if *enablePlayerTracking { 90 | result, err := s.Alpha().PlayerDisconnect(*player) 91 | if err != nil { 92 | log.Print(err) 93 | } else { 94 | log.Print(result) 95 | } 96 | } 97 | case "SHUTDOWN": 98 | if playersConnected { 99 | if err := s.Shutdown(); err != nil { 100 | log.Fatal(err) 101 | } 102 | log.Print("server has no more players. shutting down") 103 | os.Exit(0) 104 | } 105 | } 106 | } 107 | log.Fatal("tail ended") 108 | }() 109 | 110 | if err := cmd.Start(); err != nil { 111 | log.Fatalf("error starting cmd: %v", err) 112 | } 113 | 114 | err = cmd.Wait() 115 | if err != nil { 116 | fmt.Fprintln(os.Stderr, "Error waiting for Cmd", err) 117 | return 118 | } 119 | } 120 | 121 | func doHealth(sdk *sdk.SDK) { 122 | tick := time.Tick(2 * time.Second) 123 | for { 124 | if err := sdk.Health(); err != nil { 125 | log.Fatalf("could not send health ping: %v", err) 126 | } 127 | <-tick 128 | } 129 | } 130 | 131 | func handleLogLine(line string) (string, *string) { 132 | playerJoin := regexp.MustCompile(`on file descriptor \b(\w+)\.$`) 133 | playerLeave := regexp.MustCompile(`Closing connection`) 134 | noMorePlayers := regexp.MustCompile(`Broker connection count is 0`) 135 | serverStart := regexp.MustCompile(`Version`) 136 | 137 | if serverStart.MatchString(line) { 138 | log.Print("server ready") 139 | return "READY", nil 140 | } 141 | 142 | if playerJoin.MatchString(line) { 143 | matches := playerJoin.FindSubmatch([]byte(line)) 144 | player := string(matches[1]) 145 | log.Printf("Player %s joined\n", player) 146 | log.Printf("Player joined\n") 147 | return "PLAYERJOIN", &player 148 | } 149 | if playerLeave.MatchString(line) { 150 | log.Printf("Player disconnected") 151 | return "PLAYERLEAVE", nil 152 | } 153 | 154 | if noMorePlayers.MatchString(line) { 155 | return "SHUTDOWN", nil 156 | } 157 | return "", nil 158 | } 159 | -------------------------------------------------------------------------------- /manifests/agones-allocator-tls.yaml: -------------------------------------------------------------------------------- 1 | ## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | ## SPDX-License-Identifier: MIT-0 3 | apiVersion: cert-manager.io/v1 4 | kind: Certificate 5 | metadata: 6 | name: allocator-tls 7 | namespace: agones-system 8 | spec: 9 | commonName: agones-allocator 10 | dnsNames: 11 | - ${EXTERNAL_IP} 12 | - agones-allocator.agones-system.svc.cluster.local 13 | secretName: allocator-tls 14 | issuerRef: 15 | name: selfsigned 16 | kind: ClusterIssuer -------------------------------------------------------------------------------- /manifests/agones-controller-cert.yaml: -------------------------------------------------------------------------------- 1 | ## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | ## SPDX-License-Identifier: MIT-0 3 | apiVersion: cert-manager.io/v1 4 | kind: Certificate 5 | metadata: 6 | name: agones-cert 7 | namespace: agones-system 8 | spec: 9 | dnsNames: 10 | - agones-controller-service.agones-system.svc.cluster.local 11 | - agones-controller-service.agones-system.svc 12 | secretName: agones-cert 13 | issuerRef: 14 | name: selfsigned 15 | kind: ClusterIssuer 16 | 17 | -------------------------------------------------------------------------------- /manifests/cluster-issuer.yaml: -------------------------------------------------------------------------------- 1 | ## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | ## SPDX-License-Identifier: MIT-0 3 | apiVersion: cert-manager.io/v1 4 | kind: ClusterIssuer 5 | metadata: 6 | name: selfsigned 7 | spec: 8 | selfSigned: {} 9 | -------------------------------------------------------------------------------- /manifests/fleets/ncat/ncat-fleet1.yaml: -------------------------------------------------------------------------------- 1 | ## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | ## SPDX-License-Identifier: MIT-0 3 | 4 | apiVersion: "agones.dev/v1" 5 | kind: Fleet 6 | metadata: 7 | name: ncat-pool1 8 | spec: 9 | replicas: 2 10 | strategy: 11 | type: Recreate 12 | template: 13 | metadata: 14 | labels: 15 | pool: ONE 16 | region: ${REGION} 17 | spec: 18 | players: 19 | ports: 20 | - name: default 21 | protocol: TCP 22 | containerPort: 12345 23 | health: 24 | initialDelaySeconds: 30 25 | periodSeconds: 60 26 | template: 27 | spec: 28 | affinity: 29 | nodeAffinity: 30 | requiredDuringSchedulingIgnoredDuringExecution: 31 | nodeSelectorTerms: 32 | - matchExpressions: 33 | - key: agones.dev/agones-gameserver 34 | operator: Exists 35 | containers: 36 | - name: ncat 37 | args: 38 | - -p 39 | - "12345" 40 | image: ${REGISTRY}/agones-openmatch-ncat-server 41 | resources: 42 | requests: 43 | memory: "64Mi" 44 | cpu: "20m" 45 | limits: 46 | memory: "64Mi" 47 | cpu: "20m" 48 | securityContext: 49 | allowPrivilegeEscalation: false 50 | runAsNonRoot: true 51 | 52 | -------------------------------------------------------------------------------- /manifests/fleets/ncat/ncat-fleet2.yaml: -------------------------------------------------------------------------------- 1 | ## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | ## SPDX-License-Identifier: MIT-0 3 | 4 | apiVersion: "agones.dev/v1" 5 | kind: Fleet 6 | metadata: 7 | name: ncat-pool2 8 | spec: 9 | replicas: 2 10 | strategy: 11 | type: Recreate 12 | template: 13 | metadata: 14 | labels: 15 | pool: TWO 16 | region: ${REGION} 17 | spec: 18 | players: 19 | ports: 20 | - name: default 21 | protocol: TCP 22 | containerPort: 12345 23 | health: 24 | initialDelaySeconds: 30 25 | periodSeconds: 60 26 | template: 27 | spec: 28 | affinity: 29 | nodeAffinity: 30 | requiredDuringSchedulingIgnoredDuringExecution: 31 | nodeSelectorTerms: 32 | - matchExpressions: 33 | - key: agones.dev/agones-gameserver 34 | operator: Exists 35 | containers: 36 | - name: ncat 37 | args: 38 | - -p 39 | - "12345" 40 | image: ${REGISTRY}/agones-openmatch-ncat-server 41 | resources: 42 | requests: 43 | memory: "64Mi" 44 | cpu: "20m" 45 | limits: 46 | memory: "64Mi" 47 | cpu: "20m" 48 | securityContext: 49 | allowPrivilegeEscalation: false 50 | runAsNonRoot: true -------------------------------------------------------------------------------- /manifests/fleets/ncat/ncat-fleet3.yaml: -------------------------------------------------------------------------------- 1 | ## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | ## SPDX-License-Identifier: MIT-0 3 | 4 | apiVersion: "agones.dev/v1" 5 | kind: Fleet 6 | metadata: 7 | name: ncat-pool3 8 | spec: 9 | replicas: 2 10 | strategy: 11 | type: Recreate 12 | template: 13 | metadata: 14 | labels: 15 | pool: THREE 16 | region: ${REGION} 17 | spec: 18 | players: 19 | ports: 20 | - name: default 21 | protocol: TCP 22 | containerPort: 12345 23 | health: 24 | initialDelaySeconds: 30 25 | periodSeconds: 60 26 | template: 27 | spec: 28 | affinity: 29 | nodeAffinity: 30 | requiredDuringSchedulingIgnoredDuringExecution: 31 | nodeSelectorTerms: 32 | - matchExpressions: 33 | - key: agones.dev/agones-gameserver 34 | operator: Exists 35 | containers: 36 | - name: ncat 37 | args: 38 | - -p 39 | - "12345" 40 | image: ${REGISTRY}/agones-openmatch-ncat-server 41 | resources: 42 | requests: 43 | memory: "64Mi" 44 | cpu: "20m" 45 | limits: 46 | memory: "64Mi" 47 | cpu: "20m" 48 | securityContext: 49 | allowPrivilegeEscalation: false 50 | runAsNonRoot: true -------------------------------------------------------------------------------- /manifests/fleets/ncat/ncat-fleet4.yaml: -------------------------------------------------------------------------------- 1 | ## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | ## SPDX-License-Identifier: MIT-0 3 | 4 | apiVersion: "agones.dev/v1" 5 | kind: Fleet 6 | metadata: 7 | name: ncat-pool4 8 | spec: 9 | replicas: 2 10 | strategy: 11 | type: Recreate 12 | template: 13 | metadata: 14 | labels: 15 | pool: FOUR 16 | region: ${REGION} 17 | spec: 18 | players: 19 | ports: 20 | - name: default 21 | protocol: TCP 22 | containerPort: 12345 23 | health: 24 | initialDelaySeconds: 30 25 | periodSeconds: 60 26 | template: 27 | spec: 28 | affinity: 29 | nodeAffinity: 30 | requiredDuringSchedulingIgnoredDuringExecution: 31 | nodeSelectorTerms: 32 | - matchExpressions: 33 | - key: agones.dev/agones-gameserver 34 | operator: Exists 35 | containers: 36 | - name: ncat 37 | args: 38 | - -p 39 | - "12345" 40 | image: ${REGISTRY}/agones-openmatch-ncat-server 41 | resources: 42 | requests: 43 | memory: "64Mi" 44 | cpu: "20m" 45 | limits: 46 | memory: "64Mi" 47 | cpu: "20m" 48 | securityContext: 49 | allowPrivilegeEscalation: false 50 | runAsNonRoot: true -------------------------------------------------------------------------------- /manifests/fleets/stk/supertux-kart-fleet1.yaml: -------------------------------------------------------------------------------- 1 | ## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | ## SPDX-License-Identifier: MIT-0 3 | 4 | apiVersion: "agones.dev/v1" 5 | kind: Fleet 6 | metadata: 7 | name: supertuxkart 8 | spec: 9 | replicas: 2 10 | strategy: 11 | type: Recreate 12 | template: 13 | metadata: 14 | labels: 15 | mode: MAJOR_MODE_GRAND_PRIX 16 | minorGameMode: MINOR_MODE_NORMAL_RACE 17 | region: ${REGION} 18 | spec: 19 | players: 20 | # set this GameServer's initial player capacity to 10 21 | initialCapacity: 10 22 | ports: 23 | - name: default 24 | containerPort: 8080 25 | health: 26 | initialDelaySeconds: 30 27 | periodSeconds: 60 28 | template: 29 | spec: 30 | containers: 31 | - name: supertuxkart 32 | #image: gcr.io/agones-images/supertuxkart-example:0.4 33 | image: ${REGISTRY}/supertuxkart-server:latest 34 | resources: 35 | requests: 36 | memory: "64Mi" 37 | cpu: "20m" 38 | limits: 39 | memory: "64Mi" 40 | cpu: "20m" 41 | securityContext: 42 | allowPrivilegeEscalation: false 43 | runAsNonRoot: true 44 | affinity: 45 | nodeAffinity: 46 | requiredDuringSchedulingIgnoredDuringExecution: 47 | nodeSelectorTerms: 48 | - matchExpressions: 49 | - key: agones.dev/agones-gameserver 50 | operator: Exists 51 | -------------------------------------------------------------------------------- /manifests/fleets/stk/supertux-kart-fleet2.yaml: -------------------------------------------------------------------------------- 1 | ## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | ## SPDX-License-Identifier: MIT-0 3 | 4 | apiVersion: "agones.dev/v1" 5 | kind: Fleet 6 | metadata: 7 | name: supertuxkart-pool2 8 | spec: 9 | replicas: 2 10 | strategy: 11 | type: Recreate 12 | template: 13 | metadata: 14 | labels: 15 | mode: MAJOR_MODE_GRAND_PRIX 16 | minorGameMode: MINOR_MODE_TIME_TRIAL 17 | region: ${REGION} 18 | spec: 19 | players: 20 | # set this GameServer's initial player capacity to 10 21 | initialCapacity: 10 22 | ports: 23 | - name: default 24 | containerPort: 8080 25 | health: 26 | initialDelaySeconds: 30 27 | periodSeconds: 60 28 | template: 29 | spec: 30 | containers: 31 | - name: supertuxkart 32 | # image: gcr.io/agones-images/supertuxkart-example:0.4 33 | image: ${REGISTRY}/supertuxkart-server:latest 34 | resources: 35 | requests: 36 | memory: "64Mi" 37 | cpu: "20m" 38 | limits: 39 | memory: "64Mi" 40 | cpu: "20m" 41 | securityContext: 42 | allowPrivilegeEscalation: false 43 | runAsNonRoot: true 44 | affinity: 45 | nodeAffinity: 46 | requiredDuringSchedulingIgnoredDuringExecution: 47 | nodeSelectorTerms: 48 | - matchExpressions: 49 | - key: agones.dev/agones-gameserver 50 | operator: Exists 51 | -------------------------------------------------------------------------------- /manifests/fleets/stk/supertux-kart-fleet3.yaml: -------------------------------------------------------------------------------- 1 | ## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | ## SPDX-License-Identifier: MIT-0 3 | 4 | apiVersion: "agones.dev/v1" 5 | kind: Fleet 6 | metadata: 7 | name: supertuxkart-pool3 8 | spec: 9 | replicas: 2 10 | strategy: 11 | type: Recreate 12 | template: 13 | metadata: 14 | labels: 15 | mode: MAJOR_MODE_SINGLE 16 | minorGameMode: MINOR_MODE_FOLLOW_LEADER 17 | region: ${REGION} 18 | spec: 19 | players: 20 | # set this GameServer's initial player capacity to 10 21 | initialCapacity: 10 22 | ports: 23 | - name: default 24 | containerPort: 8080 25 | health: 26 | initialDelaySeconds: 30 27 | periodSeconds: 60 28 | template: 29 | spec: 30 | containers: 31 | - name: supertuxkart 32 | # image: gcr.io/agones-images/supertuxkart-example:0.4 33 | image: ${REGISTRY}/supertuxkart-server:latest 34 | resources: 35 | requests: 36 | memory: "64Mi" 37 | cpu: "20m" 38 | limits: 39 | memory: "64Mi" 40 | cpu: "20m" 41 | securityContext: 42 | allowPrivilegeEscalation: false 43 | runAsNonRoot: true 44 | affinity: 45 | nodeAffinity: 46 | requiredDuringSchedulingIgnoredDuringExecution: 47 | nodeSelectorTerms: 48 | - matchExpressions: 49 | - key: agones.dev/agones-gameserver 50 | operator: Exists 51 | -------------------------------------------------------------------------------- /manifests/fleets/stk/supertux-kart-fleet4.yaml: -------------------------------------------------------------------------------- 1 | ## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | ## SPDX-License-Identifier: MIT-0 3 | 4 | apiVersion: "agones.dev/v1" 5 | kind: Fleet 6 | metadata: 7 | name: supertuxkart-pool4 8 | spec: 9 | replicas: 2 10 | strategy: 11 | type: Recreate 12 | template: 13 | metadata: 14 | labels: 15 | mode: MAJOR_MODE_SINGLE 16 | minorGameMode: MINOR_MODE_SOCCER 17 | region: ${REGION} 18 | spec: 19 | players: 20 | # set this GameServer's initial player capacity to 10 21 | initialCapacity: 10 22 | ports: 23 | - name: default 24 | containerPort: 8080 25 | health: 26 | initialDelaySeconds: 30 27 | periodSeconds: 60 28 | template: 29 | spec: 30 | containers: 31 | - name: supertuxkart 32 | # image: gcr.io/agones-images/supertuxkart-example:0.4 33 | image: ${REGISTRY}/supertuxkart-server:latest 34 | resources: 35 | requests: 36 | memory: "64Mi" 37 | cpu: "20m" 38 | limits: 39 | memory: "64Mi" 40 | cpu: "20m" 41 | securityContext: 42 | allowPrivilegeEscalation: false 43 | runAsNonRoot: true 44 | affinity: 45 | nodeAffinity: 46 | requiredDuringSchedulingIgnoredDuringExecution: 47 | nodeSelectorTerms: 48 | - matchExpressions: 49 | - key: agones.dev/agones-gameserver 50 | operator: Exists -------------------------------------------------------------------------------- /manifests/multicluster-allocation-1-to-2.yaml: -------------------------------------------------------------------------------- 1 | 2 | ## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | ## SPDX-License-Identifier: MIT-0 4 | 5 | # Configures remote multi-cluster allocation from cluster-1 to cluster-2 6 | apiVersion: multicluster.agones.dev/v1 7 | kind: GameServerAllocationPolicy 8 | metadata: 9 | name: allocator-policy-to-cluster-2 10 | namespace: agones-system 11 | spec: 12 | connectionInfo: 13 | allocationEndpoints: 14 | - ${ALLOCATOR_IP_CLUSTER2} 15 | clusterName: "cluster-2" 16 | namespace: gameservers 17 | secretName: allocator-secret-to-cluster-2 18 | priority: 1 19 | weight: 100 -------------------------------------------------------------------------------- /manifests/multicluster-allocation-1.yaml: -------------------------------------------------------------------------------- 1 | 2 | ## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | ## SPDX-License-Identifier: MIT-0 4 | 5 | # Configures local multi-cluster allocation on cluster-1 6 | apiVersion: multicluster.agones.dev/v1 7 | kind: GameServerAllocationPolicy 8 | metadata: 9 | name: allocator-policy-to-local-cluster 10 | namespace: agones-system 11 | spec: 12 | connectionInfo: 13 | clusterName: "cluster-1" 14 | namespace: gameservers 15 | priority: 1 16 | weight: 100 -------------------------------------------------------------------------------- /manifests/multicluster-allocation-2-to-1.yaml: -------------------------------------------------------------------------------- 1 | ## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | ## SPDX-License-Identifier: MIT-0 3 | 4 | # Configures remote multi-cluster allocation from cluster-1 to cluster-2 5 | apiVersion: multicluster.agones.dev/v1 6 | kind: GameServerAllocationPolicy 7 | metadata: 8 | name: allocator-policy-to-cluster-1 9 | namespace: agones-system 10 | spec: 11 | connectionInfo: 12 | allocationEndpoints: 13 | - ${ALLOCATOR_IP_CLUSTER_1} 14 | clusterName: "cluster-1" 15 | namespace: gameservers 16 | secretName: allocator-secret-to-cluster-1 17 | priority: 1 18 | weight: 100 -------------------------------------------------------------------------------- /manifests/multicluster-allocation-2.yaml: -------------------------------------------------------------------------------- 1 | 2 | ## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | ## SPDX-License-Identifier: MIT-0 4 | 5 | # Configures local multi-cluster allocation on cluster-1 6 | apiVersion: multicluster.agones.dev/v1 7 | kind: GameServerAllocationPolicy 8 | metadata: 9 | name: allocator-policy-to-local-cluster 10 | namespace: agones-system 11 | spec: 12 | connectionInfo: 13 | clusterName: "cluster-2" 14 | namespace: gameservers 15 | priority: 1 16 | weight: 100 -------------------------------------------------------------------------------- /manifests/open-match-tls-certmanager.cert.yaml: -------------------------------------------------------------------------------- 1 | ## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | ## SPDX-License-Identifier: MIT-0 3 | 4 | apiVersion: cert-manager.io/v1 5 | kind: Certificate 6 | metadata: 7 | annotations: 8 | name: open-match-tls-certmanager 9 | namespace: open-match 10 | spec: 11 | commonName: open-match 12 | dnsNames: 13 | - open-match-backend 14 | - open-match-backend.open-match.svc.cluster.local 15 | - om-demo 16 | - om-demoevaluator 17 | - open-match-evaluator 18 | - om-demofunction 19 | - om-e2eevaluator 20 | - om-e2ematchfunction 21 | - open-match-frontend 22 | - open-match-query 23 | - open-match-query.open-match.svc.cluster.local 24 | - open-match-swaggerui 25 | - open-match-synchronizer 26 | - agones-openmatch-mmf.agones-openmatch.svc.cluster.local 27 | issuerRef: 28 | kind: ClusterIssuer 29 | name: selfsigned 30 | secretName: open-match-tls-certmanager 31 | -------------------------------------------------------------------------------- /respawn-multicluster.6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-game-server-hosting-using-agones-and-open-match-on-amazon-eks/19b9249d74a8c71e56c498362d5910033d59aa52/respawn-multicluster.6.png -------------------------------------------------------------------------------- /scripts/configure-agones-tls.sh: -------------------------------------------------------------------------------- 1 | ## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | ## SPDX-License-Identifier: MIT-0 3 | set -o xtrace 4 | echo "#####" 5 | CLUSTER_NAME=$1 6 | ROOT_PATH=$2 7 | kubectl config use-context $(kubectl config get-contexts -o=name | grep ${CLUSTER_NAME}) 8 | echo "- Verify that the Agones pods are running -" 9 | kubectl get pods -n agones-system -o wide 10 | export EXTERNAL_IP=$(kubectl get services agones-allocator -n agones-system -o jsonpath='{.status.loadBalancer.ingress[0].hostname}') 11 | echo "- Verify that the cert-manager pods are running -" 12 | kubectl get pods -n cert-manager -o wide 13 | echo "- Create a certificate for agones-allocator -" 14 | envsubst < ${ROOT_PATH}/manifests/agones-allocator-tls.yaml | kubectl apply -f - 15 | echo "- Wait and get the allocator-tls Secret -" 16 | while ! kubectl get secret allocator-tls -n agones-system; do echo "Waiting for allocator-tls secret."; sleep 5; done 17 | TLS_CA_VALUE=$(kubectl get secret allocator-tls -n agones-system -o jsonpath='{.data.ca\.crt}') 18 | echo "- Add ca.crt to the allocator-tls-ca Secret -" 19 | kubectl get secret allocator-tls-ca -o json -n agones-system | jq '.data["tls-ca.crt"]="'${TLS_CA_VALUE}'"' | kubectl apply -f - -------------------------------------------------------------------------------- /scripts/configure-multicluster-allocation.sh: -------------------------------------------------------------------------------- 1 | ## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | ## SPDX-License-Identifier: MIT-0 3 | set -o xtrace 4 | export CLUSTER1=$1 5 | export CLUSTER2=$2 6 | export ROOT_PATH=$3 7 | kubectl config use-context $(kubectl config get-contexts -o=name | grep ${CLUSTER2}) 8 | export ALLOCATOR_IP_CLUSTER2=$(kubectl get services agones-allocator -n agones-system -o jsonpath='{.status.loadBalancer.ingress[0].hostname}') 9 | kubectl config use-context $(kubectl config get-contexts -o=name | grep ${CLUSTER1}) 10 | export ALLOCATOR_IP_CLUSTER1=$(kubectl get services agones-allocator -n agones-system -o jsonpath='{.status.loadBalancer.ingress[0].hostname}') 11 | kubectl apply -f ${ROOT_PATH}/manifests/multicluster-allocation-1.yaml 12 | envsubst < ${ROOT_PATH}/manifests/multicluster-allocation-1-to-2.yaml | kubectl apply -f - 13 | # kubectl delete secret allocator-secret-to-cluster-2 -n agones-system 14 | kubectl create secret generic \ 15 | --from-file=tls.crt=${ROOT_PATH}/client_${CLUSTER2}.crt \ 16 | --from-file=tls.key=${ROOT_PATH}/client_${CLUSTER2}.key \ 17 | --from-file=ca.crt=${ROOT_PATH}/ca_${CLUSTER2}.crt \ 18 | allocator-secret-to-cluster-2 -n agones-system -------------------------------------------------------------------------------- /scripts/configure-open-match-ingress.sh: -------------------------------------------------------------------------------- 1 | ## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | ## SPDX-License-Identifier: MIT-0 3 | set -o xtrace 4 | echo "#####" 5 | CLUSTER_NAME=$1 6 | ROOT_PATH=$2 7 | kubectl config use-context $(kubectl config get-contexts -o=name | grep ${CLUSTER_NAME}) 8 | kubectl get pods -n open-match -o wide 9 | # Create Load Balancer 10 | kubectl expose deployment open-match-frontend -n open-match --type=LoadBalancer --name="${CLUSTER_NAME}-om-fe" 11 | # Add annotations to create the LB as an internet facing NLB (to be accessed by the clients and used with Global Accelerator) 12 | kubectl annotate svc ${CLUSTER_NAME}-om-fe service.beta.kubernetes.io/aws-load-balancer-scheme=internet-facing --overwrite=true -n open-match 13 | kubectl annotate svc ${CLUSTER_NAME}-om-fe service.beta.kubernetes.io/aws-load-balancer-type=nlb --overwrite=true -n open-match 14 | 15 | # Create a certificate for open-match 16 | kubectl apply -f ${ROOT_PATH}/manifests/open-match-tls-certmanager.cert.yaml 17 | 18 | # Modify the secrets open-match-tls-rootca and open-match-tls-server installed by helm with the values from open-match-tls-certmanager 19 | TLS_CA_VALUE=$(kubectl get secret open-match-tls-certmanager -n open-match -ojsonpath='{.data.ca\.crt}') 20 | TLS_CERT_VALUE=$(kubectl get secret open-match-tls-certmanager -n open-match -ojsonpath='{.data.tls\.crt}') 21 | TLS_KEY_VALUE=$(kubectl get secret open-match-tls-certmanager -n open-match -ojsonpath='{.data.tls\.key}') 22 | kubectl get secret open-match-tls-rootca -o json -n open-match | jq '.data["public.cert"]="'${TLS_CA_VALUE}'"' | kubectl apply -f - 23 | kubectl get secret open-match-tls-server -o json -n open-match | jq '.data["public.cert"]="'${TLS_CERT_VALUE}'"' | kubectl apply -f - 24 | kubectl get secret open-match-tls-server -o json -n open-match | jq '.data["private.key"]="'${TLS_KEY_VALUE}'"' | kubectl apply -f - 25 | 26 | # Restart open-match pods to use the new certificate 27 | kubectl delete pods -n open-match --all 28 | 29 | # Copy the open-match-tls-certmanager from open-match to agones-openmatch namespace 30 | kubectl get secret open-match-tls-certmanager -o json -n open-match | jq '.metadata.namespace="agones-openmatch"' | kubectl apply -f - 31 | -------------------------------------------------------------------------------- /scripts/deploy-director.sh: -------------------------------------------------------------------------------- 1 | ## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | ## SPDX-License-Identifier: MIT-0 3 | export NAMESPACE=agones-openmatch 4 | export CLUSTER_NAME=$1 5 | # We need both regions for the director deployment parameters 6 | export REGION1=$2 7 | export REGION2=$3 8 | export ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text) 9 | export REGISTRY=${ACCOUNT_ID}.dkr.ecr.${REGION1}.amazonaws.com 10 | 11 | kubectl config use-context $(kubectl config get-contexts -o=name | grep ${CLUSTER_NAME}) 12 | echo "- Create configmap -" 13 | # Create the configmap that will store the certs/keys used by the Open Match Director to access the 14 | # Agones Allocator Service (we use the files `client_*` and `ca_*` 15 | # with the certificates details created previously). `director` will communicate with Agones `allocator` 16 | # using the same way we did in our tests with `curl`. 17 | kubectl create configmap allocator-tls -n agones-openmatch \ 18 | --from-file=tls.crt=client_${CLUSTER_NAME}.crt \ 19 | --from-file=tls.key=client_${CLUSTER_NAME}.key \ 20 | --from-file=ca.crt=ca_${CLUSTER_NAME}.crt 21 | 22 | echo "- Login to ECR registry -" 23 | aws ecr get-login-password --region ${REGION1} | docker login --username AWS --password-stdin $REGISTRY 24 | echo "- Build director image -" 25 | docker buildx build --platform=linux/amd64 -t $REGISTRY/agones-openmatch-director integration/director 26 | echo "- Push image to register -" 27 | docker push $REGISTRY/agones-openmatch-director 28 | 29 | echo "- Deploy Open Match Director to cluster ${CLUSTER_NAME} -" 30 | envsubst < integration/director/director.yaml | kubectl apply --namespace ${NAMESPACE} -f - 31 | echo 32 | echo "- Display Open Match Director pod -" 33 | sleep 15 34 | kubectl get pods --namespace ${NAMESPACE} -l app=agones-openmatch-director -------------------------------------------------------------------------------- /scripts/deploy-mapping-configmap.sh: -------------------------------------------------------------------------------- 1 | ## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | ## SPDX-License-Identifier: MIT-0 3 | # set -o xtrace 4 | CLUSTER_NAME_1=$1 5 | ACCELERATOR_1=$2 6 | CLUSTER_NAME_2=$3 7 | ACCELERATOR_2=$4 8 | 9 | ACCELERATOR_1_DNS=$(aws globalaccelerator describe-custom-routing-accelerator --region us-west-2 --accelerator-arn $ACCELERATOR_1 --query Accelerator.DnsName --output text) 10 | ACCELERATOR_2_DNS=$(aws globalaccelerator describe-custom-routing-accelerator --region us-west-2 --accelerator-arn $ACCELERATOR_2 --query Accelerator.DnsName --output text) 11 | 12 | aws globalaccelerator list-custom-routing-port-mappings --region us-west-2 --accelerator-arn $ACCELERATOR_1 --query 'PortMappings[].[AcceleratorPort,DestinationSocketAddress.IpAddress,DestinationSocketAddress.Port]' | jq -c '.[] | {key: "\(.[1]):\(.[2] | tostring)", value: .[0]}' | jq -s 'from_entries' | gzip > mapping1.gz 13 | aws globalaccelerator list-custom-routing-port-mappings --region us-west-2 --accelerator-arn $ACCELERATOR_2 --query 'PortMappings[].[AcceleratorPort,DestinationSocketAddress.IpAddress,DestinationSocketAddress.Port]' | jq -c '.[] | {key: "\(.[1]):\(.[2] | tostring)", value: .[0]}' | jq -s 'from_entries' | gzip > mapping2.gz 14 | kubectl config use-context $(kubectl config get-contexts -o=name | grep ${CLUSTER_NAME_1}) 15 | kubectl delete configmap global-accelerator-mapping --namespace agones-openmatch 16 | kubectl create configmap global-accelerator-mapping --namespace agones-openmatch --from-file=mapping1.gz --from-file=mapping2.gz --from-literal=accelerator1="$ACCELERATOR_1_DNS" --from-literal=accelerator2="$ACCELERATOR_2_DNS" 17 | rm mapping1.gz mapping2.gz -------------------------------------------------------------------------------- /scripts/deploy-matchfunction.sh: -------------------------------------------------------------------------------- 1 | ## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | ## SPDX-License-Identifier: MIT-0 3 | export NAMESPACE=agones-openmatch 4 | export CLUSTER_NAME1=$1 5 | export REGION1=$2 6 | export ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text) 7 | export REGISTRY=${ACCOUNT_ID}.dkr.ecr.${REGION1}.amazonaws.com 8 | 9 | echo "- Login to ECR registry -" 10 | aws ecr get-login-password --region ${REGION1} | docker login --username AWS --password-stdin $REGISTRY 11 | echo "- Build matchfunction image -" 12 | docker buildx build --platform=linux/amd64 -t $REGISTRY/agones-openmatch-mmf integration/matchfunction 13 | echo "- Push image to register -" 14 | docker push $REGISTRY/agones-openmatch-mmf 15 | 16 | kubectl config use-context $(kubectl config get-contexts -o=name | grep ${CLUSTER_NAME1}) 17 | echo "- Deploy Open Match mmf to cluster ${CLUSTER_NAME1} -" 18 | envsubst < integration/matchfunction/matchfunction.yaml | kubectl apply --namespace ${NAMESPACE} -f - 19 | echo 20 | echo "- Display Open Match Matchfunction pod -" 21 | sleep 15 22 | kubectl get pods --namespace ${NAMESPACE} -l app=agones-openmatch-mmf -------------------------------------------------------------------------------- /scripts/deploy-ncat-fleets.sh: -------------------------------------------------------------------------------- 1 | ## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | ## SPDX-License-Identifier: MIT-0 3 | export GAMESERVER_TYPE=ncat 4 | export NAMESPACE=gameservers 5 | export CLUSTER_NAME1=$1 6 | export REGION1=$2 7 | export CLUSTER_NAME2=$3 8 | export REGION2=$4 9 | export ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text) 10 | export REGISTRY=${ACCOUNT_ID}.dkr.ecr.${REGION1}.amazonaws.com 11 | export ARCHITECTURE=$5 12 | 13 | 14 | aws ecr get-login-password --region ${REGION1} | docker login --username AWS --password-stdin $REGISTRY 15 | #docker buildx build --platform=linux/amd64 -t $REGISTRY/agones-openmatch-ncat-server integration/ncat-server 16 | #docker buildx build --platform=linux/arm64 -t $REGISTRY/agones-openmatch-ncat-server integration/ncat-server 17 | echo "Architecture is" $ARCHITECTURE 18 | if [[ $ARCHITECTURE == "arm64" ]]; 19 | then 20 | docker buildx build --platform=linux/arm64 -t $REGISTRY/agones-openmatch-ncat-server integration/ncat-server 21 | echo "building arm64 version"; 22 | else 23 | docker buildx build --platform=linux/amd64 -t $REGISTRY/agones-openmatch-ncat-server integration/ncat-server 24 | echo "building amd64 version"; 25 | fi 26 | 27 | docker push $REGISTRY/agones-openmatch-ncat-server 28 | 29 | kubectl config use-context $(kubectl config get-contexts -o=name | grep ${CLUSTER_NAME1}) 30 | export REGION=$REGION1 31 | echo "- Deploy fleets to cluster ${CLUSTER_NAME1} -" 32 | for f in manifests/fleets/${GAMESERVER_TYPE}/* 33 | do 34 | envsubst < $f | kubectl apply --namespace ${NAMESPACE} -f - 35 | done 36 | echo 37 | echo "- Display fleets and game servers -" 38 | kubectl get fleets --namespace ${NAMESPACE} 39 | kubectl get gameservers --namespace ${NAMESPACE} --show-labels 40 | echo 41 | 42 | kubectl config use-context $(kubectl config get-contexts -o=name | grep ${CLUSTER_NAME2}) 43 | export REGION=$REGION2 44 | echo "- Deploy fleets to cluster ${CLUSTER_NAME2} -" 45 | for f in manifests/fleets/${GAMESERVER_TYPE}/* 46 | do 47 | envsubst < $f | kubectl apply --namespace ${NAMESPACE} -f - 48 | done 49 | echo 50 | # echo "- Display fleets and game servers -" 51 | kubectl get fleets --namespace ${NAMESPACE} 52 | kubectl get gameservers --namespace ${NAMESPACE} --show-labels 53 | echo -------------------------------------------------------------------------------- /scripts/deploy-stk-fleets.sh: -------------------------------------------------------------------------------- 1 | ## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | ## SPDX-License-Identifier: MIT-0 3 | export GAMESERVER_TYPE=stk 4 | export NAMESPACE=gameservers 5 | export CLUSTER_NAME1=$1 6 | export REGION1=$2 7 | export CLUSTER_NAME2=$3 8 | export REGION2=$4 9 | export ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text) 10 | export REGISTRY=${ACCOUNT_ID}.dkr.ecr.${REGION1}.amazonaws.com 11 | export ARCHITECTURE=$5 12 | 13 | echo "Fetching necessary files" 14 | mkdir integration/clients/stk-server-build 15 | curl -L -o integration/clients/stk-server-build/Dockerfile https://raw.githubusercontent.com/googleforgames/agones/refs/heads/main/examples/supertuxkart/Dockerfile 16 | curl -L -o integration/clients/stk-server-build/entrypoint.sh https://raw.githubusercontent.com/googleforgames/agones/refs/heads/main/examples/supertuxkart/entrypoint.sh 17 | curl -L -o integration/clients/stk-server-build/go.mod https://raw.githubusercontent.com/googleforgames/agones/refs/heads/main/examples/supertuxkart/go.mod 18 | curl -L -o integration/clients/stk-server-build/go.sum https://raw.githubusercontent.com/googleforgames/agones/refs/heads/main/examples/supertuxkart/go.sum 19 | curl -L -o integration/clients/stk-server-build/server_config.xml https://raw.githubusercontent.com/googleforgames/agones/refs/heads/main/examples/supertuxkart/server_config.xml 20 | curl -L -o integration/clients/stk-server-build/main.go https://raw.githubusercontent.com/googleforgames/agones/refs/heads/main/examples/supertuxkart/main.go 21 | 22 | echo "- Creating tailored supertuxkart image (amd64 or arm64) -" 23 | aws ecr get-login-password --region ${REGION1} | docker login --username AWS --password-stdin $REGISTRY 24 | 25 | if [[ $ARCHITECTURE == "arm64" ]]; 26 | then 27 | echo "building arm64 version"; 28 | docker buildx build --platform=linux/arm64 -t $REGISTRY/supertuxkart-server integration/clients/stk-server-build 29 | else 30 | echo "building amd64 version"; 31 | docker buildx build --platform=linux/amd64 -t $REGISTRY/supertuxkart-server integration/clients/stk-server-build 32 | fi 33 | 34 | docker push $REGISTRY/supertuxkart-server 35 | 36 | echo "supertuxkart build and push was successful" 37 | 38 | echo "- Deploy fleets to cluster ${CLUSTER_NAME1} -" 39 | kubectl config use-context $(kubectl config get-contexts -o=name | grep ${CLUSTER_NAME1}) 40 | export REGION=$REGION1 41 | for f in manifests/fleets/${GAMESERVER_TYPE}/* 42 | do 43 | envsubst < $f | kubectl apply --namespace ${NAMESPACE} -f - 44 | done 45 | echo 46 | echo "- Display fleets and game servers -" 47 | kubectl get fleets --namespace ${NAMESPACE} 48 | kubectl get gameservers --namespace ${NAMESPACE} --show-labels 49 | echo 50 | 51 | 52 | echo "- Deploy fleets to cluster ${CLUSTER_NAME2} -" 53 | kubectl config use-context $(kubectl config get-contexts -o=name | grep ${CLUSTER_NAME2}) 54 | export REGION=$REGION2 55 | for f in manifests/fleets/${GAMESERVER_TYPE}/* 56 | do 57 | envsubst < $f | kubectl apply --namespace ${NAMESPACE} -f - 58 | done 59 | echo 60 | # echo "- Display fleets and game servers -" 61 | kubectl get fleets --namespace ${NAMESPACE} 62 | kubectl get gameservers --namespace ${NAMESPACE} --show-labels 63 | echo 64 | -------------------------------------------------------------------------------- /scripts/generate-agones-certs.sh: -------------------------------------------------------------------------------- 1 | ## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | ## SPDX-License-Identifier: MIT-0 3 | set -o xtrace 4 | echo "#####" 5 | CLUSTER_NAME=$1 6 | ROOT_PATH=$2 7 | kubectl config use-context $(kubectl config get-contexts -o=name | grep ${CLUSTER_NAME}) 8 | echo "- Verify that the cert-manager pods are running -" 9 | kubectl get pods -n cert-manager -o wide 10 | echo "- Verify the cert-manager webhook is available -" 11 | kubectl wait deployment -l app=webhook --for condition=Available=True --timeout=90s -n cert-manager 12 | echo "- Create the cluster issuer and the certificate for Agones -" 13 | kubectl apply -f ${ROOT_PATH}/manifests/cluster-issuer.yaml 14 | kubectl apply -f ${ROOT_PATH}/manifests/agones-controller-cert.yaml 15 | 16 | sleep 60 -------------------------------------------------------------------------------- /scripts/generate-tls-files.sh: -------------------------------------------------------------------------------- 1 | ## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | ## SPDX-License-Identifier: MIT-0 3 | set -o xtrace 4 | export CLUSTER_NAME=$1 5 | export ROOT_PATH=$2 6 | kubectl config use-context $(kubectl config get-contexts -o=name | grep ${CLUSTER_NAME}) 7 | KEY_FILE=${ROOT_PATH}/client_${CLUSTER_NAME}.key 8 | CERT_FILE=${ROOT_PATH}/client_${CLUSTER_NAME}.crt 9 | TLS_CA_FILE=${ROOT_PATH}/ca_${CLUSTER_NAME}.crt 10 | kubectl get secret allocator-client.default -n default -o jsonpath="{.data.tls\.crt}" | base64 -d > "${CERT_FILE}" 11 | kubectl get secret allocator-client.default -n default -o jsonpath="{.data.tls\.key}" | base64 -d > "${KEY_FILE}" 12 | kubectl get secret allocator-tls-ca -n agones-system -o jsonpath="{.data.tls-ca\.crt}" | base64 -d > "${TLS_CA_FILE}" -------------------------------------------------------------------------------- /scripts/install-linux-prerequisites.sh: -------------------------------------------------------------------------------- 1 | # install or upgrade the aws cli 2 | sudo pip uninstall -y awscli 3 | curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" 4 | unzip -o awscliv2.zip 5 | sudo ./aws/install --update 6 | . ~/.bash_profile 7 | 8 | # install eksctl 9 | curl --silent --location "https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_$(uname -s)_amd64.tar.gz" | tar xz -C /tmp 10 | sudo mv -v /tmp/eksctl /usr/local/bin 11 | eksctl version || ( echo "eksctl not found" && exit 1 ) 12 | 13 | # "Install kubectl v1.29.2" 14 | sudo curl --silent --location -o /usr/local/bin/kubectl https://dl.k8s.io/release/v1.29.2/bin/linux/amd64/kubectl > /dev/null 15 | sudo chmod +x /usr/local/bin/kubectl 16 | kubectl version --client=true || ( echo "kubectl not found" && exit 1 ) 17 | 18 | # install helm 19 | curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash 20 | 21 | # install additional tools 22 | sudo yum -y install jq gettext go openssl bash-completion moreutils 23 | 24 | # install terraform 25 | sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo 26 | sudo sed -i -e "s/\$releasever/7/g" "/etc/yum.repos.d/hashicorp.repo" 27 | sudo yum -y install terraform 28 | 29 | # enable bash completion 30 | kubectl completion bash >> ~/.bash_completion 31 | eksctl completion bash >> ~/.bash_completion 32 | . ~/.bash_completion 33 | 34 | # install yq 35 | echo 'yq() { 36 | docker run --rm -i -v "${PWD}":/workdir mikefarah/yq yq "$@" 37 | }' | tee -a ~/.bashrc && source ~/.bashrc 38 | 39 | # make sure all binaries are in the path 40 | for command in kubectl jq envsubst aws eksctl kubectl helm 41 | do 42 | which $command &>/dev/null && echo "$command in path" || ( echo "$command NOT FOUND" && exit 1 ) 43 | done 44 | 45 | echo 'Prerequisites installed successfully.' 46 | -------------------------------------------------------------------------------- /scripts/namespace-finalizer.sh: -------------------------------------------------------------------------------- 1 | ## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | ## SPDX-License-Identifier: MIT-0 3 | sleep 3 4 | NAMESPACE=$1 5 | kubectl get namespace ${NAMESPACE} && kubectl get namespace ${NAMESPACE} -o json | jq 'del(.spec.finalizers[0])' | kubectl replace --raw "/api/v1/namespaces/${NAMESPACE}/finalize" -f - 6 | 7 | -------------------------------------------------------------------------------- /scripts/set-allocator-ip.sh: -------------------------------------------------------------------------------- 1 | ## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | ## SPDX-License-Identifier: MIT-0 3 | CLUSTER_NAME=$1 4 | kubectl config use-context $(kubectl config get-contexts -o=name | grep ${CLUSTER_NAME}) 2>&1 > /dev/null 5 | ALLOCATOR_IP=$(kubectl get services agones-allocator -n agones-system -o jsonpath='{.status.loadBalancer.ingress[0].hostname}') 6 | echo ${ALLOCATOR_IP} 7 | -------------------------------------------------------------------------------- /scripts/test-agones-tls.sh: -------------------------------------------------------------------------------- 1 | ## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | ## SPDX-License-Identifier: MIT-0 3 | export CLUSTER_NAME=$1 4 | kubectl config use-context $(kubectl config get-contexts -o=name | grep ${CLUSTER_NAME}) 5 | KEY_FILE=client_${CLUSTER_NAME}.key 6 | CERT_FILE=client_${CLUSTER_NAME}.crt 7 | TLS_CA_FILE=ca_${CLUSTER_NAME}.crt 8 | kubectl port-forward -nagones-system svc/agones-allocator 4443:443 & 9 | PID=$! 10 | RESOLVE=agones-allocator.agones-system.svc.cluster.local:443:127.0.0.1 11 | ENDPOINT=https://agones-allocator.agones-system.svc.cluster.local:4443/gameserverallocation 12 | sleep 10 13 | curl $2 --resolve ${RESOLVE} ${ENDPOINT} --key ${KEY_FILE} --cert ${CERT_FILE} --cacert ${TLS_CA_FILE} -H "Content-Type: application/json" --data '{"namespace":"'gameservers'"}' 14 | echo 15 | kill ${PID} -------------------------------------------------------------------------------- /scripts/test-gameserver-allocation.sh: -------------------------------------------------------------------------------- 1 | ## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | ## SPDX-License-Identifier: MIT-0 3 | # set -o xtrace 4 | echo "#####" 5 | NAMESPACE=gameservers 6 | CLUSTER_NAME=$1 7 | REGION=$2 8 | kubectl config use-context $(kubectl config get-contexts -o=name | grep ${CLUSTER_NAME}) 9 | KEY_FILE=client_${CLUSTER_NAME}.key 10 | CERT_FILE=client_${CLUSTER_NAME}.crt 11 | TLS_CA_FILE=ca_${CLUSTER_NAME}.crt 12 | EXTERNAL_IP=$(kubectl get services agones-allocator -n agones-system -o jsonpath='{.status.loadBalancer.ingress[0].hostname}') 13 | ENDPOINT=https://${EXTERNAL_IP}/gameserverallocation 14 | echo "- Allocating server -" 15 | curl ${ENDPOINT} --key ${KEY_FILE} --cert ${CERT_FILE} --cacert ${TLS_CA_FILE} -H "Content-Type: application/json" --data '{"namespace":"'${NAMESPACE}'"}' 16 | echo 17 | echo "- Display game servers -" 18 | kubectl get gameservers --namespace ${NAMESPACE} --show-labels 19 | -------------------------------------------------------------------------------- /scripts/test-gameserver-multicluster-allocation.sh: -------------------------------------------------------------------------------- 1 | ## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | ## SPDX-License-Identifier: MIT-0 3 | # set -o xtrace 4 | echo "#####" 5 | NAMESPACE=agones-system 6 | GAMESERVER_NAMESPACE=gameservers 7 | CLUSTER_NAME1=$1 8 | REGION1=$2 9 | CLUSTER_NAME2=$3 10 | REGION2=$4 11 | KEY_FILE=client_${CLUSTER_NAME1}.key 12 | CERT_FILE=client_${CLUSTER_NAME1}.crt 13 | TLS_CA_FILE=ca_${CLUSTER_NAME1}.crt 14 | kubectl config use-context $(kubectl config get-contexts -o=name | grep ${CLUSTER_NAME1}) 15 | EXTERNAL_IP=$(kubectl get services agones-allocator -n agones-system -o jsonpath='{.status.loadBalancer.ingress[0].hostname}') 16 | ENDPOINT=https://${EXTERNAL_IP}/gameserverallocation 17 | echo "- Allocating server -" 18 | curl ${ENDPOINT} --key ${KEY_FILE} --cert ${CERT_FILE} --cacert ${TLS_CA_FILE} -H "Content-Type: application/json" --data '{"namespace":"'${NAMESPACE}'", "multiClusterSetting":{"enabled":true}}' 19 | echo 20 | echo "- Display ALLOCATED game servers on cluster ${CLUSTER_NAME1} only -" 21 | kubectl get gameservers --namespace ${GAMESERVER_NAMESPACE} | grep Allocated 22 | kubectl config use-context $(kubectl config get-contexts -o=name | grep ${CLUSTER_NAME2}) 23 | echo "- Display ALLOCATED game servers on cluster ${CLUSTER_NAME2} only -" 24 | kubectl get gameservers --namespace ${GAMESERVER_NAMESPACE} | grep Allocated 25 | -------------------------------------------------------------------------------- /security.md: -------------------------------------------------------------------------------- 1 | # Security recommendations 2 | This page provides suggestions of actions that should be taken to make the solution more secure according to AWS best practices, before using it in a production environment. 3 | 4 | ## Enable control plane logs 5 | A solutions cluster(s) must have control plane logs enabled in order to publish API, audit, controller manager, scheduler or authenticator logs to AWS CloudWatch Logs. You must enable each log type individually to send logs for your cluster.CloudWatch Logs ingestion, archive storage, and data scanning rates apply to enabled control plane logs. 6 | 7 | Use auditing tools, like [kubeaudit](https://github.com/Shopify/kubeaudit). 8 | 9 | ## Create alerts 10 | Create an alarm to automatically alert you where there is an increase in **403 Forbidden** and **401 Unauthorized** responses, and then use attributes like host, sourceIPs, and k8s_user.username to find out where those requests are coming from. 11 | 12 | - AWS Custom Config Rules for Kubernetes 13 | 14 | *eks-netPolCheck-rule* Checks that there is a network policy defined for each namespace in the cluster 15 | 16 | *eks-privEscalation-rule* Checks that there are no pods running containers with the AllowPrivilege Escalation flag 17 | 18 | *eks-trustedRegCheck-rule* Checks that container images are from trusted sources 19 | 20 | ## Use AWS KMS for envelope encryption for Kubernetes secrets 21 | With the [KMS plugin for Kubernetes](https://docs.aws.amazon.com/eks/latest/userguide/enable-kms.html), all Kubernetes secrets are stored in etcd in ciphertext instead of plain text and can only be decrypted by the Kubernetes API server. 22 | 23 | Recommendations: 24 | - Rotate your secrets periodically 25 | - Use separate namespaces as a way to isolate secrets from different applications 26 | - Use volume mounts instead of environment variables 27 | - Use an external secrets provider (AWS Secret manager or Vault) 28 | 29 | ## Harden the security of the ECR repositories 30 | - Make your ECR images IMMUTABLE to prevent code injection through image mutation 31 | - Encrypt the ECR repositories using KMS 32 | 33 | ## Scan for runtime security vulnerabilities 34 | Runtime security provides active protection for your containers while they're running. The idea is to detect and/or prevent malicious activity from occuring inside the container. 35 | Recommendations: 36 | - Use a 3rd party solution for runtime defense (Aqua/Qualys/Stackrox/Sysdig Secure/Twistlock) 37 | - Use AWS Marketplace solution 38 | - Use Linux capabilities before writing seccomp policies 39 | - Use application vulnerability scan in the pipeline and generate a report (CVEs) 40 | - Scan the produced container image 41 | - Don't deploy a container if the image scan result is higher that certain threshold 42 | 43 | ## Run periodically CIS Benchmarks or other compliance tools against you environment 44 | [kube-bench](https://github.com/aquasecurity/kube-bench) is an open source project that evaluates your cluster against the CIS benchmarks for Kubernetes. The benchmark describes the best practices for securing unmanaged Kubernetes clusters. 45 | 46 | ## Use a trusted source for 3rd party HelmCharts 47 | Third party hosted HelmChart repositories dynamically loaded in your environment could become compromised or modified unexpectedly, affecting chart availability and integrity. Ensure to use a trusted domain to load HelmChart Library. 48 | 49 | ## Adopt a process to harden Kubernetes on EKS Cluster and container images 50 | The following items should implement security best practices to secure Kubernetes on EKS Cluster in AWS. 51 | 52 | - Cluster Level Configs 53 | - Cluster Access Management 54 | - Worker Node Configuration 55 | - RBAC/ Cluster Authorization 56 | - Workload configs 57 | - Kubernetes Security Features 58 | - Network Controls 59 | 60 | ## Enable ELB/ALB access logs 61 | Use access logs to allow customers to analyze traffic patterns and identify and troubleshoot security issues. 62 | 63 | ## Enable VPC and Global Accelerator Flow Logs 64 | Flow Logs capture network flow information for a VPC, subnet, network interfaces, and Accelerators, and stores it in Amazon CloudWatch Logs. Flow log data can help customers troubleshoot network issues; for example, to diagnose why specific traffic is not reaching an instance, which might be a result of overly restrictive security group rules. -------------------------------------------------------------------------------- /terraform/cloudformation/buildspec.yml: -------------------------------------------------------------------------------- 1 | version: 0.2 2 | env: 3 | variables: 4 | REGION: $AWS_REGION 5 | MAX_RETRIES: 5 6 | RETRY_DELAY: 5 7 | phases: 8 | install: 9 | commands: 10 | - echo Installing dependencies... 11 | - sudo yum install -y yum-utils shadow-utils 12 | - sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/AmazonLinux/hashicorp.repo 13 | - sudo yum -y install terraform 14 | - terraform --version 15 | 16 | - echo "Installing Helm..." 17 | - curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 18 | - chmod 700 get_helm.sh 19 | - ./get_helm.sh 20 | - helm version 21 | 22 | - echo "Installing jq..." 23 | - sudo yum install -y jq 24 | 25 | - echo Installing GetText... 26 | - sudo yum install -y gettext 27 | - gettext --version 28 | 29 | - echo Installing Go... 30 | - sudo yum install -y golang 31 | - go version 32 | 33 | - echo "Installing kubectl..." 34 | - curl -o kubectl https://s3.us-west-2.amazonaws.com/amazon-eks/1.28.0/2023-04-19/bin/linux/amd64/kubectl 35 | - chmod +x ./kubectl 36 | - mkdir -p $HOME/bin && cp ./kubectl $HOME/bin/kubectl && export PATH=$PATH:$HOME/bin 37 | - echo 'export PATH=$PATH:$HOME/bin' >> ~/.bashrc 38 | - kubectl version --client --output=yaml 39 | 40 | - echo "Checking Docker version..." 41 | - docker version 42 | 43 | - echo "Checking OpenSSL version..." 44 | - openssl version 45 | 46 | pre_build: 47 | commands: 48 | - echo Checking out repositories... 49 | - echo "Listing contents of CODEBUILD_SRC_DIR" 50 | - ls -la $CODEBUILD_SRC_DIR 51 | build: 52 | commands: 53 | - echo "Initializing Terraform for cluster creation..." 54 | - | 55 | terraform -chdir=terraform/cluster init \ 56 | -backend-config="bucket=${TERRAFORM_STATE_BUCKET}" \ 57 | -backend-config="key=${TF_STATE_KEY}" \ 58 | -backend-config="region=${AWS_REGION}" \ 59 | -backend-config="dynamodb_table=${TERRAFORM_DYNAMO_TABLE}" \ 60 | -backend-config="encrypt=true" 61 | 62 | - echo "Creating both clusters..." 63 | - | 64 | terraform -chdir=terraform/cluster apply -auto-approve \ 65 | -var="cluster_1_name=${CLUSTER1}" \ 66 | -var="cluster_1_region=${REGION1}" \ 67 | -var="cluster_1_cidr=${CIDR1}" \ 68 | -var="cluster_2_name=${CLUSTER2}" \ 69 | -var="cluster_2_region=${REGION2}" \ 70 | -var="cluster_2_cidr=${CIDR2}" \ 71 | -var="cluster_version=${VERSION}" \ 72 | -var="admin_role_arn_from_cloudformation=${ADMIN_ROLE_ARN}" \ 73 | -var="codebuild_role_arn_from_cloudformation=${CB_SERVICE_ROLE}" 74 | 75 | - echo "Step 2 - Terraform Intra-Cluster" 76 | - echo "Initializing Terraform for intra-cluster configuration..." 77 | - | 78 | terraform -chdir=terraform/intra-cluster init \ 79 | -backend-config="bucket=${TERRAFORM_STATE_BUCKET}" \ 80 | -backend-config="key=intra-cluster-${TF_STATE_KEY}" \ 81 | -backend-config="region=${AWS_REGION}" \ 82 | -backend-config="dynamodb_table=${TERRAFORM_DYNAMO_TABLE}" \ 83 | -backend-config="encrypt=true" 84 | 85 | - echo "Deploying to the first cluster (${CLUSTER1})..." 86 | - | 87 | terraform -chdir=terraform/intra-cluster workspace select -or-create=true ${REGION1} && 88 | terraform -chdir=terraform/intra-cluster apply -auto-approve \ 89 | -var="cluster_name=${CLUSTER1}" \ 90 | -var="cluster_region=${REGION1}" \ 91 | -var="cluster_endpoint=$(terraform -chdir=terraform/cluster output -raw cluster_1_endpoint)" \ 92 | -var="cluster_certificate_authority_data=$(terraform -chdir=terraform/cluster output -raw cluster_1_certificate_authority_data)" \ 93 | -var="cluster_token=$(terraform -chdir=terraform/cluster output -raw cluster_1_token)" \ 94 | -var="cluster_version=${VERSION}" \ 95 | -var="oidc_provider_arn=$(terraform -chdir=terraform/cluster output -raw oidc_provider_1_arn)" \ 96 | -var="namespaces=[\"agones-openmatch\", \"agones-system\", \"gameservers\", \"open-match\"]" \ 97 | -var="configure_agones=true" \ 98 | -var="configure_open_match=true" 99 | 100 | - echo "Deploying to the second cluster (${CLUSTER2})..." 101 | - | 102 | terraform -chdir=terraform/intra-cluster workspace select -or-create=true ${REGION2} && 103 | terraform -chdir=terraform/intra-cluster apply -auto-approve \ 104 | -var="cluster_name=${CLUSTER2}" \ 105 | -var="cluster_region=${REGION2}" \ 106 | -var="cluster_endpoint=$(terraform -chdir=terraform/cluster output -raw cluster_2_endpoint)" \ 107 | -var="cluster_certificate_authority_data=$(terraform -chdir=terraform/cluster output -raw cluster_2_certificate_authority_data)" \ 108 | -var="cluster_token=$(terraform -chdir=terraform/cluster output -raw cluster_2_token)" \ 109 | -var="cluster_version=${VERSION}" \ 110 | -var="oidc_provider_arn=$(terraform -chdir=terraform/cluster output -raw oidc_provider_2_arn)" \ 111 | -var="namespaces=[\"agones-system\", \"gameservers\"]" \ 112 | -var="configure_agones=true" \ 113 | -var="configure_open_match=false" 114 | 115 | - echo "Fetching the load balancer ARN..." 116 | 117 | - echo "Updating kubeconfig for Cluster 1..." 118 | - aws eks --region ${REGION1} update-kubeconfig --name ${CLUSTER1} 119 | - kubectl config use-context arn:aws:eks:${REGION1}:$(aws sts get-caller-identity --query Account --output text):cluster/${CLUSTER1} 120 | 121 | - echo "Verifying current context..." 122 | - kubectl config current-context 123 | 124 | - echo "Setting the Open Match service name" 125 | - export OPEN_MATCH_SVC_NAME=$CLUSTER1-om-fe 126 | - echo "OPEN_MATCH_SVC_NAME=$OPEN_MATCH_SVC_NAME" 127 | 128 | - echo "Setting the name of the Load balancer" 129 | - | 130 | export FLB_NAME=$(kubectl get services -n open-match -o json | jq -r --arg OPEN_MATCH_SVC_NAME "$OPEN_MATCH_SVC_NAME" '.items[] | select(.metadata.name==$OPEN_MATCH_SVC_NAME) | .status.loadBalancer.ingress[0].hostname') 131 | - echo "FLB_NAME=$FLB_NAME" 132 | - echo "Retrieving the ARN of the load balancer" 133 | - | 134 | export FLB_ARN=$(aws elbv2 describe-load-balancers --region ${REGION1} | jq -r ".LoadBalancers[] | select(.DNSName==\"$FLB_NAME\") | .LoadBalancerArn") 135 | - echo "FLB_ARN=$FLB_ARN" 136 | - echo "Initializing Terraform for extra-cluster configuration" 137 | - | 138 | terraform -chdir=terraform/extra-cluster init \ 139 | -backend-config="bucket=${TERRAFORM_STATE_BUCKET}" \ 140 | -backend-config="key=extra-cluster-${TF_STATE_KEY}" \ 141 | -backend-config="region=${AWS_REGION}" \ 142 | -backend-config="dynamodb_table=${TERRAFORM_DYNAMO_TABLE}" \ 143 | -backend-config="encrypt=true" 144 | 145 | - echo "Getting values needed by Terraform" 146 | - export VPC1=$(terraform -chdir=terraform/cluster output -raw vpc_1_id) 147 | - export SUBNETS1=$(terraform -chdir=terraform/cluster output gameservers_1_subnets) 148 | - export ROUTE1=$(terraform -chdir=terraform/cluster output -raw private_route_table_1_id) 149 | - export ENDPOINT1=$(terraform -chdir=terraform/cluster output -raw cluster_2_endpoint) 150 | - export AUTH1=$(terraform -chdir=terraform/cluster output -raw cluster_1_certificate_authority_data) 151 | - export TOKEN1=$(terraform -chdir=terraform/cluster output -raw cluster_1_token) 152 | - export VPC2=$(terraform -chdir=terraform/cluster output -raw vpc_2_id) 153 | - export SUBNETS2=$(terraform -chdir=terraform/cluster output gameservers_2_subnets) 154 | - export ROUTE2=$(terraform -chdir=terraform/cluster output -raw private_route_table_2_id) 155 | - export ENDPOINT2=$(terraform -chdir=terraform/cluster output -raw cluster_2_endpoint) 156 | - export AUTH2=$(terraform -chdir=terraform/cluster output -raw cluster_2_certificate_authority_data) 157 | - export TOKEN2=$(terraform -chdir=terraform/cluster output -raw cluster_2_token) 158 | 159 | - echo "Creating extra-cluster resources" 160 | - | 161 | terraform -chdir=terraform/extra-cluster apply -auto-approve \ 162 | -var="cluster_1_name=${CLUSTER1}" \ 163 | -var="requester_cidr=${CIDR1}" \ 164 | -var="requester_vpc_id=${VPC1}" \ 165 | -var="requester_route=${ROUTE1}" \ 166 | -var="cluster_1_gameservers_subnets=${SUBNETS1}" \ 167 | -var="cluster_1_endpoint=${ENDPOINT1}" \ 168 | -var="cluster_1_certificate_authority_data=${AUTH1}" \ 169 | -var="cluster_1_token=${TOKEN1}" \ 170 | -var="cluster_2_name=${CLUSTER2}" \ 171 | -var="accepter_cidr=${CIDR2}" \ 172 | -var="accepter_vpc_id=${VPC2}" \ 173 | -var="accepter_route=${ROUTE2}" \ 174 | -var="cluster_2_gameservers_subnets=${SUBNETS2}" \ 175 | -var="cluster_2_endpoint=${ENDPOINT2}" \ 176 | -var="cluster_2_certificate_authority_data=${AUTH2}" \ 177 | -var="cluster_2_token=${TOKEN2}" \ 178 | -var="cluster_1_region=${REGION1}" \ 179 | -var="ecr_region=${REGION1}" \ 180 | -var="cluster_2_region=${REGION2}" \ 181 | -var="aws_lb_arn=${FLB_ARN}" 182 | 183 | - echo "Capturing Terraform output" 184 | - terraform -chdir=terraform/extra-cluster output > terraform_output.txt 185 | 186 | - echo "Building and deploying game server fleets..." 187 | - | 188 | chmod +x scripts/deploy-ncat-fleets.sh 189 | sh scripts/deploy-ncat-fleets.sh ${CLUSTER1} ${REGION1} ${CLUSTER2} ${REGION2} 190 | 191 | - echo "Integrating Open Match with Agones..." 192 | - | 193 | cd $CODEBUILD_SRC_DIR 194 | kubectl config use-context $(kubectl config get-contexts -o=name | grep ${CLUSTER1}) 195 | sh scripts/deploy-matchfunction.sh ${CLUSTER1} ${REGION1} 196 | sh scripts/deploy-director.sh ${CLUSTER1} ${REGION1} ${REGION2} 197 | kubectl get pods -n agones-openmatch 198 | - echo "Open Match integration with Agones completed." 199 | 200 | post_build: 201 | commands: 202 | - echo "Build completed on $(date)" 203 | - echo "Terraform Output:" 204 | - cat terraform_output.txt || echo "No terraform_output.txt file found" 205 | - echo "Global Accelerator Address:" 206 | - grep "global_accelerator_address" terraform_output.txt | cut -d '=' -f2 | tr -d ' "' || echo "Global Accelerator address not found in output" -------------------------------------------------------------------------------- /terraform/cluster/backend.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | backend "s3" { 3 | # This block is intentionally empty. 4 | # Backend configuration will be provided via -backend-config parameters 5 | # when initializing Terraform in the automated pipeline. 6 | } 7 | } -------------------------------------------------------------------------------- /terraform/cluster/main.tf: -------------------------------------------------------------------------------- 1 | ## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | ## SPDX-License-Identifier: MIT-0 3 | module "cluster1" { 4 | source = "./modules/cluster" 5 | cluster_name = var.cluster_1_name 6 | cluster_region = var.cluster_1_region 7 | cluster_cidr = var.cluster_1_cidr 8 | open_match = true 9 | all_mngs_use_arm_based_instance_types = var.all_arm_based_instances_cluster_1 10 | gameservers_mng_uses_arm_based_instances = var.gameservers_arm_based_instances_cluster_1 11 | agones_system_mng_uses_arm_based_instances = var.agones_system_arm_based_instances_cluster_1 12 | agones_metrics_mng_uses_arm_based_instances = var.agones_metrics_arm_based_instances_cluster_1 13 | admin_role_arn = var.admin_role_arn_from_cloudformation 14 | codebuild_role_arn = var.codebuild_role_arn_from_cloudformation 15 | } 16 | module "cluster2" { 17 | source = "./modules/cluster" 18 | cluster_name = var.cluster_2_name 19 | cluster_region = var.cluster_2_region 20 | cluster_cidr = var.cluster_2_cidr 21 | open_match = false 22 | all_mngs_use_arm_based_instance_types = var.all_arm_based_instances_cluster_2 23 | gameservers_mng_uses_arm_based_instances = var.gameservers_arm_based_instances_cluster_2 24 | agones_system_mng_uses_arm_based_instances = var.agones_system_arm_based_instances_cluster_2 25 | agones_metrics_mng_uses_arm_based_instances = var.agones_metrics_arm_based_instances_cluster_2 26 | admin_role_arn = var.admin_role_arn_from_cloudformation 27 | codebuild_role_arn = var.codebuild_role_arn_from_cloudformation 28 | } 29 | #-------------------------------------------------------------- 30 | # Adding guidance solution ID via AWS CloudFormation resource 31 | #-------------------------------------------------------------- 32 | resource "aws_cloudformation_stack" "guidance_deployment_metrics" { 33 | name = "tracking-stack" 34 | template_body = <