Skip to content

Commit

Permalink
feat: differentiated updates to GameServers (openkruise#120)
Browse files Browse the repository at this point in the history
Signed-off-by: ChrisLiu <[email protected]>
  • Loading branch information
chrisliu1995 authored Dec 27, 2023
1 parent 4013d3e commit ecf81d9
Show file tree
Hide file tree
Showing 11 changed files with 777 additions and 3 deletions.
15 changes: 15 additions & 0 deletions apis/v1alpha1/gameserver_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,21 @@ type GameServerSpec struct {
UpdatePriority *intstr.IntOrString `json:"updatePriority,omitempty"`
DeletionPriority *intstr.IntOrString `json:"deletionPriority,omitempty"`
NetworkDisabled bool `json:"networkDisabled,omitempty"`
// Containers can be used to make the corresponding GameServer container fields
// different from the fields defined by GameServerTemplate in GameServerSetSpec.
Containers []GameServerContainer `json:"containers,omitempty"`
}

type GameServerContainer struct {
// Name indicates the name of the container to update.
Name string `json:"name"`
// Image indicates the image of the container to update.
// When Image updated, pod.spec.containers[*].image will be updated immediately.
Image string `json:"image,omitempty"`
// Resources indicates the resources of the container to update.
// When Resources updated, pod.spec.containers[*].Resources will be not updated immediately,
// which will be updated when pod recreate.
Resources corev1.ResourceRequirements `json:"resources,omitempty"`
}

type GameServerState string
Expand Down
23 changes: 23 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.

48 changes: 48 additions & 0 deletions config/crd/bases/game.kruise.io_gameservers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,54 @@ spec:
spec:
description: GameServerSpec defines the desired state of GameServer
properties:
containers:
description: Containers can be used to make the corresponding GameServer
container fields different from the fields defined by GameServerTemplate
in GameServerSetSpec.
items:
properties:
image:
description: Image indicates the image of the container to update.
When Image updated, pod.spec.containers[*].image will be updated
immediately.
type: string
name:
description: Name indicates the name of the container to update.
type: string
resources:
description: Resources indicates the resources of the container
to update. When Resources updated, pod.spec.containers[*].Resources
will be not updated immediately, which will be updated when
pod recreate.
properties:
limits:
additionalProperties:
anyOf:
- type: integer
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
description: 'Limits describes the maximum amount of compute
resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
type: object
requests:
additionalProperties:
anyOf:
- type: integer
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
description: 'Requests describes the minimum amount of compute
resources required. If Requests is omitted for a container,
it defaults to Limits if that is explicitly specified,
otherwise to an implementation-defined value. More info:
https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
type: object
type: object
required:
- name
type: object
type: array
deletionPriority:
anyOf:
- type: integer
Expand Down
50 changes: 50 additions & 0 deletions config/crd/bases/game.kruise.io_gameserversets.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,56 @@ spec:
serviceQualityAction:
items:
properties:
containers:
description: Containers can be used to make the corresponding
GameServer container fields different from the fields
defined by GameServerTemplate in GameServerSetSpec.
items:
properties:
image:
description: Image indicates the image of the container
to update. When Image updated, pod.spec.containers[*].image
will be updated immediately.
type: string
name:
description: Name indicates the name of the container
to update.
type: string
resources:
description: Resources indicates the resources of
the container to update. When Resources updated,
pod.spec.containers[*].Resources will be not updated
immediately, which will be updated when pod recreate.
properties:
limits:
additionalProperties:
anyOf:
- type: integer
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
description: 'Limits describes the maximum amount
of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
type: object
requests:
additionalProperties:
anyOf:
- type: integer
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
description: 'Requests describes the minimum
amount of compute resources required. If Requests
is omitted for a container, it defaults to
Limits if that is explicitly specified, otherwise
to an implementation-defined value. More info:
https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
type: object
type: object
required:
- name
type: object
type: array
deletionPriority:
anyOf:
- type: integer
Expand Down
18 changes: 18 additions & 0 deletions docs/en/user_manuals/CRD_field_description.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,24 @@ type GameServerSpec struct {
// Whether to perform network isolation and cut off the access layer network
// Default is false
NetworkDisabled bool `json:"networkDisabled,omitempty"`
// Containers can be used to make the corresponding GameServer container fields
// different from the fields defined by GameServerTemplate in GameServerSetSpec.
Containers []GameServerContainer `json:"containers,omitempty"`
}
type GameServerContainer struct {
// Name indicates the name of the container to update.
Name string `json:"name"`
// Image indicates the image of the container to update.
// When Image updated, pod.spec.containers[*].image will be updated immediately.
Image string `json:"image,omitempty"`
// Resources indicates the resources of the container to update.
// When Resources updated, pod.spec.containers[*].Resources will be not updated immediately,
// which will be updated when pod recreate.
Resources corev1.ResourceRequirements `json:"resources,omitempty"`
}
```

Expand Down
18 changes: 18 additions & 0 deletions docs/中文/用户手册/CRD字段说明.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,25 @@ type GameServerSpec struct {
// 是否进行网络隔离、切断接入层网络,默认为false
NetworkDisabled bool `json:"networkDisabled,omitempty"`
// 使对应的GameServer Containers字段与GameServerSetSpec中GameServerTemplate定义的字段不同,意味着该GameServer可以拥有独立的参数配置。
// 当前支持更改 Image 与 Resources
Containers []GameServerContainer `json:"containers,omitempty"`
}
type GameServerContainer struct {
// Name 表示要更新的容器的名称。
Name string `json:"name"`
// Image 表示要更新的容器的镜像。
// 当Image更新时,pod.spec.containers[*].image会立即更新。
Image string `json:"image,omitempty"`
// Resources 表示要更新的容器的资源。
// 当Resources更新时,pod.spec.containers[*].Resources不会立即更新,它会在pod重建时更新。
Resources corev1.ResourceRequirements `json:"resources,omitempty"`
}
```

### GameServerStatus
Expand Down
36 changes: 34 additions & 2 deletions pkg/controllers/gameserver/gameserver_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,17 @@ func (manager GameServerManager) SyncGsToPod() error {
}
}

