From a00db07942dc83b2d46b3e7304b07ed9e36ebd3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Wr=C3=B3blewski?= Date: Fri, 16 Feb 2024 12:55:45 +0000 Subject: [PATCH] Add unit-tests --- .../cloudprovider/gce/kube_env_test.go | 226 ++++++++++++++++++ .../gce/mig_info_provider_test.go | 157 ++++++++++++ 2 files changed, 383 insertions(+) create mode 100644 cluster-autoscaler/cloudprovider/gce/kube_env_test.go diff --git a/cluster-autoscaler/cloudprovider/gce/kube_env_test.go b/cluster-autoscaler/cloudprovider/gce/kube_env_test.go new file mode 100644 index 000000000000..8176c0b539dc --- /dev/null +++ b/cluster-autoscaler/cloudprovider/gce/kube_env_test.go @@ -0,0 +1,226 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package gce + +import ( + "testing" + + "github.com/stretchr/testify/assert" + gce "google.golang.org/api/compute/v1" +) + +func TestExtractKubeEnv(t *testing.T) { + templateName := "instance-template" + correctKubeEnv := "VAR1: VALUE1\nVAR2: VALUE2" + someValue := "Lorem ipsum dolor sit amet" + + testCases := []struct { + name string + template *gce.InstanceTemplate + wantKubeEnv KubeEnv + wantErr bool + }{ + { + name: "template is nil", + template: nil, + wantErr: true, + }, + { + name: "template without instance properties", + template: &gce.InstanceTemplate{}, + wantErr: true, + }, + { + name: "template without instance properties metadata", + template: &gce.InstanceTemplate{ + Properties: &gce.InstanceProperties{}, + }, + wantErr: true, + }, + { + name: "template without kube-env", + template: &gce.InstanceTemplate{ + Name: templateName, + Properties: &gce.InstanceProperties{ + Metadata: &gce.Metadata{ + Items: []*gce.MetadataItems{ + {Key: "key-1", Value: &someValue}, + {Key: "key-2", Value: &someValue}, + }, + }, + }, + }, + wantKubeEnv: KubeEnv{templateName: templateName}, + }, + { + name: "template with nil kube-env", + template: &gce.InstanceTemplate{ + Name: templateName, + Properties: &gce.InstanceProperties{ + Metadata: &gce.Metadata{ + Items: []*gce.MetadataItems{ + {Key: "key-1", Value: &someValue}, + {Key: "key-2", Value: &someValue}, + {Key: "kube-env", Value: nil}, + }, + }, + }, + }, + wantErr: true, + }, + { + name: "template with incorrect kube-env", + template: &gce.InstanceTemplate{ + Properties: &gce.InstanceProperties{ + Metadata: &gce.Metadata{ + Items: []*gce.MetadataItems{ + {Key: "key-1", Value: &someValue}, + {Key: "key-2", Value: &someValue}, + {Key: "kube-env", Value: &someValue}, + }, + }, + }, + }, + wantErr: true, + }, + { + name: "template with correct kube-env", + template: &gce.InstanceTemplate{ + Name: templateName, + Properties: &gce.InstanceProperties{ + Metadata: &gce.Metadata{ + Items: []*gce.MetadataItems{ + {Key: "key-1", Value: &someValue}, + {Key: "key-2", Value: &someValue}, + {Key: "kube-env", Value: &correctKubeEnv}, + }, + }, + }, + }, + wantKubeEnv: KubeEnv{ + templateName: templateName, + env: map[string]string{ + "VAR1": "VALUE1", + "VAR2": "VALUE2", + }, + }, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + kubeEnv, err := ExtractKubeEnv(tc.template) + if tc.wantErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.Equal(t, tc.wantKubeEnv, kubeEnv) + } + }) + } +} + +func TestParseKubeEnv(t *testing.T) { + templateName := "instance-template" + testCases := []struct { + name string + kubeEnvValue string + wantKubeEnv KubeEnv + wantErr bool + }{ + { + name: "kube-env value is empty", + kubeEnvValue: "", + wantKubeEnv: KubeEnv{ + templateName: templateName, + env: map[string]string{}, + }, + }, + { + name: "kube-env value is incorrect", + kubeEnvValue: "Lorem ipsum dolor sit amet", + wantErr: true, + }, + { + name: "kube-env value is correct", + kubeEnvValue: "VAR1: VALUE1\nVAR2: VALUE2", + wantKubeEnv: KubeEnv{ + templateName: templateName, + env: map[string]string{ + "VAR1": "VALUE1", + "VAR2": "VALUE2", + }, + }, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + kubeEnv, err := ParseKubeEnv(templateName, tc.kubeEnvValue) + if tc.wantErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.Equal(t, tc.wantKubeEnv, kubeEnv) + } + }) + } +} + +func TestKubeEnvVar(t *testing.T) { + testCases := []struct { + name string + kubeEnv KubeEnv + variable string + wantValue string + wantFound bool + }{ + { + name: "kube-env is nil", + variable: "VAR1", + wantFound: false, + }, + { + name: "kube-env does not have this variable", + kubeEnv: KubeEnv{ + env: map[string]string{ + "VAR1": "VALUE1", + "VAR2": "VALUE2", + }, + }, + variable: "VAR3", + wantFound: false, + }, + { + name: "kube-env has this variable", + kubeEnv: KubeEnv{ + env: map[string]string{ + "VAR1": "VALUE1", + "VAR2": "VALUE2", + }, + }, + variable: "VAR2", + wantValue: "VALUE2", + wantFound: true, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + value, found := tc.kubeEnv.Var(tc.variable) + assert.Equal(t, tc.wantValue, value) + assert.Equal(t, tc.wantFound, found) + }) + } +} diff --git a/cluster-autoscaler/cloudprovider/gce/mig_info_provider_test.go b/cluster-autoscaler/cloudprovider/gce/mig_info_provider_test.go index 6c2ea6dc75e6..b0bd51bdf50a 100644 --- a/cluster-autoscaler/cloudprovider/gce/mig_info_provider_test.go +++ b/cluster-autoscaler/cloudprovider/gce/mig_info_provider_test.go @@ -951,6 +951,163 @@ func TestGetMigInstanceTemplate(t *testing.T) { } } +func TestGetMigInstanceKubeEnv(t *testing.T) { + templateName := "template-name" + kubeEnvValue := "VAR1: VALUE1\nVAR2: VALUE2" + kubeEnv, err := ParseKubeEnv(templateName, kubeEnvValue) + assert.NoError(t, err) + template := &gce.InstanceTemplate{ + Name: templateName, + Description: "instance template", + Properties: &gce.InstanceProperties{ + Metadata: &gce.Metadata{ + Items: []*gce.MetadataItems{ + {Key: "kube-env", Value: &kubeEnvValue}, + }, + }, + }, + } + + oldTemplateName := "old-template-name" + oldKubeEnvValue := "VAR3: VALUE3\nVAR4: VALUE4" + oldKubeEnv, err := ParseKubeEnv(oldTemplateName, oldKubeEnvValue) + assert.NoError(t, err) + oldTemplate := &gce.InstanceTemplate{ + Name: oldTemplateName, + Description: "old instance template", + Properties: &gce.InstanceProperties{ + Metadata: &gce.Metadata{ + Items: []*gce.MetadataItems{ + {Key: "kube-env", Value: &oldKubeEnvValue}, + }, + }, + }, + } + + testCases := []struct { + name string + cache *GceCache + fetchMigs func(string) ([]*gce.InstanceGroupManager, error) + fetchMigTemplateName func(GceRef) (string, error) + fetchMigTemplate func(GceRef, string) (*gce.InstanceTemplate, error) + expectedKubeEnv KubeEnv + expectedCachedKubeEnv KubeEnv + expectedErr error + }{ + { + name: "kube-env in cache", + cache: &GceCache{ + migs: map[GceRef]Mig{mig.GceRef(): mig}, + instanceTemplateNameCache: map[GceRef]string{mig.GceRef(): templateName}, + kubeEnvCache: map[GceRef]KubeEnv{mig.GceRef(): kubeEnv}, + }, + expectedKubeEnv: kubeEnv, + expectedCachedKubeEnv: kubeEnv, + }, + { + name: "cache without kube-env, template in cache", + cache: &GceCache{ + migs: map[GceRef]Mig{mig.GceRef(): mig}, + instanceTemplateNameCache: map[GceRef]string{mig.GceRef(): templateName}, + instanceTemplatesCache: map[GceRef]*gce.InstanceTemplate{mig.GceRef(): template}, + kubeEnvCache: make(map[GceRef]KubeEnv), + }, + expectedKubeEnv: kubeEnv, + expectedCachedKubeEnv: kubeEnv, + }, + { + name: "cache without kube-env, fetch success", + cache: &GceCache{ + migs: map[GceRef]Mig{mig.GceRef(): mig}, + instanceTemplateNameCache: map[GceRef]string{mig.GceRef(): templateName}, + instanceTemplatesCache: make(map[GceRef]*gce.InstanceTemplate), + kubeEnvCache: make(map[GceRef]KubeEnv), + }, + fetchMigTemplate: fetchMigTemplateConst(template), + expectedKubeEnv: kubeEnv, + expectedCachedKubeEnv: kubeEnv, + }, + { + name: "cache with old kube-env, new template cached", + cache: &GceCache{ + migs: map[GceRef]Mig{mig.GceRef(): mig}, + instanceTemplateNameCache: map[GceRef]string{mig.GceRef(): templateName}, + instanceTemplatesCache: map[GceRef]*gce.InstanceTemplate{mig.GceRef(): template}, + kubeEnvCache: map[GceRef]KubeEnv{mig.GceRef(): oldKubeEnv}, + }, + expectedKubeEnv: kubeEnv, + expectedCachedKubeEnv: kubeEnv, + }, + { + name: "cache with old kube-env, fetch success", + cache: &GceCache{ + migs: map[GceRef]Mig{mig.GceRef(): mig}, + instanceTemplateNameCache: map[GceRef]string{mig.GceRef(): templateName}, + instanceTemplatesCache: map[GceRef]*gce.InstanceTemplate{mig.GceRef(): oldTemplate}, + kubeEnvCache: map[GceRef]KubeEnv{mig.GceRef(): oldKubeEnv}, + }, + fetchMigTemplate: fetchMigTemplateConst(template), + expectedKubeEnv: kubeEnv, + expectedCachedKubeEnv: kubeEnv, + }, + { + name: "cache without kube-env, fetch failure", + cache: &GceCache{ + migs: map[GceRef]Mig{mig.GceRef(): mig}, + instanceTemplateNameCache: map[GceRef]string{mig.GceRef(): templateName}, + instanceTemplatesCache: make(map[GceRef]*gce.InstanceTemplate), + kubeEnvCache: make(map[GceRef]KubeEnv), + }, + fetchMigTemplate: fetchMigTemplateFail, + expectedErr: errFetchMigTemplate, + }, + { + name: "cache with old kube-env, fetch failure", + cache: &GceCache{ + migs: map[GceRef]Mig{mig.GceRef(): mig}, + instanceTemplateNameCache: map[GceRef]string{mig.GceRef(): templateName}, + instanceTemplatesCache: map[GceRef]*gce.InstanceTemplate{mig.GceRef(): oldTemplate}, + kubeEnvCache: map[GceRef]KubeEnv{mig.GceRef(): oldKubeEnv}, + }, + fetchMigTemplate: fetchMigTemplateFail, + expectedCachedKubeEnv: oldKubeEnv, + expectedErr: errFetchMigTemplate, + }, + { + name: "template name fetch failure", + cache: emptyCache(), + fetchMigs: fetchMigsFail, + fetchMigTemplateName: fetchMigTemplateNameFail, + expectedErr: errFetchMigTemplateName, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + client := &mockAutoscalingGceClient{ + fetchMigs: tc.fetchMigs, + fetchMigTemplateName: tc.fetchMigTemplateName, + fetchMigTemplate: tc.fetchMigTemplate, + } + migLister := NewMigLister(tc.cache) + provider := NewCachingMigInfoProvider(tc.cache, migLister, client, mig.GceRef().Project, 1, 0*time.Second) + + kubeEnv, err := provider.GetMigKubeEnv(mig.GceRef()) + cachedKubeEnv, found := tc.cache.GetMigKubeEnv(mig.GceRef()) + + assert.Equal(t, tc.expectedErr, err) + if tc.expectedErr == nil { + assert.Equal(t, tc.expectedKubeEnv, kubeEnv) + } + + assert.Equal(t, tc.expectedCachedKubeEnv.env != nil, found) + if tc.expectedCachedKubeEnv.env != nil { + assert.Equal(t, tc.expectedCachedKubeEnv, cachedKubeEnv) + } + }) + } +} + func TestGetMigMachineType(t *testing.T) { knownZone := "us-cache1-a" unknownZone := "us-nocache42-c"