Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changes sdk-server to Patch instead of Update #3803

Merged
merged 15 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion install/helm/agones/templates/serviceaccounts/sdk.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ rules:
verbs: ["create", "patch"]
- apiGroups: ["agones.dev"]
resources: ["gameservers"]
verbs: ["list", "update", "watch"]
verbs: ["list", "patch", "watch"]
---
{{- range .Values.gameservers.namespaces }}
apiVersion: rbac.authorization.k8s.io/v1
Expand Down
2 changes: 1 addition & 1 deletion install/yaml/install.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16691,7 +16691,7 @@ rules:
verbs: ["create", "patch"]
- apiGroups: ["agones.dev"]
resources: ["gameservers"]
verbs: ["list", "update", "watch"]
verbs: ["list", "patch", "watch"]
---
# Source: agones/templates/service/allocation.yaml
# Bind the agones-allocator ServiceAccount to the agones-allocator ClusterRole
Expand Down
14 changes: 11 additions & 3 deletions pkg/apis/agones/v1/gameserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -879,8 +879,10 @@ func (gs *GameServer) CountPortsForRange(name string, f func(policy PortPolicy)
return count
}

// Patch creates a JSONPatch to move the current GameServer
// to the passed in delta GameServer
// Patch creates a JSONPatch to move the current GameServer to the passed in delta GameServer.
// Returned Patch includes a "test" operation that will cause the GameServers.Patch() operation to
// fail if the Game Server has been updated (ResourceVersion has changed) in between when the Patch
// was created and applied.
func (gs *GameServer) Patch(delta *GameServer) ([]byte, error) {
var result []byte

Expand All @@ -899,7 +901,13 @@ func (gs *GameServer) Patch(delta *GameServer) ([]byte, error) {
return result, errors.Wrapf(err, "error creating patch for GameServer %s", gs.ObjectMeta.Name)
}

result, err = json.Marshal(patch)
// Per https://jsonpatch.com/ "Tests that the specified value is set in the document. If the test
// fails, then the patch as a whole should not apply."
// Used here to check the object has not been updated (has not changed ResourceVersion).
patches := []jsonpatch.JsonPatchOperation{{Operation: "test", Path: "/metadata/resourceVersion", Value: gs.ObjectMeta.ResourceVersion}}
patches = append(patches, patch...)

result, err = json.Marshal(patches)
return result, errors.Wrapf(err, "error creating json for patch for GameServer %s", gs.ObjectMeta.Name)
}

Expand Down
3 changes: 2 additions & 1 deletion pkg/apis/agones/v1/gameserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1451,7 +1451,7 @@ func TestGameServerCountPortsForRange(t *testing.T) {
}

func TestGameServerPatch(t *testing.T) {
fixture := &GameServer{ObjectMeta: metav1.ObjectMeta{Name: "lucy"},
fixture := &GameServer{ObjectMeta: metav1.ObjectMeta{Name: "lucy", ResourceVersion: "1234"},
Spec: GameServerSpec{Container: "goat"}}

delta := fixture.DeepCopy()
Expand All @@ -1461,6 +1461,7 @@ func TestGameServerPatch(t *testing.T) {
assert.Nil(t, err)

assert.Contains(t, string(patch), `{"op":"replace","path":"/spec/container","value":"bear"}`)
assert.Contains(t, string(patch), `{"op":"test","path":"/metadata/resourceVersion","value":"1234"}`)
}

func TestGameServerGetDevAddress(t *testing.T) {
Expand Down
47 changes: 29 additions & 18 deletions pkg/sdkserver/sdkserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
apiequality "k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/scheme"
Expand Down Expand Up @@ -355,7 +356,6 @@ func (s *SDKServer) updateState(ctx context.Context) error {
}
s.gsUpdateMutex.RUnlock()

gameServers := s.gameServerGetter.GameServers(s.namespace)
gs, err := s.gameServer()
if err != nil {
return err
Expand Down Expand Up @@ -406,7 +406,7 @@ func (s *SDKServer) updateState(ctx context.Context) error {
gsCopy.ObjectMeta.Annotations[gameserverallocations.LastAllocatedAnnotationKey] = string(ts)
}

gs, err = gameServers.Update(ctx, gsCopy, metav1.UpdateOptions{})
gs, err = s.patchGameServer(ctx, gs, gsCopy)
if err != nil {
return errors.Wrapf(err, "could not update GameServer %s/%s to state %s", s.namespace, s.gameServerName, gsCopy.Status.State)
}
Expand Down Expand Up @@ -449,6 +449,17 @@ func (s *SDKServer) gameServer() (*agonesv1.GameServer, error) {
return gs, nil
}

// patchGameServer is a helper function to create and apply a patch update, so the changes in
// gsCopy are applied to the original gs.
func (s *SDKServer) patchGameServer(ctx context.Context, gs, gsCopy *agonesv1.GameServer) (*agonesv1.GameServer, error) {
patch, err := gs.Patch(gsCopy)
zmerlynn marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return nil, err
}

return s.gameServerGetter.GameServers(s.namespace).Patch(ctx, gs.GetObjectMeta().GetName(), types.JSONPatchType, patch, metav1.PatchOptions{})
}

// updateLabels updates the labels on this GameServer to the ones persisted in SDKServer,
// i.e. SDKServer.gsLabels, with the prefix of "agones.dev/sdk-"
func (s *SDKServer) updateLabels(ctx context.Context) error {
Expand All @@ -469,7 +480,7 @@ func (s *SDKServer) updateLabels(ctx context.Context) error {
}
s.gsUpdateMutex.RUnlock()

_, err = s.gameServerGetter.GameServers(s.namespace).Update(ctx, gsCopy, metav1.UpdateOptions{})
_, err = s.patchGameServer(ctx, gs, gsCopy)
return err
}

Expand All @@ -493,7 +504,7 @@ func (s *SDKServer) updateAnnotations(ctx context.Context) error {
}
s.gsUpdateMutex.RUnlock()

_, err = s.gameServerGetter.GameServers(s.namespace).Update(ctx, gsCopy, metav1.UpdateOptions{})
_, err = s.patchGameServer(ctx, gs, gsCopy)
return err
}

Expand Down Expand Up @@ -815,7 +826,7 @@ func (s *SDKServer) GetCounter(ctx context.Context, in *beta.GetCounterRequest)
return nil, errors.Errorf("counter not found: %s", in.Name)
}
s.logger.WithField("Get Counter", counter).Debugf("Got Counter %s", in.Name)
protoCounter := beta.Counter{Name: in.Name, Count: counter.Count, Capacity: counter.Capacity}
protoCounter := &beta.Counter{Name: in.Name, Count: counter.Count, Capacity: counter.Capacity}
// If there are batched changes that have not yet been applied, apply them to the Counter.
// This does NOT validate batched the changes.
if counterUpdate, ok := s.gsCounterUpdates[in.Name]; ok {
Expand All @@ -837,7 +848,7 @@ func (s *SDKServer) GetCounter(ctx context.Context, in *beta.GetCounterRequest)
s.logger.WithField("Get Counter", counter).Debugf("Applied Batched Counter Updates %v", counterUpdate)
}

return &protoCounter, nil
return protoCounter, nil
}

