Skip to content

Commit bda6c7b

Browse files
authored
Merge pull request #79 from peterzhongyi/om2
Migrate Guess the Sketch demo from using OM1 to OM2
2 parents 00a5103 + 38a5970 commit bda6c7b

29 files changed

Lines changed: 1407 additions & 1214 deletions

examples/guess-the-sketch/README.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,22 @@ presented with three predefined prompts and asked to select one of them._
3131
cd ${CUR_DIR:?}/examples/guess-the-sketch
3232
skaffold run --build-concurrency=0 --cache-artifacts=false
3333
```
34-
1. Install the frontend, match maker, and open match into your cluster:
34+
1. Deploy Open Match 2 on Cloud Run by following [the documention](https://github.com/googleforgames/open-match2/blob/main/docs/DEVELOPMENT.md).
35+
- NOTE: before running `gcloud run services replace service.yaml` to deploy Open Match 2, make sure to:
36+
- Replace the IP address of the `OM_REDIS_WRITE_HOST` and `OM_REDIS_READ_HOST` in the [`service.yaml`](https://github.com/googleforgames/open-match2/blob/main/deploy/service.yaml) file to be the IP address of the Redis instance you created.
37+
- Replace the `run.googleapis.com/network-interfaces` field to use the network and subnetwork configured by terraform. In this case, it's `vpc-genai-quickstart` and `sn-usc1`.
38+
- Replace the `cloud.googleapis.com/location` field to be the location of your `sn-usc1` subnetwork (us-central1 if you followed the top level [README](https://github.com/googleforgames/GenAI-quickstart/blob/main/README.md)).
39+
1. Configure Open Match 2's permissions to your cluster.
40+
- Create a Google Service Account in your project and give it the `Cloud Run Invoker` role.
41+
- Replace the value of `iam.gke.io/gcp-service-account` of `frontend_k8s.yaml` and `director_k8s.yaml` to be the Google Service Account you created.
42+
- Bind the Kubernetes Service Account in `frontend_k8s.yaml` and `director_k8s.yaml` to the Google Service Account(in the form of `SERVICE_ACCOUNT_NAME@PROJECT_ID.iam.gserviceaccount.com`) with
43+
```
44+
gcloud iam service-accounts add-iam-policy-binding --role roles/iam.workloadIdentityUser --member "serviceAccount:PROJECT_ID.svc.id.goog[genai/frontend-sa]" YOUR_GOOGLE_SERVICE_ACCOUNT
45+
46+
gcloud iam service-accounts add-iam-policy-binding --role roles/iam.workloadIdentityUser --member "serviceAccount:PROJECT_ID.svc.id.goog[genai/fleet-allocator]" YOUR_GOOGLE_SERVICE_ACCOUNT
47+
```
48+
- Replace the value of `OM_CORE_ADDRESS` environment variable in `frontend_k8s.yaml` and `director_k8s.yaml` to be the url of the OM2 that's deployed on Cloud Run.
49+
1. Install the frontend and match maker into your cluster:
3550
- (optional) If you want to enable the consent screen, update `examples/guess-the-sketch/matchmaker/frontend/k8s.yaml` to set `showConsentPage` to `true` before running the `skaffold` command.
3651
```
3752
cd ${CUR_DIR:?}/examples/guess-the-sketch/matchmaker

examples/guess-the-sketch/fleet.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ spec:
3131
spec:
3232
containers:
3333
- name: guess-the-sketch-app
34-
image: guess-the-sketch-app
34+
image: guess-the-sketch
3535
resources:
3636
requests:
3737
memory: 256Mi

examples/guess-the-sketch/matchmaker/director/Dockerfile renamed to examples/guess-the-sketch/matchmaker/Director.Dockerfile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ COPY go.mod go.sum ./
2222
RUN go mod download
2323

