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

feat: add ReclaimPolicy for GameServer #115

Merged
merged 1 commit into from
Dec 28, 2023
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: 14 additions & 0 deletions apis/v1alpha1/gameserverset_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,22 @@ type GameServerTemplate struct {
// +kubebuilder:validation:Schemaless
corev1.PodTemplateSpec `json:",inline"`
VolumeClaimTemplates []corev1.PersistentVolumeClaim `json:"volumeClaimTemplates,omitempty"`
// ReclaimPolicy indicates the reclaim policy for GameServer.
// Default is Cascade.
ReclaimPolicy GameServerReclaimPolicy `json:"reclaimPolicy,omitempty"`
}

type GameServerReclaimPolicy string

const (
// CascadeGameServerReclaimPolicy indicates that GameServer is deleted when the pod is deleted.
// The age of GameServer is exactly the same as that of the pod.
CascadeGameServerReclaimPolicy GameServerReclaimPolicy = "Cascade"
// DeleteGameServerReclaimPolicy indicates that GameServers will be deleted when replicas of GameServerSet decreases.
// The GameServer will not be deleted when the corresponding pod is deleted due to manual deletion, update, eviction, etc.
DeleteGameServerReclaimPolicy GameServerReclaimPolicy = "Delete"
)

type Network struct {
NetworkType string `json:"networkType,omitempty"`
NetworkConf []NetworkConfParams `json:"networkConf,omitempty"`
Expand Down
4 changes: 4 additions & 0 deletions config/crd/bases/game.kruise.io_gameserversets.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ spec:
description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
Important: Run "make" to regenerate code after modifying this file'
properties:
reclaimPolicy:
description: ReclaimPolicy indicates the reclaim policy for GameServer.
Default is Cascade.
type: string
volumeClaimTemplates:
items:
description: PersistentVolumeClaim is a user's request for and
Expand Down
16 changes: 16 additions & 0 deletions docs/en/user_manuals/CRD_field_description.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,24 @@ type GameServerTemplate struct {

// Requests and claims for persistent volumes.
VolumeClaimTemplates []corev1.PersistentVolumeClaim `json:"volumeClaimTemplates,omitempty"`

// ReclaimPolicy indicates the reclaim policy for GameServer.
// Default is Cascade.
ReclaimPolicy GameServerReclaimPolicy `json:"reclaimPolicy,omitempty"`
}

type GameServerReclaimPolicy string

const (
// CascadeGameServerReclaimPolicy indicates that GameServer is deleted when the pod is deleted.
// The age of GameServer is exactly the same as that of the pod.
CascadeGameServerReclaimPolicy GameServerReclaimPolicy = "Cascade"

// DeleteGameServerReclaimPolicy indicates that GameServers will be deleted when replicas of GameServerSet decreases.
// The GameServer will not be deleted when the corresponding pod is deleted due to manual deletion, update, eviction, etc.
DeleteGameServerReclaimPolicy GameServerReclaimPolicy = "Delete"
)

```

#### UpdateStrategy
Expand Down
16 changes: 16 additions & 0 deletions docs/中文/用户手册/CRD字段说明.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,23 @@ type GameServerTemplate struct {

// 对持久卷的请求和声明
VolumeClaimTemplates []corev1.PersistentVolumeClaim `json:"volumeClaimTemplates,omitempty"`

// ReclaimPolicy 表明GameServer的回收策略
// 当前支持两种,Cascade与Delete。默认为Cascade
ReclaimPolicy GameServerReclaimPolicy `json:"reclaimPolicy,omitempty"`
}

type GameServerReclaimPolicy string

const (
// Cascade 表明pod删除时GameServer一并删除。GameServer的生命周期与pod相同
CascadeGameServerReclaimPolicy GameServerReclaimPolicy = "Cascade"

// Delete 表明 GameServers 只会在GameServerSet副本数目减少时被删除。
// 当对应的pod被手动删除、更新重建、被驱逐时,GameServer都不会被删除。
DeleteGameServerReclaimPolicy GameServerReclaimPolicy = "Delete"
)

```

### UpdateStrategy
Expand Down
79 changes: 26 additions & 53 deletions pkg/controllers/gameserver/gameserver_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/tools/record"
"k8s.io/client-go/util/workqueue"
"k8s.io/klog/v2"
Expand Down Expand Up @@ -173,8 +172,15 @@
}

