@@ -16,14 +16,17 @@ package gameservers
1616
1717import (
1818 "io"
19+ "os"
1920 "sync"
2021 "time"
2122
2223 "agones.dev/agones/pkg/apis/stable/v1alpha1"
2324 "agones.dev/agones/pkg/sdk"
25+ "github.com/fsnotify/fsnotify"
2426 "github.com/pkg/errors"
2527 "github.com/sirupsen/logrus"
2628 "golang.org/x/net/context"
29+ "k8s.io/apimachinery/pkg/util/yaml"
2730)
2831
2932var (
@@ -52,23 +55,52 @@ var (
5255// is being run for local development, and doesn't connect to the
5356// Kubernetes cluster
5457type LocalSDKServer struct {
58+ gsMutex sync.RWMutex
5559 gs * sdk.GameServer
56- watchPeriod time.Duration
5760 update chan struct {}
5861 updateObservers sync.Map
5962}
6063
6164// NewLocalSDKServer returns the default LocalSDKServer
62- func NewLocalSDKServer (gs * v1alpha1.GameServer ) * LocalSDKServer {
65+ // TODO: update all the tests at a later date
66+ func NewLocalSDKServer (filePath string ) (* LocalSDKServer , error ) {
6367 l := & LocalSDKServer {
68+ gsMutex : sync.RWMutex {},
6469 gs : defaultGs ,
65- watchPeriod : 5 * time .Second ,
6670 update : make (chan struct {}),
6771 updateObservers : sync.Map {},
6872 }
6973
70- if gs != nil {
71- l .gs = convert (gs )
74+ if filePath != "" {
75+ err := l .setGameServerFromFilePath (filePath )
76+ if err != nil {
77+ return l , err
78+ }
79+
80+ watcher , err := fsnotify .NewWatcher ()
81+ if err != nil {
82+ return l , err
83+ }
84+
85+ go func () {
86+ for event := range watcher .Events {
87+ if event .Op == fsnotify .Write {
88+ logrus .WithField ("event" , event ).Info ("File has been changed!" )
89+ err := l .setGameServerFromFilePath (filePath )
90+ if err != nil {
91+ logrus .WithError (err ).Error ("error setting GameServer from file" )
92+ continue
93+ }
94+ logrus .Info ("Sending watched GameServer!" )
95+ l .update <- struct {}{}
96+ }
97+ }
98+ }()
99+
100+ err = watcher .Add (filePath )
101+ if err != nil {
102+ logrus .WithError (err ).WithField ("filePath" , filePath ).Error ("error adding watcher" )
103+ }
72104 }
73105
74106 go func () {
@@ -81,7 +113,7 @@ func NewLocalSDKServer(gs *v1alpha1.GameServer) *LocalSDKServer {
81113 }
82114 }()
83115
84- return l
116+ return l , nil
85117}
86118
87119// Ready logs that the Ready request has been received
@@ -114,6 +146,8 @@ func (l *LocalSDKServer) Health(stream sdk.SDK_HealthServer) error {
114146// SetLabel applies a Label to the backing GameServer metadata
115147func (l * LocalSDKServer ) SetLabel (_ context.Context , kv * sdk.KeyValue ) (* sdk.Empty , error ) {
116148 logrus .WithField ("values" , kv ).Info ("Setting label" )
149+ l .gsMutex .Lock ()
150+ defer l .gsMutex .Unlock ()
117151
118152 if l .gs .ObjectMeta == nil {
119153 l .gs .ObjectMeta = & sdk.GameServer_ObjectMeta {}
@@ -130,6 +164,8 @@ func (l *LocalSDKServer) SetLabel(_ context.Context, kv *sdk.KeyValue) (*sdk.Emp
130164// SetAnnotation applies a Annotation to the backing GameServer metadata
131165func (l * LocalSDKServer ) SetAnnotation (_ context.Context , kv * sdk.KeyValue ) (* sdk.Empty , error ) {
132166 logrus .WithField ("values" , kv ).Info ("Setting annotation" )
167+ l .gsMutex .Lock ()
168+ defer l .gsMutex .Unlock ()
133169
134170 if l .gs .ObjectMeta == nil {
135171 l .gs .ObjectMeta = & sdk.GameServer_ObjectMeta {}
@@ -146,6 +182,8 @@ func (l *LocalSDKServer) SetAnnotation(_ context.Context, kv *sdk.KeyValue) (*sd
146182// GetGameServer returns a dummy game server.
147183func (l * LocalSDKServer ) GetGameServer (context.Context , * sdk.Empty ) (* sdk.GameServer , error ) {
148184 logrus .Info ("getting GameServer details" )
185+ l .gsMutex .RLock ()
186+ defer l .gsMutex .RUnlock ()
149187 return l .gs , nil
150188}
151189
@@ -156,24 +194,14 @@ func (l *LocalSDKServer) WatchGameServer(_ *sdk.Empty, stream sdk.SDK_WatchGameS
156194
157195 defer func () {
158196 l .updateObservers .Delete (observer )
159- close (observer )
160197 }()
161198
162199 l .updateObservers .Store (observer , true )
163200
164- // on connect, send 3 events, as advertised
165- go func () {
166- times := 3
167-
168- for i := 0 ; i < times ; i ++ {
169- logrus .Info ("Sending watched GameServer!" )
170- l .update <- struct {}{}
171- time .Sleep (l .watchPeriod )
172- }
173- }()
174-
175201 for range observer {
202+ l .gsMutex .RLock ()
176203 err := stream .Send (l .gs )
204+ l .gsMutex .RUnlock ()
177205 if err != nil {
178206 logrus .WithError (err ).Error ("error sending gameserver" )
179207 return err
@@ -182,3 +210,40 @@ func (l *LocalSDKServer) WatchGameServer(_ *sdk.Empty, stream sdk.SDK_WatchGameS
182210
183211 return nil
184212}
213+
214+ // Close tears down all the things
215+ func (l * LocalSDKServer ) Close () {
216+ l .updateObservers .Range (func (observer , _ interface {}) bool {
217+ close (observer .(chan struct {}))
218+ return true
219+ })
220+ }
221+
222+ func (l * LocalSDKServer ) setGameServerFromFilePath (filePath string ) error {
223+ logrus .WithField ("filePath" , filePath ).Info ("Reading GameServer configuration" )
224+
225+ reader , err := os .Open (filePath ) // nolint: gosec
226+ defer reader .Close () // nolint: megacheck
227+
228+ if err != nil {
229+ return err
230+ }
231+
232+ var gs v1alpha1.GameServer
233+ // 4096 is the number of bytes the YAMLOrJSONDecoder goes looking
234+ // into the file to determine if it's JSON or YAML
235+ // (JSON == has whitespace followed by an open brace).
236+ // The Kubernetes uses 4096 bytes as its default, so that's what we'll
237+ // use as well.
238+ // https://github.com/kubernetes/kubernetes/blob/master/plugin/pkg/admission/podnodeselector/admission.go#L86
239+ decoder := yaml .NewYAMLOrJSONDecoder (reader , 4096 )
240+ err = decoder .Decode (& gs )
241+ if err != nil {
242+ return err
243+ }
244+
245+ l .gsMutex .Lock ()
246+ defer l .gsMutex .Unlock ()
247+ l .gs = convert (& gs )
248+ return nil
249+ }
0 commit comments