2424
# Copy source code into the image
25-
COPY *.go ./
25+
COPY director/*.go ./
26+
COPY omclient/ ./omclient
27+
COPY logging/ ./logging
2628

2729
# Build
2830
RUN CGO_ENABLED=0 GOOS=linux go build -o /app/director

examples/guess-the-sketch/matchmaker/frontend/Dockerfile renamed to examples/guess-the-sketch/matchmaker/Frontend.Dockerfile

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@ COPY go.mod go.sum ./
2222
RUN go mod download
2323

2424
# Copy source code into the image
25-
COPY *.go ./
26-
COPY static/ /app/static/
25+
COPY frontend/*.go ./
26+
COPY frontend/static/ /app/static/
27+
COPY omclient/ ./omclient
28+
COPY logging/ ./logging
2729

2830
# Build
2931
RUN CGO_ENABLED=0 GOOS=linux go build -o /app/frontend

examples/guess-the-sketch/matchmaker/mmf/Dockerfile renamed to examples/guess-the-sketch/matchmaker/Mmf.Dockerfile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ RUN go mod download
2323

2424
# Copy source code into the image
2525
RUN mkdir /app
26-
COPY *.go ./
26+
COPY mmf/*.go ./
27+
COPY omclient/ ./omclient
28+
COPY logging/ ./logging
2729

2830
# Build
2931
RUN CGO_ENABLED=0 GOOS=linux go build -o /app/mmf

examples/guess-the-sketch/matchmaker/director/director.go

Lines changed: 66 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -17,61 +17,42 @@ package main
1717
import (
1818
"context"
1919
"fmt"
20-
"io"
2120
"log"
2221
"time"
2322

24-
"google.golang.org/grpc"
2523
"google.golang.org/protobuf/types/known/anypb"
26-
"open-match.dev/open-match/pkg/pb"
2724

2825
allocationv1 "agones.dev/agones/pkg/apis/allocation/v1"
2926
"agones.dev/agones/pkg/client/clientset/versioned"
30-
"google.golang.org/grpc/credentials/insecure"
27+
pb2 "github.com/googleforgames/open-match2/v2/pkg/pb"
3128
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29+
"k8s.io/client-go/kubernetes"
3230
"k8s.io/client-go/rest"
3331

34-
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
35-
)
32+
"matchmaker/omclient"
3633

37-
const (
38-
omApiHost = "open-match-backend.open-match.svc.cluster.local:50505"
39-
mmfApiHost = "guess-the-sketch-mmf.genai.svc.cluster.local"
40-
mmfApiPort = 50502
34+
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
4135
)
4236

4337
type Client struct {
44-
BackendServiceClient pb.BackendServiceClient
45-
CloserBackendServiceClient func() error
46-
AgonesClientset versioned.Interface
38+
AgonesClientset versioned.Interface
39+
OmClient *omclient.RestfulOMGrpcClient
4740
}
4841

4942
func main() {
5043
log.Println("Starting Director")
5144

5245
for range time.Tick(time.Second) {
53-
54-
omBackendClient, omCloser := createOMBackendClient()
55-
5646
var r Client
5747
r.AgonesClientset = createAgonesClient()
58-
r.BackendServiceClient = omBackendClient
59-
r.CloserBackendServiceClient = omCloser
48+
r.OmClient = omclient.CreateOMClient()
6049

6150
if err := r.run(); err != nil {
6251
log.Println("Error running director:", err.Error())
6352
}
6453
}
6554
}
6655

67-
func createOMBackendClient() (pb.BackendServiceClient, func() error) {
68-
conn, err := grpc.Dial(omApiHost, grpc.WithTransportCredentials(insecure.NewCredentials()))
69-
if err != nil {
70-
panic(err)
71-
}
72-
return pb.NewBackendServiceClient(conn), conn.Close
73-
}
74-
7556
func createAgonesClient() *versioned.Clientset {
7657
config, err := rest.InClusterConfig()
7758
if err != nil {
@@ -85,21 +66,41 @@ func createAgonesClient() *versioned.Clientset {
8566
}
8667

8768
// Customize the backend.FetchMatches request, the default one will return all tickets in the statestore
88-
func createOMFetchMatchesRequest() *pb.FetchMatchesRequest {
89-
return &pb.FetchMatchesRequest{
69+
func createOMFetchMatchesRequest() *pb2.MmfRequest {
70+
config, err := rest.InClusterConfig()
71+
if err != nil {
72+
fmt.Printf("failed to get K8s config: %v\n", err)
73+
return &pb2.MmfRequest{}
74+
}
75+
76+
client, err := kubernetes.NewForConfig(config)
77+
if err != nil {
78+
fmt.Printf("failed to create K8s client: %v\n", err)
79+
return &pb2.MmfRequest{}
80+
}
81+
82+
service, err := client.CoreV1().Services("genai").Get(context.Background(), "guess-the-sketch-mmf", metav1.GetOptions{})
83+
if err != nil {
84+
fmt.Printf("Failed to get service: %v\n", err)
85+
return &pb2.MmfRequest{}
86+
}
87+
if len(service.Status.LoadBalancer.Ingress) <= 0 {
88+
fmt.Println("No external IP found (LoadBalancer might be still provisioning)")
89+
return &pb2.MmfRequest{}
90+
}
91+
ingress := service.Status.LoadBalancer.Ingress[0]
92+
return &pb2.MmfRequest{
9093
// om-function:50502 -> the internal hostname & port number of the MMF service in our Kubernetes cluster
91-
Config: &pb.FunctionConfig{
92-
Host: mmfApiHost,
93-
Port: mmfApiPort,
94-
Type: pb.FunctionConfig_GRPC,
95-
},
96-
Profile: &pb.MatchProfile{
97-
Name: "1v1",
98-
Pools: []*pb.Pool{
99-
{
100-
Name: "everyone",
101-
},
94+
Mmfs: []*pb2.MatchmakingFunctionSpec{
95+
{
96+
Host: "http://" + ingress.IP,
97+
Port: 50502,
98+
Type: pb2.MatchmakingFunctionSpec_GRPC,
10299
},
100+
},
101+
Profile: &pb2.Profile{
102+
Name: "1v1",
103+
Pools: map[string]*pb2.Pool{"all": {Name: "everyone"}},
103104
Extensions: map[string]*anypb.Any{},
104105
},
105106
}
@@ -109,52 +110,43 @@ func createAgonesGameServerAllocation() *allocationv1.GameServerAllocation {
109110
return &allocationv1.GameServerAllocation{}
110111
}
111112

112-
func createOMAssignTicketRequest(match *pb.Match, gsa *allocationv1.GameServerAllocation) *pb.AssignTicketsRequest {
113-
tids := []string{}
114-
for _, t := range match.GetTickets() {
115-
tids = append(tids, t.GetId())
113+
func createOMAssignTicketRequest(match *pb2.Match, gsa *allocationv1.GameServerAllocation) *pb2.CreateAssignmentsRequest {
114+
tids := []*pb2.Ticket{}
115+
for _, r := range match.Rosters {
116+
tids = append(tids, r.Tickets...)
116117
}
117118

118-
return &pb.AssignTicketsRequest{
119-
Assignments: []*pb.AssignmentGroup{
120-
{
121-
TicketIds: tids,
122-
Assignment: &pb.Assignment{
123-
Connection: fmt.Sprintf("%s:%d", gsa.Status.Address, gsa.Status.Ports[0].Port),
124-
},
119+
return &pb2.CreateAssignmentsRequest{
120+
AssignmentRoster: &pb2.Roster{
121+
Name: "My_Assignment_Roster_Name",
122+
Assignment: &pb2.Assignment{
123+
Connection: fmt.Sprintf("%s:%d", gsa.Status.Address, gsa.Status.Ports[0].Port),
125124
},
125+
Tickets: tids,
126126
},
127127
}
128128
}
129129

130130
func (r Client) run() error {
131-
bc := r.BackendServiceClient
132-
closer := r.CloserBackendServiceClient
133-
defer func() {
134-
err := closer()
135-
if err != nil {
136-
log.Println(err)
137-
}
138-
}()
131+
invocationResultChan := make(chan *pb2.StreamedMmfResponse)
139132

140-
agonesClient := r.AgonesClientset
133+
fmt.Println("Director: start InvokeMatchmakingFunctions in another thread")
141134

142-
stream, err := bc.FetchMatches(context.Background(), createOMFetchMatchesRequest())
143-
if err != nil {
144-
return fmt.Errorf("fail to get response stream from backend.FetchMatches call: %w", err)
145-
}
135+
go r.OmClient.InvokeMatchmakingFunctions(context.Background(), createOMFetchMatchesRequest(), invocationResultChan)
136+
137+
agonesClient := r.AgonesClientset
146138

147139
totalMatches := 0
148140
// Read the FetchMatches response. Each loop fetches an available game match that satisfies the match profiles.
149-
for {
150-
resp, err := stream.Recv()
151-
if err == io.EOF {
152-
break
153-
}
154-
if err != nil {
155-
return fmt.Errorf("error streaming response from backend.FetchMatches call: %w", err)
156-
}
141+
fmt.Println("Director: waiting for invocationResultChan to have a resp")
142+
for resp := range invocationResultChan {
143+
144+
fmt.Println("got something from the invocationResultChan: ", resp)
145+
157146
ctx := context.Background()
147+
148+
fmt.Println("Allocating a game server")
149+
158150
gsa, err := agonesClient.AllocationV1().GameServerAllocations("default").Create(ctx, createAgonesGameServerAllocation(), metav1.CreateOptions{})
159151
if err != nil {
160152
return fmt.Errorf("error requesting allocation: %w", err)
@@ -167,7 +159,9 @@ func (r Client) run() error {
167159
continue
168160
}
169161

170-
if _, err = bc.AssignTickets(context.Background(), createOMAssignTicketRequest(resp.GetMatch(), gsa)); err != nil {
162+
fmt.Println("The game server is allocated, assigning tickets")
163+
164+
if _, err = r.OmClient.CreateAssignments(createOMAssignTicketRequest(resp.GetMatch(), gsa)); err != nil {
171165
// Corner case where we allocated a game server for players who left the queue after some waiting time.
172166
// Note that we may still leak some game servers when tickets got assigned but players left the queue before game frontend announced the assignments.
173167
if err = agonesClient.AgonesV1().GameServers("default").Delete(ctx, gsa.Status.GameServerName, metav1.DeleteOptions{}); err != nil {

examples/guess-the-sketch/matchmaker/director/go.mod

Lines changed: 0 additions & 72 deletions
This file was deleted.

0 commit comments

Comments
 (0)