agones.dev/agones@v1.54.0/sdks/go/sdk.go (about) 1 // Copyright 2017 Google LLC All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package sdk is the Go game server sdk. 16 package sdk 17 18 import ( 19 "context" 20 "fmt" 21 "io" 22 "os" 23 "time" 24 25 "github.com/pkg/errors" 26 "google.golang.org/grpc/credentials/insecure" 27 28 "google.golang.org/grpc" 29 30 "agones.dev/agones/pkg/sdk" 31 ) 32 33 // GameServerCallback is a function definition to be called 34 // when a GameServer CRD has been changed. 35 type GameServerCallback func(gs *sdk.GameServer) 36 37 // SDK is an instance of the Agones SDK. 38 type SDK struct { 39 client sdk.SDKClient 40 ctx context.Context 41 health sdk.SDK_HealthClient 42 alpha *Alpha 43 beta *Beta 44 } 45 46 // ErrorLog is a function to log the error. 47 type ErrorLog func(string, error) 48 49 // Logger is a pluggable function that outputs the error message to standard error. 50 var Logger ErrorLog = func(msg string, err error) { 51 fmt.Fprintf(os.Stderr, "%s: %s\n", msg, err) 52 } 53 54 // NewSDK starts a new SDK instance, defaulting to a connection at "localhost:9357" 55 // unless overridden by "AGONES_SDK_GRPC_HOST" and "AGONES_SDK_GRPC_PORT" environment variables. 56 // Blocks until connection and handshake are made. 57 // Times out after 30 seconds. 58 func NewSDK() (*SDK, error) { 59 host := os.Getenv("AGONES_SDK_GRPC_HOST") 60 if host == "" { 61 host = "localhost" 62 } 63 64 port := os.Getenv("AGONES_SDK_GRPC_PORT") 65 if port == "" { 66 port = "9357" 67 } 68 addr := fmt.Sprintf("%s:%s", host, port) 69 s := &SDK{ 70 ctx: context.Background(), 71 } 72 // Block for at least 30 seconds. 73 ctx, cancel := context.WithTimeout(s.ctx, 30*time.Second) 74 defer cancel() 75 // nolint: staticcheck 76 conn, err := grpc.DialContext(ctx, addr, grpc.WithBlock(), grpc.WithTransportCredentials(insecure.NewCredentials())) 77 if err != nil { 78 return s, errors.Wrapf(err, "could not connect to %s", addr) 79 } 80 s.client = sdk.NewSDKClient(conn) 81 s.health, err = s.client.Health(s.ctx) 82 s.alpha = newAlpha(conn) 83 s.beta = newBeta(conn) 84 return s, errors.Wrap(err, "could not set up health check") 85 } 86 87 // Alpha returns the Alpha SDK. 88 func (s *SDK) Alpha() *Alpha { 89 return s.alpha 90 } 91 92 // Beta returns the Beta SDK. 93 func (s *SDK) Beta() *Beta { 94 return s.beta 95 } 96 97 // Ready marks the Game Server as ready to receive connections. 98 func (s *SDK) Ready() error { 99 _, err := s.client.Ready(s.ctx, &sdk.Empty{}) 100 return errors.Wrap(err, "could not send Ready message") 101 } 102 103 // Allocate self marks this gameserver as Allocated. 104 func (s *SDK) Allocate() error { 105 _, err := s.client.Allocate(s.ctx, &sdk.Empty{}) 106 return errors.Wrap(err, "could not mark self as Allocated") 107 } 108 109 // Shutdown marks the Game Server as ready to shutdown. 110 func (s *SDK) Shutdown() error { 111 _, err := s.client.Shutdown(s.ctx, &sdk.Empty{}) 112 return errors.Wrapf(err, "could not send Shutdown message") 113 } 114 115 // Reserve marks the Game Server as Reserved for a given duration, at which point 116 // it will return the GameServer to a Ready state. 117 // Do note, the smallest unit available in the time.Duration argument is a second. 118 func (s *SDK) Reserve(d time.Duration) error { 119 _, err := s.client.Reserve(s.ctx, &sdk.Duration{Seconds: int64(d.Seconds())}) 120 return errors.Wrap(err, "could not send Reserve message") 121 } 122 123 // Health sends a ping to the sidecar health check to indicate that this Game Server is healthy. 124 func (s *SDK) Health() error { 125 return errors.Wrap(s.health.Send(&sdk.Empty{}), "could not send Health ping") 126 } 127 128 // SetLabel sets a metadata label on the `GameServer` with the prefix "agones.dev/sdk-". 129 func (s *SDK) SetLabel(key, value string) error { 130 kv := &sdk.KeyValue{Key: key, Value: value} 131 _, err := s.client.SetLabel(s.ctx, kv) 132 return errors.Wrap(err, "could not set label") 133 } 134 135 // SetAnnotation sets a metadata annotation on the `GameServer` with the prefix "agones.dev/sdk-". 136 func (s *SDK) SetAnnotation(key, value string) error { 137 kv := &sdk.KeyValue{Key: key, Value: value} 138 _, err := s.client.SetAnnotation(s.ctx, kv) 139 return errors.Wrap(err, "could not set annotation") 140 } 141 142 // GameServer retrieve the GameServer details. 143 func (s *SDK) GameServer() (*sdk.GameServer, error) { 144 gs, err := s.client.GetGameServer(s.ctx, &sdk.Empty{}) 145 return gs, errors.Wrap(err, "could not retrieve gameserver") 146 } 147 148 // WatchGameServer asynchronously calls the given GameServerCallback with the current GameServer 149 // configuration when the backing GameServer configuration is updated. 150 // This function can be called multiple times to add more than one GameServerCallback. 151 func (s *SDK) WatchGameServer(f GameServerCallback) error { 152 stream, err := s.client.WatchGameServer(s.ctx, &sdk.Empty{}) 153 if err != nil { 154 return errors.Wrap(err, "could not watch gameserver") 155 } 156 log := func(gs *sdk.GameServer, msg string, err error) { 157 if gs == nil || gs.ObjectMeta.DeletionTimestamp == 0 { 158 return 159 } 160 Logger(msg, err) 161 } 162 go func() { 163 for { 164 var gs *sdk.GameServer 165 gs, err = stream.Recv() 166 if err != nil { 167 if err == io.EOF { 168 log(gs, "gameserver event stream EOF received", nil) 169 return 170 } 171 log(gs, "error watching GameServer", err) 172 // This is to wait for the reconnection, and not peg the CPU at 100%. 173 time.Sleep(time.Second) 174 continue 175 } 176 f(gs) 177 } 178 }() 179 return nil 180 }