if podFound && !gsFound {
err := r.initGameServer(pod)
if err != nil && !errors.IsAlreadyExists(err) && !errors.IsNotFound(err) {
gss, err := r.getGameServerSet(pod)
if err != nil {
if errors.IsNotFound(err) {
return reconcile.Result{}, nil
}
return reconcile.Result{}, err

Check warning on line 180 in pkg/controllers/gameserver/gameserver_controller.go

View check run for this annotation

Codecov / codecov/patch

pkg/controllers/gameserver/gameserver_controller.go#L175-L180

Added lines #L175 - L180 were not covered by tests
}
err = r.initGameServerByPod(gss, pod)
if err != nil && !errors.IsAlreadyExists(err) {

Check warning on line 183 in pkg/controllers/gameserver/gameserver_controller.go

View check run for this annotation

Codecov / codecov/patch

pkg/controllers/gameserver/gameserver_controller.go#L182-L183

Added lines #L182 - L183 were not covered by tests
klog.Errorf("failed to create GameServer %s in %s, because of %s.", namespacedName.Name, namespacedName.Namespace, err.Error())
return reconcile.Result{}, err
}
Expand Down Expand Up @@ -237,57 +243,24 @@
return gss, err
}

func (r *GameServerReconciler) initGameServer(pod *corev1.Pod) error {
gs := &gamekruiseiov1alpha1.GameServer{}
gs.Name = pod.GetName()
gs.Namespace = pod.GetNamespace()

// set owner reference
gss, err := r.getGameServerSet(pod)
if err != nil {
return err
}
ors := make([]metav1.OwnerReference, 0)
or := metav1.OwnerReference{
APIVersion: gss.APIVersion,
Kind: gss.Kind,
Name: gss.GetName(),
UID: gss.GetUID(),
Controller: pointer.BoolPtr(true),
BlockOwnerDeletion: pointer.BoolPtr(true),
}
ors = append(ors, or)
gs.OwnerReferences = ors

// set Labels
gsLabels := gss.Spec.GameServerTemplate.GetLabels()
if gsLabels == nil {
gsLabels = make(map[string]string)
}
gsLabels[gamekruiseiov1alpha1.GameServerOwnerGssKey] = gss.GetName()
gs.SetLabels(gsLabels)

// set Annotations
gsAnnotations := gss.Spec.GameServerTemplate.GetAnnotations()
if gsAnnotations == nil {
gsAnnotations = make(map[string]string)
func (r *GameServerReconciler) initGameServerByPod(gss *gamekruiseiov1alpha1.GameServerSet, pod *corev1.Pod) error {
// default fields
gs := util.InitGameServer(gss, pod.Name)

if gss.Spec.GameServerTemplate.ReclaimPolicy == gamekruiseiov1alpha1.CascadeGameServerReclaimPolicy || gss.Spec.GameServerTemplate.ReclaimPolicy == "" {
// rewrite ownerReferences
ors := make([]metav1.OwnerReference, 0)
or := metav1.OwnerReference{
APIVersion: pod.APIVersion,
Kind: pod.Kind,
Name: pod.GetName(),
UID: pod.GetUID(),
Controller: pointer.BoolPtr(true),
BlockOwnerDeletion: pointer.BoolPtr(true),
}
ors = append(ors, or)
gs.OwnerReferences = ors

Check warning on line 262 in pkg/controllers/gameserver/gameserver_controller.go

View check run for this annotation

Codecov / codecov/patch

pkg/controllers/gameserver/gameserver_controller.go#L246-L262

Added lines #L246 - L262 were not covered by tests
}
gsAnnotations[gamekruiseiov1alpha1.GsTemplateMetadataHashKey] = util.GetGsTemplateMetadataHash(gss)
gs.SetAnnotations(gsAnnotations)

// set NetWork
gs.Spec.NetworkDisabled = false

// set OpsState
gs.Spec.OpsState = gamekruiseiov1alpha1.None

// set UpdatePriority
updatePriority := intstr.FromInt(0)
gs.Spec.UpdatePriority = &updatePriority

// set deletionPriority
deletionPriority := intstr.FromInt(0)
gs.Spec.DeletionPriority = &deletionPriority

return r.Client.Create(context.Background(), gs)
}
6 changes: 0 additions & 6 deletions pkg/controllers/gameserverset/gameserverset_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,12 +252,6 @@ func (r *GameServerSetReconciler) Reconcile(ctx context.Context, req ctrl.Reques
return reconcile.Result{}, err
}

err = gsm.SyncGameServerReplicas()
if err != nil {
klog.Errorf("GameServerSet %s failed to adjust the replicas of GameServers to match that of Pods in %s, because of %s.", namespacedName.Name, namespacedName.Namespace, err.Error())
return reconcile.Result{}, err
}

return ctrl.Result{}, nil
}

Expand Down
121 changes: 72 additions & 49 deletions pkg/controllers/gameserverset/gameserverset_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,6 @@

import (
"context"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/tools/record"
"sort"
"sync"
"time"

kruiseV1alpha1 "github.com/openkruise/kruise-api/apps/v1alpha1"
kruiseV1beta1 "github.com/openkruise/kruise-api/apps/v1beta1"
corev1 "k8s.io/api/core/v1"
Expand All @@ -32,9 +26,13 @@
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/json"
"k8s.io/client-go/tools/record"
"k8s.io/klog/v2"
"k8s.io/utils/pointer"
"sigs.k8s.io/controller-runtime/pkg/client"
"sort"
"strconv"
"sync"

gameKruiseV1alpha1 "github.com/openkruise/kruise-game/apis/v1alpha1"
"github.com/openkruise/kruise-game/pkg/util"
Expand All @@ -47,7 +45,6 @@
IsNeedToScale() bool
IsNeedToUpdateWorkload() bool
SyncPodProbeMarker() error
SyncGameServerReplicas() error
GetReplicasAfterKilling() *int32
}

Expand Down Expand Up @@ -129,7 +126,15 @@
klog.Infof("GameServers %s/%s already has %d replicas, expect to have %d replicas.", gss.GetNamespace(), gss.GetName(), currentReplicas, expectedReplicas)
manager.eventRecorder.Eventf(gss, corev1.EventTypeNormal, ScaleReason, "scale from %d to %d", currentReplicas, expectedReplicas)

newReserveIds := computeToScaleGs(gssReserveIds, reserveIds, notExistIds, expectedReplicas, podList, gss.Spec.ScaleStrategy.ScaleDownStrategyType)
newManageIds, newReserveIds := computeToScaleGs(gssReserveIds, reserveIds, notExistIds, expectedReplicas, podList, gss.Spec.ScaleStrategy.ScaleDownStrategyType)

if gss.Spec.GameServerTemplate.ReclaimPolicy == gameKruiseV1alpha1.DeleteGameServerReclaimPolicy {
err := SyncGameServer(gss, c, newManageIds, util.GetIndexListFromPodList(podList))
if err != nil {
return err
}

Check warning on line 135 in pkg/controllers/gameserverset/gameserverset_manager.go

View check run for this annotation

Codecov / codecov/patch

pkg/controllers/gameserverset/gameserverset_manager.go#L132-L135

Added lines #L132 - L135 were not covered by tests
}

asts.Spec.ReserveOrdinals = newReserveIds
asts.Spec.Replicas = gss.Spec.Replicas
asts.Spec.ScaleStrategy = &kruiseV1beta1.StatefulSetScaleStrategy{
Expand Down Expand Up @@ -157,7 +162,7 @@
return nil
}

func computeToScaleGs(gssReserveIds, reserveIds, notExistIds []int, expectedReplicas int, pods []corev1.Pod, scaleDownType gameKruiseV1alpha1.ScaleDownStrategyType) []int {
func computeToScaleGs(gssReserveIds, reserveIds, notExistIds []int, expectedReplicas int, pods []corev1.Pod, scaleDownType gameKruiseV1alpha1.ScaleDownStrategyType) ([]int, []int) {
workloadManageIds := util.GetIndexListFromPodList(pods)

var toAdd []int
Expand Down Expand Up @@ -215,12 +220,13 @@
}
}

newManageIds := append(workloadManageIds, util.GetSliceInANotInB(toAdd, workloadManageIds)...)
newManageIds = util.GetSliceInANotInB(newManageIds, toDelete)

if scaleDownType == gameKruiseV1alpha1.ReserveIdsScaleDownStrategyType {
return append(gssReserveIds, util.GetSliceInANotInB(toDelete, gssReserveIds)...)
return newManageIds, append(gssReserveIds, util.GetSliceInANotInB(toDelete, gssReserveIds)...)
}

newManageIds := append(workloadManageIds, util.GetSliceInANotInB(toAdd, workloadManageIds)...)
newManageIds = util.GetSliceInANotInB(newManageIds, toDelete)
var newReserveIds []int
if len(newManageIds) != 0 {
sort.Ints(newManageIds)
Expand All @@ -231,63 +237,80 @@
}
}

return newReserveIds
return newManageIds, newReserveIds
}

