From ababdecbd00664df46c9e43e6142198840b9666d Mon Sep 17 00:00:00 2001 From: Caden Marchese <56140267+cadenmarchese@users.noreply.github.com> Date: Thu, 12 May 2022 09:45:21 -0400 Subject: [PATCH] il5 series support, vm.go improvements and tests (#2086) --- pkg/api/admin/openshiftcluster.go | 18 ++-- pkg/api/openshiftcluster.go | 18 ++-- pkg/api/validate/dynamic/quota.go | 18 ++-- pkg/api/validate/dynamic/sku_test.go | 60 ++++++++++++- pkg/api/validate/vm.go | 130 ++++++++++++++++----------- pkg/api/validate/vm_test.go | 105 ++++++++++++++++++++++ 6 files changed, 275 insertions(+), 74 deletions(-) create mode 100644 pkg/api/validate/vm_test.go diff --git a/pkg/api/admin/openshiftcluster.go b/pkg/api/admin/openshiftcluster.go index ad65544d402..a7924468c93 100644 --- a/pkg/api/admin/openshiftcluster.go +++ b/pkg/api/admin/openshiftcluster.go @@ -176,12 +176,18 @@ const ( VMSizeStandardD16sV3 VMSize = "Standard_D16s_v3" VMSizeStandardD32sV3 VMSize = "Standard_D32s_v3" - VMSizeStandardE4sV3 VMSize = "Standard_E4s_v3" - VMSizeStandardE8sV3 VMSize = "Standard_E8s_v3" - VMSizeStandardE16sV3 VMSize = "Standard_E16s_v3" - VMSizeStandardE32sV3 VMSize = "Standard_E32s_v3" - VMSizeStandardE64isV3 VMSize = "Standard_E64is_v3" - VMSizeStandardE64iV3 VMSize = "Standard_E64i_v3" + VMSizeStandardE4sV3 VMSize = "Standard_E4s_v3" + VMSizeStandardE8sV3 VMSize = "Standard_E8s_v3" + VMSizeStandardE16sV3 VMSize = "Standard_E16s_v3" + VMSizeStandardE32sV3 VMSize = "Standard_E32s_v3" + VMSizeStandardE64isV3 VMSize = "Standard_E64is_v3" + VMSizeStandardE64iV3 VMSize = "Standard_E64i_v3" + VMSizeStandardE80isV4 VMSize = "Standard_E80is_v4" + VMSizeStandardE80idsV4 VMSize = "Standard_E80ids_v4" + VMSizeStandardE104iV5 VMSize = "Standard_E104i_v5" + VMSizeStandardE104isV5 VMSize = "Standard_E104is_v5" + VMSizeStandardE104idV5 VMSize = "Standard_E104id_v5" + VMSizeStandardE104idsV5 VMSize = "Standard_E104ids_v5" VMSizeStandardF4sV2 VMSize = "Standard_F4s_v2" VMSizeStandardF8sV2 VMSize = "Standard_F8s_v2" diff --git a/pkg/api/openshiftcluster.go b/pkg/api/openshiftcluster.go index 5635fae8bb4..65bf994cf89 100644 --- a/pkg/api/openshiftcluster.go +++ b/pkg/api/openshiftcluster.go @@ -294,12 +294,18 @@ const ( VMSizeStandardD16sV3 VMSize = "Standard_D16s_v3" VMSizeStandardD32sV3 VMSize = "Standard_D32s_v3" - VMSizeStandardE4sV3 VMSize = "Standard_E4s_v3" - VMSizeStandardE8sV3 VMSize = "Standard_E8s_v3" - VMSizeStandardE16sV3 VMSize = "Standard_E16s_v3" - VMSizeStandardE32sV3 VMSize = "Standard_E32s_v3" - VMSizeStandardE64isV3 VMSize = "Standard_E64is_v3" - VMSizeStandardE64iV3 VMSize = "Standard_E64i_v3" + VMSizeStandardE4sV3 VMSize = "Standard_E4s_v3" + VMSizeStandardE8sV3 VMSize = "Standard_E8s_v3" + VMSizeStandardE16sV3 VMSize = "Standard_E16s_v3" + VMSizeStandardE32sV3 VMSize = "Standard_E32s_v3" + VMSizeStandardE64isV3 VMSize = "Standard_E64is_v3" + VMSizeStandardE64iV3 VMSize = "Standard_E64i_v3" + VMSizeStandardE80isV4 VMSize = "Standard_E80is_v4" + VMSizeStandardE80idsV4 VMSize = "Standard_E80ids_v4" + VMSizeStandardE104iV5 VMSize = "Standard_E104i_v5" + VMSizeStandardE104isV5 VMSize = "Standard_E104is_v5" + VMSizeStandardE104idV5 VMSize = "Standard_E104id_v5" + VMSizeStandardE104idsV5 VMSize = "Standard_E104ids_v5" VMSizeStandardF4sV2 VMSize = "Standard_F4s_v2" VMSizeStandardF8sV2 VMSize = "Standard_F8s_v2" diff --git a/pkg/api/validate/dynamic/quota.go b/pkg/api/validate/dynamic/quota.go index 5fa0d58d84f..61edc8c74cd 100644 --- a/pkg/api/validate/dynamic/quota.go +++ b/pkg/api/validate/dynamic/quota.go @@ -28,12 +28,18 @@ func addRequiredResources(requiredResources map[string]int, vmSize api.VMSize, c api.VMSizeStandardD16sV3: {CoreCount: 16, Family: "standardDSv3Family"}, api.VMSizeStandardD32sV3: {CoreCount: 32, Family: "standardDSv3Family"}, - api.VMSizeStandardE4sV3: {CoreCount: 4, Family: "standardESv3Family"}, - api.VMSizeStandardE8sV3: {CoreCount: 8, Family: "standardESv3Family"}, - api.VMSizeStandardE16sV3: {CoreCount: 16, Family: "standardESv3Family"}, - api.VMSizeStandardE32sV3: {CoreCount: 32, Family: "standardESv3Family"}, - api.VMSizeStandardE64isV3: {CoreCount: 64, Family: "standardESv3Family"}, - api.VMSizeStandardE64iV3: {CoreCount: 64, Family: "standardESv3Family"}, + api.VMSizeStandardE4sV3: {CoreCount: 4, Family: "standardESv3Family"}, + api.VMSizeStandardE8sV3: {CoreCount: 8, Family: "standardESv3Family"}, + api.VMSizeStandardE16sV3: {CoreCount: 16, Family: "standardESv3Family"}, + api.VMSizeStandardE32sV3: {CoreCount: 32, Family: "standardESv3Family"}, + api.VMSizeStandardE64isV3: {CoreCount: 64, Family: "standardESv3Family"}, + api.VMSizeStandardE64iV3: {CoreCount: 64, Family: "standardESv3Family"}, + api.VMSizeStandardE80isV4: {CoreCount: 80, Family: "standardEISv4Family"}, + api.VMSizeStandardE80idsV4: {CoreCount: 80, Family: "standardEIDSv4Family"}, + api.VMSizeStandardE104iV5: {CoreCount: 104, Family: "standardEIv5Family"}, + api.VMSizeStandardE104isV5: {CoreCount: 104, Family: "standardEISv5Family"}, + api.VMSizeStandardE104idV5: {CoreCount: 104, Family: "standardEIDv5Family"}, + api.VMSizeStandardE104idsV5: {CoreCount: 104, Family: "standardEIDSv5Family"}, api.VMSizeStandardF4sV2: {CoreCount: 4, Family: "standardFSv2Family"}, api.VMSizeStandardF8sV2: {CoreCount: 8, Family: "standardFSv2Family"}, diff --git a/pkg/api/validate/dynamic/sku_test.go b/pkg/api/validate/dynamic/sku_test.go index 1a31b013dc4..71c35aa0641 100644 --- a/pkg/api/validate/dynamic/sku_test.go +++ b/pkg/api/validate/dynamic/sku_test.go @@ -23,22 +23,50 @@ func TestValidateVMSku(t *testing.T) { name string restrictions mgmtcompute.ResourceSkuRestrictionsReasonCode restrictionLocation *[]string + restrictedZones []string targetLocation string workerProfile1Sku string workerProfile2Sku string masterProfileSku string availableSku string + availableSku2 string restrictedSku string resourceSkusClientErr error wantErr string }{ { - name: "worker and master sku are valid", + name: "worker and master skus are valid", workerProfile1Sku: "Standard_D4s_v2", workerProfile2Sku: "Standard_D4s_v2", masterProfileSku: "Standard_D4s_v2", availableSku: "Standard_D4s_v2", }, + { + name: "worker and master skus are distinct, both valid", + workerProfile1Sku: "Standard_E104i_v5", + workerProfile2Sku: "Standard_E104i_v5", + masterProfileSku: "Standard_D4s_v2", + availableSku: "Standard_E104i_v5", + availableSku2: "Standard_D4s_v2", + }, + { + name: "worker and master skus are distinct, one invalid", + workerProfile1Sku: "Standard_E104i_v5", + workerProfile2Sku: "Standard_E104i_v5", + masterProfileSku: "Standard_D4s_v2", + availableSku: "Standard_E104i_v5", + availableSku2: "Standard_E104i_v5", + wantErr: "400: InvalidParameter: properties.masterProfile.VMSize: The selected SKU 'Standard_D4s_v2' is unavailable in region 'eastus'", + }, + { + name: "worker and master skus are distinct, both invalid", + workerProfile1Sku: "Standard_E104i_v5", + workerProfile2Sku: "Standard_E104i_v5", + masterProfileSku: "Standard_D4s_v2", + availableSku: "Standard_L8s_v2", + availableSku2: "Standard_L16s_v2", + wantErr: "400: InvalidParameter: properties.masterProfile.VMSize: The selected SKU 'Standard_D4s_v2' is unavailable in region 'eastus'", + }, { name: "unable to retrieve skus information", workerProfile1Sku: "Standard_D4s_v2", @@ -96,12 +124,30 @@ func TestValidateVMSku(t *testing.T) { restrictedSku: "Standard_L80", wantErr: "400: InvalidParameter: properties.masterProfile.VMSize: The selected SKU 'Standard_L80' is restricted in region 'eastus' for selected subscription", }, + { + name: "sku is restricted in a single zone", + restrictions: mgmtcompute.NotAvailableForSubscription, + restrictionLocation: &[]string{ + "eastus", + }, + restrictedZones: []string{"3"}, + workerProfile1Sku: "Standard_D4s_v2", + workerProfile2Sku: "Standard_D4s_v2", + masterProfileSku: "Standard_L80", + availableSku: "Standard_D4s_v2", + restrictedSku: "Standard_L80", + wantErr: "400: InvalidParameter: properties.masterProfile.VMSize: The selected SKU 'Standard_L80' is restricted in region 'eastus' for selected subscription", + }, } { t.Run(tt.name, func(t *testing.T) { if tt.targetLocation == "" { tt.targetLocation = "eastus" } + if tt.restrictedZones == nil { + tt.restrictedZones = []string{"1", "2", "3"} + } + controller := gomock.NewController(t) defer controller.Finish() @@ -132,11 +178,21 @@ func TestValidateVMSku(t *testing.T) { Capabilities: &[]mgmtcompute.ResourceSkuCapabilities{}, ResourceType: to.StringPtr("virtualMachines"), }, + { + Name: &tt.availableSku2, + Locations: &[]string{"eastus"}, + LocationInfo: &[]mgmtcompute.ResourceSkuLocationInfo{ + {Zones: &[]string{"1, 2, 3"}}, + }, + Restrictions: &[]mgmtcompute.ResourceSkuRestrictions{}, + Capabilities: &[]mgmtcompute.ResourceSkuCapabilities{}, + ResourceType: to.StringPtr("virtualMachines"), + }, { Name: &tt.restrictedSku, Locations: &[]string{tt.targetLocation}, LocationInfo: &[]mgmtcompute.ResourceSkuLocationInfo{ - {Zones: &[]string{"1, 2, 3"}}, + {Zones: &tt.restrictedZones}, }, Restrictions: &[]mgmtcompute.ResourceSkuRestrictions{ { diff --git a/pkg/api/validate/vm.go b/pkg/api/validate/vm.go index 79f8b19b552..88da91571fb 100644 --- a/pkg/api/validate/vm.go +++ b/pkg/api/validate/vm.go @@ -7,66 +7,88 @@ import ( "github.com/Azure/ARO-RP/pkg/api" ) +var supportedMasterVMSizes = map[api.VMSize]bool{ + // General purpose + api.VMSizeStandardD8sV3: true, + api.VMSizeStandardD16sV3: true, + api.VMSizeStandardD32sV3: true, + // Memory optimized + api.VMSizeStandardE64iV3: true, + api.VMSizeStandardE64isV3: true, + api.VMSizeStandardE80isV4: true, + api.VMSizeStandardE80idsV4: true, + api.VMSizeStandardE104iV5: true, + api.VMSizeStandardE104isV5: true, + api.VMSizeStandardE104idV5: true, + api.VMSizeStandardE104idsV5: true, + // Compute optimized + api.VMSizeStandardF72sV2: true, + // Memory and storage optimized + api.VMSizeStandardGS5: true, + api.VMSizeStandardG5: true, + // Memory and compute optimized + api.VMSizeStandardM128ms: true, +} + +var supportedWorkerVMSizes = map[api.VMSize]bool{ + // General purpose + api.VMSizeStandardD4asV4: true, + api.VMSizeStandardD8asV4: true, + api.VMSizeStandardD16asV4: true, + api.VMSizeStandardD32asV4: true, + api.VMSizeStandardD4sV3: true, + api.VMSizeStandardD8sV3: true, + api.VMSizeStandardD16sV3: true, + api.VMSizeStandardD32sV3: true, + // Memory optimized + api.VMSizeStandardE4sV3: true, + api.VMSizeStandardE8sV3: true, + api.VMSizeStandardE16sV3: true, + api.VMSizeStandardE32sV3: true, + api.VMSizeStandardE64isV3: true, + api.VMSizeStandardE64iV3: true, + api.VMSizeStandardE80isV4: true, + api.VMSizeStandardE80idsV4: true, + api.VMSizeStandardE104iV5: true, + api.VMSizeStandardE104isV5: true, + api.VMSizeStandardE104idV5: true, + api.VMSizeStandardE104idsV5: true, + // Compute optimized + api.VMSizeStandardF4sV2: true, + api.VMSizeStandardF8sV2: true, + api.VMSizeStandardF16sV2: true, + api.VMSizeStandardF32sV2: true, + api.VMSizeStandardF72sV2: true, + // Memory and storage optimized + api.VMSizeStandardG5: true, + api.VMSizeStandardGS5: true, + // Memory and compute optimized + api.VMSizeStandardM128ms: true, + // Storage optimized + api.VMSizeStandardL4s: true, + api.VMSizeStandardL8s: true, + api.VMSizeStandardL16s: true, + api.VMSizeStandardL32s: true, + api.VMSizeStandardL8sV2: true, + api.VMSizeStandardL16sV2: true, + api.VMSizeStandardL32sV2: true, + api.VMSizeStandardL48sV2: true, + api.VMSizeStandardL64sV2: true, +} + func DiskSizeIsValid(sizeGB int) bool { return sizeGB >= 128 } func VMSizeIsValid(vmSize api.VMSize, requiredD2sV3Workers, isMaster bool) bool { if isMaster { - switch vmSize { - case api.VMSizeStandardD8sV3, - api.VMSizeStandardD16sV3, - api.VMSizeStandardD32sV3, - api.VMSizeStandardE64iV3, - api.VMSizeStandardE64isV3, - api.VMSizeStandardF72sV2, - api.VMSizeStandardGS5, - api.VMSizeStandardG5, - api.VMSizeStandardM128ms: - return true - } - } else { - if requiredD2sV3Workers { - switch vmSize { - case api.VMSizeStandardD2sV3: - return true - } - } else { - switch vmSize { - case api.VMSizeStandardD4asV4, - api.VMSizeStandardD8asV4, - api.VMSizeStandardD16asV4, - api.VMSizeStandardD32asV4, - api.VMSizeStandardD4sV3, - api.VMSizeStandardD8sV3, - api.VMSizeStandardD16sV3, - api.VMSizeStandardD32sV3, - api.VMSizeStandardE4sV3, - api.VMSizeStandardE8sV3, - api.VMSizeStandardE16sV3, - api.VMSizeStandardE32sV3, - api.VMSizeStandardE64iV3, - api.VMSizeStandardE64isV3, - api.VMSizeStandardF4sV2, - api.VMSizeStandardF8sV2, - api.VMSizeStandardF16sV2, - api.VMSizeStandardF32sV2, - api.VMSizeStandardF72sV2, - api.VMSizeStandardG5, - api.VMSizeStandardGS5, - api.VMSizeStandardM128ms, - api.VMSizeStandardL4s, - api.VMSizeStandardL8s, - api.VMSizeStandardL16s, - api.VMSizeStandardL32s, - api.VMSizeStandardL8sV2, - api.VMSizeStandardL16sV2, - api.VMSizeStandardL32sV2, - api.VMSizeStandardL48sV2, - api.VMSizeStandardL64sV2: - return true - } - } + return supportedMasterVMSizes[vmSize] } + + if (supportedWorkerVMSizes[vmSize] && !requiredD2sV3Workers) || + (requiredD2sV3Workers && vmSize == api.VMSizeStandardD2sV3) { + return true + } + return false } diff --git a/pkg/api/validate/vm_test.go b/pkg/api/validate/vm_test.go new file mode 100644 index 00000000000..b2d551cb2d4 --- /dev/null +++ b/pkg/api/validate/vm_test.go @@ -0,0 +1,105 @@ +package validate + +// Copyright (c) Microsoft Corporation. +// Licensed under the Apache License 2.0. + +import ( + "testing" + + "github.com/Azure/ARO-RP/pkg/api" +) + +func TestDiskSizeIsValid(t *testing.T) { + for _, tt := range []struct { + name string + diskSize int + desiredResult bool + }{ + { + name: "size is valid", + diskSize: 129, + desiredResult: true, + }, + { + name: "size is not valid", + diskSize: 127, + desiredResult: false, + }, + } { + t.Run(tt.name, func(t *testing.T) { + result := DiskSizeIsValid(tt.diskSize) + + if result != tt.desiredResult { + t.Errorf("Want %v, got %v", tt.desiredResult, result) + } + }) + } +} + +func TestVMSizeIsValid(t *testing.T) { + for _, tt := range []struct { + name string + vmSize api.VMSize + requireD2sV3Workers bool + isMaster bool + desiredResult bool + }{ + { + name: "vmSize is supported for use in ARO as worker node", + vmSize: api.VMSizeStandardF72sV2, + requireD2sV3Workers: false, + isMaster: false, + desiredResult: true, + }, + { + name: "vmSize is not supported for use in ARO as worker node", + vmSize: api.VMSize("Unsupported_Csv_v6"), + requireD2sV3Workers: false, + isMaster: false, + desiredResult: false, + }, + { + name: "vmSize is supported for use in ARO as master node", + vmSize: api.VMSizeStandardF72sV2, + requireD2sV3Workers: false, + isMaster: true, + desiredResult: true, + }, + { + name: "vmSize is not supported for use in ARO as master node", + vmSize: api.VMSizeStandardD2sV3, + requireD2sV3Workers: false, + isMaster: true, + desiredResult: false, + }, + { + name: "install requires Standard_D2s_v3 workers, worker vmSize is not Standard_D2s_v3", + vmSize: api.VMSizeStandardF72sV2, + requireD2sV3Workers: true, + isMaster: false, + desiredResult: false, + }, + { + name: "install requires Standard_D2s_v3 workers, worker vmSize is Standard_D2s_v3", + vmSize: api.VMSizeStandardD2sV3, + requireD2sV3Workers: true, + isMaster: false, + desiredResult: true, + }, + { + name: "install requires Standard_D2s_v3 workers, vmSize is is a master", + vmSize: api.VMSizeStandardF72sV2, + requireD2sV3Workers: true, + isMaster: true, + desiredResult: true, + }, + } { + t.Run(tt.name, func(t *testing.T) { + result := VMSizeIsValid(tt.vmSize, tt.requireD2sV3Workers, tt.isMaster) + + if result != tt.desiredResult { + t.Errorf("Want %v, got %v", tt.desiredResult, result) + } + }) + } +}