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

Rolling updates for Fleets #213

Merged
merged 1 commit into from
May 23, 2018
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
14 changes: 10 additions & 4 deletions docs/fleet_spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ metadata:
spec:
replicas: 2
strategy:
type: Recreate
type: RollingUpdate
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
template:
metadata:
labels:
Expand Down Expand Up @@ -51,9 +54,12 @@ The `spec` field is the actual `Fleet` specification and it is composed as follo

- `replicas` is the number of `GameServers` to keep Ready or Allocated in this Fleet
- `strategy` is the `GameServer` replacement strategy for when the `GameServer` template is edited.
`type` "Recreate" is the only option. A "RollingUpdate" option will be implemented soon.
- `Recreate` terminates all non-allocated `GameServers`, and starts up a new set with
the new `GameServer` configuration to replace them.
- `type` is replacement strategy for when the GameServer template is changed. Default option is "RollingUpdate", but "Recreate" is also available.
- `RollingUpdate` will increment by `maxSurge` value on each iteration, while decrementing by `maxUnavailable` on each iteration, until all GameServers have been switched from one version to another.
- `Recreate` terminates all non-allocated `GameServers`, and starts up a new set with the new `GameServer` configuration to replace them.
- `rollingUpdate` is only relevant when `type: RollingUpdate`
- `maxSurge` is the amount to increment the new GameServers by. Defaults to 25%
- `maxUnavailable` is the amount to decrements GameServers by. Defaults to 25%
- `template` a full `GameServer` configuration template.
See the [GameServer](./gameserver_spec.md) reference for all available fields.

Expand Down
13 changes: 10 additions & 3 deletions examples/fleet.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,17 @@ spec:
# a GameServer template - see:
# https://github.com/GoogleCloudPlatform/agones/blob/master/docs/gameserver_spec.md for all the options
strategy:
# The replacement strategy for when the GameServer template is changed. Current option is "Recreate",
# "RollingUpdate" will come at a later date.
# The replacement strategy for when the GameServer template is changed. Default option is "RollingUpdate",
# "RollingUpdate" will increment by maxSurge value on each iteration, while decrementing by maxUnavailable on each
# iteration, until all GameServers have been switched from one version to another.
# "Recreate" terminates all non-allocated GameServers, and starts up a new set with the new details to replace them.
type: Recreate
type: RollingUpdate
# Only relevant when `type: RollingUpdate`
rollingUpdate:
# the amount to increment the new GameServers by. Defaults to 25%
maxSurge: 25%
# the amount to decrements GameServers by. Defaults to 25%
maxUnavailable: 25%
template:
# GameServer metadata
metadata:
Expand Down
60 changes: 51 additions & 9 deletions pkg/apis/stable/v1alpha1/fleet.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"agones.dev/agones/pkg/apis/stable"
appsv1 "k8s.io/api/apps/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
)

