Skip to content

Commit

Permalink
feat: add autoUpdateStrategy & new Type OnlyNew
Browse files Browse the repository at this point in the history
Signed-off-by: ChrisLiu <[email protected]>
  • Loading branch information
chrisliu1995 committed Dec 18, 2023
1 parent 250bd86 commit 578d53d
Show file tree
Hide file tree
Showing 8 changed files with 252 additions and 17 deletions.
15 changes: 15 additions & 0 deletions apis/v1alpha1/gameserverset_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,23 @@ type UpdateStrategy struct {
// RollingUpdate is used to communicate parameters when Type is RollingUpdateStatefulSetStrategyType.
// +optional
RollingUpdate *RollingUpdateStatefulSetStrategy `json:"rollingUpdate,omitempty"`
// AutoUpdateStrategy means that the update process will be performed automatically without user intervention.
// +optional
AutoUpdateStrategy *AutoUpdateStrategy `json:"autoUpdateStrategy,omitempty"`
}

type AutoUpdateStrategy struct {
//+kubebuilder:validation:Required
Type AutoUpdateStrategyType `json:"type"`
}

type AutoUpdateStrategyType string

const (
// OnlyNewAutoUpdateStrategyType indicates exist GameServers will never be updated, new GameServers will be created in new template.
OnlyNewAutoUpdateStrategyType AutoUpdateStrategyType = "OnlyNew"
)