func (manager *GameServerSetManager) SyncGameServerReplicas() error {
gss := manager.gameServerSet
gsList := &gameKruiseV1alpha1.GameServerList{}
err := manager.client.List(context.Background(), gsList, &client.ListOptions{
Namespace: gss.GetNamespace(),
LabelSelector: labels.SelectorFromSet(map[string]string{
gameKruiseV1alpha1.GameServerOwnerGssKey: gss.GetName(),
})})
if err != nil {
return err
}
podIds := util.GetIndexListFromPodList(manager.podList)

gsMap := make(map[int]int)
deleteIds := make([]int, 0)
for id, gs := range gsList.Items {
gsId := util.GetIndexFromGsName(gs.Name)
if !util.IsNumInList(gsId, podIds) {
gsMap[gsId] = id
deleteIds = append(deleteIds, gsId)
}
}

ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
func SyncGameServer(gss *gameKruiseV1alpha1.GameServerSet, c client.Client, newManageIds, oldManageIds []int) error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

errch := make(chan error, len(deleteIds))
addIds := util.GetSliceInANotInB(newManageIds, oldManageIds)
deleteIds := util.GetSliceInANotInB(oldManageIds, newManageIds)

errch := make(chan error, len(addIds)+len(deleteIds))
var wg sync.WaitGroup
for _, gsId := range deleteIds {
for _, gsId := range append(addIds, deleteIds...) {
wg.Add(1)
id := gsId
go func(ctx context.Context) {
defer wg.Done()
defer ctx.Done()

gs := gsList.Items[gsMap[id]].DeepCopy()
gsLabels := make(map[string]string)
gsLabels[gameKruiseV1alpha1.GameServerDeletingKey] = "true"
patchGs := map[string]interface{}{"metadata": map[string]map[string]string{"labels": gsLabels}}
patchBytes, err := json.Marshal(patchGs)
gs := &gameKruiseV1alpha1.GameServer{}
gsName := gss.Name + "-" + strconv.Itoa(id)
err := c.Get(ctx, types.NamespacedName{
Name: gsName,
Namespace: gss.Namespace,
}, gs)
if err != nil {
if errors.IsNotFound(err) {
return
}
errch <- err
return

Check warning on line 270 in pkg/controllers/gameserverset/gameserverset_manager.go

View check run for this annotation

Codecov / codecov/patch

pkg/controllers/gameserverset/gameserverset_manager.go#L270

Added line #L270 was not covered by tests
}
err = manager.client.Patch(context.TODO(), gs, client.RawPatch(types.MergePatchType, patchBytes))
if err != nil && !errors.IsNotFound(err) {
errch <- err

if util.IsNumInList(id, addIds) && gs.GetLabels()[gameKruiseV1alpha1.GameServerDeletingKey] == "true" {
gsLabels := make(map[string]string)
gsLabels[gameKruiseV1alpha1.GameServerDeletingKey] = "false"
patchGs := map[string]interface{}{"metadata": map[string]map[string]string{"labels": gsLabels}}
patchBytes, err := json.Marshal(patchGs)
if err != nil {
errch <- err
return
}

Check warning on line 281 in pkg/controllers/gameserverset/gameserverset_manager.go

View check run for this annotation

Codecov / codecov/patch

pkg/controllers/gameserverset/gameserverset_manager.go#L279-L281

Added lines #L279 - L281 were not covered by tests
err = c.Patch(ctx, gs, client.RawPatch(types.MergePatchType, patchBytes))
if err != nil && !errors.IsNotFound(err) {
errch <- err
return
}

Check warning on line 286 in pkg/controllers/gameserverset/gameserverset_manager.go

View check run for this annotation

Codecov / codecov/patch

pkg/controllers/gameserverset/gameserverset_manager.go#L284-L286

Added lines #L284 - L286 were not covered by tests
klog.Infof("GameServer %s/%s DeletingKey turn into false", gss.Namespace, gsName)
}

if util.IsNumInList(id, deleteIds) && gs.GetLabels()[gameKruiseV1alpha1.GameServerDeletingKey] != "true" {
gsLabels := make(map[string]string)
gsLabels[gameKruiseV1alpha1.GameServerDeletingKey] = "true"
patchGs := map[string]interface{}{"metadata": map[string]map[string]string{"labels": gsLabels}}
patchBytes, err := json.Marshal(patchGs)
if err != nil {
errch <- err
return
}

Check warning on line 298 in pkg/controllers/gameserverset/gameserverset_manager.go

View check run for this annotation

Codecov / codecov/patch

pkg/controllers/gameserverset/gameserverset_manager.go#L296-L298

Added lines #L296 - L298 were not covered by tests
err = c.Patch(ctx, gs, client.RawPatch(types.MergePatchType, patchBytes))
if err != nil && !errors.IsNotFound(err) {
errch <- err
return
}

Check warning on line 303 in pkg/controllers/gameserverset/gameserverset_manager.go

View check run for this annotation

Codecov / codecov/patch

pkg/controllers/gameserverset/gameserverset_manager.go#L301-L303

Added lines #L301 - L303 were not covered by tests
klog.Infof("GameServer %s/%s DeletingKey turn into true, who will be deleted", gss.Namespace, gsName)
}

}(ctx)
}

wg.Wait()
close(errch)
err = <-errch
err := <-errch
if err != nil {
klog.Errorf("failed to delete GameServers %s in %s because of %s.", gss.GetName(), gss.GetNamespace(), err.Error())
return err
}

Expand Down
Loading
Loading