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

Parameterize # of GameServer Creation/Deletion #432

Merged
merged 11 commits into from
Nov 2, 2022
4 changes: 4 additions & 0 deletions pkg/operator/config/manager/manager.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ spec:
value: ${API_SERVICE_SECURITY}
- name: ALLOC_API_SVC_PORT
value: "5000"
- name: MAX_NUM_GS_TO_ADD
nottagg marked this conversation as resolved.
Show resolved Hide resolved
value: "20"
- name: MAX_NUM_GS_TO_DEL
value: "20"
- name: THUNDERNETES_INIT_CONTAINER_IMAGE
value: ${IMAGE_NAME_INIT_CONTAINER}:${IMAGE_TAG}
- name: THUNDERNETES_INIT_CONTAINER_IMAGE_WIN
Expand Down
35 changes: 17 additions & 18 deletions pkg/operator/controllers/gameserverbuild_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,6 @@ import (
// value is the number of crashes
var crashesPerBuild = sync.Map{}

const (
// maximum number of GameServers to create per reconcile loop
// we have this in place since each create call is synchronous and we want to minimize the time for each reconcile loop
maxNumberOfGameServersToAdd = 20
// maximum number of GameServers to delete per reconcile loop
maxNumberOfGameServersToDelete = 20
)

// Simple async map implementation using a mutex
// used to manage the expected GameServer creations and deletions
type MutexMap struct {
Expand All @@ -65,17 +57,24 @@ type GameServerBuildReconciler struct {
PortRegistry *PortRegistry
Recorder record.EventRecorder
expectations *GameServerExpectations
//maximum number of GameServers to add per reconcile loop
// we have this in place since each create call is synchronous and we want to minimize the time for each reconcile loop
maxNumberOfGameServersToAdd int
//maximum number of GameServers to delete per reconcile loop
maxNumberOfGameServersToDelete int
}

// NewGameServerBuildReconciler returns a pointer to a new GameServerBuildReconciler
func NewGameServerBuildReconciler(mgr manager.Manager, portRegistry *PortRegistry) *GameServerBuildReconciler {
func NewGameServerBuildReconciler(mgr manager.Manager, portRegistry *PortRegistry, maxGsToAdd, maxGsToDelete int) *GameServerBuildReconciler {
cl := mgr.GetClient()
return &GameServerBuildReconciler{
Client: cl,
Scheme: mgr.GetScheme(),
PortRegistry: portRegistry,
Recorder: mgr.GetEventRecorderFor("GameServerBuild"),
expectations: NewGameServerExpectations(cl),
Client: cl,
Scheme: mgr.GetScheme(),
PortRegistry: portRegistry,
Recorder: mgr.GetEventRecorderFor("GameServerBuild"),
expectations: NewGameServerExpectations(cl),
maxNumberOfGameServersToAdd: maxGsToAdd,
maxNumberOfGameServersToDelete: maxGsToDelete,
}
}

Expand Down Expand Up @@ -187,12 +186,12 @@ func (r *GameServerBuildReconciler) Reconcile(ctx context.Context, req ctrl.Requ
var totalNumberOfGameServersToDelete int = 0
// user has decreased standingBy numbers
if nonActiveGameServersCount > gsb.Spec.StandingBy {
totalNumberOfGameServersToDelete += int(math.Min(float64(nonActiveGameServersCount-gsb.Spec.StandingBy), maxNumberOfGameServersToDelete))
totalNumberOfGameServersToDelete += int(math.Min(float64(nonActiveGameServersCount-gsb.Spec.StandingBy), float64(r.maxNumberOfGameServersToDelete)))
}
// we also need to check if we are above the max
// this can happen if the user modifies the spec.Max during the GameServerBuild's lifetime
if nonActiveGameServersCount+activeCount > gsb.Spec.Max {
totalNumberOfGameServersToDelete += int(math.Min(float64(totalNumberOfGameServersToDelete+(nonActiveGameServersCount+activeCount-gsb.Spec.Max)), maxNumberOfGameServersToDelete))
totalNumberOfGameServersToDelete += int(math.Min(float64(totalNumberOfGameServersToDelete+(nonActiveGameServersCount+activeCount-gsb.Spec.Max)), float64(r.maxNumberOfGameServersToDelete)))
}
if totalNumberOfGameServersToDelete > 0 {
err := r.deleteNonActiveGameServers(ctx, &gsb, &gameServers, totalNumberOfGameServersToDelete)
Expand All @@ -205,12 +204,12 @@ func (r *GameServerBuildReconciler) Reconcile(ctx context.Context, req ctrl.Requ
// we're also limiting the number of game servers that are created to avoid issues like this https://github.com/kubernetes-sigs/controller-runtime/issues/1782
// we attempt to create the missing number of game servers, but we don't want to create more than the max
// an error channel for the go routines to write errors
errCh := make(chan error, maxNumberOfGameServersToAdd)
errCh := make(chan error, r.maxNumberOfGameServersToAdd)
// a waitgroup for async create calls
var wg sync.WaitGroup
for i := 0; i < gsb.Spec.StandingBy-nonActiveGameServersCount &&
i+nonActiveGameServersCount+activeCount < gsb.Spec.Max &&
i < maxNumberOfGameServersToAdd; i++ {
i < r.maxNumberOfGameServersToAdd; i++ {
wg.Add(1)
go func() {
defer wg.Done()
Expand Down
10 changes: 6 additions & 4 deletions pkg/operator/controllers/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,11 @@ import (
)

const (
assertPollingInterval = 20 * time.Millisecond
assertTimeout = 2 * time.Second
allocationApiSvcPort = 5000
assertPollingInterval = 20 * time.Millisecond
assertTimeout = 2 * time.Second
allocationApiSvcPort = 5000
maxNumberOfGameServersToAdd = 20
maxNumberOfGameServersToDelete = 20
)

// These tests use Ginkgo (BDD-style Go testing framework). Refer to
Expand Down Expand Up @@ -98,7 +100,7 @@ var _ = BeforeSuite(func() {
err = portRegistry.SetupWithManager(k8sManager)
Expect(err).ToNot(HaveOccurred())

err = (NewGameServerBuildReconciler(k8sManager, portRegistry)).SetupWithManager(k8sManager)
err = (NewGameServerBuildReconciler(k8sManager, portRegistry, maxNumberOfGameServersToAdd, maxNumberOfGameServersToDelete)).SetupWithManager(k8sManager)
Expect(err).ToNot(HaveOccurred())

initContainerImageLinux, initContainerImageWin := "testImageLinux", "testImageWin"
Expand Down
4 changes: 3 additions & 1 deletion pkg/operator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ type Config struct {
AllocationApiSvcPort int32 `env:"ALLOC_API_SVC_PORT" envDefault:"5000"`
InitContainerImageLinux string `env:"THUNDERNETES_INIT_CONTAINER_IMAGE,notEmpty"`
InitContainerImageWin string `env:"THUNDERNETES_INIT_CONTAINER_IMAGE_WIN,notEmpty"`
MaxNumberOfGameServersToAdd int32 `env:"MAX_NUM_GS_TO_ADD" envDefault:"20"`
MaxNumberOfGameServersToDelete int32 `env:"MAX_NUM_GS_TO_DEL" envDefault:"20"`
}

var (
Expand Down Expand Up @@ -151,7 +153,7 @@ func main() {
}

// initialize the GameServerBuild controller
if err = controllers.NewGameServerBuildReconciler(mgr, portRegistry).SetupWithManager(mgr); err != nil {
if err = controllers.NewGameServerBuildReconciler(mgr, portRegistry, int(cfg.MaxNumberOfGameServersToAdd), int(cfg.MaxNumberOfGameServersToDelete)).SetupWithManager(mgr); err != nil {
nottagg marked this conversation as resolved.
Show resolved Hide resolved
setupLog.Error(err, "unable to create controller", "controller", "GameServerBuild")
os.Exit(1)
}
Expand Down