const (
Expand Down Expand Up @@ -74,7 +75,6 @@ func (f *Fleet) GameServerSet() *GameServerSet {
gsSet := &GameServerSet{
ObjectMeta: *f.Spec.Template.ObjectMeta.DeepCopy(),
Spec: GameServerSetSpec{
Replicas: f.Spec.Replicas,
Template: f.Spec.Template,
},
}
Expand Down Expand Up @@ -102,18 +102,60 @@ func (f *Fleet) GameServerSet() *GameServerSet {
// ApplyDefaults applies default values to the Fleet
func (f *Fleet) ApplyDefaults() {
if f.Spec.Strategy.Type == "" {
f.Spec.Strategy.Type = appsv1.RecreateDeploymentStrategyType
f.Spec.Strategy.Type = appsv1.RollingUpdateDeploymentStrategyType
}

if f.Spec.Strategy.Type == appsv1.RollingUpdateDeploymentStrategyType {
if f.Spec.Strategy.RollingUpdate == nil {
f.Spec.Strategy.RollingUpdate = &appsv1.RollingUpdateDeployment{}
}

def := intstr.FromString("25%")
if f.Spec.Strategy.RollingUpdate.MaxSurge == nil {
f.Spec.Strategy.RollingUpdate.MaxSurge = &def
}
if f.Spec.Strategy.RollingUpdate.MaxUnavailable == nil {
f.Spec.Strategy.RollingUpdate.MaxUnavailable = &def
}
}
}

// UpperBoundReplicas returns whichever is smaller,
// the value i, or the f.Spec.Replicas.
func (f *Fleet) UpperBoundReplicas(i int32) int32 {
if i > f.Spec.Replicas {
return f.Spec.Replicas
}
return i
}

// LowerBoundReplicas returns 0 (the minimum value for
// replicas) if i is < 0
func (f *Fleet) LowerBoundReplicas(i int32) int32 {
if i < 0 {
return 0
}
return i
}

// SumStatusAllocatedReplicas returns the total number of
// Status.AllocatedReplicas in the list of GameServerSets
func SumStatusAllocatedReplicas(list []*GameServerSet) int32 {
total := int32(0)
for _, gsSet := range list {
total += gsSet.Status.AllocatedReplicas
}

return total
}

// ReplicasMinusSumAllocated returns the number of Replicas that are
// currently configured against this fleet, subtracting the number
// of allocated GameServers counted on this list of GameServerSets
func (f *Fleet) ReplicasMinusSumAllocated(list []*GameServerSet) int32 {
result := f.Spec.Replicas
// SumStatusReplicas returns the total number of
// Status.Replicas in the list of GameServerSets
func SumStatusReplicas(list []*GameServerSet) int32 {
total := int32(0)
for _, gsSet := range list {
result -= gsSet.Status.AllocatedReplicas
total += gsSet.Status.Replicas
}

return result
return total
}
52 changes: 38 additions & 14 deletions pkg/apis/stable/v1alpha1/fleet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,32 +50,56 @@ func TestFleetGameServerSetGameServer(t *testing.T) {
assert.Equal(t, f.ObjectMeta.Namespace, gsSet.ObjectMeta.Namespace)
assert.Equal(t, f.ObjectMeta.Name+"-", gsSet.ObjectMeta.GenerateName)
assert.Equal(t, f.ObjectMeta.Name, gsSet.ObjectMeta.Labels[FleetGameServerSetLabel])
assert.Equal(t, f.Spec.Replicas, gsSet.Spec.Replicas)
assert.Equal(t, int32(0), gsSet.Spec.Replicas)
assert.Equal(t, f.Spec.Template, gsSet.Spec.Template)
assert.True(t, v1.IsControlledBy(gsSet, &f))
}

func TestFleetReplicasMinusSumAllocated(t *testing.T) {
f := Fleet{
Spec: FleetSpec{Replicas: 10},
}
func TestFleetApplyDefaults(t *testing.T) {
f := &Fleet{}

// gate
assert.EqualValues(t, "", f.Spec.Strategy.Type)

f.ApplyDefaults()
assert.Equal(t, appsv1.RollingUpdateDeploymentStrategyType, f.Spec.Strategy.Type)
assert.Equal(t, "25%", f.Spec.Strategy.RollingUpdate.MaxUnavailable.String())
assert.Equal(t, "25%", f.Spec.Strategy.RollingUpdate.MaxSurge.String())
}

func TestFleetUpperBoundReplicas(t *testing.T) {
f := &Fleet{Spec: FleetSpec{Replicas: 10}}

assert.Equal(t, int32(10), f.ReplicasMinusSumAllocated(nil))
assert.Equal(t, int32(10), f.UpperBoundReplicas(12))
assert.Equal(t, int32(10), f.UpperBoundReplicas(10))
assert.Equal(t, int32(5), f.UpperBoundReplicas(5))
}

func TestFleetLowerBoundReplicas(t *testing.T) {
f := &Fleet{Spec: FleetSpec{Replicas: 10}}

assert.Equal(t, int32(5), f.LowerBoundReplicas(5))
assert.Equal(t, int32(0), f.LowerBoundReplicas(0))
assert.Equal(t, int32(0), f.LowerBoundReplicas(-5))
}

func TestSumStatusAllocatedReplicas(t *testing.T) {
f := Fleet{}
gsSet1 := f.GameServerSet()
gsSet1.Status.AllocatedReplicas = 2

gsSet2 := f.GameServerSet()
gsSet2.Status.AllocatedReplicas = 3

assert.Equal(t, int32(5), f.ReplicasMinusSumAllocated([]*GameServerSet{gsSet1, gsSet2}))
assert.Equal(t, int32(5), SumStatusAllocatedReplicas([]*GameServerSet{gsSet1, gsSet2}))
}

func TestFleetApplyDefaults(t *testing.T) {
f := &Fleet{}

// gate
assert.EqualValues(t, "", f.Spec.Strategy.Type)
func TestSumStatusReplicas(t *testing.T) {
fixture := []*GameServerSet{
{Status: GameServerSetStatus{Replicas: 10}},
{Status: GameServerSetStatus{Replicas: 15}},
{Status: GameServerSetStatus{Replicas: 5}},
}

f.ApplyDefaults()
assert.Equal(t, appsv1.RecreateDeploymentStrategyType, f.Spec.Strategy.Type)
assert.Equal(t, int32(30), SumStatusReplicas(fixture))
}
Loading