type RollingUpdateStatefulSetStrategy struct {
// Partition indicates the ordinal at which the StatefulSet should be partitioned by default.
// But if unorderedUpdate has been set:
Expand Down
20 changes: 20 additions & 0 deletions apis/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions config/crd/bases/game.kruise.io_gameserversets.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,15 @@ spec:
type: array
updateStrategy:
properties:
autoUpdateStrategy:
description: AutoUpdateStrategy means that the update process
will be performed automatically without user intervention.
properties:
type:
type: string
required:
- type
type: object
rollingUpdate:
description: RollingUpdate is used to communicate parameters when
Type is RollingUpdateStatefulSetStrategyType.
Expand Down
10 changes: 10 additions & 0 deletions pkg/controllers/gameserverset/gameserverset_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,16 @@ func (r *GameServerSetReconciler) Reconcile(ctx context.Context, req ctrl.Reques
return reconcile.Result{}, nil
}

// adjust partition when autoUpdate
if gsm.IsNeedToAdjustPartition() {
err = gsm.AdjustPartition()
if err != nil {
klog.Errorf("GameServerSet %s failed to adjust partition in %s,because of %s.", namespacedName.Name, namespacedName.Namespace, err.Error())
return reconcile.Result{}, err
}
return reconcile.Result{}, nil
}

// update workload
if gsm.IsNeedToUpdateWorkload() {
err = gsm.UpdateWorkload()
Expand Down
22 changes: 22 additions & 0 deletions pkg/controllers/gameserverset/gameserverset_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ type Control interface {
SyncPodProbeMarker() error
SyncGameServerReplicas() error
GetReplicasAfterKilling() *int32
IsNeedToAdjustPartition() bool
AdjustPartition() error
}

const (
Expand Down Expand Up @@ -77,6 +79,26 @@ func NewGameServerSetManager(gss *gameKruiseV1alpha1.GameServerSet, asts *kruise
}
}

func (manager *GameServerSetManager) AdjustPartition() error {
gss := manager.gameServerSet
if gss.Spec.UpdateStrategy.RollingUpdate == nil {
gss.Spec.UpdateStrategy.RollingUpdate = &gameKruiseV1alpha1.RollingUpdateStatefulSetStrategy{}
}
gss.Spec.UpdateStrategy.RollingUpdate.Partition = gss.Spec.Replicas
return manager.client.Update(context.Background(), gss)
}

func (manager *GameServerSetManager) IsNeedToAdjustPartition() bool {
gss := manager.gameServerSet
if gss.Spec.UpdateStrategy.AutoUpdateStrategy == nil {
return false
}
if gss.Spec.UpdateStrategy.AutoUpdateStrategy.Type == gameKruiseV1alpha1.OnlyNewAutoUpdateStrategyType && (gss.Spec.UpdateStrategy.RollingUpdate == nil || gss.Spec.UpdateStrategy.RollingUpdate.Partition == nil || *gss.Spec.Replicas != *gss.Spec.UpdateStrategy.RollingUpdate.Partition) {
return true
}
return false
}

func (manager *GameServerSetManager) GetReplicasAfterKilling() *int32 {
gss := manager.gameServerSet
asts := manager.asts
Expand Down
135 changes: 135 additions & 0 deletions pkg/controllers/gameserverset/gameserverset_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -997,3 +997,138 @@ func TestNumberToKill(t *testing.T) {
}
}
}

func TestIsNeedToAdjustPartition(t *testing.T) {
tests := []struct {
gss *gameKruiseV1alpha1.GameServerSet
result bool
}{
// case 0
{
gss: &gameKruiseV1alpha1.GameServerSet{
Spec: gameKruiseV1alpha1.GameServerSetSpec{
Replicas: pointer.Int32(3),
UpdateStrategy: gameKruiseV1alpha1.UpdateStrategy{},
},
},
result: false,
},

// case 1
{
gss: &gameKruiseV1alpha1.GameServerSet{
Spec: gameKruiseV1alpha1.GameServerSetSpec{
Replicas: pointer.Int32(3),
UpdateStrategy: gameKruiseV1alpha1.UpdateStrategy{
AutoUpdateStrategy: &gameKruiseV1alpha1.AutoUpdateStrategy{
Type: gameKruiseV1alpha1.OnlyNewAutoUpdateStrategyType,
},
},
},
},
result: true,
},

// case 2
{
gss: &gameKruiseV1alpha1.GameServerSet{
Spec: gameKruiseV1alpha1.GameServerSetSpec{
Replicas: pointer.Int32(3),
UpdateStrategy: gameKruiseV1alpha1.UpdateStrategy{
RollingUpdate: &gameKruiseV1alpha1.RollingUpdateStatefulSetStrategy{
Partition: pointer.Int32(3),
PodUpdatePolicy: kruiseV1beta1.InPlaceIfPossiblePodUpdateStrategyType,
},
AutoUpdateStrategy: &gameKruiseV1alpha1.AutoUpdateStrategy{
Type: gameKruiseV1alpha1.OnlyNewAutoUpdateStrategyType,
},
},
},
},
result: false,
},

// case 3
{
gss: &gameKruiseV1alpha1.GameServerSet{
Spec: gameKruiseV1alpha1.GameServerSetSpec{
Replicas: pointer.Int32(3),
UpdateStrategy: gameKruiseV1alpha1.UpdateStrategy{
RollingUpdate: &gameKruiseV1alpha1.RollingUpdateStatefulSetStrategy{
Partition: pointer.Int32(2),
PodUpdatePolicy: kruiseV1beta1.InPlaceIfPossiblePodUpdateStrategyType,
},
AutoUpdateStrategy: &gameKruiseV1alpha1.AutoUpdateStrategy{
Type: gameKruiseV1alpha1.OnlyNewAutoUpdateStrategyType,
},
},
},
},
result: true,
},
}

for i, test := range tests {
manager := &GameServerSetManager{
gameServerSet: test.gss,
}
actual := manager.IsNeedToAdjustPartition()
expect := test.result
if actual != expect {
t.Errorf("case %d: expect IsNeedToAdjustPartition is %v but actually %v", i, expect, actual)
}
}
}

func TestAdjustPartition(t *testing.T) {
tests := []struct {
gss *gameKruiseV1alpha1.GameServerSet
}{
// case 0
{
gss: &gameKruiseV1alpha1.GameServerSet{
ObjectMeta: metav1.ObjectMeta{
Namespace: "xxx",
Name: "case0",
},
Spec: gameKruiseV1alpha1.GameServerSetSpec{
Replicas: pointer.Int32(3),
},
},
},

// case 1
{
gss: &gameKruiseV1alpha1.GameServerSet{
ObjectMeta: metav1.ObjectMeta{
Namespace: "xxx",
Name: "case1",
},
Spec: gameKruiseV1alpha1.GameServerSetSpec{
Replicas: pointer.Int32(3),
UpdateStrategy: gameKruiseV1alpha1.UpdateStrategy{
RollingUpdate: &gameKruiseV1alpha1.RollingUpdateStatefulSetStrategy{
Partition: pointer.Int32(4),
},
},
},
},
},
}

for i, test := range tests {
objs := []client.Object{test.gss}
c := fake.NewClientBuilder().WithScheme(scheme).WithObjects(objs...).Build()
manager := &GameServerSetManager{
gameServerSet: test.gss,
client: c,
}
if err := manager.AdjustPartition(); err != nil {
t.Error(err)
}

if *manager.gameServerSet.Spec.Replicas != *manager.gameServerSet.Spec.UpdateStrategy.RollingUpdate.Partition {
t.Errorf("case %d: Replicas is not equal with Partition", i)
}
}
}
40 changes: 24 additions & 16 deletions test/e2e/framework/framework.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,15 @@ func (f *Framework) GameServerScale(gss *gamekruiseiov1alpha1.GameServerSet, des
return f.client.PatchGameServerSet(data)
}

func (f *Framework) AutoUpdateOnlyNew() (*gamekruiseiov1alpha1.GameServerSet, error) {
conJson := map[string]interface{}{"spec": map[string]interface{}{"updateStrategy": map[string]interface{}{"autoUpdateStrategy": map[string]string{"type": "OnlyNew"}}}}
data, err := json.Marshal(conJson)
if err != nil {
return nil, err
}
return f.client.PatchGameServerSet(data)
}

func (f *Framework) ImageUpdate(gss *gamekruiseiov1alpha1.GameServerSet, name, image string) (*gamekruiseiov1alpha1.GameServerSet, error) {
var newContainers []corev1.Container
for _, c := range gss.Spec.GameServerTemplate.Spec.Containers {
Expand Down Expand Up @@ -164,7 +173,7 @@ func (f *Framework) WaitForGsCreated(gss *gamekruiseiov1alpha1.GameServerSet) er
})
}

