sigs.k8s.io/cluster-api-provider-aws@v1.5.5/api/v1beta1/awsmachine_webhook_test.go (about)

     1  /*
     2  Copyright 2021 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  	http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package v1beta1
    18  
    19  import (
    20  	"context"
    21  	"strings"
    22  	"testing"
    23  
    24  	"github.com/aws/aws-sdk-go/aws"
    25  	. "github.com/onsi/gomega"
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  	"k8s.io/utils/pointer"
    28  
    29  	utildefaulting "sigs.k8s.io/cluster-api/util/defaulting"
    30  )
    31  
    32  func TestMachineDefault(t *testing.T) {
    33  	machine := &AWSMachine{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"}}
    34  	t.Run("for AWSMachine", utildefaulting.DefaultValidateTest(machine))
    35  	machine.Default()
    36  	g := NewWithT(t)
    37  	g.Expect(machine.Spec.CloudInit.SecureSecretsBackend).To(Equal(SecretBackendSecretsManager))
    38  }
    39  
    40  func TestAWSMachine_Create(t *testing.T) {
    41  	tests := []struct {
    42  		name    string
    43  		machine *AWSMachine
    44  		wantErr bool
    45  	}{
    46  		{
    47  			name: "ensure IOPS exists if type equal to io1",
    48  			machine: &AWSMachine{
    49  				Spec: AWSMachineSpec{
    50  					RootVolume: &Volume{
    51  						Type: "io1",
    52  					},
    53  					InstanceType: "test",
    54  				},
    55  			},
    56  			wantErr: true,
    57  		},
    58  		{
    59  			name: "ensure IOPS exists if type equal to io2",
    60  			machine: &AWSMachine{
    61  				Spec: AWSMachineSpec{
    62  					RootVolume: &Volume{
    63  						Type: "io2",
    64  					},
    65  					InstanceType: "test",
    66  				},
    67  			},
    68  			wantErr: true,
    69  		},
    70  		{
    71  			name: "ensure root volume throughput is nonnegative",
    72  			machine: &AWSMachine{
    73  				Spec: AWSMachineSpec{
    74  					RootVolume: &Volume{
    75  						Throughput: aws.Int64(-125),
    76  					},
    77  					InstanceType: "test",
    78  				},
    79  			},
    80  			wantErr: true,
    81  		},
    82  		{
    83  			name: "ensure root volume has no device name",
    84  			machine: &AWSMachine{
    85  				Spec: AWSMachineSpec{
    86  					RootVolume: &Volume{
    87  						DeviceName: "name",
    88  					},
    89  					InstanceType: "test",
    90  				},
    91  			},
    92  			wantErr: true,
    93  		},
    94  		{
    95  			name: "ensure non root volume have device names",
    96  			machine: &AWSMachine{
    97  				Spec: AWSMachineSpec{
    98  					NonRootVolumes: []Volume{
    99  						{},
   100  					},
   101  					InstanceType: "test",
   102  				},
   103  			},
   104  			wantErr: true,
   105  		},
   106  		{
   107  			name: "ensure ensure IOPS exists if type equal to io1 for non root volumes",
   108  			machine: &AWSMachine{
   109  				Spec: AWSMachineSpec{
   110  					NonRootVolumes: []Volume{
   111  						{
   112  							DeviceName: "name",
   113  							Type:       "io1",
   114  						},
   115  					},
   116  					InstanceType: "test",
   117  				},
   118  			},
   119  			wantErr: true,
   120  		},
   121  		{
   122  			name: "ensure ensure IOPS exists if type equal to io2 for non root volumes",
   123  			machine: &AWSMachine{
   124  				Spec: AWSMachineSpec{
   125  					NonRootVolumes: []Volume{
   126  						{
   127  							DeviceName: "name",
   128  							Type:       "io2",
   129  						},
   130  					},
   131  					InstanceType: "test",
   132  				},
   133  			},
   134  			wantErr: true,
   135  		},
   136  		{
   137  			name: "ensure non root volume throughput is nonnegative",
   138  			machine: &AWSMachine{
   139  				Spec: AWSMachineSpec{
   140  					NonRootVolumes: []Volume{
   141  						{
   142  							Throughput: aws.Int64(-125),
   143  						},
   144  					},
   145  					InstanceType: "test",
   146  				},
   147  			},
   148  			wantErr: true,
   149  		},
   150  		{
   151  			name: "additional security groups may have id",
   152  			machine: &AWSMachine{
   153  				Spec: AWSMachineSpec{
   154  					AdditionalSecurityGroups: []AWSResourceReference{
   155  						{
   156  							ID: aws.String("id"),
   157  						},
   158  					},
   159  					InstanceType: "test",
   160  				},
   161  			},
   162  			wantErr: false,
   163  		},
   164  		{
   165  			name: "additional security groups may have filters",
   166  			machine: &AWSMachine{
   167  				Spec: AWSMachineSpec{
   168  					AdditionalSecurityGroups: []AWSResourceReference{
   169  						{
   170  							Filters: []Filter{
   171  								{
   172  									Name:   "example-name",
   173  									Values: []string{"example-value"},
   174  								},
   175  							},
   176  						},
   177  					},
   178  					InstanceType: "test",
   179  				},
   180  			},
   181  			wantErr: false,
   182  		},
   183  		{
   184  			name: "additional security groups can't have both id and filters",
   185  			machine: &AWSMachine{
   186  				Spec: AWSMachineSpec{
   187  					AdditionalSecurityGroups: []AWSResourceReference{
   188  						{
   189  							ID: aws.String("id"),
   190  							Filters: []Filter{
   191  								{
   192  									Name:   "example-name",
   193  									Values: []string{"example-value"},
   194  								},
   195  							},
   196  						},
   197  					},
   198  					InstanceType: "test",
   199  				},
   200  			},
   201  			wantErr: true,
   202  		},
   203  		{
   204  			name: "valid additional tags are accepted",
   205  			machine: &AWSMachine{
   206  				Spec: AWSMachineSpec{
   207  					AdditionalTags: Tags{
   208  						"key-1": "value-1",
   209  						"key-2": "value-2",
   210  					},
   211  					InstanceType: "test",
   212  				},
   213  			},
   214  			wantErr: false,
   215  		},
   216  		{
   217  			name: "empty instance type not allowed",
   218  			machine: &AWSMachine{
   219  				Spec: AWSMachineSpec{
   220  					InstanceType: "",
   221  				},
   222  			},
   223  			wantErr: true,
   224  		},
   225  		{
   226  			name: "instance type minimum length is 2",
   227  			machine: &AWSMachine{
   228  				Spec: AWSMachineSpec{
   229  					InstanceType: "t",
   230  				},
   231  			},
   232  			wantErr: true,
   233  		},
   234  		{
   235  			name: "invalid tags return error",
   236  			machine: &AWSMachine{
   237  				Spec: AWSMachineSpec{
   238  					AdditionalTags: Tags{
   239  						"key-1":                    "value-1",
   240  						"":                         "value-2",
   241  						strings.Repeat("CAPI", 33): "value-3",
   242  						"key-4":                    strings.Repeat("CAPI", 65),
   243  					},
   244  					InstanceType: "test",
   245  				},
   246  			},
   247  			wantErr: true,
   248  		},
   249  	}
   250  	for _, tt := range tests {
   251  		t.Run(tt.name, func(t *testing.T) {
   252  			machine := tt.machine.DeepCopy()
   253  			machine.ObjectMeta = metav1.ObjectMeta{
   254  				GenerateName: "machine-",
   255  				Namespace:    "default",
   256  			}
   257  			ctx := context.TODO()
   258  			if err := testEnv.Create(ctx, machine); (err != nil) != tt.wantErr {
   259  				t.Errorf("ValidateCreate() error = %v, wantErr %v", err, tt.wantErr)
   260  			}
   261  			testEnv.Delete(ctx, machine)
   262  		})
   263  	}
   264  }
   265  
   266  func TestAWSMachine_Update(t *testing.T) {
   267  	tests := []struct {
   268  		name       string
   269  		oldMachine *AWSMachine
   270  		newMachine *AWSMachine
   271  		wantErr    bool
   272  	}{
   273  		{
   274  			name: "change in providerid, cloudinit, tags and securitygroups",
   275  			oldMachine: &AWSMachine{
   276  				Spec: AWSMachineSpec{
   277  					ProviderID:               nil,
   278  					AdditionalTags:           nil,
   279  					AdditionalSecurityGroups: nil,
   280  					InstanceType:             "test",
   281  				},
   282  			},
   283  			newMachine: &AWSMachine{
   284  				Spec: AWSMachineSpec{
   285  					ProviderID:   pointer.StringPtr("ID"),
   286  					InstanceType: "test",
   287  					AdditionalTags: Tags{
   288  						"key-1": "value-1",
   289  					},
   290  					AdditionalSecurityGroups: []AWSResourceReference{
   291  						{
   292  							ID: pointer.StringPtr("ID"),
   293  						},
   294  					},
   295  					CloudInit: CloudInit{
   296  						SecretPrefix: "test",
   297  						SecretCount:  5,
   298  					},
   299  				},
   300  			},
   301  			wantErr: false,
   302  		},
   303  		{
   304  			name: "change in fields other than providerid, tags and securitygroups",
   305  			oldMachine: &AWSMachine{
   306  				Spec: AWSMachineSpec{
   307  					ProviderID:               nil,
   308  					AdditionalTags:           nil,
   309  					AdditionalSecurityGroups: nil,
   310  					InstanceType:             "test",
   311  				},
   312  			},
   313  			newMachine: &AWSMachine{
   314  				Spec: AWSMachineSpec{
   315  					ImageLookupOrg: "test",
   316  					InstanceType:   "test",
   317  					ProviderID:     pointer.StringPtr("ID"),
   318  					AdditionalTags: Tags{
   319  						"key-1": "value-1",
   320  					},
   321  					AdditionalSecurityGroups: []AWSResourceReference{
   322  						{
   323  							ID: pointer.StringPtr("ID"),
   324  						},
   325  					},
   326  				},
   327  			},
   328  			wantErr: true,
   329  		},
   330  		{
   331  			name: "change in tags adding invalid ones",
   332  			oldMachine: &AWSMachine{
   333  				Spec: AWSMachineSpec{
   334  					ProviderID: nil,
   335  					AdditionalTags: Tags{
   336  						"key-1": "value-1",
   337  					},
   338  					AdditionalSecurityGroups: nil,
   339  					InstanceType:             "test",
   340  				},
   341  			},
   342  			newMachine: &AWSMachine{
   343  				Spec: AWSMachineSpec{
   344  					ProviderID: nil,
   345  					AdditionalTags: Tags{
   346  						"key-1":                    "value-1",
   347  						"":                         "value-2",
   348  						strings.Repeat("CAPI", 33): "value-3",
   349  						"key-4":                    strings.Repeat("CAPI", 65),
   350  					},
   351  					AdditionalSecurityGroups: nil,
   352  					InstanceType:             "test",
   353  				},
   354  			},
   355  			wantErr: true,
   356  		},
   357  	}
   358  	for _, tt := range tests {
   359  		ctx := context.TODO()
   360  		t.Run(tt.name, func(t *testing.T) {
   361  			machine := tt.oldMachine.DeepCopy()
   362  			machine.ObjectMeta = metav1.ObjectMeta{
   363  				GenerateName: "machine-",
   364  				Namespace:    "default",
   365  			}
   366  			if err := testEnv.Create(ctx, machine); err != nil {
   367  				t.Errorf("failed to create machine: %v", err)
   368  			}
   369  			machine.Spec = tt.newMachine.Spec
   370  			if err := testEnv.Update(ctx, machine); (err != nil) != tt.wantErr {
   371  				t.Errorf("ValidateUpdate() error = %v, wantErr %v", err, tt.wantErr)
   372  			}
   373  		})
   374  	}
   375  }
   376  
   377  func TestAWSMachine_SecretsBackend(t *testing.T) {
   378  	baseMachine := &AWSMachine{
   379  		Spec: AWSMachineSpec{
   380  			ProviderID:               nil,
   381  			AdditionalTags:           nil,
   382  			AdditionalSecurityGroups: nil,
   383  			InstanceType:             "test",
   384  		},
   385  	}
   386  
   387  	tests := []struct {
   388  		name                   string
   389  		cloudInit              CloudInit
   390  		expectedSecretsBackend string
   391  	}{
   392  		{
   393  			name:                   "with insecure skip secrets manager unset",
   394  			cloudInit:              CloudInit{InsecureSkipSecretsManager: false},
   395  			expectedSecretsBackend: "secrets-manager",
   396  		},
   397  		{
   398  			name:                   "with insecure skip secrets manager unset and secrets backend set",
   399  			cloudInit:              CloudInit{InsecureSkipSecretsManager: false, SecureSecretsBackend: "ssm-parameter-store"},
   400  			expectedSecretsBackend: "ssm-parameter-store",
   401  		},
   402  		{
   403  			name:                   "with insecure skip secrets manager set",
   404  			cloudInit:              CloudInit{InsecureSkipSecretsManager: true},
   405  			expectedSecretsBackend: "",
   406  		},
   407  	}
   408  
   409  	for _, tt := range tests {
   410  		ctx := context.TODO()
   411  		t.Run(tt.name, func(t *testing.T) {
   412  			machine := baseMachine.DeepCopy()
   413  			machine.ObjectMeta = metav1.ObjectMeta{
   414  				GenerateName: "machine-",
   415  				Namespace:    "default",
   416  			}
   417  			machine.Spec.CloudInit = tt.cloudInit
   418  			if err := testEnv.Create(ctx, machine); err != nil {
   419  				t.Errorf("failed to create machine: %v", err)
   420  			}
   421  			g := NewWithT(t)
   422  			g.Expect(machine.Spec.CloudInit.SecureSecretsBackend).To(Equal(SecretBackend(tt.expectedSecretsBackend)))
   423  		})
   424  	}
   425  }