sigs.k8s.io/cluster-api-provider-aws@v1.5.5/pkg/eks/addons/plan_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 addons 18 19 import ( 20 "context" 21 "testing" 22 "time" 23 24 "github.com/aws/aws-sdk-go/aws" 25 "github.com/aws/aws-sdk-go/service/eks" 26 "github.com/golang/mock/gomock" 27 . "github.com/onsi/gomega" 28 29 infrav1 "sigs.k8s.io/cluster-api-provider-aws/api/v1beta1" 30 "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/services/eks/mock_eksiface" 31 ) 32 33 func TestEKSAddonPlan(t *testing.T) { 34 clusterName := "default.cluster" 35 addonARN := "aws://someaddonarn" 36 addon1Name := "addon1" 37 addon1version := "1.0.0" 38 addon1Upgrade := "2.0.0" 39 addonStatusActive := string(eks.AddonStatusActive) 40 addonStatusUpdating := string(eks.AddonStatusUpdating) 41 addonStatusDeleting := string(eks.AddonStatusDeleting) 42 addonStatusCreating := string(eks.AddonStatusCreating) 43 created := time.Now() 44 45 testCases := []struct { 46 name string 47 desiredAddons []*EKSAddon 48 installedAddons []*EKSAddon 49 expect func(m *mock_eksiface.MockEKSAPIMockRecorder) 50 expectCreateError bool 51 expectDoError bool 52 }{ 53 { 54 name: "no desired and no installed", 55 expect: func(m *mock_eksiface.MockEKSAPIMockRecorder) { 56 // Do nothing 57 }, 58 expectCreateError: false, 59 expectDoError: false, 60 }, 61 { 62 name: "no installed and 1 desired", 63 expect: func(m *mock_eksiface.MockEKSAPIMockRecorder) { 64 m. 65 CreateAddon(gomock.Eq(&eks.CreateAddonInput{ 66 AddonName: aws.String(addon1Name), 67 AddonVersion: aws.String(addon1version), 68 ClusterName: aws.String(clusterName), 69 ResolveConflicts: aws.String(eks.ResolveConflictsOverwrite), 70 Tags: convertTags(createTags()), 71 })). 72 Return(&eks.CreateAddonOutput{ 73 Addon: &eks.Addon{ 74 AddonArn: aws.String(addonARN), 75 AddonName: aws.String(addon1Name), 76 AddonVersion: aws.String(addon1version), 77 ClusterName: aws.String(clusterName), 78 CreatedAt: &created, 79 ModifiedAt: &created, 80 Status: aws.String(addonStatusCreating), 81 Tags: convertTags(createTags()), 82 }, 83 }, nil) 84 85 out := &eks.DescribeAddonOutput{ 86 Addon: &eks.Addon{ 87 Status: aws.String(eks.AddonStatusActive), 88 }, 89 } 90 m.DescribeAddon(gomock.Eq(&eks.DescribeAddonInput{ 91 AddonName: aws.String(addon1Name), 92 ClusterName: aws.String(clusterName), 93 })).Return(out, nil) 94 }, 95 desiredAddons: []*EKSAddon{ 96 createDesiredAddon(addon1Name, addon1version), 97 }, 98 expectCreateError: false, 99 expectDoError: false, 100 }, 101 { 102 name: "no installed and 2 desired", 103 expect: func(m *mock_eksiface.MockEKSAPIMockRecorder) { 104 m. 105 CreateAddon(gomock.Eq(&eks.CreateAddonInput{ 106 AddonName: aws.String(addon1Name), 107 AddonVersion: aws.String(addon1version), 108 ClusterName: aws.String(clusterName), 109 ResolveConflicts: aws.String(eks.ResolveConflictsOverwrite), 110 Tags: convertTags(createTags()), 111 })). 112 Return(&eks.CreateAddonOutput{ 113 Addon: &eks.Addon{ 114 AddonArn: aws.String(addonARN), 115 AddonName: aws.String(addon1Name), 116 AddonVersion: aws.String(addon1version), 117 ClusterName: aws.String(clusterName), 118 CreatedAt: &created, 119 ModifiedAt: &created, 120 Status: aws.String(addonStatusCreating), 121 Tags: convertTags(createTags()), 122 }, 123 }, nil) 124 125 out := &eks.DescribeAddonOutput{ 126 Addon: &eks.Addon{ 127 Status: aws.String(eks.AddonStatusActive), 128 }, 129 } 130 m.DescribeAddon(gomock.Eq(&eks.DescribeAddonInput{ 131 AddonName: aws.String(addon1Name), 132 ClusterName: aws.String(clusterName), 133 })).Return(out, nil) 134 135 m. 136 CreateAddon(gomock.Eq(&eks.CreateAddonInput{ 137 AddonName: aws.String("addon2"), 138 AddonVersion: aws.String(addon1version), 139 ClusterName: aws.String(clusterName), 140 ResolveConflicts: aws.String(eks.ResolveConflictsOverwrite), 141 Tags: convertTags(createTags()), 142 })). 143 Return(&eks.CreateAddonOutput{ 144 Addon: &eks.Addon{ 145 AddonArn: aws.String(addonARN), 146 AddonName: aws.String("addon2"), 147 AddonVersion: aws.String(addon1version), 148 ClusterName: aws.String(clusterName), 149 CreatedAt: &created, 150 ModifiedAt: &created, 151 Status: aws.String(addonStatusCreating), 152 Tags: convertTags(createTags()), 153 }, 154 }, nil) 155 156 m.DescribeAddon(gomock.Eq(&eks.DescribeAddonInput{ 157 AddonName: aws.String("addon2"), 158 ClusterName: aws.String(clusterName), 159 })).Return(out, nil) 160 }, 161 desiredAddons: []*EKSAddon{ 162 createDesiredAddon(addon1Name, addon1version), 163 createDesiredAddon("addon2", addon1version), 164 }, 165 expectCreateError: false, 166 expectDoError: false, 167 }, 168 { 169 name: "1 installed and 1 desired - both same and installed active", 170 expect: func(m *mock_eksiface.MockEKSAPIMockRecorder) { 171 // No Action expected 172 }, 173 desiredAddons: []*EKSAddon{ 174 createDesiredAddon(addon1Name, addon1version), 175 }, 176 installedAddons: []*EKSAddon{ 177 createInstalledAddon(addon1Name, addon1version, addonARN, addonStatusActive), 178 }, 179 expectCreateError: false, 180 expectDoError: false, 181 }, 182 { 183 name: "1 installed and 1 desired - both same and installed is creating", 184 expect: func(m *mock_eksiface.MockEKSAPIMockRecorder) { 185 out := &eks.DescribeAddonOutput{ 186 Addon: &eks.Addon{ 187 Status: aws.String(eks.AddonStatusActive), 188 }, 189 } 190 m.DescribeAddon(gomock.Eq(&eks.DescribeAddonInput{ 191 AddonName: aws.String(addon1Name), 192 ClusterName: aws.String(clusterName), 193 })).Return(out, nil) 194 }, 195 desiredAddons: []*EKSAddon{ 196 createDesiredAddon(addon1Name, addon1version), 197 }, 198 installedAddons: []*EKSAddon{ 199 createInstalledAddon(addon1Name, addon1version, addonARN, addonStatusCreating), 200 }, 201 expectCreateError: false, 202 expectDoError: false, 203 }, 204 { 205 name: "1 installed and 1 desired - version upgrade", 206 expect: func(m *mock_eksiface.MockEKSAPIMockRecorder) { 207 m. 208 UpdateAddon(gomock.Eq(&eks.UpdateAddonInput{ 209 AddonName: aws.String(addon1Name), 210 AddonVersion: aws.String(addon1Upgrade), 211 ClusterName: aws.String(clusterName), 212 ResolveConflicts: aws.String(eks.ResolveConflictsOverwrite), 213 })). 214 Return(&eks.UpdateAddonOutput{ 215 Update: &eks.Update{ 216 CreatedAt: &created, 217 Id: aws.String("someid"), 218 Status: aws.String(addonStatusUpdating), 219 Type: aws.String(eks.UpdateTypeVersionUpdate), 220 }, 221 }, nil) 222 223 out := &eks.DescribeAddonOutput{ 224 Addon: &eks.Addon{ 225 Status: aws.String(eks.AddonStatusActive), 226 }, 227 } 228 m.DescribeAddon(gomock.Eq(&eks.DescribeAddonInput{ 229 AddonName: aws.String(addon1Name), 230 ClusterName: aws.String(clusterName), 231 })).Return(out, nil) 232 }, 233 desiredAddons: []*EKSAddon{ 234 createDesiredAddon(addon1Name, addon1Upgrade), 235 }, 236 installedAddons: []*EKSAddon{ 237 createInstalledAddon(addon1Name, addon1version, addonARN, addonStatusActive), 238 }, 239 expectCreateError: false, 240 expectDoError: false, 241 }, 242 { 243 name: "1 installed and 1 desired - version upgrade in progress", 244 expect: func(m *mock_eksiface.MockEKSAPIMockRecorder) { 245 out := &eks.DescribeAddonOutput{ 246 Addon: &eks.Addon{ 247 Status: aws.String(eks.AddonStatusActive), 248 }, 249 } 250 m.DescribeAddon(gomock.Eq(&eks.DescribeAddonInput{ 251 AddonName: aws.String(addon1Name), 252 ClusterName: aws.String(clusterName), 253 })).Return(out, nil) 254 }, 255 desiredAddons: []*EKSAddon{ 256 createDesiredAddon(addon1Name, addon1Upgrade), 257 }, 258 installedAddons: []*EKSAddon{ 259 createInstalledAddon(addon1Name, addon1Upgrade, addonARN, addonStatusUpdating), 260 }, 261 expectCreateError: false, 262 expectDoError: false, 263 }, 264 { 265 name: "1 installed and 1 desired - tags upgrade", 266 expect: func(m *mock_eksiface.MockEKSAPIMockRecorder) { 267 m. 268 TagResource(gomock.Eq(&eks.TagResourceInput{ 269 ResourceArn: &addonARN, 270 Tags: convertTags(createTagsAdditional()), 271 })). 272 Return(&eks.TagResourceOutput{}, nil) 273 }, 274 desiredAddons: []*EKSAddon{ 275 createDesiredAddonExtraTag(addon1Name, addon1version), 276 }, 277 installedAddons: []*EKSAddon{ 278 createInstalledAddon(addon1Name, addon1version, addonARN, addonStatusActive), 279 }, 280 expectCreateError: false, 281 expectDoError: false, 282 }, 283 { 284 name: "1 installed and 1 desired - version & tags upgrade", 285 expect: func(m *mock_eksiface.MockEKSAPIMockRecorder) { 286 m. 287 TagResource(gomock.Eq(&eks.TagResourceInput{ 288 ResourceArn: &addonARN, 289 Tags: convertTags(createTagsAdditional()), 290 })). 291 Return(&eks.TagResourceOutput{}, nil) 292 m. 293 UpdateAddon(gomock.Eq(&eks.UpdateAddonInput{ 294 AddonName: aws.String(addon1Name), 295 AddonVersion: aws.String(addon1Upgrade), 296 ClusterName: aws.String(clusterName), 297 ResolveConflicts: aws.String(eks.ResolveConflictsOverwrite), 298 })). 299 Return(&eks.UpdateAddonOutput{ 300 Update: &eks.Update{ 301 CreatedAt: &created, 302 Id: aws.String("someid"), 303 Status: aws.String(addonStatusUpdating), 304 Type: aws.String(eks.UpdateTypeVersionUpdate), 305 }, 306 }, nil) 307 308 out := &eks.DescribeAddonOutput{ 309 Addon: &eks.Addon{ 310 Status: aws.String(eks.AddonStatusActive), 311 }, 312 } 313 m.DescribeAddon(gomock.Eq(&eks.DescribeAddonInput{ 314 AddonName: aws.String(addon1Name), 315 ClusterName: aws.String(clusterName), 316 })).Return(out, nil) 317 }, 318 desiredAddons: []*EKSAddon{ 319 createDesiredAddonExtraTag(addon1Name, addon1Upgrade), 320 }, 321 installedAddons: []*EKSAddon{ 322 createInstalledAddon(addon1Name, addon1version, addonARN, addonStatusActive), 323 }, 324 expectCreateError: false, 325 expectDoError: false, 326 }, 327 { 328 name: "1 installed and 0 desired - delete addon", 329 expect: func(m *mock_eksiface.MockEKSAPIMockRecorder) { 330 m. 331 DeleteAddon(gomock.Eq(&eks.DeleteAddonInput{ 332 AddonName: &addon1Name, 333 ClusterName: &clusterName, 334 })). 335 Return(&eks.DeleteAddonOutput{ 336 Addon: &eks.Addon{ 337 AddonArn: aws.String(addonARN), 338 AddonName: aws.String(addon1Name), 339 AddonVersion: aws.String(addon1version), 340 ClusterName: aws.String(clusterName), 341 CreatedAt: &created, 342 ModifiedAt: &created, 343 Status: aws.String(addonStatusDeleting), 344 Tags: convertTags(createTags()), 345 }, 346 }, nil) 347 m.WaitUntilAddonDeleted(gomock.Eq(&eks.DescribeAddonInput{ 348 AddonName: aws.String(addon1Name), 349 ClusterName: aws.String(clusterName), 350 })).Return(nil) 351 }, 352 installedAddons: []*EKSAddon{ 353 createInstalledAddon(addon1Name, addon1version, addonARN, addonStatusActive), 354 }, 355 expectCreateError: false, 356 expectDoError: false, 357 }, 358 { 359 name: "1 installed and 0 desired - addon has status of deleting", 360 expect: func(m *mock_eksiface.MockEKSAPIMockRecorder) { 361 m.WaitUntilAddonDeleted(gomock.Eq(&eks.DescribeAddonInput{ 362 AddonName: aws.String(addon1Name), 363 ClusterName: aws.String(clusterName), 364 })).Return(nil) 365 }, 366 installedAddons: []*EKSAddon{ 367 createInstalledAddon(addon1Name, addon1version, addonARN, addonStatusDeleting), 368 }, 369 expectCreateError: false, 370 expectDoError: false, 371 }, 372 } 373 374 for _, tc := range testCases { 375 t.Run(tc.name, func(t *testing.T) { 376 g := NewWithT(t) 377 378 mockControl := gomock.NewController(t) 379 defer mockControl.Finish() 380 381 eksMock := mock_eksiface.NewMockEKSAPI(mockControl) 382 tc.expect(eksMock.EXPECT()) 383 384 ctx := context.TODO() 385 386 planner := NewPlan(clusterName, tc.desiredAddons, tc.installedAddons, eksMock) 387 procedures, err := planner.Create(ctx) 388 if tc.expectCreateError { 389 g.Expect(err).To(HaveOccurred()) 390 return 391 } 392 g.Expect(err).To(BeNil()) 393 g.Expect(procedures).NotTo(BeNil()) 394 395 for _, proc := range procedures { 396 procErr := proc.Do(ctx) 397 if tc.expectDoError { 398 g.Expect(procErr).To(HaveOccurred()) 399 return 400 } 401 g.Expect(procErr).To(BeNil()) 402 } 403 }) 404 } 405 } 406 407 func createTags() infrav1.Tags { 408 tags := infrav1.Tags{} 409 tags["tag1"] = "val1" 410 411 return tags 412 } 413 414 func createTagsAdditional() infrav1.Tags { 415 tags := createTags() 416 tags["tag2"] = "val2" 417 418 return tags 419 } 420 421 func createDesiredAddon(name, version string) *EKSAddon { 422 tags := createTags() 423 424 return &EKSAddon{ 425 Name: &name, 426 Version: &version, 427 Tags: tags, 428 ResolveConflict: aws.String(eks.ResolveConflictsOverwrite), 429 } 430 } 431 432 func createDesiredAddonExtraTag(name, version string) *EKSAddon { 433 tags := createTagsAdditional() 434 435 return &EKSAddon{ 436 Name: &name, 437 Version: &version, 438 Tags: tags, 439 ResolveConflict: aws.String(eks.ResolveConflictsOverwrite), 440 } 441 } 442 443 func createInstalledAddon(name, version, arn, status string) *EKSAddon { 444 desired := createDesiredAddon(name, version) 445 desired.ARN = &arn 446 desired.Status = &status 447 448 return desired 449 }