Skip to content

Commit

Permalink
Validate worker node kubernetes version with the template name in the…
Browse files Browse the repository at this point in the history
… spec - modular upgrade scenario (#6828)
  • Loading branch information
panktishah26 authored Oct 18, 2023
1 parent 5bbccbe commit 76b841c
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 19 deletions.
28 changes: 20 additions & 8 deletions pkg/providers/cloudstack/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,11 @@ func (v *Validator) ValidateClusterMachineConfigs(ctx context.Context, clusterSp
return fmt.Errorf("cannot find CloudStackMachineConfig %v for control plane", clusterSpec.Cluster.Spec.ControlPlaneConfiguration.MachineGroupRef.Name)
}

// validate template field of each CloudStackMachineConfigs with the cluster spec kubernetes version.
if err := v.validateTemplateMatchesKubernetesVersion(ctx, controlPlaneMachineConfig.Spec.Template.Name, string(clusterSpec.Cluster.Spec.KubernetesVersion)); err != nil {
return fmt.Errorf("machine config %s validation failed: %v", controlPlaneMachineConfig.Name, err)
}

if clusterSpec.Cluster.Spec.ExternalEtcdConfiguration != nil {
etcdMachineConfig := etcdMachineConfig(clusterSpec)
if etcdMachineConfig == nil {
Expand All @@ -130,15 +135,22 @@ func (v *Validator) ValidateClusterMachineConfigs(ctx context.Context, clusterSp
if !ok {
return fmt.Errorf("cannot find CloudStackMachineConfig %v for worker nodes", workerNodeGroupConfiguration.MachineGroupRef.Name)
}

version := string(clusterSpec.Cluster.Spec.KubernetesVersion)
// validate template field of worker group spec with the kubernetes version of each workerNodeGroup - in case of modular upgrade.
if workerNodeGroupConfiguration.KubernetesVersion != nil {
version = string(*workerNodeGroupConfiguration.KubernetesVersion)
}
templateName := clusterSpec.CloudStackMachineConfigs[workerNodeGroupConfiguration.MachineGroupRef.Name].Spec.Template.Name
if err := v.validateTemplateMatchesKubernetesVersion(ctx, templateName, version); err != nil {
return fmt.Errorf("machine config %s validation failed: %v", workerNodeGroupConfiguration.Name, err)
}
}

for _, machineConfig := range clusterSpec.CloudStackMachineConfigs {
if err := v.validateMachineConfig(ctx, clusterSpec.CloudStackDatacenter, machineConfig); err != nil {
return fmt.Errorf("machine config %s validation failed: %v", machineConfig.Name, err)
}
if err := v.validateTemplateMatchesKubernetesVersion(ctx, machineConfig, clusterSpec); err != nil {
return fmt.Errorf("machine config %s validation failed: %v", machineConfig.Name, err)
}
}

logger.MarkPass("Validated cluster Machine Configs")
Expand Down Expand Up @@ -194,18 +206,18 @@ func (v *Validator) validateMachineConfig(ctx context.Context, datacenterConfig
return nil
}

func (v *Validator) validateTemplateMatchesKubernetesVersion(ctx context.Context, machineConfig *anywherev1.CloudStackMachineConfig, spec *cluster.Spec) error {
func (v *Validator) validateTemplateMatchesKubernetesVersion(ctx context.Context, templateName string, kubernetesVersionName string) error {
// Replace 1.23, 1-23, 1_23 to 123 in the template name string.
templateReplacer := strings.NewReplacer("-", "", ".", "", "_", "")
templateName := templateReplacer.Replace(machineConfig.Spec.Template.Name)
template := templateReplacer.Replace(templateName)
// Replace 1-23 to 123 in the kubernetesversion string.
replacer := strings.NewReplacer(".", "")
kubernetesVersion := replacer.Replace(string(spec.Cluster.Spec.KubernetesVersion))
kubernetesVersion := replacer.Replace(string(kubernetesVersionName))
// This will return an error if the template name does not contain specified kubernetes version.
// For ex if the kubernetes version is 1.23,
// the template name should include 1.23 or 1-23, 1_23 or 123 i.e. kubernetes-1-23-eks in the string.
if !strings.Contains(templateName, kubernetesVersion) {
return fmt.Errorf("invalid template: cluster kubernetes version is %s but template for machineconfig %s is %s. If the kubernetes version is 1.23, the template name should include 1.23, 1_23, 1-23 or 123", string(spec.Cluster.Spec.KubernetesVersion), machineConfig.Name, machineConfig.Spec.Template.Name)
if !strings.Contains(template, kubernetesVersion) {
return fmt.Errorf("missing kube version from the machine config template name: template=%s, version=%s", templateName, string(kubernetesVersionName))
}
return nil
}
Expand Down
86 changes: 75 additions & 11 deletions pkg/providers/cloudstack/validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -306,26 +306,90 @@ func TestValidateCloudStackMachineConfig(t *testing.T) {
}
}

func TestValidateTemplateMatchesKubernetesVersionError(t *testing.T) {
func TestValidateClusterMachineConfigsError(t *testing.T) {
ctx := context.Background()
cmk := mocks.NewMockProviderCmkClient(gomock.NewController(t))
clusterSpec := test.NewFullClusterSpec(t, path.Join(testDataDir, testClusterConfigMainFilename))
config, err := cluster.ParseConfigFromFile(path.Join(testDataDir, testClusterConfigMainFilename))
clusterSpec.Cluster.Spec.KubernetesVersion = "1.22"

validator := NewValidator(cmk, &DummyNetClient{}, true)

err := validator.ValidateClusterMachineConfigs(ctx, clusterSpec)
if err == nil {
t.Fatalf("validation should not pass: %v", err)
}
}

func TestValidateClusterMachineConfigsModularUpgradeError(t *testing.T) {
ctx := context.Background()
cmk := mocks.NewMockProviderCmkClient(gomock.NewController(t))
clusterSpec := test.NewFullClusterSpec(t, path.Join(testDataDir, testClusterConfigMainFilename))
kube122 := v1alpha1.KubernetesVersion("1.22")
clusterSpec.Cluster.Spec.WorkerNodeGroupConfigurations[0].KubernetesVersion = &kube122

validator := NewValidator(cmk, &DummyNetClient{}, true)

err := validator.ValidateClusterMachineConfigs(ctx, clusterSpec)
if err == nil {
t.Fatalf("validation should not pass: %v", err)
}
}

func TestValidateClusterMachineConfigsSuccess(t *testing.T) {
ctx := context.Background()
cmk := mocks.NewMockProviderCmkClient(gomock.NewController(t))
clusterSpec := test.NewFullClusterSpec(t, path.Join(testDataDir, testClusterConfigMainFilename))

clusterSpec.Cluster.Spec.KubernetesVersion = "1.22"
clusterSpec.CloudStackMachineConfigs["test-cp"].Spec.Template.Name = "kubernetes_1_22"
clusterSpec.CloudStackMachineConfigs["test-etcd"].Spec.Template.Name = "kubernetes_1_22"
clusterSpec.CloudStackMachineConfigs["test"].Spec.Template.Name = "kubernetes_1_22"

datacenterConfig, err := v1alpha1.GetCloudStackDatacenterConfig(path.Join(testDataDir, testClusterConfigMainFilename))
if err != nil {
t.Fatalf("unable to get machine configs from file: %v", err)
t.Fatalf("unable to get datacenter config from file")
}
machineConfigs := config.CloudStackMachineConfigs

validator := NewValidator(cmk, &DummyNetClient{}, true)

cmk.EXPECT().ValidateZoneAndGetId(ctx, gomock.Any(), gomock.Any()).Times(3).Return("4e3b338d-87a6-4189-b931-a1747edeea82", nil)
cmk.EXPECT().ValidateTemplatePresent(ctx, gomock.Any(), gomock.Any(), gomock.Any(), datacenterConfig.Spec.AvailabilityZones[0].Account, v1alpha1.CloudStackResourceIdentifier{Name: "kubernetes_1_22"}).Times(3)
cmk.EXPECT().ValidateServiceOfferingPresent(ctx, gomock.Any(), gomock.Any(), testOffering).Times(3)
cmk.EXPECT().ValidateDiskOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(3)
cmk.EXPECT().ValidateAffinityGroupsPresent(ctx, gomock.Any(), gomock.Any(), datacenterConfig.Spec.AvailabilityZones[0].Account, gomock.Any()).Times(3)

err = validator.ValidateClusterMachineConfigs(ctx, clusterSpec)
if err != nil {
t.Fatalf("unable to get machine configs from file %s", testClusterConfigMainFilename)
t.Fatalf("validation should pass: %v", err)
}
clusterSpec.Cluster.Spec.KubernetesVersion = "1.22"
}

func TestValidateClusterMachineConfigsModularUpgradeSuccess(t *testing.T) {
ctx := context.Background()
cmk := mocks.NewMockProviderCmkClient(gomock.NewController(t))
clusterSpec := test.NewFullClusterSpec(t, path.Join(testDataDir, testClusterConfigMainFilename))

kube122 := v1alpha1.KubernetesVersion("1.22")
clusterSpec.Cluster.Spec.WorkerNodeGroupConfigurations[0].KubernetesVersion = &kube122
clusterSpec.CloudStackMachineConfigs["test"].Spec.Template.Name = "kubernetes_1_22"

datacenterConfig, err := v1alpha1.GetCloudStackDatacenterConfig(path.Join(testDataDir, testClusterConfigMainFilename))
if err != nil {
t.Fatalf("unable to get datacenter config from file")
}

validator := NewValidator(cmk, &DummyNetClient{}, true)

for _, machineConfig := range machineConfigs {
err := validator.validateTemplateMatchesKubernetesVersion(ctx, machineConfig, clusterSpec)
if err == nil {
t.Fatalf("failed to validate CloudStackMachineConfig: %v", err)
}
cmk.EXPECT().ValidateZoneAndGetId(ctx, gomock.Any(), gomock.Any()).Times(3).Return("4e3b338d-87a6-4189-b931-a1747edeea82", nil)
cmk.EXPECT().ValidateTemplatePresent(ctx, gomock.Any(), gomock.Any(), gomock.Any(), datacenterConfig.Spec.AvailabilityZones[0].Account, v1alpha1.CloudStackResourceIdentifier{Name: "kubernetes_1_22"})
cmk.EXPECT().ValidateTemplatePresent(ctx, gomock.Any(), gomock.Any(), gomock.Any(), datacenterConfig.Spec.AvailabilityZones[0].Account, testTemplate).Times(2)
cmk.EXPECT().ValidateServiceOfferingPresent(ctx, gomock.Any(), gomock.Any(), testOffering).Times(3)
cmk.EXPECT().ValidateDiskOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(3)
cmk.EXPECT().ValidateAffinityGroupsPresent(ctx, gomock.Any(), gomock.Any(), datacenterConfig.Spec.AvailabilityZones[0].Account, gomock.Any()).Times(3)

err = validator.ValidateClusterMachineConfigs(ctx, clusterSpec)
if err != nil {
t.Fatalf("validation should pass: %v", err)
}
}

Expand Down

0 comments on commit 76b841c

Please sign in to comment.