diff --git a/pkg/corerp/frontend/controller/secretstores/kubernetes.go b/pkg/corerp/frontend/controller/secretstores/kubernetes.go index d8f2da4dfe..3b5701b970 100644 --- a/pkg/corerp/frontend/controller/secretstores/kubernetes.go +++ b/pkg/corerp/frontend/controller/secretstores/kubernetes.go @@ -185,8 +185,8 @@ func UpsertSecret(ctx context.Context, newResource, old *datamodel.SecretStore, if ref == "" && old != nil { ref = old.Properties.Resource } - if newResource.Properties.Application == "" && newResource.Properties.Resource == "" { - return rest.NewBadRequestResponse("$.properties.resource cannot be \"\" for global scoped resource."), nil + if isGlobalScopedResource(newResource) && newResource.Properties.Resource == "" { + return rest.NewBadRequestResponse("$.properties.resource cannot be empty for global scoped resource."), nil } ns, name, err := fromResourceID(ref) if err != nil { @@ -199,7 +199,7 @@ func UpsertSecret(ctx context.Context, newResource, old *datamodel.SecretStore, } } err = options.KubeClient.Create(ctx, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: ns}}) - if apierrors.IsAlreadyExists(err) { + if err != nil && apierrors.IsAlreadyExists(err) { logger.Info("Using existing namespace", "namespace", ns) } else if err != nil { return nil, err @@ -221,7 +221,7 @@ func UpsertSecret(ctx context.Context, newResource, old *datamodel.SecretStore, err = options.KubeClient.Get(ctx, runtimeclient.ObjectKey{Namespace: ns, Name: name}, ksecret) if apierrors.IsNotFound(err) { // If resource in incoming request references resource, then the resource must exist. - if ref != "" && newResource.Properties.Application != "" { + if ref != "" && !isGlobalScopedResource(newResource) { return rest.NewBadRequestResponse(fmt.Sprintf("'%s' referenced resource does not exist.", ref)), nil } app, _ := resources.ParseResource(newResource.Properties.Application) @@ -329,3 +329,11 @@ func getSecretFromOutputResources(resources []rpv1.OutputResource, options *cont return ksecret, nil } + +func isGlobalScopedResource(resource *datamodel.SecretStore) bool { + if resource.Properties.Application == "" && resource.Properties.Environment == "" { + return true + } + + return false +} diff --git a/pkg/corerp/frontend/controller/secretstores/kubernetes_test.go b/pkg/corerp/frontend/controller/secretstores/kubernetes_test.go index 6c2b44b062..4d15184027 100644 --- a/pkg/corerp/frontend/controller/secretstores/kubernetes_test.go +++ b/pkg/corerp/frontend/controller/secretstores/kubernetes_test.go @@ -45,10 +45,12 @@ const ( testEnvID = testRootScope + "/Applications.Core/environments/env0" testAppID = testRootScope + "/Applications.Core/applications/app0" - testFileCertValueFrom = "secretstores_datamodel_cert_valuefrom.json" - testFileCertValue = "secretstores_datamodel_cert_value.json" - testFileGenericValue = "secretstores_datamodel_generic.json" - testFileGenericValueGlobalScope = "secretstores_datamodel_global_scope.json" + testFileCertValueFrom = "secretstores_datamodel_cert_valuefrom.json" + testFileCertValue = "secretstores_datamodel_cert_value.json" + testFileGenericValue = "secretstores_datamodel_generic.json" + testFileGenericValueGlobalScope = "secretstores_datamodel_global_scope.json" + testFileGenericValueInvalidResource = "secretstores_datamodel_global_scope_invalid_resource.json" + testFileGenericValueEmptyResource = "secretstores_datamodel_global_scope_empty_resource.json" ) func TestGetNamespace(t *testing.T) { @@ -503,6 +505,43 @@ func TestUpsertSecret(t *testing.T) { "secret0"), }, newResource.Properties.Status.OutputResources[0]) }) + t.Run("create a new secret resource with invalid resource", func(t *testing.T) { + ctrl := gomock.NewController(t) + sc := store.NewMockStorageClient(ctrl) + + newResource := testutil.MustGetTestData[datamodel.SecretStore](testFileGenericValueInvalidResource) + + opt := &controller.Options{ + StorageClient: sc, + KubeClient: k8sutil.NewFakeKubeClient(nil), + } + + _, err := ValidateAndMutateRequest(context.TODO(), newResource, nil, opt) + require.NoError(t, err) + _, err = UpsertSecret(context.TODO(), newResource, nil, opt) + require.Error(t, err) + require.Equal(t, err.Error(), "no Kubernetes namespace") + }) + t.Run("create a new secret resource with empty resource", func(t *testing.T) { + ctrl := gomock.NewController(t) + sc := store.NewMockStorageClient(ctrl) + + newResource := testutil.MustGetTestData[datamodel.SecretStore](testFileGenericValueEmptyResource) + + opt := &controller.Options{ + StorageClient: sc, + KubeClient: k8sutil.NewFakeKubeClient(nil), + } + + _, err := ValidateAndMutateRequest(context.TODO(), newResource, nil, opt) + require.NoError(t, err) + resp, err := UpsertSecret(context.TODO(), newResource, nil, opt) + require.NoError(t, err) + + // assert + r := resp.(*rest.BadRequestResponse) + require.Equal(t, "$.properties.resource cannot be empty for global scoped resource.", r.Body.Error.Message) + }) } diff --git a/pkg/corerp/frontend/controller/secretstores/testdata/secretstores_datamodel_global_scope_empty_resource.json b/pkg/corerp/frontend/controller/secretstores/testdata/secretstores_datamodel_global_scope_empty_resource.json new file mode 100644 index 0000000000..8cacca13bd --- /dev/null +++ b/pkg/corerp/frontend/controller/secretstores/testdata/secretstores_datamodel_global_scope_empty_resource.json @@ -0,0 +1,36 @@ +{ + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Applications.Core/secretStores/secret0", + "name": "secret0", + "type": "applications.core/secretstores", + "location": "global", + "systemData": { + "createdAt": "2022-03-22T18:54:52.6857175Z", + "createdBy": "fake@hotmail.com", + "createdByType": "User", + "lastModifiedAt": "2022-03-22T18:57:52.6857175Z", + "lastModifiedBy": "fake@hotmail.com", + "lastModifiedByType": "User" + }, + "provisioningState": "Succeeded", + "properties": { + "type": "generic", + "data": { + "tls.crt": { + "encoding": "raw", + "value": "tls.crt" + }, + "tls.key": { + "encoding": "base64", + "value": "dGxzLmNlcnQK" + }, + "servicePrincipalPassword": { + "value": "10000000-1000-1000-0000-000000000000" + } + } + }, + "tenantId": "00000000-0000-0000-0000-000000000000", + "subscriptionId": "00000000-0000-0000-0000-000000000000", + "resourceGroup": "testGroup", + "createdApiVersion": "2023-10-01-preview", + "updatedApiVersion": "2023-10-01-preview" + } \ No newline at end of file diff --git a/pkg/corerp/frontend/controller/secretstores/testdata/secretstores_datamodel_global_scope_invalid_resource.json b/pkg/corerp/frontend/controller/secretstores/testdata/secretstores_datamodel_global_scope_invalid_resource.json new file mode 100644 index 0000000000..580eaa5d47 --- /dev/null +++ b/pkg/corerp/frontend/controller/secretstores/testdata/secretstores_datamodel_global_scope_invalid_resource.json @@ -0,0 +1,37 @@ +{ + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Applications.Core/secretStores/secret0", + "name": "secret0", + "type": "applications.core/secretstores", + "location": "global", + "systemData": { + "createdAt": "2022-03-22T18:54:52.6857175Z", + "createdBy": "fake@hotmail.com", + "createdByType": "User", + "lastModifiedAt": "2022-03-22T18:57:52.6857175Z", + "lastModifiedBy": "fake@hotmail.com", + "lastModifiedByType": "User" + }, + "provisioningState": "Succeeded", + "properties": { + "resource": "secret0", + "type": "generic", + "data": { + "tls.crt": { + "encoding": "raw", + "value": "tls.crt" + }, + "tls.key": { + "encoding": "base64", + "value": "dGxzLmNlcnQK" + }, + "servicePrincipalPassword": { + "value": "10000000-1000-1000-0000-000000000000" + } + } + }, + "tenantId": "00000000-0000-0000-0000-000000000000", + "subscriptionId": "00000000-0000-0000-0000-000000000000", + "resourceGroup": "testGroup", + "createdApiVersion": "2023-10-01-preview", + "updatedApiVersion": "2023-10-01-preview" + } \ No newline at end of file