diff --git a/cluster-autoscaler/cloudprovider/gce/cache.go b/cluster-autoscaler/cloudprovider/gce/cache.go index d31f20dd37b..24100d80de1 100644 --- a/cluster-autoscaler/cloudprovider/gce/cache.go +++ b/cluster-autoscaler/cloudprovider/gce/cache.go @@ -73,6 +73,7 @@ type GceCache struct { machinesCache map[MachineTypeKey]MachineType migTargetSizeCache map[GceRef]int64 migBaseNameCache map[GceRef]string + migInstancesStateCache map[GceRef]map[cloudprovider.InstanceState]int64 listManagedInstancesResultsCache map[GceRef]string instanceTemplateNameCache map[GceRef]InstanceTemplateName instanceTemplatesCache map[GceRef]*gce.InstanceTemplate @@ -91,6 +92,7 @@ func NewGceCache() *GceCache { machinesCache: map[MachineTypeKey]MachineType{}, migTargetSizeCache: map[GceRef]int64{}, migBaseNameCache: map[GceRef]string{}, + migInstancesStateCache: map[GceRef]map[cloudprovider.InstanceState]int64{}, listManagedInstancesResultsCache: map[GceRef]string{}, instanceTemplateNameCache: map[GceRef]InstanceTemplateName{}, instanceTemplatesCache: map[GceRef]*gce.InstanceTemplate{}, @@ -546,3 +548,25 @@ func (gc *GceCache) InvalidateAllListManagedInstancesResults() { defer gc.cacheMutex.Unlock() gc.listManagedInstancesResultsCache = make(map[GceRef]string) } + +// GetMigInstancesState returns instancesState for the given mig from cache. +func (gc *GceCache) GetMigInstancesState(migRef GceRef) (instanceState map[cloudprovider.InstanceState]int64, found bool) { + gc.cacheMutex.Lock() + defer gc.cacheMutex.Unlock() + instanceState, found = gc.migInstancesStateCache[migRef] + return +} + +// SetMigInstancesState sets the InstancesState for a given mig in cache. +func (gc *GceCache) SetMigInstancesState(migRef GceRef, instanceState map[cloudprovider.InstanceState]int64) { + gc.cacheMutex.Lock() + defer gc.cacheMutex.Unlock() + gc.migInstancesStateCache[migRef] = instanceState +} + +// InvalidateMigInstancesState invalidates all migInstancesStateCache entries. +func (gc *GceCache) InvalidateMigInstancesState() { + gc.cacheMutex.Lock() + defer gc.cacheMutex.Unlock() + gc.migInstancesStateCache = make(map[GceRef]map[cloudprovider.InstanceState]int64) +} diff --git a/cluster-autoscaler/cloudprovider/gce/gce_manager_test.go b/cluster-autoscaler/cloudprovider/gce/gce_manager_test.go index 3addd9fee17..275fcf7a227 100644 --- a/cluster-autoscaler/cloudprovider/gce/gce_manager_test.go +++ b/cluster-autoscaler/cloudprovider/gce/gce_manager_test.go @@ -347,6 +347,7 @@ func newTestGceManager(t *testing.T, testServerURL string, regional bool) *gceMa instanceTemplatesCache: map[GceRef]*gce.InstanceTemplate{}, kubeEnvCache: map[GceRef]KubeEnv{}, migBaseNameCache: map[GceRef]string{}, + migInstancesStateCache: map[GceRef]map[cloudprovider.InstanceState]int64{}, listManagedInstancesResultsCache: map[GceRef]string{}, } migLister := NewMigLister(cache) diff --git a/cluster-autoscaler/cloudprovider/gce/mig_info_provider.go b/cluster-autoscaler/cloudprovider/gce/mig_info_provider.go index ad407bec1b1..277bc2dd64a 100644 --- a/cluster-autoscaler/cloudprovider/gce/mig_info_provider.go +++ b/cluster-autoscaler/cloudprovider/gce/mig_info_provider.go @@ -26,8 +26,9 @@ import ( "time" gce "google.golang.org/api/compute/v1" + "k8s.io/autoscaler/cluster-autoscaler/cloudprovider" "k8s.io/client-go/util/workqueue" - klog "k8s.io/klog/v2" + "k8s.io/klog/v2" ) // MigInfoProvider allows obtaining information about MIGs @@ -359,6 +360,7 @@ func (c *cachingMigInfoProvider) fillMigInfoCache() error { c.cache.SetMigTargetSize(zoneMigRef, zoneMig.TargetSize) c.cache.SetMigBasename(zoneMigRef, zoneMig.BaseInstanceName) c.cache.SetListManagedInstancesResults(zoneMigRef, zoneMig.ListManagedInstancesResults) + c.cache.SetMigInstancesState(zoneMigRef, createInstancesState(zoneMig.TargetSize, zoneMig.CurrentActions)) templateUrl, err := url.Parse(zoneMig.InstanceTemplate) if err == nil { @@ -444,3 +446,21 @@ func (c *cachingMigInfoProvider) GetListManagedInstancesResults(migRef GceRef) ( c.cache.SetListManagedInstancesResults(migRef, listManagedInstancesResults) return listManagedInstancesResults, nil } + +func createInstancesState(targetSize int64, actionsSummary *gce.InstanceGroupManagerActionsSummary) map[cloudprovider.InstanceState]int64 { + if actionsSummary == nil { + return nil + } + state := map[cloudprovider.InstanceState]int64{ + cloudprovider.InstanceCreating: 0, + cloudprovider.InstanceDeleting: 0, + cloudprovider.InstanceRunning: 0, + } + state[getInstanceState("ABANDONING")] += actionsSummary.Abandoning + state[getInstanceState("CREATING")] += actionsSummary.Creating + state[getInstanceState("CREATING_WITHOUT_RETRIES")] += actionsSummary.CreatingWithoutRetries + state[getInstanceState("DELETING")] += actionsSummary.Deleting + state[getInstanceState("RECREATING")] += actionsSummary.Recreating + state[cloudprovider.InstanceRunning] = targetSize - state[cloudprovider.InstanceCreating] + return state +} diff --git a/cluster-autoscaler/cloudprovider/gce/mig_info_provider_test.go b/cluster-autoscaler/cloudprovider/gce/mig_info_provider_test.go index 1f4e1199a5c..6f5e6cf0aea 100644 --- a/cluster-autoscaler/cloudprovider/gce/mig_info_provider_test.go +++ b/cluster-autoscaler/cloudprovider/gce/mig_info_provider_test.go @@ -1059,6 +1059,55 @@ func TestGetMigInstanceTemplate(t *testing.T) { } } +func TestCreateInstancesState(t *testing.T) { + testCases := []struct { + name string + targetSize int64 + actionSummary *gce.InstanceGroupManagerActionsSummary + want map[cloudprovider.InstanceState]int64 + }{ + { + name: "actionSummary is nil", + targetSize: 10, + actionSummary: nil, + want: nil, + }, + { + name: "actionSummary is empty", + targetSize: 10, + actionSummary: &gce.InstanceGroupManagerActionsSummary{}, + want: map[cloudprovider.InstanceState]int64{ + cloudprovider.InstanceCreating: 0, + cloudprovider.InstanceDeleting: 0, + cloudprovider.InstanceRunning: 10, + }, + }, + { + name: "actionSummary with data", + targetSize: 30, + actionSummary: &gce.InstanceGroupManagerActionsSummary{ + Abandoning: 2, + Creating: 3, + CreatingWithoutRetries: 5, + Deleting: 7, + Recreating: 11, + }, + want: map[cloudprovider.InstanceState]int64{ + cloudprovider.InstanceCreating: 19, + cloudprovider.InstanceDeleting: 9, + cloudprovider.InstanceRunning: 11, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + state := createInstancesState(tc.targetSize, tc.actionSummary) + assert.Equal(t, tc.want, state) + }) + } +} + func TestGetMigInstanceKubeEnv(t *testing.T) { templateName := "template-name" kubeEnvValue := "VAR1: VALUE1\nVAR2: VALUE2" @@ -1397,6 +1446,7 @@ func emptyCache() *GceCache { instancesUpdateTime: make(map[GceRef]time.Time), migTargetSizeCache: make(map[GceRef]int64), migBaseNameCache: make(map[GceRef]string), + migInstancesStateCache: make(map[GceRef]map[cloudprovider.InstanceState]int64), listManagedInstancesResultsCache: make(map[GceRef]string), instanceTemplateNameCache: make(map[GceRef]InstanceTemplateName), instanceTemplatesCache: make(map[GceRef]*gce.InstanceTemplate),