func (f *Framework) WaitForUpdated(gss *gamekruiseiov1alpha1.GameServerSet, name, image string) error {
func (f *Framework) WaitForUpdated(gss *gamekruiseiov1alpha1.GameServerSet, name, image string, updateIds []int) error {
return wait.PollImmediate(10*time.Second, 10*time.Minute,
func() (done bool, err error) {
gssName := gss.GetName()
Expand All @@ -175,24 +184,23 @@ func (f *Framework) WaitForUpdated(gss *gamekruiseiov1alpha1.GameServerSet, name
if err != nil {
return false, err
}
updated := 0

for _, pod := range podList.Items {
for _, c := range pod.Status.ContainerStatuses {
if name == c.Name && strings.Contains(c.Image, image) {
updated++
break
id := util.GetIndexFromGsName(pod.GetName())
if util.IsNumInList(id, updateIds) {
// should be updated
for _, c := range pod.Status.ContainerStatuses {
if name == c.Name && !strings.Contains(c.Image, image) {
return false, nil
}
}
} else {
// should not be updated
for _, c := range pod.Status.ContainerStatuses {
if name == c.Name && strings.Contains(c.Image, image) {
return false, nil
}
}
}
}

if gss.Spec.UpdateStrategy.RollingUpdate == nil || gss.Spec.UpdateStrategy.RollingUpdate.Partition == nil {
if int32(updated) != *gss.Spec.Replicas {
return false, nil
}
} else {
if int32(updated) != *gss.Spec.Replicas-*gss.Spec.UpdateStrategy.RollingUpdate.Partition {
return false, nil
}
}
return true, nil
Expand Down
18 changes: 17 additions & 1 deletion test/e2e/testcase/testcase.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,23 @@ func RunTestCases(f *framework.Framework) {
gss, err = f.ImageUpdate(gss, client.GameContainerName, "nginx:latest")
gomega.Expect(err).To(gomega.BeNil())

err = f.WaitForUpdated(gss, client.GameContainerName, "nginx:latest")
err = f.WaitForUpdated(gss, client.GameContainerName, "nginx:latest", []int{0, 1, 2})
gomega.Expect(err).To(gomega.BeNil())

// auto update
gss, err = f.AutoUpdateOnlyNew()
gomega.Expect(err).To(gomega.BeNil())

gss, err = f.ImageUpdate(gss, client.GameContainerName, "nginx:1.7.9")
gomega.Expect(err).To(gomega.BeNil())

gss, err = f.GameServerScale(gss, 5, nil)
gomega.Expect(err).To(gomega.BeNil())

err = f.ExpectGssCorrect(gss, []int{0, 1, 2, 3, 4})
gomega.Expect(err).To(gomega.BeNil())

err = f.WaitForUpdated(gss, client.GameContainerName, "nginx:1.7.9", []int{3, 4})
gomega.Expect(err).To(gomega.BeNil())
})

Expand Down

0 comments on commit 578d53d

Please sign in to comment.