From a17f9cf2a68b63046afbf611ba449720e8f2b31b Mon Sep 17 00:00:00 2001 From: SoTrx <11771975+SoTrx@users.noreply.github.com> Date: Wed, 18 Sep 2024 20:07:30 +0200 Subject: [PATCH] Implementation of Dapr Building Block: Configuration Store (#7906) # Description Hey, This PR adds support for Dapr Configuration Building block ## Type of change - This pull request adds or changes features of Radius and has an approved issue (issue link required). Fixes: #7889 --------- Signed-off-by: SoTrx <11771975+SoTrx@users.noreply.github.com> Co-authored-by: Yetkin Timocin --- .../2023-10-01-preview/types.json | 333 ++++++++++-- hack/bicep-types-radius/generated/index.json | 9 +- pkg/cli/clients/management.go | 1 + .../backend/deployment/deploymentprocessor.go | 6 + .../configurationstore_conversion.go | 149 +++++ .../configurationstore_conversion_test.go | 271 ++++++++++ .../pubsubbroker_conversion_test.go | 2 +- ...igurationstore_invalidmanual_resource.json | 17 + ...igurationstore_invalidrecipe_resource.json | 20 + .../configurationstore_manual_datamodel.json | 45 ++ ...urationstore_manual_generic_datamodel.json | 41 ++ .../configurationstore_manual_resource.json | 26 + .../configurationstore_recipe_datamodel.json | 37 ++ .../configurationstore_recipe_resource.json | 17 + .../zz_generated_client_factory.go | 5 + ...zz_generated_configurationstores_client.go | 349 ++++++++++++ .../v20231001preview/zz_generated_models.go | 112 ++++ .../zz_generated_models_serde.go | 243 +++++++++ .../v20231001preview/zz_generated_options.go | 32 ++ .../zz_generated_response_types.go | 29 + .../converter/configurationstore_converter.go | 60 +++ .../configurationstore_converter_test.go | 217 ++++++++ .../datamodel/daprconfigurationstore.go | 91 ++++ pkg/daprrp/frontend/controller/types.go | 7 + .../processors/configurationstores/doc.go | 18 + .../configurationstores/processor.go | 156 ++++++ .../configurationstores/processor_test.go | 507 ++++++++++++++++++ .../pubsubbrokers/processor_test.go | 2 +- .../processors/secretstores/processor_test.go | 2 +- .../processors/statestores/processor_test.go | 2 +- pkg/daprrp/setup/operations.go | 30 ++ pkg/daprrp/setup/setup.go | 34 ++ pkg/daprrp/setup/setup_test.go | 25 + pkg/portableresources/handlers/util.go | 2 +- pkg/rp/portableresources/portableresources.go | 2 + .../ConfigurationStores_CreateOrUpdate.json | 77 +++ ...rationStores_CreateOrUpdateWithRecipe.json | 66 +++ .../examples/ConfigurationStores_Delete.json | 14 + .../examples/ConfigurationStores_Get.json | 35 ++ .../examples/ConfigurationStores_List.json | 73 +++ .../ConfigurationStores_ListByRootScope.json | 56 ++ .../examples/ConfigurationStores_Update.json | 77 +++ .../preview/2023-10-01-preview/openapi.json | 468 ++++++++++++++++ .../dapr_component_name_conflict_test.go | 2 +- .../resources/dapr_configurationstore_test.go | 219 ++++++++ ...ces-configurationstore-manual-secret.bicep | 98 ++++ ...-resources-configurationstore-manual.bicep | 70 +++ ...-resources-configurationstore-recipe.bicep | 74 +++ .../bindings/daprconfigurationstore.go | 36 ++ test/magpiego/server.go | 23 +- .../dapr-configuration-store.bicep | 44 ++ test/validation/shared.go | 17 +- .../Applications.Dapr/configurationStores.tsp | 93 ++++ .../ConfigurationStores_CreateOrUpdate.json | 77 +++ ...rationStores_CreateOrUpdateWithRecipe.json | 66 +++ .../ConfigurationStores_Delete.json | 14 + .../ConfigurationStores_Get.json | 35 ++ .../ConfigurationStores_List.json | 73 +++ .../ConfigurationStores_ListByRootScope.json | 56 ++ .../ConfigurationStores_Update.json | 77 +++ typespec/Applications.Dapr/main.tsp | 1 + 61 files changed, 4778 insertions(+), 62 deletions(-) create mode 100644 pkg/daprrp/api/v20231001preview/configurationstore_conversion.go create mode 100644 pkg/daprrp/api/v20231001preview/configurationstore_conversion_test.go create mode 100644 pkg/daprrp/api/v20231001preview/testdata/configurationstore_invalidmanual_resource.json create mode 100644 pkg/daprrp/api/v20231001preview/testdata/configurationstore_invalidrecipe_resource.json create mode 100644 pkg/daprrp/api/v20231001preview/testdata/configurationstore_manual_datamodel.json create mode 100644 pkg/daprrp/api/v20231001preview/testdata/configurationstore_manual_generic_datamodel.json create mode 100644 pkg/daprrp/api/v20231001preview/testdata/configurationstore_manual_resource.json create mode 100644 pkg/daprrp/api/v20231001preview/testdata/configurationstore_recipe_datamodel.json create mode 100644 pkg/daprrp/api/v20231001preview/testdata/configurationstore_recipe_resource.json create mode 100644 pkg/daprrp/api/v20231001preview/zz_generated_configurationstores_client.go create mode 100644 pkg/daprrp/datamodel/converter/configurationstore_converter.go create mode 100644 pkg/daprrp/datamodel/converter/configurationstore_converter_test.go create mode 100644 pkg/daprrp/datamodel/daprconfigurationstore.go create mode 100644 pkg/daprrp/processors/configurationstores/doc.go create mode 100644 pkg/daprrp/processors/configurationstores/processor.go create mode 100644 pkg/daprrp/processors/configurationstores/processor_test.go create mode 100644 swagger/specification/applications/resource-manager/Applications.Dapr/preview/2023-10-01-preview/examples/ConfigurationStores_CreateOrUpdate.json create mode 100644 swagger/specification/applications/resource-manager/Applications.Dapr/preview/2023-10-01-preview/examples/ConfigurationStores_CreateOrUpdateWithRecipe.json create mode 100644 swagger/specification/applications/resource-manager/Applications.Dapr/preview/2023-10-01-preview/examples/ConfigurationStores_Delete.json create mode 100644 swagger/specification/applications/resource-manager/Applications.Dapr/preview/2023-10-01-preview/examples/ConfigurationStores_Get.json create mode 100644 swagger/specification/applications/resource-manager/Applications.Dapr/preview/2023-10-01-preview/examples/ConfigurationStores_List.json create mode 100644 swagger/specification/applications/resource-manager/Applications.Dapr/preview/2023-10-01-preview/examples/ConfigurationStores_ListByRootScope.json create mode 100644 swagger/specification/applications/resource-manager/Applications.Dapr/preview/2023-10-01-preview/examples/ConfigurationStores_Update.json create mode 100644 test/functional-portable/daprrp/noncloud/resources/dapr_configurationstore_test.go create mode 100644 test/functional-portable/daprrp/noncloud/resources/testdata/daprrp-resources-configurationstore-manual-secret.bicep create mode 100644 test/functional-portable/daprrp/noncloud/resources/testdata/daprrp-resources-configurationstore-manual.bicep create mode 100644 test/functional-portable/daprrp/noncloud/resources/testdata/daprrp-resources-configurationstore-recipe.bicep create mode 100644 test/magpiego/bindings/daprconfigurationstore.go create mode 100644 test/testrecipes/test-bicep-recipes/dapr-configuration-store.bicep create mode 100644 typespec/Applications.Dapr/configurationStores.tsp create mode 100644 typespec/Applications.Dapr/examples/2023-10-01-preview/ConfigurationStores_CreateOrUpdate.json create mode 100644 typespec/Applications.Dapr/examples/2023-10-01-preview/ConfigurationStores_CreateOrUpdateWithRecipe.json create mode 100644 typespec/Applications.Dapr/examples/2023-10-01-preview/ConfigurationStores_Delete.json create mode 100644 typespec/Applications.Dapr/examples/2023-10-01-preview/ConfigurationStores_Get.json create mode 100644 typespec/Applications.Dapr/examples/2023-10-01-preview/ConfigurationStores_List.json create mode 100644 typespec/Applications.Dapr/examples/2023-10-01-preview/ConfigurationStores_ListByRootScope.json create mode 100644 typespec/Applications.Dapr/examples/2023-10-01-preview/ConfigurationStores_Update.json diff --git a/hack/bicep-types-radius/generated/applications/applications.dapr/2023-10-01-preview/types.json b/hack/bicep-types-radius/generated/applications/applications.dapr/2023-10-01-preview/types.json index 1d3187139e..646af0908f 100644 --- a/hack/bicep-types-radius/generated/applications/applications.dapr/2023-10-01-preview/types.json +++ b/hack/bicep-types-radius/generated/applications/applications.dapr/2023-10-01-preview/types.json @@ -4,7 +4,7 @@ }, { "$type": "StringLiteralType", - "value": "Applications.Dapr/pubSubBrokers" + "value": "Applications.Dapr/configurationStores" }, { "$type": "StringLiteralType", @@ -12,7 +12,7 @@ }, { "$type": "ObjectType", - "name": "Applications.Dapr/pubSubBrokers", + "name": "Applications.Dapr/configurationStores", "properties": { "id": { "type": { @@ -47,7 +47,7 @@ "$ref": "#/4" }, "flags": 1, - "description": "Dapr PubSubBroker portable resource properties" + "description": "Dapr configuration store portable resource properties" }, "tags": { "type": { @@ -74,7 +74,7 @@ }, { "$type": "ObjectType", - "name": "DaprPubSubBrokerProperties", + "name": "DaprConfigurationStoreProperties", "properties": { "environment": { "type": { @@ -144,7 +144,7 @@ "$ref": "#/30" }, "flags": 0, - "description": "A collection of references to resources associated with the pubSubBroker" + "description": "A collection of references to resources associated with the configuration store" }, "recipe": { "type": { @@ -444,7 +444,7 @@ }, { "$type": "ObjectType", - "name": "DaprPubSubBrokerPropertiesMetadata", + "name": "DaprConfigurationStorePropertiesMetadata", "properties": {}, "additionalProperties": { "$ref": "#/25" @@ -648,7 +648,7 @@ }, { "$type": "ResourceType", - "name": "Applications.Dapr/pubSubBrokers@2023-10-01-preview", + "name": "Applications.Dapr/configurationStores@2023-10-01-preview", "scopeType": 0, "body": { "$ref": "#/3" @@ -658,7 +658,7 @@ }, { "$type": "StringLiteralType", - "value": "Applications.Dapr/secretStores" + "value": "Applications.Dapr/pubSubBrokers" }, { "$type": "StringLiteralType", @@ -666,7 +666,7 @@ }, { "$type": "ObjectType", - "name": "Applications.Dapr/secretStores", + "name": "Applications.Dapr/pubSubBrokers", "properties": { "id": { "type": { @@ -701,11 +701,11 @@ "$ref": "#/52" }, "flags": 1, - "description": "Dapr SecretStore portable resource properties" + "description": "Dapr PubSubBroker portable resource properties" }, "tags": { "type": { - "$ref": "#/65" + "$ref": "#/66" }, "flags": 0, "description": "Resource tags." @@ -728,7 +728,7 @@ }, { "$type": "ObjectType", - "name": "DaprSecretStoreProperties", + "name": "DaprPubSubBrokerProperties", "properties": { "environment": { "type": { @@ -786,6 +786,20 @@ "flags": 0, "description": "Dapr component version" }, + "auth": { + "type": { + "$ref": "#/28" + }, + "flags": 0, + "description": "Authentication properties for a Dapr component object" + }, + "resources": { + "type": { + "$ref": "#/62" + }, + "flags": 0, + "description": "A collection of references to resources associated with the pubSubBroker" + }, "recipe": { "type": { "$ref": "#/31" @@ -795,7 +809,7 @@ }, "resourceProvisioning": { "type": { - "$ref": "#/64" + "$ref": "#/65" }, "flags": 0, "description": "Specifies how the underlying service/resource is provisioned and managed. Available values are 'recipe', where Radius manages the lifecycle of the resource through a Recipe, and 'manual', where a user manages the resource and provides the values." @@ -856,6 +870,257 @@ } ] }, + { + "$type": "ObjectType", + "name": "DaprPubSubBrokerPropertiesMetadata", + "properties": {}, + "additionalProperties": { + "$ref": "#/25" + } + }, + { + "$type": "ArrayType", + "itemType": { + "$ref": "#/29" + } + }, + { + "$type": "StringLiteralType", + "value": "recipe" + }, + { + "$type": "StringLiteralType", + "value": "manual" + }, + { + "$type": "UnionType", + "elements": [ + { + "$ref": "#/63" + }, + { + "$ref": "#/64" + } + ] + }, + { + "$type": "ObjectType", + "name": "TrackedResourceTags", + "properties": {}, + "additionalProperties": { + "$ref": "#/0" + } + }, + { + "$type": "ResourceType", + "name": "Applications.Dapr/pubSubBrokers@2023-10-01-preview", + "scopeType": 0, + "body": { + "$ref": "#/51" + }, + "flags": 0, + "functions": {} + }, + { + "$type": "StringLiteralType", + "value": "Applications.Dapr/secretStores" + }, + { + "$type": "StringLiteralType", + "value": "2023-10-01-preview" + }, + { + "$type": "ObjectType", + "name": "Applications.Dapr/secretStores", + "properties": { + "id": { + "type": { + "$ref": "#/0" + }, + "flags": 10, + "description": "The resource id" + }, + "name": { + "type": { + "$ref": "#/0" + }, + "flags": 25, + "description": "The resource name" + }, + "type": { + "type": { + "$ref": "#/68" + }, + "flags": 10, + "description": "The resource type" + }, + "apiVersion": { + "type": { + "$ref": "#/69" + }, + "flags": 10, + "description": "The resource api version" + }, + "properties": { + "type": { + "$ref": "#/71" + }, + "flags": 1, + "description": "Dapr SecretStore portable resource properties" + }, + "tags": { + "type": { + "$ref": "#/84" + }, + "flags": 0, + "description": "Resource tags." + }, + "location": { + "type": { + "$ref": "#/0" + }, + "flags": 0, + "description": "The geo-location where the resource lives" + }, + "systemData": { + "type": { + "$ref": "#/37" + }, + "flags": 2, + "description": "Metadata pertaining to creation and last modification of the resource." + } + } + }, + { + "$type": "ObjectType", + "name": "DaprSecretStoreProperties", + "properties": { + "environment": { + "type": { + "$ref": "#/0" + }, + "flags": 1, + "description": "Fully qualified resource ID for the environment that the portable resource is linked to" + }, + "application": { + "type": { + "$ref": "#/0" + }, + "flags": 0, + "description": "Fully qualified resource ID for the application that the portable resource is consumed by (if applicable)" + }, + "provisioningState": { + "type": { + "$ref": "#/79" + }, + "flags": 2, + "description": "Provisioning state of the resource at the time the operation was called" + }, + "status": { + "type": { + "$ref": "#/13" + }, + "flags": 2, + "description": "Status of a resource." + }, + "componentName": { + "type": { + "$ref": "#/0" + }, + "flags": 2, + "description": "The name of the Dapr component object. Use this value in your code when interacting with the Dapr client to use the Dapr component." + }, + "metadata": { + "type": { + "$ref": "#/80" + }, + "flags": 0, + "description": "The metadata for Dapr resource which must match the values specified in Dapr component spec" + }, + "type": { + "type": { + "$ref": "#/0" + }, + "flags": 0, + "description": "Dapr component type which must matches the format used by Dapr Kubernetes configuration format" + }, + "version": { + "type": { + "$ref": "#/0" + }, + "flags": 0, + "description": "Dapr component version" + }, + "recipe": { + "type": { + "$ref": "#/31" + }, + "flags": 0, + "description": "The recipe used to automatically deploy underlying infrastructure for a portable resource" + }, + "resourceProvisioning": { + "type": { + "$ref": "#/83" + }, + "flags": 0, + "description": "Specifies how the underlying service/resource is provisioned and managed. Available values are 'recipe', where Radius manages the lifecycle of the resource through a Recipe, and 'manual', where a user manages the resource and provides the values." + } + } + }, + { + "$type": "StringLiteralType", + "value": "Succeeded" + }, + { + "$type": "StringLiteralType", + "value": "Failed" + }, + { + "$type": "StringLiteralType", + "value": "Canceled" + }, + { + "$type": "StringLiteralType", + "value": "Provisioning" + }, + { + "$type": "StringLiteralType", + "value": "Updating" + }, + { + "$type": "StringLiteralType", + "value": "Deleting" + }, + { + "$type": "StringLiteralType", + "value": "Accepted" + }, + { + "$type": "UnionType", + "elements": [ + { + "$ref": "#/72" + }, + { + "$ref": "#/73" + }, + { + "$ref": "#/74" + }, + { + "$ref": "#/75" + }, + { + "$ref": "#/76" + }, + { + "$ref": "#/77" + }, + { + "$ref": "#/78" + } + ] + }, { "$type": "ObjectType", "name": "DaprSecretStorePropertiesMetadata", @@ -876,10 +1141,10 @@ "$type": "UnionType", "elements": [ { - "$ref": "#/62" + "$ref": "#/81" }, { - "$ref": "#/63" + "$ref": "#/82" } ] }, @@ -896,7 +1161,7 @@ "name": "Applications.Dapr/secretStores@2023-10-01-preview", "scopeType": 0, "body": { - "$ref": "#/51" + "$ref": "#/70" }, "flags": 0, "functions": {} @@ -929,28 +1194,28 @@ }, "type": { "type": { - "$ref": "#/67" + "$ref": "#/86" }, "flags": 10, "description": "The resource type" }, "apiVersion": { "type": { - "$ref": "#/68" + "$ref": "#/87" }, "flags": 10, "description": "The resource api version" }, "properties": { "type": { - "$ref": "#/70" + "$ref": "#/89" }, "flags": 1, "description": "Dapr StateStore portable resource properties" }, "tags": { "type": { - "$ref": "#/84" + "$ref": "#/103" }, "flags": 0, "description": "Resource tags." @@ -991,7 +1256,7 @@ }, "provisioningState": { "type": { - "$ref": "#/78" + "$ref": "#/97" }, "flags": 2, "description": "Provisioning state of the resource at the time the operation was called" @@ -1012,7 +1277,7 @@ }, "metadata": { "type": { - "$ref": "#/79" + "$ref": "#/98" }, "flags": 0, "description": "The metadata for Dapr resource which must match the values specified in Dapr component spec" @@ -1040,7 +1305,7 @@ }, "resources": { "type": { - "$ref": "#/80" + "$ref": "#/99" }, "flags": 0, "description": "A collection of references to resources associated with the state store" @@ -1054,7 +1319,7 @@ }, "resourceProvisioning": { "type": { - "$ref": "#/83" + "$ref": "#/102" }, "flags": 0, "description": "Specifies how the underlying service/resource is provisioned and managed. Available values are 'recipe', where Radius manages the lifecycle of the resource through a Recipe, and 'manual', where a user manages the resource and provides the values." @@ -1093,25 +1358,25 @@ "$type": "UnionType", "elements": [ { - "$ref": "#/71" + "$ref": "#/90" }, { - "$ref": "#/72" + "$ref": "#/91" }, { - "$ref": "#/73" + "$ref": "#/92" }, { - "$ref": "#/74" + "$ref": "#/93" }, { - "$ref": "#/75" + "$ref": "#/94" }, { - "$ref": "#/76" + "$ref": "#/95" }, { - "$ref": "#/77" + "$ref": "#/96" } ] }, @@ -1141,10 +1406,10 @@ "$type": "UnionType", "elements": [ { - "$ref": "#/81" + "$ref": "#/100" }, { - "$ref": "#/82" + "$ref": "#/101" } ] }, @@ -1161,7 +1426,7 @@ "name": "Applications.Dapr/stateStores@2023-10-01-preview", "scopeType": 0, "body": { - "$ref": "#/69" + "$ref": "#/88" }, "flags": 0, "functions": {} diff --git a/hack/bicep-types-radius/generated/index.json b/hack/bicep-types-radius/generated/index.json index 985a3aca5a..162505ee64 100644 --- a/hack/bicep-types-radius/generated/index.json +++ b/hack/bicep-types-radius/generated/index.json @@ -21,14 +21,17 @@ "Applications.Core/volumes@2023-10-01-preview": { "$ref": "applications/applications.core/2023-10-01-preview/types.json#/274" }, - "Applications.Dapr/pubSubBrokers@2023-10-01-preview": { + "Applications.Dapr/configurationStores@2023-10-01-preview": { "$ref": "applications/applications.dapr/2023-10-01-preview/types.json#/48" }, + "Applications.Dapr/pubSubBrokers@2023-10-01-preview": { + "$ref": "applications/applications.dapr/2023-10-01-preview/types.json#/67" + }, "Applications.Dapr/secretStores@2023-10-01-preview": { - "$ref": "applications/applications.dapr/2023-10-01-preview/types.json#/66" + "$ref": "applications/applications.dapr/2023-10-01-preview/types.json#/85" }, "Applications.Dapr/stateStores@2023-10-01-preview": { - "$ref": "applications/applications.dapr/2023-10-01-preview/types.json#/85" + "$ref": "applications/applications.dapr/2023-10-01-preview/types.json#/104" }, "Applications.Datastores/mongoDatabases@2023-10-01-preview": { "$ref": "applications/applications.datastores/2023-10-01-preview/types.json#/48" diff --git a/pkg/cli/clients/management.go b/pkg/cli/clients/management.go index d8cf9a4367..eb68913377 100644 --- a/pkg/cli/clients/management.go +++ b/pkg/cli/clients/management.go @@ -62,6 +62,7 @@ var ( dapr_ctrl.DaprStateStoresResourceType, dapr_ctrl.DaprSecretStoresResourceType, dapr_ctrl.DaprPubSubBrokersResourceType, + dapr_ctrl.DaprConfigurationStoresResourceType, ext_ctrl.ResourceTypeName, gtwy_ctrl.ResourceTypeName, cntr_ctrl.ResourceTypeName, diff --git a/pkg/corerp/backend/deployment/deploymentprocessor.go b/pkg/corerp/backend/deployment/deploymentprocessor.go index f5745b74d0..2ebf21a3e3 100644 --- a/pkg/corerp/backend/deployment/deploymentprocessor.go +++ b/pkg/corerp/backend/deployment/deploymentprocessor.go @@ -568,6 +568,12 @@ func (dp *deploymentProcessor) getResourceDataByID(ctx context.Context, resource return ResourceData{}, fmt.Errorf(errMsg, resourceID.String(), err) } return dp.buildResourceDependency(resourceID, obj.Properties.Application, obj, obj.Properties.Status.OutputResources, obj.ComputedValues, obj.SecretValues, portableresources.RecipeData{}) + case strings.ToLower(dapr_ctrl.DaprConfigurationStoresResourceType): + obj := &dapr_dm.DaprConfigurationStore{} + if err = resource.As(obj); err != nil { + return ResourceData{}, fmt.Errorf(errMsg, resourceID.String(), err) + } + return dp.buildResourceDependency(resourceID, obj.Properties.Application, obj, obj.Properties.Status.OutputResources, obj.ComputedValues, obj.SecretValues, portableresources.RecipeData{}) default: return ResourceData{}, fmt.Errorf("unsupported resource type: %q for resource ID: %q", resourceType, resourceID.String()) } diff --git a/pkg/daprrp/api/v20231001preview/configurationstore_conversion.go b/pkg/daprrp/api/v20231001preview/configurationstore_conversion.go new file mode 100644 index 0000000000..ebbacccc08 --- /dev/null +++ b/pkg/daprrp/api/v20231001preview/configurationstore_conversion.go @@ -0,0 +1,149 @@ +/* +Copyright 2023 The Radius Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v20231001preview + +import ( + "fmt" + "reflect" + "strings" + + v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/daprrp/datamodel" + "github.com/radius-project/radius/pkg/portableresources" + rpv1 "github.com/radius-project/radius/pkg/rp/v1" + "github.com/radius-project/radius/pkg/to" +) + +// ConvertTo converts a versioned DaprConfigurationStoreResource to a version-agnostic DaprConfigurationStore. It returns an error +// if the mode is not specified or if the required properties for the mode are not specified. +func (src *DaprConfigurationStoreResource) ConvertTo() (v1.DataModelInterface, error) { + daprConfigurationStoreProperties := datamodel.DaprConfigurationStoreProperties{ + BasicResourceProperties: rpv1.BasicResourceProperties{ + Environment: to.String(src.Properties.Environment), + Application: to.String(src.Properties.Application), + }, + } + + trackedResource := v1.TrackedResource{ + ID: to.String(src.ID), + Name: to.String(src.Name), + Type: to.String(src.Type), + Location: to.String(src.Location), + Tags: to.StringMap(src.Tags), + } + + internalMetadata := v1.InternalMetadata{ + UpdatedAPIVersion: Version, + AsyncProvisioningState: toProvisioningStateDataModel(src.Properties.ProvisioningState), + } + + converted := &datamodel.DaprConfigurationStore{} + converted.TrackedResource = trackedResource + converted.InternalMetadata = internalMetadata + converted.Properties = daprConfigurationStoreProperties + + var err error + converted.Properties.ResourceProvisioning, err = toResourceProvisiongDataModel(src.Properties.ResourceProvisioning) + if err != nil { + return nil, err + } + + converted.Properties.Resources = toResourcesDataModel(src.Properties.Resources) + converted.Properties.Auth = toAuthDataModel(src.Properties.Auth) + + // Note: The metadata, type, and version fields cannot be specified when using recipes since + // the recipe is expected to create the Dapr Component manifest. However, they are required + // when resourceProvisioning is set to manual. + msgs := []string{} + if converted.Properties.ResourceProvisioning == portableresources.ResourceProvisioningManual { + if src.Properties.Recipe != nil && (!reflect.ValueOf(*src.Properties.Recipe).IsZero()) { + msgs = append(msgs, "recipe details cannot be specified when resourceProvisioning is set to manual") + } + if src.Properties.Metadata == nil || len(src.Properties.Metadata) == 0 { + msgs = append(msgs, "metadata must be specified when resourceProvisioning is set to manual") + } + if src.Properties.Type == nil || *src.Properties.Type == "" { + msgs = append(msgs, "type must be specified when resourceProvisioning is set to manual") + } + if src.Properties.Version == nil || *src.Properties.Version == "" { + msgs = append(msgs, "version must be specified when resourceProvisioning is set to manual") + } + converted.Properties.Metadata = toMetadataDataModel(src.Properties.Metadata) + converted.Properties.Type = to.String(src.Properties.Type) + converted.Properties.Version = to.String(src.Properties.Version) + } else { + if src.Properties.Metadata != nil && (!reflect.ValueOf(src.Properties.Metadata).IsZero()) { + msgs = append(msgs, "metadata cannot be specified when resourceProvisioning is set to recipe (default)") + } + if src.Properties.Type != nil && (!reflect.ValueOf(*src.Properties.Type).IsZero()) { + msgs = append(msgs, "type cannot be specified when resourceProvisioning is set to recipe (default)") + } + if src.Properties.Version != nil && (!reflect.ValueOf(*src.Properties.Version).IsZero()) { + msgs = append(msgs, "version cannot be specified when resourceProvisioning is set to recipe (default)") + } + + converted.Properties.Recipe = toRecipeDataModel(src.Properties.Recipe) + } + if len(msgs) > 0 { + return nil, &v1.ErrClientRP{ + Code: v1.CodeInvalid, + Message: fmt.Sprintf("error(s) found:\n\t%v", strings.Join(msgs, "\n\t")), + } + } + + return converted, nil +} + +// ConvertFrom converts from version-agnostic datamodel to the versioned DaprConfigurationStore resource. +// If the DataModelInterface is not of the correct type, an error is returned. +func (dst *DaprConfigurationStoreResource) ConvertFrom(src v1.DataModelInterface) error { + daprConfigstore, ok := src.(*datamodel.DaprConfigurationStore) + if !ok { + return v1.ErrInvalidModelConversion + } + + dst.ID = to.Ptr(daprConfigstore.ID) + dst.Name = to.Ptr(daprConfigstore.Name) + dst.Type = to.Ptr(daprConfigstore.Type) + dst.SystemData = fromSystemDataModel(daprConfigstore.SystemData) + dst.Location = to.Ptr(daprConfigstore.Location) + dst.Tags = *to.StringMapPtr(daprConfigstore.Tags) + + dst.Properties = &DaprConfigurationStoreProperties{ + Environment: to.Ptr(daprConfigstore.Properties.Environment), + Application: to.Ptr(daprConfigstore.Properties.Application), + ResourceProvisioning: fromResourceProvisioningDataModel(daprConfigstore.Properties.ResourceProvisioning), + Resources: fromResourcesDataModel(daprConfigstore.Properties.Resources), + ComponentName: to.Ptr(daprConfigstore.Properties.ComponentName), + ProvisioningState: fromProvisioningStateDataModel(daprConfigstore.InternalMetadata.AsyncProvisioningState), + Status: &ResourceStatus{ + OutputResources: toOutputResources(daprConfigstore.Properties.Status.OutputResources), + Recipe: fromRecipeStatus(daprConfigstore.Properties.Status.Recipe), + }, + Auth: fromAuthDataModel(daprConfigstore.Properties.Auth), + } + + if daprConfigstore.Properties.ResourceProvisioning == portableresources.ResourceProvisioningManual { + dst.Properties.Metadata = fromMetadataDataModel(daprConfigstore.Properties.Metadata) + dst.Properties.Type = to.Ptr(daprConfigstore.Properties.Type) + dst.Properties.Version = to.Ptr(daprConfigstore.Properties.Version) + } else { + dst.Properties.Recipe = fromRecipeDataModel(daprConfigstore.Properties.Recipe) + } + + return nil +} diff --git a/pkg/daprrp/api/v20231001preview/configurationstore_conversion_test.go b/pkg/daprrp/api/v20231001preview/configurationstore_conversion_test.go new file mode 100644 index 0000000000..3fc133e505 --- /dev/null +++ b/pkg/daprrp/api/v20231001preview/configurationstore_conversion_test.go @@ -0,0 +1,271 @@ +/* +Copyright 2023 The Radius Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v20231001preview + +import ( + "encoding/json" + "testing" + + v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/daprrp/datamodel" + "github.com/radius-project/radius/pkg/daprrp/frontend/controller" + "github.com/radius-project/radius/pkg/portableresources" + rpv1 "github.com/radius-project/radius/pkg/rp/v1" + "github.com/radius-project/radius/pkg/to" + "github.com/radius-project/radius/test/testutil" + "github.com/radius-project/radius/test/testutil/resourcetypeutil" + "github.com/stretchr/testify/require" +) + +func TestDaprConfigurationStore_ConvertVersionedToDataModel(t *testing.T) { + testCases := []struct { + desc string + file string + expected *datamodel.DaprConfigurationStore + }{ + { + desc: "Manual provisioning of a DaprConfigurationStore", + file: "configurationstore_manual_resource.json", + expected: &datamodel.DaprConfigurationStore{ + BaseResource: v1.BaseResource{ + TrackedResource: v1.TrackedResource{ + ID: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Dapr/configurationStores/test-dcs", + Name: "test-dcs", + Type: controller.DaprConfigurationStoresResourceType, + Location: v1.LocationGlobal, + Tags: map[string]string{ + "env": "dev", + }, + }, + InternalMetadata: v1.InternalMetadata{ + CreatedAPIVersion: "", + UpdatedAPIVersion: "2023-10-01-preview", + AsyncProvisioningState: v1.ProvisioningStateAccepted, + }, + SystemData: v1.SystemData{}, + }, + Properties: datamodel.DaprConfigurationStoreProperties{ + BasicResourceProperties: rpv1.BasicResourceProperties{ + Application: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/applications/test-app", + Environment: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/environments/test-env", + }, + ResourceProvisioning: portableresources.ResourceProvisioningManual, + Metadata: map[string]*rpv1.DaprComponentMetadataValue{ + "foo": { + Value: "bar", + }, + }, + Resources: []*portableresources.ResourceReference{ + { + ID: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Microsoft.ServiceBus/namespaces/radius-eastus-async", + }, + }, + Type: "configuration.azure.appconfig", + Version: "v1", + }, + }, + }, + { + desc: "Provisioning by a Recipe of a configuration store", + file: "configurationstore_recipe_resource.json", + expected: &datamodel.DaprConfigurationStore{ + BaseResource: v1.BaseResource{ + TrackedResource: v1.TrackedResource{ + ID: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Dapr/configurationStores/test-dcs", + Name: "test-dcs", + Type: controller.DaprConfigurationStoresResourceType, + Location: v1.LocationGlobal, + Tags: map[string]string{ + "env": "dev", + }, + }, + InternalMetadata: v1.InternalMetadata{ + CreatedAPIVersion: "", + UpdatedAPIVersion: "2023-10-01-preview", + AsyncProvisioningState: v1.ProvisioningStateAccepted, + }, + SystemData: v1.SystemData{}, + }, + Properties: datamodel.DaprConfigurationStoreProperties{ + BasicResourceProperties: rpv1.BasicResourceProperties{ + Application: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/applications/test-app", + Environment: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/environments/test-env", + }, + ResourceProvisioning: portableresources.ResourceProvisioningRecipe, + Recipe: portableresources.ResourceRecipe{ + Name: "dcs-recipe", + }, + }, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + // arrange + rawPayload := testutil.ReadFixture(tc.file) + versionedResource := &DaprConfigurationStoreResource{} + err := json.Unmarshal(rawPayload, versionedResource) + require.NoError(t, err) + + // act + dm, err := versionedResource.ConvertTo() + + // assert + require.NoError(t, err) + convertedResource := dm.(*datamodel.DaprConfigurationStore) + + require.Equal(t, tc.expected, convertedResource) + }) + } +} + +func TestDaprConfigurationStore_ConvertVersionedToDataModel_Invalid(t *testing.T) { + testset := []struct { + payload string + errType error + message string + }{ + { + "configurationstore_invalidmanual_resource.json", + &v1.ErrClientRP{}, + "code BadRequest: err error(s) found:\n\trecipe details cannot be specified when resourceProvisioning is set to manual\n\tmetadata must be specified when resourceProvisioning is set to manual\n\ttype must be specified when resourceProvisioning is set to manual\n\tversion must be specified when resourceProvisioning is set to manual", + }, + { + "configurationstore_invalidrecipe_resource.json", + &v1.ErrClientRP{}, + "code BadRequest: err error(s) found:\n\tmetadata cannot be specified when resourceProvisioning is set to recipe (default)\n\ttype cannot be specified when resourceProvisioning is set to recipe (default)\n\tversion cannot be specified when resourceProvisioning is set to recipe (default)", + }, + } + + for _, test := range testset { + t.Run(test.payload, func(t *testing.T) { + rawPayload := testutil.ReadFixture(test.payload) + versionedResource := &DaprConfigurationStoreResource{} + err := json.Unmarshal(rawPayload, versionedResource) + require.NoError(t, err) + + dm, err := versionedResource.ConvertTo() + require.Error(t, err) + require.Nil(t, dm) + require.IsType(t, test.errType, err) + require.Equal(t, test.message, err.Error()) + }) + } +} + +func TestDaprConfigurationStore_ConvertDataModelToVersioned(t *testing.T) { + testCases := []struct { + desc string + file string + expected *DaprConfigurationStoreResource + }{ + { + desc: "Convert manually provisioned DaprConfigurationStore datamodel to versioned resource", + file: "configurationstore_manual_datamodel.json", + expected: &DaprConfigurationStoreResource{ + Location: to.Ptr(v1.LocationGlobal), + Properties: &DaprConfigurationStoreProperties{ + Environment: to.Ptr("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/environments/test-env"), + Application: to.Ptr("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/applications/test-app"), + Metadata: map[string]*MetadataValue{ + "foo": { + Value: to.Ptr("bar"), + }, + }, + ResourceProvisioning: to.Ptr(ResourceProvisioningManual), + Resources: []*ResourceReference{ + { + ID: to.Ptr("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Microsoft.ServiceBus/namespaces/radius-eastus-async"), + }, + }, + Type: to.Ptr("configuration.azure.appconfig"), + Version: to.Ptr("v1"), + ComponentName: to.Ptr("test-dcs"), + ProvisioningState: to.Ptr(ProvisioningStateAccepted), + Status: resourcetypeutil.MustPopulateResourceStatus(&ResourceStatus{}), + Auth: &DaprResourceAuth{SecretStore: to.Ptr("test-secret-store")}, + }, + Tags: map[string]*string{ + "env": to.Ptr("dev"), + }, + ID: to.Ptr("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Dapr/configurationStores/test-dcs"), + Name: to.Ptr("test-dcs"), + Type: to.Ptr(controller.DaprConfigurationStoresResourceType), + }, + }, + { + desc: "Convert DaprConfigurationStore datamodel provisioned by a recipe to versioned resource", + file: "configurationstore_recipe_datamodel.json", + expected: &DaprConfigurationStoreResource{ + Location: to.Ptr(v1.LocationGlobal), + Properties: &DaprConfigurationStoreProperties{ + Environment: to.Ptr("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/environments/test-env"), + Application: to.Ptr("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/applications/test-app"), + Recipe: &Recipe{ + Name: to.Ptr("dcs-recipe"), + }, + ResourceProvisioning: to.Ptr(ResourceProvisioningRecipe), + ComponentName: to.Ptr("test-dcs"), + ProvisioningState: to.Ptr(ProvisioningStateAccepted), + Status: resourcetypeutil.MustPopulateResourceStatusWithRecipe(&ResourceStatus{}), + Auth: nil, + }, + Tags: map[string]*string{ + "env": to.Ptr("dev"), + }, + ID: to.Ptr("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Dapr/configurationStores/test-dcs"), + Name: to.Ptr("test-dcs"), + Type: to.Ptr(controller.DaprConfigurationStoresResourceType), + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + rawPayload := testutil.ReadFixture(tc.file) + resource := &datamodel.DaprConfigurationStore{} + err := json.Unmarshal(rawPayload, resource) + require.NoError(t, err) + + versionedResource := &DaprConfigurationStoreResource{} + err = versionedResource.ConvertFrom(resource) + require.NoError(t, err) + + // Skip system data comparison + versionedResource.SystemData = nil + + require.Equal(t, tc.expected, versionedResource) + }) + } +} + +func TestDaprConfigurationStore_ConvertFromValidation(t *testing.T) { + validationTests := []struct { + src v1.DataModelInterface + err error + }{ + {&resourcetypeutil.FakeResource{}, v1.ErrInvalidModelConversion}, + {nil, v1.ErrInvalidModelConversion}, + } + + for _, tc := range validationTests { + versioned := &DaprConfigurationStoreResource{} + err := versioned.ConvertFrom(tc.src) + require.ErrorAs(t, tc.err, &err) + } +} diff --git a/pkg/daprrp/api/v20231001preview/pubsubbroker_conversion_test.go b/pkg/daprrp/api/v20231001preview/pubsubbroker_conversion_test.go index 20be95fb2a..3c18f0432d 100644 --- a/pkg/daprrp/api/v20231001preview/pubsubbroker_conversion_test.go +++ b/pkg/daprrp/api/v20231001preview/pubsubbroker_conversion_test.go @@ -155,7 +155,7 @@ func TestDaprPubSubBroker_ConvertVersionedToDataModel_Invalid(t *testing.T) { for _, test := range testset { t.Run(test.payload, func(t *testing.T) { rawPayload := testutil.ReadFixture(test.payload) - versionedResource := &DaprStateStoreResource{} + versionedResource := &DaprPubSubBrokerResource{} err := json.Unmarshal(rawPayload, versionedResource) require.NoError(t, err) diff --git a/pkg/daprrp/api/v20231001preview/testdata/configurationstore_invalidmanual_resource.json b/pkg/daprrp/api/v20231001preview/testdata/configurationstore_invalidmanual_resource.json new file mode 100644 index 0000000000..1ec26efcbe --- /dev/null +++ b/pkg/daprrp/api/v20231001preview/testdata/configurationstore_invalidmanual_resource.json @@ -0,0 +1,17 @@ +{ + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Dapr/configurationStores/test-dcs", + "name": "test-dcs", + "type": "Applications.Dapr/configurationStores", + "location": "global", + "tags": { + "env": "dev" + }, + "properties": { + "application": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/applications/test-app", + "environment": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/environments/test-env", + "resourceProvisioning": "manual", + "recipe": { + "name": "test-recipe" + } + } +} \ No newline at end of file diff --git a/pkg/daprrp/api/v20231001preview/testdata/configurationstore_invalidrecipe_resource.json b/pkg/daprrp/api/v20231001preview/testdata/configurationstore_invalidrecipe_resource.json new file mode 100644 index 0000000000..19b8a2e89a --- /dev/null +++ b/pkg/daprrp/api/v20231001preview/testdata/configurationstore_invalidrecipe_resource.json @@ -0,0 +1,20 @@ +{ + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Dapr/configurationStores/test-dcs", + "name": "test-dcs", + "type": "Applications.Dapr/configurationStores", + "location": "global", + "tags": { + "env": "dev" + }, + "properties": { + "application": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/applications/test-app", + "environment": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/environments/test-env", + "type": "configuration.azure.appconfig", + "version": "v1", + "metadata": { + "foo": { + "value": "bar" + } + } + } +} \ No newline at end of file diff --git a/pkg/daprrp/api/v20231001preview/testdata/configurationstore_manual_datamodel.json b/pkg/daprrp/api/v20231001preview/testdata/configurationstore_manual_datamodel.json new file mode 100644 index 0000000000..9b9fbc2ca9 --- /dev/null +++ b/pkg/daprrp/api/v20231001preview/testdata/configurationstore_manual_datamodel.json @@ -0,0 +1,45 @@ +{ + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Dapr/configurationStores/test-dcs", + "name": "test-dcs", + "type": "Applications.Dapr/configurationStores", + "location": "global", + "tags": { + "env": "dev" + }, + "systemData": { + "createdBy": "fakeid@live.com", + "createdByType": "User", + "createdAt": "2021-09-24T19:09:54.2403864Z", + "lastModifiedBy": "fakeid@live.com", + "lastModifiedByType": "User", + "lastModifiedAt": "2021-09-24T20:09:54.2403864Z" + }, + "properties": { + "auth": { + "secretStore": "test-secret-store" + }, + "componentName": "test-dcs", + "status": { + "outputResources": [ + { + "id": "/planes/test/local/providers/Test.Namespace/testResources/test-resource" + } + ] + }, + "application": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/applications/test-app", + "environment": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/environments/test-env", + "resourceProvisioning": "manual", + "type": "configuration.azure.appconfig", + "version": "v1", + "metadata": { + "foo": { + "value": "bar" + } + }, + "resources": [ + { + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Microsoft.ServiceBus/namespaces/radius-eastus-async" + } + ] + } +} \ No newline at end of file diff --git a/pkg/daprrp/api/v20231001preview/testdata/configurationstore_manual_generic_datamodel.json b/pkg/daprrp/api/v20231001preview/testdata/configurationstore_manual_generic_datamodel.json new file mode 100644 index 0000000000..69ed4748bf --- /dev/null +++ b/pkg/daprrp/api/v20231001preview/testdata/configurationstore_manual_generic_datamodel.json @@ -0,0 +1,41 @@ +{ + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Dapr/configurationStores/test-dcs", + "name": "test-dcs", + "type": "Applications.Dapr/configurationStores", + "location": "global", + "systemData": { + "createdBy": "fakeid@live.com", + "createdByType": "User", + "createdAt": "2021-09-24T19:09:54.2403864Z", + "lastModifiedBy": "fakeid@live.com", + "lastModifiedByType": "User", + "lastModifiedAt": "2021-09-24T20:09:54.2403864Z" + }, + "tags": { + "env": "dev" + }, + "properties": { + "auth": { + "secretStore": "test-secret-store" + }, + "componentName": "test-dcs", + "status": { + "outputResources": [ + { + "id": "/planes/test/local/providers/Test.Namespace/testResources/test-resource" + } + ] + }, + "application": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/applications/test-app", + "environment": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/environments/test-env", + "kind": "generic", + "resourceProvisioning": "manual", + "type": "configuration.redis", + "version": "v1", + "metadata": { + "foo": { + "value": "bar" + } + } + } +} \ No newline at end of file diff --git a/pkg/daprrp/api/v20231001preview/testdata/configurationstore_manual_resource.json b/pkg/daprrp/api/v20231001preview/testdata/configurationstore_manual_resource.json new file mode 100644 index 0000000000..3b81eb6794 --- /dev/null +++ b/pkg/daprrp/api/v20231001preview/testdata/configurationstore_manual_resource.json @@ -0,0 +1,26 @@ +{ + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Dapr/configurationStores/test-dcs", + "name": "test-dcs", + "type": "Applications.Dapr/configurationStores", + "location": "global", + "tags": { + "env": "dev" + }, + "properties": { + "application": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/applications/test-app", + "environment": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/environments/test-env", + "resourceProvisioning": "manual", + "type": "configuration.azure.appconfig", + "version": "v1", + "metadata": { + "foo": { + "value": "bar" + } + }, + "resources": [ + { + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Microsoft.ServiceBus/namespaces/radius-eastus-async" + } + ] + } +} \ No newline at end of file diff --git a/pkg/daprrp/api/v20231001preview/testdata/configurationstore_recipe_datamodel.json b/pkg/daprrp/api/v20231001preview/testdata/configurationstore_recipe_datamodel.json new file mode 100644 index 0000000000..0fec759021 --- /dev/null +++ b/pkg/daprrp/api/v20231001preview/testdata/configurationstore_recipe_datamodel.json @@ -0,0 +1,37 @@ +{ + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Dapr/configurationStores/test-dcs", + "name": "test-dcs", + "type": "Applications.Dapr/configurationStores", + "location": "global", + "tags": { + "env": "dev" + }, + "systemData": { + "createdBy": "fakeid@live.com", + "createdByType": "User", + "createdAt": "2021-09-24T19:09:54.2403864Z", + "lastModifiedBy": "fakeid@live.com", + "lastModifiedByType": "User", + "lastModifiedAt": "2021-09-24T20:09:54.2403864Z" + }, + "properties": { + "componentName": "test-dcs", + "status": { + "outputResources": [ + { + "id": "/planes/test/local/providers/Test.Namespace/testResources/test-resource" + } + ], + "recipe": { + "templateKind": "bicep", + "templatePath": "br:sampleregistry.azureacr.io/radius/recipes/abc" + } + }, + "application": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/applications/test-app", + "environment": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/environments/test-env", + "resourceProvisioning": "recipe", + "recipe": { + "name": "dcs-recipe" + } + } +} \ No newline at end of file diff --git a/pkg/daprrp/api/v20231001preview/testdata/configurationstore_recipe_resource.json b/pkg/daprrp/api/v20231001preview/testdata/configurationstore_recipe_resource.json new file mode 100644 index 0000000000..16fcaae0d5 --- /dev/null +++ b/pkg/daprrp/api/v20231001preview/testdata/configurationstore_recipe_resource.json @@ -0,0 +1,17 @@ +{ + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Dapr/configurationStores/test-dcs", + "name": "test-dcs", + "type": "Applications.Dapr/configurationStores", + "location": "global", + "tags": { + "env": "dev" + }, + "properties": { + "application": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/applications/test-app", + "environment": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/environments/test-env", + "resourceProvisioning": "recipe", + "recipe": { + "name": "dcs-recipe" + } + } +} \ No newline at end of file diff --git a/pkg/daprrp/api/v20231001preview/zz_generated_client_factory.go b/pkg/daprrp/api/v20231001preview/zz_generated_client_factory.go index d4df8b1ddd..e5971bf730 100644 --- a/pkg/daprrp/api/v20231001preview/zz_generated_client_factory.go +++ b/pkg/daprrp/api/v20231001preview/zz_generated_client_factory.go @@ -38,6 +38,11 @@ func NewClientFactory(rootScope string, credential azcore.TokenCredential, optio }, nil } +func (c *ClientFactory) NewConfigurationStoresClient() *ConfigurationStoresClient { + subClient, _ := NewConfigurationStoresClient(c.rootScope, c.credential, c.options) + return subClient +} + func (c *ClientFactory) NewOperationsClient() *OperationsClient { subClient, _ := NewOperationsClient(c.credential, c.options) return subClient diff --git a/pkg/daprrp/api/v20231001preview/zz_generated_configurationstores_client.go b/pkg/daprrp/api/v20231001preview/zz_generated_configurationstores_client.go new file mode 100644 index 0000000000..56cafd78e9 --- /dev/null +++ b/pkg/daprrp/api/v20231001preview/zz_generated_configurationstores_client.go @@ -0,0 +1,349 @@ +//go:build go1.18 +// +build go1.18 + +// Licensed under the Apache License, Version 2.0 . See LICENSE in the repository root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package v20231001preview + +import ( + "context" + "errors" + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" + "net/http" + "net/url" + "strings" +) + +// ConfigurationStoresClient contains the methods for the ConfigurationStores group. +// Don't use this type directly, use NewConfigurationStoresClient() instead. +type ConfigurationStoresClient struct { + internal *arm.Client + rootScope string +} + +// NewConfigurationStoresClient creates a new instance of ConfigurationStoresClient with the specified values. +// - rootScope - The scope in which the resource is present. UCP Scope is /planes/{planeType}/{planeName}/resourceGroup/{resourcegroupID} +// and Azure resource scope is +// /subscriptions/{subscriptionID}/resourceGroup/{resourcegroupID} +// - credential - used to authorize requests. Usually a credential from azidentity. +// - options - pass nil to accept the default values. +func NewConfigurationStoresClient(rootScope string, credential azcore.TokenCredential, options *arm.ClientOptions) (*ConfigurationStoresClient, error) { + cl, err := arm.NewClient(moduleName+".ConfigurationStoresClient", moduleVersion, credential, options) + if err != nil { + return nil, err + } + client := &ConfigurationStoresClient{ + rootScope: rootScope, + internal: cl, + } + return client, nil +} + +// BeginCreateOrUpdate - Create a DaprConfigurationStoreResource +// If the operation fails it returns an *azcore.ResponseError type. +// +// Generated from API version 2023-10-01-preview +// - configurationStoreName - Configuration Store name +// - resource - Resource create parameters. +// - options - ConfigurationStoresClientBeginCreateOrUpdateOptions contains the optional parameters for the ConfigurationStoresClient.BeginCreateOrUpdate +// method. +func (client *ConfigurationStoresClient) BeginCreateOrUpdate(ctx context.Context, configurationStoreName string, resource DaprConfigurationStoreResource, options *ConfigurationStoresClientBeginCreateOrUpdateOptions) (*runtime.Poller[ConfigurationStoresClientCreateOrUpdateResponse], error) { + if options == nil || options.ResumeToken == "" { + resp, err := client.createOrUpdate(ctx, configurationStoreName, resource, options) + if err != nil { + return nil, err + } + poller, err := runtime.NewPoller(resp, client.internal.Pipeline(), &runtime.NewPollerOptions[ConfigurationStoresClientCreateOrUpdateResponse]{ + FinalStateVia: runtime.FinalStateViaAzureAsyncOp, + }) + return poller, err + } else { + return runtime.NewPollerFromResumeToken[ConfigurationStoresClientCreateOrUpdateResponse](options.ResumeToken, client.internal.Pipeline(), nil) + } +} + +// CreateOrUpdate - Create a DaprConfigurationStoreResource +// If the operation fails it returns an *azcore.ResponseError type. +// +// Generated from API version 2023-10-01-preview +func (client *ConfigurationStoresClient) createOrUpdate(ctx context.Context, configurationStoreName string, resource DaprConfigurationStoreResource, options *ConfigurationStoresClientBeginCreateOrUpdateOptions) (*http.Response, error) { + var err error + req, err := client.createOrUpdateCreateRequest(ctx, configurationStoreName, resource, options) + if err != nil { + return nil, err + } + httpResp, err := client.internal.Pipeline().Do(req) + if err != nil { + return nil, err + } + if !runtime.HasStatusCode(httpResp, http.StatusOK, http.StatusCreated) { + err = runtime.NewResponseError(httpResp) + return nil, err + } + return httpResp, nil +} + +// createOrUpdateCreateRequest creates the CreateOrUpdate request. +func (client *ConfigurationStoresClient) createOrUpdateCreateRequest(ctx context.Context, configurationStoreName string, resource DaprConfigurationStoreResource, options *ConfigurationStoresClientBeginCreateOrUpdateOptions) (*policy.Request, error) { + urlPath := "/{rootScope}/providers/Applications.Dapr/configurationStores/{configurationStoreName}" + urlPath = strings.ReplaceAll(urlPath, "{rootScope}", client.rootScope) + if configurationStoreName == "" { + return nil, errors.New("parameter configurationStoreName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{configurationStoreName}", url.PathEscape(configurationStoreName)) + req, err := runtime.NewRequest(ctx, http.MethodPut, runtime.JoinPaths(client.internal.Endpoint(), urlPath)) + if err != nil { + return nil, err + } + reqQP := req.Raw().URL.Query() + reqQP.Set("api-version", "2023-10-01-preview") + req.Raw().URL.RawQuery = reqQP.Encode() + req.Raw().Header["Accept"] = []string{"application/json"} + if err := runtime.MarshalAsJSON(req, resource); err != nil { + return nil, err +} + return req, nil +} + +// BeginDelete - Delete a DaprConfigurationStoreResource +// If the operation fails it returns an *azcore.ResponseError type. +// +// Generated from API version 2023-10-01-preview +// - configurationStoreName - Configuration Store name +// - options - ConfigurationStoresClientBeginDeleteOptions contains the optional parameters for the ConfigurationStoresClient.BeginDelete +// method. +func (client *ConfigurationStoresClient) BeginDelete(ctx context.Context, configurationStoreName string, options *ConfigurationStoresClientBeginDeleteOptions) (*runtime.Poller[ConfigurationStoresClientDeleteResponse], error) { + if options == nil || options.ResumeToken == "" { + resp, err := client.deleteOperation(ctx, configurationStoreName, options) + if err != nil { + return nil, err + } + poller, err := runtime.NewPoller(resp, client.internal.Pipeline(), &runtime.NewPollerOptions[ConfigurationStoresClientDeleteResponse]{ + FinalStateVia: runtime.FinalStateViaLocation, + }) + return poller, err + } else { + return runtime.NewPollerFromResumeToken[ConfigurationStoresClientDeleteResponse](options.ResumeToken, client.internal.Pipeline(), nil) + } +} + +// Delete - Delete a DaprConfigurationStoreResource +// If the operation fails it returns an *azcore.ResponseError type. +// +// Generated from API version 2023-10-01-preview +func (client *ConfigurationStoresClient) deleteOperation(ctx context.Context, configurationStoreName string, options *ConfigurationStoresClientBeginDeleteOptions) (*http.Response, error) { + var err error + req, err := client.deleteCreateRequest(ctx, configurationStoreName, options) + if err != nil { + return nil, err + } + httpResp, err := client.internal.Pipeline().Do(req) + if err != nil { + return nil, err + } + if !runtime.HasStatusCode(httpResp, http.StatusOK, http.StatusAccepted, http.StatusNoContent) { + err = runtime.NewResponseError(httpResp) + return nil, err + } + return httpResp, nil +} + +// deleteCreateRequest creates the Delete request. +func (client *ConfigurationStoresClient) deleteCreateRequest(ctx context.Context, configurationStoreName string, options *ConfigurationStoresClientBeginDeleteOptions) (*policy.Request, error) { + urlPath := "/{rootScope}/providers/Applications.Dapr/configurationStores/{configurationStoreName}" + urlPath = strings.ReplaceAll(urlPath, "{rootScope}", client.rootScope) + if configurationStoreName == "" { + return nil, errors.New("parameter configurationStoreName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{configurationStoreName}", url.PathEscape(configurationStoreName)) + req, err := runtime.NewRequest(ctx, http.MethodDelete, runtime.JoinPaths(client.internal.Endpoint(), urlPath)) + if err != nil { + return nil, err + } + reqQP := req.Raw().URL.Query() + reqQP.Set("api-version", "2023-10-01-preview") + req.Raw().URL.RawQuery = reqQP.Encode() + req.Raw().Header["Accept"] = []string{"application/json"} + return req, nil +} + +// Get - Get a DaprConfigurationStoreResource +// If the operation fails it returns an *azcore.ResponseError type. +// +// Generated from API version 2023-10-01-preview +// - configurationStoreName - Configuration Store name +// - options - ConfigurationStoresClientGetOptions contains the optional parameters for the ConfigurationStoresClient.Get method. +func (client *ConfigurationStoresClient) Get(ctx context.Context, configurationStoreName string, options *ConfigurationStoresClientGetOptions) (ConfigurationStoresClientGetResponse, error) { + var err error + req, err := client.getCreateRequest(ctx, configurationStoreName, options) + if err != nil { + return ConfigurationStoresClientGetResponse{}, err + } + httpResp, err := client.internal.Pipeline().Do(req) + if err != nil { + return ConfigurationStoresClientGetResponse{}, err + } + if !runtime.HasStatusCode(httpResp, http.StatusOK) { + err = runtime.NewResponseError(httpResp) + return ConfigurationStoresClientGetResponse{}, err + } + resp, err := client.getHandleResponse(httpResp) + return resp, err +} + +// getCreateRequest creates the Get request. +func (client *ConfigurationStoresClient) getCreateRequest(ctx context.Context, configurationStoreName string, options *ConfigurationStoresClientGetOptions) (*policy.Request, error) { + urlPath := "/{rootScope}/providers/Applications.Dapr/configurationStores/{configurationStoreName}" + urlPath = strings.ReplaceAll(urlPath, "{rootScope}", client.rootScope) + if configurationStoreName == "" { + return nil, errors.New("parameter configurationStoreName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{configurationStoreName}", url.PathEscape(configurationStoreName)) + req, err := runtime.NewRequest(ctx, http.MethodGet, runtime.JoinPaths(client.internal.Endpoint(), urlPath)) + if err != nil { + return nil, err + } + reqQP := req.Raw().URL.Query() + reqQP.Set("api-version", "2023-10-01-preview") + req.Raw().URL.RawQuery = reqQP.Encode() + req.Raw().Header["Accept"] = []string{"application/json"} + return req, nil +} + +// getHandleResponse handles the Get response. +func (client *ConfigurationStoresClient) getHandleResponse(resp *http.Response) (ConfigurationStoresClientGetResponse, error) { + result := ConfigurationStoresClientGetResponse{} + if err := runtime.UnmarshalAsJSON(resp, &result.DaprConfigurationStoreResource); err != nil { + return ConfigurationStoresClientGetResponse{}, err + } + return result, nil +} + +// NewListByScopePager - List DaprConfigurationStoreResource resources by Scope +// +// Generated from API version 2023-10-01-preview +// - options - ConfigurationStoresClientListByScopeOptions contains the optional parameters for the ConfigurationStoresClient.NewListByScopePager +// method. +func (client *ConfigurationStoresClient) NewListByScopePager(options *ConfigurationStoresClientListByScopeOptions) (*runtime.Pager[ConfigurationStoresClientListByScopeResponse]) { + return runtime.NewPager(runtime.PagingHandler[ConfigurationStoresClientListByScopeResponse]{ + More: func(page ConfigurationStoresClientListByScopeResponse) bool { + return page.NextLink != nil && len(*page.NextLink) > 0 + }, + Fetcher: func(ctx context.Context, page *ConfigurationStoresClientListByScopeResponse) (ConfigurationStoresClientListByScopeResponse, error) { + var req *policy.Request + var err error + if page == nil { + req, err = client.listByScopeCreateRequest(ctx, options) + } else { + req, err = runtime.NewRequest(ctx, http.MethodGet, *page.NextLink) + } + if err != nil { + return ConfigurationStoresClientListByScopeResponse{}, err + } + resp, err := client.internal.Pipeline().Do(req) + if err != nil { + return ConfigurationStoresClientListByScopeResponse{}, err + } + if !runtime.HasStatusCode(resp, http.StatusOK) { + return ConfigurationStoresClientListByScopeResponse{}, runtime.NewResponseError(resp) + } + return client.listByScopeHandleResponse(resp) + }, + }) +} + +// listByScopeCreateRequest creates the ListByScope request. +func (client *ConfigurationStoresClient) listByScopeCreateRequest(ctx context.Context, options *ConfigurationStoresClientListByScopeOptions) (*policy.Request, error) { + urlPath := "/{rootScope}/providers/Applications.Dapr/configurationStores" + urlPath = strings.ReplaceAll(urlPath, "{rootScope}", client.rootScope) + req, err := runtime.NewRequest(ctx, http.MethodGet, runtime.JoinPaths(client.internal.Endpoint(), urlPath)) + if err != nil { + return nil, err + } + reqQP := req.Raw().URL.Query() + reqQP.Set("api-version", "2023-10-01-preview") + req.Raw().URL.RawQuery = reqQP.Encode() + req.Raw().Header["Accept"] = []string{"application/json"} + return req, nil +} + +// listByScopeHandleResponse handles the ListByScope response. +func (client *ConfigurationStoresClient) listByScopeHandleResponse(resp *http.Response) (ConfigurationStoresClientListByScopeResponse, error) { + result := ConfigurationStoresClientListByScopeResponse{} + if err := runtime.UnmarshalAsJSON(resp, &result.DaprConfigurationStoreResourceListResult); err != nil { + return ConfigurationStoresClientListByScopeResponse{}, err + } + return result, nil +} + +// BeginUpdate - Update a DaprConfigurationStoreResource +// If the operation fails it returns an *azcore.ResponseError type. +// +// Generated from API version 2023-10-01-preview +// - configurationStoreName - Configuration Store name +// - properties - The resource properties to be updated. +// - options - ConfigurationStoresClientBeginUpdateOptions contains the optional parameters for the ConfigurationStoresClient.BeginUpdate +// method. +func (client *ConfigurationStoresClient) BeginUpdate(ctx context.Context, configurationStoreName string, properties DaprConfigurationStoreResourceUpdate, options *ConfigurationStoresClientBeginUpdateOptions) (*runtime.Poller[ConfigurationStoresClientUpdateResponse], error) { + if options == nil || options.ResumeToken == "" { + resp, err := client.update(ctx, configurationStoreName, properties, options) + if err != nil { + return nil, err + } + poller, err := runtime.NewPoller(resp, client.internal.Pipeline(), &runtime.NewPollerOptions[ConfigurationStoresClientUpdateResponse]{ + FinalStateVia: runtime.FinalStateViaLocation, + }) + return poller, err + } else { + return runtime.NewPollerFromResumeToken[ConfigurationStoresClientUpdateResponse](options.ResumeToken, client.internal.Pipeline(), nil) + } +} + +// Update - Update a DaprConfigurationStoreResource +// If the operation fails it returns an *azcore.ResponseError type. +// +// Generated from API version 2023-10-01-preview +func (client *ConfigurationStoresClient) update(ctx context.Context, configurationStoreName string, properties DaprConfigurationStoreResourceUpdate, options *ConfigurationStoresClientBeginUpdateOptions) (*http.Response, error) { + var err error + req, err := client.updateCreateRequest(ctx, configurationStoreName, properties, options) + if err != nil { + return nil, err + } + httpResp, err := client.internal.Pipeline().Do(req) + if err != nil { + return nil, err + } + if !runtime.HasStatusCode(httpResp, http.StatusOK, http.StatusAccepted) { + err = runtime.NewResponseError(httpResp) + return nil, err + } + return httpResp, nil +} + +// updateCreateRequest creates the Update request. +func (client *ConfigurationStoresClient) updateCreateRequest(ctx context.Context, configurationStoreName string, properties DaprConfigurationStoreResourceUpdate, options *ConfigurationStoresClientBeginUpdateOptions) (*policy.Request, error) { + urlPath := "/{rootScope}/providers/Applications.Dapr/configurationStores/{configurationStoreName}" + urlPath = strings.ReplaceAll(urlPath, "{rootScope}", client.rootScope) + if configurationStoreName == "" { + return nil, errors.New("parameter configurationStoreName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{configurationStoreName}", url.PathEscape(configurationStoreName)) + req, err := runtime.NewRequest(ctx, http.MethodPatch, runtime.JoinPaths(client.internal.Endpoint(), urlPath)) + if err != nil { + return nil, err + } + reqQP := req.Raw().URL.Query() + reqQP.Set("api-version", "2023-10-01-preview") + req.Raw().URL.RawQuery = reqQP.Encode() + req.Raw().Header["Accept"] = []string{"application/json"} + if err := runtime.MarshalAsJSON(req, properties); err != nil { + return nil, err +} + return req, nil +} + diff --git a/pkg/daprrp/api/v20231001preview/zz_generated_models.go b/pkg/daprrp/api/v20231001preview/zz_generated_models.go index 46a78a2cbe..70e58c301a 100644 --- a/pkg/daprrp/api/v20231001preview/zz_generated_models.go +++ b/pkg/daprrp/api/v20231001preview/zz_generated_models.go @@ -9,6 +9,118 @@ package v20231001preview import "time" +// DaprConfigurationStoreProperties - Dapr configuration store portable resource properties +type DaprConfigurationStoreProperties struct { + // REQUIRED; Fully qualified resource ID for the environment that the portable resource is linked to + Environment *string + + // Fully qualified resource ID for the application that the portable resource is consumed by (if applicable) + Application *string + + // The name of the Dapr component to be used as a secret store + Auth *DaprResourceAuth + + // The metadata for Dapr resource which must match the values specified in Dapr component spec + Metadata map[string]*MetadataValue + + // The recipe used to automatically deploy underlying infrastructure for the resource + Recipe *Recipe + + // Specifies how the underlying service/resource is provisioned and managed. + ResourceProvisioning *ResourceProvisioning + + // A collection of references to resources associated with the configuration store + Resources []*ResourceReference + + // Dapr component type which must matches the format used by Dapr Kubernetes configuration format + Type *string + + // Dapr component version + Version *string + + // READ-ONLY; The name of the Dapr component object. Use this value in your code when interacting with the Dapr client to +// use the Dapr component. + ComponentName *string + + // READ-ONLY; The status of the asynchronous operation. + ProvisioningState *ProvisioningState + + // READ-ONLY; Status of a resource. + Status *ResourceStatus +} + +// DaprConfigurationStoreResource - Dapr configuration store portable resource +type DaprConfigurationStoreResource struct { + // REQUIRED; The geo-location where the resource lives + Location *string + + // REQUIRED; The resource-specific properties for this resource. + Properties *DaprConfigurationStoreProperties + + // Resource tags. + Tags map[string]*string + + // READ-ONLY; Fully qualified resource ID for the resource. Ex - /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{resourceType}/{resourceName} + ID *string + + // READ-ONLY; The name of the resource + Name *string + + // READ-ONLY; Azure Resource Manager metadata containing createdBy and modifiedBy information. + SystemData *SystemData + + // READ-ONLY; The type of the resource. E.g. "Microsoft.Compute/virtualMachines" or "Microsoft.Storage/storageAccounts" + Type *string +} + +// DaprConfigurationStoreResourceListResult - The response of a DaprConfigurationStoreResource list operation. +type DaprConfigurationStoreResourceListResult struct { + // REQUIRED; The DaprConfigurationStoreResource items on this page + Value []*DaprConfigurationStoreResource + + // The link to the next page of items + NextLink *string +} + +// DaprConfigurationStoreResourceUpdate - The type used for update operations of the DaprConfigurationStoreResource. +type DaprConfigurationStoreResourceUpdate struct { + // The updatable properties of the DaprConfigurationStoreResource. + Properties *DaprConfigurationStoreResourceUpdateProperties + + // Resource tags. + Tags map[string]*string +} + +// DaprConfigurationStoreResourceUpdateProperties - The updatable properties of the DaprConfigurationStoreResource. +type DaprConfigurationStoreResourceUpdateProperties struct { + // Fully qualified resource ID for the application that the portable resource is consumed by (if applicable) + Application *string + + // The name of the Dapr component to be used as a secret store + Auth *DaprResourceAuth + + // Fully qualified resource ID for the environment that the portable resource is linked to + Environment *string + + // The metadata for Dapr resource which must match the values specified in Dapr component spec + Metadata map[string]*MetadataValueUpdate + + // The recipe used to automatically deploy underlying infrastructure for the resource + Recipe *RecipeUpdate + + // Specifies how the underlying service/resource is provisioned and managed. + ResourceProvisioning *ResourceProvisioning + + // A collection of references to resources associated with the configuration store + Resources []*ResourceReference + + // Dapr component type which must matches the format used by Dapr Kubernetes configuration format + Type *string + + // Dapr component version + Version *string +} + // DaprPubSubBrokerProperties - Dapr PubSubBroker portable resource properties type DaprPubSubBrokerProperties struct { // REQUIRED; Fully qualified resource ID for the environment that the portable resource is linked to diff --git a/pkg/daprrp/api/v20231001preview/zz_generated_models_serde.go b/pkg/daprrp/api/v20231001preview/zz_generated_models_serde.go index 0ac475aaea..e6d48377f3 100644 --- a/pkg/daprrp/api/v20231001preview/zz_generated_models_serde.go +++ b/pkg/daprrp/api/v20231001preview/zz_generated_models_serde.go @@ -14,6 +14,249 @@ import ( "reflect" ) +// MarshalJSON implements the json.Marshaller interface for type DaprConfigurationStoreProperties. +func (d DaprConfigurationStoreProperties) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "application", d.Application) + populate(objectMap, "auth", d.Auth) + populate(objectMap, "componentName", d.ComponentName) + populate(objectMap, "environment", d.Environment) + populate(objectMap, "metadata", d.Metadata) + populate(objectMap, "provisioningState", d.ProvisioningState) + populate(objectMap, "recipe", d.Recipe) + populate(objectMap, "resourceProvisioning", d.ResourceProvisioning) + populate(objectMap, "resources", d.Resources) + populate(objectMap, "status", d.Status) + populate(objectMap, "type", d.Type) + populate(objectMap, "version", d.Version) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type DaprConfigurationStoreProperties. +func (d *DaprConfigurationStoreProperties) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", d, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "application": + err = unpopulate(val, "Application", &d.Application) + delete(rawMsg, key) + case "auth": + err = unpopulate(val, "Auth", &d.Auth) + delete(rawMsg, key) + case "componentName": + err = unpopulate(val, "ComponentName", &d.ComponentName) + delete(rawMsg, key) + case "environment": + err = unpopulate(val, "Environment", &d.Environment) + delete(rawMsg, key) + case "metadata": + err = unpopulate(val, "Metadata", &d.Metadata) + delete(rawMsg, key) + case "provisioningState": + err = unpopulate(val, "ProvisioningState", &d.ProvisioningState) + delete(rawMsg, key) + case "recipe": + err = unpopulate(val, "Recipe", &d.Recipe) + delete(rawMsg, key) + case "resourceProvisioning": + err = unpopulate(val, "ResourceProvisioning", &d.ResourceProvisioning) + delete(rawMsg, key) + case "resources": + err = unpopulate(val, "Resources", &d.Resources) + delete(rawMsg, key) + case "status": + err = unpopulate(val, "Status", &d.Status) + delete(rawMsg, key) + case "type": + err = unpopulate(val, "Type", &d.Type) + delete(rawMsg, key) + case "version": + err = unpopulate(val, "Version", &d.Version) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", d, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type DaprConfigurationStoreResource. +func (d DaprConfigurationStoreResource) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "id", d.ID) + populate(objectMap, "location", d.Location) + populate(objectMap, "name", d.Name) + populate(objectMap, "properties", d.Properties) + populate(objectMap, "systemData", d.SystemData) + populate(objectMap, "tags", d.Tags) + populate(objectMap, "type", d.Type) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type DaprConfigurationStoreResource. +func (d *DaprConfigurationStoreResource) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", d, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "id": + err = unpopulate(val, "ID", &d.ID) + delete(rawMsg, key) + case "location": + err = unpopulate(val, "Location", &d.Location) + delete(rawMsg, key) + case "name": + err = unpopulate(val, "Name", &d.Name) + delete(rawMsg, key) + case "properties": + err = unpopulate(val, "Properties", &d.Properties) + delete(rawMsg, key) + case "systemData": + err = unpopulate(val, "SystemData", &d.SystemData) + delete(rawMsg, key) + case "tags": + err = unpopulate(val, "Tags", &d.Tags) + delete(rawMsg, key) + case "type": + err = unpopulate(val, "Type", &d.Type) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", d, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type DaprConfigurationStoreResourceListResult. +func (d DaprConfigurationStoreResourceListResult) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "nextLink", d.NextLink) + populate(objectMap, "value", d.Value) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type DaprConfigurationStoreResourceListResult. +func (d *DaprConfigurationStoreResourceListResult) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", d, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "nextLink": + err = unpopulate(val, "NextLink", &d.NextLink) + delete(rawMsg, key) + case "value": + err = unpopulate(val, "Value", &d.Value) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", d, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type DaprConfigurationStoreResourceUpdate. +func (d DaprConfigurationStoreResourceUpdate) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "properties", d.Properties) + populate(objectMap, "tags", d.Tags) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type DaprConfigurationStoreResourceUpdate. +func (d *DaprConfigurationStoreResourceUpdate) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", d, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "properties": + err = unpopulate(val, "Properties", &d.Properties) + delete(rawMsg, key) + case "tags": + err = unpopulate(val, "Tags", &d.Tags) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", d, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type DaprConfigurationStoreResourceUpdateProperties. +func (d DaprConfigurationStoreResourceUpdateProperties) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "application", d.Application) + populate(objectMap, "auth", d.Auth) + populate(objectMap, "environment", d.Environment) + populate(objectMap, "metadata", d.Metadata) + populate(objectMap, "recipe", d.Recipe) + populate(objectMap, "resourceProvisioning", d.ResourceProvisioning) + populate(objectMap, "resources", d.Resources) + populate(objectMap, "type", d.Type) + populate(objectMap, "version", d.Version) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type DaprConfigurationStoreResourceUpdateProperties. +func (d *DaprConfigurationStoreResourceUpdateProperties) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", d, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "application": + err = unpopulate(val, "Application", &d.Application) + delete(rawMsg, key) + case "auth": + err = unpopulate(val, "Auth", &d.Auth) + delete(rawMsg, key) + case "environment": + err = unpopulate(val, "Environment", &d.Environment) + delete(rawMsg, key) + case "metadata": + err = unpopulate(val, "Metadata", &d.Metadata) + delete(rawMsg, key) + case "recipe": + err = unpopulate(val, "Recipe", &d.Recipe) + delete(rawMsg, key) + case "resourceProvisioning": + err = unpopulate(val, "ResourceProvisioning", &d.ResourceProvisioning) + delete(rawMsg, key) + case "resources": + err = unpopulate(val, "Resources", &d.Resources) + delete(rawMsg, key) + case "type": + err = unpopulate(val, "Type", &d.Type) + delete(rawMsg, key) + case "version": + err = unpopulate(val, "Version", &d.Version) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", d, err) + } + } + return nil +} + // MarshalJSON implements the json.Marshaller interface for type DaprPubSubBrokerProperties. func (d DaprPubSubBrokerProperties) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) diff --git a/pkg/daprrp/api/v20231001preview/zz_generated_options.go b/pkg/daprrp/api/v20231001preview/zz_generated_options.go index 2f0d7f2e52..e13a012369 100644 --- a/pkg/daprrp/api/v20231001preview/zz_generated_options.go +++ b/pkg/daprrp/api/v20231001preview/zz_generated_options.go @@ -7,6 +7,38 @@ package v20231001preview +// ConfigurationStoresClientBeginCreateOrUpdateOptions contains the optional parameters for the ConfigurationStoresClient.BeginCreateOrUpdate +// method. +type ConfigurationStoresClientBeginCreateOrUpdateOptions struct { + // Resumes the LRO from the provided token. + ResumeToken string +} + +// ConfigurationStoresClientBeginDeleteOptions contains the optional parameters for the ConfigurationStoresClient.BeginDelete +// method. +type ConfigurationStoresClientBeginDeleteOptions struct { + // Resumes the LRO from the provided token. + ResumeToken string +} + +// ConfigurationStoresClientBeginUpdateOptions contains the optional parameters for the ConfigurationStoresClient.BeginUpdate +// method. +type ConfigurationStoresClientBeginUpdateOptions struct { + // Resumes the LRO from the provided token. + ResumeToken string +} + +// ConfigurationStoresClientGetOptions contains the optional parameters for the ConfigurationStoresClient.Get method. +type ConfigurationStoresClientGetOptions struct { + // placeholder for future optional parameters +} + +// ConfigurationStoresClientListByScopeOptions contains the optional parameters for the ConfigurationStoresClient.NewListByScopePager +// method. +type ConfigurationStoresClientListByScopeOptions struct { + // placeholder for future optional parameters +} + // OperationsClientListOptions contains the optional parameters for the OperationsClient.NewListPager method. type OperationsClientListOptions struct { // placeholder for future optional parameters diff --git a/pkg/daprrp/api/v20231001preview/zz_generated_response_types.go b/pkg/daprrp/api/v20231001preview/zz_generated_response_types.go index 24b361f600..7f6a637d06 100644 --- a/pkg/daprrp/api/v20231001preview/zz_generated_response_types.go +++ b/pkg/daprrp/api/v20231001preview/zz_generated_response_types.go @@ -7,6 +7,35 @@ package v20231001preview +// ConfigurationStoresClientCreateOrUpdateResponse contains the response from method ConfigurationStoresClient.BeginCreateOrUpdate. +type ConfigurationStoresClientCreateOrUpdateResponse struct { + // Dapr configuration store portable resource + DaprConfigurationStoreResource +} + +// ConfigurationStoresClientDeleteResponse contains the response from method ConfigurationStoresClient.BeginDelete. +type ConfigurationStoresClientDeleteResponse struct { + // placeholder for future response values +} + +// ConfigurationStoresClientGetResponse contains the response from method ConfigurationStoresClient.Get. +type ConfigurationStoresClientGetResponse struct { + // Dapr configuration store portable resource + DaprConfigurationStoreResource +} + +// ConfigurationStoresClientListByScopeResponse contains the response from method ConfigurationStoresClient.NewListByScopePager. +type ConfigurationStoresClientListByScopeResponse struct { + // The response of a DaprConfigurationStoreResource list operation. + DaprConfigurationStoreResourceListResult +} + +// ConfigurationStoresClientUpdateResponse contains the response from method ConfigurationStoresClient.BeginUpdate. +type ConfigurationStoresClientUpdateResponse struct { + // Dapr configuration store portable resource + DaprConfigurationStoreResource +} + // OperationsClientListResponse contains the response from method OperationsClient.NewListPager. type OperationsClientListResponse struct { // A list of REST API operations supported by an Azure Resource Provider. It contains an URL link to get the next set of results. diff --git a/pkg/daprrp/datamodel/converter/configurationstore_converter.go b/pkg/daprrp/datamodel/converter/configurationstore_converter.go new file mode 100644 index 0000000000..1416aae5f4 --- /dev/null +++ b/pkg/daprrp/datamodel/converter/configurationstore_converter.go @@ -0,0 +1,60 @@ +/* +Copyright 2023 The Radius Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package converter + +import ( + "encoding/json" + + v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/daprrp/api/v20231001preview" + "github.com/radius-project/radius/pkg/daprrp/datamodel" +) + +// ConfigurationStoreDataModelToVersioned converts a version-agnostic datamodel.DaprConfigurationStore to a versioned model based on the version +// string, returning an error if the version is not supported. +func ConfigurationStoreDataModelToVersioned(model *datamodel.DaprConfigurationStore, version string) (v1.VersionedModelInterface, error) { + switch version { + case v20231001preview.Version: + versioned := &v20231001preview.DaprConfigurationStoreResource{} + err := versioned.ConvertFrom(model) + return versioned, err + + default: + return nil, v1.ErrUnsupportedAPIVersion + } +} + +// ConfigurationStoreDataModelFromVersioned unmarshals a JSON byte slice into a versioned ConfigurationStore resource and converts it +// to a version-agnostic datamodel Configuration Store, returning an error if either operation fails. +func ConfigurationStoreDataModelFromVersioned(content []byte, version string) (*datamodel.DaprConfigurationStore, error) { + switch version { + case v20231001preview.Version: + am := &v20231001preview.DaprConfigurationStoreResource{} + if err := json.Unmarshal(content, am); err != nil { + return nil, err + } + dm, err := am.ConvertTo() + if err != nil { + return nil, err + } + + return dm.(*datamodel.DaprConfigurationStore), err + + default: + return nil, v1.ErrUnsupportedAPIVersion + } +} diff --git a/pkg/daprrp/datamodel/converter/configurationstore_converter_test.go b/pkg/daprrp/datamodel/converter/configurationstore_converter_test.go new file mode 100644 index 0000000000..840ec73ca0 --- /dev/null +++ b/pkg/daprrp/datamodel/converter/configurationstore_converter_test.go @@ -0,0 +1,217 @@ +/* +Copyright 2023 The Radius Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package converter + +import ( + "encoding/json" + "testing" + "time" + + v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/daprrp/api/v20231001preview" + "github.com/radius-project/radius/pkg/daprrp/datamodel" + "github.com/radius-project/radius/pkg/to" + "github.com/radius-project/radius/test/testutil" + "github.com/radius-project/radius/test/testutil/resourcetypeutil" + "github.com/stretchr/testify/require" +) + +// Validates type conversion between versioned client side data model and RP data model. +func TestConfigurationStoreDataModelToVersioned(t *testing.T) { + createdAt, err := time.Parse(time.RFC3339Nano, "2021-09-24T19:09:54.2403864Z") + require.NoError(t, err) + + lastModifiedAt, err := time.Parse(time.RFC3339Nano, "2021-09-24T20:09:54.2403864Z") + require.NoError(t, err) + + testset := []struct { + dataModelFile string + apiVersion string + apiModelType any + expected *v20231001preview.DaprConfigurationStoreResource + err error + }{ + { + "../../api/v20231001preview/testdata/configurationstore_manual_datamodel.json", + "2023-10-01-preview", + &v20231001preview.DaprConfigurationStoreResource{}, + &v20231001preview.DaprConfigurationStoreResource{ + Location: to.Ptr("global"), + Properties: &v20231001preview.DaprConfigurationStoreProperties{ + Environment: to.Ptr("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/environments/test-env"), + Application: to.Ptr("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/applications/test-app"), + Metadata: map[string]*v20231001preview.MetadataValue{ + "foo": { + Value: to.Ptr("bar"), + }, + }, + Recipe: nil, + ResourceProvisioning: to.Ptr(v20231001preview.ResourceProvisioningManual), + Resources: []*v20231001preview.ResourceReference{ + { + ID: to.Ptr("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Microsoft.ServiceBus/namespaces/radius-eastus-async"), + }, + }, + Type: to.Ptr("configuration.azure.appconfig"), + Version: to.Ptr("v1"), + ComponentName: to.Ptr("test-dcs"), + ProvisioningState: to.Ptr(v20231001preview.ProvisioningStateAccepted), + Status: resourcetypeutil.MustPopulateResourceStatus(&v20231001preview.ResourceStatus{}), + Auth: &v20231001preview.DaprResourceAuth{ + SecretStore: to.Ptr("test-secret-store"), + }, + }, + Tags: map[string]*string{ + "env": to.Ptr("dev"), + }, + ID: to.Ptr("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Dapr/configurationStores/test-dcs"), + Name: to.Ptr("test-dcs"), + SystemData: &v20231001preview.SystemData{ + CreatedAt: &createdAt, + CreatedBy: to.Ptr("fakeid@live.com"), + CreatedByType: to.Ptr(v20231001preview.CreatedByTypeUser), + LastModifiedAt: &lastModifiedAt, + LastModifiedBy: to.Ptr("fakeid@live.com"), + LastModifiedByType: to.Ptr(v20231001preview.CreatedByTypeUser), + }, + Type: to.Ptr("Applications.Dapr/configurationStores"), + }, + nil, + }, + { + "../../api/v20231001preview/testdata/configurationstore_manual_generic_datamodel.json", + "2023-10-01-preview", + &v20231001preview.DaprConfigurationStoreResource{}, + &v20231001preview.DaprConfigurationStoreResource{ + Location: to.Ptr("global"), + Properties: &v20231001preview.DaprConfigurationStoreProperties{ + Environment: to.Ptr("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/environments/test-env"), + Application: to.Ptr("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/applications/test-app"), + Metadata: map[string]*v20231001preview.MetadataValue{ + "foo": { + Value: to.Ptr("bar"), + }, + }, + Recipe: nil, + ResourceProvisioning: to.Ptr(v20231001preview.ResourceProvisioningManual), + Resources: nil, + Type: to.Ptr("configuration.redis"), + Version: to.Ptr("v1"), + ComponentName: to.Ptr("test-dcs"), + ProvisioningState: to.Ptr(v20231001preview.ProvisioningStateAccepted), + Status: resourcetypeutil.MustPopulateResourceStatus(&v20231001preview.ResourceStatus{}), + Auth: &v20231001preview.DaprResourceAuth{ + SecretStore: to.Ptr("test-secret-store"), + }, + }, + Tags: map[string]*string{ + "env": to.Ptr("dev"), + }, + ID: to.Ptr("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Dapr/configurationStores/test-dcs"), + Name: to.Ptr("test-dcs"), + SystemData: &v20231001preview.SystemData{ + CreatedAt: &createdAt, + CreatedBy: to.Ptr("fakeid@live.com"), + CreatedByType: to.Ptr(v20231001preview.CreatedByTypeUser), + LastModifiedAt: &lastModifiedAt, + LastModifiedBy: to.Ptr("fakeid@live.com"), + LastModifiedByType: to.Ptr(v20231001preview.CreatedByTypeUser), + }, + Type: to.Ptr("Applications.Dapr/configurationStores"), + }, + nil, + }, + { + "../../api/v20231001preview/testdata/configurationstore_manual_generic_datamodel.json", + "unsupported", + nil, + nil, + v1.ErrUnsupportedAPIVersion, + }, + } + + for _, tc := range testset { + t.Run(tc.apiVersion, func(t *testing.T) { + c := testutil.ReadFixture("../" + tc.dataModelFile) + dm := &datamodel.DaprConfigurationStore{} + err = json.Unmarshal(c, dm) + require.NoError(t, err) + + am, err := ConfigurationStoreDataModelToVersioned(dm, tc.apiVersion) + if tc.err != nil { + require.ErrorAs(t, tc.err, &err) + } else { + require.NoError(t, err) + require.IsType(t, tc.apiModelType, am) + require.Equal(t, tc.expected, am) + } + }) + } +} + +func TestDaprConfigurationStoreDataModelFromVersioned(t *testing.T) { + testset := []struct { + versionedModelFile string + apiVersion string + err error + }{ + { + "../../api/v20231001preview/testdata/configurationstore_invalidrecipe_resource.json", + "2023-10-01-preview", + &v1.ErrClientRP{ + Code: v1.CodeInvalid, + Message: "error(s) found:\n\tmetadata cannot be specified when resourceProvisioning is set to recipe (default)\n\ttype cannot be specified when resourceProvisioning is set to recipe (default)\n\tversion cannot be specified when resourceProvisioning is set to recipe (default)", + }, + }, + { + "../../api/v20231001preview/testdata/configurationstore_invalidmanual_resource.json", + "2023-10-01-preview", + &v1.ErrClientRP{ + Code: "BadRequest", + Message: "error(s) found:\n\trecipe details cannot be specified when resourceProvisioning is set to manual\n\tmetadata must be specified when resourceProvisioning is set to manual\n\ttype must be specified when resourceProvisioning is set to manual\n\tversion must be specified when resourceProvisioning is set to manual", + }, + }, + { + "../../api/v20231001preview/testdata/configurationstore_recipe_resource.json", + "2023-10-01-preview", + nil, + }, + { + "../../api/v20231001preview/testdata/configurationstore_manual_resource.json", + "2023-10-01-preview", + nil, + }, + { + "../../api/v20231001preview/testdata/configurationstore_manual_resource.json", + "unsupported", + v1.ErrUnsupportedAPIVersion, + }, + } + + for _, tc := range testset { + t.Run(tc.apiVersion, func(t *testing.T) { + c := testutil.ReadFixture("../" + tc.versionedModelFile) + dm, err := ConfigurationStoreDataModelFromVersioned(c, tc.apiVersion) + if tc.err != nil { + require.Equal(t, tc.err, err) + } else { + require.NoError(t, err) + require.IsType(t, tc.apiVersion, dm.InternalMetadata.UpdatedAPIVersion) + } + }) + } +} diff --git a/pkg/daprrp/datamodel/daprconfigurationstore.go b/pkg/daprrp/datamodel/daprconfigurationstore.go new file mode 100644 index 0000000000..ff9bcf4042 --- /dev/null +++ b/pkg/daprrp/datamodel/daprconfigurationstore.go @@ -0,0 +1,91 @@ +/* +Copyright 2023 The Radius Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package datamodel + +import ( + v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/daprrp/frontend/controller" + "github.com/radius-project/radius/pkg/portableresources" + "github.com/radius-project/radius/pkg/portableresources/datamodel" + rpv1 "github.com/radius-project/radius/pkg/rp/v1" +) + +// DaprConfigurationStore represents Dapr configuration store portable resource. +type DaprConfigurationStore struct { + v1.BaseResource + + // Properties is the properties of the resource. + Properties DaprConfigurationStoreProperties `json:"properties"` + + // ResourceMetadata represents internal DataModel properties common to all portable resource types. + datamodel.PortableResourceMetadata +} + +// ApplyDeploymentOutput updates the DaprConfigurationStore resource with the DeploymentOutput values. +func (r *DaprConfigurationStore) ApplyDeploymentOutput(do rpv1.DeploymentOutput) error { + return nil +} + +// OutputResources returns the OutputResources from the Properties of the DaprConfigurationStore instance. +func (r *DaprConfigurationStore) OutputResources() []rpv1.OutputResource { + return r.Properties.Status.OutputResources +} + +// ResourceMetadata returns the BasicResourceProperties of the Dapr ConfigurationStore resource i.e. application resources metadata. +func (r *DaprConfigurationStore) ResourceMetadata() *rpv1.BasicResourceProperties { + return &r.Properties.BasicResourceProperties +} + +// ResourceTypeName returns a string representing the resource type. +func (r *DaprConfigurationStore) ResourceTypeName() string { + return controller.DaprConfigurationStoresResourceType +} + +// Recipe returns the recipe information of the resource. Returns nil if recipe execution is disabled. +func (r *DaprConfigurationStore) Recipe() *portableresources.ResourceRecipe { + if r.Properties.ResourceProvisioning == portableresources.ResourceProvisioningManual { + return nil + } + return &r.Properties.Recipe +} + +// DaprConfigurationStoreProperties represents the properties of Dapr ConfigurationStore resource. +type DaprConfigurationStoreProperties struct { + rpv1.BasicResourceProperties + rpv1.BasicDaprResourceProperties + + // ResourceProvisioning specifies how the underlying service/resource is provisioned and managed + ResourceProvisioning portableresources.ResourceProvisioning `json:"resourceProvisioning,omitempty"` + + // Metadata of the Dapr Configuration store resource. + Metadata map[string]*rpv1.DaprComponentMetadataValue `json:"metadata,omitempty"` + + // The recipe used to automatically deploy underlying infrastructure for the Dapr Configuration store resource. + Recipe portableresources.ResourceRecipe `json:"recipe,omitempty"` + + // List of the resource IDs that support the Dapr Configuration store Broker resource. + Resources []*portableresources.ResourceReference `json:"resources,omitempty"` + + // Type of the Dapr Configuration Store resource. + Type string `json:"type,omitempty"` + + // Version of the Dapr Configuration Store resource. + Version string `json:"version,omitempty"` + + // Auth information for the Dapr Configuration Store resource, mainly secret store name. + Auth *rpv1.DaprComponentAuth `json:"auth,omitempty"` +} diff --git a/pkg/daprrp/frontend/controller/types.go b/pkg/daprrp/frontend/controller/types.go index 953c8bd382..481d84f2fc 100644 --- a/pkg/daprrp/frontend/controller/types.go +++ b/pkg/daprrp/frontend/controller/types.go @@ -41,4 +41,11 @@ const ( AsyncCreateOrUpdateDaprPubSubBrokerTimeout = time.Duration(60) * time.Minute // AsyncDeleteDaprPubSubBrokerTimeout is the timeout for async delete dapr pub sub broker AsyncDeleteDaprPubSubBrokerTimeout = time.Duration(60) * time.Minute + + // DaprConfigurationStoresResourceType represents the resource type for Dapr configuration store. + DaprConfigurationStoresResourceType = "Applications.Dapr/configurationStores" + // AsyncCreateOrUpdateDaprConfigurationStoreTimeout is the timeout for async create or update Dapr Configuration Store + AsyncCreateOrUpdateDaprConfigurationStoreTimeout = time.Duration(60) * time.Minute + // AsyncDeleteDaprConfigurationStoreTimeout is the timeout for async delete dapr configuration store + AsyncDeleteDaprConfigurationStoreTimeout = time.Duration(60) * time.Minute ) diff --git a/pkg/daprrp/processors/configurationstores/doc.go b/pkg/daprrp/processors/configurationstores/doc.go new file mode 100644 index 0000000000..798b7763d7 --- /dev/null +++ b/pkg/daprrp/processors/configurationstores/doc.go @@ -0,0 +1,18 @@ +/* +Copyright 2023 The Radius Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// configurationstores contains the resource processor for Dapr Configuration Stores. See the processors package for more information. +package configurationstores diff --git a/pkg/daprrp/processors/configurationstores/processor.go b/pkg/daprrp/processors/configurationstores/processor.go new file mode 100644 index 0000000000..16685cd9cc --- /dev/null +++ b/pkg/daprrp/processors/configurationstores/processor.go @@ -0,0 +1,156 @@ +/* +Copyright 2023 The Radius Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package configurationstores + +import ( + "context" + + "github.com/radius-project/radius/pkg/daprrp/datamodel" + "github.com/radius-project/radius/pkg/daprrp/frontend/controller" + "github.com/radius-project/radius/pkg/kubernetes" + "github.com/radius-project/radius/pkg/kubeutil" + "github.com/radius-project/radius/pkg/portableresources" + "github.com/radius-project/radius/pkg/portableresources/handlers" + "github.com/radius-project/radius/pkg/portableresources/processors" + "github.com/radius-project/radius/pkg/portableresources/renderers/dapr" + rpv1 "github.com/radius-project/radius/pkg/rp/v1" + "github.com/radius-project/radius/pkg/to" + "github.com/radius-project/radius/pkg/ucp/resources" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + runtime "sigs.k8s.io/controller-runtime/pkg/client" +) + +type Processor struct { + Client runtime.Client +} + +// Process validates resource properties, and applies output values from the recipe output. If the resource is +// being provisioned manually, it creates a Dapr component in Kubernetes. +func (p *Processor) Process(ctx context.Context, resource *datamodel.DaprConfigurationStore, options processors.Options) error { + validator := processors.NewValidator(&resource.ComputedValues, &resource.SecretValues, &resource.Properties.Status.OutputResources, resource.Properties.Status.Recipe) + validator.AddResourcesField(&resource.Properties.Resources) + validator.AddComputedStringField("componentName", &resource.Properties.ComponentName, func() (string, *processors.ValidationError) { + return kubernetes.NormalizeDaprResourceName(resource.Name), nil + }) + + err := validator.SetAndValidate(options.RecipeOutput) + if err != nil { + return err + } + + if resource.Properties.ResourceProvisioning != portableresources.ResourceProvisioningManual { + // If the resource is being provisioned by recipe then we expect the recipe to create the Dapr Component + // in Kubernetes. At this point we're done so we can just return. + return nil + } + + // If the resource is being provisioned manually then *we* are responsible for creating the Dapr Component. + // Let's do this now. + + // DaprConfigurationStore resources may or may not be application scoped. + // Some Dapr Components can be specific to a single application, they would be application scoped and have + // resource.Properties.Application populated, while others could be shared across multiple applications and + // would not have resource.Properties.Application populated. + var applicationID resources.ID + if resource.Properties.Application != "" { + applicationID, err = resources.ParseResource(resource.Properties.Application) + if err != nil { + return err // This should already be validated by this point. + } + } + + component, err := dapr.ConstructDaprGeneric( + dapr.DaprGeneric{ + Auth: resource.Properties.Auth, + Metadata: resource.Properties.Metadata, + Type: to.Ptr(resource.Properties.Type), + Version: to.Ptr(resource.Properties.Version), + }, + options.RuntimeConfiguration.Kubernetes.Namespace, + resource.Properties.ComponentName, + applicationID.Name(), + resource.Name, + controller.DaprConfigurationStoresResourceType) + if err != nil { + return err + } + + err = kubeutil.PatchNamespace(ctx, p.Client, component.GetNamespace()) + if err != nil { + return &processors.ResourceError{Inner: err} + } + + err = handlers.CheckDaprResourceNameUniqueness(ctx, p.Client, resource.Properties.ComponentName, options.RuntimeConfiguration.Kubernetes.Namespace, resource.Name, controller.DaprConfigurationStoresResourceType) + if err != nil { + return &processors.ValidationError{Message: err.Error()} + } + + err = p.Client.Patch(ctx, &component, runtime.Apply, &runtime.PatchOptions{FieldManager: kubernetes.FieldManager}) + if err != nil { + return &processors.ResourceError{Inner: err} + } + + deployed := rpv1.NewKubernetesOutputResource("Component", &component, metav1.ObjectMeta{Name: component.GetName(), Namespace: component.GetNamespace()}) + deployed.RadiusManaged = to.Ptr(true) + resource.Properties.Status.OutputResources = append(resource.Properties.Status.OutputResources, deployed) + + return nil +} + +// Delete implements the processors.Processor interface for DaprConfigurationStore resources. If the resource is being +// provisioned manually, it deletes the Dapr component in Kubernetes. +func (p *Processor) Delete(ctx context.Context, resource *datamodel.DaprConfigurationStore, options processors.Options) error { + if resource.Properties.ResourceProvisioning != portableresources.ResourceProvisioningManual { + // If the resource was provisioned by recipe then we expect the recipe engine to delete the Dapr Component + // in Kubernetes. At this point we're done so we can just return. + return nil + } + + // DaprConfigurationStore resources may or may not be application scoped. + // Some Dapr Components can be specific to a single application, they would be application scoped and have + // resource.Properties.Application populated, while others could be shared across multiple applications and + // would not have resource.Properties.Application populated. + var err error + var applicationID resources.ID + if resource.Properties.Application != "" { + applicationID, err = resources.ParseResource(resource.Properties.Application) + if err != nil { + return err + } + } + + component := unstructured.Unstructured{ + Object: map[string]any{ + "apiVersion": dapr.DaprAPIVersion, + "kind": dapr.DaprKind, + "metadata": map[string]any{ + "namespace": options.RuntimeConfiguration.Kubernetes.Namespace, + "name": kubernetes.NormalizeDaprResourceName(resource.Properties.ComponentName), + "labels": kubernetes.MakeDescriptiveDaprLabels(applicationID.Name(), resource.Name, controller.DaprConfigurationStoresResourceType), + }, + }, + } + + err = p.Client.Delete(ctx, &component) + if err != nil { + return &processors.ResourceError{Inner: err} + } + + return nil +} diff --git a/pkg/daprrp/processors/configurationstores/processor_test.go b/pkg/daprrp/processors/configurationstores/processor_test.go new file mode 100644 index 0000000000..87ee8bd8f2 --- /dev/null +++ b/pkg/daprrp/processors/configurationstores/processor_test.go @@ -0,0 +1,507 @@ +/* +Copyright 2023 The Radius Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package configurationstores + +import ( + "context" + "fmt" + "testing" + + v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/daprrp/datamodel" + dapr_ctrl "github.com/radius-project/radius/pkg/daprrp/frontend/controller" + "github.com/radius-project/radius/pkg/kubernetes" + "github.com/radius-project/radius/pkg/portableresources" + "github.com/radius-project/radius/pkg/portableresources/processors" + "github.com/radius-project/radius/pkg/portableresources/renderers/dapr" + "github.com/radius-project/radius/pkg/recipes" + rpv1 "github.com/radius-project/radius/pkg/rp/v1" + "github.com/radius-project/radius/pkg/to" + "github.com/radius-project/radius/test/k8sutil" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/kubectl/pkg/scheme" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func Test_Process(t *testing.T) { + const externalResourceID1 = "/subscriptions/0000/resourceGroups/test-group/providers/Microsoft.Cache/redis/myredis1" + const externalResourceID2 = "/subscriptions/0000/resourceGroups/test-group/providers/Microsoft.Cache/redis/myredis2" + const kubernetesResource = "/planes/kubernetes/local/namespaces/test-namespace/providers/dapr.io/Component/test-component" + const appID = "/planes/radius/local/resourceGroups/test-rg/providers/Applications.Core/applications/test-app" + const envID = "/planes/radius/local/resourceGroups/test-rg/providers/Applications.Core/environments/test-env" + const componentName = "test-dapr-configuration-store" + const secretStoreComponentName = "test-dapr-secret-store" + + t.Run("success - recipe", func(t *testing.T) { + processor := Processor{ + Client: k8sutil.NewFakeKubeClient(scheme.Scheme), + } + + resource := &datamodel.DaprConfigurationStore{ + BaseResource: v1.BaseResource{ + TrackedResource: v1.TrackedResource{ + Name: componentName, + }, + }, + Properties: datamodel.DaprConfigurationStoreProperties{ + BasicResourceProperties: rpv1.BasicResourceProperties{ + Application: appID, + }, + BasicDaprResourceProperties: rpv1.BasicDaprResourceProperties{ + ComponentName: componentName, + }, + }, + } + + options := processors.Options{ + RuntimeConfiguration: recipes.RuntimeConfiguration{ + Kubernetes: &recipes.KubernetesRuntime{ + Namespace: "test-namespace", + }, + }, + RecipeOutput: &recipes.RecipeOutput{ + Resources: []string{ + externalResourceID1, + kubernetesResource, + }, + Values: map[string]any{}, // Component name will be computed for resource name. + Secrets: map[string]any{}, + }, + } + + err := processor.Process(context.Background(), resource, options) + require.NoError(t, err) + + require.Equal(t, componentName, resource.Properties.ComponentName) + + expectedValues := map[string]any{ + "componentName": componentName, + } + expectedSecrets := map[string]rpv1.SecretValueReference{} + + expectedOutputResources, err := processors.GetOutputResourcesFromRecipe(options.RecipeOutput) + require.NoError(t, err) + + require.Equal(t, expectedValues, resource.ComputedValues) + require.Equal(t, expectedSecrets, resource.SecretValues) + require.Equal(t, expectedOutputResources, resource.Properties.Status.OutputResources) + + components := unstructured.UnstructuredList{} + components.SetAPIVersion("dapr.io/v1alpha1") + components.SetKind("Component") + + // No components created for a recipe + err = processor.Client.List(context.Background(), &components, + &client.ListOptions{ + Namespace: options.RuntimeConfiguration.Kubernetes.Namespace, + }, + ) + require.NoError(t, err) + require.Empty(t, components.Items) + }) + + t.Run("success - manual", func(t *testing.T) { + testset := []struct { + description string + properties *datamodel.DaprConfigurationStoreProperties + generated *unstructured.Unstructured + }{ + { + description: "Raw values", + properties: &datamodel.DaprConfigurationStoreProperties{ + BasicResourceProperties: rpv1.BasicResourceProperties{ + Application: appID, + Environment: envID, + }, + BasicDaprResourceProperties: rpv1.BasicDaprResourceProperties{ + ComponentName: componentName, + }, + ResourceProvisioning: portableresources.ResourceProvisioningManual, + Metadata: map[string]*rpv1.DaprComponentMetadataValue{ + "config": { + Value: "extrasecure", + }, + }, + Resources: []*portableresources.ResourceReference{{ID: externalResourceID1}}, + Type: "configuration.redis", + Version: "v1", + }, + generated: &unstructured.Unstructured{ + Object: map[string]any{ + "apiVersion": dapr.DaprAPIVersion, + "kind": dapr.DaprKind, + "metadata": map[string]any{ + "namespace": "test-namespace", + "name": componentName, + "labels": kubernetes.MakeDescriptiveDaprLabels("test-app", "some-other-name", dapr_ctrl.DaprConfigurationStoresResourceType), + "resourceVersion": "1", + }, + "spec": map[string]any{ + "type": "configuration.redis", + "version": "v1", + "metadata": []any{ + map[string]any{ + "name": "config", + "value": "extrasecure", + }, + }, + }, + }, + }, + }, + { + description: "With secret store", + properties: &datamodel.DaprConfigurationStoreProperties{ + BasicResourceProperties: rpv1.BasicResourceProperties{ + Application: appID, + Environment: envID, + }, + BasicDaprResourceProperties: rpv1.BasicDaprResourceProperties{ + ComponentName: componentName, + }, + ResourceProvisioning: portableresources.ResourceProvisioningManual, + Metadata: map[string]*rpv1.DaprComponentMetadataValue{ + "config": { + Value: "extrasecure", + }, + "connectionString": { + SecretKeyRef: &rpv1.DaprComponentSecretRef{ + Name: "secretStoreName", + Key: "secretStoreKey", + }, + }, + }, + Resources: []*portableresources.ResourceReference{{ID: externalResourceID1}}, + Type: "configuration.redis", + Version: "v1", + Auth: &rpv1.DaprComponentAuth{ + SecretStore: secretStoreComponentName, + }, + }, + generated: &unstructured.Unstructured{ + Object: map[string]any{ + "apiVersion": dapr.DaprAPIVersion, + "kind": dapr.DaprKind, + "metadata": map[string]any{ + "namespace": "test-namespace", + "name": componentName, + "labels": kubernetes.MakeDescriptiveDaprLabels("test-app", "some-other-name", dapr_ctrl.DaprConfigurationStoresResourceType), + "resourceVersion": "1", + }, + "spec": map[string]any{ + "type": "configuration.redis", + "version": "v1", + "metadata": []any{ + map[string]any{ + "name": "config", + "value": "extrasecure", + }, + map[string]any{ + "name": "connectionString", + "secretKeyRef": map[string]any{ + "name": "secretStoreName", + "key": "secretStoreKey", + }, + }, + }, + }, + "auth": map[string]any{ + "secretStore": secretStoreComponentName, + }, + }, + }, + }, + } + + for _, tc := range testset { + t.Run(tc.description, func(t *testing.T) { + processor := Processor{ + Client: k8sutil.NewFakeKubeClient(scheme.Scheme, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "test-namespace"}}), + } + resource := &datamodel.DaprConfigurationStore{ + BaseResource: v1.BaseResource{ + TrackedResource: v1.TrackedResource{ + Name: "some-other-name", + }, + }, + Properties: *tc.properties, + } + options := processors.Options{ + RuntimeConfiguration: recipes.RuntimeConfiguration{ + Kubernetes: &recipes.KubernetesRuntime{ + Namespace: "test-namespace", + }, + }, + } + err := processor.Process(context.Background(), resource, options) + require.NoError(t, err) + + require.Equal(t, componentName, resource.Properties.ComponentName) + + expectedValues := map[string]any{ + "componentName": componentName, + } + expectedSecrets := map[string]rpv1.SecretValueReference{} + + expectedOutputResources, err := processors.GetOutputResourcesFromResourcesField(resource.Properties.Resources) + component := rpv1.NewKubernetesOutputResource("Component", tc.generated, metav1.ObjectMeta{Name: tc.generated.GetName(), Namespace: tc.generated.GetNamespace()}) + component.RadiusManaged = to.Ptr(true) + expectedOutputResources = append(expectedOutputResources, component) + require.NoError(t, err) + + require.Equal(t, expectedValues, resource.ComputedValues) + require.Equal(t, expectedSecrets, resource.SecretValues) + require.Equal(t, expectedOutputResources, resource.Properties.Status.OutputResources) + + components := unstructured.UnstructuredList{} + components.SetAPIVersion("dapr.io/v1alpha1") + components.SetKind("Component") + err = processor.Client.List(context.Background(), &components, &client.ListOptions{Namespace: options.RuntimeConfiguration.Kubernetes.Namespace}) + require.NoError(t, err) + require.NotEmpty(t, components.Items) + require.Equal(t, []unstructured.Unstructured{*tc.generated}, components.Items) + + }) + } + }) + + t.Run("success - manual (no application)", func(t *testing.T) { + processor := Processor{ + Client: k8sutil.NewFakeKubeClient(scheme.Scheme, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "test-namespace"}}), + } + + resource := &datamodel.DaprConfigurationStore{ + BaseResource: v1.BaseResource{ + TrackedResource: v1.TrackedResource{ + Name: "some-other-name", + }, + }, + Properties: datamodel.DaprConfigurationStoreProperties{ + BasicResourceProperties: rpv1.BasicResourceProperties{ + Environment: envID, + }, + BasicDaprResourceProperties: rpv1.BasicDaprResourceProperties{ + ComponentName: componentName, + }, + ResourceProvisioning: portableresources.ResourceProvisioningManual, + Metadata: map[string]*rpv1.DaprComponentMetadataValue{ + "config": { + Value: "extrasecure", + }, + }, + Resources: []*portableresources.ResourceReference{{ID: externalResourceID1}}, + Type: "configuration.redis", + Version: "v1", + }, + } + + options := processors.Options{ + RuntimeConfiguration: recipes.RuntimeConfiguration{ + Kubernetes: &recipes.KubernetesRuntime{ + Namespace: "test-namespace", + }, + }, + } + + err := processor.Process(context.Background(), resource, options) + require.NoError(t, err) + + require.Equal(t, componentName, resource.Properties.ComponentName) + + expectedValues := map[string]any{ + "componentName": componentName, + } + expectedSecrets := map[string]rpv1.SecretValueReference{} + + expectedOutputResources, err := processors.GetOutputResourcesFromResourcesField(resource.Properties.Resources) + + generated := &unstructured.Unstructured{ + Object: map[string]any{ + "apiVersion": dapr.DaprAPIVersion, + "kind": dapr.DaprKind, + "metadata": map[string]any{ + "namespace": "test-namespace", + "name": componentName, + "labels": kubernetes.MakeDescriptiveDaprLabels("", "some-other-name", dapr_ctrl.DaprConfigurationStoresResourceType), + "resourceVersion": "1", + }, + "spec": map[string]any{ + "type": "configuration.redis", + "version": "v1", + "metadata": []any{ + map[string]any{ + "name": "config", + "value": "extrasecure", + }, + }, + }, + }, + } + + component := rpv1.NewKubernetesOutputResource("Component", generated, metav1.ObjectMeta{Name: generated.GetName(), Namespace: generated.GetNamespace()}) + component.RadiusManaged = to.Ptr(true) + expectedOutputResources = append(expectedOutputResources, component) + require.NoError(t, err) + + require.Equal(t, expectedValues, resource.ComputedValues) + require.Equal(t, expectedSecrets, resource.SecretValues) + require.Equal(t, expectedOutputResources, resource.Properties.Status.OutputResources) + + components := unstructured.UnstructuredList{} + components.SetAPIVersion("dapr.io/v1alpha1") + components.SetKind("Component") + err = processor.Client.List(context.Background(), &components, &client.ListOptions{Namespace: options.RuntimeConfiguration.Kubernetes.Namespace}) + require.NoError(t, err) + require.NotEmpty(t, components.Items) + require.Equal(t, []unstructured.Unstructured{*generated}, components.Items) + }) + + t.Run("success - recipe with overrides", func(t *testing.T) { + processor := Processor{ + Client: k8sutil.NewFakeKubeClient(scheme.Scheme), + } + + resource := &datamodel.DaprConfigurationStore{ + BaseResource: v1.BaseResource{ + TrackedResource: v1.TrackedResource{ + Name: "some-other-name", + }, + }, + Properties: datamodel.DaprConfigurationStoreProperties{ + BasicDaprResourceProperties: rpv1.BasicDaprResourceProperties{ + ComponentName: componentName, + }, + }, + } + + options := processors.Options{ + RuntimeConfiguration: recipes.RuntimeConfiguration{ + Kubernetes: &recipes.KubernetesRuntime{ + Namespace: "test-namespace", + }, + }, + RecipeOutput: &recipes.RecipeOutput{ + Resources: []string{ + externalResourceID2, + kubernetesResource, + }, + + // Values and secrets will be overridden by the resource. + Values: map[string]any{ + "componentName": "akskdf", + }, + Secrets: map[string]any{}, + }, + } + + err := processor.Process(context.Background(), resource, options) + require.NoError(t, err) + + require.Equal(t, componentName, resource.Properties.ComponentName) + + expectedValues := map[string]any{ + "componentName": componentName, + } + expectedSecrets := map[string]rpv1.SecretValueReference{} + expectedOutputResources := []rpv1.OutputResource{} + + recipeOutputResources, err := processors.GetOutputResourcesFromRecipe(options.RecipeOutput) + require.NoError(t, err) + expectedOutputResources = append(expectedOutputResources, recipeOutputResources...) + + resourcesFieldOutputResources, err := processors.GetOutputResourcesFromResourcesField(resource.Properties.Resources) + require.NoError(t, err) + expectedOutputResources = append(expectedOutputResources, resourcesFieldOutputResources...) + + require.Equal(t, expectedValues, resource.ComputedValues) + require.Equal(t, expectedSecrets, resource.SecretValues) + require.Equal(t, expectedOutputResources, resource.Properties.Status.OutputResources) + + components := unstructured.UnstructuredList{} + components.SetAPIVersion("dapr.io/v1alpha1") + components.SetKind("Component") + err = processor.Client.List(context.Background(), &components, + &client.ListOptions{ + Namespace: options.RuntimeConfiguration.Kubernetes.Namespace, + }, + ) + require.NoError(t, err) + require.Empty(t, components.Items) + }) + + t.Run("failure - duplicate component", func(t *testing.T) { + // Create a duplicate with the same component name. + existing, err := dapr.ConstructDaprGeneric( + dapr.DaprGeneric{ + Type: to.Ptr("configuration.redis"), + Version: to.Ptr("v1"), + Metadata: map[string]*rpv1.DaprComponentMetadataValue{}, + }, + "test-namespace", + componentName, + "test-app", + "some-other-other-name", + dapr_ctrl.DaprConfigurationStoresResourceType) + require.NoError(t, err) + + processor := Processor{ + Client: k8sutil.NewFakeKubeClient(scheme.Scheme, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "test-namespace"}}, &existing), + } + + resource := &datamodel.DaprConfigurationStore{ + BaseResource: v1.BaseResource{ + TrackedResource: v1.TrackedResource{ + Name: "some-other-name", + }, + }, + Properties: datamodel.DaprConfigurationStoreProperties{ + BasicResourceProperties: rpv1.BasicResourceProperties{ + Application: appID, + }, + BasicDaprResourceProperties: rpv1.BasicDaprResourceProperties{ + ComponentName: componentName, + }, + ResourceProvisioning: portableresources.ResourceProvisioningManual, + Metadata: map[string]*rpv1.DaprComponentMetadataValue{ + "config": { + Value: "extrasecure", + }, + }, + Resources: []*portableresources.ResourceReference{{ID: externalResourceID1}}, + Type: "configuration.redis", + Version: "v1", + }, + } + + options := processors.Options{ + RuntimeConfiguration: recipes.RuntimeConfiguration{ + Kubernetes: &recipes.KubernetesRuntime{ + Namespace: "test-namespace", + }, + }, + } + + err = processor.Process(context.Background(), resource, options) + require.Error(t, err) + assert.IsType(t, &processors.ValidationError{}, err) + assert.Equal(t, fmt.Sprintf("the Dapr component name '\"%s\"' is already in use by another resource. Dapr component and resource names must be unique across all Dapr types (e.g., StateStores, PubSubBrokers, SecretStores, ConfigurationStores, etc.). Please select a new name and try again.", componentName), err.Error()) + }) +} diff --git a/pkg/daprrp/processors/pubsubbrokers/processor_test.go b/pkg/daprrp/processors/pubsubbrokers/processor_test.go index 909514a752..e9286c7005 100644 --- a/pkg/daprrp/processors/pubsubbrokers/processor_test.go +++ b/pkg/daprrp/processors/pubsubbrokers/processor_test.go @@ -500,6 +500,6 @@ func Test_Process(t *testing.T) { err = processor.Process(context.Background(), resource, options) require.Error(t, err) assert.IsType(t, &processors.ValidationError{}, err) - assert.Equal(t, "the Dapr component name '\"test-dapr-pubsub-broker\"' is already in use by another resource. Dapr component and resource names must be unique across all Dapr types (eg: StateStores, PubSubBrokers, SecretStores, etc.). Please select a new name and try again", err.Error()) + assert.Equal(t, "the Dapr component name '\"test-dapr-pubsub-broker\"' is already in use by another resource. Dapr component and resource names must be unique across all Dapr types (e.g., StateStores, PubSubBrokers, SecretStores, ConfigurationStores, etc.). Please select a new name and try again.", err.Error()) }) } diff --git a/pkg/daprrp/processors/secretstores/processor_test.go b/pkg/daprrp/processors/secretstores/processor_test.go index d65842fa1a..5170130fea 100644 --- a/pkg/daprrp/processors/secretstores/processor_test.go +++ b/pkg/daprrp/processors/secretstores/processor_test.go @@ -398,6 +398,6 @@ func Test_Process(t *testing.T) { err = processor.Process(context.Background(), resource, options) require.Error(t, err) assert.IsType(t, &processors.ValidationError{}, err) - assert.Equal(t, "the Dapr component name '\"test-component\"' is already in use by another resource. Dapr component and resource names must be unique across all Dapr types (eg: StateStores, PubSubBrokers, SecretStores, etc.). Please select a new name and try again", err.Error()) + assert.Equal(t, "the Dapr component name '\"test-component\"' is already in use by another resource. Dapr component and resource names must be unique across all Dapr types (e.g., StateStores, PubSubBrokers, SecretStores, ConfigurationStores, etc.). Please select a new name and try again.", err.Error()) }) } diff --git a/pkg/daprrp/processors/statestores/processor_test.go b/pkg/daprrp/processors/statestores/processor_test.go index 2e347c3ff2..884330cf2a 100644 --- a/pkg/daprrp/processors/statestores/processor_test.go +++ b/pkg/daprrp/processors/statestores/processor_test.go @@ -490,6 +490,6 @@ func Test_Process(t *testing.T) { err = processor.Process(context.Background(), resource, options) require.Error(t, err) assert.IsType(t, &processors.ValidationError{}, err) - assert.Equal(t, "the Dapr component name '\"test-component\"' is already in use by another resource. Dapr component and resource names must be unique across all Dapr types (eg: StateStores, PubSubBrokers, SecretStores, etc.). Please select a new name and try again", err.Error()) + assert.Equal(t, "the Dapr component name '\"test-component\"' is already in use by another resource. Dapr component and resource names must be unique across all Dapr types (e.g., StateStores, PubSubBrokers, SecretStores, ConfigurationStores, etc.). Please select a new name and try again.", err.Error()) }) } diff --git a/pkg/daprrp/setup/operations.go b/pkg/daprrp/setup/operations.go index 21aaa69509..8560005d3e 100644 --- a/pkg/daprrp/setup/operations.go +++ b/pkg/daprrp/setup/operations.go @@ -139,4 +139,34 @@ var operationList = []v1.Operation{ }, IsDataAction: false, }, + { + Name: "Applications.Dapr/configurationStores/read", + Display: &v1.OperationDisplayProperties{ + Provider: "Applications.Dapr", + Resource: "configurationStores", + Operation: "Get/List Dapr configurationStores", + Description: "Gets/Lists Dapr configurationStores resource(s).", + }, + IsDataAction: false, + }, + { + Name: "Applications.Dapr/configurationStores/write", + Display: &v1.OperationDisplayProperties{ + Provider: "Applications.Dapr", + Resource: "configurationStores", + Operation: "Create/Update Dapr configurationStores", + Description: "Creates or updates a Dapr configurationStores resource.", + }, + IsDataAction: false, + }, + { + Name: "Applications.Dapr/configurationStores/delete", + Display: &v1.OperationDisplayProperties{ + Provider: "Applications.Dapr", + Resource: "configurationStores", + Operation: "Delete Dapr configurationStores", + Description: "Deletes a Dapr configurationStores resource.", + }, + IsDataAction: false, + }, } diff --git a/pkg/daprrp/setup/setup.go b/pkg/daprrp/setup/setup.go index 2b6b426ec1..4470884c83 100644 --- a/pkg/daprrp/setup/setup.go +++ b/pkg/daprrp/setup/setup.go @@ -27,6 +27,7 @@ import ( "github.com/radius-project/radius/pkg/recipes/controllerconfig" dapr_ctrl "github.com/radius-project/radius/pkg/daprrp/frontend/controller" + configurationstores_proc "github.com/radius-project/radius/pkg/daprrp/processors/configurationstores" pubsub_proc "github.com/radius-project/radius/pkg/daprrp/processors/pubsubbrokers" secretstore_proc "github.com/radius-project/radius/pkg/daprrp/processors/secretstores" statestore_proc "github.com/radius-project/radius/pkg/daprrp/processors/statestores" @@ -142,6 +143,39 @@ func SetupNamespace(recipeControllerConfig *controllerconfig.RecipeControllerCon }, }) + _ = ns.AddResource("configurationStores", &builder.ResourceOption[*datamodel.DaprConfigurationStore, datamodel.DaprConfigurationStore]{ + RequestConverter: converter.ConfigurationStoreDataModelFromVersioned, + ResponseConverter: converter.ConfigurationStoreDataModelToVersioned, + + Put: builder.Operation[datamodel.DaprConfigurationStore]{ + UpdateFilters: []apictrl.UpdateFilter[datamodel.DaprConfigurationStore]{ + rp_frontend.PrepareRadiusResource[*datamodel.DaprConfigurationStore], + }, + AsyncJobController: func(options asyncctrl.Options) (asyncctrl.Controller, error) { + return pr_ctrl.NewCreateOrUpdateResource[*datamodel.DaprConfigurationStore, datamodel.DaprConfigurationStore](options, &configurationstores_proc.Processor{Client: options.KubeClient}, recipeControllerConfig.Engine, recipeControllerConfig.ResourceClient, recipeControllerConfig.ConfigLoader) + }, + AsyncOperationTimeout: dapr_ctrl.AsyncCreateOrUpdateDaprConfigurationStoreTimeout, + AsyncOperationRetryAfter: AsyncOperationRetryAfter, + }, + Patch: builder.Operation[datamodel.DaprConfigurationStore]{ + UpdateFilters: []apictrl.UpdateFilter[datamodel.DaprConfigurationStore]{ + rp_frontend.PrepareRadiusResource[*datamodel.DaprConfigurationStore], + }, + AsyncJobController: func(options asyncctrl.Options) (asyncctrl.Controller, error) { + return pr_ctrl.NewCreateOrUpdateResource[*datamodel.DaprConfigurationStore, datamodel.DaprConfigurationStore](options, &configurationstores_proc.Processor{Client: options.KubeClient}, recipeControllerConfig.Engine, recipeControllerConfig.ResourceClient, recipeControllerConfig.ConfigLoader) + }, + AsyncOperationTimeout: dapr_ctrl.AsyncCreateOrUpdateDaprConfigurationStoreTimeout, + AsyncOperationRetryAfter: AsyncOperationRetryAfter, + }, + Delete: builder.Operation[datamodel.DaprConfigurationStore]{ + AsyncJobController: func(options asyncctrl.Options) (asyncctrl.Controller, error) { + return pr_ctrl.NewDeleteResource[*datamodel.DaprConfigurationStore, datamodel.DaprConfigurationStore](options, &configurationstores_proc.Processor{Client: options.KubeClient}, recipeControllerConfig.Engine, recipeControllerConfig.ConfigLoader) + }, + AsyncOperationTimeout: dapr_ctrl.AsyncDeleteDaprConfigurationStoreTimeout, + AsyncOperationRetryAfter: AsyncOperationRetryAfter, + }, + }) + // Optional ns.SetAvailableOperations(operationList) diff --git a/pkg/daprrp/setup/setup_test.go b/pkg/daprrp/setup/setup_test.go index ba6b9d9cfa..9bcdc2efb6 100644 --- a/pkg/daprrp/setup/setup_test.go +++ b/pkg/daprrp/setup/setup_test.go @@ -109,6 +109,31 @@ var handlerTests = []rpctest.HandlerTestSpec{ Path: "/resourcegroups/testrg/providers/applications.dapr/secretstores/secretstore", Method: http.MethodDelete, }, + { + OperationType: v1.OperationType{Type: dapr_ctrl.DaprConfigurationStoresResourceType, Method: v1.OperationPlaneScopeList}, + Path: "/providers/applications.dapr/configurationstores", + Method: http.MethodGet, + }, { + OperationType: v1.OperationType{Type: dapr_ctrl.DaprConfigurationStoresResourceType, Method: v1.OperationList}, + Path: "/resourcegroups/testrg/providers/applications.dapr/configurationstores", + Method: http.MethodGet, + }, { + OperationType: v1.OperationType{Type: dapr_ctrl.DaprConfigurationStoresResourceType, Method: v1.OperationGet}, + Path: "/resourcegroups/testrg/providers/applications.dapr/configurationstores/configstore", + Method: http.MethodGet, + }, { + OperationType: v1.OperationType{Type: dapr_ctrl.DaprConfigurationStoresResourceType, Method: v1.OperationPut}, + Path: "/resourcegroups/testrg/providers/applications.dapr/configurationstores/configstore", + Method: http.MethodPut, + }, { + OperationType: v1.OperationType{Type: dapr_ctrl.DaprConfigurationStoresResourceType, Method: v1.OperationPatch}, + Path: "/resourcegroups/testrg/providers/applications.dapr/configurationstores/configstore", + Method: http.MethodPatch, + }, { + OperationType: v1.OperationType{Type: dapr_ctrl.DaprConfigurationStoresResourceType, Method: v1.OperationDelete}, + Path: "/resourcegroups/testrg/providers/applications.dapr/configurationstores/configstore", + Method: http.MethodDelete, + }, } func TestRouter(t *testing.T) { diff --git a/pkg/portableresources/handlers/util.go b/pkg/portableresources/handlers/util.go index a9db93e812..099155c500 100644 --- a/pkg/portableresources/handlers/util.go +++ b/pkg/portableresources/handlers/util.go @@ -29,7 +29,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) -const daprConflictFmt = "the Dapr component name '%q' is already in use by another resource. Dapr component and resource names must be unique across all Dapr types (eg: StateStores, PubSubBrokers, SecretStores, etc.). Please select a new name and try again" +const daprConflictFmt = "the Dapr component name '%q' is already in use by another resource. Dapr component and resource names must be unique across all Dapr types (e.g., StateStores, PubSubBrokers, SecretStores, ConfigurationStores, etc.). Please select a new name and try again." // CheckDaprResourceNameUniqueness checks if the resource name is unique in the namespace. If the resource name is not unique, it returns an error. // diff --git a/pkg/rp/portableresources/portableresources.go b/pkg/rp/portableresources/portableresources.go index 8175cccdc6..a67b3a0c78 100644 --- a/pkg/rp/portableresources/portableresources.go +++ b/pkg/rp/portableresources/portableresources.go @@ -34,6 +34,7 @@ func IsValidPortableResourceType(resourceType string) bool { dapr_ctrl.DaprPubSubBrokersResourceType, dapr_ctrl.DaprSecretStoresResourceType, dapr_ctrl.DaprStateStoresResourceType, + dapr_ctrl.DaprConfigurationStoresResourceType, msg_ctrl.RabbitMQQueuesResourceType, ds_ctrl.MongoDatabasesResourceType, ds_ctrl.RedisCachesResourceType, @@ -56,6 +57,7 @@ func GetValidPortableResourceTypes() []string { dapr_ctrl.DaprPubSubBrokersResourceType, dapr_ctrl.DaprSecretStoresResourceType, dapr_ctrl.DaprStateStoresResourceType, + dapr_ctrl.DaprConfigurationStoresResourceType, msg_ctrl.RabbitMQQueuesResourceType, ds_ctrl.MongoDatabasesResourceType, ds_ctrl.RedisCachesResourceType, diff --git a/swagger/specification/applications/resource-manager/Applications.Dapr/preview/2023-10-01-preview/examples/ConfigurationStores_CreateOrUpdate.json b/swagger/specification/applications/resource-manager/Applications.Dapr/preview/2023-10-01-preview/examples/ConfigurationStores_CreateOrUpdate.json new file mode 100644 index 0000000000..d3e7bcfd16 --- /dev/null +++ b/swagger/specification/applications/resource-manager/Applications.Dapr/preview/2023-10-01-preview/examples/ConfigurationStores_CreateOrUpdate.json @@ -0,0 +1,77 @@ +{ + "operationId": "ConfigurationStores_CreateOrUpdate", + "title": "Create or update a ConfigurationStore resource", + "parameters": { + "rootScope": "/planes/radius/local/resourceGroups/testGroup", + "ConfigurationStoreName": "configstore0", + "api-version": "2023-10-01-preview", + "ConfigurationStoreParameters": { + "location": "West US", + "properties": { + "application": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/applications/testApplication", + "environment": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/environments/env0", + "resourceProvisioning": "manual", + "resources": [ + { + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Microsoft.AppConfiguration/configurationStores/testappconfig" + } + ], + "type": "configuration.azure.appconfig", + "version": "v1", + "metadata": { + "foo": "bar" + } + } + } + }, + "responses": { + "200": { + "body": { + "id": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Dapr/configurationStores/configstore0", + "name": "configstore0", + "type": "Applications.Dapr/configurationStores", + "location": "West US", + "properties": { + "provisioningState": "Succeeded", + "application": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/applications/testApplication", + "environment": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/environments/env0", + "resourceProvisioning": "manual", + "resources": [ + { + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Microsoft.AppConfiguration/configurationStores/testappconfig" + } + ], + "type": "configuration.azure.appconfig", + "version": "v1", + "metadata": { + "foo": "bar" + } + } + } + }, + "201": { + "body": { + "id": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Dapr/configurationStores/configstore0", + "name": "configstore0", + "type": "Applications.Dapr/configurationStores", + "location": "West US", + "properties": { + "provisioningState": "Accepted", + "application": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/applications/testApplication", + "environment": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/environments/env0", + "resourceProvisioning": "manual", + "resources": [ + { + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Microsoft.AppConfiguration/configurationStores/testappconfig" + } + ], + "type": "configuration.azure.appconfig", + "version": "v1", + "metadata": { + "foo": "bar" + } + } + } + } + } +} \ No newline at end of file diff --git a/swagger/specification/applications/resource-manager/Applications.Dapr/preview/2023-10-01-preview/examples/ConfigurationStores_CreateOrUpdateWithRecipe.json b/swagger/specification/applications/resource-manager/Applications.Dapr/preview/2023-10-01-preview/examples/ConfigurationStores_CreateOrUpdateWithRecipe.json new file mode 100644 index 0000000000..1e2a605fbf --- /dev/null +++ b/swagger/specification/applications/resource-manager/Applications.Dapr/preview/2023-10-01-preview/examples/ConfigurationStores_CreateOrUpdateWithRecipe.json @@ -0,0 +1,66 @@ +{ + "operationId": "ConfigurationStores_CreateOrUpdate", + "title": "Create or update a configurationStore resource with recipe", + "parameters": { + "rootScope": "/planes/radius/local/resourceGroups/testGroup", + "configurationStoreName": "configstore0", + "api-version": "2023-10-01-preview", + "configurationStoreParameters": { + "location": "West US", + "properties": { + "application": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/applications/testApplication", + "environment": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/environments/env0", + "recipe": { + "name": "configstore-test", + "parameters": { + "port": 6081 + } + } + } + } + }, + "responses": { + "200": { + "body": { + "id": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Dapr/configurationStores/configstore0", + "name": "configstore0", + "type": "Applications.Dapr/configurationStores", + "location": "West US", + "properties": { + "provisioningState": "Succeeded", + "application": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/applications/testApplication", + "environment": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/environments/env0", + "resourceProvisioning": "recipe", + "type": "configuration.azure.appconfig", + "recipe": { + "name": "configstore-test", + "parameters": { + "port": 6081 + } + } + } + } + }, + "201": { + "body": { + "id": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Dapr/configurationStores/configstore0", + "name": "configstore0", + "type": "Applications.Dapr/configurationStores", + "location": "West US", + "properties": { + "provisioningState": "Accepted", + "application": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/applications/testApplication", + "environment": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/environments/env0", + "resourceProvisioning": "recipe", + "type": "configuration.azure.appconfig", + "recipe": { + "name": "configstore-test", + "parameters": { + "port": 6081 + } + } + } + } + } + } +} \ No newline at end of file diff --git a/swagger/specification/applications/resource-manager/Applications.Dapr/preview/2023-10-01-preview/examples/ConfigurationStores_Delete.json b/swagger/specification/applications/resource-manager/Applications.Dapr/preview/2023-10-01-preview/examples/ConfigurationStores_Delete.json new file mode 100644 index 0000000000..63a568f43d --- /dev/null +++ b/swagger/specification/applications/resource-manager/Applications.Dapr/preview/2023-10-01-preview/examples/ConfigurationStores_Delete.json @@ -0,0 +1,14 @@ +{ + "operationId": "ConfigurationStores_Delete", + "title": "Delete a ConfigurationStore resource", + "parameters": { + "rootScope": "/planes/radius/local/resourceGroups/testGroup/resourceGroups/testGroup", + "pubSubBrokerName": "configstore0", + "api-version": "2023-10-01-preview" + }, + "responses": { + "200": {}, + "202": {}, + "204": {} + } +} \ No newline at end of file diff --git a/swagger/specification/applications/resource-manager/Applications.Dapr/preview/2023-10-01-preview/examples/ConfigurationStores_Get.json b/swagger/specification/applications/resource-manager/Applications.Dapr/preview/2023-10-01-preview/examples/ConfigurationStores_Get.json new file mode 100644 index 0000000000..964aca70fe --- /dev/null +++ b/swagger/specification/applications/resource-manager/Applications.Dapr/preview/2023-10-01-preview/examples/ConfigurationStores_Get.json @@ -0,0 +1,35 @@ +{ + "operationId": "ConfigurationStores_Get", + "title": "Get a PubSubBroker resource", + "parameters": { + "rootScope": "/planes/radius/local/resourceGroups/testGroup", + "api-version": "2023-10-01-preview", + "pubSubBrokerName": "configstore0" + }, + "responses": { + "200": { + "body": { + "id": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Dapr/configurationStores/configstore0", + "name": "configstore0", + "type": "Applications.Dapr/configurationStores", + "location": "global", + "properties": { + "provisioningState": "Succeeded", + "application": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/applications/testApplication", + "environment": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/environments/env0", + "resourceProvisioning": "manual", + "resources": [ + { + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Microsoft.AppConfiguration/configurationStores/testappconfig" + } + ], + "type": "configuration.azure.appconfig", + "version": "v1", + "metadata": { + "foo": "bar" + } + } + } + } + } +} \ No newline at end of file diff --git a/swagger/specification/applications/resource-manager/Applications.Dapr/preview/2023-10-01-preview/examples/ConfigurationStores_List.json b/swagger/specification/applications/resource-manager/Applications.Dapr/preview/2023-10-01-preview/examples/ConfigurationStores_List.json new file mode 100644 index 0000000000..c792841bd7 --- /dev/null +++ b/swagger/specification/applications/resource-manager/Applications.Dapr/preview/2023-10-01-preview/examples/ConfigurationStores_List.json @@ -0,0 +1,73 @@ +{ + "operationId": "ConfigurationStores_ListByScope", + "title": "List a ConfigurationStore resource by resource group", + "parameters": { + "rootScope": "/planes/radius/local/resourceGroups/testGroup", + "api-version": "2023-10-01-preview" + }, + "responses": { + "200": { + "body": { + "value": [ + { + "id": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Dapr/configurationStores/configstore0", + "name": "configstore0", + "type": "Applications.Dapr/configurationStores", + "location": "global", + "properties": { + "provisioningState": "Succeeded", + "application": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/applications/testApplication", + "environment": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/environments/env0", + "resourceProvisioning": "manual", + "resources": [ + { + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Microsoft.AppConfiguration/configurationStores/testappconfig" + } + ], + "type": "configuration.azure.appconfig", + "version": "v1", + "metadata": { + "foo": "bar" + } + } + }, + { + "id": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Dapr/configurationStores/configstore1", + "name": "configstore1", + "type": "Applications.Dapr/configurationStores", + "location": "global", + "properties": { + "provisioningState": "Succeeded", + "application": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/applications/testApplication", + "environment": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/environments/env0", + "resourceProvisioning": "manual", + "type": "configuration.redis", + "version": "v1", + "metadata": { + "foo": "bar" + } + } + }, + { + "id": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Dapr/configurationStores/configstore2", + "name": "configstore2", + "type": "Applications.Dapr/configurationStores", + "location": "global", + "properties": { + "provisioningState": "Succeeded", + "application": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/applications/testApplication", + "environment": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/environments/env0", + "recipe": { + "name": "config-test", + "parameters": { + "port": 6081 + } + } + } + } + ], + "nextLink": "https://serviceRoot/planes/radius/local/resourceGroups/testGroup/providers/Applications.Dapr/configurationStores?api-version=2023-10-01-preview&$skipToken=X'12345'" + } + } + } +} \ No newline at end of file diff --git a/swagger/specification/applications/resource-manager/Applications.Dapr/preview/2023-10-01-preview/examples/ConfigurationStores_ListByRootScope.json b/swagger/specification/applications/resource-manager/Applications.Dapr/preview/2023-10-01-preview/examples/ConfigurationStores_ListByRootScope.json new file mode 100644 index 0000000000..377dfe293c --- /dev/null +++ b/swagger/specification/applications/resource-manager/Applications.Dapr/preview/2023-10-01-preview/examples/ConfigurationStores_ListByRootScope.json @@ -0,0 +1,56 @@ +{ + "operationId": "ConfigurationStores_ListByScope", + "title": "List a ConfigurationStores resource by rootScope", + "parameters": { + "rootScope": "/planes/radius/local", + "api-version": "2023-10-01-preview" + }, + "responses": { + "200": { + "body": { + "value": [ + { + "id": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Dapr/configurationStores/configstore0", + "name": "configstore0", + "type": "Applications.Dapr/configurationStores", + "location": "global", + "properties": { + "provisioningState": "Succeeded", + "application": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/applications/testApplication", + "environment": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/environments/env0", + "resourceProvisioning": "manual", + "resources": [ + { + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Microsoft.AppConfiguration/configurationStores/testappconfig" + } + ], + "type": "configuration.azure.appconfig", + "version": "v1", + "metadata": { + "foo": "bar" + } + } + }, + { + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup1/providers/Applications.Dapr/configurationStores/configstore1", + "name": "configstore1", + "type": "Applications.Dapr/configurationStores", + "location": "global", + "properties": { + "provisioningState": "Succeeded", + "application": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/applications/testApplication", + "environment": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/environments/env0", + "resourceProvisioning": "manual", + "type": "configuration.redis", + "version": "v1", + "metadata": { + "foo": "bar" + } + } + } + ], + "nextLink": "https://serviceRoot/planes/radius/local/providers/Applications.Dapr/configurationStores?api-version=2023-10-01-preview&$skipToken=X'12345'" + } + } + } +} \ No newline at end of file diff --git a/swagger/specification/applications/resource-manager/Applications.Dapr/preview/2023-10-01-preview/examples/ConfigurationStores_Update.json b/swagger/specification/applications/resource-manager/Applications.Dapr/preview/2023-10-01-preview/examples/ConfigurationStores_Update.json new file mode 100644 index 0000000000..39ee6e7a62 --- /dev/null +++ b/swagger/specification/applications/resource-manager/Applications.Dapr/preview/2023-10-01-preview/examples/ConfigurationStores_Update.json @@ -0,0 +1,77 @@ +{ + "operationId": "ConfigurationStores_Update", + "title": "Update a ConfigurationStore resource", + "parameters": { + "rootScope": "/planes/radius/local/resourceGroups/testGroup", + "ConfigurationStoreName": "configstore0", + "api-version": "2023-10-01-preview", + "ConfigurationStoreParameters": { + "location": "West US", + "properties": { + "application": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/applications/testApplication", + "environment": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/environments/env0", + "resourceProvisioning": "manual", + "resources": [ + { + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Microsoft.AppConfiguration/configurationStores/testappconfig" + } + ], + "type": "configuration.azure.appconfig", + "version": "v1", + "metadata": { + "foo": "bar" + } + } + } + }, + "responses": { + "200": { + "body": { + "id": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Dapr/configurationStores/configstore0", + "name": "configstore0", + "type": "Applications.Dapr/configurationStores", + "location": "West US", + "properties": { + "provisioningState": "Succeeded", + "application": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/applications/testApplication", + "environment": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/environments/env0", + "resourceProvisioning": "manual", + "resources": [ + { + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Microsoft.AppConfiguration/configurationStores/testappconfig" + } + ], + "type": "configuration.azure.appconfig", + "version": "v1", + "metadata": { + "foo": "bar" + } + } + } + }, + "201": { + "body": { + "id": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Dapr/configurationStores/configstore0", + "name": "configstore0", + "type": "Applications.Dapr/configurationStores", + "location": "West US", + "properties": { + "provisioningState": "Accepted", + "application": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/applications/testApplication", + "environment": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/environments/env0", + "resourceProvisioning": "manual", + "resources": [ + { + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Microsoft.AppConfiguration/configurationStores/testappconfig" + } + ], + "type": "configuration.azure.appconfig", + "version": "v1", + "metadata": { + "foo": "bar" + } + } + } + } + } +} \ No newline at end of file diff --git a/swagger/specification/applications/resource-manager/Applications.Dapr/preview/2023-10-01-preview/openapi.json b/swagger/specification/applications/resource-manager/Applications.Dapr/preview/2023-10-01-preview/openapi.json index 9710bb828d..2456969ab3 100644 --- a/swagger/specification/applications/resource-manager/Applications.Dapr/preview/2023-10-01-preview/openapi.json +++ b/swagger/specification/applications/resource-manager/Applications.Dapr/preview/2023-10-01-preview/openapi.json @@ -50,9 +50,302 @@ }, { "name": "PubSubBrokers" + }, + { + "name": "ConfigurationStores" } ], "paths": { + "/{rootScope}/providers/Applications.Dapr/configurationStores": { + "get": { + "operationId": "ConfigurationStores_ListByScope", + "tags": [ + "ConfigurationStores" + ], + "description": "List DaprConfigurationStoreResource resources by Scope", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v3/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "#/parameters/RootScopeParameter" + } + ], + "responses": { + "200": { + "description": "ARM operation completed successfully.", + "schema": { + "$ref": "#/definitions/DaprConfigurationStoreResourceListResult" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v3/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "List a ConfigurationStore resource by resource group": { + "$ref": "./examples/ConfigurationStores_List.json" + }, + "List a ConfigurationStores resource by rootScope": { + "$ref": "./examples/ConfigurationStores_ListByRootScope.json" + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/{rootScope}/providers/Applications.Dapr/configurationStores/{configurationStoreName}": { + "get": { + "operationId": "ConfigurationStores_Get", + "tags": [ + "ConfigurationStores" + ], + "description": "Get a DaprConfigurationStoreResource", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v3/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "#/parameters/RootScopeParameter" + }, + { + "name": "configurationStoreName", + "in": "path", + "description": "Configuration Store name", + "required": true, + "type": "string", + "maxLength": 63, + "pattern": "^[A-Za-z]([-A-Za-z0-9]*[A-Za-z0-9])?$" + } + ], + "responses": { + "200": { + "description": "ARM operation completed successfully.", + "schema": { + "$ref": "#/definitions/DaprConfigurationStoreResource" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v3/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Get a PubSubBroker resource": { + "$ref": "./examples/ConfigurationStores_Get.json" + } + } + }, + "put": { + "operationId": "ConfigurationStores_CreateOrUpdate", + "tags": [ + "ConfigurationStores" + ], + "description": "Create a DaprConfigurationStoreResource", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v3/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "#/parameters/RootScopeParameter" + }, + { + "name": "configurationStoreName", + "in": "path", + "description": "Configuration Store name", + "required": true, + "type": "string", + "maxLength": 63, + "pattern": "^[A-Za-z]([-A-Za-z0-9]*[A-Za-z0-9])?$" + }, + { + "name": "resource", + "in": "body", + "description": "Resource create parameters.", + "required": true, + "schema": { + "$ref": "#/definitions/DaprConfigurationStoreResource" + } + } + ], + "responses": { + "200": { + "description": "Resource 'DaprConfigurationStoreResource' update operation succeeded", + "schema": { + "$ref": "#/definitions/DaprConfigurationStoreResource" + } + }, + "201": { + "description": "Resource 'DaprConfigurationStoreResource' create operation succeeded", + "schema": { + "$ref": "#/definitions/DaprConfigurationStoreResource" + }, + "headers": { + "Retry-After": { + "type": "integer", + "format": "int32", + "description": "The Retry-After header can indicate how long the client should wait before polling the operation status." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v3/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Create or update a ConfigurationStore resource": { + "$ref": "./examples/ConfigurationStores_CreateOrUpdate.json" + }, + "Create or update a configurationStore resource with recipe": { + "$ref": "./examples/ConfigurationStores_CreateOrUpdateWithRecipe.json" + } + }, + "x-ms-long-running-operation-options": { + "final-state-via": "azure-async-operation" + }, + "x-ms-long-running-operation": true + }, + "patch": { + "operationId": "ConfigurationStores_Update", + "tags": [ + "ConfigurationStores" + ], + "description": "Update a DaprConfigurationStoreResource", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v3/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "#/parameters/RootScopeParameter" + }, + { + "name": "configurationStoreName", + "in": "path", + "description": "Configuration Store name", + "required": true, + "type": "string", + "maxLength": 63, + "pattern": "^[A-Za-z]([-A-Za-z0-9]*[A-Za-z0-9])?$" + }, + { + "name": "properties", + "in": "body", + "description": "The resource properties to be updated.", + "required": true, + "schema": { + "$ref": "#/definitions/DaprConfigurationStoreResourceUpdate" + } + } + ], + "responses": { + "200": { + "description": "ARM operation completed successfully.", + "schema": { + "$ref": "#/definitions/DaprConfigurationStoreResource" + } + }, + "202": { + "description": "Resource update request accepted.", + "headers": { + "Retry-After": { + "type": "integer", + "format": "int32", + "description": "The Retry-After header can indicate how long the client should wait before polling the operation status." + }, + "Location": { + "type": "string", + "description": "The Location header contains the URL where the status of the long running operation can be checked." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v3/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Update a ConfigurationStore resource": { + "$ref": "./examples/ConfigurationStores_Update.json" + } + }, + "x-ms-long-running-operation-options": { + "final-state-via": "location" + }, + "x-ms-long-running-operation": true + }, + "delete": { + "operationId": "ConfigurationStores_Delete", + "tags": [ + "ConfigurationStores" + ], + "description": "Delete a DaprConfigurationStoreResource", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v3/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "#/parameters/RootScopeParameter" + }, + { + "name": "configurationStoreName", + "in": "path", + "description": "Configuration Store name", + "required": true, + "type": "string", + "maxLength": 63, + "pattern": "^[A-Za-z]([-A-Za-z0-9]*[A-Za-z0-9])?$" + } + ], + "responses": { + "200": { + "description": "Resource deleted successfully." + }, + "202": { + "description": "Resource deletion accepted.", + "headers": { + "Retry-After": { + "type": "integer", + "format": "int32", + "description": "The Retry-After header can indicate how long the client should wait before polling the operation status." + }, + "Location": { + "type": "string", + "description": "The Location header contains the URL where the status of the long running operation can be checked." + } + } + }, + "204": { + "description": "Resource deleted successfully." + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v3/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Delete a ConfigurationStore resource": { + "$ref": "./examples/ConfigurationStores_Delete.json" + } + }, + "x-ms-long-running-operation-options": { + "final-state-via": "location" + }, + "x-ms-long-running-operation": true + } + }, "/{rootScope}/providers/Applications.Dapr/pubSubBrokers": { "get": { "operationId": "PubSubBrokers_ListByScope", @@ -953,6 +1246,181 @@ } }, "definitions": { + "DaprConfigurationStoreProperties": { + "type": "object", + "description": "Dapr configuration store portable resource properties", + "properties": { + "environment": { + "type": "string", + "description": "Fully qualified resource ID for the environment that the portable resource is linked to" + }, + "application": { + "type": "string", + "description": "Fully qualified resource ID for the application that the portable resource is consumed by (if applicable)" + }, + "provisioningState": { + "$ref": "#/definitions/ProvisioningState", + "description": "The status of the asynchronous operation.", + "readOnly": true + }, + "status": { + "$ref": "#/definitions/ResourceStatus", + "description": "Status of a resource.", + "readOnly": true + }, + "componentName": { + "type": "string", + "description": "The name of the Dapr component object. Use this value in your code when interacting with the Dapr client to use the Dapr component.", + "readOnly": true + }, + "metadata": { + "type": "object", + "description": "The metadata for Dapr resource which must match the values specified in Dapr component spec", + "additionalProperties": { + "$ref": "#/definitions/MetadataValue" + } + }, + "type": { + "type": "string", + "description": "Dapr component type which must matches the format used by Dapr Kubernetes configuration format" + }, + "version": { + "type": "string", + "description": "Dapr component version" + }, + "auth": { + "$ref": "#/definitions/DaprResourceAuth", + "description": "The name of the Dapr component to be used as a secret store" + }, + "resources": { + "type": "array", + "description": "A collection of references to resources associated with the configuration store", + "items": { + "$ref": "#/definitions/ResourceReference" + } + }, + "recipe": { + "$ref": "#/definitions/Recipe", + "description": "The recipe used to automatically deploy underlying infrastructure for the resource" + }, + "resourceProvisioning": { + "$ref": "#/definitions/ResourceProvisioning", + "description": "Specifies how the underlying service/resource is provisioned and managed." + } + }, + "required": [ + "environment" + ] + }, + "DaprConfigurationStoreResource": { + "type": "object", + "description": "Dapr configuration store portable resource", + "properties": { + "properties": { + "$ref": "#/definitions/DaprConfigurationStoreProperties", + "description": "The resource-specific properties for this resource.", + "x-ms-client-flatten": true, + "x-ms-mutability": [ + "read", + "create" + ] + } + }, + "required": [ + "properties" + ], + "allOf": [ + { + "$ref": "../../../../../common-types/resource-management/v3/types.json#/definitions/TrackedResource" + } + ] + }, + "DaprConfigurationStoreResourceListResult": { + "type": "object", + "description": "The response of a DaprConfigurationStoreResource list operation.", + "properties": { + "value": { + "type": "array", + "description": "The DaprConfigurationStoreResource items on this page", + "items": { + "$ref": "#/definitions/DaprConfigurationStoreResource" + } + }, + "nextLink": { + "type": "string", + "format": "uri", + "description": "The link to the next page of items" + } + }, + "required": [ + "value" + ] + }, + "DaprConfigurationStoreResourceUpdate": { + "type": "object", + "description": "The type used for update operations of the DaprConfigurationStoreResource.", + "properties": { + "tags": { + "type": "object", + "description": "Resource tags.", + "additionalProperties": { + "type": "string" + } + }, + "properties": { + "$ref": "#/definitions/DaprConfigurationStoreResourceUpdateProperties", + "x-ms-client-flatten": true + } + } + }, + "DaprConfigurationStoreResourceUpdateProperties": { + "type": "object", + "description": "The updatable properties of the DaprConfigurationStoreResource.", + "properties": { + "environment": { + "type": "string", + "description": "Fully qualified resource ID for the environment that the portable resource is linked to" + }, + "application": { + "type": "string", + "description": "Fully qualified resource ID for the application that the portable resource is consumed by (if applicable)" + }, + "metadata": { + "type": "object", + "description": "The metadata for Dapr resource which must match the values specified in Dapr component spec", + "additionalProperties": { + "$ref": "#/definitions/MetadataValueUpdate" + } + }, + "type": { + "type": "string", + "description": "Dapr component type which must matches the format used by Dapr Kubernetes configuration format" + }, + "version": { + "type": "string", + "description": "Dapr component version" + }, + "auth": { + "$ref": "#/definitions/DaprResourceAuth", + "description": "The name of the Dapr component to be used as a secret store" + }, + "resources": { + "type": "array", + "description": "A collection of references to resources associated with the configuration store", + "items": { + "$ref": "#/definitions/ResourceReference" + } + }, + "recipe": { + "$ref": "#/definitions/RecipeUpdate", + "description": "The recipe used to automatically deploy underlying infrastructure for the resource" + }, + "resourceProvisioning": { + "$ref": "#/definitions/ResourceProvisioning", + "description": "Specifies how the underlying service/resource is provisioned and managed." + } + } + }, "DaprPubSubBrokerProperties": { "type": "object", "description": "Dapr PubSubBroker portable resource properties", diff --git a/test/functional-portable/daprrp/noncloud/resources/dapr_component_name_conflict_test.go b/test/functional-portable/daprrp/noncloud/resources/dapr_component_name_conflict_test.go index 81317a294d..5ef4f852cd 100644 --- a/test/functional-portable/daprrp/noncloud/resources/dapr_component_name_conflict_test.go +++ b/test/functional-portable/daprrp/noncloud/resources/dapr_component_name_conflict_test.go @@ -34,7 +34,7 @@ func Test_DaprComponentNameConflict(t *testing.T) { Details: []step.DeploymentErrorDetail{ { Code: v1.CodeInternal, - MessageContains: "the Dapr component name '\"dapr-component\"' is already in use by another resource. Dapr component and resource names must be unique across all Dapr types (eg: StateStores, PubSubBrokers, SecretStores, etc.). Please select a new name and try again", + MessageContains: "the Dapr component name '\"dapr-component\"' is already in use by another resource. Dapr component and resource names must be unique across all Dapr types (e.g., StateStores, PubSubBrokers, SecretStores, ConfigurationStores, etc.). Please select a new name and try again.", }, }, }) diff --git a/test/functional-portable/daprrp/noncloud/resources/dapr_configurationstore_test.go b/test/functional-portable/daprrp/noncloud/resources/dapr_configurationstore_test.go new file mode 100644 index 0000000000..4534f5a48c --- /dev/null +++ b/test/functional-portable/daprrp/noncloud/resources/dapr_configurationstore_test.go @@ -0,0 +1,219 @@ +/* +Copyright 2023 The Radius Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package resource_test + +import ( + "context" + "fmt" + "testing" + + "github.com/radius-project/radius/test/rp" + "github.com/radius-project/radius/test/step" + "github.com/radius-project/radius/test/testutil" + "github.com/radius-project/radius/test/validation" +) + +func Test_ConfigurationStore_Manual_Secret(t *testing.T) { + template := "testdata/daprrp-resources-configurationstore-manual-secret.bicep" + name := "dcs-manual-secret" + appNamespace := fmt.Sprintf("default-%s", name) + redisPassword := "Password1234!" + secretName := "redisauth" + + test := rp.NewRPTest(t, name, []rp.TestStep{ + { + Executor: step.NewDeployExecutor( + template, + testutil.GetMagpieImage(), + fmt.Sprintf("namespace=%s", appNamespace), + fmt.Sprintf("baseName=%s", name), + fmt.Sprintf("redisPassword=%s", redisPassword), + fmt.Sprintf("secretName=%s", secretName), + ), + RPResources: &validation.RPResourceSet{ + Resources: []validation.RPResource{ + { + Name: name, + Type: validation.ApplicationsResource, + }, + { + Name: fmt.Sprintf("%s-ctnr", name), + Type: validation.ContainersResource, + App: name, + }, + { + Name: fmt.Sprintf("%s-dcs", name), + Type: validation.DaprConfigurationStoresResource, + App: name, + }, + { + Name: fmt.Sprintf("%s-scs", name), + Type: validation.DaprSecretStoresResource, + App: name, + }, + }, + }, + K8sObjects: &validation.K8sObjectSet{ + Namespaces: map[string][]validation.K8sObject{ + appNamespace: { + validation.NewK8sPodForResource(name, fmt.Sprintf("%s-ctnr", name)), + + // Deployed as supporting resources using Kubernetes Bicep extensibility. + validation.NewK8sPodForResource(name, fmt.Sprintf("%s-redis", name)). + ValidateLabels(false), + validation.NewK8sServiceForResource(name, fmt.Sprintf("%s-redis", name)). + ValidateLabels(false), + + validation.NewDaprComponent(name, fmt.Sprintf("%s-dcs", name)). + ValidateLabels(false), + validation.NewDaprComponent(name, fmt.Sprintf("%s-scs", name)). + ValidateLabels(false), + }, + }, + }, + }, + }, rp.K8sSecretResource(appNamespace, secretName, "", "password", redisPassword)) + + test.RequiredFeatures = []rp.RequiredFeature{rp.FeatureDapr} + + test.PostDeleteVerify = func(ctx context.Context, t *testing.T, test rp.RPTest) { + verifyDaprComponentsDeleted(ctx, t, test, "Applications.Dapr/configurationStores", fmt.Sprintf("%s-dcs", name), appNamespace) + verifyDaprComponentsDeleted(ctx, t, test, "Applications.Dapr/secretStores", fmt.Sprintf("%s-scs", name), appNamespace) + + } + + test.Test(t) +} + +func Test_ConfigurationStore_Manual(t *testing.T) { + template := "testdata/daprrp-resources-configurationstore-manual.bicep" + name := "dcs-manual" + appNamespace := fmt.Sprintf("default-%s", name) + test := rp.NewRPTest(t, name, []rp.TestStep{ + { + Executor: step.NewDeployExecutor( + template, + testutil.GetMagpieImage(), + fmt.Sprintf("namespace=%s", appNamespace), + fmt.Sprintf("baseName=%s", name), + ), + RPResources: &validation.RPResourceSet{ + Resources: []validation.RPResource{ + { + Name: name, + Type: validation.ApplicationsResource, + }, + { + Name: fmt.Sprintf("%s-ctnr", name), + Type: validation.ContainersResource, + App: name, + }, + { + Name: fmt.Sprintf("%s-dcs", name), + Type: validation.DaprConfigurationStoresResource, + App: name, + }, + }, + }, + K8sObjects: &validation.K8sObjectSet{ + Namespaces: map[string][]validation.K8sObject{ + appNamespace: { + validation.NewK8sPodForResource(name, fmt.Sprintf("%s-ctnr", name)), + + // Deployed as supporting resources using Kubernetes Bicep extensibility. + validation.NewK8sPodForResource(name, fmt.Sprintf("%s-redis", name)). + ValidateLabels(false), + validation.NewK8sServiceForResource(name, fmt.Sprintf("%s-redis", name)). + ValidateLabels(false), + + validation.NewDaprComponent(name, fmt.Sprintf("%s-dcs", name)). + ValidateLabels(false), + }, + }, + }, + }, + }) + + test.RequiredFeatures = []rp.RequiredFeature{rp.FeatureDapr} + + test.PostDeleteVerify = func(ctx context.Context, t *testing.T, test rp.RPTest) { + verifyDaprComponentsDeleted(ctx, t, test, "Applications.Dapr/configurationStores", fmt.Sprintf("%s-dcs", name), appNamespace) + } + + test.Test(t) +} + +func Test_ConfigurationStore_Recipe(t *testing.T) { + template := "testdata/daprrp-resources-configurationstore-recipe.bicep" + name := "dcs-recipe" + appNamespace := fmt.Sprintf("default-%s", name) + + test := rp.NewRPTest(t, name, []rp.TestStep{ + { + Executor: step.NewDeployExecutor( + template, + testutil.GetMagpieImage(), + testutil.GetBicepRecipeRegistry(), + testutil.GetBicepRecipeVersion(), + fmt.Sprintf("namespace=%s", appNamespace), + fmt.Sprintf("baseName=%s", name), + ), + RPResources: &validation.RPResourceSet{ + Resources: []validation.RPResource{ + { + Name: fmt.Sprintf("%s-env", name), + Type: validation.EnvironmentsResource, + }, + { + Name: name, + Type: validation.ApplicationsResource, + App: name, + }, + { + Name: fmt.Sprintf("%s-ctnr", name), + Type: validation.ContainersResource, + App: name, + }, + { + Name: fmt.Sprintf("%s-cpn", name), + Type: validation.DaprConfigurationStoresResource, + App: name, + }, + }, + }, + K8sObjects: &validation.K8sObjectSet{ + Namespaces: map[string][]validation.K8sObject{ + appNamespace: { + validation.NewK8sPodForResource(name, fmt.Sprintf("%s-ctnr", name)). + ValidateLabels(false), + + validation.NewDaprComponent(name, fmt.Sprintf("%s-cpn", name)). + ValidateLabels(false), + }, + }, + }, + }, + }) + + test.RequiredFeatures = []rp.RequiredFeature{rp.FeatureDapr} + + test.PostDeleteVerify = func(ctx context.Context, t *testing.T, test rp.RPTest) { + verifyDaprComponentsDeleted(ctx, t, test, "Applications.Dapr/configurationStores", fmt.Sprintf("%s-cpn", name), appNamespace) + } + + test.Test(t) +} diff --git a/test/functional-portable/daprrp/noncloud/resources/testdata/daprrp-resources-configurationstore-manual-secret.bicep b/test/functional-portable/daprrp/noncloud/resources/testdata/daprrp-resources-configurationstore-manual-secret.bicep new file mode 100644 index 0000000000..c43fc36137 --- /dev/null +++ b/test/functional-portable/daprrp/noncloud/resources/testdata/daprrp-resources-configurationstore-manual-secret.bicep @@ -0,0 +1,98 @@ +extension radius + +param magpieimage string +param environment string +param namespace string = 'default' +param baseName string = 'dcs-manual-secret' +@secure() +param redisPassword string = '' +param secretName string = 'redisauth' +param location string = resourceGroup().location + +resource app 'Applications.Core/applications@2023-10-01-preview' = { + name: baseName + properties: { + environment: environment + } +} + +resource myapp 'Applications.Core/containers@2023-10-01-preview' = { + name: '${baseName}-ctnr' + properties: { + application: app.id + connections: { + daprconfigurationstore: { + source: configStore.id + } + } + container: { + image: magpieimage + readinessProbe: { + kind: 'httpGet' + containerPort: 3000 + path: '/healthz' + } + } + extensions: [ + { + kind: 'daprSidecar' + appId: 'dcs-manual-secret-app-ctnr' + appPort: 3000 + } + ] + } +} + + +module redis '../../../../../../test/testrecipes/modules/redis-selfhost.bicep' = { + name: '${baseName}-redis-deployment' + params: { + name: '${baseName}-redis' + namespace: namespace + application: app.name + password: redisPassword + } +} + + +resource configStore 'Applications.Dapr/configurationStores@2023-10-01-preview' = { + name: '${baseName}-dcs' + properties: { + application: app.id + environment: environment + resourceProvisioning: 'manual' + type: 'configuration.redis' + auth: { + secretStore: secretstore.name + } + metadata: { + redisHost: { + value: '${redis.outputs.host}:${redis.outputs.port}' + } + redisPassword: { + secretKeyRef: { + name: secretName + key: 'password' + } + } + } + version: 'v1' + } +} + +resource secretstore 'Applications.Dapr/secretStores@2023-10-01-preview' = { + name: '${baseName}-scs' + location: location + properties: { + environment: environment + application: app.id + resourceProvisioning: 'manual' + type: 'secretstores.kubernetes' + version: 'v1' + metadata: { + vaultName: { + value: 'test' + } + } + } +} diff --git a/test/functional-portable/daprrp/noncloud/resources/testdata/daprrp-resources-configurationstore-manual.bicep b/test/functional-portable/daprrp/noncloud/resources/testdata/daprrp-resources-configurationstore-manual.bicep new file mode 100644 index 0000000000..69ea8e8eeb --- /dev/null +++ b/test/functional-portable/daprrp/noncloud/resources/testdata/daprrp-resources-configurationstore-manual.bicep @@ -0,0 +1,70 @@ +extension radius + +param magpieimage string +param environment string +param namespace string = 'default' +param baseName string = 'dcs-manual' + +resource app 'Applications.Core/applications@2023-10-01-preview' = { + name: baseName + properties: { + environment: environment + } +} + +resource myapp 'Applications.Core/containers@2023-10-01-preview' = { + name: '${baseName}-ctnr' + properties: { + application: app.id + connections: { + daprconfigurationstore: { + source: configStore.id + } + } + container: { + image: magpieimage + readinessProbe: { + kind: 'httpGet' + containerPort: 3000 + path: '/healthz' + } + } + extensions: [ + { + kind: 'daprSidecar' + appId: 'dcs-manual-ctnr' + appPort: 3000 + } + ] + } +} + + +module redis '../../../../../../test/testrecipes/modules/redis-selfhost.bicep' = { + name: '${baseName}-redis-deployment' + params: { + name: '${baseName}-redis' + namespace: namespace + application: app.name + } +} + + +resource configStore 'Applications.Dapr/configurationStores@2023-10-01-preview' = { + name: '${baseName}-dcs' + properties: { + application: app.id + environment: environment + resourceProvisioning: 'manual' + type: 'configuration.redis' + metadata: { + redisHost: { + value: '${redis.outputs.host}:${redis.outputs.port}' + } + redisPassword: { + value: '' + } + } + version: 'v1' + } +} diff --git a/test/functional-portable/daprrp/noncloud/resources/testdata/daprrp-resources-configurationstore-recipe.bicep b/test/functional-portable/daprrp/noncloud/resources/testdata/daprrp-resources-configurationstore-recipe.bicep new file mode 100644 index 0000000000..07be554a3f --- /dev/null +++ b/test/functional-portable/daprrp/noncloud/resources/testdata/daprrp-resources-configurationstore-recipe.bicep @@ -0,0 +1,74 @@ +extension radius + +param magpieimage string +param registry string +param version string +param namespace string = 'default' +param baseName string = 'dcs-recipe' + +resource env 'Applications.Core/environments@2023-10-01-preview' = { + name: '${baseName}-env' + properties: { + compute: { + kind: 'kubernetes' + resourceId: 'self' + namespace: namespace + } + recipes: { + 'Applications.Dapr/configurationStores': { + default: { + templateKind: 'bicep' + templatePath: '${registry}/test/testrecipes/test-bicep-recipes/dapr-configuration-store:${version}' + } + } + } + } +} + +resource app 'Applications.Core/applications@2023-10-01-preview' = { + name: baseName + properties: { + environment: env.id + extensions: [ + { + kind: 'kubernetesNamespace' + namespace: baseName + } + ] + } +} + +resource myapp 'Applications.Core/containers@2023-10-01-preview' = { + name: '${baseName}-ctnr' + properties: { + application: app.id + connections: { + daprconfigurationstore: { + source: configStore.id + } + } + container: { + image: magpieimage + readinessProbe: { + kind: 'httpGet' + containerPort: 3000 + path: '/healthz' + } + } + extensions: [ + { + kind: 'daprSidecar' + appId: '${baseName}-ctnr' + appPort: 3000 + } + ] + } +} + +resource configStore 'Applications.Dapr/configurationStores@2023-10-01-preview' = { + name: '${baseName}-cpn' + properties: { + application: app.id + environment: env.id + } +} diff --git a/test/magpiego/bindings/daprconfigurationstore.go b/test/magpiego/bindings/daprconfigurationstore.go new file mode 100644 index 0000000000..a21881aa34 --- /dev/null +++ b/test/magpiego/bindings/daprconfigurationstore.go @@ -0,0 +1,36 @@ +package bindings + +import ( + "context" + "log" + + dapr "github.com/dapr/go-sdk/client" + "os" +) + +// DaprConfigurationStoreBinding checks if the environment parameter COMPONENTNAME is set and if so, creates a Dapr client and +// retrieves a configuration item from the Dapr configuration store. +// +// Use this with a values like: +// - CONNECTION_DAPRCONFIGURATIONSTORE_COMPONENTNAME +// - DAPR_GRPC_PORT +func DaprConfigurationStoreBinding(envParams map[string]string) BindingStatus { + // From https://docs.dapr.io/getting-started/quickstarts/configuration-quickstart/ + componentName := envParams["COMPONENTNAME"] + if componentName == "" { + log.Println("COMPONENTNAME is required") + return BindingStatus{false, "COMPONENTNAME is required"} + } + client, err := dapr.NewClientWithPort(os.Getenv("DAPR_GRPC_PORT")) + if err != nil { + log.Println("failed to create Dapr client - ", err.Error()) + return BindingStatus{false, "Failed to retrieve config item"} + } + ctx := context.Background() + _, err = client.GetConfigurationItem(ctx, componentName, "myconfig") + if err != nil { + log.Println("failed to get Dapr configuration item - ", componentName, " error - ", err.Error()) + } + defer client.Close() + return BindingStatus{true, "Config item retrieved"} +} diff --git a/test/magpiego/server.go b/test/magpiego/server.go index b506f95f15..064c0262f0 100644 --- a/test/magpiego/server.go +++ b/test/magpiego/server.go @@ -19,17 +19,18 @@ const ( ) var Providers = map[string]bindings.BindingProvider{ - "DAPRPUBSUB": bindings.DaprPubSubBinding, - "KEYVAULT": bindings.KeyVaultBinding, - "MONGODB": bindings.MongoBinding, - "SERVICEBUS": bindings.ServiceBusBinding, - "SQL": bindings.MicrosoftSqlBinding, - "REDIS": bindings.RedisBinding, - "DAPRSTATESTORE": bindings.DaprStateStoreBinding, - "RABBITMQ": bindings.RabbitMQBinding, - "DAPRSECRETSTORE": bindings.DaprSecretStoreBinding, - "DAPRHTTP": bindings.DaprHttpBinding, - "STORAGE": bindings.StorageBinding, + "DAPRPUBSUB": bindings.DaprPubSubBinding, + "KEYVAULT": bindings.KeyVaultBinding, + "MONGODB": bindings.MongoBinding, + "SERVICEBUS": bindings.ServiceBusBinding, + "SQL": bindings.MicrosoftSqlBinding, + "REDIS": bindings.RedisBinding, + "DAPRSTATESTORE": bindings.DaprStateStoreBinding, + "RABBITMQ": bindings.RabbitMQBinding, + "DAPRSECRETSTORE": bindings.DaprSecretStoreBinding, + "DAPRHTTP": bindings.DaprHttpBinding, + "STORAGE": bindings.StorageBinding, + "DAPRCONFIGURATIONSTORE": bindings.DaprConfigurationStoreBinding, } func startHTTPServer() error { diff --git a/test/testrecipes/test-bicep-recipes/dapr-configuration-store.bicep b/test/testrecipes/test-bicep-recipes/dapr-configuration-store.bicep new file mode 100644 index 0000000000..aad5d7d1a9 --- /dev/null +++ b/test/testrecipes/test-bicep-recipes/dapr-configuration-store.bicep @@ -0,0 +1,44 @@ +extension kubernetes with { + namespace: context.runtime.kubernetes.namespace + kubeConfig: '' +} as kubernetes + +param context object + +module redis '../../../test/testrecipes/modules/redis-selfhost.bicep' = { + name: 'redis-${uniqueString(context.resource.id)}' + params: { + name: 'redis-${uniqueString(context.resource.id)}' + namespace: context.runtime.kubernetes.namespace + application: context.application.name + } +} + +#disable-next-line BCP081 +resource dapr 'dapr.io/Component@v1alpha1' = { + metadata: { + name: context.resource.name + namespace: context.runtime.kubernetes.namespace + } + spec: { + type: 'configuration.redis' + metadata: [ + { + name: 'redisHost' + value: '${redis.outputs.host}:${redis.outputs.port}' + } + { + name: 'redisPassword' + value: '' + } + ] + version: 'v1' + } +} + +output result object = { + resources: redis.outputs.resources + values: { + componentName: dapr.metadata.name + } +} diff --git a/test/validation/shared.go b/test/validation/shared.go index ee6552d4ee..70206effd0 100644 --- a/test/validation/shared.go +++ b/test/validation/shared.go @@ -42,14 +42,15 @@ const ( VolumesResource = "applications.core/volumes" SecretStoresResource = "applications.core/secretStores" - RabbitMQQueuesResource = "applications.messaging/rabbitMQQueues" - DaprPubSubBrokersResource = "applications.dapr/pubSubBrokers" - DaprSecretStoresResource = "applications.dapr/secretStores" - DaprStateStoresResource = "applications.dapr/stateStores" - MongoDatabasesResource = "applications.datastores/mongoDatabases" - RedisCachesResource = "applications.datastores/redisCaches" - SQLDatabasesResource = "applications.datastores/sqlDatabases" - ExtendersResource = "applications.core/extenders" + RabbitMQQueuesResource = "applications.messaging/rabbitMQQueues" + DaprPubSubBrokersResource = "applications.dapr/pubSubBrokers" + DaprSecretStoresResource = "applications.dapr/secretStores" + DaprStateStoresResource = "applications.dapr/stateStores" + DaprConfigurationStoresResource = "applications.dapr/configurationStores" + MongoDatabasesResource = "applications.datastores/mongoDatabases" + RedisCachesResource = "applications.datastores/redisCaches" + SQLDatabasesResource = "applications.datastores/sqlDatabases" + ExtendersResource = "applications.core/extenders" ) type RPResource struct { diff --git a/typespec/Applications.Dapr/configurationStores.tsp b/typespec/Applications.Dapr/configurationStores.tsp new file mode 100644 index 0000000000..01ece3f5ec --- /dev/null +++ b/typespec/Applications.Dapr/configurationStores.tsp @@ -0,0 +1,93 @@ +/* +Copyright 2023 The Radius Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import "@typespec/rest"; +import "@typespec/versioning"; +import "@typespec/openapi"; +import "@azure-tools/typespec-autorest"; +import "@azure-tools/typespec-azure-core"; +import "@azure-tools/typespec-azure-resource-manager"; +import "@azure-tools/typespec-providerhub"; + +import "../radius/v1/ucprootscope.tsp"; +import "../radius/v1/resources.tsp"; +import "./common.tsp"; +import "../radius/v1/trackedresource.tsp"; + +using TypeSpec.Http; +using TypeSpec.Rest; +using TypeSpec.Versioning; +using Autorest; +using Azure.Core; +using Azure.ResourceManager; +using OpenAPI; + +namespace Applications.Dapr; + +@doc("Dapr configuration store portable resource") +model DaprConfigurationStoreResource + is TrackedResourceRequired< + DaprConfigurationStoreProperties, + "DaprConfigurationStores" + > { + @doc("Configuration Store name") + @key("configurationStoreName") + @path + @segment("configurationStores") + name: ResourceNameString; +} + +@doc("Dapr configuration store portable resource properties") +model DaprConfigurationStoreProperties { + ...EnvironmentScopedResource; + ...DaprResourceProperties; + + @doc("A collection of references to resources associated with the configuration store") + resources?: ResourceReference[]; + + ...RecipeBaseProperties; +} + +@armResourceOperations +interface ConfigurationStores { + get is ArmResourceRead< + DaprConfigurationStoreResource, + UCPBaseParameters + >; + + createOrUpdate is ArmResourceCreateOrReplaceAsync< + DaprConfigurationStoreResource, + UCPBaseParameters + >; + + update is ArmResourcePatchAsync< + DaprConfigurationStoreResource, + DaprConfigurationStoreProperties, + UCPBaseParameters + >; + + delete is ArmResourceDeleteAsync< + DaprConfigurationStoreResource, + UCPBaseParameters + >; + + listByScope is ArmResourceListByParent< + DaprConfigurationStoreResource, + UCPBaseParameters, + "Scope", + "Scope" + >; +} diff --git a/typespec/Applications.Dapr/examples/2023-10-01-preview/ConfigurationStores_CreateOrUpdate.json b/typespec/Applications.Dapr/examples/2023-10-01-preview/ConfigurationStores_CreateOrUpdate.json new file mode 100644 index 0000000000..d3e7bcfd16 --- /dev/null +++ b/typespec/Applications.Dapr/examples/2023-10-01-preview/ConfigurationStores_CreateOrUpdate.json @@ -0,0 +1,77 @@ +{ + "operationId": "ConfigurationStores_CreateOrUpdate", + "title": "Create or update a ConfigurationStore resource", + "parameters": { + "rootScope": "/planes/radius/local/resourceGroups/testGroup", + "ConfigurationStoreName": "configstore0", + "api-version": "2023-10-01-preview", + "ConfigurationStoreParameters": { + "location": "West US", + "properties": { + "application": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/applications/testApplication", + "environment": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/environments/env0", + "resourceProvisioning": "manual", + "resources": [ + { + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Microsoft.AppConfiguration/configurationStores/testappconfig" + } + ], + "type": "configuration.azure.appconfig", + "version": "v1", + "metadata": { + "foo": "bar" + } + } + } + }, + "responses": { + "200": { + "body": { + "id": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Dapr/configurationStores/configstore0", + "name": "configstore0", + "type": "Applications.Dapr/configurationStores", + "location": "West US", + "properties": { + "provisioningState": "Succeeded", + "application": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/applications/testApplication", + "environment": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/environments/env0", + "resourceProvisioning": "manual", + "resources": [ + { + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Microsoft.AppConfiguration/configurationStores/testappconfig" + } + ], + "type": "configuration.azure.appconfig", + "version": "v1", + "metadata": { + "foo": "bar" + } + } + } + }, + "201": { + "body": { + "id": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Dapr/configurationStores/configstore0", + "name": "configstore0", + "type": "Applications.Dapr/configurationStores", + "location": "West US", + "properties": { + "provisioningState": "Accepted", + "application": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/applications/testApplication", + "environment": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/environments/env0", + "resourceProvisioning": "manual", + "resources": [ + { + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Microsoft.AppConfiguration/configurationStores/testappconfig" + } + ], + "type": "configuration.azure.appconfig", + "version": "v1", + "metadata": { + "foo": "bar" + } + } + } + } + } +} \ No newline at end of file diff --git a/typespec/Applications.Dapr/examples/2023-10-01-preview/ConfigurationStores_CreateOrUpdateWithRecipe.json b/typespec/Applications.Dapr/examples/2023-10-01-preview/ConfigurationStores_CreateOrUpdateWithRecipe.json new file mode 100644 index 0000000000..1e2a605fbf --- /dev/null +++ b/typespec/Applications.Dapr/examples/2023-10-01-preview/ConfigurationStores_CreateOrUpdateWithRecipe.json @@ -0,0 +1,66 @@ +{ + "operationId": "ConfigurationStores_CreateOrUpdate", + "title": "Create or update a configurationStore resource with recipe", + "parameters": { + "rootScope": "/planes/radius/local/resourceGroups/testGroup", + "configurationStoreName": "configstore0", + "api-version": "2023-10-01-preview", + "configurationStoreParameters": { + "location": "West US", + "properties": { + "application": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/applications/testApplication", + "environment": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/environments/env0", + "recipe": { + "name": "configstore-test", + "parameters": { + "port": 6081 + } + } + } + } + }, + "responses": { + "200": { + "body": { + "id": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Dapr/configurationStores/configstore0", + "name": "configstore0", + "type": "Applications.Dapr/configurationStores", + "location": "West US", + "properties": { + "provisioningState": "Succeeded", + "application": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/applications/testApplication", + "environment": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/environments/env0", + "resourceProvisioning": "recipe", + "type": "configuration.azure.appconfig", + "recipe": { + "name": "configstore-test", + "parameters": { + "port": 6081 + } + } + } + } + }, + "201": { + "body": { + "id": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Dapr/configurationStores/configstore0", + "name": "configstore0", + "type": "Applications.Dapr/configurationStores", + "location": "West US", + "properties": { + "provisioningState": "Accepted", + "application": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/applications/testApplication", + "environment": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/environments/env0", + "resourceProvisioning": "recipe", + "type": "configuration.azure.appconfig", + "recipe": { + "name": "configstore-test", + "parameters": { + "port": 6081 + } + } + } + } + } + } +} \ No newline at end of file diff --git a/typespec/Applications.Dapr/examples/2023-10-01-preview/ConfigurationStores_Delete.json b/typespec/Applications.Dapr/examples/2023-10-01-preview/ConfigurationStores_Delete.json new file mode 100644 index 0000000000..63a568f43d --- /dev/null +++ b/typespec/Applications.Dapr/examples/2023-10-01-preview/ConfigurationStores_Delete.json @@ -0,0 +1,14 @@ +{ + "operationId": "ConfigurationStores_Delete", + "title": "Delete a ConfigurationStore resource", + "parameters": { + "rootScope": "/planes/radius/local/resourceGroups/testGroup/resourceGroups/testGroup", + "pubSubBrokerName": "configstore0", + "api-version": "2023-10-01-preview" + }, + "responses": { + "200": {}, + "202": {}, + "204": {} + } +} \ No newline at end of file diff --git a/typespec/Applications.Dapr/examples/2023-10-01-preview/ConfigurationStores_Get.json b/typespec/Applications.Dapr/examples/2023-10-01-preview/ConfigurationStores_Get.json new file mode 100644 index 0000000000..964aca70fe --- /dev/null +++ b/typespec/Applications.Dapr/examples/2023-10-01-preview/ConfigurationStores_Get.json @@ -0,0 +1,35 @@ +{ + "operationId": "ConfigurationStores_Get", + "title": "Get a PubSubBroker resource", + "parameters": { + "rootScope": "/planes/radius/local/resourceGroups/testGroup", + "api-version": "2023-10-01-preview", + "pubSubBrokerName": "configstore0" + }, + "responses": { + "200": { + "body": { + "id": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Dapr/configurationStores/configstore0", + "name": "configstore0", + "type": "Applications.Dapr/configurationStores", + "location": "global", + "properties": { + "provisioningState": "Succeeded", + "application": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/applications/testApplication", + "environment": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/environments/env0", + "resourceProvisioning": "manual", + "resources": [ + { + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Microsoft.AppConfiguration/configurationStores/testappconfig" + } + ], + "type": "configuration.azure.appconfig", + "version": "v1", + "metadata": { + "foo": "bar" + } + } + } + } + } +} \ No newline at end of file diff --git a/typespec/Applications.Dapr/examples/2023-10-01-preview/ConfigurationStores_List.json b/typespec/Applications.Dapr/examples/2023-10-01-preview/ConfigurationStores_List.json new file mode 100644 index 0000000000..c792841bd7 --- /dev/null +++ b/typespec/Applications.Dapr/examples/2023-10-01-preview/ConfigurationStores_List.json @@ -0,0 +1,73 @@ +{ + "operationId": "ConfigurationStores_ListByScope", + "title": "List a ConfigurationStore resource by resource group", + "parameters": { + "rootScope": "/planes/radius/local/resourceGroups/testGroup", + "api-version": "2023-10-01-preview" + }, + "responses": { + "200": { + "body": { + "value": [ + { + "id": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Dapr/configurationStores/configstore0", + "name": "configstore0", + "type": "Applications.Dapr/configurationStores", + "location": "global", + "properties": { + "provisioningState": "Succeeded", + "application": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/applications/testApplication", + "environment": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/environments/env0", + "resourceProvisioning": "manual", + "resources": [ + { + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Microsoft.AppConfiguration/configurationStores/testappconfig" + } + ], + "type": "configuration.azure.appconfig", + "version": "v1", + "metadata": { + "foo": "bar" + } + } + }, + { + "id": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Dapr/configurationStores/configstore1", + "name": "configstore1", + "type": "Applications.Dapr/configurationStores", + "location": "global", + "properties": { + "provisioningState": "Succeeded", + "application": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/applications/testApplication", + "environment": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/environments/env0", + "resourceProvisioning": "manual", + "type": "configuration.redis", + "version": "v1", + "metadata": { + "foo": "bar" + } + } + }, + { + "id": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Dapr/configurationStores/configstore2", + "name": "configstore2", + "type": "Applications.Dapr/configurationStores", + "location": "global", + "properties": { + "provisioningState": "Succeeded", + "application": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/applications/testApplication", + "environment": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/environments/env0", + "recipe": { + "name": "config-test", + "parameters": { + "port": 6081 + } + } + } + } + ], + "nextLink": "https://serviceRoot/planes/radius/local/resourceGroups/testGroup/providers/Applications.Dapr/configurationStores?api-version=2023-10-01-preview&$skipToken=X'12345'" + } + } + } +} \ No newline at end of file diff --git a/typespec/Applications.Dapr/examples/2023-10-01-preview/ConfigurationStores_ListByRootScope.json b/typespec/Applications.Dapr/examples/2023-10-01-preview/ConfigurationStores_ListByRootScope.json new file mode 100644 index 0000000000..377dfe293c --- /dev/null +++ b/typespec/Applications.Dapr/examples/2023-10-01-preview/ConfigurationStores_ListByRootScope.json @@ -0,0 +1,56 @@ +{ + "operationId": "ConfigurationStores_ListByScope", + "title": "List a ConfigurationStores resource by rootScope", + "parameters": { + "rootScope": "/planes/radius/local", + "api-version": "2023-10-01-preview" + }, + "responses": { + "200": { + "body": { + "value": [ + { + "id": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Dapr/configurationStores/configstore0", + "name": "configstore0", + "type": "Applications.Dapr/configurationStores", + "location": "global", + "properties": { + "provisioningState": "Succeeded", + "application": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/applications/testApplication", + "environment": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/environments/env0", + "resourceProvisioning": "manual", + "resources": [ + { + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Microsoft.AppConfiguration/configurationStores/testappconfig" + } + ], + "type": "configuration.azure.appconfig", + "version": "v1", + "metadata": { + "foo": "bar" + } + } + }, + { + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup1/providers/Applications.Dapr/configurationStores/configstore1", + "name": "configstore1", + "type": "Applications.Dapr/configurationStores", + "location": "global", + "properties": { + "provisioningState": "Succeeded", + "application": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/applications/testApplication", + "environment": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/environments/env0", + "resourceProvisioning": "manual", + "type": "configuration.redis", + "version": "v1", + "metadata": { + "foo": "bar" + } + } + } + ], + "nextLink": "https://serviceRoot/planes/radius/local/providers/Applications.Dapr/configurationStores?api-version=2023-10-01-preview&$skipToken=X'12345'" + } + } + } +} \ No newline at end of file diff --git a/typespec/Applications.Dapr/examples/2023-10-01-preview/ConfigurationStores_Update.json b/typespec/Applications.Dapr/examples/2023-10-01-preview/ConfigurationStores_Update.json new file mode 100644 index 0000000000..39ee6e7a62 --- /dev/null +++ b/typespec/Applications.Dapr/examples/2023-10-01-preview/ConfigurationStores_Update.json @@ -0,0 +1,77 @@ +{ + "operationId": "ConfigurationStores_Update", + "title": "Update a ConfigurationStore resource", + "parameters": { + "rootScope": "/planes/radius/local/resourceGroups/testGroup", + "ConfigurationStoreName": "configstore0", + "api-version": "2023-10-01-preview", + "ConfigurationStoreParameters": { + "location": "West US", + "properties": { + "application": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/applications/testApplication", + "environment": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/environments/env0", + "resourceProvisioning": "manual", + "resources": [ + { + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Microsoft.AppConfiguration/configurationStores/testappconfig" + } + ], + "type": "configuration.azure.appconfig", + "version": "v1", + "metadata": { + "foo": "bar" + } + } + } + }, + "responses": { + "200": { + "body": { + "id": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Dapr/configurationStores/configstore0", + "name": "configstore0", + "type": "Applications.Dapr/configurationStores", + "location": "West US", + "properties": { + "provisioningState": "Succeeded", + "application": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/applications/testApplication", + "environment": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/environments/env0", + "resourceProvisioning": "manual", + "resources": [ + { + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Microsoft.AppConfiguration/configurationStores/testappconfig" + } + ], + "type": "configuration.azure.appconfig", + "version": "v1", + "metadata": { + "foo": "bar" + } + } + } + }, + "201": { + "body": { + "id": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Dapr/configurationStores/configstore0", + "name": "configstore0", + "type": "Applications.Dapr/configurationStores", + "location": "West US", + "properties": { + "provisioningState": "Accepted", + "application": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/applications/testApplication", + "environment": "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/environments/env0", + "resourceProvisioning": "manual", + "resources": [ + { + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Microsoft.AppConfiguration/configurationStores/testappconfig" + } + ], + "type": "configuration.azure.appconfig", + "version": "v1", + "metadata": { + "foo": "bar" + } + } + } + } + } +} \ No newline at end of file diff --git a/typespec/Applications.Dapr/main.tsp b/typespec/Applications.Dapr/main.tsp index 11c0f2f608..136db3e46f 100644 --- a/typespec/Applications.Dapr/main.tsp +++ b/typespec/Applications.Dapr/main.tsp @@ -20,6 +20,7 @@ import "@azure-tools/typespec-azure-resource-manager"; import "./secretStores.tsp"; import "./stateStores.tsp"; import "./pubSubBrokers.tsp"; +import "./configurationStores.tsp"; using TypeSpec.Versioning; using Azure.ResourceManager;