diff --git a/pkg/api/preflight.go b/pkg/api/preflight.go index 34fb53f4c..e678852cc 100644 --- a/pkg/api/preflight.go +++ b/pkg/api/preflight.go @@ -14,19 +14,8 @@ type PreflightRequest struct { // ValidationResult is the validation result to return in the deployment preflight response body type ValidationResult struct { - Status ValidationStatus `json:"status"` - Error *ManagementErrorWithDetails `json:"error,omitempty"` -} - -type ManagementErrorWithDetails struct { - // Code - The error code returned from the server. - Code *string `json:"code,omitempty"` - // Message - The error message returned from the server. - Message *string `json:"message,omitempty"` - // Target - The target of the error. - Target *string `json:"target,omitempty"` - // Details - Validation error. - Details *[]ManagementErrorWithDetails `json:"details,omitempty"` + Status ValidationStatus `json:"status"` + Error *CloudErrorBody `json:"error,omitempty"` } // ResourceTypeMeta is the Typemeta inside request body of preflight diff --git a/pkg/api/subscriptiondocument_example.go b/pkg/api/subscriptiondocument_example.go new file mode 100644 index 000000000..f5c7e7918 --- /dev/null +++ b/pkg/api/subscriptiondocument_example.go @@ -0,0 +1,16 @@ +package api + +// Copyright (c) Microsoft Corporation. +// Licensed under the Apache License 2.0. + +func ExampleSubscriptionDocument() *SubscriptionDocument { + return &SubscriptionDocument{ + ID: "00000000-0000-0000-0000-000000000000", + Subscription: &Subscription{ + State: SubscriptionStateRegistered, + Properties: &SubscriptionProperties{ + TenantID: "11111111-1111-1111-1111-111111111111", + }, + }, + } +} diff --git a/pkg/frontend/openshiftcluster_preflightvalidation.go b/pkg/frontend/openshiftcluster_preflightvalidation.go index 83c76f26e..3c102b6c4 100644 --- a/pkg/frontend/openshiftcluster_preflightvalidation.go +++ b/pkg/frontend/openshiftcluster_preflightvalidation.go @@ -9,10 +9,10 @@ import ( "net/http" "strings" - "github.com/Azure/go-autorest/autorest/to" "github.com/sirupsen/logrus" "github.com/Azure/ARO-RP/pkg/api" + "github.com/Azure/ARO-RP/pkg/database/cosmosdb" "github.com/Azure/ARO-RP/pkg/env" "github.com/Azure/ARO-RP/pkg/frontend/middleware" ) @@ -36,8 +36,8 @@ func (f *frontend) preflightValidation(w http.ResponseWriter, r *http.Request) { if err != nil { b = marshalValidationResult(api.ValidationResult{ Status: api.ValidationStatusFailed, - Error: &api.ManagementErrorWithDetails{ - Message: to.StringPtr(err.Error()), + Error: &api.CloudErrorBody{ + Message: err.Error(), }, }) reply(log, w, header, b, statusCodeError(http.StatusOK)) @@ -48,13 +48,20 @@ func (f *frontend) preflightValidation(w http.ResponseWriter, r *http.Request) { typeMeta := api.ResourceTypeMeta{} if err := json.Unmarshal(raw, &typeMeta); err != nil { // failing to parse the preflight body is not considered a validation failure. continue - log.Warningf("bad request. Failed to unmarshal ResourceTypeMeta: %s", err) - continue + log.Warningf("preflight validation failed with bad request. Failed to unmarshal ResourceTypeMeta: %s", err) + b = marshalValidationResult(api.ValidationResult{ + Status: api.ValidationStatusFailed, + Error: &api.CloudErrorBody{ + Message: err.Error(), + }, + }) + reply(log, w, header, b, statusCodeError(http.StatusOK)) + return } if strings.EqualFold(typeMeta.Type, "Microsoft.RedHatOpenShift/openShiftClusters") { - res := f._preflightValidation(ctx, log, raw, typeMeta.APIVersion, typeMeta.Id) + res := f._preflightValidation(ctx, log, raw, typeMeta.APIVersion, strings.ToLower(typeMeta.Id)) if res.Status == api.ValidationStatusFailed { - log.Warningf("preflight validation failed") + log.Warningf("preflight validation failed with err: '%s'", res.Error.Message) b = marshalValidationResult(res) reply(log, w, header, b, statusCodeError(http.StatusOK)) return @@ -68,6 +75,18 @@ func (f *frontend) preflightValidation(w http.ResponseWriter, r *http.Request) { } func (f *frontend) _preflightValidation(ctx context.Context, log *logrus.Entry, raw json.RawMessage, apiVersion string, resourceID string) api.ValidationResult { + log.Infof("running preflight validation on resource: %s", resourceID) + doc, err := f.dbOpenShiftClusters.Get(ctx, resourceID) + isCreate := cosmosdb.IsErrorStatusCode(err, http.StatusNotFound) + if err != nil && !isCreate { + log.Warning(err.Error()) + return api.ValidationResult{ + Status: api.ValidationStatusFailed, + Error: &api.CloudErrorBody{ + Message: "400: Cluster not found for resourceID: " + resourceID, + }, + } + } // unmarshal raw to OpenShiftCluster type oc := &api.OpenShiftCluster{} oc.Properties.ProvisioningState = api.ProvisioningStateSucceeded @@ -79,35 +98,44 @@ func (f *frontend) _preflightValidation(ctx context.Context, log *logrus.Entry, converter := f.apis[apiVersion].OpenShiftClusterConverter staticValidator := f.apis[apiVersion].OpenShiftClusterStaticValidator ext := converter.ToExternal(oc) - if err := json.Unmarshal(raw, &ext); err != nil { + if err = json.Unmarshal(raw, &ext); err != nil { + log.Warning(err.Error()) return api.ValidationResult{ Status: api.ValidationStatusFailed, - Error: &api.ManagementErrorWithDetails{ - Message: to.StringPtr(err.Error()), + Error: &api.CloudErrorBody{ + Message: api.CloudErrorCodeInternalServerError, }, } } - - converter.ToInternal(ext, oc) - if err := staticValidator.Static(ext, nil, f.env.Location(), f.env.Domain(), f.env.FeatureIsSet(env.FeatureRequireD2sV3Workers), resourceID); err != nil { - return api.ValidationResult{ - Status: api.ValidationStatusFailed, - Error: &api.ManagementErrorWithDetails{ - Message: to.StringPtr(err.Error()), - }, + if isCreate { + converter.ToInternal(ext, oc) + if err = staticValidator.Static(ext, nil, f.env.Location(), f.env.Domain(), f.env.FeatureIsSet(env.FeatureRequireD2sV3Workers), resourceID); err != nil { + return api.ValidationResult{ + Status: api.ValidationStatusFailed, + Error: &api.CloudErrorBody{ + Message: err.Error(), + }, + } + } + if err := f.validateInstallVersion(ctx, oc); err != nil { + return api.ValidationResult{ + Status: api.ValidationStatusFailed, + Error: &api.CloudErrorBody{ + Code: api.CloudErrorCodeInvalidParameter, + Message: err.Error(), + }, + } + } + } else { + if err := staticValidator.Static(ext, doc.OpenShiftCluster, f.env.Location(), f.env.Domain(), f.env.FeatureIsSet(env.FeatureRequireD2sV3Workers), resourceID); err != nil { + return api.ValidationResult{ + Status: api.ValidationStatusFailed, + Error: &api.CloudErrorBody{ + Message: err.Error(), + }, + } } } - - if err := f.validateInstallVersion(ctx, oc); err != nil { - return api.ValidationResult{ - Status: api.ValidationStatusFailed, - Error: &api.ManagementErrorWithDetails{ - Code: to.StringPtr(api.CloudErrorCodeInvalidParameter), - Message: to.StringPtr(err.Error()), - }, - } - } - return validationSuccess } diff --git a/pkg/frontend/openshiftcluster_preflightvalidation_test.go b/pkg/frontend/openshiftcluster_preflightvalidation_test.go index 466c6be8f..e0ffc2686 100644 --- a/pkg/frontend/openshiftcluster_preflightvalidation_test.go +++ b/pkg/frontend/openshiftcluster_preflightvalidation_test.go @@ -6,96 +6,123 @@ package frontend import ( "context" "encoding/json" + "fmt" "net/http" + "strings" "testing" - "github.com/Azure/go-autorest/autorest/to" - "github.com/Azure/ARO-RP/pkg/api" "github.com/Azure/ARO-RP/pkg/metrics/noop" + "github.com/Azure/ARO-RP/pkg/util/version" testdatabase "github.com/Azure/ARO-RP/test/database" ) func TestPreflightValidation(t *testing.T) { ctx := context.Background() mockSubID := "00000000-0000-0000-0000-000000000000" + apiVersion := "2020-04-30" + clusterId := "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resourceGroup/providers/Microsoft.RedHatOpenShift/openShiftClusters/resourceName" + location := "eastus" + defaultProfile := "default" + resourceGroup := "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resourceGroupTest" + netProfile := "10.128.0.0/14" + encryptionSet := "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/ms-eastus/providers/Microsoft.Compute/diskEncryptionSets/ms-eastus-disk-encryption-set" + masterSub := "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/vnetResourceGroup/providers/Microsoft.Network/virtualNetworks/vnet/subnets/master" + workerSub := "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/vnetResourceGroup/providers/Microsoft.Network/virtualNetworks/vnet/subnets/worker" + + preflightPayload := []byte(fmt.Sprintf(` + { + "apiVersion": "%s", + "id": "%s", + "name": "%s", + "type": "%s", + "location": "%s", + "properties": { + "clusterProfile": { + "domain": "%s", + "resourceGroupId": "%s", + "fipsValidatedModules": "%s", + "version": "%s" + }, + "consoleProfile": {}, + "servicePrincipalProfile": { + "clientId": "%s", + "clientSecret": "%s" + }, + "networkProfile": { + "podCidr": "%s", + "serviceCidr": "%s" + }, + "masterProfile": { + "vmSize": "%s", + "subnetId": "%s", + "encryptionAtHost": "%s", + "diskEncryptionSetId": "%s" + }, + "workerProfiles": [ + { + "name": "%s", + "vmSize": "%s", + "diskSizeGB": %v, + "encryptionAtHost": "%s", + "subnetId": "%s", + "count": %v, + "diskEncryptionSetId": "%s" + } + ], + "apiserverProfile": { + "visibility": "%s" + }, + "ingressProfiles": [ + { + "name": "%s", + "visibility": "%s", + "IP": "%s" + } + ] + } + } + `, apiVersion, clusterId, api.ExampleOpenShiftClusterDocument().OpenShiftCluster.Name, + api.ExampleOpenShiftClusterDocument().OpenShiftCluster.Type, + location, defaultProfile, resourceGroup, api.EncryptionAtHostEnabled, version.DefaultInstallStream.Version.String(), + mockSubID, mockSubID, netProfile, netProfile, api.VMSizeStandardD32sV3, masterSub, + api.EncryptionAtHostEnabled, encryptionSet, + api.ExampleOpenShiftClusterDocument().OpenShiftCluster.Properties.WorkerProfiles[0].Name, api.VMSizeStandardD32sV3, + api.ExampleOpenShiftClusterDocument().OpenShiftCluster.Properties.WorkerProfiles[0].DiskSizeGB, + api.EncryptionAtHostEnabled, workerSub, + api.ExampleOpenShiftClusterDocument().OpenShiftCluster.Properties.WorkerProfiles[0].Count, + encryptionSet, api.VisibilityPublic, defaultProfile, api.VisibilityPublic, + api.ExampleOpenShiftClusterDocument().OpenShiftCluster.Properties.IngressProfiles[0].IP)) + + defaultVersionChangeFeed := map[string]*api.OpenShiftVersion{ + version.DefaultInstallStream.Version.String(): { + Properties: api.OpenShiftVersionProperties{ + Version: version.DefaultInstallStream.Version.String(), + Enabled: true, + }, + }, + } type test struct { name string preflightRequest func() *api.PreflightRequest fixture func(*testdatabase.Fixture) + changeFeed map[string]*api.OpenShiftVersion wantStatusCode int wantError string wantResponse *api.ValidationResult } for _, tt := range []*test{ { - name: "Successful Preflight", + name: "Successful Preflight Create", fixture: func(f *testdatabase.Fixture) { - f.AddSubscriptionDocuments(&api.SubscriptionDocument{ - ID: mockSubID, - Subscription: &api.Subscription{ - State: api.SubscriptionStateRegistered, - Properties: &api.SubscriptionProperties{ - TenantID: "11111111-1111-1111-1111-111111111111", - }, - }, - }) + f.AddSubscriptionDocuments(api.ExampleSubscriptionDocument()) }, + changeFeed: defaultVersionChangeFeed, preflightRequest: func() *api.PreflightRequest { return &api.PreflightRequest{ Resources: []json.RawMessage{ - []byte(` - { - "apiVersion": "2022-04-01", - "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resourcename/providers/Microsoft.RedHatOpenShift/openShiftClusters/resourceName", - "name": "resourceName", - "type": "microsoft.redhatopenshift/openshiftclusters", - "location": "eastus", - "properties": { - "clusterProfile": { - "domain": "example.aroapp.io", - "resourceGroupId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resourcenameTest", - "fipsValidatedModules": "Enabled" - }, - "consoleProfile": {}, - "servicePrincipalProfile": { - "clientId": "00000000-0000-0000-1111-000000000000", - "clientSecret": "00000000-0000-0000-0000-000000000000" - }, - "networkProfile": { - "podCidr": "10.128.0.0/14", - "serviceCidr": "172.30.0.0/16" - }, - "masterProfile": { - "vmSize": "Standard_D32s_v3", - "subnetId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/ms-eastus/providers/Microsoft.Network/virtualNetworks/dev-vnet/subnets/CARO2-master", - "encryptionAtHost": "Enabled", - "diskEncryptionSetId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/ms-eastus/providers/Microsoft.Compute/diskEncryptionSets/ms-eastus-disk-encryption-set" - }, - "workerProfiles": [ - { - "name": "worker", - "vmSize": "Standard_D32s_v3", - "diskSizeGB": 128, - "subnetId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/ms-eastus/providers/Microsoft.Network/virtualNetworks/dev-vnet/subnets/CARO2-worker", - "count": 3, - "encryptionAtHost": "Enabled", - "diskEncryptionSetId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/ms-eastus/providers/Microsoft.Compute/diskEncryptionSets/ms-eastus-disk-encryption-set" - } - ], - "apiserverProfile": { - "visibility": "Public" - }, - "ingressProfiles": [ - { - "name": "default", - "visibility": "Public" - } - ] - } - } - `), + preflightPayload, }, } }, @@ -105,14 +132,208 @@ func TestPreflightValidation(t *testing.T) { }, }, { - name: "Failed Preflight Static", + name: "Failed Preflight Static Invalid ResourceGroup", fixture: func(f *testdatabase.Fixture) { - f.AddSubscriptionDocuments(&api.SubscriptionDocument{ - ID: mockSubID, - Subscription: &api.Subscription{ - State: api.SubscriptionStateRegistered, - Properties: &api.SubscriptionProperties{ - TenantID: "11111111-1111-1111-1111-111111111111", + f.AddSubscriptionDocuments(api.ExampleSubscriptionDocument()) + }, + preflightRequest: func() *api.PreflightRequest { + return &api.PreflightRequest{ + Resources: []json.RawMessage{ + []byte(fmt.Sprintf(` + { + "apiVersion": "%s", + "id": "%s", + "name": "%s", + "type": "%s", + "location": "%s", + "properties": { + "clusterProfile": { + "domain": "%s", + "resourceGroupId": "/subscriptions/00000000-0000-0000-0000-000000000001/resourceGroups/resourcenameTest", + "fipsValidatedModules": "%s" + } + } + } + `, apiVersion, clusterId, + api.ExampleOpenShiftClusterDocument().OpenShiftCluster.Name, + api.ExampleOpenShiftClusterDocument().OpenShiftCluster.Type, + location, defaultProfile, api.FipsValidatedModulesEnabled)), + }, + } + }, + wantStatusCode: http.StatusOK, + wantResponse: &api.ValidationResult{ + Status: api.ValidationStatusFailed, + Error: &api.CloudErrorBody{ + Message: "400: InvalidParameter: properties.clusterProfile.resourceGroupId: The provided resource group '/subscriptions/00000000-0000-0000-0000-000000000001/resourceGroups/resourcenameTest' is invalid: must be in same subscription as cluster.", + }, + }, + }, + { + name: "Failed Preflight Cluster Check", + fixture: func(f *testdatabase.Fixture) { + f.AddSubscriptionDocuments(api.ExampleSubscriptionDocument()) + }, + preflightRequest: func() *api.PreflightRequest { + return &api.PreflightRequest{ + Resources: []json.RawMessage{ + []byte(fmt.Sprintf(` + { + "apiVersion": "%s", + "id": "%s", + "name": "%s", + "type": "%s", + "location": "%s", + "properties": {} + } + `, apiVersion, "resourceGroups/resourceGroup/providers/Microsoft.RedHatOpenShift/openShiftClusters/resourceName", + api.ExampleOpenShiftClusterDocument().OpenShiftCluster.Name, + api.ExampleOpenShiftClusterDocument().OpenShiftCluster.Type, + location)), + }, + } + }, + wantStatusCode: http.StatusOK, + wantResponse: &api.ValidationResult{ + Status: api.ValidationStatusFailed, + Error: &api.CloudErrorBody{ + Message: "400: Cluster not found for resourceID: resourcegroups/resourcegroup/providers/microsoft.redhatopenshift/openshiftclusters/resourcename", + }, + }, + }, + { + name: "Failed Preflight Install Version", + fixture: func(f *testdatabase.Fixture) { + f.AddSubscriptionDocuments(api.ExampleSubscriptionDocument()) + }, + changeFeed: defaultVersionChangeFeed, + preflightRequest: func() *api.PreflightRequest { + return &api.PreflightRequest{ + Resources: []json.RawMessage{ + []byte(fmt.Sprintf(` + { + "apiVersion": "%s", + "id": "%s", + "name": "%s", + "type": "%s", + "location": "%s", + "properties": { + "clusterProfile": { + "domain": "%s", + "resourceGroupId": "%s", + "fipsValidatedModules": "%s", + "version": "4.11.43" + }, + "consoleProfile": {}, + "servicePrincipalProfile": { + "clientId": "%s", + "clientSecret": "%s" + }, + "networkProfile": { + "podCidr": "%s", + "serviceCidr": "%s" + }, + "masterProfile": { + "vmSize": "%s", + "subnetId": "%s", + "encryptionAtHost": "%s", + "diskEncryptionSetId": "%s" + }, + "workerProfiles": [ + { + "name": "%s", + "vmSize": "%s", + "diskSizeGB": %v, + "encryptionAtHost": "%s", + "subnetId": "%s", + "count": %v, + "diskEncryptionSetId": "%s" + } + ], + "apiserverProfile": { + "visibility": "%s" + }, + "ingressProfiles": [ + { + "name": "%s", + "visibility": "%s", + "IP": "%s" + } + ] + } + } + `, apiVersion, clusterId, api.ExampleOpenShiftClusterDocument().OpenShiftCluster.Name, + api.ExampleOpenShiftClusterDocument().OpenShiftCluster.Type, + location, defaultProfile, resourceGroup, api.EncryptionAtHostEnabled, + mockSubID, mockSubID, netProfile, netProfile, api.VMSizeStandardD32sV3, masterSub, + api.EncryptionAtHostEnabled, encryptionSet, + api.ExampleOpenShiftClusterDocument().OpenShiftCluster.Properties.WorkerProfiles[0].Name, api.VMSizeStandardD32sV3, + api.ExampleOpenShiftClusterDocument().OpenShiftCluster.Properties.WorkerProfiles[0].DiskSizeGB, + api.EncryptionAtHostEnabled, workerSub, + api.ExampleOpenShiftClusterDocument().OpenShiftCluster.Properties.WorkerProfiles[0].Count, + encryptionSet, api.VisibilityPublic, defaultProfile, api.VisibilityPublic, + api.ExampleOpenShiftClusterDocument().OpenShiftCluster.Properties.IngressProfiles[0].IP)), + }, + } + }, + wantStatusCode: http.StatusOK, + wantResponse: &api.ValidationResult{ + Status: api.ValidationStatusFailed, + Error: &api.CloudErrorBody{ + Code: "InvalidParameter", + Message: "400: InvalidParameter: properties.clusterProfile.version: The requested OpenShift version '4.11.43' is invalid.", + }, + }, + }, + { + name: "Successful Preflight Update", + fixture: func(f *testdatabase.Fixture) { + f.AddSubscriptionDocuments(api.ExampleSubscriptionDocument()) + f.AddOpenShiftClusterDocuments(&api.OpenShiftClusterDocument{ + Key: strings.ToLower(testdatabase.GetResourcePath(api.ExampleOpenShiftClusterDocument().ID, api.ExampleOpenShiftClusterDocument().OpenShiftCluster.Name)), + OpenShiftCluster: &api.OpenShiftCluster{ + ID: testdatabase.GetResourcePath(api.ExampleOpenShiftClusterDocument().ID, api.ExampleOpenShiftClusterDocument().OpenShiftCluster.Name), + Name: api.ExampleOpenShiftClusterDocument().OpenShiftCluster.Name, + Type: api.ExampleOpenShiftClusterDocument().OpenShiftCluster.Type, + Tags: map[string]string{"tag": "will-be-kept"}, + Location: location, + Properties: api.OpenShiftClusterProperties{ + ProvisioningState: api.ProvisioningStateSucceeded, + ClusterProfile: api.ClusterProfile{ + Domain: defaultProfile, + FipsValidatedModules: api.FipsValidatedModulesEnabled, + ResourceGroupID: resourceGroup, + Version: version.DefaultInstallStream.Version.String(), + }, + ServicePrincipalProfile: &api.ServicePrincipalProfile{ + ClientID: mockSubID, + ClientSecret: api.SecureString(mockSubID), + }, + NetworkProfile: api.NetworkProfile{ + PodCIDR: netProfile, + ServiceCIDR: netProfile, + }, + MasterProfile: api.MasterProfile{ + VMSize: api.VMSizeStandardD32sV3, + SubnetID: masterSub, + DiskEncryptionSetID: encryptionSet, + EncryptionAtHost: api.EncryptionAtHostEnabled, + }, + WorkerProfiles: []api.WorkerProfile{ + { + Name: api.ExampleOpenShiftClusterDocument().OpenShiftCluster.Properties.WorkerProfiles[0].Name, + VMSize: api.VMSizeStandardD32sV3, + DiskSizeGB: api.ExampleOpenShiftClusterDocument().OpenShiftCluster.Properties.WorkerProfiles[0].DiskSizeGB, + EncryptionAtHost: api.EncryptionAtHostEnabled, + SubnetID: workerSub, + Count: api.ExampleOpenShiftClusterDocument().OpenShiftCluster.Properties.WorkerProfiles[0].Count, + DiskEncryptionSetID: encryptionSet, + }, + }, + APIServerProfile: api.APIServerProfile{ + Visibility: api.VisibilityPublic, + }, + IngressProfiles: api.ExampleOpenShiftClusterDocument().OpenShiftCluster.Properties.IngressProfiles, }, }, }) @@ -120,37 +341,56 @@ func TestPreflightValidation(t *testing.T) { preflightRequest: func() *api.PreflightRequest { return &api.PreflightRequest{ Resources: []json.RawMessage{ - []byte(` - { - "apiVersion": "2022-04-01", - "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resourcename/providers/Microsoft.RedHatOpenShift/openShiftClusters/resourceName", - "name": "resourceName", - "type": "microsoft.redhatopenshift/openshiftclusters", - "location": "eastus", - "properties": { - "clusterProfile": { - "domain": "example.aroapp.io", - "resourceGroupId": "/subscriptions/00000000-0000-0000-0000-000000000001/resourceGroups/resourcenameTest", - "fipsValidatedModules": "Enabled" - } - } - } - `), + preflightPayload, + }, + } + }, + wantStatusCode: http.StatusOK, + wantResponse: &api.ValidationResult{ + Status: api.ValidationStatusSucceeded, + }, + }, + { + name: "Failed Preflight Update Invalid Domain", + fixture: func(f *testdatabase.Fixture) { + f.AddSubscriptionDocuments(api.ExampleSubscriptionDocument()) + f.AddOpenShiftClusterDocuments(&api.OpenShiftClusterDocument{ + Key: strings.ToLower(testdatabase.GetResourcePath(api.ExampleOpenShiftClusterDocument().ID, "resourceName")), + OpenShiftCluster: &api.OpenShiftCluster{ + ID: testdatabase.GetResourcePath(api.ExampleOpenShiftClusterDocument().ID, "resourceName"), + Name: "resourceName", + Type: "Microsoft.RedHatOpenShift/openShiftClusters", + Tags: map[string]string{"tag": "will-be-kept"}, + Location: "eastus", + Properties: api.OpenShiftClusterProperties{ + ProvisioningState: api.ProvisioningStateSucceeded, + ClusterProfile: api.ClusterProfile{ + Domain: "different", + }, + }, + }, + }) + }, + preflightRequest: func() *api.PreflightRequest { + return &api.PreflightRequest{ + Resources: []json.RawMessage{ + preflightPayload, }, } }, wantStatusCode: http.StatusOK, wantResponse: &api.ValidationResult{ Status: api.ValidationStatusFailed, - Error: &api.ManagementErrorWithDetails{ - Message: to.StringPtr("400: InvalidParameter: properties.clusterProfile.resourceGroupId: The provided resource group '/subscriptions/00000000-0000-0000-0000-000000000001/resourceGroups/resourcenameTest' is invalid: must be in same subscription as cluster."), + Error: &api.CloudErrorBody{ + Message: "400: PropertyChangeNotAllowed: properties.clusterProfile.domain: Changing property 'properties.clusterProfile.domain' is not allowed.", }, }, }, } { t.Run(tt.name, func(t *testing.T) { ti := newTestInfra(t). - WithSubscriptions() + WithSubscriptions(). + WithOpenShiftClusters() defer ti.done() err := ti.buildFixtures(tt.fixture) @@ -166,7 +406,7 @@ func TestPreflightValidation(t *testing.T) { go f.Run(ctx, nil, nil) f.ocpVersionsMu.Lock() - f.defaultOcpVersion = "4.10.0" + f.defaultOcpVersion = "4.13.40" f.enabledOcpVersions = map[string]*api.OpenShiftVersion{ f.defaultOcpVersion: { Properties: api.OpenShiftVersionProperties{ @@ -181,7 +421,7 @@ func TestPreflightValidation(t *testing.T) { } resp, b, err := ti.request(http.MethodPost, - "https://server"+testdatabase.GetPreflightPath(mockSubID, "deploymentName")+"?api-version=2020-04-30", + "https://server"+testdatabase.GetPreflightPath(api.ExampleOpenShiftClusterDocument().ID, "deploymentName")+"?api-version=2020-04-30", headers, oc) if err != nil { t.Error(err)