if len(newLabels) != 0 || len(newAnnotations) != 0 {
patchPod := map[string]interface{}{"metadata": map[string]map[string]string{"labels": newLabels, "annotations": newAnnotations}}
// sync pod containers when the containers(images) in GameServer are different from that in pod.
containers := manager.syncPodContainers(gs.Spec.Containers, pod.DeepCopy().Spec.Containers)

if len(newLabels) != 0 || len(newAnnotations) != 0 || containers != nil {
patchPod := make(map[string]interface{})
if len(newLabels) != 0 || len(newAnnotations) != 0 {
patchPod["metadata"] = map[string]map[string]string{"labels": newLabels, "annotations": newAnnotations}
}
if containers != nil {
patchPod["spec"] = map[string]interface{}{"containers": containers}
}
patchPodBytes, err := json.Marshal(patchPod)
if err != nil {
return err
Expand Down Expand Up @@ -359,6 +368,29 @@ func syncServiceQualities(serviceQualities []gameKruiseV1alpha1.ServiceQuality,
return spec, newGsConditions
}

func (manager GameServerManager) syncPodContainers(gsContainers []gameKruiseV1alpha1.GameServerContainer, podContainers []corev1.Container) []corev1.Container {
var newContainers []corev1.Container
for _, podContainer := range podContainers {
for _, gsContainer := range gsContainers {
if gsContainer.Name == podContainer.Name {
var newContainer corev1.Container
newContainer.Name = podContainer.Name
changed := false
if gsContainer.Image != "" && gsContainer.Image != podContainer.Image {
newContainer.Image = gsContainer.Image
changed = true
}

if changed {
newContainers = append(newContainers, newContainer)
}
}
}
}

return newContainers
}

func NewGameServerManager(gs *gameKruiseV1alpha1.GameServer, pod *corev1.Pod, c client.Client, recorder record.EventRecorder) Control {
return &GameServerManager{
gameServer: gs,
Expand Down
55 changes: 55 additions & 0 deletions pkg/controllers/gameserver/gameserver_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -688,3 +688,58 @@ func TestSyncNetworkStatus(t *testing.T) {
}
}
}

func TestSyncPodContainers(t *testing.T) {
tests := []struct {
gsContainers []gameKruiseV1alpha1.GameServerContainer
podContainers []corev1.Container
newContainers []corev1.Container
}{
// case 0
{
gsContainers: nil,
podContainers: []corev1.Container{
{
Name: "A",
Image: "A-v1",
},
},
newContainers: nil,
},

// case 1
{
gsContainers: []gameKruiseV1alpha1.GameServerContainer{
{
Name: "A",
Image: "A-v2",
},
},
podContainers: []corev1.Container{
{
Name: "A",
Image: "A-v1",
},
{
Name: "B",
Image: "B-v1",
},
},
newContainers: []corev1.Container{
{
Name: "A",
Image: "A-v2",
},
},
},
}

for i, test := range tests {
expect := test.newContainers
manager := &GameServerManager{}
actual := manager.syncPodContainers(test.gsContainers, test.podContainers)
if !reflect.DeepEqual(expect, actual) {
t.Errorf("case %d: expect newContainers %v, but actually got %v", i, expect, actual)
}
}
}
Loading

0 comments on commit ecf81d9

Please sign in to comment.