github.com/openshift/installer@v1.4.17/pkg/asset/installconfig/ibmcloud/metadata_test.go (about) 1 package ibmcloud 2 3 import ( 4 "context" 5 "fmt" 6 "testing" 7 8 "github.com/IBM/platform-services-go-sdk/iamidentityv1" 9 "github.com/IBM/vpc-go-sdk/vpcv1" 10 "github.com/golang/mock/gomock" 11 "github.com/stretchr/testify/assert" 12 13 "github.com/openshift/installer/pkg/asset/installconfig/ibmcloud/mock" 14 "github.com/openshift/installer/pkg/asset/installconfig/ibmcloud/responses" 15 "github.com/openshift/installer/pkg/types" 16 ibmcloudtypes "github.com/openshift/installer/pkg/types/ibmcloud" 17 ) 18 19 type editMetadata []func(m *Metadata) 20 21 var ( 22 // Shared test values. 23 goodDomain = "domain.good.com" 24 badDomain = "domain.bad" 25 region = "us-south" 26 27 // Account ID test values. 28 newAccountID = "new-account-id" 29 existingAccountID = "existing-account-id" 30 31 // CIS Instance CRN test values. 32 newCISCRN = "new-cis-crn" 33 existingCISCRN = "existing-cis-crn" 34 35 // DNS Instance test values. 36 newDNSInstanceID = "new-dns-instance-id" 37 newDNSInstanceCRN = "new-dns-instance-crn" 38 existingDNSInstanceID = "existing-dns-instance-id" 39 existingDNSInstanceCRN = "existing-dns-instance-crn" 40 unknownDNSInstanceID = "unknown-dns-instance-id" 41 unknownDNSInstanceCRN = "unknown-dns-instance-crn" 42 43 // Newly retrieved Compute Subnets. 44 newComputeSubnet1Name = "new-compute-subnet-1" 45 newComputeSubnet1ID = "new-compute-1-id" 46 newComputeSubnet1CIDR = "new-compute-1-cidr" 47 newComputeSubnet1CRN = "new-compute-1-crn" 48 newComputeSubnet1VPCName = "new-compute-1-vpc" 49 newComputeSubnet1ZoneName = "new-compute-1-zone" 50 newComputeSubnet2Name = "new-compute-subnet-2" 51 newComputeSubnet2ID = "new-compute-2-id" 52 newComputeSubnet2CIDR = "new-compute-2-cidr" 53 newComputeSubnet2CRN = "new-compute-2-crn" 54 newComputeSubnet2VPCName = "new-compute-2-vpc" 55 newComputeSubnet2ZoneName = "new-compute-2-zone" 56 newComputeSubnets = map[string]Subnet{ 57 newComputeSubnet1ID: { 58 Name: newComputeSubnet1Name, 59 ID: newComputeSubnet1ID, 60 CIDR: newComputeSubnet1CIDR, 61 CRN: newComputeSubnet1CRN, 62 VPC: newComputeSubnet1VPCName, 63 Zone: newComputeSubnet1ZoneName, 64 }, 65 newComputeSubnet2ID: { 66 Name: newComputeSubnet2Name, 67 ID: newComputeSubnet2ID, 68 CIDR: newComputeSubnet2CIDR, 69 CRN: newComputeSubnet2CRN, 70 VPC: newComputeSubnet2VPCName, 71 Zone: newComputeSubnet2ZoneName, 72 }, 73 } 74 75 // Previously retrieved Compute Subnets. 76 existingComputeSubnets = map[string]Subnet{ 77 "existing-compute-1-id": { 78 Name: "existing-compute-subnet-1", 79 ID: "existing-compute-1-id", 80 CIDR: "existing-compute-1-cidr", 81 CRN: "existing-compute-1-crn", 82 VPC: "existing-compute-1-vpc", 83 Zone: "existing-compute-1-zone", 84 }, 85 "existing-compute-2-id": { 86 Name: "existing-compute-subnet-2", 87 ID: "existing-compute-2-id", 88 CIDR: "existing-compute-2-cidr", 89 CRN: "existing-compute-2-crn", 90 VPC: "existing-compute-2-vpc", 91 Zone: "existing-compute-2-zone", 92 }, 93 } 94 95 // Newly retrieved Control Plane Subnets. 96 newControlPlaneSubnet1Name = "new-cp-subnet-1" 97 newControlPlaneSubnet1ID = "new-cp-1-id" 98 newControlPlaneSubnet1CIDR = "new-cp-1-cidr" 99 newControlPlaneSubnet1CRN = "new-cp-1-crn" 100 newControlPlaneSubnet1VPCName = "new-cp-1-vpc" 101 newControlPlaneSubnet1ZoneName = "new-cp-1-zone" 102 newControlPlaneSubnet2Name = "new-cp-subnet-2" 103 newControlPlaneSubnet2ID = "new-cp-2-id" 104 newControlPlaneSubnet2CIDR = "new-cp-2-cidr" 105 newControlPlaneSubnet2CRN = "new-cp-2-crn" 106 newControlPlaneSubnet2VPCName = "new-cp-2-vpc" 107 newControlPlaneSubnet2ZoneName = "new-cp-2-zone" 108 newControlPlaneSubnets = map[string]Subnet{ 109 newControlPlaneSubnet1ID: { 110 Name: newControlPlaneSubnet1Name, 111 ID: newControlPlaneSubnet1ID, 112 CIDR: newControlPlaneSubnet1CIDR, 113 CRN: newControlPlaneSubnet1CRN, 114 VPC: newControlPlaneSubnet1VPCName, 115 Zone: newControlPlaneSubnet1ZoneName, 116 }, 117 newControlPlaneSubnet2ID: { 118 Name: newControlPlaneSubnet2Name, 119 ID: newControlPlaneSubnet2ID, 120 CIDR: newControlPlaneSubnet2CIDR, 121 CRN: newControlPlaneSubnet2CRN, 122 VPC: newControlPlaneSubnet2VPCName, 123 Zone: newControlPlaneSubnet2ZoneName, 124 }, 125 } 126 127 // Previously retrieved Control Plane Subnets. 128 existingControlPlaneSubnets = map[string]Subnet{ 129 "existing-cp-1-id": { 130 Name: "existing-cp-subnet-1", 131 ID: "existing-cp-1-id", 132 CIDR: "existing-cp-1-cidr", 133 CRN: "existing-cp-1-crn", 134 VPC: "existing-cp-1-vpc", 135 Zone: "existing-cp-1-zone", 136 }, 137 "existing-cp-2-id": { 138 Name: "existing-cp-subnet-2", 139 ID: "existing-cp-2-id", 140 CIDR: "existing-cp-2-cidr", 141 CRN: "existing-cp-2-crn", 142 VPC: "existing-cp-2-vpc", 143 Zone: "existing-cp-2-zone", 144 }, 145 } 146 147 // Subnet Names for failure finding subnets. 148 failedGetComputeSubnetName = "failed-get-compute-subnet" 149 failedGetControlPlaneSubnetName = "failed-get-cp-subnet" 150 151 // Subnet Names and ID's for missing values. 152 noIDComputeSubnetName = "no-id-compute-subnet" 153 incompleteComputeSubnetName = "incomplete-compute-subnet-name" 154 noCIDRComputeSubnetID = "no-cidr-compute-subnet-id" 155 noCRNComputeSubnetID = "no-crn-compute-subnet-id" 156 noNameComputeSubnetID = "no-name-compute-subnet-id" 157 noVPCComputeSubnetID = "no-vpc-compute-subnet-id" 158 noZoneComputeSubnetID = "no-zone-compute-subnet-id" 159 160 // VPCReferences for Client Subnet responses. 161 vpcReferenceComputeSubnet1 = vpcv1.VPCReference{Name: &newComputeSubnet1VPCName} 162 vpcReferenceComputeSubnet2 = vpcv1.VPCReference{Name: &newComputeSubnet2VPCName} 163 vpcReferenceControlPlaneSubnet1 = vpcv1.VPCReference{Name: &newControlPlaneSubnet1VPCName} 164 vpcReferenceControlPlaneSubnet2 = vpcv1.VPCReference{Name: &newControlPlaneSubnet2VPCName} 165 166 // ZoneRefences for Client Subnet responses. 167 zoneReferenceComputeSubnet1 = vpcv1.ZoneReference{Name: &newComputeSubnet1ZoneName} 168 zoneReferenceComputeSubnet2 = vpcv1.ZoneReference{Name: &newComputeSubnet2ZoneName} 169 zoneReferenceControlPlaneSubnet1 = vpcv1.ZoneReference{Name: &newControlPlaneSubnet1ZoneName} 170 zoneReferenceControlPlaneSubnet2 = vpcv1.ZoneReference{Name: &newControlPlaneSubnet2ZoneName} 171 ) 172 173 func baseMetadata() *Metadata { 174 return NewMetadata(&types.InstallConfig{ 175 BaseDomain: goodDomain, 176 Platform: types.Platform{ 177 IBMCloud: &ibmcloudtypes.Platform{ 178 Region: region, 179 }, 180 }, 181 Publish: types.ExternalPublishingStrategy, 182 }) 183 } 184 185 func setInternalPublishingStrategy(m *Metadata) { 186 m.publishStrategy = types.InternalPublishingStrategy 187 } 188 189 func TestAccountID(t *testing.T) { 190 testCases := []struct { 191 name string 192 edits editMetadata 193 errorMsg string 194 expectedValue string 195 }{ 196 { 197 name: "new accountID", 198 expectedValue: newAccountID, 199 }, 200 { 201 name: "existing accountID", 202 edits: editMetadata{ 203 func(m *Metadata) { 204 m.accountID = existingAccountID 205 }, 206 }, 207 expectedValue: existingAccountID, 208 }, 209 { 210 name: "auth apikey error", 211 errorMsg: "bad api key", 212 }, 213 } 214 215 // IBM Cloud Client Mocks. 216 mockCtrl := gomock.NewController(t) 217 defer mockCtrl.Finish() 218 ibmcloudClient := mock.NewMockAPI(mockCtrl) 219 220 // Shared Mocks. 221 ibmcloudClient.EXPECT().SetVPCServiceURLForRegion(gomock.Any(), "us-south").AnyTimes() 222 223 // Mocks: new accountID. 224 ibmcloudClient.EXPECT().GetAuthenticatorAPIKeyDetails(gomock.Any()).Return(&iamidentityv1.APIKey{AccountID: &newAccountID}, nil) 225 226 // Mocks: existing accountID. 227 // N/A. 228 229 // Mocks: auth apikey error. 230 ibmcloudClient.EXPECT().GetAuthenticatorAPIKeyDetails(gomock.Any()).Return(nil, fmt.Errorf("bad api key")) 231 232 for _, tCase := range testCases { 233 t.Run(tCase.name, func(t *testing.T) { 234 metadata := baseMetadata() 235 metadata.client = ibmcloudClient 236 for _, edit := range tCase.edits { 237 edit(metadata) 238 } 239 240 actualValue, err := metadata.AccountID(context.TODO()) 241 if err != nil { 242 assert.Regexp(t, tCase.errorMsg, err) 243 } else { 244 assert.Equal(t, tCase.expectedValue, actualValue) 245 } 246 }) 247 } 248 } 249 250 func TestCISInstanceCRN(t *testing.T) { 251 testCases := []struct { 252 name string 253 edits editMetadata 254 errorMsg string 255 expectedValue string 256 }{ 257 { 258 name: "new cis crn", 259 expectedValue: newCISCRN, 260 }, 261 { 262 name: "existing cis crn", 263 edits: editMetadata{ 264 func(m *Metadata) { 265 m.cisInstanceCRN = existingCISCRN 266 }, 267 }, 268 expectedValue: existingCISCRN, 269 }, 270 { 271 name: "get dns zone error", 272 errorMsg: "dns zone error", 273 }, 274 { 275 name: "dns zone not found error", 276 errorMsg: fmt.Sprintf("cisInstanceCRN unknown due to DNS zone %q not found", goodDomain), 277 }, 278 } 279 280 // IBM Cloud Client Mocks. 281 mockCtrl := gomock.NewController(t) 282 defer mockCtrl.Finish() 283 ibmcloudClient := mock.NewMockAPI(mockCtrl) 284 285 // Shared Mocks. 286 // N/A. 287 288 // Mocks: new cis crn. 289 ibmcloudClient.EXPECT().GetDNSZones(gomock.Any(), types.ExternalPublishingStrategy).Return([]responses.DNSZoneResponse{{Name: goodDomain, InstanceCRN: newCISCRN}}, nil) 290 291 // Mocks: existing cis crn. 292 // N/A. 293 294 // Mocks: get dns zone error. 295 ibmcloudClient.EXPECT().GetDNSZones(gomock.Any(), types.ExternalPublishingStrategy).Return(nil, fmt.Errorf("dns zone error")) 296 297 // Mocks: dns zone not found error. 298 ibmcloudClient.EXPECT().GetDNSZones(gomock.Any(), types.ExternalPublishingStrategy).Return([]responses.DNSZoneResponse{{Name: badDomain}}, nil) 299 300 for _, tCase := range testCases { 301 t.Run(tCase.name, func(t *testing.T) { 302 metadata := baseMetadata() 303 metadata.client = ibmcloudClient 304 for _, edit := range tCase.edits { 305 edit(metadata) 306 } 307 308 actualValue, err := metadata.CISInstanceCRN(context.TODO()) 309 if err != nil { 310 assert.Regexp(t, tCase.errorMsg, err) 311 } else { 312 assert.Equal(t, tCase.expectedValue, actualValue) 313 } 314 }) 315 } 316 } 317 318 func TestSetCISInstanceCRN(t *testing.T) { 319 testCases := []struct { 320 name string 321 cisCRN string 322 }{ 323 { 324 name: "set cis crn", 325 cisCRN: "cis-instance-crn", 326 }, 327 } 328 329 for _, tCase := range testCases { 330 t.Run(tCase.name, func(t *testing.T) { 331 metadata := baseMetadata() 332 333 metadata.cisInstanceCRN = tCase.cisCRN 334 actualValue, err := metadata.CISInstanceCRN(context.TODO()) 335 assert.NoError(t, err) 336 assert.Equal(t, tCase.cisCRN, actualValue) 337 }) 338 } 339 } 340 341 func TestDNSInstance(t *testing.T) { 342 testCases := []struct { 343 name string 344 edits editMetadata 345 errorMsg string 346 expectedID string 347 expectedCRN string 348 }{ 349 { 350 name: "new dns instance", 351 expectedID: newDNSInstanceID, 352 expectedCRN: newDNSInstanceCRN, 353 }, 354 { 355 name: "existing dns instance", 356 edits: editMetadata{ 357 func(m *Metadata) { 358 m.dnsInstance = &DNSInstance{ 359 ID: existingDNSInstanceID, 360 CRN: existingDNSInstanceCRN, 361 } 362 }, 363 }, 364 expectedID: existingDNSInstanceID, 365 expectedCRN: existingDNSInstanceCRN, 366 }, 367 { 368 name: "get dns zone error", 369 errorMsg: "dns zone error", 370 }, 371 { 372 name: "dns instance unknown id error", 373 errorMsg: fmt.Sprintf("dnsInstance has unknown ID/CRN: %q - %q", "", unknownDNSInstanceCRN), 374 }, 375 { 376 name: "dns instance unknown crn error", 377 errorMsg: fmt.Sprintf("dnsInstance has unknown ID/CRN: %q - %q", unknownDNSInstanceID, ""), 378 }, 379 { 380 name: "dns zone not found error", 381 errorMsg: fmt.Sprintf("dnsInstance unknown due to DNS zone %q not found", goodDomain), 382 }, 383 } 384 385 // IBM Cloud Client Mocks. 386 mockCtrl := gomock.NewController(t) 387 defer mockCtrl.Finish() 388 ibmcloudClient := mock.NewMockAPI(mockCtrl) 389 390 // Shared Mocks. 391 // N/A. 392 393 // Mocks: new dns instance. 394 ibmcloudClient.EXPECT().GetDNSZones(gomock.Any(), types.InternalPublishingStrategy).Return([]responses.DNSZoneResponse{{Name: goodDomain, InstanceID: newDNSInstanceID, InstanceCRN: newDNSInstanceCRN}}, nil) 395 396 // Mocks: existing dns instance. 397 // N/A. 398 399 // Mocks: get dns zone error. 400 ibmcloudClient.EXPECT().GetDNSZones(gomock.Any(), types.InternalPublishingStrategy).Return(nil, fmt.Errorf("dns zone error")) 401 402 // Mocks: dns instance unknown id error. 403 ibmcloudClient.EXPECT().GetDNSZones(gomock.Any(), types.InternalPublishingStrategy).Return([]responses.DNSZoneResponse{{Name: goodDomain, InstanceCRN: unknownDNSInstanceCRN}}, nil) 404 405 // Mocks: dns instance unknown crn error. 406 ibmcloudClient.EXPECT().GetDNSZones(gomock.Any(), types.InternalPublishingStrategy).Return([]responses.DNSZoneResponse{{Name: goodDomain, InstanceID: unknownDNSInstanceID}}, nil) 407 408 // Mocks: dns zone not found error. 409 ibmcloudClient.EXPECT().GetDNSZones(gomock.Any(), types.InternalPublishingStrategy).Return([]responses.DNSZoneResponse{{Name: badDomain}}, nil) 410 411 for _, tCase := range testCases { 412 t.Run(tCase.name, func(t *testing.T) { 413 metadata := baseMetadata() 414 setInternalPublishingStrategy(metadata) 415 metadata.client = ibmcloudClient 416 for _, edit := range tCase.edits { 417 edit(metadata) 418 } 419 420 actualDNS, err := metadata.DNSInstance(context.TODO()) 421 if err != nil { 422 assert.Regexp(t, tCase.errorMsg, err) 423 } else { 424 assert.Equal(t, tCase.expectedID, actualDNS.ID) 425 assert.Equal(t, tCase.expectedCRN, actualDNS.CRN) 426 } 427 }) 428 } 429 } 430 431 func TestSetDNSInstance(t *testing.T) { 432 testCases := []struct { 433 name string 434 dnsID string 435 dnsCRN string 436 }{ 437 { 438 name: "set dns id/crn", 439 dnsID: "dns-instance-id", 440 dnsCRN: "dns-instance-crn", 441 }, 442 } 443 444 for _, tCase := range testCases { 445 t.Run(tCase.name, func(t *testing.T) { 446 metadata := baseMetadata() 447 setInternalPublishingStrategy(metadata) 448 449 metadata.dnsInstance = &DNSInstance{ 450 ID: tCase.dnsID, 451 CRN: tCase.dnsCRN, 452 } 453 actualDNS, err := metadata.DNSInstance(context.TODO()) 454 assert.NoError(t, err) 455 assert.Equal(t, tCase.dnsID, actualDNS.ID) 456 assert.Equal(t, tCase.dnsCRN, actualDNS.CRN) 457 }) 458 } 459 } 460 461 func TestComputeSubnets(t *testing.T) { 462 testCases := []struct { 463 name string 464 edits editMetadata 465 errorMsg string 466 expectedValue map[string]Subnet 467 }{ 468 { 469 name: "no compute subnets", 470 expectedValue: nil, 471 }, 472 { 473 name: "new compute subnets", 474 edits: editMetadata{ 475 func(m *Metadata) { 476 m.ComputeSubnetNames = []string{newComputeSubnet1Name, newComputeSubnet2Name} 477 }, 478 }, 479 expectedValue: newComputeSubnets, 480 }, 481 { 482 name: "new single compute subnet", 483 edits: editMetadata{ 484 func(m *Metadata) { 485 m.ComputeSubnetNames = []string{newComputeSubnet2Name} 486 }, 487 }, 488 expectedValue: map[string]Subnet{ 489 newComputeSubnet2ID: newComputeSubnets[newComputeSubnet2ID], 490 }, 491 }, 492 { 493 name: "existing compute subnets", 494 edits: editMetadata{ 495 func(m *Metadata) { 496 m.computeSubnets = existingComputeSubnets 497 }, 498 }, 499 expectedValue: existingComputeSubnets, 500 }, 501 { 502 name: "failed getting compute subnet", 503 edits: editMetadata{ 504 func(m *Metadata) { 505 m.ComputeSubnetNames = []string{failedGetComputeSubnetName} 506 }, 507 }, 508 errorMsg: fmt.Sprintf("getting subnet %s", failedGetComputeSubnetName), 509 }, 510 { 511 name: "compute subnet has no id", 512 edits: editMetadata{ 513 func(m *Metadata) { 514 m.ComputeSubnetNames = []string{noIDComputeSubnetName} 515 }, 516 }, 517 errorMsg: fmt.Sprintf("%s has no ID", noIDComputeSubnetName), 518 }, 519 { 520 name: "compute subnet has no CIDR block", 521 edits: editMetadata{ 522 func(m *Metadata) { 523 m.ComputeSubnetNames = []string{incompleteComputeSubnetName} 524 }, 525 }, 526 errorMsg: fmt.Sprintf("%s has no Ipv4CIDRBlock", noCIDRComputeSubnetID), 527 }, 528 { 529 name: "compute subnet has no CRN", 530 edits: editMetadata{ 531 func(m *Metadata) { 532 m.ComputeSubnetNames = []string{incompleteComputeSubnetName} 533 }, 534 }, 535 errorMsg: fmt.Sprintf("%s has no CRN", noCRNComputeSubnetID), 536 }, 537 { 538 name: "compute subnet has no Name", 539 edits: editMetadata{ 540 func(m *Metadata) { 541 m.ComputeSubnetNames = []string{incompleteComputeSubnetName} 542 }, 543 }, 544 errorMsg: fmt.Sprintf("%s has no Name", noNameComputeSubnetID), 545 }, 546 { 547 name: "compute subnet has no VPC", 548 edits: editMetadata{ 549 func(m *Metadata) { 550 m.ComputeSubnetNames = []string{incompleteComputeSubnetName} 551 }, 552 }, 553 errorMsg: fmt.Sprintf("%s has no VPC", noVPCComputeSubnetID), 554 }, 555 { 556 name: "compute subnet has no Zone", 557 edits: editMetadata{ 558 func(m *Metadata) { 559 m.ComputeSubnetNames = []string{incompleteComputeSubnetName} 560 }, 561 }, 562 errorMsg: fmt.Sprintf("%s has no Zone", noZoneComputeSubnetID), 563 }, 564 } 565 566 // IBM Cloud Client Mocks. 567 mockCtrl := gomock.NewController(t) 568 defer mockCtrl.Finish() 569 ibmcloudClient := mock.NewMockAPI(mockCtrl) 570 571 // Shared Mocks. 572 // N/A. 573 574 // Mocks: no compute subnets. 575 // N/A. 576 577 // Mocks: new compute subnets. 578 ibmcloudClient.EXPECT().GetSubnetByName(gomock.Any(), newComputeSubnet1Name, region).Return( 579 &vpcv1.Subnet{ 580 Name: &newComputeSubnet1Name, 581 ID: &newComputeSubnet1ID, 582 Ipv4CIDRBlock: &newComputeSubnet1CIDR, 583 CRN: &newComputeSubnet1CRN, 584 VPC: &vpcReferenceComputeSubnet1, 585 Zone: &zoneReferenceComputeSubnet1, 586 }, 587 nil, 588 ) 589 ibmcloudClient.EXPECT().GetSubnetByName(gomock.Any(), newComputeSubnet2Name, region).Return( 590 &vpcv1.Subnet{ 591 Name: &newComputeSubnet2Name, 592 ID: &newComputeSubnet2ID, 593 Ipv4CIDRBlock: &newComputeSubnet2CIDR, 594 CRN: &newComputeSubnet2CRN, 595 VPC: &vpcReferenceComputeSubnet2, 596 Zone: &zoneReferenceComputeSubnet2, 597 }, 598 nil, 599 ) 600 601 // Mocks: new single compute subnet. 602 ibmcloudClient.EXPECT().GetSubnetByName(gomock.Any(), newComputeSubnet2Name, region).Return( 603 &vpcv1.Subnet{ 604 Name: &newComputeSubnet2Name, 605 ID: &newComputeSubnet2ID, 606 Ipv4CIDRBlock: &newComputeSubnet2CIDR, 607 CRN: &newComputeSubnet2CRN, 608 VPC: &vpcReferenceComputeSubnet2, 609 Zone: &zoneReferenceComputeSubnet2, 610 }, 611 nil, 612 ) 613 614 // Mocks: existing compute subnets. 615 // N/A. 616 617 // Mocks: failed getting compute subnet. 618 ibmcloudClient.EXPECT().GetSubnetByName(gomock.Any(), failedGetComputeSubnetName, region).Return(nil, &VPCResourceNotFoundError{}) 619 620 // Mocks: compute subnet has no ID. 621 ibmcloudClient.EXPECT().GetSubnetByName(gomock.Any(), noIDComputeSubnetName, region).Return( 622 &vpcv1.Subnet{ 623 Name: &noIDComputeSubnetName, 624 // ID Skipped. 625 Ipv4CIDRBlock: &newComputeSubnet1CIDR, 626 CRN: &newComputeSubnet1CRN, 627 VPC: &vpcReferenceComputeSubnet1, 628 Zone: &zoneReferenceComputeSubnet1, 629 }, 630 nil, 631 ) 632 633 // Mocks: compute subnet has no CIDR. 634 ibmcloudClient.EXPECT().GetSubnetByName(gomock.Any(), incompleteComputeSubnetName, region).Return( 635 &vpcv1.Subnet{ 636 Name: &incompleteComputeSubnetName, 637 ID: &noCIDRComputeSubnetID, 638 // Ipv4CIDRBlock Skipped. 639 CRN: &newComputeSubnet1CRN, 640 VPC: &vpcReferenceComputeSubnet1, 641 Zone: &zoneReferenceComputeSubnet1, 642 }, 643 nil, 644 ) 645 646 // Mocks: compute subnet has no CRN. 647 ibmcloudClient.EXPECT().GetSubnetByName(gomock.Any(), incompleteComputeSubnetName, region).Return( 648 &vpcv1.Subnet{ 649 Name: &incompleteComputeSubnetName, 650 ID: &noCRNComputeSubnetID, 651 Ipv4CIDRBlock: &newComputeSubnet1CIDR, 652 // CRN Skipped. 653 VPC: &vpcReferenceComputeSubnet1, 654 Zone: &zoneReferenceComputeSubnet1, 655 }, 656 nil, 657 ) 658 659 // Mocks: compute subnet has no Name. 660 ibmcloudClient.EXPECT().GetSubnetByName(gomock.Any(), incompleteComputeSubnetName, region).Return( 661 &vpcv1.Subnet{ 662 // Name Skipped. 663 ID: &noNameComputeSubnetID, 664 Ipv4CIDRBlock: &newComputeSubnet1CIDR, 665 CRN: &newComputeSubnet1CRN, 666 VPC: &vpcReferenceComputeSubnet1, 667 Zone: &zoneReferenceComputeSubnet1, 668 }, 669 nil, 670 ) 671 672 // Mocks: compute subnet has no VPC. 673 ibmcloudClient.EXPECT().GetSubnetByName(gomock.Any(), incompleteComputeSubnetName, region).Return( 674 &vpcv1.Subnet{ 675 Name: &incompleteComputeSubnetName, 676 ID: &noVPCComputeSubnetID, 677 Ipv4CIDRBlock: &newComputeSubnet1CIDR, 678 CRN: &newComputeSubnet1CRN, 679 // VPC Skipped. 680 Zone: &zoneReferenceComputeSubnet1, 681 }, 682 nil, 683 ) 684 685 // Mocks: compute subnet has no Zone. 686 ibmcloudClient.EXPECT().GetSubnetByName(gomock.Any(), incompleteComputeSubnetName, region).Return( 687 &vpcv1.Subnet{ 688 Name: &incompleteComputeSubnetName, 689 ID: &noZoneComputeSubnetID, 690 Ipv4CIDRBlock: &newComputeSubnet1CIDR, 691 CRN: &newComputeSubnet1CRN, 692 VPC: &vpcReferenceComputeSubnet1, 693 // Zone Skipped. 694 }, 695 nil, 696 ) 697 698 for _, tCase := range testCases { 699 t.Run(tCase.name, func(t *testing.T) { 700 metadata := baseMetadata() 701 metadata.client = ibmcloudClient 702 703 for _, edit := range tCase.edits { 704 edit(metadata) 705 } 706 707 actualValue, err := metadata.ComputeSubnets(context.TODO()) 708 if err != nil { 709 assert.Regexp(t, tCase.errorMsg, err) 710 } else { 711 assert.Equal(t, tCase.expectedValue, actualValue) 712 } 713 }) 714 } 715 } 716 717 func TestControlPlaneSubnets(t *testing.T) { 718 testCases := []struct { 719 name string 720 edits editMetadata 721 errorMsg string 722 expectedValue map[string]Subnet 723 }{ 724 { 725 name: "no control plane subnets", 726 expectedValue: nil, 727 }, 728 { 729 name: "new control plane subnets", 730 edits: editMetadata{ 731 func(m *Metadata) { 732 m.ControlPlaneSubnetNames = []string{newControlPlaneSubnet1Name, newControlPlaneSubnet2Name} 733 }, 734 }, 735 expectedValue: newControlPlaneSubnets, 736 }, 737 { 738 name: "new single control plane subnet", 739 edits: editMetadata{ 740 func(m *Metadata) { 741 m.ControlPlaneSubnetNames = []string{newControlPlaneSubnet2Name} 742 }, 743 }, 744 expectedValue: map[string]Subnet{ 745 newControlPlaneSubnet2ID: newControlPlaneSubnets[newControlPlaneSubnet2ID], 746 }, 747 }, 748 { 749 name: "existing control plane subnets", 750 edits: editMetadata{ 751 func(m *Metadata) { 752 m.controlPlaneSubnets = existingControlPlaneSubnets 753 }, 754 }, 755 expectedValue: existingControlPlaneSubnets, 756 }, 757 { 758 name: "failed getting control plane subnet", 759 edits: editMetadata{ 760 func(m *Metadata) { 761 m.ControlPlaneSubnetNames = []string{failedGetControlPlaneSubnetName} 762 }, 763 }, 764 errorMsg: fmt.Sprintf("getting subnet %v", failedGetControlPlaneSubnetName), 765 }, 766 } 767 768 // IBM Cloud Client Mocks. 769 mockCtrl := gomock.NewController(t) 770 defer mockCtrl.Finish() 771 ibmcloudClient := mock.NewMockAPI(mockCtrl) 772 773 // Shared Mocks. 774 // N/A. 775 776 // Mocks: no control plane subnets. 777 // N/A. 778 779 // Mocks: new control plane subnets. 780 ibmcloudClient.EXPECT().GetSubnetByName(gomock.Any(), newControlPlaneSubnet1Name, region).Return( 781 &vpcv1.Subnet{ 782 Name: &newControlPlaneSubnet1Name, 783 ID: &newControlPlaneSubnet1ID, 784 Ipv4CIDRBlock: &newControlPlaneSubnet1CIDR, 785 CRN: &newControlPlaneSubnet1CRN, 786 VPC: &vpcReferenceControlPlaneSubnet1, 787 Zone: &zoneReferenceControlPlaneSubnet1, 788 }, 789 nil, 790 ) 791 ibmcloudClient.EXPECT().GetSubnetByName(gomock.Any(), newControlPlaneSubnet2Name, region).Return( 792 &vpcv1.Subnet{ 793 Name: &newControlPlaneSubnet2Name, 794 ID: &newControlPlaneSubnet2ID, 795 Ipv4CIDRBlock: &newControlPlaneSubnet2CIDR, 796 CRN: &newControlPlaneSubnet2CRN, 797 VPC: &vpcReferenceControlPlaneSubnet2, 798 Zone: &zoneReferenceControlPlaneSubnet2, 799 }, 800 nil, 801 ) 802 803 // Mocks: new single control plane subnet. 804 ibmcloudClient.EXPECT().GetSubnetByName(gomock.Any(), newControlPlaneSubnet2Name, region).Return( 805 &vpcv1.Subnet{ 806 Name: &newControlPlaneSubnet2Name, 807 ID: &newControlPlaneSubnet2ID, 808 Ipv4CIDRBlock: &newControlPlaneSubnet2CIDR, 809 CRN: &newControlPlaneSubnet2CRN, 810 VPC: &vpcReferenceControlPlaneSubnet2, 811 Zone: &zoneReferenceControlPlaneSubnet2, 812 }, 813 nil, 814 ) 815 816 // Mocks: existing control plane subnets. 817 // N/A. 818 819 // Mocks: failed getting control plane subnet. 820 ibmcloudClient.EXPECT().GetSubnetByName(gomock.Any(), failedGetControlPlaneSubnetName, region).Return(nil, &VPCResourceNotFoundError{}) 821 822 for _, tCase := range testCases { 823 t.Run(tCase.name, func(t *testing.T) { 824 metadata := baseMetadata() 825 metadata.client = ibmcloudClient 826 827 for _, edit := range tCase.edits { 828 edit(metadata) 829 } 830 831 actualValue, err := metadata.ControlPlaneSubnets(context.TODO()) 832 if err != nil { 833 assert.Regexp(t, tCase.errorMsg, err) 834 } else { 835 assert.Equal(t, tCase.expectedValue, actualValue) 836 } 837 }) 838 } 839 } 840 841 func TestClient(t *testing.T) { 842 mockCtrl := gomock.NewController(t) 843 defer mockCtrl.Finish() 844 ibmcloudClient := mock.NewMockAPI(mockCtrl) 845 846 testCases := []struct { 847 name string 848 edits editMetadata 849 errorMsg string 850 expectedValue API 851 }{ 852 { 853 name: "existing client", 854 edits: editMetadata{ 855 func(m *Metadata) { 856 m.client = ibmcloudClient 857 }, 858 }, 859 expectedValue: ibmcloudClient, 860 }, 861 } 862 863 // IBM Cloud Client Mocks. 864 // N/A. 865 866 for _, tCase := range testCases { 867 t.Run(tCase.name, func(t *testing.T) { 868 metadata := baseMetadata() 869 870 actualValue, err := metadata.Client() 871 if err != nil { 872 assert.Regexp(t, tCase.errorMsg, err) 873 } else { 874 assert.Equal(t, tCase.expectedValue, actualValue) 875 } 876 }) 877 } 878 }