// UpdateCounter collapses all UpdateCounterRequests for a given Counter into a single request.
Expand Down Expand Up @@ -973,7 +984,7 @@ func (s *SDKServer) updateCounter(ctx context.Context) error {
names = append(names, name)
}

gs, err = s.gameServerGetter.GameServers(s.namespace).Update(ctx, gsCopy, metav1.UpdateOptions{})
gs, err = s.patchGameServer(ctx, gs, gsCopy)
if err != nil {
return err
}
Expand Down Expand Up @@ -1204,7 +1215,7 @@ func (s *SDKServer) updateList(ctx context.Context) error {
names = append(names, name)
}

gs, err = s.gameServerGetter.GameServers(s.namespace).Update(ctx, gsCopy, metav1.UpdateOptions{})
gs, err = s.patchGameServer(ctx, gs, gsCopy)
if err != nil {
return err
}
Expand Down Expand Up @@ -1357,12 +1368,12 @@ func (s *SDKServer) updatePlayerCapacity(ctx context.Context) error {
gsCopy.Status.Players.Capacity = s.gsPlayerCapacity
s.gsUpdateMutex.RUnlock()

gs, err = s.gameServerGetter.GameServers(s.namespace).Update(ctx, gsCopy, metav1.UpdateOptions{})
if err != nil {
return err
gs, err = s.patchGameServer(ctx, gs, gsCopy)
if err == nil {
s.recorder.Event(gs, corev1.EventTypeNormal, "PlayerCapacity", fmt.Sprintf("Set to %d", gs.Status.Players.Capacity))
}
s.recorder.Event(gs, corev1.EventTypeNormal, "PlayerCapacity", fmt.Sprintf("Set to %d", gs.Status.Players.Capacity))
return nil

return err
}

// updateConnectedPlayers updates the Player IDs and Count fields in the GameServer's Status.
Expand Down Expand Up @@ -1390,12 +1401,12 @@ func (s *SDKServer) updateConnectedPlayers(ctx context.Context) error {
return nil
}

gs, err = s.gameServerGetter.GameServers(s.namespace).Update(ctx, gsCopy, metav1.UpdateOptions{})
if err != nil {
return err
gs, err = s.patchGameServer(ctx, gs, gsCopy)
if err == nil {
s.recorder.Event(gs, corev1.EventTypeNormal, "PlayerCount", fmt.Sprintf("Set to %d", gs.Status.Players.Count))
}
s.recorder.Event(gs, corev1.EventTypeNormal, "PlayerCount", fmt.Sprintf("Set to %d", gs.Status.Players.Count))
return nil

return err
}

// NewSDKServerContext returns a Context that cancels when SIGTERM or os.Interrupt
Expand Down
Loading
Loading