From 8c415655c06dc64ca66602cad88dc6c88e336391 Mon Sep 17 00:00:00 2001 From: Vishwanath Hiremath Date: Wed, 7 Feb 2024 00:50:55 -0800 Subject: [PATCH 01/22] initial commit Signed-off-by: Vishwanath Hiremath --- .../2023-10-01-preview/types.json | 2 +- hack/bicep-types-radius/generated/index.json | 2 +- .../environment_conversion.go | 33 ++++ .../v20231001preview/zz_generated_models.go | 40 +++++ .../zz_generated_models_serde.go | 147 ++++++++++++++++++ pkg/corerp/datamodel/environment.go | 31 +++- pkg/recipes/configloader/environment.go | 5 +- pkg/recipes/controllerconfig/config.go | 2 +- pkg/recipes/driver/terraform.go | 5 +- pkg/recipes/terraform/execute.go | 34 +++- pkg/recipes/types.go | 2 + .../preview/2023-10-01-preview/openapi.json | 65 ++++++++ typespec/Applications.Core/environments.tsp | 33 ++++ 13 files changed, 382 insertions(+), 19 deletions(-) diff --git a/hack/bicep-types-radius/generated/applications/applications.core/2023-10-01-preview/types.json b/hack/bicep-types-radius/generated/applications/applications.core/2023-10-01-preview/types.json index 2655f064ba..4ac34c99dc 100644 --- a/hack/bicep-types-radius/generated/applications/applications.core/2023-10-01-preview/types.json +++ b/hack/bicep-types-radius/generated/applications/applications.core/2023-10-01-preview/types.json @@ -1 +1 @@ -[{"1":{"Kind":1}},{"1":{"Kind":2}},{"1":{"Kind":3}},{"1":{"Kind":4}},{"1":{"Kind":5}},{"1":{"Kind":6}},{"1":{"Kind":7}},{"1":{"Kind":8}},{"6":{"Value":"Applications.Core/applications"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/applications","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":8,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":9,"Flags":10,"Description":"The resource api version"},"properties":{"Type":11,"Flags":1,"Description":"Application properties"},"tags":{"Type":46,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ApplicationProperties","Properties":{"provisioningState":{"Type":19,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"environment":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"extensions":{"Type":34,"Flags":0,"Description":"The application extension."},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[12,13,14,15,16,17,18]}},{"7":{"Name":"Extension","Discriminator":"kind","BaseProperties":{},"Elements":{"daprSidecar":21,"kubernetesMetadata":26,"kubernetesNamespace":30,"manualScaling":32}}},{"2":{"Name":"DaprSidecarExtension","Properties":{"appPort":{"Type":3,"Flags":0,"Description":"The Dapr appPort. Specifies the internal listening port for the application to handle requests from the Dapr sidecar."},"appId":{"Type":4,"Flags":1,"Description":"The Dapr appId. Specifies the identifier used by Dapr for service invocation."},"config":{"Type":4,"Flags":0,"Description":"Specifies the Dapr configuration to use for the resource."},"protocol":{"Type":24,"Flags":0,"Description":"The Dapr sidecar extension protocol"},"kind":{"Type":25,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"http"}},{"6":{"Value":"grpc"}},{"5":{"Elements":[22,23]}},{"6":{"Value":"daprSidecar"}},{"2":{"Name":"KubernetesMetadataExtension","Properties":{"annotations":{"Type":27,"Flags":0,"Description":"Annotations to be applied to the Kubernetes resources output by the resource"},"labels":{"Type":28,"Flags":0,"Description":"Labels to be applied to the Kubernetes resources output by the resource"},"kind":{"Type":29,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"2":{"Name":"KubernetesMetadataExtensionAnnotations","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"KubernetesMetadataExtensionLabels","Properties":{},"AdditionalProperties":4}},{"6":{"Value":"kubernetesMetadata"}},{"2":{"Name":"KubernetesNamespaceExtension","Properties":{"namespace":{"Type":4,"Flags":1,"Description":"The namespace of the application environment."},"kind":{"Type":31,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"kubernetesNamespace"}},{"2":{"Name":"ManualScalingExtension","Properties":{"replicas":{"Type":3,"Flags":1,"Description":"Replica count."},"kind":{"Type":33,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"manualScaling"}},{"3":{"ItemType":20}},{"2":{"Name":"ResourceStatus","Properties":{"compute":{"Type":36,"Flags":0,"Description":"Represents backing compute resource"},"recipe":{"Type":43,"Flags":2,"Description":"Recipe status at deployment time for a resource."},"outputResources":{"Type":45,"Flags":0,"Description":"Properties of an output resource"}}}},{"7":{"Name":"EnvironmentCompute","Discriminator":"kind","BaseProperties":{"resourceId":{"Type":4,"Flags":0,"Description":"The resource id of the compute resource for application environment."},"identity":{"Type":37,"Flags":0,"Description":"IdentitySettings is the external identity setting."}},"Elements":{"kubernetes":41}}},{"2":{"Name":"IdentitySettings","Properties":{"kind":{"Type":40,"Flags":1,"Description":"IdentitySettingKind is the kind of supported external identity setting"},"oidcIssuer":{"Type":4,"Flags":0,"Description":"The URI for your compute platform's OIDC issuer"},"resource":{"Type":4,"Flags":0,"Description":"The resource ID of the provisioned identity"}}}},{"6":{"Value":"undefined"}},{"6":{"Value":"azure.com.workload"}},{"5":{"Elements":[38,39]}},{"2":{"Name":"KubernetesCompute","Properties":{"namespace":{"Type":4,"Flags":1,"Description":"The namespace to use for the environment."},"kind":{"Type":42,"Flags":1,"Description":"Discriminator property for EnvironmentCompute."}}}},{"6":{"Value":"kubernetes"}},{"2":{"Name":"RecipeStatus","Properties":{"templateKind":{"Type":4,"Flags":1,"Description":"TemplateKind is the kind of the recipe template used by the portable resource upon deployment."},"templatePath":{"Type":4,"Flags":1,"Description":"TemplatePath is the path of the recipe consumed by the portable resource upon deployment."},"templateVersion":{"Type":4,"Flags":0,"Description":"TemplateVersion is the version number of the template."}}}},{"2":{"Name":"OutputResource","Properties":{"localId":{"Type":4,"Flags":0,"Description":"The logical identifier scoped to the owning Radius resource. This is only needed or used when a resource has a dependency relationship. LocalIDs do not have any particular format or meaning beyond being compared to determine dependency relationships."},"id":{"Type":4,"Flags":0,"Description":"The UCP resource ID of the underlying resource."},"radiusManaged":{"Type":2,"Flags":0,"Description":"Determines whether Radius manages the lifecycle of the underlying resource."}}}},{"3":{"ItemType":44}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"SystemData","Properties":{"createdBy":{"Type":4,"Flags":0,"Description":"The identity that created the resource."},"createdByType":{"Type":52,"Flags":0,"Description":"The type of identity that created the resource."},"createdAt":{"Type":4,"Flags":0,"Description":"The timestamp of resource creation (UTC)."},"lastModifiedBy":{"Type":4,"Flags":0,"Description":"The identity that last modified the resource."},"lastModifiedByType":{"Type":57,"Flags":0,"Description":"The type of identity that created the resource."},"lastModifiedAt":{"Type":4,"Flags":0,"Description":"The timestamp of resource last modification (UTC)"}}}},{"6":{"Value":"User"}},{"6":{"Value":"Application"}},{"6":{"Value":"ManagedIdentity"}},{"6":{"Value":"Key"}},{"5":{"Elements":[48,49,50,51]}},{"6":{"Value":"User"}},{"6":{"Value":"Application"}},{"6":{"Value":"ManagedIdentity"}},{"6":{"Value":"Key"}},{"5":{"Elements":[53,54,55,56]}},{"4":{"Name":"Applications.Core/applications@2023-10-01-preview","ScopeType":0,"Body":10}},{"6":{"Value":"Applications.Core/containers"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/containers","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":59,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":60,"Flags":10,"Description":"The resource api version"},"properties":{"Type":62,"Flags":1,"Description":"Container properties"},"tags":{"Type":122,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ContainerProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":70,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"container":{"Type":71,"Flags":1,"Description":"Definition of a container"},"connections":{"Type":108,"Flags":0,"Description":"Specifies a connection to another resource."},"identity":{"Type":37,"Flags":0,"Description":"IdentitySettings is the external identity setting."},"extensions":{"Type":109,"Flags":0,"Description":"Extensions spec of the resource"},"resourceProvisioning":{"Type":112,"Flags":0,"Description":"Specifies how the underlying service/resource is provisioned and managed. Available values are 'internal', where Radius manages the lifecycle of the resource internally, and 'manual', where a user manages the resource."},"resources":{"Type":114,"Flags":0,"Description":"A collection of references to resources associated with the container"},"restartPolicy":{"Type":118,"Flags":0,"Description":"Restart policy for the container"},"runtimes":{"Type":119,"Flags":0,"Description":"The properties for runtime configuration"}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[63,64,65,66,67,68,69]}},{"2":{"Name":"Container","Properties":{"image":{"Type":4,"Flags":1,"Description":"The registry and image to download and run in your container"},"imagePullPolicy":{"Type":75,"Flags":0,"Description":"The image pull policy for the container"},"env":{"Type":76,"Flags":0,"Description":"environment"},"ports":{"Type":81,"Flags":0,"Description":"container ports"},"readinessProbe":{"Type":82,"Flags":0,"Description":"Properties for readiness/liveness probe"},"livenessProbe":{"Type":82,"Flags":0,"Description":"Properties for readiness/liveness probe"},"volumes":{"Type":101,"Flags":0,"Description":"container volumes"},"command":{"Type":102,"Flags":0,"Description":"Entrypoint array. Overrides the container image's ENTRYPOINT"},"args":{"Type":103,"Flags":0,"Description":"Arguments to the entrypoint. Overrides the container image's CMD"},"workingDir":{"Type":4,"Flags":0,"Description":"Working directory for the container"}}}},{"6":{"Value":"Always"}},{"6":{"Value":"IfNotPresent"}},{"6":{"Value":"Never"}},{"5":{"Elements":[72,73,74]}},{"2":{"Name":"ContainerEnv","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"ContainerPortProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"protocol":{"Type":80,"Flags":0,"Description":"The protocol in use by the port"},"provides":{"Type":4,"Flags":0,"Description":"Specifies a route provided by this port"},"scheme":{"Type":4,"Flags":0,"Description":"Specifies the URL scheme of the communication protocol. Consumers can use the scheme to construct a URL. The value defaults to 'http' or 'https' depending on the port value"},"port":{"Type":3,"Flags":0,"Description":"Specifies the port that will be exposed by this container. Must be set when value different from containerPort is desired"}}}},{"6":{"Value":"TCP"}},{"6":{"Value":"UDP"}},{"5":{"Elements":[78,79]}},{"2":{"Name":"ContainerPorts","Properties":{},"AdditionalProperties":77}},{"7":{"Name":"HealthProbeProperties","Discriminator":"kind","BaseProperties":{"initialDelaySeconds":{"Type":3,"Flags":0,"Description":"Initial delay in seconds before probing for readiness/liveness"},"failureThreshold":{"Type":3,"Flags":0,"Description":"Threshold number of times the probe fails after which a failure would be reported"},"periodSeconds":{"Type":3,"Flags":0,"Description":"Interval for the readiness/liveness probe in seconds"},"timeoutSeconds":{"Type":3,"Flags":0,"Description":"Number of seconds after which the readiness/liveness probe times out. Defaults to 5 seconds"}},"Elements":{"exec":83,"httpGet":85,"tcp":88}}},{"2":{"Name":"ExecHealthProbeProperties","Properties":{"command":{"Type":4,"Flags":1,"Description":"Command to execute to probe readiness/liveness"},"kind":{"Type":84,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"6":{"Value":"exec"}},{"2":{"Name":"HttpGetHealthProbeProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"path":{"Type":4,"Flags":1,"Description":"The route to make the HTTP request on"},"headers":{"Type":86,"Flags":0,"Description":"Custom HTTP headers to add to the get request"},"kind":{"Type":87,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"2":{"Name":"HttpGetHealthProbePropertiesHeaders","Properties":{},"AdditionalProperties":4}},{"6":{"Value":"httpGet"}},{"2":{"Name":"TcpHealthProbeProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"kind":{"Type":89,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"6":{"Value":"tcp"}},{"7":{"Name":"Volume","Discriminator":"kind","BaseProperties":{"mountPath":{"Type":4,"Flags":0,"Description":"The path where the volume is mounted"}},"Elements":{"ephemeral":91,"persistent":96}}},{"2":{"Name":"EphemeralVolume","Properties":{"managedStore":{"Type":94,"Flags":1,"Description":"The managed store for the ephemeral volume"},"kind":{"Type":95,"Flags":1,"Description":"Discriminator property for Volume."}}}},{"6":{"Value":"memory"}},{"6":{"Value":"disk"}},{"5":{"Elements":[92,93]}},{"6":{"Value":"ephemeral"}},{"2":{"Name":"PersistentVolume","Properties":{"permission":{"Type":99,"Flags":0,"Description":"The persistent volume permission"},"source":{"Type":4,"Flags":1,"Description":"The source of the volume"},"kind":{"Type":100,"Flags":1,"Description":"Discriminator property for Volume."}}}},{"6":{"Value":"read"}},{"6":{"Value":"write"}},{"5":{"Elements":[97,98]}},{"6":{"Value":"persistent"}},{"2":{"Name":"ContainerVolumes","Properties":{},"AdditionalProperties":90}},{"3":{"ItemType":4}},{"3":{"ItemType":4}},{"2":{"Name":"ConnectionProperties","Properties":{"source":{"Type":4,"Flags":1,"Description":"The source of the connection"},"disableDefaultEnvVars":{"Type":2,"Flags":0,"Description":"default environment variable override"},"iam":{"Type":105,"Flags":0,"Description":"IAM properties"}}}},{"2":{"Name":"IamProperties","Properties":{"kind":{"Type":106,"Flags":1,"Description":"The kind of IAM provider to configure"},"roles":{"Type":107,"Flags":0,"Description":"RBAC permissions to be assigned on the source resource"}}}},{"6":{"Value":"azure"}},{"3":{"ItemType":4}},{"2":{"Name":"ContainerPropertiesConnections","Properties":{},"AdditionalProperties":104}},{"3":{"ItemType":20}},{"6":{"Value":"internal"}},{"6":{"Value":"manual"}},{"5":{"Elements":[110,111]}},{"2":{"Name":"ResourceReference","Properties":{"id":{"Type":4,"Flags":1,"Description":"Resource id of an existing resource"}}}},{"3":{"ItemType":113}},{"6":{"Value":"Always"}},{"6":{"Value":"OnFailure"}},{"6":{"Value":"Never"}},{"5":{"Elements":[115,116,117]}},{"2":{"Name":"RuntimesProperties","Properties":{"kubernetes":{"Type":120,"Flags":0,"Description":"The runtime configuration properties for Kubernetes"}}}},{"2":{"Name":"KubernetesRuntimeProperties","Properties":{"base":{"Type":4,"Flags":0,"Description":"The serialized YAML manifest which represents the base Kubernetes resources to deploy, such as Deployment, Service, ServiceAccount, Secrets, and ConfigMaps."},"pod":{"Type":121,"Flags":0,"Description":"A strategic merge patch that will be applied to the PodSpec object when this container is being deployed."}}}},{"2":{"Name":"KubernetesPodSpec","Properties":{},"AdditionalProperties":0}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/containers@2023-10-01-preview","ScopeType":0,"Body":61}},{"6":{"Value":"Applications.Core/environments"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/environments","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":124,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":125,"Flags":10,"Description":"The resource api version"},"properties":{"Type":127,"Flags":1,"Description":"Environment properties"},"tags":{"Type":147,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"EnvironmentProperties","Properties":{"provisioningState":{"Type":135,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"compute":{"Type":36,"Flags":1,"Description":"Represents backing compute resource"},"providers":{"Type":136,"Flags":0,"Description":"The Cloud providers configuration"},"simulated":{"Type":2,"Flags":0,"Description":"Simulated environment."},"recipes":{"Type":145,"Flags":0,"Description":"Specifies Recipes linked to the Environment."},"extensions":{"Type":146,"Flags":0,"Description":"The environment extension."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[128,129,130,131,132,133,134]}},{"2":{"Name":"Providers","Properties":{"azure":{"Type":137,"Flags":0,"Description":"The Azure cloud provider definition"},"aws":{"Type":138,"Flags":0,"Description":"The AWS cloud provider definition"}}}},{"2":{"Name":"ProvidersAzure","Properties":{"scope":{"Type":4,"Flags":1,"Description":"Target scope for Azure resources to be deployed into. For example: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup'"}}}},{"2":{"Name":"ProvidersAws","Properties":{"scope":{"Type":4,"Flags":1,"Description":"Target scope for AWS resources to be deployed into. For example: '/planes/aws/aws/accounts/000000000000/regions/us-west-2'"}}}},{"7":{"Name":"RecipeProperties","Discriminator":"templateKind","BaseProperties":{"templatePath":{"Type":4,"Flags":1,"Description":"Path to the template provided by the recipe. Currently only link to Azure Container Registry is supported."},"parameters":{"Type":0,"Flags":0,"Description":"Any object"}},"Elements":{"bicep":140,"terraform":142}}},{"2":{"Name":"BicepRecipeProperties","Properties":{"plainHttp":{"Type":2,"Flags":0,"Description":"Connect to the Bicep registry using HTTP (not-HTTPS). This should be used when the registry is known not to support HTTPS, for example in a locally-hosted registry. Defaults to false (use HTTPS/TLS)."},"templateKind":{"Type":141,"Flags":1,"Description":"Discriminator property for RecipeProperties."}}}},{"6":{"Value":"bicep"}},{"2":{"Name":"TerraformRecipeProperties","Properties":{"templateVersion":{"Type":4,"Flags":0,"Description":"Version of the template to deploy. For Terraform recipes using a module registry this is required, but must be omitted for other module sources."},"templateKind":{"Type":143,"Flags":1,"Description":"Discriminator property for RecipeProperties."}}}},{"6":{"Value":"terraform"}},{"2":{"Name":"DictionaryOfRecipeProperties","Properties":{},"AdditionalProperties":139}},{"2":{"Name":"EnvironmentPropertiesRecipes","Properties":{},"AdditionalProperties":144}},{"3":{"ItemType":20}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/environments@2023-10-01-preview","ScopeType":0,"Body":126}},{"6":{"Value":"Applications.Core/extenders"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/extenders","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":149,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":150,"Flags":10,"Description":"The resource api version"},"properties":{"Type":152,"Flags":1,"Description":"ExtenderResource portable resource properties"},"tags":{"Type":165,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ExtenderProperties","Properties":{"environment":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the environment that the portable resource is linked to"},"application":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the application that the portable resource is consumed by (if applicable)"},"provisioningState":{"Type":160,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"secrets":{"Type":0,"Flags":0,"Description":"Any object"},"recipe":{"Type":161,"Flags":0,"Description":"The recipe used to automatically deploy underlying infrastructure for a portable resource"},"resourceProvisioning":{"Type":164,"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."}},"AdditionalProperties":0}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[153,154,155,156,157,158,159]}},{"2":{"Name":"Recipe","Properties":{"name":{"Type":4,"Flags":1,"Description":"The name of the recipe within the environment to use"},"parameters":{"Type":0,"Flags":0,"Description":"Any object"}}}},{"6":{"Value":"recipe"}},{"6":{"Value":"manual"}},{"5":{"Elements":[162,163]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/extenders@2023-10-01-preview","ScopeType":0,"Body":151}},{"6":{"Value":"Applications.Core/gateways"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/gateways","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":167,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":168,"Flags":10,"Description":"The resource api version"},"properties":{"Type":170,"Flags":1,"Description":"Gateway properties"},"tags":{"Type":186,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"GatewayProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":178,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"internal":{"Type":2,"Flags":0,"Description":"Sets Gateway to not be exposed externally (no public IP address associated). Defaults to false (exposed to internet)."},"hostname":{"Type":179,"Flags":0,"Description":"Declare hostname information for the Gateway. Leaving the hostname empty auto-assigns one: mygateway.myapp.PUBLICHOSTNAMEORIP.nip.io."},"routes":{"Type":181,"Flags":1,"Description":"Routes attached to this Gateway"},"tls":{"Type":182,"Flags":0,"Description":"TLS configuration definition for Gateway resource."},"url":{"Type":4,"Flags":2,"Description":"URL of the gateway resource. Readonly"}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[171,172,173,174,175,176,177]}},{"2":{"Name":"GatewayHostname","Properties":{"prefix":{"Type":4,"Flags":0,"Description":"Specify a prefix for the hostname: myhostname.myapp.PUBLICHOSTNAMEORIP.nip.io. Mutually exclusive with 'fullyQualifiedHostname' and will be overridden if both are defined."},"fullyQualifiedHostname":{"Type":4,"Flags":0,"Description":"Specify a fully-qualified domain name: myapp.mydomain.com. Mutually exclusive with 'prefix' and will take priority if both are defined."}}}},{"2":{"Name":"GatewayRoute","Properties":{"path":{"Type":4,"Flags":0,"Description":"The path to match the incoming request path on. Ex - /myservice."},"destination":{"Type":4,"Flags":0,"Description":"The HttpRoute to route to. Ex - myserviceroute.id."},"replacePrefix":{"Type":4,"Flags":0,"Description":"Optionally update the prefix when sending the request to the service. Ex - replacePrefix: '/' and path: '/myservice' will transform '/myservice/myroute' to '/myroute'"}}}},{"3":{"ItemType":180}},{"2":{"Name":"GatewayTls","Properties":{"sslPassthrough":{"Type":2,"Flags":0,"Description":"If true, gateway lets the https traffic sslPassthrough to the backend servers for decryption."},"minimumProtocolVersion":{"Type":185,"Flags":0,"Description":"Tls Minimum versions for Gateway resource."},"certificateFrom":{"Type":4,"Flags":0,"Description":"The resource id for the secret containing the TLS certificate and key for the gateway."}}}},{"6":{"Value":"1.2"}},{"6":{"Value":"1.3"}},{"5":{"Elements":[183,184]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/gateways@2023-10-01-preview","ScopeType":0,"Body":169}},{"6":{"Value":"Applications.Core/httpRoutes"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/httpRoutes","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":188,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":189,"Flags":10,"Description":"The resource api version"},"properties":{"Type":191,"Flags":1,"Description":"HTTPRoute properties"},"tags":{"Type":200,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"HttpRouteProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":199,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"hostname":{"Type":4,"Flags":0,"Description":"The internal hostname accepting traffic for the HTTP Route. Readonly."},"port":{"Type":3,"Flags":0,"Description":"The port number for the HTTP Route. Defaults to 80. Readonly."},"scheme":{"Type":4,"Flags":2,"Description":"The scheme used for traffic. Readonly."},"url":{"Type":4,"Flags":2,"Description":"A stable URL that that can be used to route traffic to a resource. Readonly."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[192,193,194,195,196,197,198]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/httpRoutes@2023-10-01-preview","ScopeType":0,"Body":190}},{"6":{"Value":"Applications.Core/secretStores"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/secretStores","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":202,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":203,"Flags":10,"Description":"The resource api version"},"properties":{"Type":205,"Flags":1,"Description":"The properties of SecretStore"},"tags":{"Type":223,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"SecretStoreProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":213,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"type":{"Type":216,"Flags":0,"Description":"The type of SecretStore data"},"data":{"Type":222,"Flags":1,"Description":"An object to represent key-value type secrets"},"resource":{"Type":4,"Flags":0,"Description":"The resource id of external secret store."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[206,207,208,209,210,211,212]}},{"6":{"Value":"generic"}},{"6":{"Value":"certificate"}},{"5":{"Elements":[214,215]}},{"2":{"Name":"SecretValueProperties","Properties":{"encoding":{"Type":220,"Flags":0,"Description":"The type of SecretValue Encoding"},"value":{"Type":4,"Flags":0,"Description":"The value of secret."},"valueFrom":{"Type":221,"Flags":0,"Description":"The Secret value source properties"}}}},{"6":{"Value":"raw"}},{"6":{"Value":"base64"}},{"5":{"Elements":[218,219]}},{"2":{"Name":"ValueFromProperties","Properties":{"name":{"Type":4,"Flags":1,"Description":"The name of the referenced secret."},"version":{"Type":4,"Flags":0,"Description":"The version of the referenced secret."}}}},{"2":{"Name":"SecretStorePropertiesData","Properties":{},"AdditionalProperties":217}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/secretStores@2023-10-01-preview","ScopeType":0,"Body":204}},{"6":{"Value":"Applications.Core/volumes"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/volumes","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":225,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":226,"Flags":10,"Description":"The resource api version"},"properties":{"Type":228,"Flags":1,"Description":"Volume properties"},"tags":{"Type":260,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"7":{"Name":"VolumeProperties","Discriminator":"kind","BaseProperties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":236,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."}},"Elements":{"azure.com.keyvault":237}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[229,230,231,232,233,234,235]}},{"2":{"Name":"AzureKeyVaultVolumeProperties","Properties":{"certificates":{"Type":250,"Flags":0,"Description":"The KeyVault certificates that this volume exposes"},"keys":{"Type":252,"Flags":0,"Description":"The KeyVault keys that this volume exposes"},"resource":{"Type":4,"Flags":1,"Description":"The ID of the keyvault to use for this volume resource"},"secrets":{"Type":258,"Flags":0,"Description":"The KeyVault secrets that this volume exposes"},"kind":{"Type":259,"Flags":1,"Description":"Discriminator property for VolumeProperties."}}}},{"2":{"Name":"CertificateObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"encoding":{"Type":242,"Flags":0,"Description":"Represents secret encodings"},"format":{"Type":245,"Flags":0,"Description":"Represents certificate formats"},"name":{"Type":4,"Flags":1,"Description":"The name of the certificate"},"certType":{"Type":249,"Flags":0,"Description":"Represents certificate types"},"version":{"Type":4,"Flags":0,"Description":"Certificate version"}}}},{"6":{"Value":"utf-8"}},{"6":{"Value":"hex"}},{"6":{"Value":"base64"}},{"5":{"Elements":[239,240,241]}},{"6":{"Value":"pem"}},{"6":{"Value":"pfx"}},{"5":{"Elements":[243,244]}},{"6":{"Value":"certificate"}},{"6":{"Value":"privatekey"}},{"6":{"Value":"publickey"}},{"5":{"Elements":[246,247,248]}},{"2":{"Name":"AzureKeyVaultVolumePropertiesCertificates","Properties":{},"AdditionalProperties":238}},{"2":{"Name":"KeyObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"name":{"Type":4,"Flags":1,"Description":"The name of the key"},"version":{"Type":4,"Flags":0,"Description":"Key version"}}}},{"2":{"Name":"AzureKeyVaultVolumePropertiesKeys","Properties":{},"AdditionalProperties":251}},{"2":{"Name":"SecretObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"encoding":{"Type":257,"Flags":0,"Description":"Represents secret encodings"},"name":{"Type":4,"Flags":1,"Description":"The name of the secret"},"version":{"Type":4,"Flags":0,"Description":"secret version"}}}},{"6":{"Value":"utf-8"}},{"6":{"Value":"hex"}},{"6":{"Value":"base64"}},{"5":{"Elements":[254,255,256]}},{"2":{"Name":"AzureKeyVaultVolumePropertiesSecrets","Properties":{},"AdditionalProperties":253}},{"6":{"Value":"azure.com.keyvault"}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/volumes@2023-10-01-preview","ScopeType":0,"Body":227}},{"8":{"Name":"listSecrets","ResourceType":"Applications.Core/extenders","ApiVersion":"2023-10-01-preview","Output":0,"Input":0}},{"2":{"Name":"SecretStoreListSecretsResult","Properties":{"type":{"Type":266,"Flags":2,"Description":"The type of SecretStore data"},"data":{"Type":267,"Flags":2,"Description":"An object to represent key-value type secrets"}}}},{"6":{"Value":"generic"}},{"6":{"Value":"certificate"}},{"5":{"Elements":[264,265]}},{"2":{"Name":"SecretStoreListSecretsResultData","Properties":{},"AdditionalProperties":217}},{"8":{"Name":"listSecrets","ResourceType":"Applications.Core/secretStores","ApiVersion":"2023-10-01-preview","Output":263,"Input":0}}] \ No newline at end of file +[{"1":{"Kind":1}},{"1":{"Kind":2}},{"1":{"Kind":3}},{"1":{"Kind":4}},{"1":{"Kind":5}},{"1":{"Kind":6}},{"1":{"Kind":7}},{"1":{"Kind":8}},{"6":{"Value":"Applications.Core/applications"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/applications","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":8,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":9,"Flags":10,"Description":"The resource api version"},"properties":{"Type":11,"Flags":1,"Description":"Application properties"},"tags":{"Type":46,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ApplicationProperties","Properties":{"provisioningState":{"Type":19,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"environment":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"extensions":{"Type":34,"Flags":0,"Description":"The application extension."},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[12,13,14,15,16,17,18]}},{"7":{"Name":"Extension","Discriminator":"kind","BaseProperties":{},"Elements":{"daprSidecar":21,"kubernetesMetadata":26,"kubernetesNamespace":30,"manualScaling":32}}},{"2":{"Name":"DaprSidecarExtension","Properties":{"appPort":{"Type":3,"Flags":0,"Description":"The Dapr appPort. Specifies the internal listening port for the application to handle requests from the Dapr sidecar."},"appId":{"Type":4,"Flags":1,"Description":"The Dapr appId. Specifies the identifier used by Dapr for service invocation."},"config":{"Type":4,"Flags":0,"Description":"Specifies the Dapr configuration to use for the resource."},"protocol":{"Type":24,"Flags":0,"Description":"The Dapr sidecar extension protocol"},"kind":{"Type":25,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"http"}},{"6":{"Value":"grpc"}},{"5":{"Elements":[22,23]}},{"6":{"Value":"daprSidecar"}},{"2":{"Name":"KubernetesMetadataExtension","Properties":{"annotations":{"Type":27,"Flags":0,"Description":"Annotations to be applied to the Kubernetes resources output by the resource"},"labels":{"Type":28,"Flags":0,"Description":"Labels to be applied to the Kubernetes resources output by the resource"},"kind":{"Type":29,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"2":{"Name":"KubernetesMetadataExtensionAnnotations","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"KubernetesMetadataExtensionLabels","Properties":{},"AdditionalProperties":4}},{"6":{"Value":"kubernetesMetadata"}},{"2":{"Name":"KubernetesNamespaceExtension","Properties":{"namespace":{"Type":4,"Flags":1,"Description":"The namespace of the application environment."},"kind":{"Type":31,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"kubernetesNamespace"}},{"2":{"Name":"ManualScalingExtension","Properties":{"replicas":{"Type":3,"Flags":1,"Description":"Replica count."},"kind":{"Type":33,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"manualScaling"}},{"3":{"ItemType":20}},{"2":{"Name":"ResourceStatus","Properties":{"compute":{"Type":36,"Flags":0,"Description":"Represents backing compute resource"},"recipe":{"Type":43,"Flags":2,"Description":"Recipe status at deployment time for a resource."},"outputResources":{"Type":45,"Flags":0,"Description":"Properties of an output resource"}}}},{"7":{"Name":"EnvironmentCompute","Discriminator":"kind","BaseProperties":{"resourceId":{"Type":4,"Flags":0,"Description":"The resource id of the compute resource for application environment."},"identity":{"Type":37,"Flags":0,"Description":"IdentitySettings is the external identity setting."}},"Elements":{"kubernetes":41}}},{"2":{"Name":"IdentitySettings","Properties":{"kind":{"Type":40,"Flags":1,"Description":"IdentitySettingKind is the kind of supported external identity setting"},"oidcIssuer":{"Type":4,"Flags":0,"Description":"The URI for your compute platform's OIDC issuer"},"resource":{"Type":4,"Flags":0,"Description":"The resource ID of the provisioned identity"}}}},{"6":{"Value":"undefined"}},{"6":{"Value":"azure.com.workload"}},{"5":{"Elements":[38,39]}},{"2":{"Name":"KubernetesCompute","Properties":{"namespace":{"Type":4,"Flags":1,"Description":"The namespace to use for the environment."},"kind":{"Type":42,"Flags":1,"Description":"Discriminator property for EnvironmentCompute."}}}},{"6":{"Value":"kubernetes"}},{"2":{"Name":"RecipeStatus","Properties":{"templateKind":{"Type":4,"Flags":1,"Description":"TemplateKind is the kind of the recipe template used by the portable resource upon deployment."},"templatePath":{"Type":4,"Flags":1,"Description":"TemplatePath is the path of the recipe consumed by the portable resource upon deployment."},"templateVersion":{"Type":4,"Flags":0,"Description":"TemplateVersion is the version number of the template."}}}},{"2":{"Name":"OutputResource","Properties":{"localId":{"Type":4,"Flags":0,"Description":"The logical identifier scoped to the owning Radius resource. This is only needed or used when a resource has a dependency relationship. LocalIDs do not have any particular format or meaning beyond being compared to determine dependency relationships."},"id":{"Type":4,"Flags":0,"Description":"The UCP resource ID of the underlying resource."},"radiusManaged":{"Type":2,"Flags":0,"Description":"Determines whether Radius manages the lifecycle of the underlying resource."}}}},{"3":{"ItemType":44}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"SystemData","Properties":{"createdBy":{"Type":4,"Flags":0,"Description":"The identity that created the resource."},"createdByType":{"Type":52,"Flags":0,"Description":"The type of identity that created the resource."},"createdAt":{"Type":4,"Flags":0,"Description":"The timestamp of resource creation (UTC)."},"lastModifiedBy":{"Type":4,"Flags":0,"Description":"The identity that last modified the resource."},"lastModifiedByType":{"Type":57,"Flags":0,"Description":"The type of identity that created the resource."},"lastModifiedAt":{"Type":4,"Flags":0,"Description":"The timestamp of resource last modification (UTC)"}}}},{"6":{"Value":"User"}},{"6":{"Value":"Application"}},{"6":{"Value":"ManagedIdentity"}},{"6":{"Value":"Key"}},{"5":{"Elements":[48,49,50,51]}},{"6":{"Value":"User"}},{"6":{"Value":"Application"}},{"6":{"Value":"ManagedIdentity"}},{"6":{"Value":"Key"}},{"5":{"Elements":[53,54,55,56]}},{"4":{"Name":"Applications.Core/applications@2023-10-01-preview","ScopeType":0,"Body":10}},{"6":{"Value":"Applications.Core/containers"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/containers","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":59,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":60,"Flags":10,"Description":"The resource api version"},"properties":{"Type":62,"Flags":1,"Description":"Container properties"},"tags":{"Type":122,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ContainerProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":70,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"container":{"Type":71,"Flags":1,"Description":"Definition of a container"},"connections":{"Type":108,"Flags":0,"Description":"Specifies a connection to another resource."},"identity":{"Type":37,"Flags":0,"Description":"IdentitySettings is the external identity setting."},"extensions":{"Type":109,"Flags":0,"Description":"Extensions spec of the resource"},"resourceProvisioning":{"Type":112,"Flags":0,"Description":"Specifies how the underlying service/resource is provisioned and managed. Available values are 'internal', where Radius manages the lifecycle of the resource internally, and 'manual', where a user manages the resource."},"resources":{"Type":114,"Flags":0,"Description":"A collection of references to resources associated with the container"},"restartPolicy":{"Type":118,"Flags":0,"Description":"Restart policy for the container"},"runtimes":{"Type":119,"Flags":0,"Description":"The properties for runtime configuration"}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[63,64,65,66,67,68,69]}},{"2":{"Name":"Container","Properties":{"image":{"Type":4,"Flags":1,"Description":"The registry and image to download and run in your container"},"imagePullPolicy":{"Type":75,"Flags":0,"Description":"The image pull policy for the container"},"env":{"Type":76,"Flags":0,"Description":"environment"},"ports":{"Type":81,"Flags":0,"Description":"container ports"},"readinessProbe":{"Type":82,"Flags":0,"Description":"Properties for readiness/liveness probe"},"livenessProbe":{"Type":82,"Flags":0,"Description":"Properties for readiness/liveness probe"},"volumes":{"Type":101,"Flags":0,"Description":"container volumes"},"command":{"Type":102,"Flags":0,"Description":"Entrypoint array. Overrides the container image's ENTRYPOINT"},"args":{"Type":103,"Flags":0,"Description":"Arguments to the entrypoint. Overrides the container image's CMD"},"workingDir":{"Type":4,"Flags":0,"Description":"Working directory for the container"}}}},{"6":{"Value":"Always"}},{"6":{"Value":"IfNotPresent"}},{"6":{"Value":"Never"}},{"5":{"Elements":[72,73,74]}},{"2":{"Name":"ContainerEnv","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"ContainerPortProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"protocol":{"Type":80,"Flags":0,"Description":"The protocol in use by the port"},"provides":{"Type":4,"Flags":0,"Description":"Specifies a route provided by this port"},"scheme":{"Type":4,"Flags":0,"Description":"Specifies the URL scheme of the communication protocol. Consumers can use the scheme to construct a URL. The value defaults to 'http' or 'https' depending on the port value"},"port":{"Type":3,"Flags":0,"Description":"Specifies the port that will be exposed by this container. Must be set when value different from containerPort is desired"}}}},{"6":{"Value":"TCP"}},{"6":{"Value":"UDP"}},{"5":{"Elements":[78,79]}},{"2":{"Name":"ContainerPorts","Properties":{},"AdditionalProperties":77}},{"7":{"Name":"HealthProbeProperties","Discriminator":"kind","BaseProperties":{"initialDelaySeconds":{"Type":3,"Flags":0,"Description":"Initial delay in seconds before probing for readiness/liveness"},"failureThreshold":{"Type":3,"Flags":0,"Description":"Threshold number of times the probe fails after which a failure would be reported"},"periodSeconds":{"Type":3,"Flags":0,"Description":"Interval for the readiness/liveness probe in seconds"},"timeoutSeconds":{"Type":3,"Flags":0,"Description":"Number of seconds after which the readiness/liveness probe times out. Defaults to 5 seconds"}},"Elements":{"exec":83,"httpGet":85,"tcp":88}}},{"2":{"Name":"ExecHealthProbeProperties","Properties":{"command":{"Type":4,"Flags":1,"Description":"Command to execute to probe readiness/liveness"},"kind":{"Type":84,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"6":{"Value":"exec"}},{"2":{"Name":"HttpGetHealthProbeProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"path":{"Type":4,"Flags":1,"Description":"The route to make the HTTP request on"},"headers":{"Type":86,"Flags":0,"Description":"Custom HTTP headers to add to the get request"},"kind":{"Type":87,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"2":{"Name":"HttpGetHealthProbePropertiesHeaders","Properties":{},"AdditionalProperties":4}},{"6":{"Value":"httpGet"}},{"2":{"Name":"TcpHealthProbeProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"kind":{"Type":89,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"6":{"Value":"tcp"}},{"7":{"Name":"Volume","Discriminator":"kind","BaseProperties":{"mountPath":{"Type":4,"Flags":0,"Description":"The path where the volume is mounted"}},"Elements":{"ephemeral":91,"persistent":96}}},{"2":{"Name":"EphemeralVolume","Properties":{"managedStore":{"Type":94,"Flags":1,"Description":"The managed store for the ephemeral volume"},"kind":{"Type":95,"Flags":1,"Description":"Discriminator property for Volume."}}}},{"6":{"Value":"memory"}},{"6":{"Value":"disk"}},{"5":{"Elements":[92,93]}},{"6":{"Value":"ephemeral"}},{"2":{"Name":"PersistentVolume","Properties":{"permission":{"Type":99,"Flags":0,"Description":"The persistent volume permission"},"source":{"Type":4,"Flags":1,"Description":"The source of the volume"},"kind":{"Type":100,"Flags":1,"Description":"Discriminator property for Volume."}}}},{"6":{"Value":"read"}},{"6":{"Value":"write"}},{"5":{"Elements":[97,98]}},{"6":{"Value":"persistent"}},{"2":{"Name":"ContainerVolumes","Properties":{},"AdditionalProperties":90}},{"3":{"ItemType":4}},{"3":{"ItemType":4}},{"2":{"Name":"ConnectionProperties","Properties":{"source":{"Type":4,"Flags":1,"Description":"The source of the connection"},"disableDefaultEnvVars":{"Type":2,"Flags":0,"Description":"default environment variable override"},"iam":{"Type":105,"Flags":0,"Description":"IAM properties"}}}},{"2":{"Name":"IamProperties","Properties":{"kind":{"Type":106,"Flags":1,"Description":"The kind of IAM provider to configure"},"roles":{"Type":107,"Flags":0,"Description":"RBAC permissions to be assigned on the source resource"}}}},{"6":{"Value":"azure"}},{"3":{"ItemType":4}},{"2":{"Name":"ContainerPropertiesConnections","Properties":{},"AdditionalProperties":104}},{"3":{"ItemType":20}},{"6":{"Value":"internal"}},{"6":{"Value":"manual"}},{"5":{"Elements":[110,111]}},{"2":{"Name":"ResourceReference","Properties":{"id":{"Type":4,"Flags":1,"Description":"Resource id of an existing resource"}}}},{"3":{"ItemType":113}},{"6":{"Value":"Always"}},{"6":{"Value":"OnFailure"}},{"6":{"Value":"Never"}},{"5":{"Elements":[115,116,117]}},{"2":{"Name":"RuntimesProperties","Properties":{"kubernetes":{"Type":120,"Flags":0,"Description":"The runtime configuration properties for Kubernetes"}}}},{"2":{"Name":"KubernetesRuntimeProperties","Properties":{"base":{"Type":4,"Flags":0,"Description":"The serialized YAML manifest which represents the base Kubernetes resources to deploy, such as Deployment, Service, ServiceAccount, Secrets, and ConfigMaps."},"pod":{"Type":121,"Flags":0,"Description":"A strategic merge patch that will be applied to the PodSpec object when this container is being deployed."}}}},{"2":{"Name":"KubernetesPodSpec","Properties":{},"AdditionalProperties":0}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/containers@2023-10-01-preview","ScopeType":0,"Body":61}},{"6":{"Value":"Applications.Core/environments"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/environments","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":124,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":125,"Flags":10,"Description":"The resource api version"},"properties":{"Type":127,"Flags":1,"Description":"Environment properties"},"tags":{"Type":153,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"EnvironmentProperties","Properties":{"provisioningState":{"Type":135,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"compute":{"Type":36,"Flags":1,"Description":"Represents backing compute resource"},"providers":{"Type":136,"Flags":0,"Description":"The Cloud providers configuration"},"simulated":{"Type":2,"Flags":0,"Description":"Simulated environment."},"recipes":{"Type":145,"Flags":0,"Description":"Specifies Recipes linked to the Environment."},"recipeConfig":{"Type":146,"Flags":0,"Description":"Specifies recipe configurations needed for the recipes."},"extensions":{"Type":152,"Flags":0,"Description":"The environment extension."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[128,129,130,131,132,133,134]}},{"2":{"Name":"Providers","Properties":{"azure":{"Type":137,"Flags":0,"Description":"The Azure cloud provider definition"},"aws":{"Type":138,"Flags":0,"Description":"The AWS cloud provider definition"}}}},{"2":{"Name":"ProvidersAzure","Properties":{"scope":{"Type":4,"Flags":1,"Description":"Target scope for Azure resources to be deployed into. For example: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup'"}}}},{"2":{"Name":"ProvidersAws","Properties":{"scope":{"Type":4,"Flags":1,"Description":"Target scope for AWS resources to be deployed into. For example: '/planes/aws/aws/accounts/000000000000/regions/us-west-2'"}}}},{"7":{"Name":"RecipeProperties","Discriminator":"templateKind","BaseProperties":{"templatePath":{"Type":4,"Flags":1,"Description":"Path to the template provided by the recipe. Currently only link to Azure Container Registry is supported."},"parameters":{"Type":0,"Flags":0,"Description":"Any object"}},"Elements":{"bicep":140,"terraform":142}}},{"2":{"Name":"BicepRecipeProperties","Properties":{"plainHttp":{"Type":2,"Flags":0,"Description":"Connect to the Bicep registry using HTTP (not-HTTPS). This should be used when the registry is known not to support HTTPS, for example in a locally-hosted registry. Defaults to false (use HTTPS/TLS)."},"templateKind":{"Type":141,"Flags":1,"Description":"Discriminator property for RecipeProperties."}}}},{"6":{"Value":"bicep"}},{"2":{"Name":"TerraformRecipeProperties","Properties":{"templateVersion":{"Type":4,"Flags":0,"Description":"Version of the template to deploy. For Terraform recipes using a module registry this is required, but must be omitted for other module sources."},"templateKind":{"Type":143,"Flags":1,"Description":"Discriminator property for RecipeProperties."}}}},{"6":{"Value":"terraform"}},{"2":{"Name":"DictionaryOfRecipeProperties","Properties":{},"AdditionalProperties":139}},{"2":{"Name":"EnvironmentPropertiesRecipes","Properties":{},"AdditionalProperties":144}},{"2":{"Name":"RecipeConfigProperties","Properties":{"terraform":{"Type":147,"Flags":0,"Description":"Specifies the terraform config properties"}}}},{"2":{"Name":"TerraformConfigProperties","Properties":{"authentication":{"Type":148,"Flags":0,"Description":"Specifies authentication information needed to use private terraform module repositories."}}}},{"2":{"Name":"AuthConfig","Properties":{"git":{"Type":149,"Flags":0,"Description":"Specifies authentication information needed to use private terraform module repositories from git module source"}}}},{"2":{"Name":"GitAuthConfig","Properties":{"pat":{"Type":151,"Flags":0,"Description":"Specifies the secret details of type personal access token for each different git platforms"}}}},{"2":{"Name":"Secret","Properties":{"secret":{"Type":4,"Flags":0,"Description":"The resource id for the secret containing credentials"}}}},{"2":{"Name":"GitAuthConfigPat","Properties":{},"AdditionalProperties":150}},{"3":{"ItemType":20}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/environments@2023-10-01-preview","ScopeType":0,"Body":126}},{"6":{"Value":"Applications.Core/extenders"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/extenders","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":155,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":156,"Flags":10,"Description":"The resource api version"},"properties":{"Type":158,"Flags":1,"Description":"ExtenderResource portable resource properties"},"tags":{"Type":171,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ExtenderProperties","Properties":{"environment":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the environment that the portable resource is linked to"},"application":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the application that the portable resource is consumed by (if applicable)"},"provisioningState":{"Type":166,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"secrets":{"Type":0,"Flags":0,"Description":"Any object"},"recipe":{"Type":167,"Flags":0,"Description":"The recipe used to automatically deploy underlying infrastructure for a portable resource"},"resourceProvisioning":{"Type":170,"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."}},"AdditionalProperties":0}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[159,160,161,162,163,164,165]}},{"2":{"Name":"Recipe","Properties":{"name":{"Type":4,"Flags":1,"Description":"The name of the recipe within the environment to use"},"parameters":{"Type":0,"Flags":0,"Description":"Any object"}}}},{"6":{"Value":"recipe"}},{"6":{"Value":"manual"}},{"5":{"Elements":[168,169]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/extenders@2023-10-01-preview","ScopeType":0,"Body":157}},{"6":{"Value":"Applications.Core/gateways"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/gateways","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":173,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":174,"Flags":10,"Description":"The resource api version"},"properties":{"Type":176,"Flags":1,"Description":"Gateway properties"},"tags":{"Type":192,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"GatewayProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":184,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"internal":{"Type":2,"Flags":0,"Description":"Sets Gateway to not be exposed externally (no public IP address associated). Defaults to false (exposed to internet)."},"hostname":{"Type":185,"Flags":0,"Description":"Declare hostname information for the Gateway. Leaving the hostname empty auto-assigns one: mygateway.myapp.PUBLICHOSTNAMEORIP.nip.io."},"routes":{"Type":187,"Flags":1,"Description":"Routes attached to this Gateway"},"tls":{"Type":188,"Flags":0,"Description":"TLS configuration definition for Gateway resource."},"url":{"Type":4,"Flags":2,"Description":"URL of the gateway resource. Readonly"}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[177,178,179,180,181,182,183]}},{"2":{"Name":"GatewayHostname","Properties":{"prefix":{"Type":4,"Flags":0,"Description":"Specify a prefix for the hostname: myhostname.myapp.PUBLICHOSTNAMEORIP.nip.io. Mutually exclusive with 'fullyQualifiedHostname' and will be overridden if both are defined."},"fullyQualifiedHostname":{"Type":4,"Flags":0,"Description":"Specify a fully-qualified domain name: myapp.mydomain.com. Mutually exclusive with 'prefix' and will take priority if both are defined."}}}},{"2":{"Name":"GatewayRoute","Properties":{"path":{"Type":4,"Flags":0,"Description":"The path to match the incoming request path on. Ex - /myservice."},"destination":{"Type":4,"Flags":0,"Description":"The HttpRoute to route to. Ex - myserviceroute.id."},"replacePrefix":{"Type":4,"Flags":0,"Description":"Optionally update the prefix when sending the request to the service. Ex - replacePrefix: '/' and path: '/myservice' will transform '/myservice/myroute' to '/myroute'"}}}},{"3":{"ItemType":186}},{"2":{"Name":"GatewayTls","Properties":{"sslPassthrough":{"Type":2,"Flags":0,"Description":"If true, gateway lets the https traffic sslPassthrough to the backend servers for decryption."},"minimumProtocolVersion":{"Type":191,"Flags":0,"Description":"Tls Minimum versions for Gateway resource."},"certificateFrom":{"Type":4,"Flags":0,"Description":"The resource id for the secret containing the TLS certificate and key for the gateway."}}}},{"6":{"Value":"1.2"}},{"6":{"Value":"1.3"}},{"5":{"Elements":[189,190]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/gateways@2023-10-01-preview","ScopeType":0,"Body":175}},{"6":{"Value":"Applications.Core/httpRoutes"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/httpRoutes","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":194,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":195,"Flags":10,"Description":"The resource api version"},"properties":{"Type":197,"Flags":1,"Description":"HTTPRoute properties"},"tags":{"Type":206,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"HttpRouteProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":205,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"hostname":{"Type":4,"Flags":0,"Description":"The internal hostname accepting traffic for the HTTP Route. Readonly."},"port":{"Type":3,"Flags":0,"Description":"The port number for the HTTP Route. Defaults to 80. Readonly."},"scheme":{"Type":4,"Flags":2,"Description":"The scheme used for traffic. Readonly."},"url":{"Type":4,"Flags":2,"Description":"A stable URL that that can be used to route traffic to a resource. Readonly."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[198,199,200,201,202,203,204]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/httpRoutes@2023-10-01-preview","ScopeType":0,"Body":196}},{"6":{"Value":"Applications.Core/secretStores"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/secretStores","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":208,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":209,"Flags":10,"Description":"The resource api version"},"properties":{"Type":211,"Flags":1,"Description":"The properties of SecretStore"},"tags":{"Type":229,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"SecretStoreProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":219,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"type":{"Type":222,"Flags":0,"Description":"The type of SecretStore data"},"data":{"Type":228,"Flags":1,"Description":"An object to represent key-value type secrets"},"resource":{"Type":4,"Flags":0,"Description":"The resource id of external secret store."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[212,213,214,215,216,217,218]}},{"6":{"Value":"generic"}},{"6":{"Value":"certificate"}},{"5":{"Elements":[220,221]}},{"2":{"Name":"SecretValueProperties","Properties":{"encoding":{"Type":226,"Flags":0,"Description":"The type of SecretValue Encoding"},"value":{"Type":4,"Flags":0,"Description":"The value of secret."},"valueFrom":{"Type":227,"Flags":0,"Description":"The Secret value source properties"}}}},{"6":{"Value":"raw"}},{"6":{"Value":"base64"}},{"5":{"Elements":[224,225]}},{"2":{"Name":"ValueFromProperties","Properties":{"name":{"Type":4,"Flags":1,"Description":"The name of the referenced secret."},"version":{"Type":4,"Flags":0,"Description":"The version of the referenced secret."}}}},{"2":{"Name":"SecretStorePropertiesData","Properties":{},"AdditionalProperties":223}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/secretStores@2023-10-01-preview","ScopeType":0,"Body":210}},{"6":{"Value":"Applications.Core/volumes"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/volumes","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":231,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":232,"Flags":10,"Description":"The resource api version"},"properties":{"Type":234,"Flags":1,"Description":"Volume properties"},"tags":{"Type":266,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"7":{"Name":"VolumeProperties","Discriminator":"kind","BaseProperties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":242,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."}},"Elements":{"azure.com.keyvault":243}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[235,236,237,238,239,240,241]}},{"2":{"Name":"AzureKeyVaultVolumeProperties","Properties":{"certificates":{"Type":256,"Flags":0,"Description":"The KeyVault certificates that this volume exposes"},"keys":{"Type":258,"Flags":0,"Description":"The KeyVault keys that this volume exposes"},"resource":{"Type":4,"Flags":1,"Description":"The ID of the keyvault to use for this volume resource"},"secrets":{"Type":264,"Flags":0,"Description":"The KeyVault secrets that this volume exposes"},"kind":{"Type":265,"Flags":1,"Description":"Discriminator property for VolumeProperties."}}}},{"2":{"Name":"CertificateObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"encoding":{"Type":248,"Flags":0,"Description":"Represents secret encodings"},"format":{"Type":251,"Flags":0,"Description":"Represents certificate formats"},"name":{"Type":4,"Flags":1,"Description":"The name of the certificate"},"certType":{"Type":255,"Flags":0,"Description":"Represents certificate types"},"version":{"Type":4,"Flags":0,"Description":"Certificate version"}}}},{"6":{"Value":"utf-8"}},{"6":{"Value":"hex"}},{"6":{"Value":"base64"}},{"5":{"Elements":[245,246,247]}},{"6":{"Value":"pem"}},{"6":{"Value":"pfx"}},{"5":{"Elements":[249,250]}},{"6":{"Value":"certificate"}},{"6":{"Value":"privatekey"}},{"6":{"Value":"publickey"}},{"5":{"Elements":[252,253,254]}},{"2":{"Name":"AzureKeyVaultVolumePropertiesCertificates","Properties":{},"AdditionalProperties":244}},{"2":{"Name":"KeyObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"name":{"Type":4,"Flags":1,"Description":"The name of the key"},"version":{"Type":4,"Flags":0,"Description":"Key version"}}}},{"2":{"Name":"AzureKeyVaultVolumePropertiesKeys","Properties":{},"AdditionalProperties":257}},{"2":{"Name":"SecretObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"encoding":{"Type":263,"Flags":0,"Description":"Represents secret encodings"},"name":{"Type":4,"Flags":1,"Description":"The name of the secret"},"version":{"Type":4,"Flags":0,"Description":"secret version"}}}},{"6":{"Value":"utf-8"}},{"6":{"Value":"hex"}},{"6":{"Value":"base64"}},{"5":{"Elements":[260,261,262]}},{"2":{"Name":"AzureKeyVaultVolumePropertiesSecrets","Properties":{},"AdditionalProperties":259}},{"6":{"Value":"azure.com.keyvault"}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/volumes@2023-10-01-preview","ScopeType":0,"Body":233}},{"8":{"Name":"listSecrets","ResourceType":"Applications.Core/extenders","ApiVersion":"2023-10-01-preview","Output":0,"Input":0}},{"2":{"Name":"SecretStoreListSecretsResult","Properties":{"type":{"Type":272,"Flags":2,"Description":"The type of SecretStore data"},"data":{"Type":273,"Flags":2,"Description":"An object to represent key-value type secrets"}}}},{"6":{"Value":"generic"}},{"6":{"Value":"certificate"}},{"5":{"Elements":[270,271]}},{"2":{"Name":"SecretStoreListSecretsResultData","Properties":{},"AdditionalProperties":223}},{"8":{"Name":"listSecrets","ResourceType":"Applications.Core/secretStores","ApiVersion":"2023-10-01-preview","Output":269,"Input":0}}] \ No newline at end of file diff --git a/hack/bicep-types-radius/generated/index.json b/hack/bicep-types-radius/generated/index.json index 735c06d6d0..927e764089 100644 --- a/hack/bicep-types-radius/generated/index.json +++ b/hack/bicep-types-radius/generated/index.json @@ -1 +1 @@ -{"Resources":{"Applications.Core/applications@2023-10-01-preview":{"RelativePath":"applications/applications.core/2023-10-01-preview/types.json","Index":58},"Applications.Core/containers@2023-10-01-preview":{"RelativePath":"applications/applications.core/2023-10-01-preview/types.json","Index":123},"Applications.Core/environments@2023-10-01-preview":{"RelativePath":"applications/applications.core/2023-10-01-preview/types.json","Index":148},"Applications.Core/extenders@2023-10-01-preview":{"RelativePath":"applications/applications.core/2023-10-01-preview/types.json","Index":166},"Applications.Core/gateways@2023-10-01-preview":{"RelativePath":"applications/applications.core/2023-10-01-preview/types.json","Index":187},"Applications.Core/httpRoutes@2023-10-01-preview":{"RelativePath":"applications/applications.core/2023-10-01-preview/types.json","Index":201},"Applications.Core/secretStores@2023-10-01-preview":{"RelativePath":"applications/applications.core/2023-10-01-preview/types.json","Index":224},"Applications.Core/volumes@2023-10-01-preview":{"RelativePath":"applications/applications.core/2023-10-01-preview/types.json","Index":261},"Applications.Dapr/pubSubBrokers@2023-10-01-preview":{"RelativePath":"applications/applications.dapr/2023-10-01-preview/types.json","Index":49},"Applications.Dapr/secretStores@2023-10-01-preview":{"RelativePath":"applications/applications.dapr/2023-10-01-preview/types.json","Index":66},"Applications.Dapr/stateStores@2023-10-01-preview":{"RelativePath":"applications/applications.dapr/2023-10-01-preview/types.json","Index":84},"Applications.Datastores/mongoDatabases@2023-10-01-preview":{"RelativePath":"applications/applications.datastores/2023-10-01-preview/types.json","Index":50},"Applications.Datastores/redisCaches@2023-10-01-preview":{"RelativePath":"applications/applications.datastores/2023-10-01-preview/types.json","Index":69},"Applications.Datastores/sqlDatabases@2023-10-01-preview":{"RelativePath":"applications/applications.datastores/2023-10-01-preview/types.json","Index":88},"Applications.Messaging/rabbitMQQueues@2023-10-01-preview":{"RelativePath":"applications/applications.messaging/2023-10-01-preview/types.json","Index":50}},"Functions":{"applications.core/extenders":{"2023-10-01-preview":[{"RelativePath":"applications/applications.core/2023-10-01-preview/types.json","Index":262}]},"applications.core/secretstores":{"2023-10-01-preview":[{"RelativePath":"applications/applications.core/2023-10-01-preview/types.json","Index":268}]},"applications.datastores/mongodatabases":{"2023-10-01-preview":[{"RelativePath":"applications/applications.datastores/2023-10-01-preview/types.json","Index":90}]},"applications.datastores/rediscaches":{"2023-10-01-preview":[{"RelativePath":"applications/applications.datastores/2023-10-01-preview/types.json","Index":92}]},"applications.datastores/sqldatabases":{"2023-10-01-preview":[{"RelativePath":"applications/applications.datastores/2023-10-01-preview/types.json","Index":94}]},"applications.messaging/rabbitmqqueues":{"2023-10-01-preview":[{"RelativePath":"applications/applications.messaging/2023-10-01-preview/types.json","Index":52}]}}} \ No newline at end of file +{"Resources":{"Applications.Core/applications@2023-10-01-preview":{"RelativePath":"applications/applications.core/2023-10-01-preview/types.json","Index":58},"Applications.Core/containers@2023-10-01-preview":{"RelativePath":"applications/applications.core/2023-10-01-preview/types.json","Index":123},"Applications.Core/environments@2023-10-01-preview":{"RelativePath":"applications/applications.core/2023-10-01-preview/types.json","Index":154},"Applications.Core/extenders@2023-10-01-preview":{"RelativePath":"applications/applications.core/2023-10-01-preview/types.json","Index":172},"Applications.Core/gateways@2023-10-01-preview":{"RelativePath":"applications/applications.core/2023-10-01-preview/types.json","Index":193},"Applications.Core/httpRoutes@2023-10-01-preview":{"RelativePath":"applications/applications.core/2023-10-01-preview/types.json","Index":207},"Applications.Core/secretStores@2023-10-01-preview":{"RelativePath":"applications/applications.core/2023-10-01-preview/types.json","Index":230},"Applications.Core/volumes@2023-10-01-preview":{"RelativePath":"applications/applications.core/2023-10-01-preview/types.json","Index":267},"Applications.Dapr/pubSubBrokers@2023-10-01-preview":{"RelativePath":"applications/applications.dapr/2023-10-01-preview/types.json","Index":49},"Applications.Dapr/secretStores@2023-10-01-preview":{"RelativePath":"applications/applications.dapr/2023-10-01-preview/types.json","Index":66},"Applications.Dapr/stateStores@2023-10-01-preview":{"RelativePath":"applications/applications.dapr/2023-10-01-preview/types.json","Index":84},"Applications.Datastores/mongoDatabases@2023-10-01-preview":{"RelativePath":"applications/applications.datastores/2023-10-01-preview/types.json","Index":50},"Applications.Datastores/redisCaches@2023-10-01-preview":{"RelativePath":"applications/applications.datastores/2023-10-01-preview/types.json","Index":69},"Applications.Datastores/sqlDatabases@2023-10-01-preview":{"RelativePath":"applications/applications.datastores/2023-10-01-preview/types.json","Index":88},"Applications.Messaging/rabbitMQQueues@2023-10-01-preview":{"RelativePath":"applications/applications.messaging/2023-10-01-preview/types.json","Index":50}},"Functions":{"applications.core/extenders":{"2023-10-01-preview":[{"RelativePath":"applications/applications.core/2023-10-01-preview/types.json","Index":268}]},"applications.core/secretstores":{"2023-10-01-preview":[{"RelativePath":"applications/applications.core/2023-10-01-preview/types.json","Index":274}]},"applications.datastores/mongodatabases":{"2023-10-01-preview":[{"RelativePath":"applications/applications.datastores/2023-10-01-preview/types.json","Index":90}]},"applications.datastores/rediscaches":{"2023-10-01-preview":[{"RelativePath":"applications/applications.datastores/2023-10-01-preview/types.json","Index":92}]},"applications.datastores/sqldatabases":{"2023-10-01-preview":[{"RelativePath":"applications/applications.datastores/2023-10-01-preview/types.json","Index":94}]},"applications.messaging/rabbitmqqueues":{"2023-10-01-preview":[{"RelativePath":"applications/applications.messaging/2023-10-01-preview/types.json","Index":52}]}}} \ No newline at end of file diff --git a/pkg/corerp/api/v20231001preview/environment_conversion.go b/pkg/corerp/api/v20231001preview/environment_conversion.go index 1b083b1adb..435a7f2015 100644 --- a/pkg/corerp/api/v20231001preview/environment_conversion.go +++ b/pkg/corerp/api/v20231001preview/environment_conversion.go @@ -63,6 +63,24 @@ func (src *EnvironmentResource) ConvertTo() (v1.DataModelInterface, error) { } converted.Properties.Compute = *envCompute + recipeConfig := src.Properties.RecipeConfig + if recipeConfig != nil { + if recipeConfig.Terraform != nil { + if recipeConfig.Terraform.Authentication != nil { + gitConfig := recipeConfig.Terraform.Authentication.Git + if gitConfig != nil { + pat := gitConfig.Pat + p := map[string]datamodel.Secret{} + for k, v := range pat { + p[k] = datamodel.Secret{ + SecretStore: to.String(v.SecretStore), + } + } + converted.Properties.RecipeConfig.Terraform.Authentication.Git.PAT = p + } + } + } + } if src.Properties.Recipes != nil { envRecipes := make(map[string]map[string]datamodel.EnvironmentRecipeProperties) for resourceType, recipes := range src.Properties.Recipes { @@ -151,6 +169,21 @@ func (dst *EnvironmentResource) ConvertFrom(src v1.DataModelInterface) error { dst.Properties.Recipes = recipes } + dst.Properties.RecipeConfig = &RecipeConfigProperties{ + Terraform: &TerraformConfigProperties{ + Authentication: &AuthConfig{ + Git: &GitAuthConfig{ + Pat: map[string]*Secret{}, + }, + }, + }, + } + for k, v := range env.Properties.RecipeConfig.Terraform.Authentication.Git.PAT { + dst.Properties.RecipeConfig.Terraform.Authentication.Git.Pat[k] = &Secret{ + SecretStore: to.Ptr(v.SecretStore), + } + } + if env.Properties.Providers != (datamodel.Providers{}) { dst.Properties.Providers = &Providers{} if env.Properties.Providers.Azure != (datamodel.ProvidersAzure{}) { diff --git a/pkg/corerp/api/v20231001preview/zz_generated_models.go b/pkg/corerp/api/v20231001preview/zz_generated_models.go index 73b3f0a979..78ea8a1e77 100644 --- a/pkg/corerp/api/v20231001preview/zz_generated_models.go +++ b/pkg/corerp/api/v20231001preview/zz_generated_models.go @@ -126,6 +126,9 @@ type ApplicationResourceUpdateProperties struct { // Cloud providers configuration for the environment. Providers *ProvidersUpdate + // Specifies recipe configurations needed for the recipes. + RecipeConfig *RecipeConfigProperties + // Specifies Recipes linked to the Environment. Recipes map[string]map[string]RecipePropertiesUpdateClassification @@ -133,6 +136,12 @@ type ApplicationResourceUpdateProperties struct { Simulated *bool } +// AuthConfig - Specifies authentication information needed to use private terraform module repositories. +type AuthConfig struct { + // Specifies authentication information needed to use private terraform module repositories from git module source + Git *GitAuthConfig +} + // AzureKeyVaultVolumeProperties - Represents Azure Key Vault Volume properties type AzureKeyVaultVolumeProperties struct { // REQUIRED; Fully qualified resource ID for the application @@ -553,6 +562,9 @@ type EnvironmentProperties struct { // Cloud providers configuration for the environment. Providers *Providers + // Specifies recipe configurations needed for the recipes. + RecipeConfig *RecipeConfigProperties + // Specifies Recipes linked to the Environment. Recipes map[string]map[string]RecipePropertiesClassification @@ -616,6 +628,9 @@ type EnvironmentResourceUpdateProperties struct { // Cloud providers configuration for the environment. Providers *ProvidersUpdate + // Specifies recipe configurations needed for the recipes. + RecipeConfig *RecipeConfigProperties + // Specifies Recipes linked to the Environment. Recipes map[string]map[string]RecipePropertiesUpdateClassification @@ -934,6 +949,13 @@ type GatewayTLS struct { SSLPassthrough *bool } +// GitAuthConfig - Specifies authentication information needed to use private terraform module repositories from git module +// source +type GitAuthConfig struct { + // Specifies the secret details of type personal access token for each different git platforms + Pat map[string]*Secret +} + // HTTPGetHealthProbeProperties - Specifies the properties for readiness/liveness probe using HTTP Get type HTTPGetHealthProbeProperties struct { // REQUIRED; The listening port number @@ -1377,6 +1399,12 @@ type Recipe struct { Parameters map[string]any } +// RecipeConfigProperties - Specifies recipe configurations needed for the recipes. +type RecipeConfigProperties struct { + // Specifies the terraform config properties + Terraform *TerraformConfigProperties +} + // RecipeGetMetadata - Represents the request body of the getmetadata action. type RecipeGetMetadata struct { // REQUIRED; The name of the recipe registered to the environment @@ -1496,6 +1524,12 @@ type RuntimesProperties struct { Kubernetes *KubernetesRuntimeProperties } +// Secret - Specifies the secret details of type personal access token for each different git platforms +type Secret struct { + // The resource id for the secret store containing credentials + SecretStore *string +} + // SecretObjectProperties - Represents secret object properties type SecretObjectProperties struct { // REQUIRED; The name of the secret @@ -1669,6 +1703,12 @@ func (t *TCPHealthProbeProperties) GetHealthProbeProperties() *HealthProbeProper } } +// TerraformConfigProperties - Specifies the terraform config properties +type TerraformConfigProperties struct { + // Specifies authentication information needed to use private terraform module repositories. + Authentication *AuthConfig +} + // TerraformRecipeProperties - Represents Terraform recipe properties. type TerraformRecipeProperties struct { // REQUIRED; Discriminator property for RecipeProperties. diff --git a/pkg/corerp/api/v20231001preview/zz_generated_models_serde.go b/pkg/corerp/api/v20231001preview/zz_generated_models_serde.go index 6446564248..6b5c2777b5 100644 --- a/pkg/corerp/api/v20231001preview/zz_generated_models_serde.go +++ b/pkg/corerp/api/v20231001preview/zz_generated_models_serde.go @@ -312,6 +312,7 @@ func (a ApplicationResourceUpdateProperties) MarshalJSON() ([]byte, error) { populate(objectMap, "compute", a.Compute) populate(objectMap, "extensions", a.Extensions) populate(objectMap, "providers", a.Providers) + populate(objectMap, "recipeConfig", a.RecipeConfig) populate(objectMap, "recipes", a.Recipes) populate(objectMap, "simulated", a.Simulated) return json.Marshal(objectMap) @@ -335,6 +336,9 @@ func (a *ApplicationResourceUpdateProperties) UnmarshalJSON(data []byte) error { case "providers": err = unpopulate(val, "Providers", &a.Providers) delete(rawMsg, key) + case "recipeConfig": + err = unpopulate(val, "RecipeConfig", &a.RecipeConfig) + delete(rawMsg, key) case "recipes": var recipesRaw map[string]json.RawMessage if err = json.Unmarshal(val, &recipesRaw); err != nil { @@ -360,6 +364,33 @@ func (a *ApplicationResourceUpdateProperties) UnmarshalJSON(data []byte) error { return nil } +// MarshalJSON implements the json.Marshaller interface for type AuthConfig. +func (a AuthConfig) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "git", a.Git) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type AuthConfig. +func (a *AuthConfig) 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", a, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "git": + err = unpopulate(val, "Git", &a.Git) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", a, err) + } + } + return nil +} + // MarshalJSON implements the json.Marshaller interface for type AzureKeyVaultVolumeProperties. func (a AzureKeyVaultVolumeProperties) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) @@ -1193,6 +1224,7 @@ func (e EnvironmentProperties) MarshalJSON() ([]byte, error) { populate(objectMap, "extensions", e.Extensions) populate(objectMap, "providers", e.Providers) populate(objectMap, "provisioningState", e.ProvisioningState) + populate(objectMap, "recipeConfig", e.RecipeConfig) populate(objectMap, "recipes", e.Recipes) populate(objectMap, "simulated", e.Simulated) return json.Marshal(objectMap) @@ -1219,6 +1251,9 @@ func (e *EnvironmentProperties) UnmarshalJSON(data []byte) error { case "provisioningState": err = unpopulate(val, "ProvisioningState", &e.ProvisioningState) delete(rawMsg, key) + case "recipeConfig": + err = unpopulate(val, "RecipeConfig", &e.RecipeConfig) + delete(rawMsg, key) case "recipes": var recipesRaw map[string]json.RawMessage if err = json.Unmarshal(val, &recipesRaw); err != nil { @@ -1363,6 +1398,7 @@ func (e EnvironmentResourceUpdateProperties) MarshalJSON() ([]byte, error) { populate(objectMap, "compute", e.Compute) populate(objectMap, "extensions", e.Extensions) populate(objectMap, "providers", e.Providers) + populate(objectMap, "recipeConfig", e.RecipeConfig) populate(objectMap, "recipes", e.Recipes) populate(objectMap, "simulated", e.Simulated) return json.Marshal(objectMap) @@ -1386,6 +1422,9 @@ func (e *EnvironmentResourceUpdateProperties) UnmarshalJSON(data []byte) error { case "providers": err = unpopulate(val, "Providers", &e.Providers) delete(rawMsg, key) + case "recipeConfig": + err = unpopulate(val, "RecipeConfig", &e.RecipeConfig) + delete(rawMsg, key) case "recipes": var recipesRaw map[string]json.RawMessage if err = json.Unmarshal(val, &recipesRaw); err != nil { @@ -2163,6 +2202,33 @@ func (g *GatewayTLS) UnmarshalJSON(data []byte) error { return nil } +// MarshalJSON implements the json.Marshaller interface for type GitAuthConfig. +func (g GitAuthConfig) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "pat", g.Pat) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type GitAuthConfig. +func (g *GitAuthConfig) 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", g, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "pat": + err = unpopulate(val, "Pat", &g.Pat) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", g, err) + } + } + return nil +} + // MarshalJSON implements the json.Marshaller interface for type HTTPGetHealthProbeProperties. func (h HTTPGetHealthProbeProperties) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) @@ -3229,6 +3295,33 @@ func (r *Recipe) UnmarshalJSON(data []byte) error { return nil } +// MarshalJSON implements the json.Marshaller interface for type RecipeConfigProperties. +func (r RecipeConfigProperties) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "terraform", r.Terraform) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type RecipeConfigProperties. +func (r *RecipeConfigProperties) 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", r, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "terraform": + err = unpopulate(val, "Terraform", &r.Terraform) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", r, err) + } + } + return nil +} + // MarshalJSON implements the json.Marshaller interface for type RecipeGetMetadata. func (r RecipeGetMetadata) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) @@ -3567,6 +3660,33 @@ func (r *RuntimesProperties) UnmarshalJSON(data []byte) error { return nil } +// MarshalJSON implements the json.Marshaller interface for type Secret. +func (s Secret) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "secretStore", s.SecretStore) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type Secret. +func (s *Secret) 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", s, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "secretStore": + err = unpopulate(val, "SecretStore", &s.SecretStore) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", s, err) + } + } + return nil +} + // MarshalJSON implements the json.Marshaller interface for type SecretObjectProperties. func (s SecretObjectProperties) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) @@ -3973,6 +4093,33 @@ func (t *TCPHealthProbeProperties) UnmarshalJSON(data []byte) error { return nil } +// MarshalJSON implements the json.Marshaller interface for type TerraformConfigProperties. +func (t TerraformConfigProperties) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "authentication", t.Authentication) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type TerraformConfigProperties. +func (t *TerraformConfigProperties) 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", t, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "authentication": + err = unpopulate(val, "Authentication", &t.Authentication) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", t, err) + } + } + return nil +} + // MarshalJSON implements the json.Marshaller interface for type TerraformRecipeProperties. func (t TerraformRecipeProperties) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) diff --git a/pkg/corerp/datamodel/environment.go b/pkg/corerp/datamodel/environment.go index d74c638144..a26c735f2d 100644 --- a/pkg/corerp/datamodel/environment.go +++ b/pkg/corerp/datamodel/environment.go @@ -38,11 +38,32 @@ func (e *Environment) ResourceTypeName() string { // EnvironmentProperties represents the properties of Environment. type EnvironmentProperties struct { - Compute rpv1.EnvironmentCompute `json:"compute,omitempty"` - Recipes map[string]map[string]EnvironmentRecipeProperties `json:"recipes,omitempty"` - Providers Providers `json:"providers,omitempty"` - Extensions []Extension `json:"extensions,omitempty"` - Simulated bool `json:"simulated,omitempty"` + Compute rpv1.EnvironmentCompute `json:"compute,omitempty"` + Recipes map[string]map[string]EnvironmentRecipeProperties `json:"recipes,omitempty"` + Providers Providers `json:"providers,omitempty"` + RecipeConfig RecipeConfigProperties `json:"recipeConfig,omitempty"` + Extensions []Extension `json:"extensions,omitempty"` + Simulated bool `json:"simulated,omitempty"` +} + +type RecipeConfigProperties struct { + Terraform TerraformConfigProperties `json:"terraform,omitempty"` +} + +type TerraformConfigProperties struct { + Authentication AuthConfig `json:"authentication,omitempty"` +} + +type AuthConfig struct { + Git GitAuthConfig `json:"git,omitempty"` +} + +type GitAuthConfig struct { + PAT map[string]Secret `json:"pat,omitempty"` +} + +type Secret struct { + SecretStore string `json:"secretStore,omitempty"` } // EnvironmentRecipeProperties represents the properties of environment's recipe. diff --git a/pkg/recipes/configloader/environment.go b/pkg/recipes/configloader/environment.go index 108fb3bb49..f9967715e7 100644 --- a/pkg/recipes/configloader/environment.go +++ b/pkg/recipes/configloader/environment.go @@ -72,8 +72,9 @@ func (e *environmentLoader) LoadConfiguration(ctx context.Context, recipe recipe func getConfiguration(environment *v20231001preview.EnvironmentResource, application *v20231001preview.ApplicationResource) (*recipes.Configuration, error) { config := recipes.Configuration{ - Runtime: recipes.RuntimeConfiguration{}, - Providers: datamodel.Providers{}, + Runtime: recipes.RuntimeConfiguration{}, + Providers: datamodel.Providers{}, + RecipeConfig: *environment.Properties.RecipeConfig.Terraform.Authentication.Git.Pat["dev.azure.com"].SecretStore, } switch environment.Properties.Compute.(type) { diff --git a/pkg/recipes/controllerconfig/config.go b/pkg/recipes/controllerconfig/config.go index 750ffbbf28..2c4252b7c1 100644 --- a/pkg/recipes/controllerconfig/config.go +++ b/pkg/recipes/controllerconfig/config.go @@ -102,7 +102,7 @@ func New(options hostoptions.HostOptions) (*RecipeControllerConfig, error) { recipes.TemplateKindTerraform: driver.NewTerraformDriver(options.UCPConnection, provider.NewSecretProvider(options.Config.SecretProvider), driver.TerraformOptions{ Path: options.Config.Terraform.Path, - }, cfg.K8sClients.ClientSet), + }, cfg.K8sClients.ClientSet, clientOptions), }, }) diff --git a/pkg/recipes/driver/terraform.go b/pkg/recipes/driver/terraform.go index b551495597..ccc1cf9054 100644 --- a/pkg/recipes/driver/terraform.go +++ b/pkg/recipes/driver/terraform.go @@ -24,6 +24,7 @@ import ( "path/filepath" "strings" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" "github.com/google/uuid" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" rpv1 "github.com/radius-project/radius/pkg/rp/v1" @@ -48,9 +49,9 @@ import ( var _ Driver = (*terraformDriver)(nil) // NewTerraformDriver creates a new instance of driver to execute a Terraform recipe. -func NewTerraformDriver(ucpConn sdk.Connection, secretProvider *ucp_provider.SecretProvider, options TerraformOptions, k8sClientSet kubernetes.Interface) Driver { +func NewTerraformDriver(ucpConn sdk.Connection, secretProvider *ucp_provider.SecretProvider, options TerraformOptions, k8sClientSet kubernetes.Interface, armOptions *arm.ClientOptions) Driver { return &terraformDriver{ - terraformExecutor: terraform.NewExecutor(ucpConn, secretProvider, k8sClientSet), + terraformExecutor: terraform.NewExecutor(ucpConn, secretProvider, k8sClientSet, armOptions), options: options, } } diff --git a/pkg/recipes/terraform/execute.go b/pkg/recipes/terraform/execute.go index b10ec88fa4..f03d252058 100644 --- a/pkg/recipes/terraform/execute.go +++ b/pkg/recipes/terraform/execute.go @@ -22,9 +22,12 @@ import ( "fmt" "time" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" install "github.com/hashicorp/hc-install" "github.com/hashicorp/terraform-exec/tfexec" tfjson "github.com/hashicorp/terraform-json" + aztoken "github.com/radius-project/radius/pkg/azure/tokencredentials" + "github.com/radius-project/radius/pkg/corerp/api/v20231001preview" "github.com/radius-project/radius/pkg/metrics" "github.com/radius-project/radius/pkg/recipes" "github.com/radius-project/radius/pkg/recipes/recipecontext" @@ -33,6 +36,7 @@ import ( "github.com/radius-project/radius/pkg/recipes/terraform/config/providers" "github.com/radius-project/radius/pkg/recipes/util" "github.com/radius-project/radius/pkg/sdk" + "github.com/radius-project/radius/pkg/ucp/resources" ucp_provider "github.com/radius-project/radius/pkg/ucp/secret/provider" "github.com/radius-project/radius/pkg/ucp/ucplog" "go.opentelemetry.io/otel/attribute" @@ -48,8 +52,8 @@ var ( var _ TerraformExecutor = (*executor)(nil) // NewExecutor creates a new Executor with the given UCP connection and secret provider, to execute a Terraform recipe. -func NewExecutor(ucpConn sdk.Connection, secretProvider *ucp_provider.SecretProvider, k8sClientSet kubernetes.Interface) *executor { - return &executor{ucpConn: ucpConn, secretProvider: secretProvider, k8sClientSet: k8sClientSet} +func NewExecutor(ucpConn sdk.Connection, secretProvider *ucp_provider.SecretProvider, k8sClientSet kubernetes.Interface, armOptions *arm.ClientOptions) *executor { + return &executor{ucpConn: ucpConn, secretProvider: secretProvider, k8sClientSet: k8sClientSet, armOptions: armOptions} } type executor struct { @@ -61,6 +65,8 @@ type executor struct { // k8sClientSet is the Kubernetes client. k8sClientSet kubernetes.Interface + + armOptions *arm.ClientOptions } // Deploy installs Terraform, creates a working directory, generates a config, and runs Terraform init and @@ -184,7 +190,7 @@ func (e *executor) GetRecipeMetadata(ctx context.Context, options Options) (map[ return nil, err } - result, err := downloadAndInspect(ctx, tf, options) + result, err := downloadAndInspect(ctx, tf, options, e.armOptions) if err != nil { return nil, err } @@ -204,7 +210,7 @@ func (e *executor) generateConfig(ctx context.Context, tf *tfexec.Terraform, opt return "", err } - loadedModule, err := downloadAndInspect(ctx, tf, options) + loadedModule, err := downloadAndInspect(ctx, tf, options, e.armOptions) if err != nil { return "", err } @@ -262,13 +268,15 @@ func (e *executor) generateConfig(ctx context.Context, tf *tfexec.Terraform, opt } // downloadAndInspect handles downloading the TF module and retrieving the necessary information -func downloadAndInspect(ctx context.Context, tf *tfexec.Terraform, options Options) (*moduleInspectResult, error) { +func downloadAndInspect(ctx context.Context, tf *tfexec.Terraform, options Options, armOptions *arm.ClientOptions) (*moduleInspectResult, error) { logger := ucplog.FromContextOrDiscard(ctx) // Download the Terraform module to the working directory. logger.Info(fmt.Sprintf("Downloading Terraform module: %s", options.EnvRecipe.TemplatePath)) downloadStartTime := time.Now() - if err := downloadModule(ctx, tf, options.EnvRecipe.TemplatePath); err != nil { + path, _ := getTemplatePath(ctx, options, armOptions) + //path := options.EnvRecipe.TemplatePath + if err := downloadModule(ctx, tf, path); err != nil { metrics.DefaultRecipeEngineMetrics.RecordRecipeDownloadDuration(ctx, downloadStartTime, metrics.NewRecipeAttributes(metrics.RecipeEngineOperationDownloadRecipe, options.EnvRecipe.Name, options.EnvRecipe, recipes.RecipeDownloadFailed)) @@ -280,7 +288,7 @@ func downloadAndInspect(ctx context.Context, tf *tfexec.Terraform, options Optio // Load the downloaded module to retrieve providers and variables required by the module. // This is needed to add the appropriate providers config and populate the value of recipe context variable. - logger.Info(fmt.Sprintf("Inspecting the downloaded Terraform module: %s", options.EnvRecipe.TemplatePath)) + logger.Info(fmt.Sprintf("Inspecting the downloaded Terraform module: %s", path)) loadedModule, err := inspectModule(tf.WorkingDir(), options.EnvRecipe) if err != nil { return nil, err @@ -289,6 +297,18 @@ func downloadAndInspect(ctx context.Context, tf *tfexec.Terraform, options Optio return loadedModule, nil } +func getTemplatePath(ctx context.Context, options Options, armOptions *arm.ClientOptions) (string, error) { + secretStoreID, err := resources.ParseResource(options.EnvConfig.RecipeConfig) + if err != nil { + return "", err + } + client, err := v20231001preview.NewSecretStoresClient(secretStoreID.RootScope(), &aztoken.AnonymousCredential{}, armOptions) + p, err := client.ListSecrets(ctx, secretStoreID.Name(), map[string]any{}, nil) + fmt.Println(p) + path := fmt.Sprintf("git::https://%s:%s@%s", *p.Data["username"].Value, *p.Data["pat"].Value, options.EnvRecipe.TemplatePath) + return path, err +} + // getTerraformConfig initializes the Terraform json config with provided module source and saves it func getTerraformConfig(ctx context.Context, workingDir string, options Options) (*config.TerraformConfig, error) { // Generate Terraform json config in the working directory diff --git a/pkg/recipes/types.go b/pkg/recipes/types.go index d5245eaf5a..5250ef40f6 100644 --- a/pkg/recipes/types.go +++ b/pkg/recipes/types.go @@ -32,6 +32,8 @@ type Configuration struct { Providers datamodel.Providers // Simulated represents whether the environment is simulated or not. Simulated bool + + RecipeConfig string } // RuntimeConfiguration represents Kubernetes Runtime configuration for the environment. diff --git a/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/openapi.json b/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/openapi.json index b251b023ec..05ee458132 100644 --- a/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/openapi.json +++ b/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/openapi.json @@ -2745,6 +2745,10 @@ "type": "object" } }, + "recipeConfig": { + "$ref": "#/definitions/RecipeConfigProperties", + "description": "Specifies recipe configurations needed for the recipes." + }, "extensions": { "type": "array", "description": "The environment extension.", @@ -2755,6 +2759,16 @@ } } }, + "AuthConfig": { + "type": "object", + "description": "Specifies authentication information needed to use private terraform module repositories.", + "properties": { + "git": { + "$ref": "#/definitions/GitAuthConfig", + "description": "Specifies authentication information needed to use private terraform module repositories from git module source" + } + } + }, "AzureKeyVaultVolumeProperties": { "type": "object", "description": "Represents Azure Key Vault Volume properties", @@ -3507,6 +3521,10 @@ "type": "object" } }, + "recipeConfig": { + "$ref": "#/definitions/RecipeConfigProperties", + "description": "Specifies recipe configurations needed for the recipes." + }, "extensions": { "type": "array", "description": "The environment extension.", @@ -3607,6 +3625,10 @@ "type": "object" } }, + "recipeConfig": { + "$ref": "#/definitions/RecipeConfigProperties", + "description": "Specifies recipe configurations needed for the recipes." + }, "extensions": { "type": "array", "description": "The environment extension.", @@ -4011,6 +4033,19 @@ } } }, + "GitAuthConfig": { + "type": "object", + "description": "Specifies authentication information needed to use private terraform module repositories from git module source", + "properties": { + "pat": { + "type": "object", + "description": "Specifies the secret details of type personal access token for each different git platforms", + "additionalProperties": { + "$ref": "#/definitions/Secret" + } + } + } + }, "HealthProbeProperties": { "type": "object", "description": "Properties for readiness/liveness probe", @@ -4732,6 +4767,16 @@ "name" ] }, + "RecipeConfigProperties": { + "type": "object", + "description": "Specifies recipe configurations needed for the recipes.", + "properties": { + "terraform": { + "$ref": "#/definitions/TerraformConfigProperties", + "description": "Specifies the terraform config properties" + } + } + }, "RecipeGetMetadata": { "type": "object", "description": "Represents the request body of the getmetadata action.", @@ -4966,6 +5011,16 @@ } } }, + "Secret": { + "type": "object", + "description": "Specifies the secret details of type personal access token for each different git platforms", + "properties": { + "secretStore": { + "type": "string", + "description": "The resource id for the secret store containing credentials" + } + } + }, "SecretObjectProperties": { "type": "object", "description": "Represents secret object properties", @@ -5237,6 +5292,16 @@ ], "x-ms-discriminator-value": "tcp" }, + "TerraformConfigProperties": { + "type": "object", + "description": "Specifies the terraform config properties", + "properties": { + "authentication": { + "$ref": "#/definitions/AuthConfig", + "description": "Specifies authentication information needed to use private terraform module repositories." + } + } + }, "TerraformRecipeProperties": { "type": "object", "description": "Represents Terraform recipe properties.", diff --git a/typespec/Applications.Core/environments.tsp b/typespec/Applications.Core/environments.tsp index 8f721c88a3..362d213975 100644 --- a/typespec/Applications.Core/environments.tsp +++ b/typespec/Applications.Core/environments.tsp @@ -65,11 +65,44 @@ model EnvironmentProperties { @doc("Specifies Recipes linked to the Environment.") recipes?: Record>; + @doc("Specifies recipe configurations needed for the recipes.") + recipeConfig?: RecipeConfigProperties; + @doc("The environment extension.") @extension("x-ms-identifiers", []) extensions?: Array; } +@doc("Specifies recipe configurations needed for the recipes.") +model RecipeConfigProperties { + @doc("Specifies the terraform config properties") + terraform?: TerraformConfigProperties; +} + +@doc("Specifies the terraform config properties") +model TerraformConfigProperties{ + @doc("Specifies authentication information needed to use private terraform module repositories.") + authentication?: AuthConfig; +} + +@doc("Specifies authentication information needed to use private terraform module repositories.") +model AuthConfig{ + @doc("Specifies authentication information needed to use private terraform module repositories from git module source") + git?: GitAuthConfig; +} + +@doc("Specifies authentication information needed to use private terraform module repositories from git module source") +model GitAuthConfig{ + @doc("Specifies the secret details of type personal access token for each different git platforms") + pat?: Record; +} + +@doc("Specifies the secret details of type personal access token for each different git platforms") +model Secret { + @doc("The resource id for the secret store containing credentials") + secretStore?: string; +} + @doc("The Cloud providers configuration") model Providers { @doc("The Azure cloud provider configuration") From d212a664f0a684da23a780468aea8dbc09715c0e Mon Sep 17 00:00:00 2001 From: Vishwanath Hiremath Date: Mon, 12 Feb 2024 22:12:57 -0800 Subject: [PATCH 02/22] adding changes to support private git repo Signed-off-by: Vishwanath Hiremath --- .../environment_conversion.go | 33 ++++---- .../environment_conversion_test.go | 14 ++++ .../testdata/environmentresource.json | 13 ++++ .../environmentresourcedatamodel.json | 13 ++++ pkg/recipes/configloader/environment.go | 20 ++--- pkg/recipes/configloader/environment_test.go | 47 ++++++++++++ pkg/recipes/terraform/config/config.go | 76 ++++++++++++++++++- pkg/recipes/terraform/config/config_test.go | 15 ++-- pkg/recipes/terraform/execute.go | 35 +++------ pkg/recipes/terraform/execute_test.go | 6 +- pkg/recipes/types.go | 2 +- 11 files changed, 213 insertions(+), 61 deletions(-) diff --git a/pkg/corerp/api/v20231001preview/environment_conversion.go b/pkg/corerp/api/v20231001preview/environment_conversion.go index 435a7f2015..016a1337d5 100644 --- a/pkg/corerp/api/v20231001preview/environment_conversion.go +++ b/pkg/corerp/api/v20231001preview/environment_conversion.go @@ -18,6 +18,7 @@ package v20231001preview import ( "fmt" + "reflect" "strings" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" @@ -169,21 +170,27 @@ func (dst *EnvironmentResource) ConvertFrom(src v1.DataModelInterface) error { dst.Properties.Recipes = recipes } - dst.Properties.RecipeConfig = &RecipeConfigProperties{ - Terraform: &TerraformConfigProperties{ - Authentication: &AuthConfig{ - Git: &GitAuthConfig{ - Pat: map[string]*Secret{}, - }, - }, - }, - } - for k, v := range env.Properties.RecipeConfig.Terraform.Authentication.Git.PAT { - dst.Properties.RecipeConfig.Terraform.Authentication.Git.Pat[k] = &Secret{ - SecretStore: to.Ptr(v.SecretStore), + if !reflect.DeepEqual(env.Properties.RecipeConfig, datamodel.RecipeConfigProperties{}) { + recipeConfig := &RecipeConfigProperties{} + if !reflect.DeepEqual(env.Properties.RecipeConfig.Terraform, datamodel.TerraformConfigProperties{}) { + recipeConfig.Terraform = &TerraformConfigProperties{} + if !reflect.DeepEqual(env.Properties.RecipeConfig.Terraform.Authentication, datamodel.AuthConfig{}) { + recipeConfig.Terraform.Authentication = &AuthConfig{} + if !reflect.DeepEqual(env.Properties.RecipeConfig.Terraform.Authentication.Git, datamodel.GitAuthConfig{}) { + recipeConfig.Terraform.Authentication.Git = &GitAuthConfig{} + if env.Properties.RecipeConfig.Terraform.Authentication.Git.PAT != nil { + recipeConfig.Terraform.Authentication.Git.Pat = map[string]*Secret{} + } + } + } + } + for k, v := range env.Properties.RecipeConfig.Terraform.Authentication.Git.PAT { + recipeConfig.Terraform.Authentication.Git.Pat[k] = &Secret{ + SecretStore: to.Ptr(v.SecretStore), + } } + dst.Properties.RecipeConfig = recipeConfig } - if env.Properties.Providers != (datamodel.Providers{}) { dst.Properties.Providers = &Providers{} if env.Properties.Providers.Azure != (datamodel.ProvidersAzure{}) { diff --git a/pkg/corerp/api/v20231001preview/environment_conversion_test.go b/pkg/corerp/api/v20231001preview/environment_conversion_test.go index 6f8ba179ae..92fff82c39 100644 --- a/pkg/corerp/api/v20231001preview/environment_conversion_test.go +++ b/pkg/corerp/api/v20231001preview/environment_conversion_test.go @@ -117,6 +117,19 @@ func TestConvertVersionedToDataModel(t *testing.T) { Scope: "/planes/aws/aws/accounts/140313373712/regions/us-west-2", }, }, + RecipeConfig: datamodel.RecipeConfigProperties{ + Terraform: datamodel.TerraformConfigProperties{ + Authentication: datamodel.AuthConfig{ + Git: datamodel.GitAuthConfig{ + PAT: map[string]datamodel.Secret{ + "dev.azure.com": datamodel.Secret{ + SecretStore: "/planes/radius/local/resourcegroups/default/providers/Applications.Core/secretStores/github", + }, + }, + }, + }, + }, + }, Recipes: map[string]map[string]datamodel.EnvironmentRecipeProperties{ ds_ctrl.MongoDatabasesResourceType: { "cosmos-recipe": datamodel.EnvironmentRecipeProperties{ @@ -368,6 +381,7 @@ func TestConvertDataModelToVersioned(t *testing.T) { if tt.filename == "environmentresourcedatamodel.json" { require.Equal(t, "Azure/cosmosdb/azurerm", string(*versioned.Properties.Recipes[ds_ctrl.MongoDatabasesResourceType]["terraform-recipe"].GetRecipeProperties().TemplatePath)) require.Equal(t, recipes.TemplateKindTerraform, string(*versioned.Properties.Recipes[ds_ctrl.MongoDatabasesResourceType]["terraform-recipe"].GetRecipeProperties().TemplateKind)) + require.Equal(t, "/planes/radius/local/resourcegroups/default/providers/Applications.Core/secretStores/github", string(*versioned.Properties.RecipeConfig.Terraform.Authentication.Git.Pat["dev.azure.com"].SecretStore)) switch c := recipeDetails.(type) { case *TerraformRecipeProperties: require.Equal(t, "1.1.0", string(*c.TemplateVersion)) diff --git a/pkg/corerp/api/v20231001preview/testdata/environmentresource.json b/pkg/corerp/api/v20231001preview/testdata/environmentresource.json index 371de02ab6..8a6067f38d 100644 --- a/pkg/corerp/api/v20231001preview/testdata/environmentresource.json +++ b/pkg/corerp/api/v20231001preview/testdata/environmentresource.json @@ -16,6 +16,19 @@ "scope": "/planes/aws/aws/accounts/140313373712/regions/us-west-2" } }, + "recipeConfig": { + "terraform": { + "authentication": { + "git": { + "pat": { + "dev.azure.com":{ + "secretStore":"/planes/radius/local/resourcegroups/default/providers/Applications.Core/secretStores/github" + } + } + } + } + } + }, "recipes": { "Applications.Datastores/mongoDatabases":{ "cosmos-recipe": { diff --git a/pkg/corerp/api/v20231001preview/testdata/environmentresourcedatamodel.json b/pkg/corerp/api/v20231001preview/testdata/environmentresourcedatamodel.json index fd2d29a3d4..96294d3ecd 100644 --- a/pkg/corerp/api/v20231001preview/testdata/environmentresourcedatamodel.json +++ b/pkg/corerp/api/v20231001preview/testdata/environmentresourcedatamodel.json @@ -29,6 +29,19 @@ "scope": "/planes/aws/aws/accounts/140313373712/regions/us-west-2" } }, + "recipeConfig": { + "terraform": { + "authentication": { + "git": { + "pat": { + "dev.azure.com":{ + "secretStore":"/planes/radius/local/resourcegroups/default/providers/Applications.Core/secretStores/github" + } + } + } + } + } + }, "recipes": { "Applications.Datastores/mongoDatabases":{ "cosmos-recipe": { diff --git a/pkg/recipes/configloader/environment.go b/pkg/recipes/configloader/environment.go index f9967715e7..9ca33c073f 100644 --- a/pkg/recipes/configloader/environment.go +++ b/pkg/recipes/configloader/environment.go @@ -28,7 +28,6 @@ import ( recipes_util "github.com/radius-project/radius/pkg/recipes/util" "github.com/radius-project/radius/pkg/rp/kube" "github.com/radius-project/radius/pkg/rp/util" - "github.com/radius-project/radius/pkg/to" "github.com/radius-project/radius/pkg/ucp/resources" ) @@ -74,7 +73,7 @@ func getConfiguration(environment *v20231001preview.EnvironmentResource, applica config := recipes.Configuration{ Runtime: recipes.RuntimeConfiguration{}, Providers: datamodel.Providers{}, - RecipeConfig: *environment.Properties.RecipeConfig.Terraform.Authentication.Git.Pat["dev.azure.com"].SecretStore, + RecipeConfig: datamodel.RecipeConfigProperties{}, } switch environment.Properties.Compute.(type) { @@ -102,14 +101,15 @@ func getConfiguration(environment *v20231001preview.EnvironmentResource, applica return nil, ErrUnsupportedComputeKind } - providers := environment.Properties.Providers - if providers != nil { - if providers.Aws != nil { - config.Providers.AWS.Scope = to.String(providers.Aws.Scope) - } - if providers.Azure != nil { - config.Providers.Azure.Scope = to.String(providers.Azure.Scope) - } + env, err := environment.ConvertTo() + if err != nil { + return nil, err + } + if environment.Properties.Providers != nil { + config.Providers = env.(*datamodel.Environment).Properties.Providers + } + if environment.Properties.RecipeConfig != nil { + config.RecipeConfig = env.(*datamodel.Environment).Properties.RecipeConfig } if environment.Properties.Simulated != nil && *environment.Properties.Simulated { diff --git a/pkg/recipes/configloader/environment_test.go b/pkg/recipes/configloader/environment_test.go index 24b8f01dbf..f1f62f3f91 100644 --- a/pkg/recipes/configloader/environment_test.go +++ b/pkg/recipes/configloader/environment_test.go @@ -177,6 +177,53 @@ func TestGetConfiguration(t *testing.T) { }, errString: ErrUnsupportedComputeKind.Error(), }, + { + name: "recipe config with env resource", + envResource: &model.EnvironmentResource{ + Properties: &model.EnvironmentProperties{ + Compute: &model.KubernetesCompute{ + Kind: to.Ptr(kind), + Namespace: to.Ptr(envNamespace), + ResourceID: to.Ptr(envResourceId), + }, + RecipeConfig: &model.RecipeConfigProperties{ + Terraform: &model.TerraformConfigProperties{ + Authentication: &model.AuthConfig{ + Git: &model.GitAuthConfig{ + Pat: map[string]*model.Secret{ + "dev.azure.com": &model.Secret{ + SecretStore: to.Ptr("secretStoreID"), + }, + }, + }, + }, + }, + }, + }, + }, + appResource: nil, + expectedConfig: &recipes.Configuration{ + Runtime: recipes.RuntimeConfiguration{ + Kubernetes: &recipes.KubernetesRuntime{ + Namespace: envNamespace, + EnvironmentNamespace: envNamespace, + }, + }, + RecipeConfig: datamodel.RecipeConfigProperties{ + Terraform: datamodel.TerraformConfigProperties{ + Authentication: datamodel.AuthConfig{ + Git: datamodel.GitAuthConfig{ + PAT: map[string]datamodel.Secret{ + "dev.azure.com": datamodel.Secret{ + SecretStore: "secretStoreID", + }, + }, + }, + }, + }, + }, + }, + }, } for _, tc := range configTests { diff --git a/pkg/recipes/terraform/config/config.go b/pkg/recipes/terraform/config/config.go index 3d77abfa0a..0780674c52 100644 --- a/pkg/recipes/terraform/config/config.go +++ b/pkg/recipes/terraform/config/config.go @@ -22,12 +22,18 @@ import ( "errors" "fmt" "io/fs" + "net/url" "os" + "strings" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" + aztoken "github.com/radius-project/radius/pkg/azure/tokencredentials" + "github.com/radius-project/radius/pkg/corerp/api/v20231001preview" "github.com/radius-project/radius/pkg/recipes" "github.com/radius-project/radius/pkg/recipes/recipecontext" "github.com/radius-project/radius/pkg/recipes/terraform/config/backends" "github.com/radius-project/radius/pkg/recipes/terraform/config/providers" + "github.com/radius-project/radius/pkg/ucp/resources" "github.com/radius-project/radius/pkg/ucp/ucplog" ) @@ -38,10 +44,12 @@ const ( // New creates TerraformConfig with the given module name and its inputs (module source, version, parameters) // Parameters are populated from environment recipe and resource recipe metadata. -func New(moduleName string, envRecipe *recipes.EnvironmentDefinition, resourceRecipe *recipes.ResourceMetadata) *TerraformConfig { +func New(ctx context.Context, moduleName string, envRecipe *recipes.EnvironmentDefinition, resourceRecipe *recipes.ResourceMetadata, envConfig *recipes.Configuration, armOptions *arm.ClientOptions) *TerraformConfig { + + path, _ := getTemplatePath(ctx, envConfig, envRecipe, armOptions) // Resource parameter gets precedence over environment level parameter, // if same parameter is defined in both environment and resource recipe metadata. - moduleData := newModuleConfig(envRecipe.TemplatePath, envRecipe.TemplateVersion, envRecipe.Parameters, resourceRecipe.Parameters) + moduleData := newModuleConfig(path, envRecipe.TemplateVersion, envRecipe.Parameters, resourceRecipe.Parameters) return &TerraformConfig{ Terraform: nil, @@ -52,6 +60,70 @@ func New(moduleName string, envRecipe *recipes.EnvironmentDefinition, resourceRe } } +func getTemplatePath(ctx context.Context, envConfig *recipes.Configuration, envRecipe *recipes.EnvironmentDefinition, armOptions *arm.ClientOptions) (string, error) { + var templatePath string + var err error + if strings.HasPrefix(envRecipe.TemplatePath, "git::") { + templatePath, err = getGitTemplatePath(ctx, envConfig, envRecipe, armOptions) + if err != nil { + return "", err + } + } else { + templatePath = envRecipe.TemplatePath + } + return templatePath, err +} + +func getGitTemplatePath(ctx context.Context, envConfig *recipes.Configuration, envRecipe *recipes.EnvironmentDefinition, armOptions *arm.ClientOptions) (string, error) { + secretStore, err := getSecretStoreID(envConfig, envRecipe) + if err != nil { + return "", err + } + if secretStore != "" { + secretStoreID, err := resources.ParseResource(secretStore) + if err != nil { + return "", err + } + client, err := v20231001preview.NewSecretStoresClient(secretStoreID.RootScope(), &aztoken.AnonymousCredential{}, armOptions) + if err != nil { + return "", err + } + secrets, err := client.ListSecrets(ctx, secretStoreID.Name(), map[string]any{}, nil) + if err != nil { + return "", err + } + url, err := getGitURL(envRecipe.TemplatePath) + if err != nil { + return "", err + } + urlString := url.String() + urlString = strings.TrimPrefix(urlString, "https://") + urlString = strings.TrimPrefix(urlString, "http://") + path := fmt.Sprintf("git::https://%s:%s@%s", *secrets.Data["username"].Value, *secrets.Data["pat"].Value, urlString) + return path, err + } + + return envRecipe.TemplatePath, err +} + +func getGitURL(templatePath string) (*url.URL, error) { + paths := strings.Split(templatePath, "git::") + gitUrl := paths[len(paths)-1] + url, err := url.Parse(gitUrl) + if err != nil { + return nil, err + } + return url, nil +} + +func getSecretStoreID(envConfig *recipes.Configuration, envRecipe *recipes.EnvironmentDefinition) (string, error) { + url, err := getGitURL(envRecipe.TemplatePath) + if err != nil { + return "", err + } + return envConfig.RecipeConfig.Terraform.Authentication.Git.PAT[strings.TrimPrefix(url.Hostname(), "www.")].SecretStore, nil +} + // getMainConfigFilePath returns the path of the Terraform main config file. func getMainConfigFilePath(workingDir string) string { return fmt.Sprintf("%s/%s", workingDir, mainConfigFileName) diff --git a/pkg/recipes/terraform/config/config_test.go b/pkg/recipes/terraform/config/config_test.go index 17aebc0be3..f0fc535c4d 100644 --- a/pkg/recipes/terraform/config/config_test.go +++ b/pkg/recipes/terraform/config/config_test.go @@ -17,6 +17,7 @@ limitations under the License. package config import ( + "context" "errors" "fmt" "os" @@ -179,7 +180,7 @@ func Test_NewConfig(t *testing.T) { t.Run(tc.desc, func(t *testing.T) { workingDir := t.TempDir() - tfconfig := New(testRecipeName, tc.envdef, tc.metadata) + tfconfig := New(context.Background(), testRecipeName, tc.envdef, tc.metadata, nil, nil) // validate generated config err := tfconfig.Save(testcontext.New(t), workingDir) @@ -272,7 +273,7 @@ func Test_AddRecipeContext(t *testing.T) { ctx := testcontext.New(t) workingDir := t.TempDir() - tfconfig := New(testRecipeName, tc.envdef, tc.metadata) + tfconfig := New(context.Background(), testRecipeName, tc.envdef, tc.metadata, nil, nil) err := tfconfig.AddRecipeContext(ctx, tc.moduleName, tc.recipeContext) if tc.err == "" { @@ -416,7 +417,7 @@ func Test_AddProviders(t *testing.T) { ctx := testcontext.New(t) workingDir := t.TempDir() - tfconfig := New(testRecipeName, &envRecipe, &resourceRecipe) + tfconfig := New(context.Background(), testRecipeName, &envRecipe, &resourceRecipe, nil, nil) for _, p := range tc.expectedProviders { mProvider.EXPECT().BuildConfig(ctx, &tc.envConfig).Times(1).Return(p, nil) } @@ -476,7 +477,7 @@ func Test_AddOutputs(t *testing.T) { for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { - tfconfig := New(testRecipeName, &envRecipe, &resourceRecipe) + tfconfig := New(context.Background(), testRecipeName, &envRecipe, &resourceRecipe, nil, nil) err := tfconfig.AddOutputs(tc.moduleName) if tc.expectedErr { @@ -505,7 +506,7 @@ func Test_Save_overwrite(t *testing.T) { ctx := testcontext.New(t) testDir := t.TempDir() envRecipe, resourceRecipe := getTestInputs() - tfconfig := New(testRecipeName, &envRecipe, &resourceRecipe) + tfconfig := New(context.Background(), testRecipeName, &envRecipe, &resourceRecipe, nil, nil) err := tfconfig.Save(ctx, testDir) require.NoError(t, err) @@ -517,7 +518,7 @@ func Test_Save_overwrite(t *testing.T) { func Test_Save_ConfigFileReadOnly(t *testing.T) { testDir := t.TempDir() envRecipe, resourceRecipe := getTestInputs() - tfconfig := New(testRecipeName, &envRecipe, &resourceRecipe) + tfconfig := New(context.Background(), testRecipeName, &envRecipe, &resourceRecipe, nil, nil) // Create a test configuration file with read only permission. err := os.WriteFile(getMainConfigFilePath(testDir), []byte(`{"module":{}}`), 0400) @@ -533,7 +534,7 @@ func Test_Save_InvalidWorkingDir(t *testing.T) { testDir := filepath.Join("invalid", uuid.New().String()) envRecipe, resourceRecipe := getTestInputs() - tfconfig := New(testRecipeName, &envRecipe, &resourceRecipe) + tfconfig := New(context.Background(), testRecipeName, &envRecipe, &resourceRecipe, nil, nil) err := tfconfig.Save(testcontext.New(t), testDir) require.Error(t, err) diff --git a/pkg/recipes/terraform/execute.go b/pkg/recipes/terraform/execute.go index f03d252058..c9966f2ea6 100644 --- a/pkg/recipes/terraform/execute.go +++ b/pkg/recipes/terraform/execute.go @@ -26,8 +26,6 @@ import ( install "github.com/hashicorp/hc-install" "github.com/hashicorp/terraform-exec/tfexec" tfjson "github.com/hashicorp/terraform-json" - aztoken "github.com/radius-project/radius/pkg/azure/tokencredentials" - "github.com/radius-project/radius/pkg/corerp/api/v20231001preview" "github.com/radius-project/radius/pkg/metrics" "github.com/radius-project/radius/pkg/recipes" "github.com/radius-project/radius/pkg/recipes/recipecontext" @@ -36,7 +34,6 @@ import ( "github.com/radius-project/radius/pkg/recipes/terraform/config/providers" "github.com/radius-project/radius/pkg/recipes/util" "github.com/radius-project/radius/pkg/sdk" - "github.com/radius-project/radius/pkg/ucp/resources" ucp_provider "github.com/radius-project/radius/pkg/ucp/secret/provider" "github.com/radius-project/radius/pkg/ucp/ucplog" "go.opentelemetry.io/otel/attribute" @@ -185,12 +182,12 @@ func (e *executor) GetRecipeMetadata(ctx context.Context, options Options) (map[ return nil, err } - _, err = getTerraformConfig(ctx, tf.WorkingDir(), options) + _, err = getTerraformConfig(ctx, tf.WorkingDir(), options, e.armOptions) if err != nil { return nil, err } - result, err := downloadAndInspect(ctx, tf, options, e.armOptions) + result, err := downloadAndInspect(ctx, tf, options) if err != nil { return nil, err } @@ -205,12 +202,12 @@ func (e *executor) generateConfig(ctx context.Context, tf *tfexec.Terraform, opt logger := ucplog.FromContextOrDiscard(ctx) workingDir := tf.WorkingDir() - tfConfig, err := getTerraformConfig(ctx, workingDir, options) + tfConfig, err := getTerraformConfig(ctx, workingDir, options, e.armOptions) if err != nil { return "", err } - loadedModule, err := downloadAndInspect(ctx, tf, options, e.armOptions) + loadedModule, err := downloadAndInspect(ctx, tf, options) if err != nil { return "", err } @@ -268,15 +265,15 @@ func (e *executor) generateConfig(ctx context.Context, tf *tfexec.Terraform, opt } // downloadAndInspect handles downloading the TF module and retrieving the necessary information -func downloadAndInspect(ctx context.Context, tf *tfexec.Terraform, options Options, armOptions *arm.ClientOptions) (*moduleInspectResult, error) { +func downloadAndInspect(ctx context.Context, tf *tfexec.Terraform, options Options) (*moduleInspectResult, error) { logger := ucplog.FromContextOrDiscard(ctx) // Download the Terraform module to the working directory. logger.Info(fmt.Sprintf("Downloading Terraform module: %s", options.EnvRecipe.TemplatePath)) downloadStartTime := time.Now() - path, _ := getTemplatePath(ctx, options, armOptions) + //path := options.EnvRecipe.TemplatePath - if err := downloadModule(ctx, tf, path); err != nil { + if err := downloadModule(ctx, tf, options.EnvRecipe.TemplatePath); err != nil { metrics.DefaultRecipeEngineMetrics.RecordRecipeDownloadDuration(ctx, downloadStartTime, metrics.NewRecipeAttributes(metrics.RecipeEngineOperationDownloadRecipe, options.EnvRecipe.Name, options.EnvRecipe, recipes.RecipeDownloadFailed)) @@ -288,7 +285,7 @@ func downloadAndInspect(ctx context.Context, tf *tfexec.Terraform, options Optio // Load the downloaded module to retrieve providers and variables required by the module. // This is needed to add the appropriate providers config and populate the value of recipe context variable. - logger.Info(fmt.Sprintf("Inspecting the downloaded Terraform module: %s", path)) + logger.Info(fmt.Sprintf("Inspecting the downloaded Terraform module: %s", options.EnvRecipe.TemplatePath)) loadedModule, err := inspectModule(tf.WorkingDir(), options.EnvRecipe) if err != nil { return nil, err @@ -297,20 +294,8 @@ func downloadAndInspect(ctx context.Context, tf *tfexec.Terraform, options Optio return loadedModule, nil } -func getTemplatePath(ctx context.Context, options Options, armOptions *arm.ClientOptions) (string, error) { - secretStoreID, err := resources.ParseResource(options.EnvConfig.RecipeConfig) - if err != nil { - return "", err - } - client, err := v20231001preview.NewSecretStoresClient(secretStoreID.RootScope(), &aztoken.AnonymousCredential{}, armOptions) - p, err := client.ListSecrets(ctx, secretStoreID.Name(), map[string]any{}, nil) - fmt.Println(p) - path := fmt.Sprintf("git::https://%s:%s@%s", *p.Data["username"].Value, *p.Data["pat"].Value, options.EnvRecipe.TemplatePath) - return path, err -} - // getTerraformConfig initializes the Terraform json config with provided module source and saves it -func getTerraformConfig(ctx context.Context, workingDir string, options Options) (*config.TerraformConfig, error) { +func getTerraformConfig(ctx context.Context, workingDir string, options Options, armOptions *arm.ClientOptions) (*config.TerraformConfig, error) { // Generate Terraform json config in the working directory // Use recipe name as a local reference to the module. // Modules are downloaded in a subdirectory in the working directory. Name of the module specified in the @@ -322,7 +307,7 @@ func getTerraformConfig(ctx context.Context, workingDir string, options Options) } // Create Terraform configuration containing module information with the given recipe parameters. - tfConfig := config.New(localModuleName, options.EnvRecipe, options.ResourceRecipe) + tfConfig := config.New(ctx, localModuleName, options.EnvRecipe, options.ResourceRecipe, options.EnvConfig, armOptions) // Before downloading the module, Teraform configuration needs to be persisted in the working directory. // Terraform Get command uses this config file to download module from the source specified in the config. diff --git a/pkg/recipes/terraform/execute_test.go b/pkg/recipes/terraform/execute_test.go index 87e54e8e37..4e953ecea8 100644 --- a/pkg/recipes/terraform/execute_test.go +++ b/pkg/recipes/terraform/execute_test.go @@ -78,7 +78,7 @@ func Test_GetTerraformConfig(t *testing.T) { Module: map[string]config.TFModuleConfig{ "test-recipe": {"source": "test/module/source"}}, } - tfConfig, err := getTerraformConfig(testcontext.New(t), testDir, options) + tfConfig, err := getTerraformConfig(testcontext.New(t), testDir, options, nil) require.NoError(t, err) require.Equal(t, &expectedConfig, tfConfig) } @@ -95,7 +95,7 @@ func Test_GetTerraformConfig_EmptyRecipeName(t *testing.T) { ResourceRecipe: &recipes.ResourceMetadata{}, } - _, err := getTerraformConfig(testcontext.New(t), testDir, options) + _, err := getTerraformConfig(testcontext.New(t), testDir, options, nil) require.Error(t, err) require.Equal(t, err, ErrRecipeNameEmpty) } @@ -110,7 +110,7 @@ func Test_GetTerraformConfig_InvalidDirectory(t *testing.T) { ResourceRecipe: &recipes.ResourceMetadata{}, } - _, err := getTerraformConfig(testcontext.New(t), workingDir, options) + _, err := getTerraformConfig(testcontext.New(t), workingDir, options, nil) require.Error(t, err) require.Contains(t, err.Error(), "error creating file: open invalid-directory/main.tf.json: no such file or directory") } diff --git a/pkg/recipes/types.go b/pkg/recipes/types.go index 5250ef40f6..50da74df49 100644 --- a/pkg/recipes/types.go +++ b/pkg/recipes/types.go @@ -33,7 +33,7 @@ type Configuration struct { // Simulated represents whether the environment is simulated or not. Simulated bool - RecipeConfig string + RecipeConfig datamodel.RecipeConfigProperties } // RuntimeConfiguration represents Kubernetes Runtime configuration for the environment. From de5399d4bfd728804f23c4ca3c6a6d5dc6362173 Mon Sep 17 00:00:00 2001 From: Vishwanath Hiremath Date: Mon, 12 Feb 2024 23:39:07 -0800 Subject: [PATCH 03/22] refactor Signed-off-by: Vishwanath Hiremath --- pkg/recipes/terraform/config/config.go | 44 +++++++++++++-------- pkg/recipes/terraform/config/config_test.go | 22 +++++++++++ 2 files changed, 49 insertions(+), 17 deletions(-) diff --git a/pkg/recipes/terraform/config/config.go b/pkg/recipes/terraform/config/config.go index 0780674c52..639d61b582 100644 --- a/pkg/recipes/terraform/config/config.go +++ b/pkg/recipes/terraform/config/config.go @@ -46,7 +46,7 @@ const ( // Parameters are populated from environment recipe and resource recipe metadata. func New(ctx context.Context, moduleName string, envRecipe *recipes.EnvironmentDefinition, resourceRecipe *recipes.ResourceMetadata, envConfig *recipes.Configuration, armOptions *arm.ClientOptions) *TerraformConfig { - path, _ := getTemplatePath(ctx, envConfig, envRecipe, armOptions) + path, _ := getModuleSource(ctx, envConfig, envRecipe.TemplatePath, armOptions) // Resource parameter gets precedence over environment level parameter, // if same parameter is defined in both environment and resource recipe metadata. moduleData := newModuleConfig(path, envRecipe.TemplateVersion, envRecipe.Parameters, resourceRecipe.Parameters) @@ -60,22 +60,22 @@ func New(ctx context.Context, moduleName string, envRecipe *recipes.EnvironmentD } } -func getTemplatePath(ctx context.Context, envConfig *recipes.Configuration, envRecipe *recipes.EnvironmentDefinition, armOptions *arm.ClientOptions) (string, error) { - var templatePath string +func getModuleSource(ctx context.Context, envConfig *recipes.Configuration, templatePath string, armOptions *arm.ClientOptions) (string, error) { + var source string var err error - if strings.HasPrefix(envRecipe.TemplatePath, "git::") { - templatePath, err = getGitTemplatePath(ctx, envConfig, envRecipe, armOptions) + if strings.HasPrefix(templatePath, "git::") { + source, err = getGitSource(ctx, envConfig, templatePath, armOptions) if err != nil { return "", err } } else { - templatePath = envRecipe.TemplatePath + source = templatePath } - return templatePath, err + return source, err } -func getGitTemplatePath(ctx context.Context, envConfig *recipes.Configuration, envRecipe *recipes.EnvironmentDefinition, armOptions *arm.ClientOptions) (string, error) { - secretStore, err := getSecretStoreID(envConfig, envRecipe) +func getGitSource(ctx context.Context, envConfig *recipes.Configuration, templatePath string, armOptions *arm.ClientOptions) (string, error) { + secretStore, err := getSecretStoreID(*envConfig, templatePath) if err != nil { return "", err } @@ -92,18 +92,27 @@ func getGitTemplatePath(ctx context.Context, envConfig *recipes.Configuration, e if err != nil { return "", err } - url, err := getGitURL(envRecipe.TemplatePath) + url, err := getGitURL(templatePath) if err != nil { return "", err } - urlString := url.String() - urlString = strings.TrimPrefix(urlString, "https://") - urlString = strings.TrimPrefix(urlString, "http://") - path := fmt.Sprintf("git::https://%s:%s@%s", *secrets.Data["username"].Value, *secrets.Data["pat"].Value, urlString) + var username, pat *string + path := "git::https://" + user, ok := secrets.Data["username"] + if ok { + username = user.Value + path += fmt.Sprintf("%s:", *username) + } + token, ok := secrets.Data["pat"] + if ok { + pat = token.Value + path += *pat + } + path += fmt.Sprintf("@%s", strings.TrimPrefix(url.String(), "https://")) return path, err } - return envRecipe.TemplatePath, err + return templatePath, err } func getGitURL(templatePath string) (*url.URL, error) { @@ -116,11 +125,12 @@ func getGitURL(templatePath string) (*url.URL, error) { return url, nil } -func getSecretStoreID(envConfig *recipes.Configuration, envRecipe *recipes.EnvironmentDefinition) (string, error) { - url, err := getGitURL(envRecipe.TemplatePath) +func getSecretStoreID(envConfig recipes.Configuration, templatePath string) (string, error) { + url, err := getGitURL(templatePath) if err != nil { return "", err } + return envConfig.RecipeConfig.Terraform.Authentication.Git.PAT[strings.TrimPrefix(url.Hostname(), "www.")].SecretStore, nil } diff --git a/pkg/recipes/terraform/config/config_test.go b/pkg/recipes/terraform/config/config_test.go index f0fc535c4d..1cb2b4770c 100644 --- a/pkg/recipes/terraform/config/config_test.go +++ b/pkg/recipes/terraform/config/config_test.go @@ -540,3 +540,25 @@ func Test_Save_InvalidWorkingDir(t *testing.T) { require.Error(t, err) require.Equal(t, fmt.Sprintf("error creating file: open %s/main.tf.json: no such file or directory", testDir), err.Error()) } + +func Test_getSecretStoreID(t *testing.T) { + config := &recipes.Configuration{ + RecipeConfig: datamodel.RecipeConfigProperties{ + Terraform: datamodel.TerraformConfigProperties{ + Authentication: datamodel.AuthConfig{ + Git: datamodel.GitAuthConfig{ + PAT: map[string]datamodel.Secret{ + "dev.azure.com": datamodel.Secret{ + SecretStore: "secret-store1", + }, + }, + }, + }, + }, + }, + } + templatePath := "https://dev.azure.com/recipes/redis" + path, err := getSecretStoreID(*config, templatePath) + require.Equal(t, "secret-store1", path) + require.NoError(t, err) +} From ce877cc82bc179776da1d099709a2a0186094667 Mon Sep 17 00:00:00 2001 From: Vishwanath Hiremath Date: Tue, 13 Feb 2024 00:53:23 -0800 Subject: [PATCH 04/22] adding comments Signed-off-by: Vishwanath Hiremath --- .../2023-10-01-preview/types.json | 2 +- pkg/corerp/datamodel/environment.go | 10 ++++ pkg/recipes/terraform/config/config.go | 6 ++- pkg/recipes/terraform/config/config_test.go | 47 ++++++++++++++----- 4 files changed, 51 insertions(+), 14 deletions(-) diff --git a/hack/bicep-types-radius/generated/applications/applications.core/2023-10-01-preview/types.json b/hack/bicep-types-radius/generated/applications/applications.core/2023-10-01-preview/types.json index 4ac34c99dc..9630aaa167 100644 --- a/hack/bicep-types-radius/generated/applications/applications.core/2023-10-01-preview/types.json +++ b/hack/bicep-types-radius/generated/applications/applications.core/2023-10-01-preview/types.json @@ -1 +1 @@ -[{"1":{"Kind":1}},{"1":{"Kind":2}},{"1":{"Kind":3}},{"1":{"Kind":4}},{"1":{"Kind":5}},{"1":{"Kind":6}},{"1":{"Kind":7}},{"1":{"Kind":8}},{"6":{"Value":"Applications.Core/applications"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/applications","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":8,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":9,"Flags":10,"Description":"The resource api version"},"properties":{"Type":11,"Flags":1,"Description":"Application properties"},"tags":{"Type":46,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ApplicationProperties","Properties":{"provisioningState":{"Type":19,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"environment":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"extensions":{"Type":34,"Flags":0,"Description":"The application extension."},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[12,13,14,15,16,17,18]}},{"7":{"Name":"Extension","Discriminator":"kind","BaseProperties":{},"Elements":{"daprSidecar":21,"kubernetesMetadata":26,"kubernetesNamespace":30,"manualScaling":32}}},{"2":{"Name":"DaprSidecarExtension","Properties":{"appPort":{"Type":3,"Flags":0,"Description":"The Dapr appPort. Specifies the internal listening port for the application to handle requests from the Dapr sidecar."},"appId":{"Type":4,"Flags":1,"Description":"The Dapr appId. Specifies the identifier used by Dapr for service invocation."},"config":{"Type":4,"Flags":0,"Description":"Specifies the Dapr configuration to use for the resource."},"protocol":{"Type":24,"Flags":0,"Description":"The Dapr sidecar extension protocol"},"kind":{"Type":25,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"http"}},{"6":{"Value":"grpc"}},{"5":{"Elements":[22,23]}},{"6":{"Value":"daprSidecar"}},{"2":{"Name":"KubernetesMetadataExtension","Properties":{"annotations":{"Type":27,"Flags":0,"Description":"Annotations to be applied to the Kubernetes resources output by the resource"},"labels":{"Type":28,"Flags":0,"Description":"Labels to be applied to the Kubernetes resources output by the resource"},"kind":{"Type":29,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"2":{"Name":"KubernetesMetadataExtensionAnnotations","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"KubernetesMetadataExtensionLabels","Properties":{},"AdditionalProperties":4}},{"6":{"Value":"kubernetesMetadata"}},{"2":{"Name":"KubernetesNamespaceExtension","Properties":{"namespace":{"Type":4,"Flags":1,"Description":"The namespace of the application environment."},"kind":{"Type":31,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"kubernetesNamespace"}},{"2":{"Name":"ManualScalingExtension","Properties":{"replicas":{"Type":3,"Flags":1,"Description":"Replica count."},"kind":{"Type":33,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"manualScaling"}},{"3":{"ItemType":20}},{"2":{"Name":"ResourceStatus","Properties":{"compute":{"Type":36,"Flags":0,"Description":"Represents backing compute resource"},"recipe":{"Type":43,"Flags":2,"Description":"Recipe status at deployment time for a resource."},"outputResources":{"Type":45,"Flags":0,"Description":"Properties of an output resource"}}}},{"7":{"Name":"EnvironmentCompute","Discriminator":"kind","BaseProperties":{"resourceId":{"Type":4,"Flags":0,"Description":"The resource id of the compute resource for application environment."},"identity":{"Type":37,"Flags":0,"Description":"IdentitySettings is the external identity setting."}},"Elements":{"kubernetes":41}}},{"2":{"Name":"IdentitySettings","Properties":{"kind":{"Type":40,"Flags":1,"Description":"IdentitySettingKind is the kind of supported external identity setting"},"oidcIssuer":{"Type":4,"Flags":0,"Description":"The URI for your compute platform's OIDC issuer"},"resource":{"Type":4,"Flags":0,"Description":"The resource ID of the provisioned identity"}}}},{"6":{"Value":"undefined"}},{"6":{"Value":"azure.com.workload"}},{"5":{"Elements":[38,39]}},{"2":{"Name":"KubernetesCompute","Properties":{"namespace":{"Type":4,"Flags":1,"Description":"The namespace to use for the environment."},"kind":{"Type":42,"Flags":1,"Description":"Discriminator property for EnvironmentCompute."}}}},{"6":{"Value":"kubernetes"}},{"2":{"Name":"RecipeStatus","Properties":{"templateKind":{"Type":4,"Flags":1,"Description":"TemplateKind is the kind of the recipe template used by the portable resource upon deployment."},"templatePath":{"Type":4,"Flags":1,"Description":"TemplatePath is the path of the recipe consumed by the portable resource upon deployment."},"templateVersion":{"Type":4,"Flags":0,"Description":"TemplateVersion is the version number of the template."}}}},{"2":{"Name":"OutputResource","Properties":{"localId":{"Type":4,"Flags":0,"Description":"The logical identifier scoped to the owning Radius resource. This is only needed or used when a resource has a dependency relationship. LocalIDs do not have any particular format or meaning beyond being compared to determine dependency relationships."},"id":{"Type":4,"Flags":0,"Description":"The UCP resource ID of the underlying resource."},"radiusManaged":{"Type":2,"Flags":0,"Description":"Determines whether Radius manages the lifecycle of the underlying resource."}}}},{"3":{"ItemType":44}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"SystemData","Properties":{"createdBy":{"Type":4,"Flags":0,"Description":"The identity that created the resource."},"createdByType":{"Type":52,"Flags":0,"Description":"The type of identity that created the resource."},"createdAt":{"Type":4,"Flags":0,"Description":"The timestamp of resource creation (UTC)."},"lastModifiedBy":{"Type":4,"Flags":0,"Description":"The identity that last modified the resource."},"lastModifiedByType":{"Type":57,"Flags":0,"Description":"The type of identity that created the resource."},"lastModifiedAt":{"Type":4,"Flags":0,"Description":"The timestamp of resource last modification (UTC)"}}}},{"6":{"Value":"User"}},{"6":{"Value":"Application"}},{"6":{"Value":"ManagedIdentity"}},{"6":{"Value":"Key"}},{"5":{"Elements":[48,49,50,51]}},{"6":{"Value":"User"}},{"6":{"Value":"Application"}},{"6":{"Value":"ManagedIdentity"}},{"6":{"Value":"Key"}},{"5":{"Elements":[53,54,55,56]}},{"4":{"Name":"Applications.Core/applications@2023-10-01-preview","ScopeType":0,"Body":10}},{"6":{"Value":"Applications.Core/containers"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/containers","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":59,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":60,"Flags":10,"Description":"The resource api version"},"properties":{"Type":62,"Flags":1,"Description":"Container properties"},"tags":{"Type":122,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ContainerProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":70,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"container":{"Type":71,"Flags":1,"Description":"Definition of a container"},"connections":{"Type":108,"Flags":0,"Description":"Specifies a connection to another resource."},"identity":{"Type":37,"Flags":0,"Description":"IdentitySettings is the external identity setting."},"extensions":{"Type":109,"Flags":0,"Description":"Extensions spec of the resource"},"resourceProvisioning":{"Type":112,"Flags":0,"Description":"Specifies how the underlying service/resource is provisioned and managed. Available values are 'internal', where Radius manages the lifecycle of the resource internally, and 'manual', where a user manages the resource."},"resources":{"Type":114,"Flags":0,"Description":"A collection of references to resources associated with the container"},"restartPolicy":{"Type":118,"Flags":0,"Description":"Restart policy for the container"},"runtimes":{"Type":119,"Flags":0,"Description":"The properties for runtime configuration"}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[63,64,65,66,67,68,69]}},{"2":{"Name":"Container","Properties":{"image":{"Type":4,"Flags":1,"Description":"The registry and image to download and run in your container"},"imagePullPolicy":{"Type":75,"Flags":0,"Description":"The image pull policy for the container"},"env":{"Type":76,"Flags":0,"Description":"environment"},"ports":{"Type":81,"Flags":0,"Description":"container ports"},"readinessProbe":{"Type":82,"Flags":0,"Description":"Properties for readiness/liveness probe"},"livenessProbe":{"Type":82,"Flags":0,"Description":"Properties for readiness/liveness probe"},"volumes":{"Type":101,"Flags":0,"Description":"container volumes"},"command":{"Type":102,"Flags":0,"Description":"Entrypoint array. Overrides the container image's ENTRYPOINT"},"args":{"Type":103,"Flags":0,"Description":"Arguments to the entrypoint. Overrides the container image's CMD"},"workingDir":{"Type":4,"Flags":0,"Description":"Working directory for the container"}}}},{"6":{"Value":"Always"}},{"6":{"Value":"IfNotPresent"}},{"6":{"Value":"Never"}},{"5":{"Elements":[72,73,74]}},{"2":{"Name":"ContainerEnv","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"ContainerPortProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"protocol":{"Type":80,"Flags":0,"Description":"The protocol in use by the port"},"provides":{"Type":4,"Flags":0,"Description":"Specifies a route provided by this port"},"scheme":{"Type":4,"Flags":0,"Description":"Specifies the URL scheme of the communication protocol. Consumers can use the scheme to construct a URL. The value defaults to 'http' or 'https' depending on the port value"},"port":{"Type":3,"Flags":0,"Description":"Specifies the port that will be exposed by this container. Must be set when value different from containerPort is desired"}}}},{"6":{"Value":"TCP"}},{"6":{"Value":"UDP"}},{"5":{"Elements":[78,79]}},{"2":{"Name":"ContainerPorts","Properties":{},"AdditionalProperties":77}},{"7":{"Name":"HealthProbeProperties","Discriminator":"kind","BaseProperties":{"initialDelaySeconds":{"Type":3,"Flags":0,"Description":"Initial delay in seconds before probing for readiness/liveness"},"failureThreshold":{"Type":3,"Flags":0,"Description":"Threshold number of times the probe fails after which a failure would be reported"},"periodSeconds":{"Type":3,"Flags":0,"Description":"Interval for the readiness/liveness probe in seconds"},"timeoutSeconds":{"Type":3,"Flags":0,"Description":"Number of seconds after which the readiness/liveness probe times out. Defaults to 5 seconds"}},"Elements":{"exec":83,"httpGet":85,"tcp":88}}},{"2":{"Name":"ExecHealthProbeProperties","Properties":{"command":{"Type":4,"Flags":1,"Description":"Command to execute to probe readiness/liveness"},"kind":{"Type":84,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"6":{"Value":"exec"}},{"2":{"Name":"HttpGetHealthProbeProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"path":{"Type":4,"Flags":1,"Description":"The route to make the HTTP request on"},"headers":{"Type":86,"Flags":0,"Description":"Custom HTTP headers to add to the get request"},"kind":{"Type":87,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"2":{"Name":"HttpGetHealthProbePropertiesHeaders","Properties":{},"AdditionalProperties":4}},{"6":{"Value":"httpGet"}},{"2":{"Name":"TcpHealthProbeProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"kind":{"Type":89,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"6":{"Value":"tcp"}},{"7":{"Name":"Volume","Discriminator":"kind","BaseProperties":{"mountPath":{"Type":4,"Flags":0,"Description":"The path where the volume is mounted"}},"Elements":{"ephemeral":91,"persistent":96}}},{"2":{"Name":"EphemeralVolume","Properties":{"managedStore":{"Type":94,"Flags":1,"Description":"The managed store for the ephemeral volume"},"kind":{"Type":95,"Flags":1,"Description":"Discriminator property for Volume."}}}},{"6":{"Value":"memory"}},{"6":{"Value":"disk"}},{"5":{"Elements":[92,93]}},{"6":{"Value":"ephemeral"}},{"2":{"Name":"PersistentVolume","Properties":{"permission":{"Type":99,"Flags":0,"Description":"The persistent volume permission"},"source":{"Type":4,"Flags":1,"Description":"The source of the volume"},"kind":{"Type":100,"Flags":1,"Description":"Discriminator property for Volume."}}}},{"6":{"Value":"read"}},{"6":{"Value":"write"}},{"5":{"Elements":[97,98]}},{"6":{"Value":"persistent"}},{"2":{"Name":"ContainerVolumes","Properties":{},"AdditionalProperties":90}},{"3":{"ItemType":4}},{"3":{"ItemType":4}},{"2":{"Name":"ConnectionProperties","Properties":{"source":{"Type":4,"Flags":1,"Description":"The source of the connection"},"disableDefaultEnvVars":{"Type":2,"Flags":0,"Description":"default environment variable override"},"iam":{"Type":105,"Flags":0,"Description":"IAM properties"}}}},{"2":{"Name":"IamProperties","Properties":{"kind":{"Type":106,"Flags":1,"Description":"The kind of IAM provider to configure"},"roles":{"Type":107,"Flags":0,"Description":"RBAC permissions to be assigned on the source resource"}}}},{"6":{"Value":"azure"}},{"3":{"ItemType":4}},{"2":{"Name":"ContainerPropertiesConnections","Properties":{},"AdditionalProperties":104}},{"3":{"ItemType":20}},{"6":{"Value":"internal"}},{"6":{"Value":"manual"}},{"5":{"Elements":[110,111]}},{"2":{"Name":"ResourceReference","Properties":{"id":{"Type":4,"Flags":1,"Description":"Resource id of an existing resource"}}}},{"3":{"ItemType":113}},{"6":{"Value":"Always"}},{"6":{"Value":"OnFailure"}},{"6":{"Value":"Never"}},{"5":{"Elements":[115,116,117]}},{"2":{"Name":"RuntimesProperties","Properties":{"kubernetes":{"Type":120,"Flags":0,"Description":"The runtime configuration properties for Kubernetes"}}}},{"2":{"Name":"KubernetesRuntimeProperties","Properties":{"base":{"Type":4,"Flags":0,"Description":"The serialized YAML manifest which represents the base Kubernetes resources to deploy, such as Deployment, Service, ServiceAccount, Secrets, and ConfigMaps."},"pod":{"Type":121,"Flags":0,"Description":"A strategic merge patch that will be applied to the PodSpec object when this container is being deployed."}}}},{"2":{"Name":"KubernetesPodSpec","Properties":{},"AdditionalProperties":0}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/containers@2023-10-01-preview","ScopeType":0,"Body":61}},{"6":{"Value":"Applications.Core/environments"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/environments","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":124,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":125,"Flags":10,"Description":"The resource api version"},"properties":{"Type":127,"Flags":1,"Description":"Environment properties"},"tags":{"Type":153,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"EnvironmentProperties","Properties":{"provisioningState":{"Type":135,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"compute":{"Type":36,"Flags":1,"Description":"Represents backing compute resource"},"providers":{"Type":136,"Flags":0,"Description":"The Cloud providers configuration"},"simulated":{"Type":2,"Flags":0,"Description":"Simulated environment."},"recipes":{"Type":145,"Flags":0,"Description":"Specifies Recipes linked to the Environment."},"recipeConfig":{"Type":146,"Flags":0,"Description":"Specifies recipe configurations needed for the recipes."},"extensions":{"Type":152,"Flags":0,"Description":"The environment extension."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[128,129,130,131,132,133,134]}},{"2":{"Name":"Providers","Properties":{"azure":{"Type":137,"Flags":0,"Description":"The Azure cloud provider definition"},"aws":{"Type":138,"Flags":0,"Description":"The AWS cloud provider definition"}}}},{"2":{"Name":"ProvidersAzure","Properties":{"scope":{"Type":4,"Flags":1,"Description":"Target scope for Azure resources to be deployed into. For example: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup'"}}}},{"2":{"Name":"ProvidersAws","Properties":{"scope":{"Type":4,"Flags":1,"Description":"Target scope for AWS resources to be deployed into. For example: '/planes/aws/aws/accounts/000000000000/regions/us-west-2'"}}}},{"7":{"Name":"RecipeProperties","Discriminator":"templateKind","BaseProperties":{"templatePath":{"Type":4,"Flags":1,"Description":"Path to the template provided by the recipe. Currently only link to Azure Container Registry is supported."},"parameters":{"Type":0,"Flags":0,"Description":"Any object"}},"Elements":{"bicep":140,"terraform":142}}},{"2":{"Name":"BicepRecipeProperties","Properties":{"plainHttp":{"Type":2,"Flags":0,"Description":"Connect to the Bicep registry using HTTP (not-HTTPS). This should be used when the registry is known not to support HTTPS, for example in a locally-hosted registry. Defaults to false (use HTTPS/TLS)."},"templateKind":{"Type":141,"Flags":1,"Description":"Discriminator property for RecipeProperties."}}}},{"6":{"Value":"bicep"}},{"2":{"Name":"TerraformRecipeProperties","Properties":{"templateVersion":{"Type":4,"Flags":0,"Description":"Version of the template to deploy. For Terraform recipes using a module registry this is required, but must be omitted for other module sources."},"templateKind":{"Type":143,"Flags":1,"Description":"Discriminator property for RecipeProperties."}}}},{"6":{"Value":"terraform"}},{"2":{"Name":"DictionaryOfRecipeProperties","Properties":{},"AdditionalProperties":139}},{"2":{"Name":"EnvironmentPropertiesRecipes","Properties":{},"AdditionalProperties":144}},{"2":{"Name":"RecipeConfigProperties","Properties":{"terraform":{"Type":147,"Flags":0,"Description":"Specifies the terraform config properties"}}}},{"2":{"Name":"TerraformConfigProperties","Properties":{"authentication":{"Type":148,"Flags":0,"Description":"Specifies authentication information needed to use private terraform module repositories."}}}},{"2":{"Name":"AuthConfig","Properties":{"git":{"Type":149,"Flags":0,"Description":"Specifies authentication information needed to use private terraform module repositories from git module source"}}}},{"2":{"Name":"GitAuthConfig","Properties":{"pat":{"Type":151,"Flags":0,"Description":"Specifies the secret details of type personal access token for each different git platforms"}}}},{"2":{"Name":"Secret","Properties":{"secret":{"Type":4,"Flags":0,"Description":"The resource id for the secret containing credentials"}}}},{"2":{"Name":"GitAuthConfigPat","Properties":{},"AdditionalProperties":150}},{"3":{"ItemType":20}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/environments@2023-10-01-preview","ScopeType":0,"Body":126}},{"6":{"Value":"Applications.Core/extenders"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/extenders","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":155,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":156,"Flags":10,"Description":"The resource api version"},"properties":{"Type":158,"Flags":1,"Description":"ExtenderResource portable resource properties"},"tags":{"Type":171,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ExtenderProperties","Properties":{"environment":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the environment that the portable resource is linked to"},"application":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the application that the portable resource is consumed by (if applicable)"},"provisioningState":{"Type":166,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"secrets":{"Type":0,"Flags":0,"Description":"Any object"},"recipe":{"Type":167,"Flags":0,"Description":"The recipe used to automatically deploy underlying infrastructure for a portable resource"},"resourceProvisioning":{"Type":170,"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."}},"AdditionalProperties":0}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[159,160,161,162,163,164,165]}},{"2":{"Name":"Recipe","Properties":{"name":{"Type":4,"Flags":1,"Description":"The name of the recipe within the environment to use"},"parameters":{"Type":0,"Flags":0,"Description":"Any object"}}}},{"6":{"Value":"recipe"}},{"6":{"Value":"manual"}},{"5":{"Elements":[168,169]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/extenders@2023-10-01-preview","ScopeType":0,"Body":157}},{"6":{"Value":"Applications.Core/gateways"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/gateways","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":173,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":174,"Flags":10,"Description":"The resource api version"},"properties":{"Type":176,"Flags":1,"Description":"Gateway properties"},"tags":{"Type":192,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"GatewayProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":184,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"internal":{"Type":2,"Flags":0,"Description":"Sets Gateway to not be exposed externally (no public IP address associated). Defaults to false (exposed to internet)."},"hostname":{"Type":185,"Flags":0,"Description":"Declare hostname information for the Gateway. Leaving the hostname empty auto-assigns one: mygateway.myapp.PUBLICHOSTNAMEORIP.nip.io."},"routes":{"Type":187,"Flags":1,"Description":"Routes attached to this Gateway"},"tls":{"Type":188,"Flags":0,"Description":"TLS configuration definition for Gateway resource."},"url":{"Type":4,"Flags":2,"Description":"URL of the gateway resource. Readonly"}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[177,178,179,180,181,182,183]}},{"2":{"Name":"GatewayHostname","Properties":{"prefix":{"Type":4,"Flags":0,"Description":"Specify a prefix for the hostname: myhostname.myapp.PUBLICHOSTNAMEORIP.nip.io. Mutually exclusive with 'fullyQualifiedHostname' and will be overridden if both are defined."},"fullyQualifiedHostname":{"Type":4,"Flags":0,"Description":"Specify a fully-qualified domain name: myapp.mydomain.com. Mutually exclusive with 'prefix' and will take priority if both are defined."}}}},{"2":{"Name":"GatewayRoute","Properties":{"path":{"Type":4,"Flags":0,"Description":"The path to match the incoming request path on. Ex - /myservice."},"destination":{"Type":4,"Flags":0,"Description":"The HttpRoute to route to. Ex - myserviceroute.id."},"replacePrefix":{"Type":4,"Flags":0,"Description":"Optionally update the prefix when sending the request to the service. Ex - replacePrefix: '/' and path: '/myservice' will transform '/myservice/myroute' to '/myroute'"}}}},{"3":{"ItemType":186}},{"2":{"Name":"GatewayTls","Properties":{"sslPassthrough":{"Type":2,"Flags":0,"Description":"If true, gateway lets the https traffic sslPassthrough to the backend servers for decryption."},"minimumProtocolVersion":{"Type":191,"Flags":0,"Description":"Tls Minimum versions for Gateway resource."},"certificateFrom":{"Type":4,"Flags":0,"Description":"The resource id for the secret containing the TLS certificate and key for the gateway."}}}},{"6":{"Value":"1.2"}},{"6":{"Value":"1.3"}},{"5":{"Elements":[189,190]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/gateways@2023-10-01-preview","ScopeType":0,"Body":175}},{"6":{"Value":"Applications.Core/httpRoutes"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/httpRoutes","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":194,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":195,"Flags":10,"Description":"The resource api version"},"properties":{"Type":197,"Flags":1,"Description":"HTTPRoute properties"},"tags":{"Type":206,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"HttpRouteProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":205,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"hostname":{"Type":4,"Flags":0,"Description":"The internal hostname accepting traffic for the HTTP Route. Readonly."},"port":{"Type":3,"Flags":0,"Description":"The port number for the HTTP Route. Defaults to 80. Readonly."},"scheme":{"Type":4,"Flags":2,"Description":"The scheme used for traffic. Readonly."},"url":{"Type":4,"Flags":2,"Description":"A stable URL that that can be used to route traffic to a resource. Readonly."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[198,199,200,201,202,203,204]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/httpRoutes@2023-10-01-preview","ScopeType":0,"Body":196}},{"6":{"Value":"Applications.Core/secretStores"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/secretStores","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":208,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":209,"Flags":10,"Description":"The resource api version"},"properties":{"Type":211,"Flags":1,"Description":"The properties of SecretStore"},"tags":{"Type":229,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"SecretStoreProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":219,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"type":{"Type":222,"Flags":0,"Description":"The type of SecretStore data"},"data":{"Type":228,"Flags":1,"Description":"An object to represent key-value type secrets"},"resource":{"Type":4,"Flags":0,"Description":"The resource id of external secret store."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[212,213,214,215,216,217,218]}},{"6":{"Value":"generic"}},{"6":{"Value":"certificate"}},{"5":{"Elements":[220,221]}},{"2":{"Name":"SecretValueProperties","Properties":{"encoding":{"Type":226,"Flags":0,"Description":"The type of SecretValue Encoding"},"value":{"Type":4,"Flags":0,"Description":"The value of secret."},"valueFrom":{"Type":227,"Flags":0,"Description":"The Secret value source properties"}}}},{"6":{"Value":"raw"}},{"6":{"Value":"base64"}},{"5":{"Elements":[224,225]}},{"2":{"Name":"ValueFromProperties","Properties":{"name":{"Type":4,"Flags":1,"Description":"The name of the referenced secret."},"version":{"Type":4,"Flags":0,"Description":"The version of the referenced secret."}}}},{"2":{"Name":"SecretStorePropertiesData","Properties":{},"AdditionalProperties":223}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/secretStores@2023-10-01-preview","ScopeType":0,"Body":210}},{"6":{"Value":"Applications.Core/volumes"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/volumes","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":231,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":232,"Flags":10,"Description":"The resource api version"},"properties":{"Type":234,"Flags":1,"Description":"Volume properties"},"tags":{"Type":266,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"7":{"Name":"VolumeProperties","Discriminator":"kind","BaseProperties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":242,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."}},"Elements":{"azure.com.keyvault":243}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[235,236,237,238,239,240,241]}},{"2":{"Name":"AzureKeyVaultVolumeProperties","Properties":{"certificates":{"Type":256,"Flags":0,"Description":"The KeyVault certificates that this volume exposes"},"keys":{"Type":258,"Flags":0,"Description":"The KeyVault keys that this volume exposes"},"resource":{"Type":4,"Flags":1,"Description":"The ID of the keyvault to use for this volume resource"},"secrets":{"Type":264,"Flags":0,"Description":"The KeyVault secrets that this volume exposes"},"kind":{"Type":265,"Flags":1,"Description":"Discriminator property for VolumeProperties."}}}},{"2":{"Name":"CertificateObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"encoding":{"Type":248,"Flags":0,"Description":"Represents secret encodings"},"format":{"Type":251,"Flags":0,"Description":"Represents certificate formats"},"name":{"Type":4,"Flags":1,"Description":"The name of the certificate"},"certType":{"Type":255,"Flags":0,"Description":"Represents certificate types"},"version":{"Type":4,"Flags":0,"Description":"Certificate version"}}}},{"6":{"Value":"utf-8"}},{"6":{"Value":"hex"}},{"6":{"Value":"base64"}},{"5":{"Elements":[245,246,247]}},{"6":{"Value":"pem"}},{"6":{"Value":"pfx"}},{"5":{"Elements":[249,250]}},{"6":{"Value":"certificate"}},{"6":{"Value":"privatekey"}},{"6":{"Value":"publickey"}},{"5":{"Elements":[252,253,254]}},{"2":{"Name":"AzureKeyVaultVolumePropertiesCertificates","Properties":{},"AdditionalProperties":244}},{"2":{"Name":"KeyObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"name":{"Type":4,"Flags":1,"Description":"The name of the key"},"version":{"Type":4,"Flags":0,"Description":"Key version"}}}},{"2":{"Name":"AzureKeyVaultVolumePropertiesKeys","Properties":{},"AdditionalProperties":257}},{"2":{"Name":"SecretObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"encoding":{"Type":263,"Flags":0,"Description":"Represents secret encodings"},"name":{"Type":4,"Flags":1,"Description":"The name of the secret"},"version":{"Type":4,"Flags":0,"Description":"secret version"}}}},{"6":{"Value":"utf-8"}},{"6":{"Value":"hex"}},{"6":{"Value":"base64"}},{"5":{"Elements":[260,261,262]}},{"2":{"Name":"AzureKeyVaultVolumePropertiesSecrets","Properties":{},"AdditionalProperties":259}},{"6":{"Value":"azure.com.keyvault"}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/volumes@2023-10-01-preview","ScopeType":0,"Body":233}},{"8":{"Name":"listSecrets","ResourceType":"Applications.Core/extenders","ApiVersion":"2023-10-01-preview","Output":0,"Input":0}},{"2":{"Name":"SecretStoreListSecretsResult","Properties":{"type":{"Type":272,"Flags":2,"Description":"The type of SecretStore data"},"data":{"Type":273,"Flags":2,"Description":"An object to represent key-value type secrets"}}}},{"6":{"Value":"generic"}},{"6":{"Value":"certificate"}},{"5":{"Elements":[270,271]}},{"2":{"Name":"SecretStoreListSecretsResultData","Properties":{},"AdditionalProperties":223}},{"8":{"Name":"listSecrets","ResourceType":"Applications.Core/secretStores","ApiVersion":"2023-10-01-preview","Output":269,"Input":0}}] \ No newline at end of file +[{"1":{"Kind":1}},{"1":{"Kind":2}},{"1":{"Kind":3}},{"1":{"Kind":4}},{"1":{"Kind":5}},{"1":{"Kind":6}},{"1":{"Kind":7}},{"1":{"Kind":8}},{"6":{"Value":"Applications.Core/applications"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/applications","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":8,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":9,"Flags":10,"Description":"The resource api version"},"properties":{"Type":11,"Flags":1,"Description":"Application properties"},"tags":{"Type":46,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ApplicationProperties","Properties":{"provisioningState":{"Type":19,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"environment":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"extensions":{"Type":34,"Flags":0,"Description":"The application extension."},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[12,13,14,15,16,17,18]}},{"7":{"Name":"Extension","Discriminator":"kind","BaseProperties":{},"Elements":{"daprSidecar":21,"kubernetesMetadata":26,"kubernetesNamespace":30,"manualScaling":32}}},{"2":{"Name":"DaprSidecarExtension","Properties":{"appPort":{"Type":3,"Flags":0,"Description":"The Dapr appPort. Specifies the internal listening port for the application to handle requests from the Dapr sidecar."},"appId":{"Type":4,"Flags":1,"Description":"The Dapr appId. Specifies the identifier used by Dapr for service invocation."},"config":{"Type":4,"Flags":0,"Description":"Specifies the Dapr configuration to use for the resource."},"protocol":{"Type":24,"Flags":0,"Description":"The Dapr sidecar extension protocol"},"kind":{"Type":25,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"http"}},{"6":{"Value":"grpc"}},{"5":{"Elements":[22,23]}},{"6":{"Value":"daprSidecar"}},{"2":{"Name":"KubernetesMetadataExtension","Properties":{"annotations":{"Type":27,"Flags":0,"Description":"Annotations to be applied to the Kubernetes resources output by the resource"},"labels":{"Type":28,"Flags":0,"Description":"Labels to be applied to the Kubernetes resources output by the resource"},"kind":{"Type":29,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"2":{"Name":"KubernetesMetadataExtensionAnnotations","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"KubernetesMetadataExtensionLabels","Properties":{},"AdditionalProperties":4}},{"6":{"Value":"kubernetesMetadata"}},{"2":{"Name":"KubernetesNamespaceExtension","Properties":{"namespace":{"Type":4,"Flags":1,"Description":"The namespace of the application environment."},"kind":{"Type":31,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"kubernetesNamespace"}},{"2":{"Name":"ManualScalingExtension","Properties":{"replicas":{"Type":3,"Flags":1,"Description":"Replica count."},"kind":{"Type":33,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"manualScaling"}},{"3":{"ItemType":20}},{"2":{"Name":"ResourceStatus","Properties":{"compute":{"Type":36,"Flags":0,"Description":"Represents backing compute resource"},"recipe":{"Type":43,"Flags":2,"Description":"Recipe status at deployment time for a resource."},"outputResources":{"Type":45,"Flags":0,"Description":"Properties of an output resource"}}}},{"7":{"Name":"EnvironmentCompute","Discriminator":"kind","BaseProperties":{"resourceId":{"Type":4,"Flags":0,"Description":"The resource id of the compute resource for application environment."},"identity":{"Type":37,"Flags":0,"Description":"IdentitySettings is the external identity setting."}},"Elements":{"kubernetes":41}}},{"2":{"Name":"IdentitySettings","Properties":{"kind":{"Type":40,"Flags":1,"Description":"IdentitySettingKind is the kind of supported external identity setting"},"oidcIssuer":{"Type":4,"Flags":0,"Description":"The URI for your compute platform's OIDC issuer"},"resource":{"Type":4,"Flags":0,"Description":"The resource ID of the provisioned identity"}}}},{"6":{"Value":"undefined"}},{"6":{"Value":"azure.com.workload"}},{"5":{"Elements":[38,39]}},{"2":{"Name":"KubernetesCompute","Properties":{"namespace":{"Type":4,"Flags":1,"Description":"The namespace to use for the environment."},"kind":{"Type":42,"Flags":1,"Description":"Discriminator property for EnvironmentCompute."}}}},{"6":{"Value":"kubernetes"}},{"2":{"Name":"RecipeStatus","Properties":{"templateKind":{"Type":4,"Flags":1,"Description":"TemplateKind is the kind of the recipe template used by the portable resource upon deployment."},"templatePath":{"Type":4,"Flags":1,"Description":"TemplatePath is the path of the recipe consumed by the portable resource upon deployment."},"templateVersion":{"Type":4,"Flags":0,"Description":"TemplateVersion is the version number of the template."}}}},{"2":{"Name":"OutputResource","Properties":{"localId":{"Type":4,"Flags":0,"Description":"The logical identifier scoped to the owning Radius resource. This is only needed or used when a resource has a dependency relationship. LocalIDs do not have any particular format or meaning beyond being compared to determine dependency relationships."},"id":{"Type":4,"Flags":0,"Description":"The UCP resource ID of the underlying resource."},"radiusManaged":{"Type":2,"Flags":0,"Description":"Determines whether Radius manages the lifecycle of the underlying resource."}}}},{"3":{"ItemType":44}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"SystemData","Properties":{"createdBy":{"Type":4,"Flags":0,"Description":"The identity that created the resource."},"createdByType":{"Type":52,"Flags":0,"Description":"The type of identity that created the resource."},"createdAt":{"Type":4,"Flags":0,"Description":"The timestamp of resource creation (UTC)."},"lastModifiedBy":{"Type":4,"Flags":0,"Description":"The identity that last modified the resource."},"lastModifiedByType":{"Type":57,"Flags":0,"Description":"The type of identity that created the resource."},"lastModifiedAt":{"Type":4,"Flags":0,"Description":"The timestamp of resource last modification (UTC)"}}}},{"6":{"Value":"User"}},{"6":{"Value":"Application"}},{"6":{"Value":"ManagedIdentity"}},{"6":{"Value":"Key"}},{"5":{"Elements":[48,49,50,51]}},{"6":{"Value":"User"}},{"6":{"Value":"Application"}},{"6":{"Value":"ManagedIdentity"}},{"6":{"Value":"Key"}},{"5":{"Elements":[53,54,55,56]}},{"4":{"Name":"Applications.Core/applications@2023-10-01-preview","ScopeType":0,"Body":10}},{"6":{"Value":"Applications.Core/containers"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/containers","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":59,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":60,"Flags":10,"Description":"The resource api version"},"properties":{"Type":62,"Flags":1,"Description":"Container properties"},"tags":{"Type":122,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ContainerProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":70,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"container":{"Type":71,"Flags":1,"Description":"Definition of a container"},"connections":{"Type":108,"Flags":0,"Description":"Specifies a connection to another resource."},"identity":{"Type":37,"Flags":0,"Description":"IdentitySettings is the external identity setting."},"extensions":{"Type":109,"Flags":0,"Description":"Extensions spec of the resource"},"resourceProvisioning":{"Type":112,"Flags":0,"Description":"Specifies how the underlying service/resource is provisioned and managed. Available values are 'internal', where Radius manages the lifecycle of the resource internally, and 'manual', where a user manages the resource."},"resources":{"Type":114,"Flags":0,"Description":"A collection of references to resources associated with the container"},"restartPolicy":{"Type":118,"Flags":0,"Description":"Restart policy for the container"},"runtimes":{"Type":119,"Flags":0,"Description":"The properties for runtime configuration"}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[63,64,65,66,67,68,69]}},{"2":{"Name":"Container","Properties":{"image":{"Type":4,"Flags":1,"Description":"The registry and image to download and run in your container"},"imagePullPolicy":{"Type":75,"Flags":0,"Description":"The image pull policy for the container"},"env":{"Type":76,"Flags":0,"Description":"environment"},"ports":{"Type":81,"Flags":0,"Description":"container ports"},"readinessProbe":{"Type":82,"Flags":0,"Description":"Properties for readiness/liveness probe"},"livenessProbe":{"Type":82,"Flags":0,"Description":"Properties for readiness/liveness probe"},"volumes":{"Type":101,"Flags":0,"Description":"container volumes"},"command":{"Type":102,"Flags":0,"Description":"Entrypoint array. Overrides the container image's ENTRYPOINT"},"args":{"Type":103,"Flags":0,"Description":"Arguments to the entrypoint. Overrides the container image's CMD"},"workingDir":{"Type":4,"Flags":0,"Description":"Working directory for the container"}}}},{"6":{"Value":"Always"}},{"6":{"Value":"IfNotPresent"}},{"6":{"Value":"Never"}},{"5":{"Elements":[72,73,74]}},{"2":{"Name":"ContainerEnv","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"ContainerPortProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"protocol":{"Type":80,"Flags":0,"Description":"The protocol in use by the port"},"provides":{"Type":4,"Flags":0,"Description":"Specifies a route provided by this port"},"scheme":{"Type":4,"Flags":0,"Description":"Specifies the URL scheme of the communication protocol. Consumers can use the scheme to construct a URL. The value defaults to 'http' or 'https' depending on the port value"},"port":{"Type":3,"Flags":0,"Description":"Specifies the port that will be exposed by this container. Must be set when value different from containerPort is desired"}}}},{"6":{"Value":"TCP"}},{"6":{"Value":"UDP"}},{"5":{"Elements":[78,79]}},{"2":{"Name":"ContainerPorts","Properties":{},"AdditionalProperties":77}},{"7":{"Name":"HealthProbeProperties","Discriminator":"kind","BaseProperties":{"initialDelaySeconds":{"Type":3,"Flags":0,"Description":"Initial delay in seconds before probing for readiness/liveness"},"failureThreshold":{"Type":3,"Flags":0,"Description":"Threshold number of times the probe fails after which a failure would be reported"},"periodSeconds":{"Type":3,"Flags":0,"Description":"Interval for the readiness/liveness probe in seconds"},"timeoutSeconds":{"Type":3,"Flags":0,"Description":"Number of seconds after which the readiness/liveness probe times out. Defaults to 5 seconds"}},"Elements":{"exec":83,"httpGet":85,"tcp":88}}},{"2":{"Name":"ExecHealthProbeProperties","Properties":{"command":{"Type":4,"Flags":1,"Description":"Command to execute to probe readiness/liveness"},"kind":{"Type":84,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"6":{"Value":"exec"}},{"2":{"Name":"HttpGetHealthProbeProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"path":{"Type":4,"Flags":1,"Description":"The route to make the HTTP request on"},"headers":{"Type":86,"Flags":0,"Description":"Custom HTTP headers to add to the get request"},"kind":{"Type":87,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"2":{"Name":"HttpGetHealthProbePropertiesHeaders","Properties":{},"AdditionalProperties":4}},{"6":{"Value":"httpGet"}},{"2":{"Name":"TcpHealthProbeProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"kind":{"Type":89,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"6":{"Value":"tcp"}},{"7":{"Name":"Volume","Discriminator":"kind","BaseProperties":{"mountPath":{"Type":4,"Flags":0,"Description":"The path where the volume is mounted"}},"Elements":{"ephemeral":91,"persistent":96}}},{"2":{"Name":"EphemeralVolume","Properties":{"managedStore":{"Type":94,"Flags":1,"Description":"The managed store for the ephemeral volume"},"kind":{"Type":95,"Flags":1,"Description":"Discriminator property for Volume."}}}},{"6":{"Value":"memory"}},{"6":{"Value":"disk"}},{"5":{"Elements":[92,93]}},{"6":{"Value":"ephemeral"}},{"2":{"Name":"PersistentVolume","Properties":{"permission":{"Type":99,"Flags":0,"Description":"The persistent volume permission"},"source":{"Type":4,"Flags":1,"Description":"The source of the volume"},"kind":{"Type":100,"Flags":1,"Description":"Discriminator property for Volume."}}}},{"6":{"Value":"read"}},{"6":{"Value":"write"}},{"5":{"Elements":[97,98]}},{"6":{"Value":"persistent"}},{"2":{"Name":"ContainerVolumes","Properties":{},"AdditionalProperties":90}},{"3":{"ItemType":4}},{"3":{"ItemType":4}},{"2":{"Name":"ConnectionProperties","Properties":{"source":{"Type":4,"Flags":1,"Description":"The source of the connection"},"disableDefaultEnvVars":{"Type":2,"Flags":0,"Description":"default environment variable override"},"iam":{"Type":105,"Flags":0,"Description":"IAM properties"}}}},{"2":{"Name":"IamProperties","Properties":{"kind":{"Type":106,"Flags":1,"Description":"The kind of IAM provider to configure"},"roles":{"Type":107,"Flags":0,"Description":"RBAC permissions to be assigned on the source resource"}}}},{"6":{"Value":"azure"}},{"3":{"ItemType":4}},{"2":{"Name":"ContainerPropertiesConnections","Properties":{},"AdditionalProperties":104}},{"3":{"ItemType":20}},{"6":{"Value":"internal"}},{"6":{"Value":"manual"}},{"5":{"Elements":[110,111]}},{"2":{"Name":"ResourceReference","Properties":{"id":{"Type":4,"Flags":1,"Description":"Resource id of an existing resource"}}}},{"3":{"ItemType":113}},{"6":{"Value":"Always"}},{"6":{"Value":"OnFailure"}},{"6":{"Value":"Never"}},{"5":{"Elements":[115,116,117]}},{"2":{"Name":"RuntimesProperties","Properties":{"kubernetes":{"Type":120,"Flags":0,"Description":"The runtime configuration properties for Kubernetes"}}}},{"2":{"Name":"KubernetesRuntimeProperties","Properties":{"base":{"Type":4,"Flags":0,"Description":"The serialized YAML manifest which represents the base Kubernetes resources to deploy, such as Deployment, Service, ServiceAccount, Secrets, and ConfigMaps."},"pod":{"Type":121,"Flags":0,"Description":"A strategic merge patch that will be applied to the PodSpec object when this container is being deployed."}}}},{"2":{"Name":"KubernetesPodSpec","Properties":{},"AdditionalProperties":0}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/containers@2023-10-01-preview","ScopeType":0,"Body":61}},{"6":{"Value":"Applications.Core/environments"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/environments","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":124,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":125,"Flags":10,"Description":"The resource api version"},"properties":{"Type":127,"Flags":1,"Description":"Environment properties"},"tags":{"Type":153,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"EnvironmentProperties","Properties":{"provisioningState":{"Type":135,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"compute":{"Type":36,"Flags":1,"Description":"Represents backing compute resource"},"providers":{"Type":136,"Flags":0,"Description":"The Cloud providers configuration"},"simulated":{"Type":2,"Flags":0,"Description":"Simulated environment."},"recipes":{"Type":145,"Flags":0,"Description":"Specifies Recipes linked to the Environment."},"recipeConfig":{"Type":146,"Flags":0,"Description":"Specifies recipe configurations needed for the recipes."},"extensions":{"Type":152,"Flags":0,"Description":"The environment extension."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[128,129,130,131,132,133,134]}},{"2":{"Name":"Providers","Properties":{"azure":{"Type":137,"Flags":0,"Description":"The Azure cloud provider definition"},"aws":{"Type":138,"Flags":0,"Description":"The AWS cloud provider definition"}}}},{"2":{"Name":"ProvidersAzure","Properties":{"scope":{"Type":4,"Flags":1,"Description":"Target scope for Azure resources to be deployed into. For example: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup'"}}}},{"2":{"Name":"ProvidersAws","Properties":{"scope":{"Type":4,"Flags":1,"Description":"Target scope for AWS resources to be deployed into. For example: '/planes/aws/aws/accounts/000000000000/regions/us-west-2'"}}}},{"7":{"Name":"RecipeProperties","Discriminator":"templateKind","BaseProperties":{"templatePath":{"Type":4,"Flags":1,"Description":"Path to the template provided by the recipe. Currently only link to Azure Container Registry is supported."},"parameters":{"Type":0,"Flags":0,"Description":"Any object"}},"Elements":{"bicep":140,"terraform":142}}},{"2":{"Name":"BicepRecipeProperties","Properties":{"plainHttp":{"Type":2,"Flags":0,"Description":"Connect to the Bicep registry using HTTP (not-HTTPS). This should be used when the registry is known not to support HTTPS, for example in a locally-hosted registry. Defaults to false (use HTTPS/TLS)."},"templateKind":{"Type":141,"Flags":1,"Description":"Discriminator property for RecipeProperties."}}}},{"6":{"Value":"bicep"}},{"2":{"Name":"TerraformRecipeProperties","Properties":{"templateVersion":{"Type":4,"Flags":0,"Description":"Version of the template to deploy. For Terraform recipes using a module registry this is required, but must be omitted for other module sources."},"templateKind":{"Type":143,"Flags":1,"Description":"Discriminator property for RecipeProperties."}}}},{"6":{"Value":"terraform"}},{"2":{"Name":"DictionaryOfRecipeProperties","Properties":{},"AdditionalProperties":139}},{"2":{"Name":"EnvironmentPropertiesRecipes","Properties":{},"AdditionalProperties":144}},{"2":{"Name":"RecipeConfigProperties","Properties":{"terraform":{"Type":147,"Flags":0,"Description":"Specifies the terraform config properties"}}}},{"2":{"Name":"TerraformConfigProperties","Properties":{"authentication":{"Type":148,"Flags":0,"Description":"Specifies authentication information needed to use private terraform module repositories."}}}},{"2":{"Name":"AuthConfig","Properties":{"git":{"Type":149,"Flags":0,"Description":"Specifies authentication information needed to use private terraform module repositories from git module source"}}}},{"2":{"Name":"GitAuthConfig","Properties":{"pat":{"Type":151,"Flags":0,"Description":"Specifies the secret details of type personal access token for each different git platforms"}}}},{"2":{"Name":"Secret","Properties":{"secretStore":{"Type":4,"Flags":0,"Description":"The resource id for the secret store containing credentials"}}}},{"2":{"Name":"GitAuthConfigPat","Properties":{},"AdditionalProperties":150}},{"3":{"ItemType":20}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/environments@2023-10-01-preview","ScopeType":0,"Body":126}},{"6":{"Value":"Applications.Core/extenders"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/extenders","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":155,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":156,"Flags":10,"Description":"The resource api version"},"properties":{"Type":158,"Flags":1,"Description":"ExtenderResource portable resource properties"},"tags":{"Type":171,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ExtenderProperties","Properties":{"environment":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the environment that the portable resource is linked to"},"application":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the application that the portable resource is consumed by (if applicable)"},"provisioningState":{"Type":166,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"secrets":{"Type":0,"Flags":0,"Description":"Any object"},"recipe":{"Type":167,"Flags":0,"Description":"The recipe used to automatically deploy underlying infrastructure for a portable resource"},"resourceProvisioning":{"Type":170,"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."}},"AdditionalProperties":0}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[159,160,161,162,163,164,165]}},{"2":{"Name":"Recipe","Properties":{"name":{"Type":4,"Flags":1,"Description":"The name of the recipe within the environment to use"},"parameters":{"Type":0,"Flags":0,"Description":"Any object"}}}},{"6":{"Value":"recipe"}},{"6":{"Value":"manual"}},{"5":{"Elements":[168,169]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/extenders@2023-10-01-preview","ScopeType":0,"Body":157}},{"6":{"Value":"Applications.Core/gateways"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/gateways","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":173,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":174,"Flags":10,"Description":"The resource api version"},"properties":{"Type":176,"Flags":1,"Description":"Gateway properties"},"tags":{"Type":192,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"GatewayProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":184,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"internal":{"Type":2,"Flags":0,"Description":"Sets Gateway to not be exposed externally (no public IP address associated). Defaults to false (exposed to internet)."},"hostname":{"Type":185,"Flags":0,"Description":"Declare hostname information for the Gateway. Leaving the hostname empty auto-assigns one: mygateway.myapp.PUBLICHOSTNAMEORIP.nip.io."},"routes":{"Type":187,"Flags":1,"Description":"Routes attached to this Gateway"},"tls":{"Type":188,"Flags":0,"Description":"TLS configuration definition for Gateway resource."},"url":{"Type":4,"Flags":2,"Description":"URL of the gateway resource. Readonly"}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[177,178,179,180,181,182,183]}},{"2":{"Name":"GatewayHostname","Properties":{"prefix":{"Type":4,"Flags":0,"Description":"Specify a prefix for the hostname: myhostname.myapp.PUBLICHOSTNAMEORIP.nip.io. Mutually exclusive with 'fullyQualifiedHostname' and will be overridden if both are defined."},"fullyQualifiedHostname":{"Type":4,"Flags":0,"Description":"Specify a fully-qualified domain name: myapp.mydomain.com. Mutually exclusive with 'prefix' and will take priority if both are defined."}}}},{"2":{"Name":"GatewayRoute","Properties":{"path":{"Type":4,"Flags":0,"Description":"The path to match the incoming request path on. Ex - /myservice."},"destination":{"Type":4,"Flags":0,"Description":"The HttpRoute to route to. Ex - myserviceroute.id."},"replacePrefix":{"Type":4,"Flags":0,"Description":"Optionally update the prefix when sending the request to the service. Ex - replacePrefix: '/' and path: '/myservice' will transform '/myservice/myroute' to '/myroute'"}}}},{"3":{"ItemType":186}},{"2":{"Name":"GatewayTls","Properties":{"sslPassthrough":{"Type":2,"Flags":0,"Description":"If true, gateway lets the https traffic sslPassthrough to the backend servers for decryption."},"minimumProtocolVersion":{"Type":191,"Flags":0,"Description":"Tls Minimum versions for Gateway resource."},"certificateFrom":{"Type":4,"Flags":0,"Description":"The resource id for the secret containing the TLS certificate and key for the gateway."}}}},{"6":{"Value":"1.2"}},{"6":{"Value":"1.3"}},{"5":{"Elements":[189,190]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/gateways@2023-10-01-preview","ScopeType":0,"Body":175}},{"6":{"Value":"Applications.Core/httpRoutes"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/httpRoutes","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":194,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":195,"Flags":10,"Description":"The resource api version"},"properties":{"Type":197,"Flags":1,"Description":"HTTPRoute properties"},"tags":{"Type":206,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"HttpRouteProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":205,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"hostname":{"Type":4,"Flags":0,"Description":"The internal hostname accepting traffic for the HTTP Route. Readonly."},"port":{"Type":3,"Flags":0,"Description":"The port number for the HTTP Route. Defaults to 80. Readonly."},"scheme":{"Type":4,"Flags":2,"Description":"The scheme used for traffic. Readonly."},"url":{"Type":4,"Flags":2,"Description":"A stable URL that that can be used to route traffic to a resource. Readonly."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[198,199,200,201,202,203,204]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/httpRoutes@2023-10-01-preview","ScopeType":0,"Body":196}},{"6":{"Value":"Applications.Core/secretStores"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/secretStores","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":208,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":209,"Flags":10,"Description":"The resource api version"},"properties":{"Type":211,"Flags":1,"Description":"The properties of SecretStore"},"tags":{"Type":229,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"SecretStoreProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":219,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"type":{"Type":222,"Flags":0,"Description":"The type of SecretStore data"},"data":{"Type":228,"Flags":1,"Description":"An object to represent key-value type secrets"},"resource":{"Type":4,"Flags":0,"Description":"The resource id of external secret store."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[212,213,214,215,216,217,218]}},{"6":{"Value":"generic"}},{"6":{"Value":"certificate"}},{"5":{"Elements":[220,221]}},{"2":{"Name":"SecretValueProperties","Properties":{"encoding":{"Type":226,"Flags":0,"Description":"The type of SecretValue Encoding"},"value":{"Type":4,"Flags":0,"Description":"The value of secret."},"valueFrom":{"Type":227,"Flags":0,"Description":"The Secret value source properties"}}}},{"6":{"Value":"raw"}},{"6":{"Value":"base64"}},{"5":{"Elements":[224,225]}},{"2":{"Name":"ValueFromProperties","Properties":{"name":{"Type":4,"Flags":1,"Description":"The name of the referenced secret."},"version":{"Type":4,"Flags":0,"Description":"The version of the referenced secret."}}}},{"2":{"Name":"SecretStorePropertiesData","Properties":{},"AdditionalProperties":223}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/secretStores@2023-10-01-preview","ScopeType":0,"Body":210}},{"6":{"Value":"Applications.Core/volumes"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/volumes","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":231,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":232,"Flags":10,"Description":"The resource api version"},"properties":{"Type":234,"Flags":1,"Description":"Volume properties"},"tags":{"Type":266,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"7":{"Name":"VolumeProperties","Discriminator":"kind","BaseProperties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":242,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."}},"Elements":{"azure.com.keyvault":243}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[235,236,237,238,239,240,241]}},{"2":{"Name":"AzureKeyVaultVolumeProperties","Properties":{"certificates":{"Type":256,"Flags":0,"Description":"The KeyVault certificates that this volume exposes"},"keys":{"Type":258,"Flags":0,"Description":"The KeyVault keys that this volume exposes"},"resource":{"Type":4,"Flags":1,"Description":"The ID of the keyvault to use for this volume resource"},"secrets":{"Type":264,"Flags":0,"Description":"The KeyVault secrets that this volume exposes"},"kind":{"Type":265,"Flags":1,"Description":"Discriminator property for VolumeProperties."}}}},{"2":{"Name":"CertificateObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"encoding":{"Type":248,"Flags":0,"Description":"Represents secret encodings"},"format":{"Type":251,"Flags":0,"Description":"Represents certificate formats"},"name":{"Type":4,"Flags":1,"Description":"The name of the certificate"},"certType":{"Type":255,"Flags":0,"Description":"Represents certificate types"},"version":{"Type":4,"Flags":0,"Description":"Certificate version"}}}},{"6":{"Value":"utf-8"}},{"6":{"Value":"hex"}},{"6":{"Value":"base64"}},{"5":{"Elements":[245,246,247]}},{"6":{"Value":"pem"}},{"6":{"Value":"pfx"}},{"5":{"Elements":[249,250]}},{"6":{"Value":"certificate"}},{"6":{"Value":"privatekey"}},{"6":{"Value":"publickey"}},{"5":{"Elements":[252,253,254]}},{"2":{"Name":"AzureKeyVaultVolumePropertiesCertificates","Properties":{},"AdditionalProperties":244}},{"2":{"Name":"KeyObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"name":{"Type":4,"Flags":1,"Description":"The name of the key"},"version":{"Type":4,"Flags":0,"Description":"Key version"}}}},{"2":{"Name":"AzureKeyVaultVolumePropertiesKeys","Properties":{},"AdditionalProperties":257}},{"2":{"Name":"SecretObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"encoding":{"Type":263,"Flags":0,"Description":"Represents secret encodings"},"name":{"Type":4,"Flags":1,"Description":"The name of the secret"},"version":{"Type":4,"Flags":0,"Description":"secret version"}}}},{"6":{"Value":"utf-8"}},{"6":{"Value":"hex"}},{"6":{"Value":"base64"}},{"5":{"Elements":[260,261,262]}},{"2":{"Name":"AzureKeyVaultVolumePropertiesSecrets","Properties":{},"AdditionalProperties":259}},{"6":{"Value":"azure.com.keyvault"}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/volumes@2023-10-01-preview","ScopeType":0,"Body":233}},{"8":{"Name":"listSecrets","ResourceType":"Applications.Core/extenders","ApiVersion":"2023-10-01-preview","Output":0,"Input":0}},{"2":{"Name":"SecretStoreListSecretsResult","Properties":{"type":{"Type":272,"Flags":2,"Description":"The type of SecretStore data"},"data":{"Type":273,"Flags":2,"Description":"An object to represent key-value type secrets"}}}},{"6":{"Value":"generic"}},{"6":{"Value":"certificate"}},{"5":{"Elements":[270,271]}},{"2":{"Name":"SecretStoreListSecretsResultData","Properties":{},"AdditionalProperties":223}},{"8":{"Name":"listSecrets","ResourceType":"Applications.Core/secretStores","ApiVersion":"2023-10-01-preview","Output":269,"Input":0}}] \ No newline at end of file diff --git a/pkg/corerp/datamodel/environment.go b/pkg/corerp/datamodel/environment.go index a26c735f2d..0671745edc 100644 --- a/pkg/corerp/datamodel/environment.go +++ b/pkg/corerp/datamodel/environment.go @@ -46,23 +46,33 @@ type EnvironmentProperties struct { Simulated bool `json:"simulated,omitempty"` } +// RecipeConfigProperties - Specifies recipe configurations needed for the recipes. type RecipeConfigProperties struct { + // Specifies the terraform config properties Terraform TerraformConfigProperties `json:"terraform,omitempty"` } +// TerraformConfigProperties - Specifies the terraform config properties type TerraformConfigProperties struct { + // Specifies authentication information needed to use private terraform module repositories. Authentication AuthConfig `json:"authentication,omitempty"` } +// AuthConfig - Specifies authentication information needed to use private terraform module repositories. type AuthConfig struct { + // Specifies authentication information needed to use private terraform module repositories from git module source Git GitAuthConfig `json:"git,omitempty"` } +// GitAuthConfig - Specifies authentication information needed to use private terraform module repositories from git module source type GitAuthConfig struct { + // Specifies the secret details of type personal access token for each different git platforms PAT map[string]Secret `json:"pat,omitempty"` } +// Secret - Specifies the secret details of type personal access token for each different git platforms type Secret struct { + // The resource id for the secret store containing credentials SecretStore string `json:"secretStore,omitempty"` } diff --git a/pkg/recipes/terraform/config/config.go b/pkg/recipes/terraform/config/config.go index 639d61b582..c020174c91 100644 --- a/pkg/recipes/terraform/config/config.go +++ b/pkg/recipes/terraform/config/config.go @@ -45,7 +45,7 @@ const ( // New creates TerraformConfig with the given module name and its inputs (module source, version, parameters) // Parameters are populated from environment recipe and resource recipe metadata. func New(ctx context.Context, moduleName string, envRecipe *recipes.EnvironmentDefinition, resourceRecipe *recipes.ResourceMetadata, envConfig *recipes.Configuration, armOptions *arm.ClientOptions) *TerraformConfig { - + // Getting the module source path with credentials information if its a private source. path, _ := getModuleSource(ctx, envConfig, envRecipe.TemplatePath, armOptions) // Resource parameter gets precedence over environment level parameter, // if same parameter is defined in both environment and resource recipe metadata. @@ -60,6 +60,8 @@ func New(ctx context.Context, moduleName string, envRecipe *recipes.EnvironmentD } } +// getModuleSource is called to get the module source path with credential information +// if template path represent a private source else returns the provided template path func getModuleSource(ctx context.Context, envConfig *recipes.Configuration, templatePath string, armOptions *arm.ClientOptions) (string, error) { var source string var err error @@ -74,6 +76,8 @@ func getModuleSource(ctx context.Context, envConfig *recipes.Configuration, temp return source, err } +// getGitSource creates a terraform module source in a generic git format with credential information +// e.g: git::https://:@/ func getGitSource(ctx context.Context, envConfig *recipes.Configuration, templatePath string, armOptions *arm.ClientOptions) (string, error) { secretStore, err := getSecretStoreID(*envConfig, templatePath) if err != nil { diff --git a/pkg/recipes/terraform/config/config_test.go b/pkg/recipes/terraform/config/config_test.go index 1cb2b4770c..7b049cae43 100644 --- a/pkg/recipes/terraform/config/config_test.go +++ b/pkg/recipes/terraform/config/config_test.go @@ -542,23 +542,46 @@ func Test_Save_InvalidWorkingDir(t *testing.T) { } func Test_getSecretStoreID(t *testing.T) { - config := &recipes.Configuration{ - RecipeConfig: datamodel.RecipeConfigProperties{ - Terraform: datamodel.TerraformConfigProperties{ - Authentication: datamodel.AuthConfig{ - Git: datamodel.GitAuthConfig{ - PAT: map[string]datamodel.Secret{ - "dev.azure.com": datamodel.Secret{ - SecretStore: "secret-store1", + tests := []struct { + desc string + templatePath string + config *recipes.Configuration + expSecretStoreID string + expectedErr bool + }{ + { + desc: "success", + templatePath: "https://dev.azure.com/recipes/redis", + config: &recipes.Configuration{ + RecipeConfig: datamodel.RecipeConfigProperties{ + Terraform: datamodel.TerraformConfigProperties{ + Authentication: datamodel.AuthConfig{ + Git: datamodel.GitAuthConfig{ + PAT: map[string]datamodel.Secret{ + "dev.azure.com": datamodel.Secret{ + SecretStore: "secret-store1", + }, + }, }, }, }, }, }, + expSecretStoreID: "secret-store1", + expectedErr: false, }, } - templatePath := "https://dev.azure.com/recipes/redis" - path, err := getSecretStoreID(*config, templatePath) - require.Equal(t, "secret-store1", path) - require.NoError(t, err) + + for _, tc := range tests { + t.Run(tc.desc, func(t *testing.T) { + ss, err := getSecretStoreID(*tc.config, tc.templatePath) + if !tc.expectedErr { + require.Equal(t, tc.expSecretStoreID, ss) + require.NoError(t, err) + } else { + require.Error(t, err) + } + }) + } + } From 3b29b9ea1813e08907a8e2f0f11515aa34b66e2c Mon Sep 17 00:00:00 2001 From: Vishwanath Hiremath Date: Thu, 15 Feb 2024 14:23:23 -0800 Subject: [PATCH 05/22] addressing comments Signed-off-by: Vishwanath Hiremath --- .../environment_conversion.go | 94 +++++++++++-------- pkg/corerp/datamodel/environment.go | 10 +- pkg/recipes/configloader/environment.go | 5 +- pkg/recipes/terraform/config/config.go | 74 ++++++++------- pkg/recipes/terraform/config/config_test.go | 46 +++++++-- pkg/recipes/terraform/execute.go | 6 +- 6 files changed, 145 insertions(+), 90 deletions(-) diff --git a/pkg/corerp/api/v20231001preview/environment_conversion.go b/pkg/corerp/api/v20231001preview/environment_conversion.go index 016a1337d5..a5e1f35387 100644 --- a/pkg/corerp/api/v20231001preview/environment_conversion.go +++ b/pkg/corerp/api/v20231001preview/environment_conversion.go @@ -63,25 +63,8 @@ func (src *EnvironmentResource) ConvertTo() (v1.DataModelInterface, error) { return nil, err } converted.Properties.Compute = *envCompute + converted.Properties.RecipeConfig = toRecipeConfigDatamodel(src.Properties.RecipeConfig) - recipeConfig := src.Properties.RecipeConfig - if recipeConfig != nil { - if recipeConfig.Terraform != nil { - if recipeConfig.Terraform.Authentication != nil { - gitConfig := recipeConfig.Terraform.Authentication.Git - if gitConfig != nil { - pat := gitConfig.Pat - p := map[string]datamodel.Secret{} - for k, v := range pat { - p[k] = datamodel.Secret{ - SecretStore: to.String(v.SecretStore), - } - } - converted.Properties.RecipeConfig.Terraform.Authentication.Git.PAT = p - } - } - } - } if src.Properties.Recipes != nil { envRecipes := make(map[string]map[string]datamodel.EnvironmentRecipeProperties) for resourceType, recipes := range src.Properties.Recipes { @@ -169,28 +152,8 @@ func (dst *EnvironmentResource) ConvertFrom(src v1.DataModelInterface) error { } dst.Properties.Recipes = recipes } + dst.Properties.RecipeConfig = fromRecipeConfigDatamodel(env.Properties.RecipeConfig) - if !reflect.DeepEqual(env.Properties.RecipeConfig, datamodel.RecipeConfigProperties{}) { - recipeConfig := &RecipeConfigProperties{} - if !reflect.DeepEqual(env.Properties.RecipeConfig.Terraform, datamodel.TerraformConfigProperties{}) { - recipeConfig.Terraform = &TerraformConfigProperties{} - if !reflect.DeepEqual(env.Properties.RecipeConfig.Terraform.Authentication, datamodel.AuthConfig{}) { - recipeConfig.Terraform.Authentication = &AuthConfig{} - if !reflect.DeepEqual(env.Properties.RecipeConfig.Terraform.Authentication.Git, datamodel.GitAuthConfig{}) { - recipeConfig.Terraform.Authentication.Git = &GitAuthConfig{} - if env.Properties.RecipeConfig.Terraform.Authentication.Git.PAT != nil { - recipeConfig.Terraform.Authentication.Git.Pat = map[string]*Secret{} - } - } - } - } - for k, v := range env.Properties.RecipeConfig.Terraform.Authentication.Git.PAT { - recipeConfig.Terraform.Authentication.Git.Pat[k] = &Secret{ - SecretStore: to.Ptr(v.SecretStore), - } - } - dst.Properties.RecipeConfig = recipeConfig - } if env.Properties.Providers != (datamodel.Providers{}) { dst.Properties.Providers = &Providers{} if env.Properties.Providers.Azure != (datamodel.ProvidersAzure{}) { @@ -220,6 +183,59 @@ func (dst *EnvironmentResource) ConvertFrom(src v1.DataModelInterface) error { return nil } +func toRecipeConfigDatamodel(config *RecipeConfigProperties) datamodel.RecipeConfigProperties { + if config != nil { + recipeConfig := datamodel.RecipeConfigProperties{} + if config.Terraform != nil { + recipeConfig.Terraform = datamodel.TerraformConfigProperties{} + if config.Terraform.Authentication != nil { + recipeConfig.Terraform.Authentication = datamodel.AuthConfig{} + gitConfig := config.Terraform.Authentication.Git + if gitConfig != nil { + recipeConfig.Terraform.Authentication.Git = datamodel.GitAuthConfig{} + if gitConfig.Pat != nil { + p := map[string]datamodel.Secret{} + for k, v := range gitConfig.Pat { + p[k] = datamodel.Secret{ + SecretStore: to.String(v.SecretStore), + } + } + recipeConfig.Terraform.Authentication.Git.PAT = p + } + + } + } + } + return recipeConfig + } + return datamodel.RecipeConfigProperties{} +} + +func fromRecipeConfigDatamodel(config datamodel.RecipeConfigProperties) *RecipeConfigProperties { + if !reflect.DeepEqual(config, datamodel.RecipeConfigProperties{}) { + recipeConfig := &RecipeConfigProperties{} + if !reflect.DeepEqual(config.Terraform, datamodel.TerraformConfigProperties{}) { + recipeConfig.Terraform = &TerraformConfigProperties{} + if !reflect.DeepEqual(config.Terraform.Authentication, datamodel.AuthConfig{}) { + recipeConfig.Terraform.Authentication = &AuthConfig{} + if !reflect.DeepEqual(config.Terraform.Authentication.Git, datamodel.GitAuthConfig{}) { + recipeConfig.Terraform.Authentication.Git = &GitAuthConfig{} + if config.Terraform.Authentication.Git.PAT != nil { + recipeConfig.Terraform.Authentication.Git.Pat = map[string]*Secret{} + } + } + } + } + for k, v := range config.Terraform.Authentication.Git.PAT { + recipeConfig.Terraform.Authentication.Git.Pat[k] = &Secret{ + SecretStore: to.Ptr(v.SecretStore), + } + } + return recipeConfig + } + return nil +} + func toEnvironmentComputeDataModel(h EnvironmentComputeClassification) (*rpv1.EnvironmentCompute, error) { switch v := h.(type) { case *KubernetesCompute: diff --git a/pkg/corerp/datamodel/environment.go b/pkg/corerp/datamodel/environment.go index 0671745edc..864f1aeef0 100644 --- a/pkg/corerp/datamodel/environment.go +++ b/pkg/corerp/datamodel/environment.go @@ -48,31 +48,31 @@ type EnvironmentProperties struct { // RecipeConfigProperties - Specifies recipe configurations needed for the recipes. type RecipeConfigProperties struct { - // Specifies the terraform config properties + // Terraform specifies the terraform config properties Terraform TerraformConfigProperties `json:"terraform,omitempty"` } // TerraformConfigProperties - Specifies the terraform config properties type TerraformConfigProperties struct { - // Specifies authentication information needed to use private terraform module repositories. + // Authentication specifies authentication information needed to use private terraform module repositories. Authentication AuthConfig `json:"authentication,omitempty"` } // AuthConfig - Specifies authentication information needed to use private terraform module repositories. type AuthConfig struct { - // Specifies authentication information needed to use private terraform module repositories from git module source + // Git specifies authentication information needed to use private terraform module repositories from git module source Git GitAuthConfig `json:"git,omitempty"` } // GitAuthConfig - Specifies authentication information needed to use private terraform module repositories from git module source type GitAuthConfig struct { - // Specifies the secret details of type personal access token for each different git platforms + // GitAuthConfig specifies the secret details of type personal access token for each different git platforms PAT map[string]Secret `json:"pat,omitempty"` } // Secret - Specifies the secret details of type personal access token for each different git platforms type Secret struct { - // The resource id for the secret store containing credentials + // Secret represent the resource id for the secret store containing credentials SecretStore string `json:"secretStore,omitempty"` } diff --git a/pkg/recipes/configloader/environment.go b/pkg/recipes/configloader/environment.go index 9ca33c073f..92f016e1d2 100644 --- a/pkg/recipes/configloader/environment.go +++ b/pkg/recipes/configloader/environment.go @@ -105,11 +105,12 @@ func getConfiguration(environment *v20231001preview.EnvironmentResource, applica if err != nil { return nil, err } + envDatamodel := env.(*datamodel.Environment) if environment.Properties.Providers != nil { - config.Providers = env.(*datamodel.Environment).Properties.Providers + config.Providers = envDatamodel.Properties.Providers } if environment.Properties.RecipeConfig != nil { - config.RecipeConfig = env.(*datamodel.Environment).Properties.RecipeConfig + config.RecipeConfig = envDatamodel.Properties.RecipeConfig } if environment.Properties.Simulated != nil && *environment.Properties.Simulated { diff --git a/pkg/recipes/terraform/config/config.go b/pkg/recipes/terraform/config/config.go index c020174c91..d98847f287 100644 --- a/pkg/recipes/terraform/config/config.go +++ b/pkg/recipes/terraform/config/config.go @@ -44,9 +44,12 @@ const ( // New creates TerraformConfig with the given module name and its inputs (module source, version, parameters) // Parameters are populated from environment recipe and resource recipe metadata. -func New(ctx context.Context, moduleName string, envRecipe *recipes.EnvironmentDefinition, resourceRecipe *recipes.ResourceMetadata, envConfig *recipes.Configuration, armOptions *arm.ClientOptions) *TerraformConfig { +func New(ctx context.Context, moduleName string, envRecipe *recipes.EnvironmentDefinition, resourceRecipe *recipes.ResourceMetadata, envConfig *recipes.Configuration, armOptions *arm.ClientOptions) (*TerraformConfig, error) { // Getting the module source path with credentials information if its a private source. - path, _ := getModuleSource(ctx, envConfig, envRecipe.TemplatePath, armOptions) + path, err := getModuleSource(ctx, envConfig, envRecipe.TemplatePath, armOptions) + if err != nil { + return nil, err + } // Resource parameter gets precedence over environment level parameter, // if same parameter is defined in both environment and resource recipe metadata. moduleData := newModuleConfig(path, envRecipe.TemplateVersion, envRecipe.Parameters, resourceRecipe.Parameters) @@ -57,7 +60,7 @@ func New(ctx context.Context, moduleName string, envRecipe *recipes.EnvironmentD Module: map[string]TFModuleConfig{ moduleName: moduleData, }, - } + }, nil } // getModuleSource is called to get the module source path with credential information @@ -83,40 +86,41 @@ func getGitSource(ctx context.Context, envConfig *recipes.Configuration, templat if err != nil { return "", err } - if secretStore != "" { - secretStoreID, err := resources.ParseResource(secretStore) - if err != nil { - return "", err - } - client, err := v20231001preview.NewSecretStoresClient(secretStoreID.RootScope(), &aztoken.AnonymousCredential{}, armOptions) - if err != nil { - return "", err - } - secrets, err := client.ListSecrets(ctx, secretStoreID.Name(), map[string]any{}, nil) - if err != nil { - return "", err - } - url, err := getGitURL(templatePath) - if err != nil { - return "", err - } - var username, pat *string - path := "git::https://" - user, ok := secrets.Data["username"] - if ok { - username = user.Value - path += fmt.Sprintf("%s:", *username) - } - token, ok := secrets.Data["pat"] - if ok { - pat = token.Value - path += *pat - } - path += fmt.Sprintf("@%s", strings.TrimPrefix(url.String(), "https://")) - return path, err + if secretStore == "" { + return templatePath, err + } + + secretStoreID, err := resources.ParseResource(secretStore) + if err != nil { + return "", err + } + client, err := v20231001preview.NewSecretStoresClient(secretStoreID.RootScope(), &aztoken.AnonymousCredential{}, armOptions) + if err != nil { + return "", err + } + secrets, err := client.ListSecrets(ctx, secretStoreID.Name(), map[string]any{}, nil) + if err != nil { + return "", err + } + url, err := getGitURL(templatePath) + if err != nil { + return "", err + } + var username, pat *string + path := "git::https://" + user, ok := secrets.Data["username"] + if ok { + username = user.Value + path += fmt.Sprintf("%s:", *username) + } + token, ok := secrets.Data["pat"] + if ok { + pat = token.Value + path += *pat } + path += fmt.Sprintf("@%s", strings.TrimPrefix(url.String(), "https://")) + return path, err - return templatePath, err } func getGitURL(templatePath string) (*url.URL, error) { diff --git a/pkg/recipes/terraform/config/config_test.go b/pkg/recipes/terraform/config/config_test.go index 7b049cae43..f206740aab 100644 --- a/pkg/recipes/terraform/config/config_test.go +++ b/pkg/recipes/terraform/config/config_test.go @@ -180,7 +180,7 @@ func Test_NewConfig(t *testing.T) { t.Run(tc.desc, func(t *testing.T) { workingDir := t.TempDir() - tfconfig := New(context.Background(), testRecipeName, tc.envdef, tc.metadata, nil, nil) + tfconfig, _ := New(context.Background(), testRecipeName, tc.envdef, tc.metadata, nil, nil) // validate generated config err := tfconfig.Save(testcontext.New(t), workingDir) @@ -273,7 +273,7 @@ func Test_AddRecipeContext(t *testing.T) { ctx := testcontext.New(t) workingDir := t.TempDir() - tfconfig := New(context.Background(), testRecipeName, tc.envdef, tc.metadata, nil, nil) + tfconfig, _ := New(context.Background(), testRecipeName, tc.envdef, tc.metadata, nil, nil) err := tfconfig.AddRecipeContext(ctx, tc.moduleName, tc.recipeContext) if tc.err == "" { @@ -417,7 +417,7 @@ func Test_AddProviders(t *testing.T) { ctx := testcontext.New(t) workingDir := t.TempDir() - tfconfig := New(context.Background(), testRecipeName, &envRecipe, &resourceRecipe, nil, nil) + tfconfig, _ := New(context.Background(), testRecipeName, &envRecipe, &resourceRecipe, nil, nil) for _, p := range tc.expectedProviders { mProvider.EXPECT().BuildConfig(ctx, &tc.envConfig).Times(1).Return(p, nil) } @@ -477,7 +477,7 @@ func Test_AddOutputs(t *testing.T) { for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { - tfconfig := New(context.Background(), testRecipeName, &envRecipe, &resourceRecipe, nil, nil) + tfconfig, _ := New(context.Background(), testRecipeName, &envRecipe, &resourceRecipe, nil, nil) err := tfconfig.AddOutputs(tc.moduleName) if tc.expectedErr { @@ -506,7 +506,7 @@ func Test_Save_overwrite(t *testing.T) { ctx := testcontext.New(t) testDir := t.TempDir() envRecipe, resourceRecipe := getTestInputs() - tfconfig := New(context.Background(), testRecipeName, &envRecipe, &resourceRecipe, nil, nil) + tfconfig, _ := New(context.Background(), testRecipeName, &envRecipe, &resourceRecipe, nil, nil) err := tfconfig.Save(ctx, testDir) require.NoError(t, err) @@ -518,7 +518,7 @@ func Test_Save_overwrite(t *testing.T) { func Test_Save_ConfigFileReadOnly(t *testing.T) { testDir := t.TempDir() envRecipe, resourceRecipe := getTestInputs() - tfconfig := New(context.Background(), testRecipeName, &envRecipe, &resourceRecipe, nil, nil) + tfconfig, _ := New(context.Background(), testRecipeName, &envRecipe, &resourceRecipe, nil, nil) // Create a test configuration file with read only permission. err := os.WriteFile(getMainConfigFilePath(testDir), []byte(`{"module":{}}`), 0400) @@ -534,7 +534,7 @@ func Test_Save_InvalidWorkingDir(t *testing.T) { testDir := filepath.Join("invalid", uuid.New().String()) envRecipe, resourceRecipe := getTestInputs() - tfconfig := New(context.Background(), testRecipeName, &envRecipe, &resourceRecipe, nil, nil) + tfconfig, _ := New(context.Background(), testRecipeName, &envRecipe, &resourceRecipe, nil, nil) err := tfconfig.Save(testcontext.New(t), testDir) require.Error(t, err) @@ -585,3 +585,35 @@ func Test_getSecretStoreID(t *testing.T) { } } + +func Test_getGitURL(t *testing.T) { + tests := []struct { + desc string + templatePath string + expectedURL string + expectedErr bool + }{ + { + desc: "valid url", + templatePath: "git::https://github.com/project/module", + expectedURL: "https://github.com/project/module", + expectedErr: false, + }, + { + desc: "invalid url", + templatePath: "git::https://git hub.com/project/module", + expectedErr: true, + }, + } + for _, tc := range tests { + t.Run(tc.desc, func(t *testing.T) { + url, err := getGitURL(tc.templatePath) + if tc.expectedErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, url.String(), tc.expectedURL) + } + }) + } +} diff --git a/pkg/recipes/terraform/execute.go b/pkg/recipes/terraform/execute.go index c9966f2ea6..a236619fd4 100644 --- a/pkg/recipes/terraform/execute.go +++ b/pkg/recipes/terraform/execute.go @@ -272,7 +272,6 @@ func downloadAndInspect(ctx context.Context, tf *tfexec.Terraform, options Optio logger.Info(fmt.Sprintf("Downloading Terraform module: %s", options.EnvRecipe.TemplatePath)) downloadStartTime := time.Now() - //path := options.EnvRecipe.TemplatePath if err := downloadModule(ctx, tf, options.EnvRecipe.TemplatePath); err != nil { metrics.DefaultRecipeEngineMetrics.RecordRecipeDownloadDuration(ctx, downloadStartTime, metrics.NewRecipeAttributes(metrics.RecipeEngineOperationDownloadRecipe, options.EnvRecipe.Name, @@ -307,7 +306,10 @@ func getTerraformConfig(ctx context.Context, workingDir string, options Options, } // Create Terraform configuration containing module information with the given recipe parameters. - tfConfig := config.New(ctx, localModuleName, options.EnvRecipe, options.ResourceRecipe, options.EnvConfig, armOptions) + tfConfig, err := config.New(ctx, localModuleName, options.EnvRecipe, options.ResourceRecipe, options.EnvConfig, armOptions) + if err != nil { + return nil, err + } // Before downloading the module, Teraform configuration needs to be persisted in the working directory. // Terraform Get command uses this config file to download module from the source specified in the config. From 188e3d8cbd96325073fe1dd07854224c3d0eeab8 Mon Sep 17 00:00:00 2001 From: Vishwanath Hiremath Date: Thu, 15 Feb 2024 23:56:16 -0800 Subject: [PATCH 06/22] addressing comments Signed-off-by: Vishwanath Hiremath --- .../2023-10-01-preview/types.json | 2 +- .../api/v20231001preview/zz_generated_models.go | 16 ++++++++-------- .../preview/2023-10-01-preview/openapi.json | 14 +++++++------- typespec/Applications.Core/environments.tsp | 10 +++++----- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/hack/bicep-types-radius/generated/applications/applications.core/2023-10-01-preview/types.json b/hack/bicep-types-radius/generated/applications/applications.core/2023-10-01-preview/types.json index 9630aaa167..79526ef0e1 100644 --- a/hack/bicep-types-radius/generated/applications/applications.core/2023-10-01-preview/types.json +++ b/hack/bicep-types-radius/generated/applications/applications.core/2023-10-01-preview/types.json @@ -1 +1 @@ -[{"1":{"Kind":1}},{"1":{"Kind":2}},{"1":{"Kind":3}},{"1":{"Kind":4}},{"1":{"Kind":5}},{"1":{"Kind":6}},{"1":{"Kind":7}},{"1":{"Kind":8}},{"6":{"Value":"Applications.Core/applications"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/applications","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":8,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":9,"Flags":10,"Description":"The resource api version"},"properties":{"Type":11,"Flags":1,"Description":"Application properties"},"tags":{"Type":46,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ApplicationProperties","Properties":{"provisioningState":{"Type":19,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"environment":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"extensions":{"Type":34,"Flags":0,"Description":"The application extension."},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[12,13,14,15,16,17,18]}},{"7":{"Name":"Extension","Discriminator":"kind","BaseProperties":{},"Elements":{"daprSidecar":21,"kubernetesMetadata":26,"kubernetesNamespace":30,"manualScaling":32}}},{"2":{"Name":"DaprSidecarExtension","Properties":{"appPort":{"Type":3,"Flags":0,"Description":"The Dapr appPort. Specifies the internal listening port for the application to handle requests from the Dapr sidecar."},"appId":{"Type":4,"Flags":1,"Description":"The Dapr appId. Specifies the identifier used by Dapr for service invocation."},"config":{"Type":4,"Flags":0,"Description":"Specifies the Dapr configuration to use for the resource."},"protocol":{"Type":24,"Flags":0,"Description":"The Dapr sidecar extension protocol"},"kind":{"Type":25,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"http"}},{"6":{"Value":"grpc"}},{"5":{"Elements":[22,23]}},{"6":{"Value":"daprSidecar"}},{"2":{"Name":"KubernetesMetadataExtension","Properties":{"annotations":{"Type":27,"Flags":0,"Description":"Annotations to be applied to the Kubernetes resources output by the resource"},"labels":{"Type":28,"Flags":0,"Description":"Labels to be applied to the Kubernetes resources output by the resource"},"kind":{"Type":29,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"2":{"Name":"KubernetesMetadataExtensionAnnotations","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"KubernetesMetadataExtensionLabels","Properties":{},"AdditionalProperties":4}},{"6":{"Value":"kubernetesMetadata"}},{"2":{"Name":"KubernetesNamespaceExtension","Properties":{"namespace":{"Type":4,"Flags":1,"Description":"The namespace of the application environment."},"kind":{"Type":31,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"kubernetesNamespace"}},{"2":{"Name":"ManualScalingExtension","Properties":{"replicas":{"Type":3,"Flags":1,"Description":"Replica count."},"kind":{"Type":33,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"manualScaling"}},{"3":{"ItemType":20}},{"2":{"Name":"ResourceStatus","Properties":{"compute":{"Type":36,"Flags":0,"Description":"Represents backing compute resource"},"recipe":{"Type":43,"Flags":2,"Description":"Recipe status at deployment time for a resource."},"outputResources":{"Type":45,"Flags":0,"Description":"Properties of an output resource"}}}},{"7":{"Name":"EnvironmentCompute","Discriminator":"kind","BaseProperties":{"resourceId":{"Type":4,"Flags":0,"Description":"The resource id of the compute resource for application environment."},"identity":{"Type":37,"Flags":0,"Description":"IdentitySettings is the external identity setting."}},"Elements":{"kubernetes":41}}},{"2":{"Name":"IdentitySettings","Properties":{"kind":{"Type":40,"Flags":1,"Description":"IdentitySettingKind is the kind of supported external identity setting"},"oidcIssuer":{"Type":4,"Flags":0,"Description":"The URI for your compute platform's OIDC issuer"},"resource":{"Type":4,"Flags":0,"Description":"The resource ID of the provisioned identity"}}}},{"6":{"Value":"undefined"}},{"6":{"Value":"azure.com.workload"}},{"5":{"Elements":[38,39]}},{"2":{"Name":"KubernetesCompute","Properties":{"namespace":{"Type":4,"Flags":1,"Description":"The namespace to use for the environment."},"kind":{"Type":42,"Flags":1,"Description":"Discriminator property for EnvironmentCompute."}}}},{"6":{"Value":"kubernetes"}},{"2":{"Name":"RecipeStatus","Properties":{"templateKind":{"Type":4,"Flags":1,"Description":"TemplateKind is the kind of the recipe template used by the portable resource upon deployment."},"templatePath":{"Type":4,"Flags":1,"Description":"TemplatePath is the path of the recipe consumed by the portable resource upon deployment."},"templateVersion":{"Type":4,"Flags":0,"Description":"TemplateVersion is the version number of the template."}}}},{"2":{"Name":"OutputResource","Properties":{"localId":{"Type":4,"Flags":0,"Description":"The logical identifier scoped to the owning Radius resource. This is only needed or used when a resource has a dependency relationship. LocalIDs do not have any particular format or meaning beyond being compared to determine dependency relationships."},"id":{"Type":4,"Flags":0,"Description":"The UCP resource ID of the underlying resource."},"radiusManaged":{"Type":2,"Flags":0,"Description":"Determines whether Radius manages the lifecycle of the underlying resource."}}}},{"3":{"ItemType":44}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"SystemData","Properties":{"createdBy":{"Type":4,"Flags":0,"Description":"The identity that created the resource."},"createdByType":{"Type":52,"Flags":0,"Description":"The type of identity that created the resource."},"createdAt":{"Type":4,"Flags":0,"Description":"The timestamp of resource creation (UTC)."},"lastModifiedBy":{"Type":4,"Flags":0,"Description":"The identity that last modified the resource."},"lastModifiedByType":{"Type":57,"Flags":0,"Description":"The type of identity that created the resource."},"lastModifiedAt":{"Type":4,"Flags":0,"Description":"The timestamp of resource last modification (UTC)"}}}},{"6":{"Value":"User"}},{"6":{"Value":"Application"}},{"6":{"Value":"ManagedIdentity"}},{"6":{"Value":"Key"}},{"5":{"Elements":[48,49,50,51]}},{"6":{"Value":"User"}},{"6":{"Value":"Application"}},{"6":{"Value":"ManagedIdentity"}},{"6":{"Value":"Key"}},{"5":{"Elements":[53,54,55,56]}},{"4":{"Name":"Applications.Core/applications@2023-10-01-preview","ScopeType":0,"Body":10}},{"6":{"Value":"Applications.Core/containers"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/containers","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":59,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":60,"Flags":10,"Description":"The resource api version"},"properties":{"Type":62,"Flags":1,"Description":"Container properties"},"tags":{"Type":122,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ContainerProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":70,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"container":{"Type":71,"Flags":1,"Description":"Definition of a container"},"connections":{"Type":108,"Flags":0,"Description":"Specifies a connection to another resource."},"identity":{"Type":37,"Flags":0,"Description":"IdentitySettings is the external identity setting."},"extensions":{"Type":109,"Flags":0,"Description":"Extensions spec of the resource"},"resourceProvisioning":{"Type":112,"Flags":0,"Description":"Specifies how the underlying service/resource is provisioned and managed. Available values are 'internal', where Radius manages the lifecycle of the resource internally, and 'manual', where a user manages the resource."},"resources":{"Type":114,"Flags":0,"Description":"A collection of references to resources associated with the container"},"restartPolicy":{"Type":118,"Flags":0,"Description":"Restart policy for the container"},"runtimes":{"Type":119,"Flags":0,"Description":"The properties for runtime configuration"}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[63,64,65,66,67,68,69]}},{"2":{"Name":"Container","Properties":{"image":{"Type":4,"Flags":1,"Description":"The registry and image to download and run in your container"},"imagePullPolicy":{"Type":75,"Flags":0,"Description":"The image pull policy for the container"},"env":{"Type":76,"Flags":0,"Description":"environment"},"ports":{"Type":81,"Flags":0,"Description":"container ports"},"readinessProbe":{"Type":82,"Flags":0,"Description":"Properties for readiness/liveness probe"},"livenessProbe":{"Type":82,"Flags":0,"Description":"Properties for readiness/liveness probe"},"volumes":{"Type":101,"Flags":0,"Description":"container volumes"},"command":{"Type":102,"Flags":0,"Description":"Entrypoint array. Overrides the container image's ENTRYPOINT"},"args":{"Type":103,"Flags":0,"Description":"Arguments to the entrypoint. Overrides the container image's CMD"},"workingDir":{"Type":4,"Flags":0,"Description":"Working directory for the container"}}}},{"6":{"Value":"Always"}},{"6":{"Value":"IfNotPresent"}},{"6":{"Value":"Never"}},{"5":{"Elements":[72,73,74]}},{"2":{"Name":"ContainerEnv","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"ContainerPortProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"protocol":{"Type":80,"Flags":0,"Description":"The protocol in use by the port"},"provides":{"Type":4,"Flags":0,"Description":"Specifies a route provided by this port"},"scheme":{"Type":4,"Flags":0,"Description":"Specifies the URL scheme of the communication protocol. Consumers can use the scheme to construct a URL. The value defaults to 'http' or 'https' depending on the port value"},"port":{"Type":3,"Flags":0,"Description":"Specifies the port that will be exposed by this container. Must be set when value different from containerPort is desired"}}}},{"6":{"Value":"TCP"}},{"6":{"Value":"UDP"}},{"5":{"Elements":[78,79]}},{"2":{"Name":"ContainerPorts","Properties":{},"AdditionalProperties":77}},{"7":{"Name":"HealthProbeProperties","Discriminator":"kind","BaseProperties":{"initialDelaySeconds":{"Type":3,"Flags":0,"Description":"Initial delay in seconds before probing for readiness/liveness"},"failureThreshold":{"Type":3,"Flags":0,"Description":"Threshold number of times the probe fails after which a failure would be reported"},"periodSeconds":{"Type":3,"Flags":0,"Description":"Interval for the readiness/liveness probe in seconds"},"timeoutSeconds":{"Type":3,"Flags":0,"Description":"Number of seconds after which the readiness/liveness probe times out. Defaults to 5 seconds"}},"Elements":{"exec":83,"httpGet":85,"tcp":88}}},{"2":{"Name":"ExecHealthProbeProperties","Properties":{"command":{"Type":4,"Flags":1,"Description":"Command to execute to probe readiness/liveness"},"kind":{"Type":84,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"6":{"Value":"exec"}},{"2":{"Name":"HttpGetHealthProbeProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"path":{"Type":4,"Flags":1,"Description":"The route to make the HTTP request on"},"headers":{"Type":86,"Flags":0,"Description":"Custom HTTP headers to add to the get request"},"kind":{"Type":87,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"2":{"Name":"HttpGetHealthProbePropertiesHeaders","Properties":{},"AdditionalProperties":4}},{"6":{"Value":"httpGet"}},{"2":{"Name":"TcpHealthProbeProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"kind":{"Type":89,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"6":{"Value":"tcp"}},{"7":{"Name":"Volume","Discriminator":"kind","BaseProperties":{"mountPath":{"Type":4,"Flags":0,"Description":"The path where the volume is mounted"}},"Elements":{"ephemeral":91,"persistent":96}}},{"2":{"Name":"EphemeralVolume","Properties":{"managedStore":{"Type":94,"Flags":1,"Description":"The managed store for the ephemeral volume"},"kind":{"Type":95,"Flags":1,"Description":"Discriminator property for Volume."}}}},{"6":{"Value":"memory"}},{"6":{"Value":"disk"}},{"5":{"Elements":[92,93]}},{"6":{"Value":"ephemeral"}},{"2":{"Name":"PersistentVolume","Properties":{"permission":{"Type":99,"Flags":0,"Description":"The persistent volume permission"},"source":{"Type":4,"Flags":1,"Description":"The source of the volume"},"kind":{"Type":100,"Flags":1,"Description":"Discriminator property for Volume."}}}},{"6":{"Value":"read"}},{"6":{"Value":"write"}},{"5":{"Elements":[97,98]}},{"6":{"Value":"persistent"}},{"2":{"Name":"ContainerVolumes","Properties":{},"AdditionalProperties":90}},{"3":{"ItemType":4}},{"3":{"ItemType":4}},{"2":{"Name":"ConnectionProperties","Properties":{"source":{"Type":4,"Flags":1,"Description":"The source of the connection"},"disableDefaultEnvVars":{"Type":2,"Flags":0,"Description":"default environment variable override"},"iam":{"Type":105,"Flags":0,"Description":"IAM properties"}}}},{"2":{"Name":"IamProperties","Properties":{"kind":{"Type":106,"Flags":1,"Description":"The kind of IAM provider to configure"},"roles":{"Type":107,"Flags":0,"Description":"RBAC permissions to be assigned on the source resource"}}}},{"6":{"Value":"azure"}},{"3":{"ItemType":4}},{"2":{"Name":"ContainerPropertiesConnections","Properties":{},"AdditionalProperties":104}},{"3":{"ItemType":20}},{"6":{"Value":"internal"}},{"6":{"Value":"manual"}},{"5":{"Elements":[110,111]}},{"2":{"Name":"ResourceReference","Properties":{"id":{"Type":4,"Flags":1,"Description":"Resource id of an existing resource"}}}},{"3":{"ItemType":113}},{"6":{"Value":"Always"}},{"6":{"Value":"OnFailure"}},{"6":{"Value":"Never"}},{"5":{"Elements":[115,116,117]}},{"2":{"Name":"RuntimesProperties","Properties":{"kubernetes":{"Type":120,"Flags":0,"Description":"The runtime configuration properties for Kubernetes"}}}},{"2":{"Name":"KubernetesRuntimeProperties","Properties":{"base":{"Type":4,"Flags":0,"Description":"The serialized YAML manifest which represents the base Kubernetes resources to deploy, such as Deployment, Service, ServiceAccount, Secrets, and ConfigMaps."},"pod":{"Type":121,"Flags":0,"Description":"A strategic merge patch that will be applied to the PodSpec object when this container is being deployed."}}}},{"2":{"Name":"KubernetesPodSpec","Properties":{},"AdditionalProperties":0}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/containers@2023-10-01-preview","ScopeType":0,"Body":61}},{"6":{"Value":"Applications.Core/environments"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/environments","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":124,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":125,"Flags":10,"Description":"The resource api version"},"properties":{"Type":127,"Flags":1,"Description":"Environment properties"},"tags":{"Type":153,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"EnvironmentProperties","Properties":{"provisioningState":{"Type":135,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"compute":{"Type":36,"Flags":1,"Description":"Represents backing compute resource"},"providers":{"Type":136,"Flags":0,"Description":"The Cloud providers configuration"},"simulated":{"Type":2,"Flags":0,"Description":"Simulated environment."},"recipes":{"Type":145,"Flags":0,"Description":"Specifies Recipes linked to the Environment."},"recipeConfig":{"Type":146,"Flags":0,"Description":"Specifies recipe configurations needed for the recipes."},"extensions":{"Type":152,"Flags":0,"Description":"The environment extension."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[128,129,130,131,132,133,134]}},{"2":{"Name":"Providers","Properties":{"azure":{"Type":137,"Flags":0,"Description":"The Azure cloud provider definition"},"aws":{"Type":138,"Flags":0,"Description":"The AWS cloud provider definition"}}}},{"2":{"Name":"ProvidersAzure","Properties":{"scope":{"Type":4,"Flags":1,"Description":"Target scope for Azure resources to be deployed into. For example: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup'"}}}},{"2":{"Name":"ProvidersAws","Properties":{"scope":{"Type":4,"Flags":1,"Description":"Target scope for AWS resources to be deployed into. For example: '/planes/aws/aws/accounts/000000000000/regions/us-west-2'"}}}},{"7":{"Name":"RecipeProperties","Discriminator":"templateKind","BaseProperties":{"templatePath":{"Type":4,"Flags":1,"Description":"Path to the template provided by the recipe. Currently only link to Azure Container Registry is supported."},"parameters":{"Type":0,"Flags":0,"Description":"Any object"}},"Elements":{"bicep":140,"terraform":142}}},{"2":{"Name":"BicepRecipeProperties","Properties":{"plainHttp":{"Type":2,"Flags":0,"Description":"Connect to the Bicep registry using HTTP (not-HTTPS). This should be used when the registry is known not to support HTTPS, for example in a locally-hosted registry. Defaults to false (use HTTPS/TLS)."},"templateKind":{"Type":141,"Flags":1,"Description":"Discriminator property for RecipeProperties."}}}},{"6":{"Value":"bicep"}},{"2":{"Name":"TerraformRecipeProperties","Properties":{"templateVersion":{"Type":4,"Flags":0,"Description":"Version of the template to deploy. For Terraform recipes using a module registry this is required, but must be omitted for other module sources."},"templateKind":{"Type":143,"Flags":1,"Description":"Discriminator property for RecipeProperties."}}}},{"6":{"Value":"terraform"}},{"2":{"Name":"DictionaryOfRecipeProperties","Properties":{},"AdditionalProperties":139}},{"2":{"Name":"EnvironmentPropertiesRecipes","Properties":{},"AdditionalProperties":144}},{"2":{"Name":"RecipeConfigProperties","Properties":{"terraform":{"Type":147,"Flags":0,"Description":"Specifies the terraform config properties"}}}},{"2":{"Name":"TerraformConfigProperties","Properties":{"authentication":{"Type":148,"Flags":0,"Description":"Specifies authentication information needed to use private terraform module repositories."}}}},{"2":{"Name":"AuthConfig","Properties":{"git":{"Type":149,"Flags":0,"Description":"Specifies authentication information needed to use private terraform module repositories from git module source"}}}},{"2":{"Name":"GitAuthConfig","Properties":{"pat":{"Type":151,"Flags":0,"Description":"Specifies the secret details of type personal access token for each different git platforms"}}}},{"2":{"Name":"Secret","Properties":{"secretStore":{"Type":4,"Flags":0,"Description":"The resource id for the secret store containing credentials"}}}},{"2":{"Name":"GitAuthConfigPat","Properties":{},"AdditionalProperties":150}},{"3":{"ItemType":20}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/environments@2023-10-01-preview","ScopeType":0,"Body":126}},{"6":{"Value":"Applications.Core/extenders"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/extenders","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":155,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":156,"Flags":10,"Description":"The resource api version"},"properties":{"Type":158,"Flags":1,"Description":"ExtenderResource portable resource properties"},"tags":{"Type":171,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ExtenderProperties","Properties":{"environment":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the environment that the portable resource is linked to"},"application":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the application that the portable resource is consumed by (if applicable)"},"provisioningState":{"Type":166,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"secrets":{"Type":0,"Flags":0,"Description":"Any object"},"recipe":{"Type":167,"Flags":0,"Description":"The recipe used to automatically deploy underlying infrastructure for a portable resource"},"resourceProvisioning":{"Type":170,"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."}},"AdditionalProperties":0}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[159,160,161,162,163,164,165]}},{"2":{"Name":"Recipe","Properties":{"name":{"Type":4,"Flags":1,"Description":"The name of the recipe within the environment to use"},"parameters":{"Type":0,"Flags":0,"Description":"Any object"}}}},{"6":{"Value":"recipe"}},{"6":{"Value":"manual"}},{"5":{"Elements":[168,169]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/extenders@2023-10-01-preview","ScopeType":0,"Body":157}},{"6":{"Value":"Applications.Core/gateways"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/gateways","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":173,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":174,"Flags":10,"Description":"The resource api version"},"properties":{"Type":176,"Flags":1,"Description":"Gateway properties"},"tags":{"Type":192,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"GatewayProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":184,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"internal":{"Type":2,"Flags":0,"Description":"Sets Gateway to not be exposed externally (no public IP address associated). Defaults to false (exposed to internet)."},"hostname":{"Type":185,"Flags":0,"Description":"Declare hostname information for the Gateway. Leaving the hostname empty auto-assigns one: mygateway.myapp.PUBLICHOSTNAMEORIP.nip.io."},"routes":{"Type":187,"Flags":1,"Description":"Routes attached to this Gateway"},"tls":{"Type":188,"Flags":0,"Description":"TLS configuration definition for Gateway resource."},"url":{"Type":4,"Flags":2,"Description":"URL of the gateway resource. Readonly"}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[177,178,179,180,181,182,183]}},{"2":{"Name":"GatewayHostname","Properties":{"prefix":{"Type":4,"Flags":0,"Description":"Specify a prefix for the hostname: myhostname.myapp.PUBLICHOSTNAMEORIP.nip.io. Mutually exclusive with 'fullyQualifiedHostname' and will be overridden if both are defined."},"fullyQualifiedHostname":{"Type":4,"Flags":0,"Description":"Specify a fully-qualified domain name: myapp.mydomain.com. Mutually exclusive with 'prefix' and will take priority if both are defined."}}}},{"2":{"Name":"GatewayRoute","Properties":{"path":{"Type":4,"Flags":0,"Description":"The path to match the incoming request path on. Ex - /myservice."},"destination":{"Type":4,"Flags":0,"Description":"The HttpRoute to route to. Ex - myserviceroute.id."},"replacePrefix":{"Type":4,"Flags":0,"Description":"Optionally update the prefix when sending the request to the service. Ex - replacePrefix: '/' and path: '/myservice' will transform '/myservice/myroute' to '/myroute'"}}}},{"3":{"ItemType":186}},{"2":{"Name":"GatewayTls","Properties":{"sslPassthrough":{"Type":2,"Flags":0,"Description":"If true, gateway lets the https traffic sslPassthrough to the backend servers for decryption."},"minimumProtocolVersion":{"Type":191,"Flags":0,"Description":"Tls Minimum versions for Gateway resource."},"certificateFrom":{"Type":4,"Flags":0,"Description":"The resource id for the secret containing the TLS certificate and key for the gateway."}}}},{"6":{"Value":"1.2"}},{"6":{"Value":"1.3"}},{"5":{"Elements":[189,190]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/gateways@2023-10-01-preview","ScopeType":0,"Body":175}},{"6":{"Value":"Applications.Core/httpRoutes"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/httpRoutes","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":194,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":195,"Flags":10,"Description":"The resource api version"},"properties":{"Type":197,"Flags":1,"Description":"HTTPRoute properties"},"tags":{"Type":206,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"HttpRouteProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":205,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"hostname":{"Type":4,"Flags":0,"Description":"The internal hostname accepting traffic for the HTTP Route. Readonly."},"port":{"Type":3,"Flags":0,"Description":"The port number for the HTTP Route. Defaults to 80. Readonly."},"scheme":{"Type":4,"Flags":2,"Description":"The scheme used for traffic. Readonly."},"url":{"Type":4,"Flags":2,"Description":"A stable URL that that can be used to route traffic to a resource. Readonly."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[198,199,200,201,202,203,204]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/httpRoutes@2023-10-01-preview","ScopeType":0,"Body":196}},{"6":{"Value":"Applications.Core/secretStores"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/secretStores","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":208,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":209,"Flags":10,"Description":"The resource api version"},"properties":{"Type":211,"Flags":1,"Description":"The properties of SecretStore"},"tags":{"Type":229,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"SecretStoreProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":219,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"type":{"Type":222,"Flags":0,"Description":"The type of SecretStore data"},"data":{"Type":228,"Flags":1,"Description":"An object to represent key-value type secrets"},"resource":{"Type":4,"Flags":0,"Description":"The resource id of external secret store."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[212,213,214,215,216,217,218]}},{"6":{"Value":"generic"}},{"6":{"Value":"certificate"}},{"5":{"Elements":[220,221]}},{"2":{"Name":"SecretValueProperties","Properties":{"encoding":{"Type":226,"Flags":0,"Description":"The type of SecretValue Encoding"},"value":{"Type":4,"Flags":0,"Description":"The value of secret."},"valueFrom":{"Type":227,"Flags":0,"Description":"The Secret value source properties"}}}},{"6":{"Value":"raw"}},{"6":{"Value":"base64"}},{"5":{"Elements":[224,225]}},{"2":{"Name":"ValueFromProperties","Properties":{"name":{"Type":4,"Flags":1,"Description":"The name of the referenced secret."},"version":{"Type":4,"Flags":0,"Description":"The version of the referenced secret."}}}},{"2":{"Name":"SecretStorePropertiesData","Properties":{},"AdditionalProperties":223}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/secretStores@2023-10-01-preview","ScopeType":0,"Body":210}},{"6":{"Value":"Applications.Core/volumes"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/volumes","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":231,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":232,"Flags":10,"Description":"The resource api version"},"properties":{"Type":234,"Flags":1,"Description":"Volume properties"},"tags":{"Type":266,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"7":{"Name":"VolumeProperties","Discriminator":"kind","BaseProperties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":242,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."}},"Elements":{"azure.com.keyvault":243}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[235,236,237,238,239,240,241]}},{"2":{"Name":"AzureKeyVaultVolumeProperties","Properties":{"certificates":{"Type":256,"Flags":0,"Description":"The KeyVault certificates that this volume exposes"},"keys":{"Type":258,"Flags":0,"Description":"The KeyVault keys that this volume exposes"},"resource":{"Type":4,"Flags":1,"Description":"The ID of the keyvault to use for this volume resource"},"secrets":{"Type":264,"Flags":0,"Description":"The KeyVault secrets that this volume exposes"},"kind":{"Type":265,"Flags":1,"Description":"Discriminator property for VolumeProperties."}}}},{"2":{"Name":"CertificateObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"encoding":{"Type":248,"Flags":0,"Description":"Represents secret encodings"},"format":{"Type":251,"Flags":0,"Description":"Represents certificate formats"},"name":{"Type":4,"Flags":1,"Description":"The name of the certificate"},"certType":{"Type":255,"Flags":0,"Description":"Represents certificate types"},"version":{"Type":4,"Flags":0,"Description":"Certificate version"}}}},{"6":{"Value":"utf-8"}},{"6":{"Value":"hex"}},{"6":{"Value":"base64"}},{"5":{"Elements":[245,246,247]}},{"6":{"Value":"pem"}},{"6":{"Value":"pfx"}},{"5":{"Elements":[249,250]}},{"6":{"Value":"certificate"}},{"6":{"Value":"privatekey"}},{"6":{"Value":"publickey"}},{"5":{"Elements":[252,253,254]}},{"2":{"Name":"AzureKeyVaultVolumePropertiesCertificates","Properties":{},"AdditionalProperties":244}},{"2":{"Name":"KeyObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"name":{"Type":4,"Flags":1,"Description":"The name of the key"},"version":{"Type":4,"Flags":0,"Description":"Key version"}}}},{"2":{"Name":"AzureKeyVaultVolumePropertiesKeys","Properties":{},"AdditionalProperties":257}},{"2":{"Name":"SecretObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"encoding":{"Type":263,"Flags":0,"Description":"Represents secret encodings"},"name":{"Type":4,"Flags":1,"Description":"The name of the secret"},"version":{"Type":4,"Flags":0,"Description":"secret version"}}}},{"6":{"Value":"utf-8"}},{"6":{"Value":"hex"}},{"6":{"Value":"base64"}},{"5":{"Elements":[260,261,262]}},{"2":{"Name":"AzureKeyVaultVolumePropertiesSecrets","Properties":{},"AdditionalProperties":259}},{"6":{"Value":"azure.com.keyvault"}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/volumes@2023-10-01-preview","ScopeType":0,"Body":233}},{"8":{"Name":"listSecrets","ResourceType":"Applications.Core/extenders","ApiVersion":"2023-10-01-preview","Output":0,"Input":0}},{"2":{"Name":"SecretStoreListSecretsResult","Properties":{"type":{"Type":272,"Flags":2,"Description":"The type of SecretStore data"},"data":{"Type":273,"Flags":2,"Description":"An object to represent key-value type secrets"}}}},{"6":{"Value":"generic"}},{"6":{"Value":"certificate"}},{"5":{"Elements":[270,271]}},{"2":{"Name":"SecretStoreListSecretsResultData","Properties":{},"AdditionalProperties":223}},{"8":{"Name":"listSecrets","ResourceType":"Applications.Core/secretStores","ApiVersion":"2023-10-01-preview","Output":269,"Input":0}}] \ No newline at end of file +[{"1":{"Kind":1}},{"1":{"Kind":2}},{"1":{"Kind":3}},{"1":{"Kind":4}},{"1":{"Kind":5}},{"1":{"Kind":6}},{"1":{"Kind":7}},{"1":{"Kind":8}},{"6":{"Value":"Applications.Core/applications"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/applications","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":8,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":9,"Flags":10,"Description":"The resource api version"},"properties":{"Type":11,"Flags":1,"Description":"Application properties"},"tags":{"Type":46,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ApplicationProperties","Properties":{"provisioningState":{"Type":19,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"environment":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"extensions":{"Type":34,"Flags":0,"Description":"The application extension."},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[12,13,14,15,16,17,18]}},{"7":{"Name":"Extension","Discriminator":"kind","BaseProperties":{},"Elements":{"daprSidecar":21,"kubernetesMetadata":26,"kubernetesNamespace":30,"manualScaling":32}}},{"2":{"Name":"DaprSidecarExtension","Properties":{"appPort":{"Type":3,"Flags":0,"Description":"The Dapr appPort. Specifies the internal listening port for the application to handle requests from the Dapr sidecar."},"appId":{"Type":4,"Flags":1,"Description":"The Dapr appId. Specifies the identifier used by Dapr for service invocation."},"config":{"Type":4,"Flags":0,"Description":"Specifies the Dapr configuration to use for the resource."},"protocol":{"Type":24,"Flags":0,"Description":"The Dapr sidecar extension protocol"},"kind":{"Type":25,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"http"}},{"6":{"Value":"grpc"}},{"5":{"Elements":[22,23]}},{"6":{"Value":"daprSidecar"}},{"2":{"Name":"KubernetesMetadataExtension","Properties":{"annotations":{"Type":27,"Flags":0,"Description":"Annotations to be applied to the Kubernetes resources output by the resource"},"labels":{"Type":28,"Flags":0,"Description":"Labels to be applied to the Kubernetes resources output by the resource"},"kind":{"Type":29,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"2":{"Name":"KubernetesMetadataExtensionAnnotations","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"KubernetesMetadataExtensionLabels","Properties":{},"AdditionalProperties":4}},{"6":{"Value":"kubernetesMetadata"}},{"2":{"Name":"KubernetesNamespaceExtension","Properties":{"namespace":{"Type":4,"Flags":1,"Description":"The namespace of the application environment."},"kind":{"Type":31,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"kubernetesNamespace"}},{"2":{"Name":"ManualScalingExtension","Properties":{"replicas":{"Type":3,"Flags":1,"Description":"Replica count."},"kind":{"Type":33,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"manualScaling"}},{"3":{"ItemType":20}},{"2":{"Name":"ResourceStatus","Properties":{"compute":{"Type":36,"Flags":0,"Description":"Represents backing compute resource"},"recipe":{"Type":43,"Flags":2,"Description":"Recipe status at deployment time for a resource."},"outputResources":{"Type":45,"Flags":0,"Description":"Properties of an output resource"}}}},{"7":{"Name":"EnvironmentCompute","Discriminator":"kind","BaseProperties":{"resourceId":{"Type":4,"Flags":0,"Description":"The resource id of the compute resource for application environment."},"identity":{"Type":37,"Flags":0,"Description":"IdentitySettings is the external identity setting."}},"Elements":{"kubernetes":41}}},{"2":{"Name":"IdentitySettings","Properties":{"kind":{"Type":40,"Flags":1,"Description":"IdentitySettingKind is the kind of supported external identity setting"},"oidcIssuer":{"Type":4,"Flags":0,"Description":"The URI for your compute platform's OIDC issuer"},"resource":{"Type":4,"Flags":0,"Description":"The resource ID of the provisioned identity"}}}},{"6":{"Value":"undefined"}},{"6":{"Value":"azure.com.workload"}},{"5":{"Elements":[38,39]}},{"2":{"Name":"KubernetesCompute","Properties":{"namespace":{"Type":4,"Flags":1,"Description":"The namespace to use for the environment."},"kind":{"Type":42,"Flags":1,"Description":"Discriminator property for EnvironmentCompute."}}}},{"6":{"Value":"kubernetes"}},{"2":{"Name":"RecipeStatus","Properties":{"templateKind":{"Type":4,"Flags":1,"Description":"TemplateKind is the kind of the recipe template used by the portable resource upon deployment."},"templatePath":{"Type":4,"Flags":1,"Description":"TemplatePath is the path of the recipe consumed by the portable resource upon deployment."},"templateVersion":{"Type":4,"Flags":0,"Description":"TemplateVersion is the version number of the template."}}}},{"2":{"Name":"OutputResource","Properties":{"localId":{"Type":4,"Flags":0,"Description":"The logical identifier scoped to the owning Radius resource. This is only needed or used when a resource has a dependency relationship. LocalIDs do not have any particular format or meaning beyond being compared to determine dependency relationships."},"id":{"Type":4,"Flags":0,"Description":"The UCP resource ID of the underlying resource."},"radiusManaged":{"Type":2,"Flags":0,"Description":"Determines whether Radius manages the lifecycle of the underlying resource."}}}},{"3":{"ItemType":44}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"SystemData","Properties":{"createdBy":{"Type":4,"Flags":0,"Description":"The identity that created the resource."},"createdByType":{"Type":52,"Flags":0,"Description":"The type of identity that created the resource."},"createdAt":{"Type":4,"Flags":0,"Description":"The timestamp of resource creation (UTC)."},"lastModifiedBy":{"Type":4,"Flags":0,"Description":"The identity that last modified the resource."},"lastModifiedByType":{"Type":57,"Flags":0,"Description":"The type of identity that created the resource."},"lastModifiedAt":{"Type":4,"Flags":0,"Description":"The timestamp of resource last modification (UTC)"}}}},{"6":{"Value":"User"}},{"6":{"Value":"Application"}},{"6":{"Value":"ManagedIdentity"}},{"6":{"Value":"Key"}},{"5":{"Elements":[48,49,50,51]}},{"6":{"Value":"User"}},{"6":{"Value":"Application"}},{"6":{"Value":"ManagedIdentity"}},{"6":{"Value":"Key"}},{"5":{"Elements":[53,54,55,56]}},{"4":{"Name":"Applications.Core/applications@2023-10-01-preview","ScopeType":0,"Body":10}},{"6":{"Value":"Applications.Core/containers"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/containers","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":59,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":60,"Flags":10,"Description":"The resource api version"},"properties":{"Type":62,"Flags":1,"Description":"Container properties"},"tags":{"Type":122,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ContainerProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":70,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"container":{"Type":71,"Flags":1,"Description":"Definition of a container"},"connections":{"Type":108,"Flags":0,"Description":"Specifies a connection to another resource."},"identity":{"Type":37,"Flags":0,"Description":"IdentitySettings is the external identity setting."},"extensions":{"Type":109,"Flags":0,"Description":"Extensions spec of the resource"},"resourceProvisioning":{"Type":112,"Flags":0,"Description":"Specifies how the underlying service/resource is provisioned and managed. Available values are 'internal', where Radius manages the lifecycle of the resource internally, and 'manual', where a user manages the resource."},"resources":{"Type":114,"Flags":0,"Description":"A collection of references to resources associated with the container"},"restartPolicy":{"Type":118,"Flags":0,"Description":"Restart policy for the container"},"runtimes":{"Type":119,"Flags":0,"Description":"The properties for runtime configuration"}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[63,64,65,66,67,68,69]}},{"2":{"Name":"Container","Properties":{"image":{"Type":4,"Flags":1,"Description":"The registry and image to download and run in your container"},"imagePullPolicy":{"Type":75,"Flags":0,"Description":"The image pull policy for the container"},"env":{"Type":76,"Flags":0,"Description":"environment"},"ports":{"Type":81,"Flags":0,"Description":"container ports"},"readinessProbe":{"Type":82,"Flags":0,"Description":"Properties for readiness/liveness probe"},"livenessProbe":{"Type":82,"Flags":0,"Description":"Properties for readiness/liveness probe"},"volumes":{"Type":101,"Flags":0,"Description":"container volumes"},"command":{"Type":102,"Flags":0,"Description":"Entrypoint array. Overrides the container image's ENTRYPOINT"},"args":{"Type":103,"Flags":0,"Description":"Arguments to the entrypoint. Overrides the container image's CMD"},"workingDir":{"Type":4,"Flags":0,"Description":"Working directory for the container"}}}},{"6":{"Value":"Always"}},{"6":{"Value":"IfNotPresent"}},{"6":{"Value":"Never"}},{"5":{"Elements":[72,73,74]}},{"2":{"Name":"ContainerEnv","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"ContainerPortProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"protocol":{"Type":80,"Flags":0,"Description":"The protocol in use by the port"},"provides":{"Type":4,"Flags":0,"Description":"Specifies a route provided by this port"},"scheme":{"Type":4,"Flags":0,"Description":"Specifies the URL scheme of the communication protocol. Consumers can use the scheme to construct a URL. The value defaults to 'http' or 'https' depending on the port value"},"port":{"Type":3,"Flags":0,"Description":"Specifies the port that will be exposed by this container. Must be set when value different from containerPort is desired"}}}},{"6":{"Value":"TCP"}},{"6":{"Value":"UDP"}},{"5":{"Elements":[78,79]}},{"2":{"Name":"ContainerPorts","Properties":{},"AdditionalProperties":77}},{"7":{"Name":"HealthProbeProperties","Discriminator":"kind","BaseProperties":{"initialDelaySeconds":{"Type":3,"Flags":0,"Description":"Initial delay in seconds before probing for readiness/liveness"},"failureThreshold":{"Type":3,"Flags":0,"Description":"Threshold number of times the probe fails after which a failure would be reported"},"periodSeconds":{"Type":3,"Flags":0,"Description":"Interval for the readiness/liveness probe in seconds"},"timeoutSeconds":{"Type":3,"Flags":0,"Description":"Number of seconds after which the readiness/liveness probe times out. Defaults to 5 seconds"}},"Elements":{"exec":83,"httpGet":85,"tcp":88}}},{"2":{"Name":"ExecHealthProbeProperties","Properties":{"command":{"Type":4,"Flags":1,"Description":"Command to execute to probe readiness/liveness"},"kind":{"Type":84,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"6":{"Value":"exec"}},{"2":{"Name":"HttpGetHealthProbeProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"path":{"Type":4,"Flags":1,"Description":"The route to make the HTTP request on"},"headers":{"Type":86,"Flags":0,"Description":"Custom HTTP headers to add to the get request"},"kind":{"Type":87,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"2":{"Name":"HttpGetHealthProbePropertiesHeaders","Properties":{},"AdditionalProperties":4}},{"6":{"Value":"httpGet"}},{"2":{"Name":"TcpHealthProbeProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"kind":{"Type":89,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"6":{"Value":"tcp"}},{"7":{"Name":"Volume","Discriminator":"kind","BaseProperties":{"mountPath":{"Type":4,"Flags":0,"Description":"The path where the volume is mounted"}},"Elements":{"ephemeral":91,"persistent":96}}},{"2":{"Name":"EphemeralVolume","Properties":{"managedStore":{"Type":94,"Flags":1,"Description":"The managed store for the ephemeral volume"},"kind":{"Type":95,"Flags":1,"Description":"Discriminator property for Volume."}}}},{"6":{"Value":"memory"}},{"6":{"Value":"disk"}},{"5":{"Elements":[92,93]}},{"6":{"Value":"ephemeral"}},{"2":{"Name":"PersistentVolume","Properties":{"permission":{"Type":99,"Flags":0,"Description":"The persistent volume permission"},"source":{"Type":4,"Flags":1,"Description":"The source of the volume"},"kind":{"Type":100,"Flags":1,"Description":"Discriminator property for Volume."}}}},{"6":{"Value":"read"}},{"6":{"Value":"write"}},{"5":{"Elements":[97,98]}},{"6":{"Value":"persistent"}},{"2":{"Name":"ContainerVolumes","Properties":{},"AdditionalProperties":90}},{"3":{"ItemType":4}},{"3":{"ItemType":4}},{"2":{"Name":"ConnectionProperties","Properties":{"source":{"Type":4,"Flags":1,"Description":"The source of the connection"},"disableDefaultEnvVars":{"Type":2,"Flags":0,"Description":"default environment variable override"},"iam":{"Type":105,"Flags":0,"Description":"IAM properties"}}}},{"2":{"Name":"IamProperties","Properties":{"kind":{"Type":106,"Flags":1,"Description":"The kind of IAM provider to configure"},"roles":{"Type":107,"Flags":0,"Description":"RBAC permissions to be assigned on the source resource"}}}},{"6":{"Value":"azure"}},{"3":{"ItemType":4}},{"2":{"Name":"ContainerPropertiesConnections","Properties":{},"AdditionalProperties":104}},{"3":{"ItemType":20}},{"6":{"Value":"internal"}},{"6":{"Value":"manual"}},{"5":{"Elements":[110,111]}},{"2":{"Name":"ResourceReference","Properties":{"id":{"Type":4,"Flags":1,"Description":"Resource id of an existing resource"}}}},{"3":{"ItemType":113}},{"6":{"Value":"Always"}},{"6":{"Value":"OnFailure"}},{"6":{"Value":"Never"}},{"5":{"Elements":[115,116,117]}},{"2":{"Name":"RuntimesProperties","Properties":{"kubernetes":{"Type":120,"Flags":0,"Description":"The runtime configuration properties for Kubernetes"}}}},{"2":{"Name":"KubernetesRuntimeProperties","Properties":{"base":{"Type":4,"Flags":0,"Description":"The serialized YAML manifest which represents the base Kubernetes resources to deploy, such as Deployment, Service, ServiceAccount, Secrets, and ConfigMaps."},"pod":{"Type":121,"Flags":0,"Description":"A strategic merge patch that will be applied to the PodSpec object when this container is being deployed."}}}},{"2":{"Name":"KubernetesPodSpec","Properties":{},"AdditionalProperties":0}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/containers@2023-10-01-preview","ScopeType":0,"Body":61}},{"6":{"Value":"Applications.Core/environments"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/environments","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":124,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":125,"Flags":10,"Description":"The resource api version"},"properties":{"Type":127,"Flags":1,"Description":"Environment properties"},"tags":{"Type":153,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"EnvironmentProperties","Properties":{"provisioningState":{"Type":135,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"compute":{"Type":36,"Flags":1,"Description":"Represents backing compute resource"},"providers":{"Type":136,"Flags":0,"Description":"The Cloud providers configuration"},"simulated":{"Type":2,"Flags":0,"Description":"Simulated environment."},"recipes":{"Type":145,"Flags":0,"Description":"Specifies Recipes linked to the Environment."},"recipeConfig":{"Type":146,"Flags":0,"Description":"Specifies recipe configurations needed for the recipes."},"extensions":{"Type":152,"Flags":0,"Description":"The environment extension."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[128,129,130,131,132,133,134]}},{"2":{"Name":"Providers","Properties":{"azure":{"Type":137,"Flags":0,"Description":"The Azure cloud provider definition"},"aws":{"Type":138,"Flags":0,"Description":"The AWS cloud provider definition"}}}},{"2":{"Name":"ProvidersAzure","Properties":{"scope":{"Type":4,"Flags":1,"Description":"Target scope for Azure resources to be deployed into. For example: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup'"}}}},{"2":{"Name":"ProvidersAws","Properties":{"scope":{"Type":4,"Flags":1,"Description":"Target scope for AWS resources to be deployed into. For example: '/planes/aws/aws/accounts/000000000000/regions/us-west-2'"}}}},{"7":{"Name":"RecipeProperties","Discriminator":"templateKind","BaseProperties":{"templatePath":{"Type":4,"Flags":1,"Description":"Path to the template provided by the recipe. Currently only link to Azure Container Registry is supported."},"parameters":{"Type":0,"Flags":0,"Description":"Any object"}},"Elements":{"bicep":140,"terraform":142}}},{"2":{"Name":"BicepRecipeProperties","Properties":{"plainHttp":{"Type":2,"Flags":0,"Description":"Connect to the Bicep registry using HTTP (not-HTTPS). This should be used when the registry is known not to support HTTPS, for example in a locally-hosted registry. Defaults to false (use HTTPS/TLS)."},"templateKind":{"Type":141,"Flags":1,"Description":"Discriminator property for RecipeProperties."}}}},{"6":{"Value":"bicep"}},{"2":{"Name":"TerraformRecipeProperties","Properties":{"templateVersion":{"Type":4,"Flags":0,"Description":"Version of the template to deploy. For Terraform recipes using a module registry this is required, but must be omitted for other module sources."},"templateKind":{"Type":143,"Flags":1,"Description":"Discriminator property for RecipeProperties."}}}},{"6":{"Value":"terraform"}},{"2":{"Name":"DictionaryOfRecipeProperties","Properties":{},"AdditionalProperties":139}},{"2":{"Name":"EnvironmentPropertiesRecipes","Properties":{},"AdditionalProperties":144}},{"2":{"Name":"RecipeConfigProperties","Properties":{"terraform":{"Type":147,"Flags":0,"Description":"Specifies the terraform config properties"}}}},{"2":{"Name":"TerraformConfigProperties","Properties":{"authentication":{"Type":148,"Flags":0,"Description":"Specifies authentication information needed to use private terraform module repositories."}}}},{"2":{"Name":"AuthConfig","Properties":{"git":{"Type":149,"Flags":0,"Description":"Specifies authentication information needed to access private terraform modules from Git repository sources."}}}},{"2":{"Name":"GitAuthConfig","Properties":{"pat":{"Type":151,"Flags":0,"Description":"Specifies the secret details of type personal access token for each different git platforms"}}}},{"2":{"Name":"Secret","Properties":{"secretStore":{"Type":4,"Flags":0,"Description":"The resource id for the Applications.Core/SecretStore resource containing credentials."}}}},{"2":{"Name":"GitAuthConfigPat","Properties":{},"AdditionalProperties":150}},{"3":{"ItemType":20}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/environments@2023-10-01-preview","ScopeType":0,"Body":126}},{"6":{"Value":"Applications.Core/extenders"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/extenders","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":155,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":156,"Flags":10,"Description":"The resource api version"},"properties":{"Type":158,"Flags":1,"Description":"ExtenderResource portable resource properties"},"tags":{"Type":171,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ExtenderProperties","Properties":{"environment":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the environment that the portable resource is linked to"},"application":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the application that the portable resource is consumed by (if applicable)"},"provisioningState":{"Type":166,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"secrets":{"Type":0,"Flags":0,"Description":"Any object"},"recipe":{"Type":167,"Flags":0,"Description":"The recipe used to automatically deploy underlying infrastructure for a portable resource"},"resourceProvisioning":{"Type":170,"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."}},"AdditionalProperties":0}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[159,160,161,162,163,164,165]}},{"2":{"Name":"Recipe","Properties":{"name":{"Type":4,"Flags":1,"Description":"The name of the recipe within the environment to use"},"parameters":{"Type":0,"Flags":0,"Description":"Any object"}}}},{"6":{"Value":"recipe"}},{"6":{"Value":"manual"}},{"5":{"Elements":[168,169]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/extenders@2023-10-01-preview","ScopeType":0,"Body":157}},{"6":{"Value":"Applications.Core/gateways"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/gateways","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":173,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":174,"Flags":10,"Description":"The resource api version"},"properties":{"Type":176,"Flags":1,"Description":"Gateway properties"},"tags":{"Type":192,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"GatewayProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":184,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"internal":{"Type":2,"Flags":0,"Description":"Sets Gateway to not be exposed externally (no public IP address associated). Defaults to false (exposed to internet)."},"hostname":{"Type":185,"Flags":0,"Description":"Declare hostname information for the Gateway. Leaving the hostname empty auto-assigns one: mygateway.myapp.PUBLICHOSTNAMEORIP.nip.io."},"routes":{"Type":187,"Flags":1,"Description":"Routes attached to this Gateway"},"tls":{"Type":188,"Flags":0,"Description":"TLS configuration definition for Gateway resource."},"url":{"Type":4,"Flags":2,"Description":"URL of the gateway resource. Readonly"}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[177,178,179,180,181,182,183]}},{"2":{"Name":"GatewayHostname","Properties":{"prefix":{"Type":4,"Flags":0,"Description":"Specify a prefix for the hostname: myhostname.myapp.PUBLICHOSTNAMEORIP.nip.io. Mutually exclusive with 'fullyQualifiedHostname' and will be overridden if both are defined."},"fullyQualifiedHostname":{"Type":4,"Flags":0,"Description":"Specify a fully-qualified domain name: myapp.mydomain.com. Mutually exclusive with 'prefix' and will take priority if both are defined."}}}},{"2":{"Name":"GatewayRoute","Properties":{"path":{"Type":4,"Flags":0,"Description":"The path to match the incoming request path on. Ex - /myservice."},"destination":{"Type":4,"Flags":0,"Description":"The HttpRoute to route to. Ex - myserviceroute.id."},"replacePrefix":{"Type":4,"Flags":0,"Description":"Optionally update the prefix when sending the request to the service. Ex - replacePrefix: '/' and path: '/myservice' will transform '/myservice/myroute' to '/myroute'"}}}},{"3":{"ItemType":186}},{"2":{"Name":"GatewayTls","Properties":{"sslPassthrough":{"Type":2,"Flags":0,"Description":"If true, gateway lets the https traffic sslPassthrough to the backend servers for decryption."},"minimumProtocolVersion":{"Type":191,"Flags":0,"Description":"Tls Minimum versions for Gateway resource."},"certificateFrom":{"Type":4,"Flags":0,"Description":"The resource id for the secret containing the TLS certificate and key for the gateway."}}}},{"6":{"Value":"1.2"}},{"6":{"Value":"1.3"}},{"5":{"Elements":[189,190]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/gateways@2023-10-01-preview","ScopeType":0,"Body":175}},{"6":{"Value":"Applications.Core/httpRoutes"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/httpRoutes","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":194,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":195,"Flags":10,"Description":"The resource api version"},"properties":{"Type":197,"Flags":1,"Description":"HTTPRoute properties"},"tags":{"Type":206,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"HttpRouteProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":205,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"hostname":{"Type":4,"Flags":0,"Description":"The internal hostname accepting traffic for the HTTP Route. Readonly."},"port":{"Type":3,"Flags":0,"Description":"The port number for the HTTP Route. Defaults to 80. Readonly."},"scheme":{"Type":4,"Flags":2,"Description":"The scheme used for traffic. Readonly."},"url":{"Type":4,"Flags":2,"Description":"A stable URL that that can be used to route traffic to a resource. Readonly."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[198,199,200,201,202,203,204]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/httpRoutes@2023-10-01-preview","ScopeType":0,"Body":196}},{"6":{"Value":"Applications.Core/secretStores"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/secretStores","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":208,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":209,"Flags":10,"Description":"The resource api version"},"properties":{"Type":211,"Flags":1,"Description":"The properties of SecretStore"},"tags":{"Type":229,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"SecretStoreProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":219,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"type":{"Type":222,"Flags":0,"Description":"The type of SecretStore data"},"data":{"Type":228,"Flags":1,"Description":"An object to represent key-value type secrets"},"resource":{"Type":4,"Flags":0,"Description":"The resource id of external secret store."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[212,213,214,215,216,217,218]}},{"6":{"Value":"generic"}},{"6":{"Value":"certificate"}},{"5":{"Elements":[220,221]}},{"2":{"Name":"SecretValueProperties","Properties":{"encoding":{"Type":226,"Flags":0,"Description":"The type of SecretValue Encoding"},"value":{"Type":4,"Flags":0,"Description":"The value of secret."},"valueFrom":{"Type":227,"Flags":0,"Description":"The Secret value source properties"}}}},{"6":{"Value":"raw"}},{"6":{"Value":"base64"}},{"5":{"Elements":[224,225]}},{"2":{"Name":"ValueFromProperties","Properties":{"name":{"Type":4,"Flags":1,"Description":"The name of the referenced secret."},"version":{"Type":4,"Flags":0,"Description":"The version of the referenced secret."}}}},{"2":{"Name":"SecretStorePropertiesData","Properties":{},"AdditionalProperties":223}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/secretStores@2023-10-01-preview","ScopeType":0,"Body":210}},{"6":{"Value":"Applications.Core/volumes"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/volumes","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":231,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":232,"Flags":10,"Description":"The resource api version"},"properties":{"Type":234,"Flags":1,"Description":"Volume properties"},"tags":{"Type":266,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"7":{"Name":"VolumeProperties","Discriminator":"kind","BaseProperties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":242,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."}},"Elements":{"azure.com.keyvault":243}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[235,236,237,238,239,240,241]}},{"2":{"Name":"AzureKeyVaultVolumeProperties","Properties":{"certificates":{"Type":256,"Flags":0,"Description":"The KeyVault certificates that this volume exposes"},"keys":{"Type":258,"Flags":0,"Description":"The KeyVault keys that this volume exposes"},"resource":{"Type":4,"Flags":1,"Description":"The ID of the keyvault to use for this volume resource"},"secrets":{"Type":264,"Flags":0,"Description":"The KeyVault secrets that this volume exposes"},"kind":{"Type":265,"Flags":1,"Description":"Discriminator property for VolumeProperties."}}}},{"2":{"Name":"CertificateObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"encoding":{"Type":248,"Flags":0,"Description":"Represents secret encodings"},"format":{"Type":251,"Flags":0,"Description":"Represents certificate formats"},"name":{"Type":4,"Flags":1,"Description":"The name of the certificate"},"certType":{"Type":255,"Flags":0,"Description":"Represents certificate types"},"version":{"Type":4,"Flags":0,"Description":"Certificate version"}}}},{"6":{"Value":"utf-8"}},{"6":{"Value":"hex"}},{"6":{"Value":"base64"}},{"5":{"Elements":[245,246,247]}},{"6":{"Value":"pem"}},{"6":{"Value":"pfx"}},{"5":{"Elements":[249,250]}},{"6":{"Value":"certificate"}},{"6":{"Value":"privatekey"}},{"6":{"Value":"publickey"}},{"5":{"Elements":[252,253,254]}},{"2":{"Name":"AzureKeyVaultVolumePropertiesCertificates","Properties":{},"AdditionalProperties":244}},{"2":{"Name":"KeyObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"name":{"Type":4,"Flags":1,"Description":"The name of the key"},"version":{"Type":4,"Flags":0,"Description":"Key version"}}}},{"2":{"Name":"AzureKeyVaultVolumePropertiesKeys","Properties":{},"AdditionalProperties":257}},{"2":{"Name":"SecretObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"encoding":{"Type":263,"Flags":0,"Description":"Represents secret encodings"},"name":{"Type":4,"Flags":1,"Description":"The name of the secret"},"version":{"Type":4,"Flags":0,"Description":"secret version"}}}},{"6":{"Value":"utf-8"}},{"6":{"Value":"hex"}},{"6":{"Value":"base64"}},{"5":{"Elements":[260,261,262]}},{"2":{"Name":"AzureKeyVaultVolumePropertiesSecrets","Properties":{},"AdditionalProperties":259}},{"6":{"Value":"azure.com.keyvault"}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/volumes@2023-10-01-preview","ScopeType":0,"Body":233}},{"8":{"Name":"listSecrets","ResourceType":"Applications.Core/extenders","ApiVersion":"2023-10-01-preview","Output":0,"Input":0}},{"2":{"Name":"SecretStoreListSecretsResult","Properties":{"type":{"Type":272,"Flags":2,"Description":"The type of SecretStore data"},"data":{"Type":273,"Flags":2,"Description":"An object to represent key-value type secrets"}}}},{"6":{"Value":"generic"}},{"6":{"Value":"certificate"}},{"5":{"Elements":[270,271]}},{"2":{"Name":"SecretStoreListSecretsResultData","Properties":{},"AdditionalProperties":223}},{"8":{"Name":"listSecrets","ResourceType":"Applications.Core/secretStores","ApiVersion":"2023-10-01-preview","Output":269,"Input":0}}] \ No newline at end of file diff --git a/pkg/corerp/api/v20231001preview/zz_generated_models.go b/pkg/corerp/api/v20231001preview/zz_generated_models.go index 78ea8a1e77..4d34dbd412 100644 --- a/pkg/corerp/api/v20231001preview/zz_generated_models.go +++ b/pkg/corerp/api/v20231001preview/zz_generated_models.go @@ -126,7 +126,7 @@ type ApplicationResourceUpdateProperties struct { // Cloud providers configuration for the environment. Providers *ProvidersUpdate - // Specifies recipe configurations needed for the recipes. + // Specifies configurations needed for the recipes registered to the environment. RecipeConfig *RecipeConfigProperties // Specifies Recipes linked to the Environment. @@ -138,7 +138,7 @@ type ApplicationResourceUpdateProperties struct { // AuthConfig - Specifies authentication information needed to use private terraform module repositories. type AuthConfig struct { - // Specifies authentication information needed to use private terraform module repositories from git module source + // Specifies authentication information needed to access private terraform modules from Git repository sources. Git *GitAuthConfig } @@ -562,7 +562,7 @@ type EnvironmentProperties struct { // Cloud providers configuration for the environment. Providers *Providers - // Specifies recipe configurations needed for the recipes. + // Specifies configurations needed for the recipes registered to the environment. RecipeConfig *RecipeConfigProperties // Specifies Recipes linked to the Environment. @@ -628,7 +628,7 @@ type EnvironmentResourceUpdateProperties struct { // Cloud providers configuration for the environment. Providers *ProvidersUpdate - // Specifies recipe configurations needed for the recipes. + // Specifies configurations needed for the recipes registered to the environment. RecipeConfig *RecipeConfigProperties // Specifies Recipes linked to the Environment. @@ -949,8 +949,7 @@ type GatewayTLS struct { SSLPassthrough *bool } -// GitAuthConfig - Specifies authentication information needed to use private terraform module repositories from git module -// source +// GitAuthConfig - Specifies authentication information needed to access private terraform modules from Git repository sources. type GitAuthConfig struct { // Specifies the secret details of type personal access token for each different git platforms Pat map[string]*Secret @@ -1526,7 +1525,7 @@ type RuntimesProperties struct { // Secret - Specifies the secret details of type personal access token for each different git platforms type Secret struct { - // The resource id for the secret store containing credentials + // The resource id for the Applications.Core/SecretStore resource containing credentials. SecretStore *string } @@ -1705,7 +1704,8 @@ func (t *TCPHealthProbeProperties) GetHealthProbeProperties() *HealthProbeProper // TerraformConfigProperties - Specifies the terraform config properties type TerraformConfigProperties struct { - // Specifies authentication information needed to use private terraform module repositories. + // Specifies authentication information needed to access private terraform module sources. Currently supported module sources: +// Git. Authentication *AuthConfig } diff --git a/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/openapi.json b/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/openapi.json index 05ee458132..d5ee2fb02d 100644 --- a/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/openapi.json +++ b/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/openapi.json @@ -2747,7 +2747,7 @@ }, "recipeConfig": { "$ref": "#/definitions/RecipeConfigProperties", - "description": "Specifies recipe configurations needed for the recipes." + "description": "Specifies configurations needed for the recipes registered to the environment." }, "extensions": { "type": "array", @@ -2765,7 +2765,7 @@ "properties": { "git": { "$ref": "#/definitions/GitAuthConfig", - "description": "Specifies authentication information needed to use private terraform module repositories from git module source" + "description": "Specifies authentication information needed to access private terraform modules from Git repository sources." } } }, @@ -3523,7 +3523,7 @@ }, "recipeConfig": { "$ref": "#/definitions/RecipeConfigProperties", - "description": "Specifies recipe configurations needed for the recipes." + "description": "Specifies configurations needed for the recipes registered to the environment." }, "extensions": { "type": "array", @@ -3627,7 +3627,7 @@ }, "recipeConfig": { "$ref": "#/definitions/RecipeConfigProperties", - "description": "Specifies recipe configurations needed for the recipes." + "description": "Specifies configurations needed for the recipes registered to the environment." }, "extensions": { "type": "array", @@ -4035,7 +4035,7 @@ }, "GitAuthConfig": { "type": "object", - "description": "Specifies authentication information needed to use private terraform module repositories from git module source", + "description": "Specifies authentication information needed to access private terraform modules from Git repository sources.", "properties": { "pat": { "type": "object", @@ -5017,7 +5017,7 @@ "properties": { "secretStore": { "type": "string", - "description": "The resource id for the secret store containing credentials" + "description": "The resource id for the Applications.Core/SecretStore resource containing credentials." } } }, @@ -5298,7 +5298,7 @@ "properties": { "authentication": { "$ref": "#/definitions/AuthConfig", - "description": "Specifies authentication information needed to use private terraform module repositories." + "description": "Specifies authentication information needed to access private terraform module sources. Currently supported module sources: Git." } } }, diff --git a/typespec/Applications.Core/environments.tsp b/typespec/Applications.Core/environments.tsp index 362d213975..1c6bb310ef 100644 --- a/typespec/Applications.Core/environments.tsp +++ b/typespec/Applications.Core/environments.tsp @@ -65,7 +65,7 @@ model EnvironmentProperties { @doc("Specifies Recipes linked to the Environment.") recipes?: Record>; - @doc("Specifies recipe configurations needed for the recipes.") + @doc("Specifies configurations needed for the recipes registered to the environment.") recipeConfig?: RecipeConfigProperties; @doc("The environment extension.") @@ -81,17 +81,17 @@ model RecipeConfigProperties { @doc("Specifies the terraform config properties") model TerraformConfigProperties{ - @doc("Specifies authentication information needed to use private terraform module repositories.") + @doc("Specifies authentication information needed to access private terraform module sources. Currently supported module sources: Git.") authentication?: AuthConfig; } @doc("Specifies authentication information needed to use private terraform module repositories.") model AuthConfig{ - @doc("Specifies authentication information needed to use private terraform module repositories from git module source") + @doc("Specifies authentication information needed to access private terraform modules from Git repository sources.") git?: GitAuthConfig; } -@doc("Specifies authentication information needed to use private terraform module repositories from git module source") +@doc("Specifies authentication information needed to access private terraform modules from Git repository sources.") model GitAuthConfig{ @doc("Specifies the secret details of type personal access token for each different git platforms") pat?: Record; @@ -99,7 +99,7 @@ model GitAuthConfig{ @doc("Specifies the secret details of type personal access token for each different git platforms") model Secret { - @doc("The resource id for the secret store containing credentials") + @doc("The resource id for the Applications.Core/SecretStore resource containing credentials.") secretStore?: string; } From e717d111d1380b733d3e1fdd1dd10c0adc16b504 Mon Sep 17 00:00:00 2001 From: Vishwanath Hiremath Date: Fri, 16 Feb 2024 12:06:56 -0800 Subject: [PATCH 07/22] changing the field name secretstore->secret Signed-off-by: Vishwanath Hiremath --- .../2023-10-01-preview/types.json | 2 +- .../api/v20231001preview/environment_conversion.go | 12 ++++++------ .../environment_conversion_test.go | 8 ++++---- .../api/v20231001preview/zz_generated_models.go | 8 ++++---- .../v20231001preview/zz_generated_models_serde.go | 14 +++++++------- pkg/corerp/datamodel/environment.go | 8 ++++---- pkg/recipes/configloader/environment_test.go | 12 ++++++------ pkg/recipes/terraform/config/config.go | 2 +- pkg/recipes/terraform/config/config_test.go | 6 +++--- .../preview/2023-10-01-preview/openapi.json | 6 +++--- typespec/Applications.Core/environments.tsp | 6 +++--- 11 files changed, 42 insertions(+), 42 deletions(-) diff --git a/hack/bicep-types-radius/generated/applications/applications.core/2023-10-01-preview/types.json b/hack/bicep-types-radius/generated/applications/applications.core/2023-10-01-preview/types.json index 79526ef0e1..42469b1345 100644 --- a/hack/bicep-types-radius/generated/applications/applications.core/2023-10-01-preview/types.json +++ b/hack/bicep-types-radius/generated/applications/applications.core/2023-10-01-preview/types.json @@ -1 +1 @@ -[{"1":{"Kind":1}},{"1":{"Kind":2}},{"1":{"Kind":3}},{"1":{"Kind":4}},{"1":{"Kind":5}},{"1":{"Kind":6}},{"1":{"Kind":7}},{"1":{"Kind":8}},{"6":{"Value":"Applications.Core/applications"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/applications","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":8,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":9,"Flags":10,"Description":"The resource api version"},"properties":{"Type":11,"Flags":1,"Description":"Application properties"},"tags":{"Type":46,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ApplicationProperties","Properties":{"provisioningState":{"Type":19,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"environment":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"extensions":{"Type":34,"Flags":0,"Description":"The application extension."},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[12,13,14,15,16,17,18]}},{"7":{"Name":"Extension","Discriminator":"kind","BaseProperties":{},"Elements":{"daprSidecar":21,"kubernetesMetadata":26,"kubernetesNamespace":30,"manualScaling":32}}},{"2":{"Name":"DaprSidecarExtension","Properties":{"appPort":{"Type":3,"Flags":0,"Description":"The Dapr appPort. Specifies the internal listening port for the application to handle requests from the Dapr sidecar."},"appId":{"Type":4,"Flags":1,"Description":"The Dapr appId. Specifies the identifier used by Dapr for service invocation."},"config":{"Type":4,"Flags":0,"Description":"Specifies the Dapr configuration to use for the resource."},"protocol":{"Type":24,"Flags":0,"Description":"The Dapr sidecar extension protocol"},"kind":{"Type":25,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"http"}},{"6":{"Value":"grpc"}},{"5":{"Elements":[22,23]}},{"6":{"Value":"daprSidecar"}},{"2":{"Name":"KubernetesMetadataExtension","Properties":{"annotations":{"Type":27,"Flags":0,"Description":"Annotations to be applied to the Kubernetes resources output by the resource"},"labels":{"Type":28,"Flags":0,"Description":"Labels to be applied to the Kubernetes resources output by the resource"},"kind":{"Type":29,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"2":{"Name":"KubernetesMetadataExtensionAnnotations","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"KubernetesMetadataExtensionLabels","Properties":{},"AdditionalProperties":4}},{"6":{"Value":"kubernetesMetadata"}},{"2":{"Name":"KubernetesNamespaceExtension","Properties":{"namespace":{"Type":4,"Flags":1,"Description":"The namespace of the application environment."},"kind":{"Type":31,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"kubernetesNamespace"}},{"2":{"Name":"ManualScalingExtension","Properties":{"replicas":{"Type":3,"Flags":1,"Description":"Replica count."},"kind":{"Type":33,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"manualScaling"}},{"3":{"ItemType":20}},{"2":{"Name":"ResourceStatus","Properties":{"compute":{"Type":36,"Flags":0,"Description":"Represents backing compute resource"},"recipe":{"Type":43,"Flags":2,"Description":"Recipe status at deployment time for a resource."},"outputResources":{"Type":45,"Flags":0,"Description":"Properties of an output resource"}}}},{"7":{"Name":"EnvironmentCompute","Discriminator":"kind","BaseProperties":{"resourceId":{"Type":4,"Flags":0,"Description":"The resource id of the compute resource for application environment."},"identity":{"Type":37,"Flags":0,"Description":"IdentitySettings is the external identity setting."}},"Elements":{"kubernetes":41}}},{"2":{"Name":"IdentitySettings","Properties":{"kind":{"Type":40,"Flags":1,"Description":"IdentitySettingKind is the kind of supported external identity setting"},"oidcIssuer":{"Type":4,"Flags":0,"Description":"The URI for your compute platform's OIDC issuer"},"resource":{"Type":4,"Flags":0,"Description":"The resource ID of the provisioned identity"}}}},{"6":{"Value":"undefined"}},{"6":{"Value":"azure.com.workload"}},{"5":{"Elements":[38,39]}},{"2":{"Name":"KubernetesCompute","Properties":{"namespace":{"Type":4,"Flags":1,"Description":"The namespace to use for the environment."},"kind":{"Type":42,"Flags":1,"Description":"Discriminator property for EnvironmentCompute."}}}},{"6":{"Value":"kubernetes"}},{"2":{"Name":"RecipeStatus","Properties":{"templateKind":{"Type":4,"Flags":1,"Description":"TemplateKind is the kind of the recipe template used by the portable resource upon deployment."},"templatePath":{"Type":4,"Flags":1,"Description":"TemplatePath is the path of the recipe consumed by the portable resource upon deployment."},"templateVersion":{"Type":4,"Flags":0,"Description":"TemplateVersion is the version number of the template."}}}},{"2":{"Name":"OutputResource","Properties":{"localId":{"Type":4,"Flags":0,"Description":"The logical identifier scoped to the owning Radius resource. This is only needed or used when a resource has a dependency relationship. LocalIDs do not have any particular format or meaning beyond being compared to determine dependency relationships."},"id":{"Type":4,"Flags":0,"Description":"The UCP resource ID of the underlying resource."},"radiusManaged":{"Type":2,"Flags":0,"Description":"Determines whether Radius manages the lifecycle of the underlying resource."}}}},{"3":{"ItemType":44}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"SystemData","Properties":{"createdBy":{"Type":4,"Flags":0,"Description":"The identity that created the resource."},"createdByType":{"Type":52,"Flags":0,"Description":"The type of identity that created the resource."},"createdAt":{"Type":4,"Flags":0,"Description":"The timestamp of resource creation (UTC)."},"lastModifiedBy":{"Type":4,"Flags":0,"Description":"The identity that last modified the resource."},"lastModifiedByType":{"Type":57,"Flags":0,"Description":"The type of identity that created the resource."},"lastModifiedAt":{"Type":4,"Flags":0,"Description":"The timestamp of resource last modification (UTC)"}}}},{"6":{"Value":"User"}},{"6":{"Value":"Application"}},{"6":{"Value":"ManagedIdentity"}},{"6":{"Value":"Key"}},{"5":{"Elements":[48,49,50,51]}},{"6":{"Value":"User"}},{"6":{"Value":"Application"}},{"6":{"Value":"ManagedIdentity"}},{"6":{"Value":"Key"}},{"5":{"Elements":[53,54,55,56]}},{"4":{"Name":"Applications.Core/applications@2023-10-01-preview","ScopeType":0,"Body":10}},{"6":{"Value":"Applications.Core/containers"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/containers","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":59,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":60,"Flags":10,"Description":"The resource api version"},"properties":{"Type":62,"Flags":1,"Description":"Container properties"},"tags":{"Type":122,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ContainerProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":70,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"container":{"Type":71,"Flags":1,"Description":"Definition of a container"},"connections":{"Type":108,"Flags":0,"Description":"Specifies a connection to another resource."},"identity":{"Type":37,"Flags":0,"Description":"IdentitySettings is the external identity setting."},"extensions":{"Type":109,"Flags":0,"Description":"Extensions spec of the resource"},"resourceProvisioning":{"Type":112,"Flags":0,"Description":"Specifies how the underlying service/resource is provisioned and managed. Available values are 'internal', where Radius manages the lifecycle of the resource internally, and 'manual', where a user manages the resource."},"resources":{"Type":114,"Flags":0,"Description":"A collection of references to resources associated with the container"},"restartPolicy":{"Type":118,"Flags":0,"Description":"Restart policy for the container"},"runtimes":{"Type":119,"Flags":0,"Description":"The properties for runtime configuration"}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[63,64,65,66,67,68,69]}},{"2":{"Name":"Container","Properties":{"image":{"Type":4,"Flags":1,"Description":"The registry and image to download and run in your container"},"imagePullPolicy":{"Type":75,"Flags":0,"Description":"The image pull policy for the container"},"env":{"Type":76,"Flags":0,"Description":"environment"},"ports":{"Type":81,"Flags":0,"Description":"container ports"},"readinessProbe":{"Type":82,"Flags":0,"Description":"Properties for readiness/liveness probe"},"livenessProbe":{"Type":82,"Flags":0,"Description":"Properties for readiness/liveness probe"},"volumes":{"Type":101,"Flags":0,"Description":"container volumes"},"command":{"Type":102,"Flags":0,"Description":"Entrypoint array. Overrides the container image's ENTRYPOINT"},"args":{"Type":103,"Flags":0,"Description":"Arguments to the entrypoint. Overrides the container image's CMD"},"workingDir":{"Type":4,"Flags":0,"Description":"Working directory for the container"}}}},{"6":{"Value":"Always"}},{"6":{"Value":"IfNotPresent"}},{"6":{"Value":"Never"}},{"5":{"Elements":[72,73,74]}},{"2":{"Name":"ContainerEnv","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"ContainerPortProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"protocol":{"Type":80,"Flags":0,"Description":"The protocol in use by the port"},"provides":{"Type":4,"Flags":0,"Description":"Specifies a route provided by this port"},"scheme":{"Type":4,"Flags":0,"Description":"Specifies the URL scheme of the communication protocol. Consumers can use the scheme to construct a URL. The value defaults to 'http' or 'https' depending on the port value"},"port":{"Type":3,"Flags":0,"Description":"Specifies the port that will be exposed by this container. Must be set when value different from containerPort is desired"}}}},{"6":{"Value":"TCP"}},{"6":{"Value":"UDP"}},{"5":{"Elements":[78,79]}},{"2":{"Name":"ContainerPorts","Properties":{},"AdditionalProperties":77}},{"7":{"Name":"HealthProbeProperties","Discriminator":"kind","BaseProperties":{"initialDelaySeconds":{"Type":3,"Flags":0,"Description":"Initial delay in seconds before probing for readiness/liveness"},"failureThreshold":{"Type":3,"Flags":0,"Description":"Threshold number of times the probe fails after which a failure would be reported"},"periodSeconds":{"Type":3,"Flags":0,"Description":"Interval for the readiness/liveness probe in seconds"},"timeoutSeconds":{"Type":3,"Flags":0,"Description":"Number of seconds after which the readiness/liveness probe times out. Defaults to 5 seconds"}},"Elements":{"exec":83,"httpGet":85,"tcp":88}}},{"2":{"Name":"ExecHealthProbeProperties","Properties":{"command":{"Type":4,"Flags":1,"Description":"Command to execute to probe readiness/liveness"},"kind":{"Type":84,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"6":{"Value":"exec"}},{"2":{"Name":"HttpGetHealthProbeProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"path":{"Type":4,"Flags":1,"Description":"The route to make the HTTP request on"},"headers":{"Type":86,"Flags":0,"Description":"Custom HTTP headers to add to the get request"},"kind":{"Type":87,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"2":{"Name":"HttpGetHealthProbePropertiesHeaders","Properties":{},"AdditionalProperties":4}},{"6":{"Value":"httpGet"}},{"2":{"Name":"TcpHealthProbeProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"kind":{"Type":89,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"6":{"Value":"tcp"}},{"7":{"Name":"Volume","Discriminator":"kind","BaseProperties":{"mountPath":{"Type":4,"Flags":0,"Description":"The path where the volume is mounted"}},"Elements":{"ephemeral":91,"persistent":96}}},{"2":{"Name":"EphemeralVolume","Properties":{"managedStore":{"Type":94,"Flags":1,"Description":"The managed store for the ephemeral volume"},"kind":{"Type":95,"Flags":1,"Description":"Discriminator property for Volume."}}}},{"6":{"Value":"memory"}},{"6":{"Value":"disk"}},{"5":{"Elements":[92,93]}},{"6":{"Value":"ephemeral"}},{"2":{"Name":"PersistentVolume","Properties":{"permission":{"Type":99,"Flags":0,"Description":"The persistent volume permission"},"source":{"Type":4,"Flags":1,"Description":"The source of the volume"},"kind":{"Type":100,"Flags":1,"Description":"Discriminator property for Volume."}}}},{"6":{"Value":"read"}},{"6":{"Value":"write"}},{"5":{"Elements":[97,98]}},{"6":{"Value":"persistent"}},{"2":{"Name":"ContainerVolumes","Properties":{},"AdditionalProperties":90}},{"3":{"ItemType":4}},{"3":{"ItemType":4}},{"2":{"Name":"ConnectionProperties","Properties":{"source":{"Type":4,"Flags":1,"Description":"The source of the connection"},"disableDefaultEnvVars":{"Type":2,"Flags":0,"Description":"default environment variable override"},"iam":{"Type":105,"Flags":0,"Description":"IAM properties"}}}},{"2":{"Name":"IamProperties","Properties":{"kind":{"Type":106,"Flags":1,"Description":"The kind of IAM provider to configure"},"roles":{"Type":107,"Flags":0,"Description":"RBAC permissions to be assigned on the source resource"}}}},{"6":{"Value":"azure"}},{"3":{"ItemType":4}},{"2":{"Name":"ContainerPropertiesConnections","Properties":{},"AdditionalProperties":104}},{"3":{"ItemType":20}},{"6":{"Value":"internal"}},{"6":{"Value":"manual"}},{"5":{"Elements":[110,111]}},{"2":{"Name":"ResourceReference","Properties":{"id":{"Type":4,"Flags":1,"Description":"Resource id of an existing resource"}}}},{"3":{"ItemType":113}},{"6":{"Value":"Always"}},{"6":{"Value":"OnFailure"}},{"6":{"Value":"Never"}},{"5":{"Elements":[115,116,117]}},{"2":{"Name":"RuntimesProperties","Properties":{"kubernetes":{"Type":120,"Flags":0,"Description":"The runtime configuration properties for Kubernetes"}}}},{"2":{"Name":"KubernetesRuntimeProperties","Properties":{"base":{"Type":4,"Flags":0,"Description":"The serialized YAML manifest which represents the base Kubernetes resources to deploy, such as Deployment, Service, ServiceAccount, Secrets, and ConfigMaps."},"pod":{"Type":121,"Flags":0,"Description":"A strategic merge patch that will be applied to the PodSpec object when this container is being deployed."}}}},{"2":{"Name":"KubernetesPodSpec","Properties":{},"AdditionalProperties":0}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/containers@2023-10-01-preview","ScopeType":0,"Body":61}},{"6":{"Value":"Applications.Core/environments"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/environments","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":124,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":125,"Flags":10,"Description":"The resource api version"},"properties":{"Type":127,"Flags":1,"Description":"Environment properties"},"tags":{"Type":153,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"EnvironmentProperties","Properties":{"provisioningState":{"Type":135,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"compute":{"Type":36,"Flags":1,"Description":"Represents backing compute resource"},"providers":{"Type":136,"Flags":0,"Description":"The Cloud providers configuration"},"simulated":{"Type":2,"Flags":0,"Description":"Simulated environment."},"recipes":{"Type":145,"Flags":0,"Description":"Specifies Recipes linked to the Environment."},"recipeConfig":{"Type":146,"Flags":0,"Description":"Specifies recipe configurations needed for the recipes."},"extensions":{"Type":152,"Flags":0,"Description":"The environment extension."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[128,129,130,131,132,133,134]}},{"2":{"Name":"Providers","Properties":{"azure":{"Type":137,"Flags":0,"Description":"The Azure cloud provider definition"},"aws":{"Type":138,"Flags":0,"Description":"The AWS cloud provider definition"}}}},{"2":{"Name":"ProvidersAzure","Properties":{"scope":{"Type":4,"Flags":1,"Description":"Target scope for Azure resources to be deployed into. For example: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup'"}}}},{"2":{"Name":"ProvidersAws","Properties":{"scope":{"Type":4,"Flags":1,"Description":"Target scope for AWS resources to be deployed into. For example: '/planes/aws/aws/accounts/000000000000/regions/us-west-2'"}}}},{"7":{"Name":"RecipeProperties","Discriminator":"templateKind","BaseProperties":{"templatePath":{"Type":4,"Flags":1,"Description":"Path to the template provided by the recipe. Currently only link to Azure Container Registry is supported."},"parameters":{"Type":0,"Flags":0,"Description":"Any object"}},"Elements":{"bicep":140,"terraform":142}}},{"2":{"Name":"BicepRecipeProperties","Properties":{"plainHttp":{"Type":2,"Flags":0,"Description":"Connect to the Bicep registry using HTTP (not-HTTPS). This should be used when the registry is known not to support HTTPS, for example in a locally-hosted registry. Defaults to false (use HTTPS/TLS)."},"templateKind":{"Type":141,"Flags":1,"Description":"Discriminator property for RecipeProperties."}}}},{"6":{"Value":"bicep"}},{"2":{"Name":"TerraformRecipeProperties","Properties":{"templateVersion":{"Type":4,"Flags":0,"Description":"Version of the template to deploy. For Terraform recipes using a module registry this is required, but must be omitted for other module sources."},"templateKind":{"Type":143,"Flags":1,"Description":"Discriminator property for RecipeProperties."}}}},{"6":{"Value":"terraform"}},{"2":{"Name":"DictionaryOfRecipeProperties","Properties":{},"AdditionalProperties":139}},{"2":{"Name":"EnvironmentPropertiesRecipes","Properties":{},"AdditionalProperties":144}},{"2":{"Name":"RecipeConfigProperties","Properties":{"terraform":{"Type":147,"Flags":0,"Description":"Specifies the terraform config properties"}}}},{"2":{"Name":"TerraformConfigProperties","Properties":{"authentication":{"Type":148,"Flags":0,"Description":"Specifies authentication information needed to use private terraform module repositories."}}}},{"2":{"Name":"AuthConfig","Properties":{"git":{"Type":149,"Flags":0,"Description":"Specifies authentication information needed to access private terraform modules from Git repository sources."}}}},{"2":{"Name":"GitAuthConfig","Properties":{"pat":{"Type":151,"Flags":0,"Description":"Specifies the secret details of type personal access token for each different git platforms"}}}},{"2":{"Name":"Secret","Properties":{"secretStore":{"Type":4,"Flags":0,"Description":"The resource id for the Applications.Core/SecretStore resource containing credentials."}}}},{"2":{"Name":"GitAuthConfigPat","Properties":{},"AdditionalProperties":150}},{"3":{"ItemType":20}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/environments@2023-10-01-preview","ScopeType":0,"Body":126}},{"6":{"Value":"Applications.Core/extenders"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/extenders","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":155,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":156,"Flags":10,"Description":"The resource api version"},"properties":{"Type":158,"Flags":1,"Description":"ExtenderResource portable resource properties"},"tags":{"Type":171,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ExtenderProperties","Properties":{"environment":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the environment that the portable resource is linked to"},"application":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the application that the portable resource is consumed by (if applicable)"},"provisioningState":{"Type":166,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"secrets":{"Type":0,"Flags":0,"Description":"Any object"},"recipe":{"Type":167,"Flags":0,"Description":"The recipe used to automatically deploy underlying infrastructure for a portable resource"},"resourceProvisioning":{"Type":170,"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."}},"AdditionalProperties":0}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[159,160,161,162,163,164,165]}},{"2":{"Name":"Recipe","Properties":{"name":{"Type":4,"Flags":1,"Description":"The name of the recipe within the environment to use"},"parameters":{"Type":0,"Flags":0,"Description":"Any object"}}}},{"6":{"Value":"recipe"}},{"6":{"Value":"manual"}},{"5":{"Elements":[168,169]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/extenders@2023-10-01-preview","ScopeType":0,"Body":157}},{"6":{"Value":"Applications.Core/gateways"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/gateways","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":173,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":174,"Flags":10,"Description":"The resource api version"},"properties":{"Type":176,"Flags":1,"Description":"Gateway properties"},"tags":{"Type":192,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"GatewayProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":184,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"internal":{"Type":2,"Flags":0,"Description":"Sets Gateway to not be exposed externally (no public IP address associated). Defaults to false (exposed to internet)."},"hostname":{"Type":185,"Flags":0,"Description":"Declare hostname information for the Gateway. Leaving the hostname empty auto-assigns one: mygateway.myapp.PUBLICHOSTNAMEORIP.nip.io."},"routes":{"Type":187,"Flags":1,"Description":"Routes attached to this Gateway"},"tls":{"Type":188,"Flags":0,"Description":"TLS configuration definition for Gateway resource."},"url":{"Type":4,"Flags":2,"Description":"URL of the gateway resource. Readonly"}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[177,178,179,180,181,182,183]}},{"2":{"Name":"GatewayHostname","Properties":{"prefix":{"Type":4,"Flags":0,"Description":"Specify a prefix for the hostname: myhostname.myapp.PUBLICHOSTNAMEORIP.nip.io. Mutually exclusive with 'fullyQualifiedHostname' and will be overridden if both are defined."},"fullyQualifiedHostname":{"Type":4,"Flags":0,"Description":"Specify a fully-qualified domain name: myapp.mydomain.com. Mutually exclusive with 'prefix' and will take priority if both are defined."}}}},{"2":{"Name":"GatewayRoute","Properties":{"path":{"Type":4,"Flags":0,"Description":"The path to match the incoming request path on. Ex - /myservice."},"destination":{"Type":4,"Flags":0,"Description":"The HttpRoute to route to. Ex - myserviceroute.id."},"replacePrefix":{"Type":4,"Flags":0,"Description":"Optionally update the prefix when sending the request to the service. Ex - replacePrefix: '/' and path: '/myservice' will transform '/myservice/myroute' to '/myroute'"}}}},{"3":{"ItemType":186}},{"2":{"Name":"GatewayTls","Properties":{"sslPassthrough":{"Type":2,"Flags":0,"Description":"If true, gateway lets the https traffic sslPassthrough to the backend servers for decryption."},"minimumProtocolVersion":{"Type":191,"Flags":0,"Description":"Tls Minimum versions for Gateway resource."},"certificateFrom":{"Type":4,"Flags":0,"Description":"The resource id for the secret containing the TLS certificate and key for the gateway."}}}},{"6":{"Value":"1.2"}},{"6":{"Value":"1.3"}},{"5":{"Elements":[189,190]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/gateways@2023-10-01-preview","ScopeType":0,"Body":175}},{"6":{"Value":"Applications.Core/httpRoutes"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/httpRoutes","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":194,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":195,"Flags":10,"Description":"The resource api version"},"properties":{"Type":197,"Flags":1,"Description":"HTTPRoute properties"},"tags":{"Type":206,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"HttpRouteProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":205,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"hostname":{"Type":4,"Flags":0,"Description":"The internal hostname accepting traffic for the HTTP Route. Readonly."},"port":{"Type":3,"Flags":0,"Description":"The port number for the HTTP Route. Defaults to 80. Readonly."},"scheme":{"Type":4,"Flags":2,"Description":"The scheme used for traffic. Readonly."},"url":{"Type":4,"Flags":2,"Description":"A stable URL that that can be used to route traffic to a resource. Readonly."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[198,199,200,201,202,203,204]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/httpRoutes@2023-10-01-preview","ScopeType":0,"Body":196}},{"6":{"Value":"Applications.Core/secretStores"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/secretStores","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":208,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":209,"Flags":10,"Description":"The resource api version"},"properties":{"Type":211,"Flags":1,"Description":"The properties of SecretStore"},"tags":{"Type":229,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"SecretStoreProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":219,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"type":{"Type":222,"Flags":0,"Description":"The type of SecretStore data"},"data":{"Type":228,"Flags":1,"Description":"An object to represent key-value type secrets"},"resource":{"Type":4,"Flags":0,"Description":"The resource id of external secret store."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[212,213,214,215,216,217,218]}},{"6":{"Value":"generic"}},{"6":{"Value":"certificate"}},{"5":{"Elements":[220,221]}},{"2":{"Name":"SecretValueProperties","Properties":{"encoding":{"Type":226,"Flags":0,"Description":"The type of SecretValue Encoding"},"value":{"Type":4,"Flags":0,"Description":"The value of secret."},"valueFrom":{"Type":227,"Flags":0,"Description":"The Secret value source properties"}}}},{"6":{"Value":"raw"}},{"6":{"Value":"base64"}},{"5":{"Elements":[224,225]}},{"2":{"Name":"ValueFromProperties","Properties":{"name":{"Type":4,"Flags":1,"Description":"The name of the referenced secret."},"version":{"Type":4,"Flags":0,"Description":"The version of the referenced secret."}}}},{"2":{"Name":"SecretStorePropertiesData","Properties":{},"AdditionalProperties":223}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/secretStores@2023-10-01-preview","ScopeType":0,"Body":210}},{"6":{"Value":"Applications.Core/volumes"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/volumes","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":231,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":232,"Flags":10,"Description":"The resource api version"},"properties":{"Type":234,"Flags":1,"Description":"Volume properties"},"tags":{"Type":266,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"7":{"Name":"VolumeProperties","Discriminator":"kind","BaseProperties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":242,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."}},"Elements":{"azure.com.keyvault":243}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[235,236,237,238,239,240,241]}},{"2":{"Name":"AzureKeyVaultVolumeProperties","Properties":{"certificates":{"Type":256,"Flags":0,"Description":"The KeyVault certificates that this volume exposes"},"keys":{"Type":258,"Flags":0,"Description":"The KeyVault keys that this volume exposes"},"resource":{"Type":4,"Flags":1,"Description":"The ID of the keyvault to use for this volume resource"},"secrets":{"Type":264,"Flags":0,"Description":"The KeyVault secrets that this volume exposes"},"kind":{"Type":265,"Flags":1,"Description":"Discriminator property for VolumeProperties."}}}},{"2":{"Name":"CertificateObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"encoding":{"Type":248,"Flags":0,"Description":"Represents secret encodings"},"format":{"Type":251,"Flags":0,"Description":"Represents certificate formats"},"name":{"Type":4,"Flags":1,"Description":"The name of the certificate"},"certType":{"Type":255,"Flags":0,"Description":"Represents certificate types"},"version":{"Type":4,"Flags":0,"Description":"Certificate version"}}}},{"6":{"Value":"utf-8"}},{"6":{"Value":"hex"}},{"6":{"Value":"base64"}},{"5":{"Elements":[245,246,247]}},{"6":{"Value":"pem"}},{"6":{"Value":"pfx"}},{"5":{"Elements":[249,250]}},{"6":{"Value":"certificate"}},{"6":{"Value":"privatekey"}},{"6":{"Value":"publickey"}},{"5":{"Elements":[252,253,254]}},{"2":{"Name":"AzureKeyVaultVolumePropertiesCertificates","Properties":{},"AdditionalProperties":244}},{"2":{"Name":"KeyObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"name":{"Type":4,"Flags":1,"Description":"The name of the key"},"version":{"Type":4,"Flags":0,"Description":"Key version"}}}},{"2":{"Name":"AzureKeyVaultVolumePropertiesKeys","Properties":{},"AdditionalProperties":257}},{"2":{"Name":"SecretObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"encoding":{"Type":263,"Flags":0,"Description":"Represents secret encodings"},"name":{"Type":4,"Flags":1,"Description":"The name of the secret"},"version":{"Type":4,"Flags":0,"Description":"secret version"}}}},{"6":{"Value":"utf-8"}},{"6":{"Value":"hex"}},{"6":{"Value":"base64"}},{"5":{"Elements":[260,261,262]}},{"2":{"Name":"AzureKeyVaultVolumePropertiesSecrets","Properties":{},"AdditionalProperties":259}},{"6":{"Value":"azure.com.keyvault"}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/volumes@2023-10-01-preview","ScopeType":0,"Body":233}},{"8":{"Name":"listSecrets","ResourceType":"Applications.Core/extenders","ApiVersion":"2023-10-01-preview","Output":0,"Input":0}},{"2":{"Name":"SecretStoreListSecretsResult","Properties":{"type":{"Type":272,"Flags":2,"Description":"The type of SecretStore data"},"data":{"Type":273,"Flags":2,"Description":"An object to represent key-value type secrets"}}}},{"6":{"Value":"generic"}},{"6":{"Value":"certificate"}},{"5":{"Elements":[270,271]}},{"2":{"Name":"SecretStoreListSecretsResultData","Properties":{},"AdditionalProperties":223}},{"8":{"Name":"listSecrets","ResourceType":"Applications.Core/secretStores","ApiVersion":"2023-10-01-preview","Output":269,"Input":0}}] \ No newline at end of file +[{"1":{"Kind":1}},{"1":{"Kind":2}},{"1":{"Kind":3}},{"1":{"Kind":4}},{"1":{"Kind":5}},{"1":{"Kind":6}},{"1":{"Kind":7}},{"1":{"Kind":8}},{"6":{"Value":"Applications.Core/applications"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/applications","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":8,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":9,"Flags":10,"Description":"The resource api version"},"properties":{"Type":11,"Flags":1,"Description":"Application properties"},"tags":{"Type":46,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ApplicationProperties","Properties":{"provisioningState":{"Type":19,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"environment":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"extensions":{"Type":34,"Flags":0,"Description":"The application extension."},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[12,13,14,15,16,17,18]}},{"7":{"Name":"Extension","Discriminator":"kind","BaseProperties":{},"Elements":{"daprSidecar":21,"kubernetesMetadata":26,"kubernetesNamespace":30,"manualScaling":32}}},{"2":{"Name":"DaprSidecarExtension","Properties":{"appPort":{"Type":3,"Flags":0,"Description":"The Dapr appPort. Specifies the internal listening port for the application to handle requests from the Dapr sidecar."},"appId":{"Type":4,"Flags":1,"Description":"The Dapr appId. Specifies the identifier used by Dapr for service invocation."},"config":{"Type":4,"Flags":0,"Description":"Specifies the Dapr configuration to use for the resource."},"protocol":{"Type":24,"Flags":0,"Description":"The Dapr sidecar extension protocol"},"kind":{"Type":25,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"http"}},{"6":{"Value":"grpc"}},{"5":{"Elements":[22,23]}},{"6":{"Value":"daprSidecar"}},{"2":{"Name":"KubernetesMetadataExtension","Properties":{"annotations":{"Type":27,"Flags":0,"Description":"Annotations to be applied to the Kubernetes resources output by the resource"},"labels":{"Type":28,"Flags":0,"Description":"Labels to be applied to the Kubernetes resources output by the resource"},"kind":{"Type":29,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"2":{"Name":"KubernetesMetadataExtensionAnnotations","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"KubernetesMetadataExtensionLabels","Properties":{},"AdditionalProperties":4}},{"6":{"Value":"kubernetesMetadata"}},{"2":{"Name":"KubernetesNamespaceExtension","Properties":{"namespace":{"Type":4,"Flags":1,"Description":"The namespace of the application environment."},"kind":{"Type":31,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"kubernetesNamespace"}},{"2":{"Name":"ManualScalingExtension","Properties":{"replicas":{"Type":3,"Flags":1,"Description":"Replica count."},"kind":{"Type":33,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"manualScaling"}},{"3":{"ItemType":20}},{"2":{"Name":"ResourceStatus","Properties":{"compute":{"Type":36,"Flags":0,"Description":"Represents backing compute resource"},"recipe":{"Type":43,"Flags":2,"Description":"Recipe status at deployment time for a resource."},"outputResources":{"Type":45,"Flags":0,"Description":"Properties of an output resource"}}}},{"7":{"Name":"EnvironmentCompute","Discriminator":"kind","BaseProperties":{"resourceId":{"Type":4,"Flags":0,"Description":"The resource id of the compute resource for application environment."},"identity":{"Type":37,"Flags":0,"Description":"IdentitySettings is the external identity setting."}},"Elements":{"kubernetes":41}}},{"2":{"Name":"IdentitySettings","Properties":{"kind":{"Type":40,"Flags":1,"Description":"IdentitySettingKind is the kind of supported external identity setting"},"oidcIssuer":{"Type":4,"Flags":0,"Description":"The URI for your compute platform's OIDC issuer"},"resource":{"Type":4,"Flags":0,"Description":"The resource ID of the provisioned identity"}}}},{"6":{"Value":"undefined"}},{"6":{"Value":"azure.com.workload"}},{"5":{"Elements":[38,39]}},{"2":{"Name":"KubernetesCompute","Properties":{"namespace":{"Type":4,"Flags":1,"Description":"The namespace to use for the environment."},"kind":{"Type":42,"Flags":1,"Description":"Discriminator property for EnvironmentCompute."}}}},{"6":{"Value":"kubernetes"}},{"2":{"Name":"RecipeStatus","Properties":{"templateKind":{"Type":4,"Flags":1,"Description":"TemplateKind is the kind of the recipe template used by the portable resource upon deployment."},"templatePath":{"Type":4,"Flags":1,"Description":"TemplatePath is the path of the recipe consumed by the portable resource upon deployment."},"templateVersion":{"Type":4,"Flags":0,"Description":"TemplateVersion is the version number of the template."}}}},{"2":{"Name":"OutputResource","Properties":{"localId":{"Type":4,"Flags":0,"Description":"The logical identifier scoped to the owning Radius resource. This is only needed or used when a resource has a dependency relationship. LocalIDs do not have any particular format or meaning beyond being compared to determine dependency relationships."},"id":{"Type":4,"Flags":0,"Description":"The UCP resource ID of the underlying resource."},"radiusManaged":{"Type":2,"Flags":0,"Description":"Determines whether Radius manages the lifecycle of the underlying resource."}}}},{"3":{"ItemType":44}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"SystemData","Properties":{"createdBy":{"Type":4,"Flags":0,"Description":"The identity that created the resource."},"createdByType":{"Type":52,"Flags":0,"Description":"The type of identity that created the resource."},"createdAt":{"Type":4,"Flags":0,"Description":"The timestamp of resource creation (UTC)."},"lastModifiedBy":{"Type":4,"Flags":0,"Description":"The identity that last modified the resource."},"lastModifiedByType":{"Type":57,"Flags":0,"Description":"The type of identity that created the resource."},"lastModifiedAt":{"Type":4,"Flags":0,"Description":"The timestamp of resource last modification (UTC)"}}}},{"6":{"Value":"User"}},{"6":{"Value":"Application"}},{"6":{"Value":"ManagedIdentity"}},{"6":{"Value":"Key"}},{"5":{"Elements":[48,49,50,51]}},{"6":{"Value":"User"}},{"6":{"Value":"Application"}},{"6":{"Value":"ManagedIdentity"}},{"6":{"Value":"Key"}},{"5":{"Elements":[53,54,55,56]}},{"4":{"Name":"Applications.Core/applications@2023-10-01-preview","ScopeType":0,"Body":10}},{"6":{"Value":"Applications.Core/containers"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/containers","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":59,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":60,"Flags":10,"Description":"The resource api version"},"properties":{"Type":62,"Flags":1,"Description":"Container properties"},"tags":{"Type":122,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ContainerProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":70,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"container":{"Type":71,"Flags":1,"Description":"Definition of a container"},"connections":{"Type":108,"Flags":0,"Description":"Specifies a connection to another resource."},"identity":{"Type":37,"Flags":0,"Description":"IdentitySettings is the external identity setting."},"extensions":{"Type":109,"Flags":0,"Description":"Extensions spec of the resource"},"resourceProvisioning":{"Type":112,"Flags":0,"Description":"Specifies how the underlying service/resource is provisioned and managed. Available values are 'internal', where Radius manages the lifecycle of the resource internally, and 'manual', where a user manages the resource."},"resources":{"Type":114,"Flags":0,"Description":"A collection of references to resources associated with the container"},"restartPolicy":{"Type":118,"Flags":0,"Description":"Restart policy for the container"},"runtimes":{"Type":119,"Flags":0,"Description":"The properties for runtime configuration"}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[63,64,65,66,67,68,69]}},{"2":{"Name":"Container","Properties":{"image":{"Type":4,"Flags":1,"Description":"The registry and image to download and run in your container"},"imagePullPolicy":{"Type":75,"Flags":0,"Description":"The image pull policy for the container"},"env":{"Type":76,"Flags":0,"Description":"environment"},"ports":{"Type":81,"Flags":0,"Description":"container ports"},"readinessProbe":{"Type":82,"Flags":0,"Description":"Properties for readiness/liveness probe"},"livenessProbe":{"Type":82,"Flags":0,"Description":"Properties for readiness/liveness probe"},"volumes":{"Type":101,"Flags":0,"Description":"container volumes"},"command":{"Type":102,"Flags":0,"Description":"Entrypoint array. Overrides the container image's ENTRYPOINT"},"args":{"Type":103,"Flags":0,"Description":"Arguments to the entrypoint. Overrides the container image's CMD"},"workingDir":{"Type":4,"Flags":0,"Description":"Working directory for the container"}}}},{"6":{"Value":"Always"}},{"6":{"Value":"IfNotPresent"}},{"6":{"Value":"Never"}},{"5":{"Elements":[72,73,74]}},{"2":{"Name":"ContainerEnv","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"ContainerPortProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"protocol":{"Type":80,"Flags":0,"Description":"The protocol in use by the port"},"provides":{"Type":4,"Flags":0,"Description":"Specifies a route provided by this port"},"scheme":{"Type":4,"Flags":0,"Description":"Specifies the URL scheme of the communication protocol. Consumers can use the scheme to construct a URL. The value defaults to 'http' or 'https' depending on the port value"},"port":{"Type":3,"Flags":0,"Description":"Specifies the port that will be exposed by this container. Must be set when value different from containerPort is desired"}}}},{"6":{"Value":"TCP"}},{"6":{"Value":"UDP"}},{"5":{"Elements":[78,79]}},{"2":{"Name":"ContainerPorts","Properties":{},"AdditionalProperties":77}},{"7":{"Name":"HealthProbeProperties","Discriminator":"kind","BaseProperties":{"initialDelaySeconds":{"Type":3,"Flags":0,"Description":"Initial delay in seconds before probing for readiness/liveness"},"failureThreshold":{"Type":3,"Flags":0,"Description":"Threshold number of times the probe fails after which a failure would be reported"},"periodSeconds":{"Type":3,"Flags":0,"Description":"Interval for the readiness/liveness probe in seconds"},"timeoutSeconds":{"Type":3,"Flags":0,"Description":"Number of seconds after which the readiness/liveness probe times out. Defaults to 5 seconds"}},"Elements":{"exec":83,"httpGet":85,"tcp":88}}},{"2":{"Name":"ExecHealthProbeProperties","Properties":{"command":{"Type":4,"Flags":1,"Description":"Command to execute to probe readiness/liveness"},"kind":{"Type":84,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"6":{"Value":"exec"}},{"2":{"Name":"HttpGetHealthProbeProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"path":{"Type":4,"Flags":1,"Description":"The route to make the HTTP request on"},"headers":{"Type":86,"Flags":0,"Description":"Custom HTTP headers to add to the get request"},"kind":{"Type":87,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"2":{"Name":"HttpGetHealthProbePropertiesHeaders","Properties":{},"AdditionalProperties":4}},{"6":{"Value":"httpGet"}},{"2":{"Name":"TcpHealthProbeProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"kind":{"Type":89,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"6":{"Value":"tcp"}},{"7":{"Name":"Volume","Discriminator":"kind","BaseProperties":{"mountPath":{"Type":4,"Flags":0,"Description":"The path where the volume is mounted"}},"Elements":{"ephemeral":91,"persistent":96}}},{"2":{"Name":"EphemeralVolume","Properties":{"managedStore":{"Type":94,"Flags":1,"Description":"The managed store for the ephemeral volume"},"kind":{"Type":95,"Flags":1,"Description":"Discriminator property for Volume."}}}},{"6":{"Value":"memory"}},{"6":{"Value":"disk"}},{"5":{"Elements":[92,93]}},{"6":{"Value":"ephemeral"}},{"2":{"Name":"PersistentVolume","Properties":{"permission":{"Type":99,"Flags":0,"Description":"The persistent volume permission"},"source":{"Type":4,"Flags":1,"Description":"The source of the volume"},"kind":{"Type":100,"Flags":1,"Description":"Discriminator property for Volume."}}}},{"6":{"Value":"read"}},{"6":{"Value":"write"}},{"5":{"Elements":[97,98]}},{"6":{"Value":"persistent"}},{"2":{"Name":"ContainerVolumes","Properties":{},"AdditionalProperties":90}},{"3":{"ItemType":4}},{"3":{"ItemType":4}},{"2":{"Name":"ConnectionProperties","Properties":{"source":{"Type":4,"Flags":1,"Description":"The source of the connection"},"disableDefaultEnvVars":{"Type":2,"Flags":0,"Description":"default environment variable override"},"iam":{"Type":105,"Flags":0,"Description":"IAM properties"}}}},{"2":{"Name":"IamProperties","Properties":{"kind":{"Type":106,"Flags":1,"Description":"The kind of IAM provider to configure"},"roles":{"Type":107,"Flags":0,"Description":"RBAC permissions to be assigned on the source resource"}}}},{"6":{"Value":"azure"}},{"3":{"ItemType":4}},{"2":{"Name":"ContainerPropertiesConnections","Properties":{},"AdditionalProperties":104}},{"3":{"ItemType":20}},{"6":{"Value":"internal"}},{"6":{"Value":"manual"}},{"5":{"Elements":[110,111]}},{"2":{"Name":"ResourceReference","Properties":{"id":{"Type":4,"Flags":1,"Description":"Resource id of an existing resource"}}}},{"3":{"ItemType":113}},{"6":{"Value":"Always"}},{"6":{"Value":"OnFailure"}},{"6":{"Value":"Never"}},{"5":{"Elements":[115,116,117]}},{"2":{"Name":"RuntimesProperties","Properties":{"kubernetes":{"Type":120,"Flags":0,"Description":"The runtime configuration properties for Kubernetes"}}}},{"2":{"Name":"KubernetesRuntimeProperties","Properties":{"base":{"Type":4,"Flags":0,"Description":"The serialized YAML manifest which represents the base Kubernetes resources to deploy, such as Deployment, Service, ServiceAccount, Secrets, and ConfigMaps."},"pod":{"Type":121,"Flags":0,"Description":"A strategic merge patch that will be applied to the PodSpec object when this container is being deployed."}}}},{"2":{"Name":"KubernetesPodSpec","Properties":{},"AdditionalProperties":0}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/containers@2023-10-01-preview","ScopeType":0,"Body":61}},{"6":{"Value":"Applications.Core/environments"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/environments","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":124,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":125,"Flags":10,"Description":"The resource api version"},"properties":{"Type":127,"Flags":1,"Description":"Environment properties"},"tags":{"Type":153,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"EnvironmentProperties","Properties":{"provisioningState":{"Type":135,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"compute":{"Type":36,"Flags":1,"Description":"Represents backing compute resource"},"providers":{"Type":136,"Flags":0,"Description":"The Cloud providers configuration"},"simulated":{"Type":2,"Flags":0,"Description":"Simulated environment."},"recipes":{"Type":145,"Flags":0,"Description":"Specifies Recipes linked to the Environment."},"recipeConfig":{"Type":146,"Flags":0,"Description":"Specifies recipe configurations needed for the recipes."},"extensions":{"Type":152,"Flags":0,"Description":"The environment extension."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[128,129,130,131,132,133,134]}},{"2":{"Name":"Providers","Properties":{"azure":{"Type":137,"Flags":0,"Description":"The Azure cloud provider definition"},"aws":{"Type":138,"Flags":0,"Description":"The AWS cloud provider definition"}}}},{"2":{"Name":"ProvidersAzure","Properties":{"scope":{"Type":4,"Flags":1,"Description":"Target scope for Azure resources to be deployed into. For example: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup'"}}}},{"2":{"Name":"ProvidersAws","Properties":{"scope":{"Type":4,"Flags":1,"Description":"Target scope for AWS resources to be deployed into. For example: '/planes/aws/aws/accounts/000000000000/regions/us-west-2'"}}}},{"7":{"Name":"RecipeProperties","Discriminator":"templateKind","BaseProperties":{"templatePath":{"Type":4,"Flags":1,"Description":"Path to the template provided by the recipe. Currently only link to Azure Container Registry is supported."},"parameters":{"Type":0,"Flags":0,"Description":"Any object"}},"Elements":{"bicep":140,"terraform":142}}},{"2":{"Name":"BicepRecipeProperties","Properties":{"plainHttp":{"Type":2,"Flags":0,"Description":"Connect to the Bicep registry using HTTP (not-HTTPS). This should be used when the registry is known not to support HTTPS, for example in a locally-hosted registry. Defaults to false (use HTTPS/TLS)."},"templateKind":{"Type":141,"Flags":1,"Description":"Discriminator property for RecipeProperties."}}}},{"6":{"Value":"bicep"}},{"2":{"Name":"TerraformRecipeProperties","Properties":{"templateVersion":{"Type":4,"Flags":0,"Description":"Version of the template to deploy. For Terraform recipes using a module registry this is required, but must be omitted for other module sources."},"templateKind":{"Type":143,"Flags":1,"Description":"Discriminator property for RecipeProperties."}}}},{"6":{"Value":"terraform"}},{"2":{"Name":"DictionaryOfRecipeProperties","Properties":{},"AdditionalProperties":139}},{"2":{"Name":"EnvironmentPropertiesRecipes","Properties":{},"AdditionalProperties":144}},{"2":{"Name":"RecipeConfigProperties","Properties":{"terraform":{"Type":147,"Flags":0,"Description":"Specifies the terraform config properties"}}}},{"2":{"Name":"TerraformConfigProperties","Properties":{"authentication":{"Type":148,"Flags":0,"Description":"Specifies authentication information needed to use private terraform module repositories."}}}},{"2":{"Name":"AuthConfig","Properties":{"git":{"Type":149,"Flags":0,"Description":"Specifies authentication information needed to access private terraform modules from Git repository sources."}}}},{"2":{"Name":"GitAuthConfig","Properties":{"pat":{"Type":151,"Flags":0,"Description":"Specifies the secret details of type personal access token for each different git platforms"}}}},{"2":{"Name":"SecretConfig","Properties":{"secret":{"Type":4,"Flags":0,"Description":"The resource id for the Applications.Core/SecretStore resource containing credentials."}}}},{"2":{"Name":"GitAuthConfigPat","Properties":{},"AdditionalProperties":150}},{"3":{"ItemType":20}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/environments@2023-10-01-preview","ScopeType":0,"Body":126}},{"6":{"Value":"Applications.Core/extenders"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/extenders","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":155,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":156,"Flags":10,"Description":"The resource api version"},"properties":{"Type":158,"Flags":1,"Description":"ExtenderResource portable resource properties"},"tags":{"Type":171,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ExtenderProperties","Properties":{"environment":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the environment that the portable resource is linked to"},"application":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the application that the portable resource is consumed by (if applicable)"},"provisioningState":{"Type":166,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"secrets":{"Type":0,"Flags":0,"Description":"Any object"},"recipe":{"Type":167,"Flags":0,"Description":"The recipe used to automatically deploy underlying infrastructure for a portable resource"},"resourceProvisioning":{"Type":170,"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."}},"AdditionalProperties":0}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[159,160,161,162,163,164,165]}},{"2":{"Name":"Recipe","Properties":{"name":{"Type":4,"Flags":1,"Description":"The name of the recipe within the environment to use"},"parameters":{"Type":0,"Flags":0,"Description":"Any object"}}}},{"6":{"Value":"recipe"}},{"6":{"Value":"manual"}},{"5":{"Elements":[168,169]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/extenders@2023-10-01-preview","ScopeType":0,"Body":157}},{"6":{"Value":"Applications.Core/gateways"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/gateways","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":173,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":174,"Flags":10,"Description":"The resource api version"},"properties":{"Type":176,"Flags":1,"Description":"Gateway properties"},"tags":{"Type":192,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"GatewayProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":184,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"internal":{"Type":2,"Flags":0,"Description":"Sets Gateway to not be exposed externally (no public IP address associated). Defaults to false (exposed to internet)."},"hostname":{"Type":185,"Flags":0,"Description":"Declare hostname information for the Gateway. Leaving the hostname empty auto-assigns one: mygateway.myapp.PUBLICHOSTNAMEORIP.nip.io."},"routes":{"Type":187,"Flags":1,"Description":"Routes attached to this Gateway"},"tls":{"Type":188,"Flags":0,"Description":"TLS configuration definition for Gateway resource."},"url":{"Type":4,"Flags":2,"Description":"URL of the gateway resource. Readonly"}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[177,178,179,180,181,182,183]}},{"2":{"Name":"GatewayHostname","Properties":{"prefix":{"Type":4,"Flags":0,"Description":"Specify a prefix for the hostname: myhostname.myapp.PUBLICHOSTNAMEORIP.nip.io. Mutually exclusive with 'fullyQualifiedHostname' and will be overridden if both are defined."},"fullyQualifiedHostname":{"Type":4,"Flags":0,"Description":"Specify a fully-qualified domain name: myapp.mydomain.com. Mutually exclusive with 'prefix' and will take priority if both are defined."}}}},{"2":{"Name":"GatewayRoute","Properties":{"path":{"Type":4,"Flags":0,"Description":"The path to match the incoming request path on. Ex - /myservice."},"destination":{"Type":4,"Flags":0,"Description":"The HttpRoute to route to. Ex - myserviceroute.id."},"replacePrefix":{"Type":4,"Flags":0,"Description":"Optionally update the prefix when sending the request to the service. Ex - replacePrefix: '/' and path: '/myservice' will transform '/myservice/myroute' to '/myroute'"}}}},{"3":{"ItemType":186}},{"2":{"Name":"GatewayTls","Properties":{"sslPassthrough":{"Type":2,"Flags":0,"Description":"If true, gateway lets the https traffic sslPassthrough to the backend servers for decryption."},"minimumProtocolVersion":{"Type":191,"Flags":0,"Description":"Tls Minimum versions for Gateway resource."},"certificateFrom":{"Type":4,"Flags":0,"Description":"The resource id for the secret containing the TLS certificate and key for the gateway."}}}},{"6":{"Value":"1.2"}},{"6":{"Value":"1.3"}},{"5":{"Elements":[189,190]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/gateways@2023-10-01-preview","ScopeType":0,"Body":175}},{"6":{"Value":"Applications.Core/httpRoutes"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/httpRoutes","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":194,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":195,"Flags":10,"Description":"The resource api version"},"properties":{"Type":197,"Flags":1,"Description":"HTTPRoute properties"},"tags":{"Type":206,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"HttpRouteProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":205,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"hostname":{"Type":4,"Flags":0,"Description":"The internal hostname accepting traffic for the HTTP Route. Readonly."},"port":{"Type":3,"Flags":0,"Description":"The port number for the HTTP Route. Defaults to 80. Readonly."},"scheme":{"Type":4,"Flags":2,"Description":"The scheme used for traffic. Readonly."},"url":{"Type":4,"Flags":2,"Description":"A stable URL that that can be used to route traffic to a resource. Readonly."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[198,199,200,201,202,203,204]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/httpRoutes@2023-10-01-preview","ScopeType":0,"Body":196}},{"6":{"Value":"Applications.Core/secretStores"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/secretStores","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":208,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":209,"Flags":10,"Description":"The resource api version"},"properties":{"Type":211,"Flags":1,"Description":"The properties of SecretStore"},"tags":{"Type":229,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"SecretStoreProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":219,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"type":{"Type":222,"Flags":0,"Description":"The type of SecretStore data"},"data":{"Type":228,"Flags":1,"Description":"An object to represent key-value type secrets"},"resource":{"Type":4,"Flags":0,"Description":"The resource id of external secret store."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[212,213,214,215,216,217,218]}},{"6":{"Value":"generic"}},{"6":{"Value":"certificate"}},{"5":{"Elements":[220,221]}},{"2":{"Name":"SecretValueProperties","Properties":{"encoding":{"Type":226,"Flags":0,"Description":"The type of SecretValue Encoding"},"value":{"Type":4,"Flags":0,"Description":"The value of secret."},"valueFrom":{"Type":227,"Flags":0,"Description":"The Secret value source properties"}}}},{"6":{"Value":"raw"}},{"6":{"Value":"base64"}},{"5":{"Elements":[224,225]}},{"2":{"Name":"ValueFromProperties","Properties":{"name":{"Type":4,"Flags":1,"Description":"The name of the referenced secret."},"version":{"Type":4,"Flags":0,"Description":"The version of the referenced secret."}}}},{"2":{"Name":"SecretStorePropertiesData","Properties":{},"AdditionalProperties":223}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/secretStores@2023-10-01-preview","ScopeType":0,"Body":210}},{"6":{"Value":"Applications.Core/volumes"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/volumes","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":231,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":232,"Flags":10,"Description":"The resource api version"},"properties":{"Type":234,"Flags":1,"Description":"Volume properties"},"tags":{"Type":266,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"7":{"Name":"VolumeProperties","Discriminator":"kind","BaseProperties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":242,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."}},"Elements":{"azure.com.keyvault":243}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[235,236,237,238,239,240,241]}},{"2":{"Name":"AzureKeyVaultVolumeProperties","Properties":{"certificates":{"Type":256,"Flags":0,"Description":"The KeyVault certificates that this volume exposes"},"keys":{"Type":258,"Flags":0,"Description":"The KeyVault keys that this volume exposes"},"resource":{"Type":4,"Flags":1,"Description":"The ID of the keyvault to use for this volume resource"},"secrets":{"Type":264,"Flags":0,"Description":"The KeyVault secrets that this volume exposes"},"kind":{"Type":265,"Flags":1,"Description":"Discriminator property for VolumeProperties."}}}},{"2":{"Name":"CertificateObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"encoding":{"Type":248,"Flags":0,"Description":"Represents secret encodings"},"format":{"Type":251,"Flags":0,"Description":"Represents certificate formats"},"name":{"Type":4,"Flags":1,"Description":"The name of the certificate"},"certType":{"Type":255,"Flags":0,"Description":"Represents certificate types"},"version":{"Type":4,"Flags":0,"Description":"Certificate version"}}}},{"6":{"Value":"utf-8"}},{"6":{"Value":"hex"}},{"6":{"Value":"base64"}},{"5":{"Elements":[245,246,247]}},{"6":{"Value":"pem"}},{"6":{"Value":"pfx"}},{"5":{"Elements":[249,250]}},{"6":{"Value":"certificate"}},{"6":{"Value":"privatekey"}},{"6":{"Value":"publickey"}},{"5":{"Elements":[252,253,254]}},{"2":{"Name":"AzureKeyVaultVolumePropertiesCertificates","Properties":{},"AdditionalProperties":244}},{"2":{"Name":"KeyObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"name":{"Type":4,"Flags":1,"Description":"The name of the key"},"version":{"Type":4,"Flags":0,"Description":"Key version"}}}},{"2":{"Name":"AzureKeyVaultVolumePropertiesKeys","Properties":{},"AdditionalProperties":257}},{"2":{"Name":"SecretObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"encoding":{"Type":263,"Flags":0,"Description":"Represents secret encodings"},"name":{"Type":4,"Flags":1,"Description":"The name of the secret"},"version":{"Type":4,"Flags":0,"Description":"secret version"}}}},{"6":{"Value":"utf-8"}},{"6":{"Value":"hex"}},{"6":{"Value":"base64"}},{"5":{"Elements":[260,261,262]}},{"2":{"Name":"AzureKeyVaultVolumePropertiesSecrets","Properties":{},"AdditionalProperties":259}},{"6":{"Value":"azure.com.keyvault"}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/volumes@2023-10-01-preview","ScopeType":0,"Body":233}},{"8":{"Name":"listSecrets","ResourceType":"Applications.Core/extenders","ApiVersion":"2023-10-01-preview","Output":0,"Input":0}},{"2":{"Name":"SecretStoreListSecretsResult","Properties":{"type":{"Type":272,"Flags":2,"Description":"The type of SecretStore data"},"data":{"Type":273,"Flags":2,"Description":"An object to represent key-value type secrets"}}}},{"6":{"Value":"generic"}},{"6":{"Value":"certificate"}},{"5":{"Elements":[270,271]}},{"2":{"Name":"SecretStoreListSecretsResultData","Properties":{},"AdditionalProperties":223}},{"8":{"Name":"listSecrets","ResourceType":"Applications.Core/secretStores","ApiVersion":"2023-10-01-preview","Output":269,"Input":0}}] \ No newline at end of file diff --git a/pkg/corerp/api/v20231001preview/environment_conversion.go b/pkg/corerp/api/v20231001preview/environment_conversion.go index a5e1f35387..b3d2964221 100644 --- a/pkg/corerp/api/v20231001preview/environment_conversion.go +++ b/pkg/corerp/api/v20231001preview/environment_conversion.go @@ -194,10 +194,10 @@ func toRecipeConfigDatamodel(config *RecipeConfigProperties) datamodel.RecipeCon if gitConfig != nil { recipeConfig.Terraform.Authentication.Git = datamodel.GitAuthConfig{} if gitConfig.Pat != nil { - p := map[string]datamodel.Secret{} + p := map[string]datamodel.SecretConfig{} for k, v := range gitConfig.Pat { - p[k] = datamodel.Secret{ - SecretStore: to.String(v.SecretStore), + p[k] = datamodel.SecretConfig{ + Secret: to.String(v.Secret), } } recipeConfig.Terraform.Authentication.Git.PAT = p @@ -221,14 +221,14 @@ func fromRecipeConfigDatamodel(config datamodel.RecipeConfigProperties) *RecipeC if !reflect.DeepEqual(config.Terraform.Authentication.Git, datamodel.GitAuthConfig{}) { recipeConfig.Terraform.Authentication.Git = &GitAuthConfig{} if config.Terraform.Authentication.Git.PAT != nil { - recipeConfig.Terraform.Authentication.Git.Pat = map[string]*Secret{} + recipeConfig.Terraform.Authentication.Git.Pat = map[string]*SecretConfig{} } } } } for k, v := range config.Terraform.Authentication.Git.PAT { - recipeConfig.Terraform.Authentication.Git.Pat[k] = &Secret{ - SecretStore: to.Ptr(v.SecretStore), + recipeConfig.Terraform.Authentication.Git.Pat[k] = &SecretConfig{ + Secret: to.Ptr(v.Secret), } } return recipeConfig diff --git a/pkg/corerp/api/v20231001preview/environment_conversion_test.go b/pkg/corerp/api/v20231001preview/environment_conversion_test.go index 92fff82c39..fff8312a15 100644 --- a/pkg/corerp/api/v20231001preview/environment_conversion_test.go +++ b/pkg/corerp/api/v20231001preview/environment_conversion_test.go @@ -121,9 +121,9 @@ func TestConvertVersionedToDataModel(t *testing.T) { Terraform: datamodel.TerraformConfigProperties{ Authentication: datamodel.AuthConfig{ Git: datamodel.GitAuthConfig{ - PAT: map[string]datamodel.Secret{ - "dev.azure.com": datamodel.Secret{ - SecretStore: "/planes/radius/local/resourcegroups/default/providers/Applications.Core/secretStores/github", + PAT: map[string]datamodel.SecretConfig{ + "dev.azure.com": datamodel.SecretConfig{ + Secret: "/planes/radius/local/resourcegroups/default/providers/Applications.Core/secretStores/github", }, }, }, @@ -381,7 +381,7 @@ func TestConvertDataModelToVersioned(t *testing.T) { if tt.filename == "environmentresourcedatamodel.json" { require.Equal(t, "Azure/cosmosdb/azurerm", string(*versioned.Properties.Recipes[ds_ctrl.MongoDatabasesResourceType]["terraform-recipe"].GetRecipeProperties().TemplatePath)) require.Equal(t, recipes.TemplateKindTerraform, string(*versioned.Properties.Recipes[ds_ctrl.MongoDatabasesResourceType]["terraform-recipe"].GetRecipeProperties().TemplateKind)) - require.Equal(t, "/planes/radius/local/resourcegroups/default/providers/Applications.Core/secretStores/github", string(*versioned.Properties.RecipeConfig.Terraform.Authentication.Git.Pat["dev.azure.com"].SecretStore)) + require.Equal(t, "/planes/radius/local/resourcegroups/default/providers/Applications.Core/secretStores/github", string(*versioned.Properties.RecipeConfig.Terraform.Authentication.Git.Pat["dev.azure.com"].Secret)) switch c := recipeDetails.(type) { case *TerraformRecipeProperties: require.Equal(t, "1.1.0", string(*c.TemplateVersion)) diff --git a/pkg/corerp/api/v20231001preview/zz_generated_models.go b/pkg/corerp/api/v20231001preview/zz_generated_models.go index 4d34dbd412..e1b8ef72c1 100644 --- a/pkg/corerp/api/v20231001preview/zz_generated_models.go +++ b/pkg/corerp/api/v20231001preview/zz_generated_models.go @@ -952,7 +952,7 @@ type GatewayTLS struct { // GitAuthConfig - Specifies authentication information needed to access private terraform modules from Git repository sources. type GitAuthConfig struct { // Specifies the secret details of type personal access token for each different git platforms - Pat map[string]*Secret + Pat map[string]*SecretConfig } // HTTPGetHealthProbeProperties - Specifies the properties for readiness/liveness probe using HTTP Get @@ -1523,10 +1523,10 @@ type RuntimesProperties struct { Kubernetes *KubernetesRuntimeProperties } -// Secret - Specifies the secret details of type personal access token for each different git platforms -type Secret struct { +// SecretConfig - Specifies the secret details of type personal access token for each different git platforms +type SecretConfig struct { // The resource id for the Applications.Core/SecretStore resource containing credentials. - SecretStore *string + Secret *string } // SecretObjectProperties - Represents secret object properties diff --git a/pkg/corerp/api/v20231001preview/zz_generated_models_serde.go b/pkg/corerp/api/v20231001preview/zz_generated_models_serde.go index 6b5c2777b5..67e1489f00 100644 --- a/pkg/corerp/api/v20231001preview/zz_generated_models_serde.go +++ b/pkg/corerp/api/v20231001preview/zz_generated_models_serde.go @@ -3660,15 +3660,15 @@ func (r *RuntimesProperties) UnmarshalJSON(data []byte) error { return nil } -// MarshalJSON implements the json.Marshaller interface for type Secret. -func (s Secret) MarshalJSON() ([]byte, error) { +// MarshalJSON implements the json.Marshaller interface for type SecretConfig. +func (s SecretConfig) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) - populate(objectMap, "secretStore", s.SecretStore) + populate(objectMap, "secret", s.Secret) return json.Marshal(objectMap) } -// UnmarshalJSON implements the json.Unmarshaller interface for type Secret. -func (s *Secret) UnmarshalJSON(data []byte) error { +// UnmarshalJSON implements the json.Unmarshaller interface for type SecretConfig. +func (s *SecretConfig) 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", s, err) @@ -3676,8 +3676,8 @@ func (s *Secret) UnmarshalJSON(data []byte) error { for key, val := range rawMsg { var err error switch key { - case "secretStore": - err = unpopulate(val, "SecretStore", &s.SecretStore) + case "secret": + err = unpopulate(val, "Secret", &s.Secret) delete(rawMsg, key) } if err != nil { diff --git a/pkg/corerp/datamodel/environment.go b/pkg/corerp/datamodel/environment.go index 864f1aeef0..ed044ae40d 100644 --- a/pkg/corerp/datamodel/environment.go +++ b/pkg/corerp/datamodel/environment.go @@ -67,13 +67,13 @@ type AuthConfig struct { // GitAuthConfig - Specifies authentication information needed to use private terraform module repositories from git module source type GitAuthConfig struct { // GitAuthConfig specifies the secret details of type personal access token for each different git platforms - PAT map[string]Secret `json:"pat,omitempty"` + PAT map[string]SecretConfig `json:"pat,omitempty"` } -// Secret - Specifies the secret details of type personal access token for each different git platforms -type Secret struct { +// SecretConfig - Specifies the secret details of type personal access token for each different git platforms +type SecretConfig struct { // Secret represent the resource id for the secret store containing credentials - SecretStore string `json:"secretStore,omitempty"` + Secret string `json:"secret,omitempty"` } // EnvironmentRecipeProperties represents the properties of environment's recipe. diff --git a/pkg/recipes/configloader/environment_test.go b/pkg/recipes/configloader/environment_test.go index f1f62f3f91..60157b9eeb 100644 --- a/pkg/recipes/configloader/environment_test.go +++ b/pkg/recipes/configloader/environment_test.go @@ -190,9 +190,9 @@ func TestGetConfiguration(t *testing.T) { Terraform: &model.TerraformConfigProperties{ Authentication: &model.AuthConfig{ Git: &model.GitAuthConfig{ - Pat: map[string]*model.Secret{ - "dev.azure.com": &model.Secret{ - SecretStore: to.Ptr("secretStoreID"), + Pat: map[string]*model.SecretConfig{ + "dev.azure.com": &model.SecretConfig{ + Secret: to.Ptr("secretStoreID"), }, }, }, @@ -213,9 +213,9 @@ func TestGetConfiguration(t *testing.T) { Terraform: datamodel.TerraformConfigProperties{ Authentication: datamodel.AuthConfig{ Git: datamodel.GitAuthConfig{ - PAT: map[string]datamodel.Secret{ - "dev.azure.com": datamodel.Secret{ - SecretStore: "secretStoreID", + PAT: map[string]datamodel.SecretConfig{ + "dev.azure.com": datamodel.SecretConfig{ + Secret: "secretStoreID", }, }, }, diff --git a/pkg/recipes/terraform/config/config.go b/pkg/recipes/terraform/config/config.go index d98847f287..186821fe57 100644 --- a/pkg/recipes/terraform/config/config.go +++ b/pkg/recipes/terraform/config/config.go @@ -139,7 +139,7 @@ func getSecretStoreID(envConfig recipes.Configuration, templatePath string) (str return "", err } - return envConfig.RecipeConfig.Terraform.Authentication.Git.PAT[strings.TrimPrefix(url.Hostname(), "www.")].SecretStore, nil + return envConfig.RecipeConfig.Terraform.Authentication.Git.PAT[strings.TrimPrefix(url.Hostname(), "www.")].Secret, nil } // getMainConfigFilePath returns the path of the Terraform main config file. diff --git a/pkg/recipes/terraform/config/config_test.go b/pkg/recipes/terraform/config/config_test.go index f206740aab..9c44b4cd1e 100644 --- a/pkg/recipes/terraform/config/config_test.go +++ b/pkg/recipes/terraform/config/config_test.go @@ -557,9 +557,9 @@ func Test_getSecretStoreID(t *testing.T) { Terraform: datamodel.TerraformConfigProperties{ Authentication: datamodel.AuthConfig{ Git: datamodel.GitAuthConfig{ - PAT: map[string]datamodel.Secret{ - "dev.azure.com": datamodel.Secret{ - SecretStore: "secret-store1", + PAT: map[string]datamodel.SecretConfig{ + "dev.azure.com": datamodel.SecretConfig{ + Secret: "secret-store1", }, }, }, diff --git a/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/openapi.json b/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/openapi.json index d5ee2fb02d..fa435237d9 100644 --- a/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/openapi.json +++ b/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/openapi.json @@ -4041,7 +4041,7 @@ "type": "object", "description": "Specifies the secret details of type personal access token for each different git platforms", "additionalProperties": { - "$ref": "#/definitions/Secret" + "$ref": "#/definitions/SecretConfig" } } } @@ -5011,11 +5011,11 @@ } } }, - "Secret": { + "SecretConfig": { "type": "object", "description": "Specifies the secret details of type personal access token for each different git platforms", "properties": { - "secretStore": { + "secret": { "type": "string", "description": "The resource id for the Applications.Core/SecretStore resource containing credentials." } diff --git a/typespec/Applications.Core/environments.tsp b/typespec/Applications.Core/environments.tsp index 1c6bb310ef..7f216257fa 100644 --- a/typespec/Applications.Core/environments.tsp +++ b/typespec/Applications.Core/environments.tsp @@ -94,13 +94,13 @@ model AuthConfig{ @doc("Specifies authentication information needed to access private terraform modules from Git repository sources.") model GitAuthConfig{ @doc("Specifies the secret details of type personal access token for each different git platforms") - pat?: Record; + pat?: Record; } @doc("Specifies the secret details of type personal access token for each different git platforms") -model Secret { +model SecretConfig { @doc("The resource id for the Applications.Core/SecretStore resource containing credentials.") - secretStore?: string; + secret?: string; } @doc("The Cloud providers configuration") From e2d4936de1a15c480ced713806ca2dfd5eaa71ab Mon Sep 17 00:00:00 2001 From: Vishwanath Hiremath Date: Tue, 20 Feb 2024 00:02:46 -0800 Subject: [PATCH 08/22] addressing comments Signed-off-by: Vishwanath Hiremath --- .../2023-10-01-preview/types.json | 2 +- .../environment_conversion.go | 10 +- .../environment_conversion_test.go | 11 ++- ...onmentresource-with-workload-identity.json | 9 ++ .../testdata/environmentresource.json | 2 +- ...ourcedatamodel-with-workload-identity.json | 9 ++ .../environmentresourcedatamodel.json | 2 +- .../v20231001preview/zz_generated_models.go | 2 +- pkg/recipes/configloader/environment.go | 1 + pkg/recipes/configloader/environment_test.go | 91 +++++++++---------- .../preview/2023-10-01-preview/openapi.json | 2 +- typespec/Applications.Core/environments.tsp | 2 +- 12 files changed, 84 insertions(+), 59 deletions(-) diff --git a/hack/bicep-types-radius/generated/applications/applications.core/2023-10-01-preview/types.json b/hack/bicep-types-radius/generated/applications/applications.core/2023-10-01-preview/types.json index 42469b1345..6dfe57e442 100644 --- a/hack/bicep-types-radius/generated/applications/applications.core/2023-10-01-preview/types.json +++ b/hack/bicep-types-radius/generated/applications/applications.core/2023-10-01-preview/types.json @@ -1 +1 @@ -[{"1":{"Kind":1}},{"1":{"Kind":2}},{"1":{"Kind":3}},{"1":{"Kind":4}},{"1":{"Kind":5}},{"1":{"Kind":6}},{"1":{"Kind":7}},{"1":{"Kind":8}},{"6":{"Value":"Applications.Core/applications"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/applications","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":8,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":9,"Flags":10,"Description":"The resource api version"},"properties":{"Type":11,"Flags":1,"Description":"Application properties"},"tags":{"Type":46,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ApplicationProperties","Properties":{"provisioningState":{"Type":19,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"environment":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"extensions":{"Type":34,"Flags":0,"Description":"The application extension."},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[12,13,14,15,16,17,18]}},{"7":{"Name":"Extension","Discriminator":"kind","BaseProperties":{},"Elements":{"daprSidecar":21,"kubernetesMetadata":26,"kubernetesNamespace":30,"manualScaling":32}}},{"2":{"Name":"DaprSidecarExtension","Properties":{"appPort":{"Type":3,"Flags":0,"Description":"The Dapr appPort. Specifies the internal listening port for the application to handle requests from the Dapr sidecar."},"appId":{"Type":4,"Flags":1,"Description":"The Dapr appId. Specifies the identifier used by Dapr for service invocation."},"config":{"Type":4,"Flags":0,"Description":"Specifies the Dapr configuration to use for the resource."},"protocol":{"Type":24,"Flags":0,"Description":"The Dapr sidecar extension protocol"},"kind":{"Type":25,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"http"}},{"6":{"Value":"grpc"}},{"5":{"Elements":[22,23]}},{"6":{"Value":"daprSidecar"}},{"2":{"Name":"KubernetesMetadataExtension","Properties":{"annotations":{"Type":27,"Flags":0,"Description":"Annotations to be applied to the Kubernetes resources output by the resource"},"labels":{"Type":28,"Flags":0,"Description":"Labels to be applied to the Kubernetes resources output by the resource"},"kind":{"Type":29,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"2":{"Name":"KubernetesMetadataExtensionAnnotations","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"KubernetesMetadataExtensionLabels","Properties":{},"AdditionalProperties":4}},{"6":{"Value":"kubernetesMetadata"}},{"2":{"Name":"KubernetesNamespaceExtension","Properties":{"namespace":{"Type":4,"Flags":1,"Description":"The namespace of the application environment."},"kind":{"Type":31,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"kubernetesNamespace"}},{"2":{"Name":"ManualScalingExtension","Properties":{"replicas":{"Type":3,"Flags":1,"Description":"Replica count."},"kind":{"Type":33,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"manualScaling"}},{"3":{"ItemType":20}},{"2":{"Name":"ResourceStatus","Properties":{"compute":{"Type":36,"Flags":0,"Description":"Represents backing compute resource"},"recipe":{"Type":43,"Flags":2,"Description":"Recipe status at deployment time for a resource."},"outputResources":{"Type":45,"Flags":0,"Description":"Properties of an output resource"}}}},{"7":{"Name":"EnvironmentCompute","Discriminator":"kind","BaseProperties":{"resourceId":{"Type":4,"Flags":0,"Description":"The resource id of the compute resource for application environment."},"identity":{"Type":37,"Flags":0,"Description":"IdentitySettings is the external identity setting."}},"Elements":{"kubernetes":41}}},{"2":{"Name":"IdentitySettings","Properties":{"kind":{"Type":40,"Flags":1,"Description":"IdentitySettingKind is the kind of supported external identity setting"},"oidcIssuer":{"Type":4,"Flags":0,"Description":"The URI for your compute platform's OIDC issuer"},"resource":{"Type":4,"Flags":0,"Description":"The resource ID of the provisioned identity"}}}},{"6":{"Value":"undefined"}},{"6":{"Value":"azure.com.workload"}},{"5":{"Elements":[38,39]}},{"2":{"Name":"KubernetesCompute","Properties":{"namespace":{"Type":4,"Flags":1,"Description":"The namespace to use for the environment."},"kind":{"Type":42,"Flags":1,"Description":"Discriminator property for EnvironmentCompute."}}}},{"6":{"Value":"kubernetes"}},{"2":{"Name":"RecipeStatus","Properties":{"templateKind":{"Type":4,"Flags":1,"Description":"TemplateKind is the kind of the recipe template used by the portable resource upon deployment."},"templatePath":{"Type":4,"Flags":1,"Description":"TemplatePath is the path of the recipe consumed by the portable resource upon deployment."},"templateVersion":{"Type":4,"Flags":0,"Description":"TemplateVersion is the version number of the template."}}}},{"2":{"Name":"OutputResource","Properties":{"localId":{"Type":4,"Flags":0,"Description":"The logical identifier scoped to the owning Radius resource. This is only needed or used when a resource has a dependency relationship. LocalIDs do not have any particular format or meaning beyond being compared to determine dependency relationships."},"id":{"Type":4,"Flags":0,"Description":"The UCP resource ID of the underlying resource."},"radiusManaged":{"Type":2,"Flags":0,"Description":"Determines whether Radius manages the lifecycle of the underlying resource."}}}},{"3":{"ItemType":44}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"SystemData","Properties":{"createdBy":{"Type":4,"Flags":0,"Description":"The identity that created the resource."},"createdByType":{"Type":52,"Flags":0,"Description":"The type of identity that created the resource."},"createdAt":{"Type":4,"Flags":0,"Description":"The timestamp of resource creation (UTC)."},"lastModifiedBy":{"Type":4,"Flags":0,"Description":"The identity that last modified the resource."},"lastModifiedByType":{"Type":57,"Flags":0,"Description":"The type of identity that created the resource."},"lastModifiedAt":{"Type":4,"Flags":0,"Description":"The timestamp of resource last modification (UTC)"}}}},{"6":{"Value":"User"}},{"6":{"Value":"Application"}},{"6":{"Value":"ManagedIdentity"}},{"6":{"Value":"Key"}},{"5":{"Elements":[48,49,50,51]}},{"6":{"Value":"User"}},{"6":{"Value":"Application"}},{"6":{"Value":"ManagedIdentity"}},{"6":{"Value":"Key"}},{"5":{"Elements":[53,54,55,56]}},{"4":{"Name":"Applications.Core/applications@2023-10-01-preview","ScopeType":0,"Body":10}},{"6":{"Value":"Applications.Core/containers"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/containers","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":59,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":60,"Flags":10,"Description":"The resource api version"},"properties":{"Type":62,"Flags":1,"Description":"Container properties"},"tags":{"Type":122,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ContainerProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":70,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"container":{"Type":71,"Flags":1,"Description":"Definition of a container"},"connections":{"Type":108,"Flags":0,"Description":"Specifies a connection to another resource."},"identity":{"Type":37,"Flags":0,"Description":"IdentitySettings is the external identity setting."},"extensions":{"Type":109,"Flags":0,"Description":"Extensions spec of the resource"},"resourceProvisioning":{"Type":112,"Flags":0,"Description":"Specifies how the underlying service/resource is provisioned and managed. Available values are 'internal', where Radius manages the lifecycle of the resource internally, and 'manual', where a user manages the resource."},"resources":{"Type":114,"Flags":0,"Description":"A collection of references to resources associated with the container"},"restartPolicy":{"Type":118,"Flags":0,"Description":"Restart policy for the container"},"runtimes":{"Type":119,"Flags":0,"Description":"The properties for runtime configuration"}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[63,64,65,66,67,68,69]}},{"2":{"Name":"Container","Properties":{"image":{"Type":4,"Flags":1,"Description":"The registry and image to download and run in your container"},"imagePullPolicy":{"Type":75,"Flags":0,"Description":"The image pull policy for the container"},"env":{"Type":76,"Flags":0,"Description":"environment"},"ports":{"Type":81,"Flags":0,"Description":"container ports"},"readinessProbe":{"Type":82,"Flags":0,"Description":"Properties for readiness/liveness probe"},"livenessProbe":{"Type":82,"Flags":0,"Description":"Properties for readiness/liveness probe"},"volumes":{"Type":101,"Flags":0,"Description":"container volumes"},"command":{"Type":102,"Flags":0,"Description":"Entrypoint array. Overrides the container image's ENTRYPOINT"},"args":{"Type":103,"Flags":0,"Description":"Arguments to the entrypoint. Overrides the container image's CMD"},"workingDir":{"Type":4,"Flags":0,"Description":"Working directory for the container"}}}},{"6":{"Value":"Always"}},{"6":{"Value":"IfNotPresent"}},{"6":{"Value":"Never"}},{"5":{"Elements":[72,73,74]}},{"2":{"Name":"ContainerEnv","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"ContainerPortProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"protocol":{"Type":80,"Flags":0,"Description":"The protocol in use by the port"},"provides":{"Type":4,"Flags":0,"Description":"Specifies a route provided by this port"},"scheme":{"Type":4,"Flags":0,"Description":"Specifies the URL scheme of the communication protocol. Consumers can use the scheme to construct a URL. The value defaults to 'http' or 'https' depending on the port value"},"port":{"Type":3,"Flags":0,"Description":"Specifies the port that will be exposed by this container. Must be set when value different from containerPort is desired"}}}},{"6":{"Value":"TCP"}},{"6":{"Value":"UDP"}},{"5":{"Elements":[78,79]}},{"2":{"Name":"ContainerPorts","Properties":{},"AdditionalProperties":77}},{"7":{"Name":"HealthProbeProperties","Discriminator":"kind","BaseProperties":{"initialDelaySeconds":{"Type":3,"Flags":0,"Description":"Initial delay in seconds before probing for readiness/liveness"},"failureThreshold":{"Type":3,"Flags":0,"Description":"Threshold number of times the probe fails after which a failure would be reported"},"periodSeconds":{"Type":3,"Flags":0,"Description":"Interval for the readiness/liveness probe in seconds"},"timeoutSeconds":{"Type":3,"Flags":0,"Description":"Number of seconds after which the readiness/liveness probe times out. Defaults to 5 seconds"}},"Elements":{"exec":83,"httpGet":85,"tcp":88}}},{"2":{"Name":"ExecHealthProbeProperties","Properties":{"command":{"Type":4,"Flags":1,"Description":"Command to execute to probe readiness/liveness"},"kind":{"Type":84,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"6":{"Value":"exec"}},{"2":{"Name":"HttpGetHealthProbeProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"path":{"Type":4,"Flags":1,"Description":"The route to make the HTTP request on"},"headers":{"Type":86,"Flags":0,"Description":"Custom HTTP headers to add to the get request"},"kind":{"Type":87,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"2":{"Name":"HttpGetHealthProbePropertiesHeaders","Properties":{},"AdditionalProperties":4}},{"6":{"Value":"httpGet"}},{"2":{"Name":"TcpHealthProbeProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"kind":{"Type":89,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"6":{"Value":"tcp"}},{"7":{"Name":"Volume","Discriminator":"kind","BaseProperties":{"mountPath":{"Type":4,"Flags":0,"Description":"The path where the volume is mounted"}},"Elements":{"ephemeral":91,"persistent":96}}},{"2":{"Name":"EphemeralVolume","Properties":{"managedStore":{"Type":94,"Flags":1,"Description":"The managed store for the ephemeral volume"},"kind":{"Type":95,"Flags":1,"Description":"Discriminator property for Volume."}}}},{"6":{"Value":"memory"}},{"6":{"Value":"disk"}},{"5":{"Elements":[92,93]}},{"6":{"Value":"ephemeral"}},{"2":{"Name":"PersistentVolume","Properties":{"permission":{"Type":99,"Flags":0,"Description":"The persistent volume permission"},"source":{"Type":4,"Flags":1,"Description":"The source of the volume"},"kind":{"Type":100,"Flags":1,"Description":"Discriminator property for Volume."}}}},{"6":{"Value":"read"}},{"6":{"Value":"write"}},{"5":{"Elements":[97,98]}},{"6":{"Value":"persistent"}},{"2":{"Name":"ContainerVolumes","Properties":{},"AdditionalProperties":90}},{"3":{"ItemType":4}},{"3":{"ItemType":4}},{"2":{"Name":"ConnectionProperties","Properties":{"source":{"Type":4,"Flags":1,"Description":"The source of the connection"},"disableDefaultEnvVars":{"Type":2,"Flags":0,"Description":"default environment variable override"},"iam":{"Type":105,"Flags":0,"Description":"IAM properties"}}}},{"2":{"Name":"IamProperties","Properties":{"kind":{"Type":106,"Flags":1,"Description":"The kind of IAM provider to configure"},"roles":{"Type":107,"Flags":0,"Description":"RBAC permissions to be assigned on the source resource"}}}},{"6":{"Value":"azure"}},{"3":{"ItemType":4}},{"2":{"Name":"ContainerPropertiesConnections","Properties":{},"AdditionalProperties":104}},{"3":{"ItemType":20}},{"6":{"Value":"internal"}},{"6":{"Value":"manual"}},{"5":{"Elements":[110,111]}},{"2":{"Name":"ResourceReference","Properties":{"id":{"Type":4,"Flags":1,"Description":"Resource id of an existing resource"}}}},{"3":{"ItemType":113}},{"6":{"Value":"Always"}},{"6":{"Value":"OnFailure"}},{"6":{"Value":"Never"}},{"5":{"Elements":[115,116,117]}},{"2":{"Name":"RuntimesProperties","Properties":{"kubernetes":{"Type":120,"Flags":0,"Description":"The runtime configuration properties for Kubernetes"}}}},{"2":{"Name":"KubernetesRuntimeProperties","Properties":{"base":{"Type":4,"Flags":0,"Description":"The serialized YAML manifest which represents the base Kubernetes resources to deploy, such as Deployment, Service, ServiceAccount, Secrets, and ConfigMaps."},"pod":{"Type":121,"Flags":0,"Description":"A strategic merge patch that will be applied to the PodSpec object when this container is being deployed."}}}},{"2":{"Name":"KubernetesPodSpec","Properties":{},"AdditionalProperties":0}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/containers@2023-10-01-preview","ScopeType":0,"Body":61}},{"6":{"Value":"Applications.Core/environments"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/environments","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":124,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":125,"Flags":10,"Description":"The resource api version"},"properties":{"Type":127,"Flags":1,"Description":"Environment properties"},"tags":{"Type":153,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"EnvironmentProperties","Properties":{"provisioningState":{"Type":135,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"compute":{"Type":36,"Flags":1,"Description":"Represents backing compute resource"},"providers":{"Type":136,"Flags":0,"Description":"The Cloud providers configuration"},"simulated":{"Type":2,"Flags":0,"Description":"Simulated environment."},"recipes":{"Type":145,"Flags":0,"Description":"Specifies Recipes linked to the Environment."},"recipeConfig":{"Type":146,"Flags":0,"Description":"Specifies recipe configurations needed for the recipes."},"extensions":{"Type":152,"Flags":0,"Description":"The environment extension."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[128,129,130,131,132,133,134]}},{"2":{"Name":"Providers","Properties":{"azure":{"Type":137,"Flags":0,"Description":"The Azure cloud provider definition"},"aws":{"Type":138,"Flags":0,"Description":"The AWS cloud provider definition"}}}},{"2":{"Name":"ProvidersAzure","Properties":{"scope":{"Type":4,"Flags":1,"Description":"Target scope for Azure resources to be deployed into. For example: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup'"}}}},{"2":{"Name":"ProvidersAws","Properties":{"scope":{"Type":4,"Flags":1,"Description":"Target scope for AWS resources to be deployed into. For example: '/planes/aws/aws/accounts/000000000000/regions/us-west-2'"}}}},{"7":{"Name":"RecipeProperties","Discriminator":"templateKind","BaseProperties":{"templatePath":{"Type":4,"Flags":1,"Description":"Path to the template provided by the recipe. Currently only link to Azure Container Registry is supported."},"parameters":{"Type":0,"Flags":0,"Description":"Any object"}},"Elements":{"bicep":140,"terraform":142}}},{"2":{"Name":"BicepRecipeProperties","Properties":{"plainHttp":{"Type":2,"Flags":0,"Description":"Connect to the Bicep registry using HTTP (not-HTTPS). This should be used when the registry is known not to support HTTPS, for example in a locally-hosted registry. Defaults to false (use HTTPS/TLS)."},"templateKind":{"Type":141,"Flags":1,"Description":"Discriminator property for RecipeProperties."}}}},{"6":{"Value":"bicep"}},{"2":{"Name":"TerraformRecipeProperties","Properties":{"templateVersion":{"Type":4,"Flags":0,"Description":"Version of the template to deploy. For Terraform recipes using a module registry this is required, but must be omitted for other module sources."},"templateKind":{"Type":143,"Flags":1,"Description":"Discriminator property for RecipeProperties."}}}},{"6":{"Value":"terraform"}},{"2":{"Name":"DictionaryOfRecipeProperties","Properties":{},"AdditionalProperties":139}},{"2":{"Name":"EnvironmentPropertiesRecipes","Properties":{},"AdditionalProperties":144}},{"2":{"Name":"RecipeConfigProperties","Properties":{"terraform":{"Type":147,"Flags":0,"Description":"Specifies the terraform config properties"}}}},{"2":{"Name":"TerraformConfigProperties","Properties":{"authentication":{"Type":148,"Flags":0,"Description":"Specifies authentication information needed to use private terraform module repositories."}}}},{"2":{"Name":"AuthConfig","Properties":{"git":{"Type":149,"Flags":0,"Description":"Specifies authentication information needed to access private terraform modules from Git repository sources."}}}},{"2":{"Name":"GitAuthConfig","Properties":{"pat":{"Type":151,"Flags":0,"Description":"Specifies the secret details of type personal access token for each different git platforms"}}}},{"2":{"Name":"SecretConfig","Properties":{"secret":{"Type":4,"Flags":0,"Description":"The resource id for the Applications.Core/SecretStore resource containing credentials."}}}},{"2":{"Name":"GitAuthConfigPat","Properties":{},"AdditionalProperties":150}},{"3":{"ItemType":20}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/environments@2023-10-01-preview","ScopeType":0,"Body":126}},{"6":{"Value":"Applications.Core/extenders"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/extenders","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":155,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":156,"Flags":10,"Description":"The resource api version"},"properties":{"Type":158,"Flags":1,"Description":"ExtenderResource portable resource properties"},"tags":{"Type":171,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ExtenderProperties","Properties":{"environment":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the environment that the portable resource is linked to"},"application":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the application that the portable resource is consumed by (if applicable)"},"provisioningState":{"Type":166,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"secrets":{"Type":0,"Flags":0,"Description":"Any object"},"recipe":{"Type":167,"Flags":0,"Description":"The recipe used to automatically deploy underlying infrastructure for a portable resource"},"resourceProvisioning":{"Type":170,"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."}},"AdditionalProperties":0}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[159,160,161,162,163,164,165]}},{"2":{"Name":"Recipe","Properties":{"name":{"Type":4,"Flags":1,"Description":"The name of the recipe within the environment to use"},"parameters":{"Type":0,"Flags":0,"Description":"Any object"}}}},{"6":{"Value":"recipe"}},{"6":{"Value":"manual"}},{"5":{"Elements":[168,169]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/extenders@2023-10-01-preview","ScopeType":0,"Body":157}},{"6":{"Value":"Applications.Core/gateways"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/gateways","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":173,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":174,"Flags":10,"Description":"The resource api version"},"properties":{"Type":176,"Flags":1,"Description":"Gateway properties"},"tags":{"Type":192,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"GatewayProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":184,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"internal":{"Type":2,"Flags":0,"Description":"Sets Gateway to not be exposed externally (no public IP address associated). Defaults to false (exposed to internet)."},"hostname":{"Type":185,"Flags":0,"Description":"Declare hostname information for the Gateway. Leaving the hostname empty auto-assigns one: mygateway.myapp.PUBLICHOSTNAMEORIP.nip.io."},"routes":{"Type":187,"Flags":1,"Description":"Routes attached to this Gateway"},"tls":{"Type":188,"Flags":0,"Description":"TLS configuration definition for Gateway resource."},"url":{"Type":4,"Flags":2,"Description":"URL of the gateway resource. Readonly"}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[177,178,179,180,181,182,183]}},{"2":{"Name":"GatewayHostname","Properties":{"prefix":{"Type":4,"Flags":0,"Description":"Specify a prefix for the hostname: myhostname.myapp.PUBLICHOSTNAMEORIP.nip.io. Mutually exclusive with 'fullyQualifiedHostname' and will be overridden if both are defined."},"fullyQualifiedHostname":{"Type":4,"Flags":0,"Description":"Specify a fully-qualified domain name: myapp.mydomain.com. Mutually exclusive with 'prefix' and will take priority if both are defined."}}}},{"2":{"Name":"GatewayRoute","Properties":{"path":{"Type":4,"Flags":0,"Description":"The path to match the incoming request path on. Ex - /myservice."},"destination":{"Type":4,"Flags":0,"Description":"The HttpRoute to route to. Ex - myserviceroute.id."},"replacePrefix":{"Type":4,"Flags":0,"Description":"Optionally update the prefix when sending the request to the service. Ex - replacePrefix: '/' and path: '/myservice' will transform '/myservice/myroute' to '/myroute'"}}}},{"3":{"ItemType":186}},{"2":{"Name":"GatewayTls","Properties":{"sslPassthrough":{"Type":2,"Flags":0,"Description":"If true, gateway lets the https traffic sslPassthrough to the backend servers for decryption."},"minimumProtocolVersion":{"Type":191,"Flags":0,"Description":"Tls Minimum versions for Gateway resource."},"certificateFrom":{"Type":4,"Flags":0,"Description":"The resource id for the secret containing the TLS certificate and key for the gateway."}}}},{"6":{"Value":"1.2"}},{"6":{"Value":"1.3"}},{"5":{"Elements":[189,190]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/gateways@2023-10-01-preview","ScopeType":0,"Body":175}},{"6":{"Value":"Applications.Core/httpRoutes"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/httpRoutes","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":194,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":195,"Flags":10,"Description":"The resource api version"},"properties":{"Type":197,"Flags":1,"Description":"HTTPRoute properties"},"tags":{"Type":206,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"HttpRouteProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":205,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"hostname":{"Type":4,"Flags":0,"Description":"The internal hostname accepting traffic for the HTTP Route. Readonly."},"port":{"Type":3,"Flags":0,"Description":"The port number for the HTTP Route. Defaults to 80. Readonly."},"scheme":{"Type":4,"Flags":2,"Description":"The scheme used for traffic. Readonly."},"url":{"Type":4,"Flags":2,"Description":"A stable URL that that can be used to route traffic to a resource. Readonly."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[198,199,200,201,202,203,204]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/httpRoutes@2023-10-01-preview","ScopeType":0,"Body":196}},{"6":{"Value":"Applications.Core/secretStores"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/secretStores","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":208,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":209,"Flags":10,"Description":"The resource api version"},"properties":{"Type":211,"Flags":1,"Description":"The properties of SecretStore"},"tags":{"Type":229,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"SecretStoreProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":219,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"type":{"Type":222,"Flags":0,"Description":"The type of SecretStore data"},"data":{"Type":228,"Flags":1,"Description":"An object to represent key-value type secrets"},"resource":{"Type":4,"Flags":0,"Description":"The resource id of external secret store."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[212,213,214,215,216,217,218]}},{"6":{"Value":"generic"}},{"6":{"Value":"certificate"}},{"5":{"Elements":[220,221]}},{"2":{"Name":"SecretValueProperties","Properties":{"encoding":{"Type":226,"Flags":0,"Description":"The type of SecretValue Encoding"},"value":{"Type":4,"Flags":0,"Description":"The value of secret."},"valueFrom":{"Type":227,"Flags":0,"Description":"The Secret value source properties"}}}},{"6":{"Value":"raw"}},{"6":{"Value":"base64"}},{"5":{"Elements":[224,225]}},{"2":{"Name":"ValueFromProperties","Properties":{"name":{"Type":4,"Flags":1,"Description":"The name of the referenced secret."},"version":{"Type":4,"Flags":0,"Description":"The version of the referenced secret."}}}},{"2":{"Name":"SecretStorePropertiesData","Properties":{},"AdditionalProperties":223}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/secretStores@2023-10-01-preview","ScopeType":0,"Body":210}},{"6":{"Value":"Applications.Core/volumes"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/volumes","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":231,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":232,"Flags":10,"Description":"The resource api version"},"properties":{"Type":234,"Flags":1,"Description":"Volume properties"},"tags":{"Type":266,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"7":{"Name":"VolumeProperties","Discriminator":"kind","BaseProperties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":242,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."}},"Elements":{"azure.com.keyvault":243}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[235,236,237,238,239,240,241]}},{"2":{"Name":"AzureKeyVaultVolumeProperties","Properties":{"certificates":{"Type":256,"Flags":0,"Description":"The KeyVault certificates that this volume exposes"},"keys":{"Type":258,"Flags":0,"Description":"The KeyVault keys that this volume exposes"},"resource":{"Type":4,"Flags":1,"Description":"The ID of the keyvault to use for this volume resource"},"secrets":{"Type":264,"Flags":0,"Description":"The KeyVault secrets that this volume exposes"},"kind":{"Type":265,"Flags":1,"Description":"Discriminator property for VolumeProperties."}}}},{"2":{"Name":"CertificateObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"encoding":{"Type":248,"Flags":0,"Description":"Represents secret encodings"},"format":{"Type":251,"Flags":0,"Description":"Represents certificate formats"},"name":{"Type":4,"Flags":1,"Description":"The name of the certificate"},"certType":{"Type":255,"Flags":0,"Description":"Represents certificate types"},"version":{"Type":4,"Flags":0,"Description":"Certificate version"}}}},{"6":{"Value":"utf-8"}},{"6":{"Value":"hex"}},{"6":{"Value":"base64"}},{"5":{"Elements":[245,246,247]}},{"6":{"Value":"pem"}},{"6":{"Value":"pfx"}},{"5":{"Elements":[249,250]}},{"6":{"Value":"certificate"}},{"6":{"Value":"privatekey"}},{"6":{"Value":"publickey"}},{"5":{"Elements":[252,253,254]}},{"2":{"Name":"AzureKeyVaultVolumePropertiesCertificates","Properties":{},"AdditionalProperties":244}},{"2":{"Name":"KeyObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"name":{"Type":4,"Flags":1,"Description":"The name of the key"},"version":{"Type":4,"Flags":0,"Description":"Key version"}}}},{"2":{"Name":"AzureKeyVaultVolumePropertiesKeys","Properties":{},"AdditionalProperties":257}},{"2":{"Name":"SecretObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"encoding":{"Type":263,"Flags":0,"Description":"Represents secret encodings"},"name":{"Type":4,"Flags":1,"Description":"The name of the secret"},"version":{"Type":4,"Flags":0,"Description":"secret version"}}}},{"6":{"Value":"utf-8"}},{"6":{"Value":"hex"}},{"6":{"Value":"base64"}},{"5":{"Elements":[260,261,262]}},{"2":{"Name":"AzureKeyVaultVolumePropertiesSecrets","Properties":{},"AdditionalProperties":259}},{"6":{"Value":"azure.com.keyvault"}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/volumes@2023-10-01-preview","ScopeType":0,"Body":233}},{"8":{"Name":"listSecrets","ResourceType":"Applications.Core/extenders","ApiVersion":"2023-10-01-preview","Output":0,"Input":0}},{"2":{"Name":"SecretStoreListSecretsResult","Properties":{"type":{"Type":272,"Flags":2,"Description":"The type of SecretStore data"},"data":{"Type":273,"Flags":2,"Description":"An object to represent key-value type secrets"}}}},{"6":{"Value":"generic"}},{"6":{"Value":"certificate"}},{"5":{"Elements":[270,271]}},{"2":{"Name":"SecretStoreListSecretsResultData","Properties":{},"AdditionalProperties":223}},{"8":{"Name":"listSecrets","ResourceType":"Applications.Core/secretStores","ApiVersion":"2023-10-01-preview","Output":269,"Input":0}}] \ No newline at end of file +[{"1":{"Kind":1}},{"1":{"Kind":2}},{"1":{"Kind":3}},{"1":{"Kind":4}},{"1":{"Kind":5}},{"1":{"Kind":6}},{"1":{"Kind":7}},{"1":{"Kind":8}},{"6":{"Value":"Applications.Core/applications"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/applications","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":8,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":9,"Flags":10,"Description":"The resource api version"},"properties":{"Type":11,"Flags":1,"Description":"Application properties"},"tags":{"Type":46,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ApplicationProperties","Properties":{"provisioningState":{"Type":19,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"environment":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"extensions":{"Type":34,"Flags":0,"Description":"The application extension."},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[12,13,14,15,16,17,18]}},{"7":{"Name":"Extension","Discriminator":"kind","BaseProperties":{},"Elements":{"daprSidecar":21,"kubernetesMetadata":26,"kubernetesNamespace":30,"manualScaling":32}}},{"2":{"Name":"DaprSidecarExtension","Properties":{"appPort":{"Type":3,"Flags":0,"Description":"The Dapr appPort. Specifies the internal listening port for the application to handle requests from the Dapr sidecar."},"appId":{"Type":4,"Flags":1,"Description":"The Dapr appId. Specifies the identifier used by Dapr for service invocation."},"config":{"Type":4,"Flags":0,"Description":"Specifies the Dapr configuration to use for the resource."},"protocol":{"Type":24,"Flags":0,"Description":"The Dapr sidecar extension protocol"},"kind":{"Type":25,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"http"}},{"6":{"Value":"grpc"}},{"5":{"Elements":[22,23]}},{"6":{"Value":"daprSidecar"}},{"2":{"Name":"KubernetesMetadataExtension","Properties":{"annotations":{"Type":27,"Flags":0,"Description":"Annotations to be applied to the Kubernetes resources output by the resource"},"labels":{"Type":28,"Flags":0,"Description":"Labels to be applied to the Kubernetes resources output by the resource"},"kind":{"Type":29,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"2":{"Name":"KubernetesMetadataExtensionAnnotations","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"KubernetesMetadataExtensionLabels","Properties":{},"AdditionalProperties":4}},{"6":{"Value":"kubernetesMetadata"}},{"2":{"Name":"KubernetesNamespaceExtension","Properties":{"namespace":{"Type":4,"Flags":1,"Description":"The namespace of the application environment."},"kind":{"Type":31,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"kubernetesNamespace"}},{"2":{"Name":"ManualScalingExtension","Properties":{"replicas":{"Type":3,"Flags":1,"Description":"Replica count."},"kind":{"Type":33,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"manualScaling"}},{"3":{"ItemType":20}},{"2":{"Name":"ResourceStatus","Properties":{"compute":{"Type":36,"Flags":0,"Description":"Represents backing compute resource"},"recipe":{"Type":43,"Flags":2,"Description":"Recipe status at deployment time for a resource."},"outputResources":{"Type":45,"Flags":0,"Description":"Properties of an output resource"}}}},{"7":{"Name":"EnvironmentCompute","Discriminator":"kind","BaseProperties":{"resourceId":{"Type":4,"Flags":0,"Description":"The resource id of the compute resource for application environment."},"identity":{"Type":37,"Flags":0,"Description":"IdentitySettings is the external identity setting."}},"Elements":{"kubernetes":41}}},{"2":{"Name":"IdentitySettings","Properties":{"kind":{"Type":40,"Flags":1,"Description":"IdentitySettingKind is the kind of supported external identity setting"},"oidcIssuer":{"Type":4,"Flags":0,"Description":"The URI for your compute platform's OIDC issuer"},"resource":{"Type":4,"Flags":0,"Description":"The resource ID of the provisioned identity"}}}},{"6":{"Value":"undefined"}},{"6":{"Value":"azure.com.workload"}},{"5":{"Elements":[38,39]}},{"2":{"Name":"KubernetesCompute","Properties":{"namespace":{"Type":4,"Flags":1,"Description":"The namespace to use for the environment."},"kind":{"Type":42,"Flags":1,"Description":"Discriminator property for EnvironmentCompute."}}}},{"6":{"Value":"kubernetes"}},{"2":{"Name":"RecipeStatus","Properties":{"templateKind":{"Type":4,"Flags":1,"Description":"TemplateKind is the kind of the recipe template used by the portable resource upon deployment."},"templatePath":{"Type":4,"Flags":1,"Description":"TemplatePath is the path of the recipe consumed by the portable resource upon deployment."},"templateVersion":{"Type":4,"Flags":0,"Description":"TemplateVersion is the version number of the template."}}}},{"2":{"Name":"OutputResource","Properties":{"localId":{"Type":4,"Flags":0,"Description":"The logical identifier scoped to the owning Radius resource. This is only needed or used when a resource has a dependency relationship. LocalIDs do not have any particular format or meaning beyond being compared to determine dependency relationships."},"id":{"Type":4,"Flags":0,"Description":"The UCP resource ID of the underlying resource."},"radiusManaged":{"Type":2,"Flags":0,"Description":"Determines whether Radius manages the lifecycle of the underlying resource."}}}},{"3":{"ItemType":44}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"SystemData","Properties":{"createdBy":{"Type":4,"Flags":0,"Description":"The identity that created the resource."},"createdByType":{"Type":52,"Flags":0,"Description":"The type of identity that created the resource."},"createdAt":{"Type":4,"Flags":0,"Description":"The timestamp of resource creation (UTC)."},"lastModifiedBy":{"Type":4,"Flags":0,"Description":"The identity that last modified the resource."},"lastModifiedByType":{"Type":57,"Flags":0,"Description":"The type of identity that created the resource."},"lastModifiedAt":{"Type":4,"Flags":0,"Description":"The timestamp of resource last modification (UTC)"}}}},{"6":{"Value":"User"}},{"6":{"Value":"Application"}},{"6":{"Value":"ManagedIdentity"}},{"6":{"Value":"Key"}},{"5":{"Elements":[48,49,50,51]}},{"6":{"Value":"User"}},{"6":{"Value":"Application"}},{"6":{"Value":"ManagedIdentity"}},{"6":{"Value":"Key"}},{"5":{"Elements":[53,54,55,56]}},{"4":{"Name":"Applications.Core/applications@2023-10-01-preview","ScopeType":0,"Body":10}},{"6":{"Value":"Applications.Core/containers"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/containers","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":59,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":60,"Flags":10,"Description":"The resource api version"},"properties":{"Type":62,"Flags":1,"Description":"Container properties"},"tags":{"Type":122,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ContainerProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":70,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"container":{"Type":71,"Flags":1,"Description":"Definition of a container"},"connections":{"Type":108,"Flags":0,"Description":"Specifies a connection to another resource."},"identity":{"Type":37,"Flags":0,"Description":"IdentitySettings is the external identity setting."},"extensions":{"Type":109,"Flags":0,"Description":"Extensions spec of the resource"},"resourceProvisioning":{"Type":112,"Flags":0,"Description":"Specifies how the underlying service/resource is provisioned and managed. Available values are 'internal', where Radius manages the lifecycle of the resource internally, and 'manual', where a user manages the resource."},"resources":{"Type":114,"Flags":0,"Description":"A collection of references to resources associated with the container"},"restartPolicy":{"Type":118,"Flags":0,"Description":"Restart policy for the container"},"runtimes":{"Type":119,"Flags":0,"Description":"The properties for runtime configuration"}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[63,64,65,66,67,68,69]}},{"2":{"Name":"Container","Properties":{"image":{"Type":4,"Flags":1,"Description":"The registry and image to download and run in your container"},"imagePullPolicy":{"Type":75,"Flags":0,"Description":"The image pull policy for the container"},"env":{"Type":76,"Flags":0,"Description":"environment"},"ports":{"Type":81,"Flags":0,"Description":"container ports"},"readinessProbe":{"Type":82,"Flags":0,"Description":"Properties for readiness/liveness probe"},"livenessProbe":{"Type":82,"Flags":0,"Description":"Properties for readiness/liveness probe"},"volumes":{"Type":101,"Flags":0,"Description":"container volumes"},"command":{"Type":102,"Flags":0,"Description":"Entrypoint array. Overrides the container image's ENTRYPOINT"},"args":{"Type":103,"Flags":0,"Description":"Arguments to the entrypoint. Overrides the container image's CMD"},"workingDir":{"Type":4,"Flags":0,"Description":"Working directory for the container"}}}},{"6":{"Value":"Always"}},{"6":{"Value":"IfNotPresent"}},{"6":{"Value":"Never"}},{"5":{"Elements":[72,73,74]}},{"2":{"Name":"ContainerEnv","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"ContainerPortProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"protocol":{"Type":80,"Flags":0,"Description":"The protocol in use by the port"},"provides":{"Type":4,"Flags":0,"Description":"Specifies a route provided by this port"},"scheme":{"Type":4,"Flags":0,"Description":"Specifies the URL scheme of the communication protocol. Consumers can use the scheme to construct a URL. The value defaults to 'http' or 'https' depending on the port value"},"port":{"Type":3,"Flags":0,"Description":"Specifies the port that will be exposed by this container. Must be set when value different from containerPort is desired"}}}},{"6":{"Value":"TCP"}},{"6":{"Value":"UDP"}},{"5":{"Elements":[78,79]}},{"2":{"Name":"ContainerPorts","Properties":{},"AdditionalProperties":77}},{"7":{"Name":"HealthProbeProperties","Discriminator":"kind","BaseProperties":{"initialDelaySeconds":{"Type":3,"Flags":0,"Description":"Initial delay in seconds before probing for readiness/liveness"},"failureThreshold":{"Type":3,"Flags":0,"Description":"Threshold number of times the probe fails after which a failure would be reported"},"periodSeconds":{"Type":3,"Flags":0,"Description":"Interval for the readiness/liveness probe in seconds"},"timeoutSeconds":{"Type":3,"Flags":0,"Description":"Number of seconds after which the readiness/liveness probe times out. Defaults to 5 seconds"}},"Elements":{"exec":83,"httpGet":85,"tcp":88}}},{"2":{"Name":"ExecHealthProbeProperties","Properties":{"command":{"Type":4,"Flags":1,"Description":"Command to execute to probe readiness/liveness"},"kind":{"Type":84,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"6":{"Value":"exec"}},{"2":{"Name":"HttpGetHealthProbeProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"path":{"Type":4,"Flags":1,"Description":"The route to make the HTTP request on"},"headers":{"Type":86,"Flags":0,"Description":"Custom HTTP headers to add to the get request"},"kind":{"Type":87,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"2":{"Name":"HttpGetHealthProbePropertiesHeaders","Properties":{},"AdditionalProperties":4}},{"6":{"Value":"httpGet"}},{"2":{"Name":"TcpHealthProbeProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"kind":{"Type":89,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"6":{"Value":"tcp"}},{"7":{"Name":"Volume","Discriminator":"kind","BaseProperties":{"mountPath":{"Type":4,"Flags":0,"Description":"The path where the volume is mounted"}},"Elements":{"ephemeral":91,"persistent":96}}},{"2":{"Name":"EphemeralVolume","Properties":{"managedStore":{"Type":94,"Flags":1,"Description":"The managed store for the ephemeral volume"},"kind":{"Type":95,"Flags":1,"Description":"Discriminator property for Volume."}}}},{"6":{"Value":"memory"}},{"6":{"Value":"disk"}},{"5":{"Elements":[92,93]}},{"6":{"Value":"ephemeral"}},{"2":{"Name":"PersistentVolume","Properties":{"permission":{"Type":99,"Flags":0,"Description":"The persistent volume permission"},"source":{"Type":4,"Flags":1,"Description":"The source of the volume"},"kind":{"Type":100,"Flags":1,"Description":"Discriminator property for Volume."}}}},{"6":{"Value":"read"}},{"6":{"Value":"write"}},{"5":{"Elements":[97,98]}},{"6":{"Value":"persistent"}},{"2":{"Name":"ContainerVolumes","Properties":{},"AdditionalProperties":90}},{"3":{"ItemType":4}},{"3":{"ItemType":4}},{"2":{"Name":"ConnectionProperties","Properties":{"source":{"Type":4,"Flags":1,"Description":"The source of the connection"},"disableDefaultEnvVars":{"Type":2,"Flags":0,"Description":"default environment variable override"},"iam":{"Type":105,"Flags":0,"Description":"IAM properties"}}}},{"2":{"Name":"IamProperties","Properties":{"kind":{"Type":106,"Flags":1,"Description":"The kind of IAM provider to configure"},"roles":{"Type":107,"Flags":0,"Description":"RBAC permissions to be assigned on the source resource"}}}},{"6":{"Value":"azure"}},{"3":{"ItemType":4}},{"2":{"Name":"ContainerPropertiesConnections","Properties":{},"AdditionalProperties":104}},{"3":{"ItemType":20}},{"6":{"Value":"internal"}},{"6":{"Value":"manual"}},{"5":{"Elements":[110,111]}},{"2":{"Name":"ResourceReference","Properties":{"id":{"Type":4,"Flags":1,"Description":"Resource id of an existing resource"}}}},{"3":{"ItemType":113}},{"6":{"Value":"Always"}},{"6":{"Value":"OnFailure"}},{"6":{"Value":"Never"}},{"5":{"Elements":[115,116,117]}},{"2":{"Name":"RuntimesProperties","Properties":{"kubernetes":{"Type":120,"Flags":0,"Description":"The runtime configuration properties for Kubernetes"}}}},{"2":{"Name":"KubernetesRuntimeProperties","Properties":{"base":{"Type":4,"Flags":0,"Description":"The serialized YAML manifest which represents the base Kubernetes resources to deploy, such as Deployment, Service, ServiceAccount, Secrets, and ConfigMaps."},"pod":{"Type":121,"Flags":0,"Description":"A strategic merge patch that will be applied to the PodSpec object when this container is being deployed."}}}},{"2":{"Name":"KubernetesPodSpec","Properties":{},"AdditionalProperties":0}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/containers@2023-10-01-preview","ScopeType":0,"Body":61}},{"6":{"Value":"Applications.Core/environments"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/environments","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":124,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":125,"Flags":10,"Description":"The resource api version"},"properties":{"Type":127,"Flags":1,"Description":"Environment properties"},"tags":{"Type":153,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"EnvironmentProperties","Properties":{"provisioningState":{"Type":135,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"compute":{"Type":36,"Flags":1,"Description":"Represents backing compute resource"},"providers":{"Type":136,"Flags":0,"Description":"The Cloud providers configuration"},"simulated":{"Type":2,"Flags":0,"Description":"Simulated environment."},"recipes":{"Type":145,"Flags":0,"Description":"Specifies Recipes linked to the Environment."},"recipeConfig":{"Type":146,"Flags":0,"Description":"Specifies recipe configurations needed for the recipes."},"extensions":{"Type":152,"Flags":0,"Description":"The environment extension."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[128,129,130,131,132,133,134]}},{"2":{"Name":"Providers","Properties":{"azure":{"Type":137,"Flags":0,"Description":"The Azure cloud provider definition"},"aws":{"Type":138,"Flags":0,"Description":"The AWS cloud provider definition"}}}},{"2":{"Name":"ProvidersAzure","Properties":{"scope":{"Type":4,"Flags":1,"Description":"Target scope for Azure resources to be deployed into. For example: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup'"}}}},{"2":{"Name":"ProvidersAws","Properties":{"scope":{"Type":4,"Flags":1,"Description":"Target scope for AWS resources to be deployed into. For example: '/planes/aws/aws/accounts/000000000000/regions/us-west-2'"}}}},{"7":{"Name":"RecipeProperties","Discriminator":"templateKind","BaseProperties":{"templatePath":{"Type":4,"Flags":1,"Description":"Path to the template provided by the recipe. Currently only link to Azure Container Registry is supported."},"parameters":{"Type":0,"Flags":0,"Description":"Any object"}},"Elements":{"bicep":140,"terraform":142}}},{"2":{"Name":"BicepRecipeProperties","Properties":{"plainHttp":{"Type":2,"Flags":0,"Description":"Connect to the Bicep registry using HTTP (not-HTTPS). This should be used when the registry is known not to support HTTPS, for example in a locally-hosted registry. Defaults to false (use HTTPS/TLS)."},"templateKind":{"Type":141,"Flags":1,"Description":"Discriminator property for RecipeProperties."}}}},{"6":{"Value":"bicep"}},{"2":{"Name":"TerraformRecipeProperties","Properties":{"templateVersion":{"Type":4,"Flags":0,"Description":"Version of the template to deploy. For Terraform recipes using a module registry this is required, but must be omitted for other module sources."},"templateKind":{"Type":143,"Flags":1,"Description":"Discriminator property for RecipeProperties."}}}},{"6":{"Value":"terraform"}},{"2":{"Name":"DictionaryOfRecipeProperties","Properties":{},"AdditionalProperties":139}},{"2":{"Name":"EnvironmentPropertiesRecipes","Properties":{},"AdditionalProperties":144}},{"2":{"Name":"RecipeConfigProperties","Properties":{"terraform":{"Type":147,"Flags":0,"Description":"Specifies the terraform config properties"}}}},{"2":{"Name":"TerraformConfigProperties","Properties":{"authentication":{"Type":148,"Flags":0,"Description":"Specifies authentication information needed to access private terraform module repositories."}}}},{"2":{"Name":"AuthConfig","Properties":{"git":{"Type":149,"Flags":0,"Description":"Specifies authentication information needed to access private terraform modules from Git repository sources."}}}},{"2":{"Name":"GitAuthConfig","Properties":{"pat":{"Type":151,"Flags":0,"Description":"Specifies the secret details of type personal access token for each different git platforms"}}}},{"2":{"Name":"SecretConfig","Properties":{"secret":{"Type":4,"Flags":0,"Description":"The resource id for the Applications.Core/SecretStore resource containing credentials."}}}},{"2":{"Name":"GitAuthConfigPat","Properties":{},"AdditionalProperties":150}},{"3":{"ItemType":20}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/environments@2023-10-01-preview","ScopeType":0,"Body":126}},{"6":{"Value":"Applications.Core/extenders"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/extenders","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":155,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":156,"Flags":10,"Description":"The resource api version"},"properties":{"Type":158,"Flags":1,"Description":"ExtenderResource portable resource properties"},"tags":{"Type":171,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ExtenderProperties","Properties":{"environment":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the environment that the portable resource is linked to"},"application":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the application that the portable resource is consumed by (if applicable)"},"provisioningState":{"Type":166,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"secrets":{"Type":0,"Flags":0,"Description":"Any object"},"recipe":{"Type":167,"Flags":0,"Description":"The recipe used to automatically deploy underlying infrastructure for a portable resource"},"resourceProvisioning":{"Type":170,"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."}},"AdditionalProperties":0}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[159,160,161,162,163,164,165]}},{"2":{"Name":"Recipe","Properties":{"name":{"Type":4,"Flags":1,"Description":"The name of the recipe within the environment to use"},"parameters":{"Type":0,"Flags":0,"Description":"Any object"}}}},{"6":{"Value":"recipe"}},{"6":{"Value":"manual"}},{"5":{"Elements":[168,169]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/extenders@2023-10-01-preview","ScopeType":0,"Body":157}},{"6":{"Value":"Applications.Core/gateways"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/gateways","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":173,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":174,"Flags":10,"Description":"The resource api version"},"properties":{"Type":176,"Flags":1,"Description":"Gateway properties"},"tags":{"Type":192,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"GatewayProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":184,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"internal":{"Type":2,"Flags":0,"Description":"Sets Gateway to not be exposed externally (no public IP address associated). Defaults to false (exposed to internet)."},"hostname":{"Type":185,"Flags":0,"Description":"Declare hostname information for the Gateway. Leaving the hostname empty auto-assigns one: mygateway.myapp.PUBLICHOSTNAMEORIP.nip.io."},"routes":{"Type":187,"Flags":1,"Description":"Routes attached to this Gateway"},"tls":{"Type":188,"Flags":0,"Description":"TLS configuration definition for Gateway resource."},"url":{"Type":4,"Flags":2,"Description":"URL of the gateway resource. Readonly"}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[177,178,179,180,181,182,183]}},{"2":{"Name":"GatewayHostname","Properties":{"prefix":{"Type":4,"Flags":0,"Description":"Specify a prefix for the hostname: myhostname.myapp.PUBLICHOSTNAMEORIP.nip.io. Mutually exclusive with 'fullyQualifiedHostname' and will be overridden if both are defined."},"fullyQualifiedHostname":{"Type":4,"Flags":0,"Description":"Specify a fully-qualified domain name: myapp.mydomain.com. Mutually exclusive with 'prefix' and will take priority if both are defined."}}}},{"2":{"Name":"GatewayRoute","Properties":{"path":{"Type":4,"Flags":0,"Description":"The path to match the incoming request path on. Ex - /myservice."},"destination":{"Type":4,"Flags":0,"Description":"The HttpRoute to route to. Ex - myserviceroute.id."},"replacePrefix":{"Type":4,"Flags":0,"Description":"Optionally update the prefix when sending the request to the service. Ex - replacePrefix: '/' and path: '/myservice' will transform '/myservice/myroute' to '/myroute'"}}}},{"3":{"ItemType":186}},{"2":{"Name":"GatewayTls","Properties":{"sslPassthrough":{"Type":2,"Flags":0,"Description":"If true, gateway lets the https traffic sslPassthrough to the backend servers for decryption."},"minimumProtocolVersion":{"Type":191,"Flags":0,"Description":"Tls Minimum versions for Gateway resource."},"certificateFrom":{"Type":4,"Flags":0,"Description":"The resource id for the secret containing the TLS certificate and key for the gateway."}}}},{"6":{"Value":"1.2"}},{"6":{"Value":"1.3"}},{"5":{"Elements":[189,190]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/gateways@2023-10-01-preview","ScopeType":0,"Body":175}},{"6":{"Value":"Applications.Core/httpRoutes"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/httpRoutes","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":194,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":195,"Flags":10,"Description":"The resource api version"},"properties":{"Type":197,"Flags":1,"Description":"HTTPRoute properties"},"tags":{"Type":206,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"HttpRouteProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":205,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"hostname":{"Type":4,"Flags":0,"Description":"The internal hostname accepting traffic for the HTTP Route. Readonly."},"port":{"Type":3,"Flags":0,"Description":"The port number for the HTTP Route. Defaults to 80. Readonly."},"scheme":{"Type":4,"Flags":2,"Description":"The scheme used for traffic. Readonly."},"url":{"Type":4,"Flags":2,"Description":"A stable URL that that can be used to route traffic to a resource. Readonly."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[198,199,200,201,202,203,204]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/httpRoutes@2023-10-01-preview","ScopeType":0,"Body":196}},{"6":{"Value":"Applications.Core/secretStores"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/secretStores","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":208,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":209,"Flags":10,"Description":"The resource api version"},"properties":{"Type":211,"Flags":1,"Description":"The properties of SecretStore"},"tags":{"Type":229,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"SecretStoreProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":219,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"type":{"Type":222,"Flags":0,"Description":"The type of SecretStore data"},"data":{"Type":228,"Flags":1,"Description":"An object to represent key-value type secrets"},"resource":{"Type":4,"Flags":0,"Description":"The resource id of external secret store."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[212,213,214,215,216,217,218]}},{"6":{"Value":"generic"}},{"6":{"Value":"certificate"}},{"5":{"Elements":[220,221]}},{"2":{"Name":"SecretValueProperties","Properties":{"encoding":{"Type":226,"Flags":0,"Description":"The type of SecretValue Encoding"},"value":{"Type":4,"Flags":0,"Description":"The value of secret."},"valueFrom":{"Type":227,"Flags":0,"Description":"The Secret value source properties"}}}},{"6":{"Value":"raw"}},{"6":{"Value":"base64"}},{"5":{"Elements":[224,225]}},{"2":{"Name":"ValueFromProperties","Properties":{"name":{"Type":4,"Flags":1,"Description":"The name of the referenced secret."},"version":{"Type":4,"Flags":0,"Description":"The version of the referenced secret."}}}},{"2":{"Name":"SecretStorePropertiesData","Properties":{},"AdditionalProperties":223}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/secretStores@2023-10-01-preview","ScopeType":0,"Body":210}},{"6":{"Value":"Applications.Core/volumes"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/volumes","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":231,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":232,"Flags":10,"Description":"The resource api version"},"properties":{"Type":234,"Flags":1,"Description":"Volume properties"},"tags":{"Type":266,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"7":{"Name":"VolumeProperties","Discriminator":"kind","BaseProperties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":242,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."}},"Elements":{"azure.com.keyvault":243}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[235,236,237,238,239,240,241]}},{"2":{"Name":"AzureKeyVaultVolumeProperties","Properties":{"certificates":{"Type":256,"Flags":0,"Description":"The KeyVault certificates that this volume exposes"},"keys":{"Type":258,"Flags":0,"Description":"The KeyVault keys that this volume exposes"},"resource":{"Type":4,"Flags":1,"Description":"The ID of the keyvault to use for this volume resource"},"secrets":{"Type":264,"Flags":0,"Description":"The KeyVault secrets that this volume exposes"},"kind":{"Type":265,"Flags":1,"Description":"Discriminator property for VolumeProperties."}}}},{"2":{"Name":"CertificateObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"encoding":{"Type":248,"Flags":0,"Description":"Represents secret encodings"},"format":{"Type":251,"Flags":0,"Description":"Represents certificate formats"},"name":{"Type":4,"Flags":1,"Description":"The name of the certificate"},"certType":{"Type":255,"Flags":0,"Description":"Represents certificate types"},"version":{"Type":4,"Flags":0,"Description":"Certificate version"}}}},{"6":{"Value":"utf-8"}},{"6":{"Value":"hex"}},{"6":{"Value":"base64"}},{"5":{"Elements":[245,246,247]}},{"6":{"Value":"pem"}},{"6":{"Value":"pfx"}},{"5":{"Elements":[249,250]}},{"6":{"Value":"certificate"}},{"6":{"Value":"privatekey"}},{"6":{"Value":"publickey"}},{"5":{"Elements":[252,253,254]}},{"2":{"Name":"AzureKeyVaultVolumePropertiesCertificates","Properties":{},"AdditionalProperties":244}},{"2":{"Name":"KeyObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"name":{"Type":4,"Flags":1,"Description":"The name of the key"},"version":{"Type":4,"Flags":0,"Description":"Key version"}}}},{"2":{"Name":"AzureKeyVaultVolumePropertiesKeys","Properties":{},"AdditionalProperties":257}},{"2":{"Name":"SecretObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"encoding":{"Type":263,"Flags":0,"Description":"Represents secret encodings"},"name":{"Type":4,"Flags":1,"Description":"The name of the secret"},"version":{"Type":4,"Flags":0,"Description":"secret version"}}}},{"6":{"Value":"utf-8"}},{"6":{"Value":"hex"}},{"6":{"Value":"base64"}},{"5":{"Elements":[260,261,262]}},{"2":{"Name":"AzureKeyVaultVolumePropertiesSecrets","Properties":{},"AdditionalProperties":259}},{"6":{"Value":"azure.com.keyvault"}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/volumes@2023-10-01-preview","ScopeType":0,"Body":233}},{"8":{"Name":"listSecrets","ResourceType":"Applications.Core/extenders","ApiVersion":"2023-10-01-preview","Output":0,"Input":0}},{"2":{"Name":"SecretStoreListSecretsResult","Properties":{"type":{"Type":272,"Flags":2,"Description":"The type of SecretStore data"},"data":{"Type":273,"Flags":2,"Description":"An object to represent key-value type secrets"}}}},{"6":{"Value":"generic"}},{"6":{"Value":"certificate"}},{"5":{"Elements":[270,271]}},{"2":{"Name":"SecretStoreListSecretsResultData","Properties":{},"AdditionalProperties":223}},{"8":{"Name":"listSecrets","ResourceType":"Applications.Core/secretStores","ApiVersion":"2023-10-01-preview","Output":269,"Input":0}}] \ No newline at end of file diff --git a/pkg/corerp/api/v20231001preview/environment_conversion.go b/pkg/corerp/api/v20231001preview/environment_conversion.go index b3d2964221..23ee1fde0c 100644 --- a/pkg/corerp/api/v20231001preview/environment_conversion.go +++ b/pkg/corerp/api/v20231001preview/environment_conversion.go @@ -222,15 +222,15 @@ func fromRecipeConfigDatamodel(config datamodel.RecipeConfigProperties) *RecipeC recipeConfig.Terraform.Authentication.Git = &GitAuthConfig{} if config.Terraform.Authentication.Git.PAT != nil { recipeConfig.Terraform.Authentication.Git.Pat = map[string]*SecretConfig{} + for k, v := range config.Terraform.Authentication.Git.PAT { + recipeConfig.Terraform.Authentication.Git.Pat[k] = &SecretConfig{ + Secret: to.Ptr(v.Secret), + } + } } } } } - for k, v := range config.Terraform.Authentication.Git.PAT { - recipeConfig.Terraform.Authentication.Git.Pat[k] = &SecretConfig{ - Secret: to.Ptr(v.Secret), - } - } return recipeConfig } return nil diff --git a/pkg/corerp/api/v20231001preview/environment_conversion_test.go b/pkg/corerp/api/v20231001preview/environment_conversion_test.go index fff8312a15..45b9f95b48 100644 --- a/pkg/corerp/api/v20231001preview/environment_conversion_test.go +++ b/pkg/corerp/api/v20231001preview/environment_conversion_test.go @@ -73,6 +73,15 @@ func TestConvertVersionedToDataModel(t *testing.T) { Scope: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup", }, }, + RecipeConfig: datamodel.RecipeConfigProperties{ + Terraform: datamodel.TerraformConfigProperties{ + Authentication: datamodel.AuthConfig{ + Git: datamodel.GitAuthConfig{ + PAT: map[string]datamodel.SecretConfig{}, + }, + }, + }, + }, Recipes: map[string]map[string]datamodel.EnvironmentRecipeProperties{ ds_ctrl.MongoDatabasesResourceType: { "cosmos-recipe": datamodel.EnvironmentRecipeProperties{ @@ -122,7 +131,7 @@ func TestConvertVersionedToDataModel(t *testing.T) { Authentication: datamodel.AuthConfig{ Git: datamodel.GitAuthConfig{ PAT: map[string]datamodel.SecretConfig{ - "dev.azure.com": datamodel.SecretConfig{ + "dev.azure.com": { Secret: "/planes/radius/local/resourcegroups/default/providers/Applications.Core/secretStores/github", }, }, diff --git a/pkg/corerp/api/v20231001preview/testdata/environmentresource-with-workload-identity.json b/pkg/corerp/api/v20231001preview/testdata/environmentresource-with-workload-identity.json index 071dcd34d0..3522684771 100644 --- a/pkg/corerp/api/v20231001preview/testdata/environmentresource-with-workload-identity.json +++ b/pkg/corerp/api/v20231001preview/testdata/environmentresource-with-workload-identity.json @@ -18,6 +18,15 @@ "scope": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup" } }, + "recipeConfig": { + "terraform": { + "authentication": { + "git": { + "pat": {} + } + } + } + }, "recipes": { "Applications.Datastores/mongoDatabases":{ "cosmos-recipe": { diff --git a/pkg/corerp/api/v20231001preview/testdata/environmentresource.json b/pkg/corerp/api/v20231001preview/testdata/environmentresource.json index 8a6067f38d..e89fe6d6c7 100644 --- a/pkg/corerp/api/v20231001preview/testdata/environmentresource.json +++ b/pkg/corerp/api/v20231001preview/testdata/environmentresource.json @@ -22,7 +22,7 @@ "git": { "pat": { "dev.azure.com":{ - "secretStore":"/planes/radius/local/resourcegroups/default/providers/Applications.Core/secretStores/github" + "secret":"/planes/radius/local/resourcegroups/default/providers/Applications.Core/secretStores/github" } } } diff --git a/pkg/corerp/api/v20231001preview/testdata/environmentresourcedatamodel-with-workload-identity.json b/pkg/corerp/api/v20231001preview/testdata/environmentresourcedatamodel-with-workload-identity.json index 19cee1e6c0..d2c054b9d5 100644 --- a/pkg/corerp/api/v20231001preview/testdata/environmentresourcedatamodel-with-workload-identity.json +++ b/pkg/corerp/api/v20231001preview/testdata/environmentresourcedatamodel-with-workload-identity.json @@ -31,6 +31,15 @@ "scope": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup" } }, + "recipeConfig": { + "terraform": { + "authentication": { + "git": { + "pat": {} + } + } + } + }, "recipes": { "Applications.Datastores/mongoDatabases":{ "cosmos-recipe": { diff --git a/pkg/corerp/api/v20231001preview/testdata/environmentresourcedatamodel.json b/pkg/corerp/api/v20231001preview/testdata/environmentresourcedatamodel.json index 96294d3ecd..fb126cef61 100644 --- a/pkg/corerp/api/v20231001preview/testdata/environmentresourcedatamodel.json +++ b/pkg/corerp/api/v20231001preview/testdata/environmentresourcedatamodel.json @@ -35,7 +35,7 @@ "git": { "pat": { "dev.azure.com":{ - "secretStore":"/planes/radius/local/resourcegroups/default/providers/Applications.Core/secretStores/github" + "secret":"/planes/radius/local/resourcegroups/default/providers/Applications.Core/secretStores/github" } } } diff --git a/pkg/corerp/api/v20231001preview/zz_generated_models.go b/pkg/corerp/api/v20231001preview/zz_generated_models.go index e1b8ef72c1..78a1da0b41 100644 --- a/pkg/corerp/api/v20231001preview/zz_generated_models.go +++ b/pkg/corerp/api/v20231001preview/zz_generated_models.go @@ -136,7 +136,7 @@ type ApplicationResourceUpdateProperties struct { Simulated *bool } -// AuthConfig - Specifies authentication information needed to use private terraform module repositories. +// AuthConfig - Specifies authentication information needed to access private terraform module repositories. type AuthConfig struct { // Specifies authentication information needed to access private terraform modules from Git repository sources. Git *GitAuthConfig diff --git a/pkg/recipes/configloader/environment.go b/pkg/recipes/configloader/environment.go index 92f016e1d2..2c8e2fbb33 100644 --- a/pkg/recipes/configloader/environment.go +++ b/pkg/recipes/configloader/environment.go @@ -101,6 +101,7 @@ func getConfiguration(environment *v20231001preview.EnvironmentResource, applica return nil, ErrUnsupportedComputeKind } + // convert versioned Environment resource to internal datamodel. env, err := environment.ConvertTo() if err != nil { return nil, err diff --git a/pkg/recipes/configloader/environment_test.go b/pkg/recipes/configloader/environment_test.go index 60157b9eeb..cd9a3b635a 100644 --- a/pkg/recipes/configloader/environment_test.go +++ b/pkg/recipes/configloader/environment_test.go @@ -63,6 +63,19 @@ func TestGetConfiguration(t *testing.T) { Scope: to.Ptr(azureScope), }, }, + RecipeConfig: &model.RecipeConfigProperties{ + Terraform: &model.TerraformConfigProperties{ + Authentication: &model.AuthConfig{ + Git: &model.GitAuthConfig{ + Pat: map[string]*model.SecretConfig{ + "dev.azure.com": &model.SecretConfig{ + Secret: to.Ptr("secretStoreID"), + }, + }, + }, + }, + }, + }, }, }, appResource: nil, @@ -74,6 +87,19 @@ func TestGetConfiguration(t *testing.T) { }, }, Providers: createAzureProvider(), + RecipeConfig: datamodel.RecipeConfigProperties{ + Terraform: datamodel.TerraformConfigProperties{ + Authentication: datamodel.AuthConfig{ + Git: datamodel.GitAuthConfig{ + PAT: map[string]datamodel.SecretConfig{ + "dev.azure.com": datamodel.SecretConfig{ + Secret: "secretStoreID", + }, + }, + }, + }, + }, + }, }, }, { @@ -85,6 +111,15 @@ func TestGetConfiguration(t *testing.T) { Namespace: to.Ptr(envNamespace), ResourceID: to.Ptr(envResourceId), }, + RecipeConfig: &model.RecipeConfigProperties{ + Terraform: &model.TerraformConfigProperties{ + Authentication: &model.AuthConfig{ + Git: &model.GitAuthConfig{ + Pat: map[string]*model.SecretConfig{}, + }, + }, + }, + }, Providers: &model.Providers{ Aws: &model.ProvidersAws{ Scope: to.Ptr(awsScope), @@ -100,6 +135,15 @@ func TestGetConfiguration(t *testing.T) { EnvironmentNamespace: envNamespace, }, }, + RecipeConfig: datamodel.RecipeConfigProperties{ + Terraform: datamodel.TerraformConfigProperties{ + Authentication: datamodel.AuthConfig{ + Git: datamodel.GitAuthConfig{ + PAT: map[string]datamodel.SecretConfig{}, + }, + }, + }, + }, Providers: createAWSProvider(), }, }, @@ -177,53 +221,6 @@ func TestGetConfiguration(t *testing.T) { }, errString: ErrUnsupportedComputeKind.Error(), }, - { - name: "recipe config with env resource", - envResource: &model.EnvironmentResource{ - Properties: &model.EnvironmentProperties{ - Compute: &model.KubernetesCompute{ - Kind: to.Ptr(kind), - Namespace: to.Ptr(envNamespace), - ResourceID: to.Ptr(envResourceId), - }, - RecipeConfig: &model.RecipeConfigProperties{ - Terraform: &model.TerraformConfigProperties{ - Authentication: &model.AuthConfig{ - Git: &model.GitAuthConfig{ - Pat: map[string]*model.SecretConfig{ - "dev.azure.com": &model.SecretConfig{ - Secret: to.Ptr("secretStoreID"), - }, - }, - }, - }, - }, - }, - }, - }, - appResource: nil, - expectedConfig: &recipes.Configuration{ - Runtime: recipes.RuntimeConfiguration{ - Kubernetes: &recipes.KubernetesRuntime{ - Namespace: envNamespace, - EnvironmentNamespace: envNamespace, - }, - }, - RecipeConfig: datamodel.RecipeConfigProperties{ - Terraform: datamodel.TerraformConfigProperties{ - Authentication: datamodel.AuthConfig{ - Git: datamodel.GitAuthConfig{ - PAT: map[string]datamodel.SecretConfig{ - "dev.azure.com": datamodel.SecretConfig{ - Secret: "secretStoreID", - }, - }, - }, - }, - }, - }, - }, - }, } for _, tc := range configTests { diff --git a/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/openapi.json b/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/openapi.json index fa435237d9..84836d0e1a 100644 --- a/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/openapi.json +++ b/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/openapi.json @@ -2761,7 +2761,7 @@ }, "AuthConfig": { "type": "object", - "description": "Specifies authentication information needed to use private terraform module repositories.", + "description": "Specifies authentication information needed to access private terraform module repositories.", "properties": { "git": { "$ref": "#/definitions/GitAuthConfig", diff --git a/typespec/Applications.Core/environments.tsp b/typespec/Applications.Core/environments.tsp index 7f216257fa..5f08209d16 100644 --- a/typespec/Applications.Core/environments.tsp +++ b/typespec/Applications.Core/environments.tsp @@ -85,7 +85,7 @@ model TerraformConfigProperties{ authentication?: AuthConfig; } -@doc("Specifies authentication information needed to use private terraform module repositories.") +@doc("Specifies authentication information needed to access private terraform module repositories.") model AuthConfig{ @doc("Specifies authentication information needed to access private terraform modules from Git repository sources.") git?: GitAuthConfig; From 4b6b8b953a52945c1b7e52c45cc203cac64f74fc Mon Sep 17 00:00:00 2001 From: Vishwanath Hiremath Date: Tue, 20 Feb 2024 23:51:38 -0800 Subject: [PATCH 09/22] adding change to add url config to gitconfig Signed-off-by: Vishwanath Hiremath --- pkg/recipes/driver/terraform.go | 117 ++++++++++++- pkg/recipes/driver/terraform_test.go | 141 +++++++++++++++- pkg/recipes/terraform/config/config.go | 109 +++--------- pkg/recipes/terraform/config/config_test.go | 91 +--------- pkg/recipes/terraform/execute.go | 6 +- pkg/recipes/types.go | 40 +++++ pkg/recipes/types_test.go | 176 ++++++++++++++++++++ 7 files changed, 502 insertions(+), 178 deletions(-) diff --git a/pkg/recipes/driver/terraform.go b/pkg/recipes/driver/terraform.go index ccc1cf9054..c17d6b1145 100644 --- a/pkg/recipes/driver/terraform.go +++ b/pkg/recipes/driver/terraform.go @@ -21,18 +21,22 @@ import ( "errors" "fmt" "os" + "os/exec" "path/filepath" + "reflect" "strings" "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" "github.com/google/uuid" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/corerp/api/v20231001preview" rpv1 "github.com/radius-project/radius/pkg/rp/v1" "golang.org/x/exp/slices" "k8s.io/client-go/kubernetes" "github.com/radius-project/radius/pkg/recipes" + aztoken "github.com/radius-project/radius/pkg/azure/tokencredentials" "github.com/radius-project/radius/pkg/recipes/terraform" recipes_util "github.com/radius-project/radius/pkg/recipes/util" "github.com/radius-project/radius/pkg/sdk" @@ -51,7 +55,8 @@ var _ Driver = (*terraformDriver)(nil) // NewTerraformDriver creates a new instance of driver to execute a Terraform recipe. func NewTerraformDriver(ucpConn sdk.Connection, secretProvider *ucp_provider.SecretProvider, options TerraformOptions, k8sClientSet kubernetes.Interface, armOptions *arm.ClientOptions) Driver { return &terraformDriver{ - terraformExecutor: terraform.NewExecutor(ucpConn, secretProvider, k8sClientSet, armOptions), + ArmClientOptions: armOptions, + terraformExecutor: terraform.NewExecutor(ucpConn, secretProvider, k8sClientSet), options: options, } } @@ -64,6 +69,7 @@ type TerraformOptions struct { // terraformDriver represents a driver to interact with Terraform Recipe - deploy recipe, delete resources, etc. type terraformDriver struct { + ArmClientOptions *arm.ClientOptions // terraformExecutor is used to execute Terraform commands - deploy, destroy, etc. terraformExecutor terraform.TerraformExecutor @@ -90,6 +96,10 @@ func (d *terraformDriver) Execute(ctx context.Context, opts ExecuteOptions) (*re logger.Info("simulated environment is set to true, skipping deployment") return nil, nil } + secrets, err := configureSecretsForModuleSource(ctx, &opts.Configuration, &opts.Recipe, opts.Definition.TemplatePath, d.ArmClientOptions) + if err != nil { + return nil, err + } tfState, err := d.terraformExecutor.Deploy(ctx, terraform.Options{ RootDir: requestDirPath, @@ -97,6 +107,14 @@ func (d *terraformDriver) Execute(ctx context.Context, opts ExecuteOptions) (*re ResourceRecipe: &opts.Recipe, EnvRecipe: &opts.Definition, }) + + if !reflect.DeepEqual(secrets, v20231001preview.SecretStoresClientListSecretsResponse{}) { + unsetError := unsetSecretsFromGitConfig(secrets, opts.Definition.TemplatePath) + if unsetError != nil { + return nil, unsetError + } + } + if err != nil { return nil, recipes.NewRecipeError(recipes.RecipeDeploymentFailed, err.Error(), recipes_util.ExecutionError, recipes.GetErrorDetails(err)) } @@ -109,6 +127,103 @@ func (d *terraformDriver) Execute(ctx context.Context, opts ExecuteOptions) (*re return recipeOutputs, nil } +func configureSecretsForModuleSource(ctx context.Context, envConfig *recipes.Configuration, recipeMetadata *recipes.ResourceMetadata, templatePath string, armOptions *arm.ClientOptions) (v20231001preview.SecretStoresClientListSecretsResponse, error) { + if strings.HasPrefix(templatePath, "git::") { + secretStore, err := recipes.GetSecretStoreID(*envConfig, templatePath) + if err != nil { + return v20231001preview.SecretStoresClientListSecretsResponse{}, err + } + + if secretStore == "" { + return v20231001preview.SecretStoresClientListSecretsResponse{}, nil + } + + secrets, err := getSecrets(ctx, secretStore, armOptions) + if err != nil { + return v20231001preview.SecretStoresClientListSecretsResponse{}, err + } + + err = addSecretsToGitConfig(secrets, recipeMetadata, templatePath) + if err != nil { + return v20231001preview.SecretStoresClientListSecretsResponse{}, err + } + + return secrets, nil + } + return v20231001preview.SecretStoresClientListSecretsResponse{}, nil +} +func getURLConfigKeyValue(secrets v20231001preview.SecretStoresClientListSecretsResponse, templatePath string) (string, string, error) { + url, err := recipes.GetGitURL(templatePath) + if err != nil { + return "", "", err + } + + var username, pat *string + path := "https://" + user, ok := secrets.Data["username"] + if ok { + username = user.Value + path += fmt.Sprintf("%s:", *username) + } + + token, ok := secrets.Data["pat"] + if ok { + pat = token.Value + path += *pat + } + + path += fmt.Sprintf("@%s", url.Hostname()) + return fmt.Sprintf("url.%s.insteadOf", path), url.Hostname(), nil +} +func addSecretsToGitConfig(secrets v20231001preview.SecretStoresClientListSecretsResponse, recipeMetadata *recipes.ResourceMetadata, templatePath string) error { + urlConfigKey, urlConfigValue, err := getURLConfigKeyValue(secrets, templatePath) + if err != nil { + return err + } + env, app, resource, err := recipes.GetEnvAppResourceNames(recipeMetadata) + if err != nil { + return err + } + urlConfigValue = fmt.Sprintf("https://%s-%s-%s-%s", env, app, resource, urlConfigValue) + cmd := exec.Command("git", "config", "--global", urlConfigKey, urlConfigValue) + _, err = cmd.Output() + if err != nil { + return errors.New("failed to add git config") + } + + return err +} + +func unsetSecretsFromGitConfig(secrets v20231001preview.SecretStoresClientListSecretsResponse, templatePath string) error { + urlConfigKey, _, err := getURLConfigKeyValue(secrets, templatePath) + if err != nil { + return err + } + + cmd := exec.Command("git", "config", "--global", "--unset", urlConfigKey) + _, err = cmd.Output() + if err != nil { + return errors.New("failed to unset git config") + } + + return err +} +func getSecrets(ctx context.Context, secretStore string, armOptions *arm.ClientOptions) (v20231001preview.SecretStoresClientListSecretsResponse, error) { + secretStoreID, err := resources.ParseResource(secretStore) + if err != nil { + return v20231001preview.SecretStoresClientListSecretsResponse{}, err + } + client, err := v20231001preview.NewSecretStoresClient(secretStoreID.RootScope(), &aztoken.AnonymousCredential{}, armOptions) + if err != nil { + return v20231001preview.SecretStoresClientListSecretsResponse{}, err + } + secrets, err := client.ListSecrets(ctx, secretStoreID.Name(), map[string]any{}, nil) + if err != nil { + return v20231001preview.SecretStoresClientListSecretsResponse{}, err + } + return secrets, nil +} + // Delete returns an error if called as it is not yet implemented. func (d *terraformDriver) Delete(ctx context.Context, opts DeleteOptions) error { logger := ucplog.FromContextOrDiscard(ctx) diff --git a/pkg/recipes/driver/terraform_test.go b/pkg/recipes/driver/terraform_test.go index 3e42acdcb8..a269f502b0 100644 --- a/pkg/recipes/driver/terraform_test.go +++ b/pkg/recipes/driver/terraform_test.go @@ -21,6 +21,7 @@ import ( "encoding/json" "errors" "os" + "path/filepath" "strings" "testing" @@ -28,9 +29,11 @@ import ( "github.com/google/uuid" tfjson "github.com/hashicorp/terraform-json" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/corerp/api/v20231001preview" "github.com/radius-project/radius/pkg/corerp/datamodel" "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/pkg/recipes/terraform" "github.com/radius-project/radius/test/testcontext" @@ -41,7 +44,7 @@ func setup(t *testing.T) (terraform.MockTerraformExecutor, terraformDriver) { ctrl := gomock.NewController(t) tfExecutor := terraform.NewMockTerraformExecutor(ctrl) - driver := terraformDriver{tfExecutor, TerraformOptions{Path: t.TempDir()}} + driver := terraformDriver{nil, tfExecutor, TerraformOptions{Path: t.TempDir()}} return *tfExecutor, driver } @@ -778,3 +781,139 @@ func Test_Terraform_PrepareRecipeResponse(t *testing.T) { }) } } + +func TestAddConfig(t *testing.T) { + tests := []struct { + desc string + templatePath string + expectedResponse string + expectedErr error + }{ + { + desc: "success", + templatePath: "git::https://github.com/project/module", + expectedResponse: "[url \"https://test-user:ghp_token@github.com\"]\n\tinsteadOf = https://env1-app1-test-redis-recipe-github.com\n", + expectedErr: nil, + }, + { + desc: "invalid git url", + templatePath: "git::https://gith ub.com/project/module", + expectedErr: errors.New("failed to parse git url"), + }, + { + desc: "invalid resource id", + templatePath: "git::https://github.com/project/module", + expectedErr: errors.New(" is not a valid resource id"), + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + tmpdir := t.TempDir() + config := withGlobalGitConfigFile(tmpdir, ``) + defer config() + _, recipeMetadata, _ := buildTestInputs() + if tt.desc == "invalid resource id" { + recipeMetadata.EnvironmentID = "//planes/radius/local/resourceGroups/r1/providers/Applications.Core/environments/env" + } + err := addSecretsToGitConfig(getSecretList(), &recipeMetadata, tt.templatePath) + if tt.expectedErr == nil { + require.NoError(t, err) + fileContent, err := os.ReadFile(filepath.Join(tmpdir, ".gitconfig")) + require.NoError(t, err) + require.Contains(t, string(fileContent), tt.expectedResponse) + } else { + require.Error(t, err) + require.Contains(t, err.Error(), tt.expectedErr.Error()) + } + }) + } + +} +func TestUnsetConfig(t *testing.T) { + tests := []struct { + desc string + templatePath string + fileContent string + expectedResponse string + expectedErr error + }{ + { + desc: "success", + templatePath: "git::https://github.com/project/module", + fileContent: ` + [url "https://test-user:ghp_token@github.com"] + insteadOf = https://env1-app1-test-redis-recipe-github.com + `, + expectedErr: nil, + }, + { + desc: "invalid url", + templatePath: "git::https://git hub.com/project/module", + fileContent: ` + [url "https://test-user:ghp_token@github.com"] + insteadOf = https://env1-app1-test-redis-recipe-github.com + `, + expectedErr: errors.New("failed to parse git url"), + }, + { + desc: "empty config file", + templatePath: "git::https://github.com/project/module", + fileContent: "", + expectedErr: errors.New("failed to unset git config"), + }, + } + for _, tt := range tests { + tmpdir := t.TempDir() + config := withGlobalGitConfigFile(tmpdir, tt.fileContent) + defer config() + err := unsetSecretsFromGitConfig(getSecretList(), tt.templatePath) + if tt.expectedErr == nil { + require.NoError(t, err) + contents, err := os.ReadFile(filepath.Join(tmpdir, ".gitconfig")) + require.NoError(t, err) + require.NotContains(t, string(contents), tt.fileContent) + } else { + require.Error(t, err) + require.Contains(t, err.Error(), tt.expectedErr.Error()) + } + } +} + +func withGlobalGitConfigFile(tmpdir string, content string) func() { + //tmpdir := t.TempDir() + // tmpdir := os.TempDir(workingDir) + // if err != nil { + // panic(err) + // } + + tmpGitConfigFile := filepath.Join(tmpdir, ".gitconfig") + + os.WriteFile( + tmpGitConfigFile, + []byte(content), + 0777, + ) + + prevGitConfigEnv := os.Getenv("HOME") + os.Setenv("HOME", tmpdir) + + return func() { + os.Setenv("HOME", prevGitConfigEnv) + } +} + +func getSecretList() v20231001preview.SecretStoresClientListSecretsResponse { + secrets := v20231001preview.SecretStoresClientListSecretsResponse{ + SecretStoreListSecretsResult: v20231001preview.SecretStoreListSecretsResult{ + Data: map[string]*v20231001preview.SecretValueProperties{ + "username": { + Value: to.Ptr("test-user"), + }, + "pat": { + Value: to.Ptr("ghp_token"), + }, + }, + }, + } + return secrets +} diff --git a/pkg/recipes/terraform/config/config.go b/pkg/recipes/terraform/config/config.go index 186821fe57..9170c0ab1c 100644 --- a/pkg/recipes/terraform/config/config.go +++ b/pkg/recipes/terraform/config/config.go @@ -22,18 +22,13 @@ import ( "errors" "fmt" "io/fs" - "net/url" "os" "strings" - "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" - aztoken "github.com/radius-project/radius/pkg/azure/tokencredentials" - "github.com/radius-project/radius/pkg/corerp/api/v20231001preview" "github.com/radius-project/radius/pkg/recipes" "github.com/radius-project/radius/pkg/recipes/recipecontext" "github.com/radius-project/radius/pkg/recipes/terraform/config/backends" "github.com/radius-project/radius/pkg/recipes/terraform/config/providers" - "github.com/radius-project/radius/pkg/ucp/resources" "github.com/radius-project/radius/pkg/ucp/ucplog" ) @@ -44,11 +39,26 @@ const ( // New creates TerraformConfig with the given module name and its inputs (module source, version, parameters) // Parameters are populated from environment recipe and resource recipe metadata. -func New(ctx context.Context, moduleName string, envRecipe *recipes.EnvironmentDefinition, resourceRecipe *recipes.ResourceMetadata, envConfig *recipes.Configuration, armOptions *arm.ClientOptions) (*TerraformConfig, error) { - // Getting the module source path with credentials information if its a private source. - path, err := getModuleSource(ctx, envConfig, envRecipe.TemplatePath, armOptions) - if err != nil { - return nil, err +func New(ctx context.Context, moduleName string, envRecipe *recipes.EnvironmentDefinition, resourceRecipe *recipes.ResourceMetadata, envConfig *recipes.Configuration) (*TerraformConfig, error) { + path := envRecipe.TemplatePath + if strings.HasPrefix(envRecipe.TemplatePath, "git::") { + secretStore, err := recipes.GetSecretStoreID(*envConfig, envRecipe.TemplatePath) + if err != nil { + return nil, err + } + + if secretStore != "" { + env, app, resource, err := recipes.GetEnvAppResourceNames(resourceRecipe) + if err != nil { + return nil, err + } + url, err := recipes.GetGitURL(envRecipe.TemplatePath) + if err != nil { + return nil, err + } + path = fmt.Sprintf("git::https://%s-%s-%s-%s", env, app, resource, strings.TrimPrefix(url.String(), "https://")) + } + } // Resource parameter gets precedence over environment level parameter, // if same parameter is defined in both environment and resource recipe metadata. @@ -63,85 +73,6 @@ func New(ctx context.Context, moduleName string, envRecipe *recipes.EnvironmentD }, nil } -// getModuleSource is called to get the module source path with credential information -// if template path represent a private source else returns the provided template path -func getModuleSource(ctx context.Context, envConfig *recipes.Configuration, templatePath string, armOptions *arm.ClientOptions) (string, error) { - var source string - var err error - if strings.HasPrefix(templatePath, "git::") { - source, err = getGitSource(ctx, envConfig, templatePath, armOptions) - if err != nil { - return "", err - } - } else { - source = templatePath - } - return source, err -} - -// getGitSource creates a terraform module source in a generic git format with credential information -// e.g: git::https://:@/ -func getGitSource(ctx context.Context, envConfig *recipes.Configuration, templatePath string, armOptions *arm.ClientOptions) (string, error) { - secretStore, err := getSecretStoreID(*envConfig, templatePath) - if err != nil { - return "", err - } - if secretStore == "" { - return templatePath, err - } - - secretStoreID, err := resources.ParseResource(secretStore) - if err != nil { - return "", err - } - client, err := v20231001preview.NewSecretStoresClient(secretStoreID.RootScope(), &aztoken.AnonymousCredential{}, armOptions) - if err != nil { - return "", err - } - secrets, err := client.ListSecrets(ctx, secretStoreID.Name(), map[string]any{}, nil) - if err != nil { - return "", err - } - url, err := getGitURL(templatePath) - if err != nil { - return "", err - } - var username, pat *string - path := "git::https://" - user, ok := secrets.Data["username"] - if ok { - username = user.Value - path += fmt.Sprintf("%s:", *username) - } - token, ok := secrets.Data["pat"] - if ok { - pat = token.Value - path += *pat - } - path += fmt.Sprintf("@%s", strings.TrimPrefix(url.String(), "https://")) - return path, err - -} - -func getGitURL(templatePath string) (*url.URL, error) { - paths := strings.Split(templatePath, "git::") - gitUrl := paths[len(paths)-1] - url, err := url.Parse(gitUrl) - if err != nil { - return nil, err - } - return url, nil -} - -func getSecretStoreID(envConfig recipes.Configuration, templatePath string) (string, error) { - url, err := getGitURL(templatePath) - if err != nil { - return "", err - } - - return envConfig.RecipeConfig.Terraform.Authentication.Git.PAT[strings.TrimPrefix(url.Hostname(), "www.")].Secret, nil -} - // getMainConfigFilePath returns the path of the Terraform main config file. func getMainConfigFilePath(workingDir string) string { return fmt.Sprintf("%s/%s", workingDir, mainConfigFileName) diff --git a/pkg/recipes/terraform/config/config_test.go b/pkg/recipes/terraform/config/config_test.go index 9c44b4cd1e..53d91cac24 100644 --- a/pkg/recipes/terraform/config/config_test.go +++ b/pkg/recipes/terraform/config/config_test.go @@ -180,7 +180,7 @@ func Test_NewConfig(t *testing.T) { t.Run(tc.desc, func(t *testing.T) { workingDir := t.TempDir() - tfconfig, _ := New(context.Background(), testRecipeName, tc.envdef, tc.metadata, nil, nil) + tfconfig, _ := New(context.Background(), testRecipeName, tc.envdef, tc.metadata, nil) // validate generated config err := tfconfig.Save(testcontext.New(t), workingDir) @@ -273,7 +273,7 @@ func Test_AddRecipeContext(t *testing.T) { ctx := testcontext.New(t) workingDir := t.TempDir() - tfconfig, _ := New(context.Background(), testRecipeName, tc.envdef, tc.metadata, nil, nil) + tfconfig, _ := New(context.Background(), testRecipeName, tc.envdef, tc.metadata, nil) err := tfconfig.AddRecipeContext(ctx, tc.moduleName, tc.recipeContext) if tc.err == "" { @@ -417,7 +417,7 @@ func Test_AddProviders(t *testing.T) { ctx := testcontext.New(t) workingDir := t.TempDir() - tfconfig, _ := New(context.Background(), testRecipeName, &envRecipe, &resourceRecipe, nil, nil) + tfconfig, _ := New(context.Background(), testRecipeName, &envRecipe, &resourceRecipe, nil) for _, p := range tc.expectedProviders { mProvider.EXPECT().BuildConfig(ctx, &tc.envConfig).Times(1).Return(p, nil) } @@ -477,7 +477,7 @@ func Test_AddOutputs(t *testing.T) { for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { - tfconfig, _ := New(context.Background(), testRecipeName, &envRecipe, &resourceRecipe, nil, nil) + tfconfig, _ := New(context.Background(), testRecipeName, &envRecipe, &resourceRecipe, nil) err := tfconfig.AddOutputs(tc.moduleName) if tc.expectedErr { @@ -506,7 +506,7 @@ func Test_Save_overwrite(t *testing.T) { ctx := testcontext.New(t) testDir := t.TempDir() envRecipe, resourceRecipe := getTestInputs() - tfconfig, _ := New(context.Background(), testRecipeName, &envRecipe, &resourceRecipe, nil, nil) + tfconfig, _ := New(context.Background(), testRecipeName, &envRecipe, &resourceRecipe, nil) err := tfconfig.Save(ctx, testDir) require.NoError(t, err) @@ -518,7 +518,7 @@ func Test_Save_overwrite(t *testing.T) { func Test_Save_ConfigFileReadOnly(t *testing.T) { testDir := t.TempDir() envRecipe, resourceRecipe := getTestInputs() - tfconfig, _ := New(context.Background(), testRecipeName, &envRecipe, &resourceRecipe, nil, nil) + tfconfig, _ := New(context.Background(), testRecipeName, &envRecipe, &resourceRecipe, nil) // Create a test configuration file with read only permission. err := os.WriteFile(getMainConfigFilePath(testDir), []byte(`{"module":{}}`), 0400) @@ -534,86 +534,9 @@ func Test_Save_InvalidWorkingDir(t *testing.T) { testDir := filepath.Join("invalid", uuid.New().String()) envRecipe, resourceRecipe := getTestInputs() - tfconfig, _ := New(context.Background(), testRecipeName, &envRecipe, &resourceRecipe, nil, nil) + tfconfig, _ := New(context.Background(), testRecipeName, &envRecipe, &resourceRecipe, nil) err := tfconfig.Save(testcontext.New(t), testDir) require.Error(t, err) require.Equal(t, fmt.Sprintf("error creating file: open %s/main.tf.json: no such file or directory", testDir), err.Error()) } - -func Test_getSecretStoreID(t *testing.T) { - tests := []struct { - desc string - templatePath string - config *recipes.Configuration - expSecretStoreID string - expectedErr bool - }{ - { - desc: "success", - templatePath: "https://dev.azure.com/recipes/redis", - config: &recipes.Configuration{ - RecipeConfig: datamodel.RecipeConfigProperties{ - Terraform: datamodel.TerraformConfigProperties{ - Authentication: datamodel.AuthConfig{ - Git: datamodel.GitAuthConfig{ - PAT: map[string]datamodel.SecretConfig{ - "dev.azure.com": datamodel.SecretConfig{ - Secret: "secret-store1", - }, - }, - }, - }, - }, - }, - }, - expSecretStoreID: "secret-store1", - expectedErr: false, - }, - } - - for _, tc := range tests { - t.Run(tc.desc, func(t *testing.T) { - ss, err := getSecretStoreID(*tc.config, tc.templatePath) - if !tc.expectedErr { - require.Equal(t, tc.expSecretStoreID, ss) - require.NoError(t, err) - } else { - require.Error(t, err) - } - }) - } - -} - -func Test_getGitURL(t *testing.T) { - tests := []struct { - desc string - templatePath string - expectedURL string - expectedErr bool - }{ - { - desc: "valid url", - templatePath: "git::https://github.com/project/module", - expectedURL: "https://github.com/project/module", - expectedErr: false, - }, - { - desc: "invalid url", - templatePath: "git::https://git hub.com/project/module", - expectedErr: true, - }, - } - for _, tc := range tests { - t.Run(tc.desc, func(t *testing.T) { - url, err := getGitURL(tc.templatePath) - if tc.expectedErr { - require.Error(t, err) - } else { - require.NoError(t, err) - require.Equal(t, url.String(), tc.expectedURL) - } - }) - } -} diff --git a/pkg/recipes/terraform/execute.go b/pkg/recipes/terraform/execute.go index a236619fd4..cda0e3450d 100644 --- a/pkg/recipes/terraform/execute.go +++ b/pkg/recipes/terraform/execute.go @@ -49,8 +49,8 @@ var ( var _ TerraformExecutor = (*executor)(nil) // NewExecutor creates a new Executor with the given UCP connection and secret provider, to execute a Terraform recipe. -func NewExecutor(ucpConn sdk.Connection, secretProvider *ucp_provider.SecretProvider, k8sClientSet kubernetes.Interface, armOptions *arm.ClientOptions) *executor { - return &executor{ucpConn: ucpConn, secretProvider: secretProvider, k8sClientSet: k8sClientSet, armOptions: armOptions} +func NewExecutor(ucpConn sdk.Connection, secretProvider *ucp_provider.SecretProvider, k8sClientSet kubernetes.Interface) *executor { + return &executor{ucpConn: ucpConn, secretProvider: secretProvider, k8sClientSet: k8sClientSet} } type executor struct { @@ -306,7 +306,7 @@ func getTerraformConfig(ctx context.Context, workingDir string, options Options, } // Create Terraform configuration containing module information with the given recipe parameters. - tfConfig, err := config.New(ctx, localModuleName, options.EnvRecipe, options.ResourceRecipe, options.EnvConfig, armOptions) + tfConfig, err := config.New(ctx, localModuleName, options.EnvRecipe, options.ResourceRecipe, options.EnvConfig) if err != nil { return nil, err } diff --git a/pkg/recipes/types.go b/pkg/recipes/types.go index 50da74df49..9d1c505b6b 100644 --- a/pkg/recipes/types.go +++ b/pkg/recipes/types.go @@ -19,9 +19,13 @@ package recipes import ( "bytes" "encoding/json" + "fmt" + "net/url" + "strings" "github.com/radius-project/radius/pkg/corerp/datamodel" rpv1 "github.com/radius-project/radius/pkg/rp/v1" + "github.com/radius-project/radius/pkg/ucp/resources" ) // Configuration represents kubernetes runtime and cloud provider configuration, which is used by the driver while deploying recipes. @@ -137,3 +141,39 @@ func (ro *RecipeOutput) PrepareRecipeResponse(resultValue map[string]any) error return nil } + +func GetSecretStoreID(envConfig Configuration, templatePath string) (string, error) { + url, err := GetGitURL(templatePath) + if err != nil { + return "", err + } + + return envConfig.RecipeConfig.Terraform.Authentication.Git.PAT[strings.TrimPrefix(url.Hostname(), "www.")].Secret, nil +} + +func GetGitURL(templatePath string) (*url.URL, error) { + paths := strings.Split(templatePath, "git::") + gitUrl := paths[len(paths)-1] + url, err := url.Parse(gitUrl) + if err != nil { + return nil, fmt.Errorf("failed to parse git url %s : %w", gitUrl, err) + } + + return url, nil +} + +func GetEnvAppResourceNames(resourceMetadata *ResourceMetadata) (string, string, string, error) { + app, err := resources.ParseResource(resourceMetadata.ApplicationID) + if err != nil { + return "", "", "", err + } + env, err := resources.ParseResource(resourceMetadata.EnvironmentID) + if err != nil { + return "", "", "", err + } + resource, err := resources.ParseResource(resourceMetadata.ResourceID) + if err != nil { + return "", "", "", err + } + return env.Name(), app.Name(), resource.Name(), nil +} diff --git a/pkg/recipes/types_test.go b/pkg/recipes/types_test.go index 0535f6f060..1cfba2733c 100644 --- a/pkg/recipes/types_test.go +++ b/pkg/recipes/types_test.go @@ -19,6 +19,7 @@ package recipes import ( "testing" + "github.com/radius-project/radius/pkg/corerp/datamodel" rpv1 "github.com/radius-project/radius/pkg/rp/v1" "github.com/stretchr/testify/require" ) @@ -80,3 +81,178 @@ func TestRecipeOutput_PrepareRecipeResponse(t *testing.T) { }) } } + +func Test_GetEnvAppResourceNames(t *testing.T) { + tests := []struct { + desc string + metadata ResourceMetadata + expApp string + expEnv string + expResource string + expectedErr bool + }{ + { + desc: "success", + metadata: ResourceMetadata{ + Name: "redis-azure", + ApplicationID: "/planes/radius/local/resourcegroups/test-rg/providers/applications.core/applications/app1", + EnvironmentID: "/planes/radius/local/resourcegroups/test-rg/providers/applications.core/environments/env1", + ResourceID: "/planes/radius/local/resourceGroups/test-rg/providers/applications.datastores/rediscaches/test-redis-recipe", + Parameters: map[string]any{ + "redis_cache_name": "redis-test", + }, + }, + expApp: "app1", + expEnv: "env1", + expResource: "test-redis-recipe", + expectedErr: false, + }, + { + desc: "invalid env id", + metadata: ResourceMetadata{ + Name: "redis-azure", + ApplicationID: "/planes/radius/local/resourcegroups/test-rg/providers/applications.core/applications/app1", + EnvironmentID: "//planes/radius/local/resourcegroups/test-rg/providers/applications.core/environments/env1", + ResourceID: "/planes/radius/local/resourceGroups/test-rg/providers/applications.datastores/rediscaches/test-redis-recipe", + Parameters: map[string]any{ + "redis_cache_name": "redis-test", + }, + }, + expApp: "app1", + expEnv: "env1", + expResource: "test-redis-recipe", + expectedErr: true, + }, + { + desc: "invalid app id", + metadata: ResourceMetadata{ + Name: "redis-azure", + ApplicationID: "//planes/radius/local/resourcegroups/test-rg/providers/applications.core/applications/app1", + EnvironmentID: "/planes/radius/local/resourcegroups/test-rg/providers/applications.core/environments/env1", + ResourceID: "/planes/radius/local/resourceGroups/test-rg/providers/applications.datastores/rediscaches/test-redis-recipe", + Parameters: map[string]any{ + "redis_cache_name": "redis-test", + }, + }, + expApp: "app1", + expEnv: "env1", + expResource: "test-redis-recipe", + expectedErr: true, + }, + { + desc: "invalid resource id", + metadata: ResourceMetadata{ + Name: "redis-azure", + ApplicationID: "/planes/radius/local/resourcegroups/test-rg/providers/applications.core/applications/app1", + EnvironmentID: "/planes/radius/local/resourcegroups/test-rg/providers/applications.core/environments/env1", + ResourceID: "//planes/radius/local/resourceGroups/test-rg/providers/applications.datastores/rediscaches/test-redis-recipe", + Parameters: map[string]any{ + "redis_cache_name": "redis-test", + }, + }, + expApp: "app1", + expEnv: "env1", + expResource: "test-redis-recipe", + expectedErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + env, app, res, err := GetEnvAppResourceNames(&tt.metadata) + if !tt.expectedErr { + require.Equal(t, tt.expApp, app) + require.Equal(t, tt.expEnv, env) + require.Equal(t, tt.expResource, res) + } else { + require.Error(t, err) + require.Contains(t, err.Error(), "not a valid resource id") + } + }) + } +} +func Test_GetGitURL(t *testing.T) { + tests := []struct { + desc string + templatePath string + expectedURL string + expectedErr bool + }{ + { + desc: "success", + templatePath: "git::https://dev.azure.com/project/module", + expectedURL: "https://dev.azure.com/project/module", + expectedErr: false, + }, + { + desc: "invalid url", + templatePath: "git::https://dev.az ure.com/project/module", + expectedErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + url, err := GetGitURL(tt.templatePath) + if !tt.expectedErr { + require.NoError(t, err) + require.Equal(t, tt.expectedURL, url.String()) + } else { + require.Error(t, err) + } + }) + } + +} +func Test_GetSecretStoreID(t *testing.T) { + tests := []struct { + desc string + envConfig Configuration + templatePath string + expectedSecretStore string + expectedErr bool + }{ + { + desc: "success", + envConfig: Configuration{ + RecipeConfig: datamodel.RecipeConfigProperties{ + Terraform: datamodel.TerraformConfigProperties{ + Authentication: datamodel.AuthConfig{ + Git: datamodel.GitAuthConfig{ + PAT: map[string]datamodel.SecretConfig{ + "dev.azure.com": datamodel.SecretConfig{ + Secret: "secret-store1", + }, + }, + }, + }, + }, + }, + }, + templatePath: "git::https://dev.azure.com/project/module", + expectedSecretStore: "secret-store1", + expectedErr: false, + }, + { + desc: "empty config", + templatePath: "git::https://dev.azure.com/project/module", + expectedSecretStore: "", + expectedErr: false, + }, + { + desc: "invalid template path", + templatePath: "git::https://dev.azu re.com/project/module", + expectedSecretStore: "", + expectedErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + ss, err := GetSecretStoreID(tt.envConfig, tt.templatePath) + if !tt.expectedErr { + require.NoError(t, err) + require.Equal(t, ss, tt.expectedSecretStore) + } else { + require.Error(t, err) + } + }) + } +} From 2149aadd365ed644d5f500b1fd55e0e84e98e0f2 Mon Sep 17 00:00:00 2001 From: Vishwanath Hiremath Date: Wed, 21 Feb 2024 01:02:54 -0800 Subject: [PATCH 10/22] addressing comments Signed-off-by: Vishwanath Hiremath --- .../2023-10-01-preview/types.json | 2 +- .../v20231001preview/zz_generated_models.go | 27 ++++++++++--------- .../preview/2023-10-01-preview/openapi.json | 24 ++++++++--------- typespec/Applications.Core/environments.tsp | 20 +++++++------- 4 files changed, 37 insertions(+), 36 deletions(-) diff --git a/hack/bicep-types-radius/generated/applications/applications.core/2023-10-01-preview/types.json b/hack/bicep-types-radius/generated/applications/applications.core/2023-10-01-preview/types.json index 6dfe57e442..524622e785 100644 --- a/hack/bicep-types-radius/generated/applications/applications.core/2023-10-01-preview/types.json +++ b/hack/bicep-types-radius/generated/applications/applications.core/2023-10-01-preview/types.json @@ -1 +1 @@ -[{"1":{"Kind":1}},{"1":{"Kind":2}},{"1":{"Kind":3}},{"1":{"Kind":4}},{"1":{"Kind":5}},{"1":{"Kind":6}},{"1":{"Kind":7}},{"1":{"Kind":8}},{"6":{"Value":"Applications.Core/applications"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/applications","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":8,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":9,"Flags":10,"Description":"The resource api version"},"properties":{"Type":11,"Flags":1,"Description":"Application properties"},"tags":{"Type":46,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ApplicationProperties","Properties":{"provisioningState":{"Type":19,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"environment":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"extensions":{"Type":34,"Flags":0,"Description":"The application extension."},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[12,13,14,15,16,17,18]}},{"7":{"Name":"Extension","Discriminator":"kind","BaseProperties":{},"Elements":{"daprSidecar":21,"kubernetesMetadata":26,"kubernetesNamespace":30,"manualScaling":32}}},{"2":{"Name":"DaprSidecarExtension","Properties":{"appPort":{"Type":3,"Flags":0,"Description":"The Dapr appPort. Specifies the internal listening port for the application to handle requests from the Dapr sidecar."},"appId":{"Type":4,"Flags":1,"Description":"The Dapr appId. Specifies the identifier used by Dapr for service invocation."},"config":{"Type":4,"Flags":0,"Description":"Specifies the Dapr configuration to use for the resource."},"protocol":{"Type":24,"Flags":0,"Description":"The Dapr sidecar extension protocol"},"kind":{"Type":25,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"http"}},{"6":{"Value":"grpc"}},{"5":{"Elements":[22,23]}},{"6":{"Value":"daprSidecar"}},{"2":{"Name":"KubernetesMetadataExtension","Properties":{"annotations":{"Type":27,"Flags":0,"Description":"Annotations to be applied to the Kubernetes resources output by the resource"},"labels":{"Type":28,"Flags":0,"Description":"Labels to be applied to the Kubernetes resources output by the resource"},"kind":{"Type":29,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"2":{"Name":"KubernetesMetadataExtensionAnnotations","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"KubernetesMetadataExtensionLabels","Properties":{},"AdditionalProperties":4}},{"6":{"Value":"kubernetesMetadata"}},{"2":{"Name":"KubernetesNamespaceExtension","Properties":{"namespace":{"Type":4,"Flags":1,"Description":"The namespace of the application environment."},"kind":{"Type":31,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"kubernetesNamespace"}},{"2":{"Name":"ManualScalingExtension","Properties":{"replicas":{"Type":3,"Flags":1,"Description":"Replica count."},"kind":{"Type":33,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"manualScaling"}},{"3":{"ItemType":20}},{"2":{"Name":"ResourceStatus","Properties":{"compute":{"Type":36,"Flags":0,"Description":"Represents backing compute resource"},"recipe":{"Type":43,"Flags":2,"Description":"Recipe status at deployment time for a resource."},"outputResources":{"Type":45,"Flags":0,"Description":"Properties of an output resource"}}}},{"7":{"Name":"EnvironmentCompute","Discriminator":"kind","BaseProperties":{"resourceId":{"Type":4,"Flags":0,"Description":"The resource id of the compute resource for application environment."},"identity":{"Type":37,"Flags":0,"Description":"IdentitySettings is the external identity setting."}},"Elements":{"kubernetes":41}}},{"2":{"Name":"IdentitySettings","Properties":{"kind":{"Type":40,"Flags":1,"Description":"IdentitySettingKind is the kind of supported external identity setting"},"oidcIssuer":{"Type":4,"Flags":0,"Description":"The URI for your compute platform's OIDC issuer"},"resource":{"Type":4,"Flags":0,"Description":"The resource ID of the provisioned identity"}}}},{"6":{"Value":"undefined"}},{"6":{"Value":"azure.com.workload"}},{"5":{"Elements":[38,39]}},{"2":{"Name":"KubernetesCompute","Properties":{"namespace":{"Type":4,"Flags":1,"Description":"The namespace to use for the environment."},"kind":{"Type":42,"Flags":1,"Description":"Discriminator property for EnvironmentCompute."}}}},{"6":{"Value":"kubernetes"}},{"2":{"Name":"RecipeStatus","Properties":{"templateKind":{"Type":4,"Flags":1,"Description":"TemplateKind is the kind of the recipe template used by the portable resource upon deployment."},"templatePath":{"Type":4,"Flags":1,"Description":"TemplatePath is the path of the recipe consumed by the portable resource upon deployment."},"templateVersion":{"Type":4,"Flags":0,"Description":"TemplateVersion is the version number of the template."}}}},{"2":{"Name":"OutputResource","Properties":{"localId":{"Type":4,"Flags":0,"Description":"The logical identifier scoped to the owning Radius resource. This is only needed or used when a resource has a dependency relationship. LocalIDs do not have any particular format or meaning beyond being compared to determine dependency relationships."},"id":{"Type":4,"Flags":0,"Description":"The UCP resource ID of the underlying resource."},"radiusManaged":{"Type":2,"Flags":0,"Description":"Determines whether Radius manages the lifecycle of the underlying resource."}}}},{"3":{"ItemType":44}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"SystemData","Properties":{"createdBy":{"Type":4,"Flags":0,"Description":"The identity that created the resource."},"createdByType":{"Type":52,"Flags":0,"Description":"The type of identity that created the resource."},"createdAt":{"Type":4,"Flags":0,"Description":"The timestamp of resource creation (UTC)."},"lastModifiedBy":{"Type":4,"Flags":0,"Description":"The identity that last modified the resource."},"lastModifiedByType":{"Type":57,"Flags":0,"Description":"The type of identity that created the resource."},"lastModifiedAt":{"Type":4,"Flags":0,"Description":"The timestamp of resource last modification (UTC)"}}}},{"6":{"Value":"User"}},{"6":{"Value":"Application"}},{"6":{"Value":"ManagedIdentity"}},{"6":{"Value":"Key"}},{"5":{"Elements":[48,49,50,51]}},{"6":{"Value":"User"}},{"6":{"Value":"Application"}},{"6":{"Value":"ManagedIdentity"}},{"6":{"Value":"Key"}},{"5":{"Elements":[53,54,55,56]}},{"4":{"Name":"Applications.Core/applications@2023-10-01-preview","ScopeType":0,"Body":10}},{"6":{"Value":"Applications.Core/containers"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/containers","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":59,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":60,"Flags":10,"Description":"The resource api version"},"properties":{"Type":62,"Flags":1,"Description":"Container properties"},"tags":{"Type":122,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ContainerProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":70,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"container":{"Type":71,"Flags":1,"Description":"Definition of a container"},"connections":{"Type":108,"Flags":0,"Description":"Specifies a connection to another resource."},"identity":{"Type":37,"Flags":0,"Description":"IdentitySettings is the external identity setting."},"extensions":{"Type":109,"Flags":0,"Description":"Extensions spec of the resource"},"resourceProvisioning":{"Type":112,"Flags":0,"Description":"Specifies how the underlying service/resource is provisioned and managed. Available values are 'internal', where Radius manages the lifecycle of the resource internally, and 'manual', where a user manages the resource."},"resources":{"Type":114,"Flags":0,"Description":"A collection of references to resources associated with the container"},"restartPolicy":{"Type":118,"Flags":0,"Description":"Restart policy for the container"},"runtimes":{"Type":119,"Flags":0,"Description":"The properties for runtime configuration"}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[63,64,65,66,67,68,69]}},{"2":{"Name":"Container","Properties":{"image":{"Type":4,"Flags":1,"Description":"The registry and image to download and run in your container"},"imagePullPolicy":{"Type":75,"Flags":0,"Description":"The image pull policy for the container"},"env":{"Type":76,"Flags":0,"Description":"environment"},"ports":{"Type":81,"Flags":0,"Description":"container ports"},"readinessProbe":{"Type":82,"Flags":0,"Description":"Properties for readiness/liveness probe"},"livenessProbe":{"Type":82,"Flags":0,"Description":"Properties for readiness/liveness probe"},"volumes":{"Type":101,"Flags":0,"Description":"container volumes"},"command":{"Type":102,"Flags":0,"Description":"Entrypoint array. Overrides the container image's ENTRYPOINT"},"args":{"Type":103,"Flags":0,"Description":"Arguments to the entrypoint. Overrides the container image's CMD"},"workingDir":{"Type":4,"Flags":0,"Description":"Working directory for the container"}}}},{"6":{"Value":"Always"}},{"6":{"Value":"IfNotPresent"}},{"6":{"Value":"Never"}},{"5":{"Elements":[72,73,74]}},{"2":{"Name":"ContainerEnv","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"ContainerPortProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"protocol":{"Type":80,"Flags":0,"Description":"The protocol in use by the port"},"provides":{"Type":4,"Flags":0,"Description":"Specifies a route provided by this port"},"scheme":{"Type":4,"Flags":0,"Description":"Specifies the URL scheme of the communication protocol. Consumers can use the scheme to construct a URL. The value defaults to 'http' or 'https' depending on the port value"},"port":{"Type":3,"Flags":0,"Description":"Specifies the port that will be exposed by this container. Must be set when value different from containerPort is desired"}}}},{"6":{"Value":"TCP"}},{"6":{"Value":"UDP"}},{"5":{"Elements":[78,79]}},{"2":{"Name":"ContainerPorts","Properties":{},"AdditionalProperties":77}},{"7":{"Name":"HealthProbeProperties","Discriminator":"kind","BaseProperties":{"initialDelaySeconds":{"Type":3,"Flags":0,"Description":"Initial delay in seconds before probing for readiness/liveness"},"failureThreshold":{"Type":3,"Flags":0,"Description":"Threshold number of times the probe fails after which a failure would be reported"},"periodSeconds":{"Type":3,"Flags":0,"Description":"Interval for the readiness/liveness probe in seconds"},"timeoutSeconds":{"Type":3,"Flags":0,"Description":"Number of seconds after which the readiness/liveness probe times out. Defaults to 5 seconds"}},"Elements":{"exec":83,"httpGet":85,"tcp":88}}},{"2":{"Name":"ExecHealthProbeProperties","Properties":{"command":{"Type":4,"Flags":1,"Description":"Command to execute to probe readiness/liveness"},"kind":{"Type":84,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"6":{"Value":"exec"}},{"2":{"Name":"HttpGetHealthProbeProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"path":{"Type":4,"Flags":1,"Description":"The route to make the HTTP request on"},"headers":{"Type":86,"Flags":0,"Description":"Custom HTTP headers to add to the get request"},"kind":{"Type":87,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"2":{"Name":"HttpGetHealthProbePropertiesHeaders","Properties":{},"AdditionalProperties":4}},{"6":{"Value":"httpGet"}},{"2":{"Name":"TcpHealthProbeProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"kind":{"Type":89,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"6":{"Value":"tcp"}},{"7":{"Name":"Volume","Discriminator":"kind","BaseProperties":{"mountPath":{"Type":4,"Flags":0,"Description":"The path where the volume is mounted"}},"Elements":{"ephemeral":91,"persistent":96}}},{"2":{"Name":"EphemeralVolume","Properties":{"managedStore":{"Type":94,"Flags":1,"Description":"The managed store for the ephemeral volume"},"kind":{"Type":95,"Flags":1,"Description":"Discriminator property for Volume."}}}},{"6":{"Value":"memory"}},{"6":{"Value":"disk"}},{"5":{"Elements":[92,93]}},{"6":{"Value":"ephemeral"}},{"2":{"Name":"PersistentVolume","Properties":{"permission":{"Type":99,"Flags":0,"Description":"The persistent volume permission"},"source":{"Type":4,"Flags":1,"Description":"The source of the volume"},"kind":{"Type":100,"Flags":1,"Description":"Discriminator property for Volume."}}}},{"6":{"Value":"read"}},{"6":{"Value":"write"}},{"5":{"Elements":[97,98]}},{"6":{"Value":"persistent"}},{"2":{"Name":"ContainerVolumes","Properties":{},"AdditionalProperties":90}},{"3":{"ItemType":4}},{"3":{"ItemType":4}},{"2":{"Name":"ConnectionProperties","Properties":{"source":{"Type":4,"Flags":1,"Description":"The source of the connection"},"disableDefaultEnvVars":{"Type":2,"Flags":0,"Description":"default environment variable override"},"iam":{"Type":105,"Flags":0,"Description":"IAM properties"}}}},{"2":{"Name":"IamProperties","Properties":{"kind":{"Type":106,"Flags":1,"Description":"The kind of IAM provider to configure"},"roles":{"Type":107,"Flags":0,"Description":"RBAC permissions to be assigned on the source resource"}}}},{"6":{"Value":"azure"}},{"3":{"ItemType":4}},{"2":{"Name":"ContainerPropertiesConnections","Properties":{},"AdditionalProperties":104}},{"3":{"ItemType":20}},{"6":{"Value":"internal"}},{"6":{"Value":"manual"}},{"5":{"Elements":[110,111]}},{"2":{"Name":"ResourceReference","Properties":{"id":{"Type":4,"Flags":1,"Description":"Resource id of an existing resource"}}}},{"3":{"ItemType":113}},{"6":{"Value":"Always"}},{"6":{"Value":"OnFailure"}},{"6":{"Value":"Never"}},{"5":{"Elements":[115,116,117]}},{"2":{"Name":"RuntimesProperties","Properties":{"kubernetes":{"Type":120,"Flags":0,"Description":"The runtime configuration properties for Kubernetes"}}}},{"2":{"Name":"KubernetesRuntimeProperties","Properties":{"base":{"Type":4,"Flags":0,"Description":"The serialized YAML manifest which represents the base Kubernetes resources to deploy, such as Deployment, Service, ServiceAccount, Secrets, and ConfigMaps."},"pod":{"Type":121,"Flags":0,"Description":"A strategic merge patch that will be applied to the PodSpec object when this container is being deployed."}}}},{"2":{"Name":"KubernetesPodSpec","Properties":{},"AdditionalProperties":0}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/containers@2023-10-01-preview","ScopeType":0,"Body":61}},{"6":{"Value":"Applications.Core/environments"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/environments","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":124,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":125,"Flags":10,"Description":"The resource api version"},"properties":{"Type":127,"Flags":1,"Description":"Environment properties"},"tags":{"Type":153,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"EnvironmentProperties","Properties":{"provisioningState":{"Type":135,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"compute":{"Type":36,"Flags":1,"Description":"Represents backing compute resource"},"providers":{"Type":136,"Flags":0,"Description":"The Cloud providers configuration"},"simulated":{"Type":2,"Flags":0,"Description":"Simulated environment."},"recipes":{"Type":145,"Flags":0,"Description":"Specifies Recipes linked to the Environment."},"recipeConfig":{"Type":146,"Flags":0,"Description":"Specifies recipe configurations needed for the recipes."},"extensions":{"Type":152,"Flags":0,"Description":"The environment extension."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[128,129,130,131,132,133,134]}},{"2":{"Name":"Providers","Properties":{"azure":{"Type":137,"Flags":0,"Description":"The Azure cloud provider definition"},"aws":{"Type":138,"Flags":0,"Description":"The AWS cloud provider definition"}}}},{"2":{"Name":"ProvidersAzure","Properties":{"scope":{"Type":4,"Flags":1,"Description":"Target scope for Azure resources to be deployed into. For example: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup'"}}}},{"2":{"Name":"ProvidersAws","Properties":{"scope":{"Type":4,"Flags":1,"Description":"Target scope for AWS resources to be deployed into. For example: '/planes/aws/aws/accounts/000000000000/regions/us-west-2'"}}}},{"7":{"Name":"RecipeProperties","Discriminator":"templateKind","BaseProperties":{"templatePath":{"Type":4,"Flags":1,"Description":"Path to the template provided by the recipe. Currently only link to Azure Container Registry is supported."},"parameters":{"Type":0,"Flags":0,"Description":"Any object"}},"Elements":{"bicep":140,"terraform":142}}},{"2":{"Name":"BicepRecipeProperties","Properties":{"plainHttp":{"Type":2,"Flags":0,"Description":"Connect to the Bicep registry using HTTP (not-HTTPS). This should be used when the registry is known not to support HTTPS, for example in a locally-hosted registry. Defaults to false (use HTTPS/TLS)."},"templateKind":{"Type":141,"Flags":1,"Description":"Discriminator property for RecipeProperties."}}}},{"6":{"Value":"bicep"}},{"2":{"Name":"TerraformRecipeProperties","Properties":{"templateVersion":{"Type":4,"Flags":0,"Description":"Version of the template to deploy. For Terraform recipes using a module registry this is required, but must be omitted for other module sources."},"templateKind":{"Type":143,"Flags":1,"Description":"Discriminator property for RecipeProperties."}}}},{"6":{"Value":"terraform"}},{"2":{"Name":"DictionaryOfRecipeProperties","Properties":{},"AdditionalProperties":139}},{"2":{"Name":"EnvironmentPropertiesRecipes","Properties":{},"AdditionalProperties":144}},{"2":{"Name":"RecipeConfigProperties","Properties":{"terraform":{"Type":147,"Flags":0,"Description":"Specifies the terraform config properties"}}}},{"2":{"Name":"TerraformConfigProperties","Properties":{"authentication":{"Type":148,"Flags":0,"Description":"Specifies authentication information needed to access private terraform module repositories."}}}},{"2":{"Name":"AuthConfig","Properties":{"git":{"Type":149,"Flags":0,"Description":"Specifies authentication information needed to access private terraform modules from Git repository sources."}}}},{"2":{"Name":"GitAuthConfig","Properties":{"pat":{"Type":151,"Flags":0,"Description":"Specifies the secret details of type personal access token for each different git platforms"}}}},{"2":{"Name":"SecretConfig","Properties":{"secret":{"Type":4,"Flags":0,"Description":"The resource id for the Applications.Core/SecretStore resource containing credentials."}}}},{"2":{"Name":"GitAuthConfigPat","Properties":{},"AdditionalProperties":150}},{"3":{"ItemType":20}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/environments@2023-10-01-preview","ScopeType":0,"Body":126}},{"6":{"Value":"Applications.Core/extenders"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/extenders","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":155,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":156,"Flags":10,"Description":"The resource api version"},"properties":{"Type":158,"Flags":1,"Description":"ExtenderResource portable resource properties"},"tags":{"Type":171,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ExtenderProperties","Properties":{"environment":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the environment that the portable resource is linked to"},"application":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the application that the portable resource is consumed by (if applicable)"},"provisioningState":{"Type":166,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"secrets":{"Type":0,"Flags":0,"Description":"Any object"},"recipe":{"Type":167,"Flags":0,"Description":"The recipe used to automatically deploy underlying infrastructure for a portable resource"},"resourceProvisioning":{"Type":170,"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."}},"AdditionalProperties":0}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[159,160,161,162,163,164,165]}},{"2":{"Name":"Recipe","Properties":{"name":{"Type":4,"Flags":1,"Description":"The name of the recipe within the environment to use"},"parameters":{"Type":0,"Flags":0,"Description":"Any object"}}}},{"6":{"Value":"recipe"}},{"6":{"Value":"manual"}},{"5":{"Elements":[168,169]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/extenders@2023-10-01-preview","ScopeType":0,"Body":157}},{"6":{"Value":"Applications.Core/gateways"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/gateways","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":173,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":174,"Flags":10,"Description":"The resource api version"},"properties":{"Type":176,"Flags":1,"Description":"Gateway properties"},"tags":{"Type":192,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"GatewayProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":184,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"internal":{"Type":2,"Flags":0,"Description":"Sets Gateway to not be exposed externally (no public IP address associated). Defaults to false (exposed to internet)."},"hostname":{"Type":185,"Flags":0,"Description":"Declare hostname information for the Gateway. Leaving the hostname empty auto-assigns one: mygateway.myapp.PUBLICHOSTNAMEORIP.nip.io."},"routes":{"Type":187,"Flags":1,"Description":"Routes attached to this Gateway"},"tls":{"Type":188,"Flags":0,"Description":"TLS configuration definition for Gateway resource."},"url":{"Type":4,"Flags":2,"Description":"URL of the gateway resource. Readonly"}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[177,178,179,180,181,182,183]}},{"2":{"Name":"GatewayHostname","Properties":{"prefix":{"Type":4,"Flags":0,"Description":"Specify a prefix for the hostname: myhostname.myapp.PUBLICHOSTNAMEORIP.nip.io. Mutually exclusive with 'fullyQualifiedHostname' and will be overridden if both are defined."},"fullyQualifiedHostname":{"Type":4,"Flags":0,"Description":"Specify a fully-qualified domain name: myapp.mydomain.com. Mutually exclusive with 'prefix' and will take priority if both are defined."}}}},{"2":{"Name":"GatewayRoute","Properties":{"path":{"Type":4,"Flags":0,"Description":"The path to match the incoming request path on. Ex - /myservice."},"destination":{"Type":4,"Flags":0,"Description":"The HttpRoute to route to. Ex - myserviceroute.id."},"replacePrefix":{"Type":4,"Flags":0,"Description":"Optionally update the prefix when sending the request to the service. Ex - replacePrefix: '/' and path: '/myservice' will transform '/myservice/myroute' to '/myroute'"}}}},{"3":{"ItemType":186}},{"2":{"Name":"GatewayTls","Properties":{"sslPassthrough":{"Type":2,"Flags":0,"Description":"If true, gateway lets the https traffic sslPassthrough to the backend servers for decryption."},"minimumProtocolVersion":{"Type":191,"Flags":0,"Description":"Tls Minimum versions for Gateway resource."},"certificateFrom":{"Type":4,"Flags":0,"Description":"The resource id for the secret containing the TLS certificate and key for the gateway."}}}},{"6":{"Value":"1.2"}},{"6":{"Value":"1.3"}},{"5":{"Elements":[189,190]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/gateways@2023-10-01-preview","ScopeType":0,"Body":175}},{"6":{"Value":"Applications.Core/httpRoutes"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/httpRoutes","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":194,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":195,"Flags":10,"Description":"The resource api version"},"properties":{"Type":197,"Flags":1,"Description":"HTTPRoute properties"},"tags":{"Type":206,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"HttpRouteProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":205,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"hostname":{"Type":4,"Flags":0,"Description":"The internal hostname accepting traffic for the HTTP Route. Readonly."},"port":{"Type":3,"Flags":0,"Description":"The port number for the HTTP Route. Defaults to 80. Readonly."},"scheme":{"Type":4,"Flags":2,"Description":"The scheme used for traffic. Readonly."},"url":{"Type":4,"Flags":2,"Description":"A stable URL that that can be used to route traffic to a resource. Readonly."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[198,199,200,201,202,203,204]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/httpRoutes@2023-10-01-preview","ScopeType":0,"Body":196}},{"6":{"Value":"Applications.Core/secretStores"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/secretStores","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":208,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":209,"Flags":10,"Description":"The resource api version"},"properties":{"Type":211,"Flags":1,"Description":"The properties of SecretStore"},"tags":{"Type":229,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"SecretStoreProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":219,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"type":{"Type":222,"Flags":0,"Description":"The type of SecretStore data"},"data":{"Type":228,"Flags":1,"Description":"An object to represent key-value type secrets"},"resource":{"Type":4,"Flags":0,"Description":"The resource id of external secret store."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[212,213,214,215,216,217,218]}},{"6":{"Value":"generic"}},{"6":{"Value":"certificate"}},{"5":{"Elements":[220,221]}},{"2":{"Name":"SecretValueProperties","Properties":{"encoding":{"Type":226,"Flags":0,"Description":"The type of SecretValue Encoding"},"value":{"Type":4,"Flags":0,"Description":"The value of secret."},"valueFrom":{"Type":227,"Flags":0,"Description":"The Secret value source properties"}}}},{"6":{"Value":"raw"}},{"6":{"Value":"base64"}},{"5":{"Elements":[224,225]}},{"2":{"Name":"ValueFromProperties","Properties":{"name":{"Type":4,"Flags":1,"Description":"The name of the referenced secret."},"version":{"Type":4,"Flags":0,"Description":"The version of the referenced secret."}}}},{"2":{"Name":"SecretStorePropertiesData","Properties":{},"AdditionalProperties":223}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/secretStores@2023-10-01-preview","ScopeType":0,"Body":210}},{"6":{"Value":"Applications.Core/volumes"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/volumes","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":231,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":232,"Flags":10,"Description":"The resource api version"},"properties":{"Type":234,"Flags":1,"Description":"Volume properties"},"tags":{"Type":266,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"7":{"Name":"VolumeProperties","Discriminator":"kind","BaseProperties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":242,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."}},"Elements":{"azure.com.keyvault":243}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[235,236,237,238,239,240,241]}},{"2":{"Name":"AzureKeyVaultVolumeProperties","Properties":{"certificates":{"Type":256,"Flags":0,"Description":"The KeyVault certificates that this volume exposes"},"keys":{"Type":258,"Flags":0,"Description":"The KeyVault keys that this volume exposes"},"resource":{"Type":4,"Flags":1,"Description":"The ID of the keyvault to use for this volume resource"},"secrets":{"Type":264,"Flags":0,"Description":"The KeyVault secrets that this volume exposes"},"kind":{"Type":265,"Flags":1,"Description":"Discriminator property for VolumeProperties."}}}},{"2":{"Name":"CertificateObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"encoding":{"Type":248,"Flags":0,"Description":"Represents secret encodings"},"format":{"Type":251,"Flags":0,"Description":"Represents certificate formats"},"name":{"Type":4,"Flags":1,"Description":"The name of the certificate"},"certType":{"Type":255,"Flags":0,"Description":"Represents certificate types"},"version":{"Type":4,"Flags":0,"Description":"Certificate version"}}}},{"6":{"Value":"utf-8"}},{"6":{"Value":"hex"}},{"6":{"Value":"base64"}},{"5":{"Elements":[245,246,247]}},{"6":{"Value":"pem"}},{"6":{"Value":"pfx"}},{"5":{"Elements":[249,250]}},{"6":{"Value":"certificate"}},{"6":{"Value":"privatekey"}},{"6":{"Value":"publickey"}},{"5":{"Elements":[252,253,254]}},{"2":{"Name":"AzureKeyVaultVolumePropertiesCertificates","Properties":{},"AdditionalProperties":244}},{"2":{"Name":"KeyObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"name":{"Type":4,"Flags":1,"Description":"The name of the key"},"version":{"Type":4,"Flags":0,"Description":"Key version"}}}},{"2":{"Name":"AzureKeyVaultVolumePropertiesKeys","Properties":{},"AdditionalProperties":257}},{"2":{"Name":"SecretObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"encoding":{"Type":263,"Flags":0,"Description":"Represents secret encodings"},"name":{"Type":4,"Flags":1,"Description":"The name of the secret"},"version":{"Type":4,"Flags":0,"Description":"secret version"}}}},{"6":{"Value":"utf-8"}},{"6":{"Value":"hex"}},{"6":{"Value":"base64"}},{"5":{"Elements":[260,261,262]}},{"2":{"Name":"AzureKeyVaultVolumePropertiesSecrets","Properties":{},"AdditionalProperties":259}},{"6":{"Value":"azure.com.keyvault"}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/volumes@2023-10-01-preview","ScopeType":0,"Body":233}},{"8":{"Name":"listSecrets","ResourceType":"Applications.Core/extenders","ApiVersion":"2023-10-01-preview","Output":0,"Input":0}},{"2":{"Name":"SecretStoreListSecretsResult","Properties":{"type":{"Type":272,"Flags":2,"Description":"The type of SecretStore data"},"data":{"Type":273,"Flags":2,"Description":"An object to represent key-value type secrets"}}}},{"6":{"Value":"generic"}},{"6":{"Value":"certificate"}},{"5":{"Elements":[270,271]}},{"2":{"Name":"SecretStoreListSecretsResultData","Properties":{},"AdditionalProperties":223}},{"8":{"Name":"listSecrets","ResourceType":"Applications.Core/secretStores","ApiVersion":"2023-10-01-preview","Output":269,"Input":0}}] \ No newline at end of file +[{"1":{"Kind":1}},{"1":{"Kind":2}},{"1":{"Kind":3}},{"1":{"Kind":4}},{"1":{"Kind":5}},{"1":{"Kind":6}},{"1":{"Kind":7}},{"1":{"Kind":8}},{"6":{"Value":"Applications.Core/applications"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/applications","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":8,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":9,"Flags":10,"Description":"The resource api version"},"properties":{"Type":11,"Flags":1,"Description":"Application properties"},"tags":{"Type":46,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ApplicationProperties","Properties":{"provisioningState":{"Type":19,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"environment":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"extensions":{"Type":34,"Flags":0,"Description":"The application extension."},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[12,13,14,15,16,17,18]}},{"7":{"Name":"Extension","Discriminator":"kind","BaseProperties":{},"Elements":{"daprSidecar":21,"kubernetesMetadata":26,"kubernetesNamespace":30,"manualScaling":32}}},{"2":{"Name":"DaprSidecarExtension","Properties":{"appPort":{"Type":3,"Flags":0,"Description":"The Dapr appPort. Specifies the internal listening port for the application to handle requests from the Dapr sidecar."},"appId":{"Type":4,"Flags":1,"Description":"The Dapr appId. Specifies the identifier used by Dapr for service invocation."},"config":{"Type":4,"Flags":0,"Description":"Specifies the Dapr configuration to use for the resource."},"protocol":{"Type":24,"Flags":0,"Description":"The Dapr sidecar extension protocol"},"kind":{"Type":25,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"http"}},{"6":{"Value":"grpc"}},{"5":{"Elements":[22,23]}},{"6":{"Value":"daprSidecar"}},{"2":{"Name":"KubernetesMetadataExtension","Properties":{"annotations":{"Type":27,"Flags":0,"Description":"Annotations to be applied to the Kubernetes resources output by the resource"},"labels":{"Type":28,"Flags":0,"Description":"Labels to be applied to the Kubernetes resources output by the resource"},"kind":{"Type":29,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"2":{"Name":"KubernetesMetadataExtensionAnnotations","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"KubernetesMetadataExtensionLabels","Properties":{},"AdditionalProperties":4}},{"6":{"Value":"kubernetesMetadata"}},{"2":{"Name":"KubernetesNamespaceExtension","Properties":{"namespace":{"Type":4,"Flags":1,"Description":"The namespace of the application environment."},"kind":{"Type":31,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"kubernetesNamespace"}},{"2":{"Name":"ManualScalingExtension","Properties":{"replicas":{"Type":3,"Flags":1,"Description":"Replica count."},"kind":{"Type":33,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"manualScaling"}},{"3":{"ItemType":20}},{"2":{"Name":"ResourceStatus","Properties":{"compute":{"Type":36,"Flags":0,"Description":"Represents backing compute resource"},"recipe":{"Type":43,"Flags":2,"Description":"Recipe status at deployment time for a resource."},"outputResources":{"Type":45,"Flags":0,"Description":"Properties of an output resource"}}}},{"7":{"Name":"EnvironmentCompute","Discriminator":"kind","BaseProperties":{"resourceId":{"Type":4,"Flags":0,"Description":"The resource id of the compute resource for application environment."},"identity":{"Type":37,"Flags":0,"Description":"IdentitySettings is the external identity setting."}},"Elements":{"kubernetes":41}}},{"2":{"Name":"IdentitySettings","Properties":{"kind":{"Type":40,"Flags":1,"Description":"IdentitySettingKind is the kind of supported external identity setting"},"oidcIssuer":{"Type":4,"Flags":0,"Description":"The URI for your compute platform's OIDC issuer"},"resource":{"Type":4,"Flags":0,"Description":"The resource ID of the provisioned identity"}}}},{"6":{"Value":"undefined"}},{"6":{"Value":"azure.com.workload"}},{"5":{"Elements":[38,39]}},{"2":{"Name":"KubernetesCompute","Properties":{"namespace":{"Type":4,"Flags":1,"Description":"The namespace to use for the environment."},"kind":{"Type":42,"Flags":1,"Description":"Discriminator property for EnvironmentCompute."}}}},{"6":{"Value":"kubernetes"}},{"2":{"Name":"RecipeStatus","Properties":{"templateKind":{"Type":4,"Flags":1,"Description":"TemplateKind is the kind of the recipe template used by the portable resource upon deployment."},"templatePath":{"Type":4,"Flags":1,"Description":"TemplatePath is the path of the recipe consumed by the portable resource upon deployment."},"templateVersion":{"Type":4,"Flags":0,"Description":"TemplateVersion is the version number of the template."}}}},{"2":{"Name":"OutputResource","Properties":{"localId":{"Type":4,"Flags":0,"Description":"The logical identifier scoped to the owning Radius resource. This is only needed or used when a resource has a dependency relationship. LocalIDs do not have any particular format or meaning beyond being compared to determine dependency relationships."},"id":{"Type":4,"Flags":0,"Description":"The UCP resource ID of the underlying resource."},"radiusManaged":{"Type":2,"Flags":0,"Description":"Determines whether Radius manages the lifecycle of the underlying resource."}}}},{"3":{"ItemType":44}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"SystemData","Properties":{"createdBy":{"Type":4,"Flags":0,"Description":"The identity that created the resource."},"createdByType":{"Type":52,"Flags":0,"Description":"The type of identity that created the resource."},"createdAt":{"Type":4,"Flags":0,"Description":"The timestamp of resource creation (UTC)."},"lastModifiedBy":{"Type":4,"Flags":0,"Description":"The identity that last modified the resource."},"lastModifiedByType":{"Type":57,"Flags":0,"Description":"The type of identity that created the resource."},"lastModifiedAt":{"Type":4,"Flags":0,"Description":"The timestamp of resource last modification (UTC)"}}}},{"6":{"Value":"User"}},{"6":{"Value":"Application"}},{"6":{"Value":"ManagedIdentity"}},{"6":{"Value":"Key"}},{"5":{"Elements":[48,49,50,51]}},{"6":{"Value":"User"}},{"6":{"Value":"Application"}},{"6":{"Value":"ManagedIdentity"}},{"6":{"Value":"Key"}},{"5":{"Elements":[53,54,55,56]}},{"4":{"Name":"Applications.Core/applications@2023-10-01-preview","ScopeType":0,"Body":10}},{"6":{"Value":"Applications.Core/containers"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/containers","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":59,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":60,"Flags":10,"Description":"The resource api version"},"properties":{"Type":62,"Flags":1,"Description":"Container properties"},"tags":{"Type":122,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ContainerProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":70,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"container":{"Type":71,"Flags":1,"Description":"Definition of a container"},"connections":{"Type":108,"Flags":0,"Description":"Specifies a connection to another resource."},"identity":{"Type":37,"Flags":0,"Description":"IdentitySettings is the external identity setting."},"extensions":{"Type":109,"Flags":0,"Description":"Extensions spec of the resource"},"resourceProvisioning":{"Type":112,"Flags":0,"Description":"Specifies how the underlying service/resource is provisioned and managed. Available values are 'internal', where Radius manages the lifecycle of the resource internally, and 'manual', where a user manages the resource."},"resources":{"Type":114,"Flags":0,"Description":"A collection of references to resources associated with the container"},"restartPolicy":{"Type":118,"Flags":0,"Description":"Restart policy for the container"},"runtimes":{"Type":119,"Flags":0,"Description":"The properties for runtime configuration"}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[63,64,65,66,67,68,69]}},{"2":{"Name":"Container","Properties":{"image":{"Type":4,"Flags":1,"Description":"The registry and image to download and run in your container"},"imagePullPolicy":{"Type":75,"Flags":0,"Description":"The image pull policy for the container"},"env":{"Type":76,"Flags":0,"Description":"environment"},"ports":{"Type":81,"Flags":0,"Description":"container ports"},"readinessProbe":{"Type":82,"Flags":0,"Description":"Properties for readiness/liveness probe"},"livenessProbe":{"Type":82,"Flags":0,"Description":"Properties for readiness/liveness probe"},"volumes":{"Type":101,"Flags":0,"Description":"container volumes"},"command":{"Type":102,"Flags":0,"Description":"Entrypoint array. Overrides the container image's ENTRYPOINT"},"args":{"Type":103,"Flags":0,"Description":"Arguments to the entrypoint. Overrides the container image's CMD"},"workingDir":{"Type":4,"Flags":0,"Description":"Working directory for the container"}}}},{"6":{"Value":"Always"}},{"6":{"Value":"IfNotPresent"}},{"6":{"Value":"Never"}},{"5":{"Elements":[72,73,74]}},{"2":{"Name":"ContainerEnv","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"ContainerPortProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"protocol":{"Type":80,"Flags":0,"Description":"The protocol in use by the port"},"provides":{"Type":4,"Flags":0,"Description":"Specifies a route provided by this port"},"scheme":{"Type":4,"Flags":0,"Description":"Specifies the URL scheme of the communication protocol. Consumers can use the scheme to construct a URL. The value defaults to 'http' or 'https' depending on the port value"},"port":{"Type":3,"Flags":0,"Description":"Specifies the port that will be exposed by this container. Must be set when value different from containerPort is desired"}}}},{"6":{"Value":"TCP"}},{"6":{"Value":"UDP"}},{"5":{"Elements":[78,79]}},{"2":{"Name":"ContainerPorts","Properties":{},"AdditionalProperties":77}},{"7":{"Name":"HealthProbeProperties","Discriminator":"kind","BaseProperties":{"initialDelaySeconds":{"Type":3,"Flags":0,"Description":"Initial delay in seconds before probing for readiness/liveness"},"failureThreshold":{"Type":3,"Flags":0,"Description":"Threshold number of times the probe fails after which a failure would be reported"},"periodSeconds":{"Type":3,"Flags":0,"Description":"Interval for the readiness/liveness probe in seconds"},"timeoutSeconds":{"Type":3,"Flags":0,"Description":"Number of seconds after which the readiness/liveness probe times out. Defaults to 5 seconds"}},"Elements":{"exec":83,"httpGet":85,"tcp":88}}},{"2":{"Name":"ExecHealthProbeProperties","Properties":{"command":{"Type":4,"Flags":1,"Description":"Command to execute to probe readiness/liveness"},"kind":{"Type":84,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"6":{"Value":"exec"}},{"2":{"Name":"HttpGetHealthProbeProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"path":{"Type":4,"Flags":1,"Description":"The route to make the HTTP request on"},"headers":{"Type":86,"Flags":0,"Description":"Custom HTTP headers to add to the get request"},"kind":{"Type":87,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"2":{"Name":"HttpGetHealthProbePropertiesHeaders","Properties":{},"AdditionalProperties":4}},{"6":{"Value":"httpGet"}},{"2":{"Name":"TcpHealthProbeProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"kind":{"Type":89,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"6":{"Value":"tcp"}},{"7":{"Name":"Volume","Discriminator":"kind","BaseProperties":{"mountPath":{"Type":4,"Flags":0,"Description":"The path where the volume is mounted"}},"Elements":{"ephemeral":91,"persistent":96}}},{"2":{"Name":"EphemeralVolume","Properties":{"managedStore":{"Type":94,"Flags":1,"Description":"The managed store for the ephemeral volume"},"kind":{"Type":95,"Flags":1,"Description":"Discriminator property for Volume."}}}},{"6":{"Value":"memory"}},{"6":{"Value":"disk"}},{"5":{"Elements":[92,93]}},{"6":{"Value":"ephemeral"}},{"2":{"Name":"PersistentVolume","Properties":{"permission":{"Type":99,"Flags":0,"Description":"The persistent volume permission"},"source":{"Type":4,"Flags":1,"Description":"The source of the volume"},"kind":{"Type":100,"Flags":1,"Description":"Discriminator property for Volume."}}}},{"6":{"Value":"read"}},{"6":{"Value":"write"}},{"5":{"Elements":[97,98]}},{"6":{"Value":"persistent"}},{"2":{"Name":"ContainerVolumes","Properties":{},"AdditionalProperties":90}},{"3":{"ItemType":4}},{"3":{"ItemType":4}},{"2":{"Name":"ConnectionProperties","Properties":{"source":{"Type":4,"Flags":1,"Description":"The source of the connection"},"disableDefaultEnvVars":{"Type":2,"Flags":0,"Description":"default environment variable override"},"iam":{"Type":105,"Flags":0,"Description":"IAM properties"}}}},{"2":{"Name":"IamProperties","Properties":{"kind":{"Type":106,"Flags":1,"Description":"The kind of IAM provider to configure"},"roles":{"Type":107,"Flags":0,"Description":"RBAC permissions to be assigned on the source resource"}}}},{"6":{"Value":"azure"}},{"3":{"ItemType":4}},{"2":{"Name":"ContainerPropertiesConnections","Properties":{},"AdditionalProperties":104}},{"3":{"ItemType":20}},{"6":{"Value":"internal"}},{"6":{"Value":"manual"}},{"5":{"Elements":[110,111]}},{"2":{"Name":"ResourceReference","Properties":{"id":{"Type":4,"Flags":1,"Description":"Resource id of an existing resource"}}}},{"3":{"ItemType":113}},{"6":{"Value":"Always"}},{"6":{"Value":"OnFailure"}},{"6":{"Value":"Never"}},{"5":{"Elements":[115,116,117]}},{"2":{"Name":"RuntimesProperties","Properties":{"kubernetes":{"Type":120,"Flags":0,"Description":"The runtime configuration properties for Kubernetes"}}}},{"2":{"Name":"KubernetesRuntimeProperties","Properties":{"base":{"Type":4,"Flags":0,"Description":"The serialized YAML manifest which represents the base Kubernetes resources to deploy, such as Deployment, Service, ServiceAccount, Secrets, and ConfigMaps."},"pod":{"Type":121,"Flags":0,"Description":"A strategic merge patch that will be applied to the PodSpec object when this container is being deployed."}}}},{"2":{"Name":"KubernetesPodSpec","Properties":{},"AdditionalProperties":0}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/containers@2023-10-01-preview","ScopeType":0,"Body":61}},{"6":{"Value":"Applications.Core/environments"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/environments","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":124,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":125,"Flags":10,"Description":"The resource api version"},"properties":{"Type":127,"Flags":1,"Description":"Environment properties"},"tags":{"Type":153,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"EnvironmentProperties","Properties":{"provisioningState":{"Type":135,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"compute":{"Type":36,"Flags":1,"Description":"Represents backing compute resource"},"providers":{"Type":136,"Flags":0,"Description":"The Cloud providers configuration"},"simulated":{"Type":2,"Flags":0,"Description":"Simulated environment."},"recipes":{"Type":145,"Flags":0,"Description":"Specifies Recipes linked to the Environment."},"recipeConfig":{"Type":146,"Flags":0,"Description":"Configuration for Recipes. Defines how each type of Recipe should be configured and run."},"extensions":{"Type":152,"Flags":0,"Description":"The environment extension."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[128,129,130,131,132,133,134]}},{"2":{"Name":"Providers","Properties":{"azure":{"Type":137,"Flags":0,"Description":"The Azure cloud provider definition"},"aws":{"Type":138,"Flags":0,"Description":"The AWS cloud provider definition"}}}},{"2":{"Name":"ProvidersAzure","Properties":{"scope":{"Type":4,"Flags":1,"Description":"Target scope for Azure resources to be deployed into. For example: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup'"}}}},{"2":{"Name":"ProvidersAws","Properties":{"scope":{"Type":4,"Flags":1,"Description":"Target scope for AWS resources to be deployed into. For example: '/planes/aws/aws/accounts/000000000000/regions/us-west-2'"}}}},{"7":{"Name":"RecipeProperties","Discriminator":"templateKind","BaseProperties":{"templatePath":{"Type":4,"Flags":1,"Description":"Path to the template provided by the recipe. Currently only link to Azure Container Registry is supported."},"parameters":{"Type":0,"Flags":0,"Description":"Any object"}},"Elements":{"bicep":140,"terraform":142}}},{"2":{"Name":"BicepRecipeProperties","Properties":{"plainHttp":{"Type":2,"Flags":0,"Description":"Connect to the Bicep registry using HTTP (not-HTTPS). This should be used when the registry is known not to support HTTPS, for example in a locally-hosted registry. Defaults to false (use HTTPS/TLS)."},"templateKind":{"Type":141,"Flags":1,"Description":"Discriminator property for RecipeProperties."}}}},{"6":{"Value":"bicep"}},{"2":{"Name":"TerraformRecipeProperties","Properties":{"templateVersion":{"Type":4,"Flags":0,"Description":"Version of the template to deploy. For Terraform recipes using a module registry this is required, but must be omitted for other module sources."},"templateKind":{"Type":143,"Flags":1,"Description":"Discriminator property for RecipeProperties."}}}},{"6":{"Value":"terraform"}},{"2":{"Name":"DictionaryOfRecipeProperties","Properties":{},"AdditionalProperties":139}},{"2":{"Name":"EnvironmentPropertiesRecipes","Properties":{},"AdditionalProperties":144}},{"2":{"Name":"RecipeConfigProperties","Properties":{"terraform":{"Type":147,"Flags":0,"Description":"Configuration for Terraform Recipes. Controls how Terraform plans and applies templates as part of Recipe deployment."}}}},{"2":{"Name":"TerraformConfigProperties","Properties":{"authentication":{"Type":148,"Flags":0,"Description":"Authentication information used to access private Terraform module sources. Supported module sources: Git."}}}},{"2":{"Name":"AuthConfig","Properties":{"git":{"Type":149,"Flags":0,"Description":"Authentication information used to access private Terraform modules from Git repository sources."}}}},{"2":{"Name":"GitAuthConfig","Properties":{"pat":{"Type":151,"Flags":0,"Description":"Personal Access Token (PAT) configuration used to authenticate to Git platforms."}}}},{"2":{"Name":"SecretConfig","Properties":{"secret":{"Type":4,"Flags":0,"Description":"The resource id for the Applications.Core/SecretStore resource containing credentials. Secret names required: 'pat', and 'username' is optional"}}}},{"2":{"Name":"GitAuthConfigPat","Properties":{},"AdditionalProperties":150}},{"3":{"ItemType":20}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/environments@2023-10-01-preview","ScopeType":0,"Body":126}},{"6":{"Value":"Applications.Core/extenders"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/extenders","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":155,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":156,"Flags":10,"Description":"The resource api version"},"properties":{"Type":158,"Flags":1,"Description":"ExtenderResource portable resource properties"},"tags":{"Type":171,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ExtenderProperties","Properties":{"environment":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the environment that the portable resource is linked to"},"application":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the application that the portable resource is consumed by (if applicable)"},"provisioningState":{"Type":166,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"secrets":{"Type":0,"Flags":0,"Description":"Any object"},"recipe":{"Type":167,"Flags":0,"Description":"The recipe used to automatically deploy underlying infrastructure for a portable resource"},"resourceProvisioning":{"Type":170,"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."}},"AdditionalProperties":0}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[159,160,161,162,163,164,165]}},{"2":{"Name":"Recipe","Properties":{"name":{"Type":4,"Flags":1,"Description":"The name of the recipe within the environment to use"},"parameters":{"Type":0,"Flags":0,"Description":"Any object"}}}},{"6":{"Value":"recipe"}},{"6":{"Value":"manual"}},{"5":{"Elements":[168,169]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/extenders@2023-10-01-preview","ScopeType":0,"Body":157}},{"6":{"Value":"Applications.Core/gateways"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/gateways","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":173,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":174,"Flags":10,"Description":"The resource api version"},"properties":{"Type":176,"Flags":1,"Description":"Gateway properties"},"tags":{"Type":192,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"GatewayProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":184,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"internal":{"Type":2,"Flags":0,"Description":"Sets Gateway to not be exposed externally (no public IP address associated). Defaults to false (exposed to internet)."},"hostname":{"Type":185,"Flags":0,"Description":"Declare hostname information for the Gateway. Leaving the hostname empty auto-assigns one: mygateway.myapp.PUBLICHOSTNAMEORIP.nip.io."},"routes":{"Type":187,"Flags":1,"Description":"Routes attached to this Gateway"},"tls":{"Type":188,"Flags":0,"Description":"TLS configuration definition for Gateway resource."},"url":{"Type":4,"Flags":2,"Description":"URL of the gateway resource. Readonly"}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[177,178,179,180,181,182,183]}},{"2":{"Name":"GatewayHostname","Properties":{"prefix":{"Type":4,"Flags":0,"Description":"Specify a prefix for the hostname: myhostname.myapp.PUBLICHOSTNAMEORIP.nip.io. Mutually exclusive with 'fullyQualifiedHostname' and will be overridden if both are defined."},"fullyQualifiedHostname":{"Type":4,"Flags":0,"Description":"Specify a fully-qualified domain name: myapp.mydomain.com. Mutually exclusive with 'prefix' and will take priority if both are defined."}}}},{"2":{"Name":"GatewayRoute","Properties":{"path":{"Type":4,"Flags":0,"Description":"The path to match the incoming request path on. Ex - /myservice."},"destination":{"Type":4,"Flags":0,"Description":"The HttpRoute to route to. Ex - myserviceroute.id."},"replacePrefix":{"Type":4,"Flags":0,"Description":"Optionally update the prefix when sending the request to the service. Ex - replacePrefix: '/' and path: '/myservice' will transform '/myservice/myroute' to '/myroute'"}}}},{"3":{"ItemType":186}},{"2":{"Name":"GatewayTls","Properties":{"sslPassthrough":{"Type":2,"Flags":0,"Description":"If true, gateway lets the https traffic sslPassthrough to the backend servers for decryption."},"minimumProtocolVersion":{"Type":191,"Flags":0,"Description":"Tls Minimum versions for Gateway resource."},"certificateFrom":{"Type":4,"Flags":0,"Description":"The resource id for the secret containing the TLS certificate and key for the gateway."}}}},{"6":{"Value":"1.2"}},{"6":{"Value":"1.3"}},{"5":{"Elements":[189,190]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/gateways@2023-10-01-preview","ScopeType":0,"Body":175}},{"6":{"Value":"Applications.Core/httpRoutes"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/httpRoutes","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":194,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":195,"Flags":10,"Description":"The resource api version"},"properties":{"Type":197,"Flags":1,"Description":"HTTPRoute properties"},"tags":{"Type":206,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"HttpRouteProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":205,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"hostname":{"Type":4,"Flags":0,"Description":"The internal hostname accepting traffic for the HTTP Route. Readonly."},"port":{"Type":3,"Flags":0,"Description":"The port number for the HTTP Route. Defaults to 80. Readonly."},"scheme":{"Type":4,"Flags":2,"Description":"The scheme used for traffic. Readonly."},"url":{"Type":4,"Flags":2,"Description":"A stable URL that that can be used to route traffic to a resource. Readonly."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[198,199,200,201,202,203,204]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/httpRoutes@2023-10-01-preview","ScopeType":0,"Body":196}},{"6":{"Value":"Applications.Core/secretStores"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/secretStores","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":208,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":209,"Flags":10,"Description":"The resource api version"},"properties":{"Type":211,"Flags":1,"Description":"The properties of SecretStore"},"tags":{"Type":229,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"SecretStoreProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":219,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"type":{"Type":222,"Flags":0,"Description":"The type of SecretStore data"},"data":{"Type":228,"Flags":1,"Description":"An object to represent key-value type secrets"},"resource":{"Type":4,"Flags":0,"Description":"The resource id of external secret store."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[212,213,214,215,216,217,218]}},{"6":{"Value":"generic"}},{"6":{"Value":"certificate"}},{"5":{"Elements":[220,221]}},{"2":{"Name":"SecretValueProperties","Properties":{"encoding":{"Type":226,"Flags":0,"Description":"The type of SecretValue Encoding"},"value":{"Type":4,"Flags":0,"Description":"The value of secret."},"valueFrom":{"Type":227,"Flags":0,"Description":"The Secret value source properties"}}}},{"6":{"Value":"raw"}},{"6":{"Value":"base64"}},{"5":{"Elements":[224,225]}},{"2":{"Name":"ValueFromProperties","Properties":{"name":{"Type":4,"Flags":1,"Description":"The name of the referenced secret."},"version":{"Type":4,"Flags":0,"Description":"The version of the referenced secret."}}}},{"2":{"Name":"SecretStorePropertiesData","Properties":{},"AdditionalProperties":223}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/secretStores@2023-10-01-preview","ScopeType":0,"Body":210}},{"6":{"Value":"Applications.Core/volumes"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/volumes","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":231,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":232,"Flags":10,"Description":"The resource api version"},"properties":{"Type":234,"Flags":1,"Description":"Volume properties"},"tags":{"Type":266,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"7":{"Name":"VolumeProperties","Discriminator":"kind","BaseProperties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":242,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."}},"Elements":{"azure.com.keyvault":243}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[235,236,237,238,239,240,241]}},{"2":{"Name":"AzureKeyVaultVolumeProperties","Properties":{"certificates":{"Type":256,"Flags":0,"Description":"The KeyVault certificates that this volume exposes"},"keys":{"Type":258,"Flags":0,"Description":"The KeyVault keys that this volume exposes"},"resource":{"Type":4,"Flags":1,"Description":"The ID of the keyvault to use for this volume resource"},"secrets":{"Type":264,"Flags":0,"Description":"The KeyVault secrets that this volume exposes"},"kind":{"Type":265,"Flags":1,"Description":"Discriminator property for VolumeProperties."}}}},{"2":{"Name":"CertificateObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"encoding":{"Type":248,"Flags":0,"Description":"Represents secret encodings"},"format":{"Type":251,"Flags":0,"Description":"Represents certificate formats"},"name":{"Type":4,"Flags":1,"Description":"The name of the certificate"},"certType":{"Type":255,"Flags":0,"Description":"Represents certificate types"},"version":{"Type":4,"Flags":0,"Description":"Certificate version"}}}},{"6":{"Value":"utf-8"}},{"6":{"Value":"hex"}},{"6":{"Value":"base64"}},{"5":{"Elements":[245,246,247]}},{"6":{"Value":"pem"}},{"6":{"Value":"pfx"}},{"5":{"Elements":[249,250]}},{"6":{"Value":"certificate"}},{"6":{"Value":"privatekey"}},{"6":{"Value":"publickey"}},{"5":{"Elements":[252,253,254]}},{"2":{"Name":"AzureKeyVaultVolumePropertiesCertificates","Properties":{},"AdditionalProperties":244}},{"2":{"Name":"KeyObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"name":{"Type":4,"Flags":1,"Description":"The name of the key"},"version":{"Type":4,"Flags":0,"Description":"Key version"}}}},{"2":{"Name":"AzureKeyVaultVolumePropertiesKeys","Properties":{},"AdditionalProperties":257}},{"2":{"Name":"SecretObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"encoding":{"Type":263,"Flags":0,"Description":"Represents secret encodings"},"name":{"Type":4,"Flags":1,"Description":"The name of the secret"},"version":{"Type":4,"Flags":0,"Description":"secret version"}}}},{"6":{"Value":"utf-8"}},{"6":{"Value":"hex"}},{"6":{"Value":"base64"}},{"5":{"Elements":[260,261,262]}},{"2":{"Name":"AzureKeyVaultVolumePropertiesSecrets","Properties":{},"AdditionalProperties":259}},{"6":{"Value":"azure.com.keyvault"}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/volumes@2023-10-01-preview","ScopeType":0,"Body":233}},{"8":{"Name":"listSecrets","ResourceType":"Applications.Core/extenders","ApiVersion":"2023-10-01-preview","Output":0,"Input":0}},{"2":{"Name":"SecretStoreListSecretsResult","Properties":{"type":{"Type":272,"Flags":2,"Description":"The type of SecretStore data"},"data":{"Type":273,"Flags":2,"Description":"An object to represent key-value type secrets"}}}},{"6":{"Value":"generic"}},{"6":{"Value":"certificate"}},{"5":{"Elements":[270,271]}},{"2":{"Name":"SecretStoreListSecretsResultData","Properties":{},"AdditionalProperties":223}},{"8":{"Name":"listSecrets","ResourceType":"Applications.Core/secretStores","ApiVersion":"2023-10-01-preview","Output":269,"Input":0}}] \ No newline at end of file diff --git a/pkg/corerp/api/v20231001preview/zz_generated_models.go b/pkg/corerp/api/v20231001preview/zz_generated_models.go index 78a1da0b41..6868ef548c 100644 --- a/pkg/corerp/api/v20231001preview/zz_generated_models.go +++ b/pkg/corerp/api/v20231001preview/zz_generated_models.go @@ -126,7 +126,7 @@ type ApplicationResourceUpdateProperties struct { // Cloud providers configuration for the environment. Providers *ProvidersUpdate - // Specifies configurations needed for the recipes registered to the environment. + // Configuration for Recipes. Defines how each type of Recipe should be configured and run. RecipeConfig *RecipeConfigProperties // Specifies Recipes linked to the Environment. @@ -136,9 +136,9 @@ type ApplicationResourceUpdateProperties struct { Simulated *bool } -// AuthConfig - Specifies authentication information needed to access private terraform module repositories. +// AuthConfig - Authentication information used to access private Terraform module sources. Supported module sources: Git. type AuthConfig struct { - // Specifies authentication information needed to access private terraform modules from Git repository sources. + // Authentication information used to access private Terraform modules from Git repository sources. Git *GitAuthConfig } @@ -562,7 +562,7 @@ type EnvironmentProperties struct { // Cloud providers configuration for the environment. Providers *Providers - // Specifies configurations needed for the recipes registered to the environment. + // Configuration for Recipes. Defines how each type of Recipe should be configured and run. RecipeConfig *RecipeConfigProperties // Specifies Recipes linked to the Environment. @@ -628,7 +628,7 @@ type EnvironmentResourceUpdateProperties struct { // Cloud providers configuration for the environment. Providers *ProvidersUpdate - // Specifies configurations needed for the recipes registered to the environment. + // Configuration for Recipes. Defines how each type of Recipe should be configured and run. RecipeConfig *RecipeConfigProperties // Specifies Recipes linked to the Environment. @@ -949,9 +949,9 @@ type GatewayTLS struct { SSLPassthrough *bool } -// GitAuthConfig - Specifies authentication information needed to access private terraform modules from Git repository sources. +// GitAuthConfig - Authentication information used to access private Terraform modules from Git repository sources. type GitAuthConfig struct { - // Specifies the secret details of type personal access token for each different git platforms + // Personal Access Token (PAT) configuration used to authenticate to Git platforms. Pat map[string]*SecretConfig } @@ -1398,7 +1398,7 @@ type Recipe struct { Parameters map[string]any } -// RecipeConfigProperties - Specifies recipe configurations needed for the recipes. +// RecipeConfigProperties - Configuration for Recipes. Defines how each type of Recipe should be configured and run. type RecipeConfigProperties struct { // Specifies the terraform config properties Terraform *TerraformConfigProperties @@ -1523,9 +1523,10 @@ type RuntimesProperties struct { Kubernetes *KubernetesRuntimeProperties } -// SecretConfig - Specifies the secret details of type personal access token for each different git platforms +// SecretConfig - Personal Access Token (PAT) configuration used to authenticate to Git platforms. type SecretConfig struct { - // The resource id for the Applications.Core/SecretStore resource containing credentials. + // The resource id for the Applications.Core/SecretStore resource containing credentials. Secret names required: 'pat', and +// 'username' is optional Secret *string } @@ -1702,10 +1703,10 @@ func (t *TCPHealthProbeProperties) GetHealthProbeProperties() *HealthProbeProper } } -// TerraformConfigProperties - Specifies the terraform config properties +// TerraformConfigProperties - Configuration for Terraform Recipes. Controls how Terraform plans and applies templates as +// part of Recipe deployment. type TerraformConfigProperties struct { - // Specifies authentication information needed to access private terraform module sources. Currently supported module sources: -// Git. + // Authentication information used to access private Terraform module sources. Supported module sources: Git. Authentication *AuthConfig } diff --git a/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/openapi.json b/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/openapi.json index 84836d0e1a..8811d3b18a 100644 --- a/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/openapi.json +++ b/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/openapi.json @@ -2747,7 +2747,7 @@ }, "recipeConfig": { "$ref": "#/definitions/RecipeConfigProperties", - "description": "Specifies configurations needed for the recipes registered to the environment." + "description": "Configuration for Recipes. Defines how each type of Recipe should be configured and run." }, "extensions": { "type": "array", @@ -2761,11 +2761,11 @@ }, "AuthConfig": { "type": "object", - "description": "Specifies authentication information needed to access private terraform module repositories.", + "description": "Authentication information used to access private Terraform module sources. Supported module sources: Git.", "properties": { "git": { "$ref": "#/definitions/GitAuthConfig", - "description": "Specifies authentication information needed to access private terraform modules from Git repository sources." + "description": "Authentication information used to access private Terraform modules from Git repository sources." } } }, @@ -3523,7 +3523,7 @@ }, "recipeConfig": { "$ref": "#/definitions/RecipeConfigProperties", - "description": "Specifies configurations needed for the recipes registered to the environment." + "description": "Configuration for Recipes. Defines how each type of Recipe should be configured and run." }, "extensions": { "type": "array", @@ -3627,7 +3627,7 @@ }, "recipeConfig": { "$ref": "#/definitions/RecipeConfigProperties", - "description": "Specifies configurations needed for the recipes registered to the environment." + "description": "Configuration for Recipes. Defines how each type of Recipe should be configured and run." }, "extensions": { "type": "array", @@ -4035,11 +4035,11 @@ }, "GitAuthConfig": { "type": "object", - "description": "Specifies authentication information needed to access private terraform modules from Git repository sources.", + "description": "Authentication information used to access private Terraform modules from Git repository sources.", "properties": { "pat": { "type": "object", - "description": "Specifies the secret details of type personal access token for each different git platforms", + "description": "Personal Access Token (PAT) configuration used to authenticate to Git platforms.", "additionalProperties": { "$ref": "#/definitions/SecretConfig" } @@ -4769,7 +4769,7 @@ }, "RecipeConfigProperties": { "type": "object", - "description": "Specifies recipe configurations needed for the recipes.", + "description": "Configuration for Recipes. Defines how each type of Recipe should be configured and run.", "properties": { "terraform": { "$ref": "#/definitions/TerraformConfigProperties", @@ -5013,11 +5013,11 @@ }, "SecretConfig": { "type": "object", - "description": "Specifies the secret details of type personal access token for each different git platforms", + "description": "Personal Access Token (PAT) configuration used to authenticate to Git platforms.", "properties": { "secret": { "type": "string", - "description": "The resource id for the Applications.Core/SecretStore resource containing credentials." + "description": "The resource id for the Applications.Core/SecretStore resource containing credentials. Secret names required: 'pat', and 'username' is optional" } } }, @@ -5294,11 +5294,11 @@ }, "TerraformConfigProperties": { "type": "object", - "description": "Specifies the terraform config properties", + "description": "Configuration for Terraform Recipes. Controls how Terraform plans and applies templates as part of Recipe deployment.", "properties": { "authentication": { "$ref": "#/definitions/AuthConfig", - "description": "Specifies authentication information needed to access private terraform module sources. Currently supported module sources: Git." + "description": "Authentication information used to access private Terraform module sources. Supported module sources: Git." } } }, diff --git a/typespec/Applications.Core/environments.tsp b/typespec/Applications.Core/environments.tsp index 5f08209d16..cab440f810 100644 --- a/typespec/Applications.Core/environments.tsp +++ b/typespec/Applications.Core/environments.tsp @@ -65,7 +65,7 @@ model EnvironmentProperties { @doc("Specifies Recipes linked to the Environment.") recipes?: Record>; - @doc("Specifies configurations needed for the recipes registered to the environment.") + @doc("Configuration for Recipes. Defines how each type of Recipe should be configured and run.") recipeConfig?: RecipeConfigProperties; @doc("The environment extension.") @@ -73,33 +73,33 @@ model EnvironmentProperties { extensions?: Array; } -@doc("Specifies recipe configurations needed for the recipes.") +@doc("Configuration for Recipes. Defines how each type of Recipe should be configured and run.") model RecipeConfigProperties { @doc("Specifies the terraform config properties") terraform?: TerraformConfigProperties; } -@doc("Specifies the terraform config properties") +@doc("Configuration for Terraform Recipes. Controls how Terraform plans and applies templates as part of Recipe deployment.") model TerraformConfigProperties{ - @doc("Specifies authentication information needed to access private terraform module sources. Currently supported module sources: Git.") + @doc("Authentication information used to access private Terraform module sources. Supported module sources: Git.") authentication?: AuthConfig; } -@doc("Specifies authentication information needed to access private terraform module repositories.") +@doc("Authentication information used to access private Terraform module sources. Supported module sources: Git.") model AuthConfig{ - @doc("Specifies authentication information needed to access private terraform modules from Git repository sources.") + @doc("Authentication information used to access private Terraform modules from Git repository sources.") git?: GitAuthConfig; } -@doc("Specifies authentication information needed to access private terraform modules from Git repository sources.") +@doc("Authentication information used to access private Terraform modules from Git repository sources.") model GitAuthConfig{ - @doc("Specifies the secret details of type personal access token for each different git platforms") + @doc("Personal Access Token (PAT) configuration used to authenticate to Git platforms.") pat?: Record; } -@doc("Specifies the secret details of type personal access token for each different git platforms") +@doc("Personal Access Token (PAT) configuration used to authenticate to Git platforms.") model SecretConfig { - @doc("The resource id for the Applications.Core/SecretStore resource containing credentials.") + @doc("The resource id for the Applications.Core/SecretStore resource containing credentials. Secret names required: 'pat', and 'username' is optional") secret?: string; } From f66fcc7d1dd4766c90c8419e5550bf1f1300afe2 Mon Sep 17 00:00:00 2001 From: Vishwanath Hiremath Date: Wed, 21 Feb 2024 02:25:30 -0800 Subject: [PATCH 11/22] adding comments Signed-off-by: Vishwanath Hiremath --- pkg/recipes/terraform/config/config_test.go | 69 +++++++++++++++---- .../testdata/module-private-git-repo.tf.json | 11 +++ pkg/recipes/types.go | 6 ++ 3 files changed, 71 insertions(+), 15 deletions(-) create mode 100644 pkg/recipes/terraform/config/testdata/module-private-git-repo.tf.json diff --git a/pkg/recipes/terraform/config/config_test.go b/pkg/recipes/terraform/config/config_test.go index 53d91cac24..a9f1f90174 100644 --- a/pkg/recipes/terraform/config/config_test.go +++ b/pkg/recipes/terraform/config/config_test.go @@ -117,6 +117,7 @@ func Test_NewConfig(t *testing.T) { desc string moduleName string envdef *recipes.EnvironmentDefinition + envConfig *recipes.Configuration metadata *recipes.ResourceMetadata expectedConfigFile string }{ @@ -174,16 +175,49 @@ func Test_NewConfig(t *testing.T) { }, expectedConfigFile: "testdata/module-emptytemplateversion.tf.json", }, + { + desc: "git private repo module", + moduleName: testRecipeName, + envdef: &recipes.EnvironmentDefinition{ + Name: testRecipeName, + TemplatePath: "git::https://dev.azure.com/project/module", + Parameters: envParams, + }, + envConfig: &recipes.Configuration{ + RecipeConfig: datamodel.RecipeConfigProperties{ + Terraform: datamodel.TerraformConfigProperties{ + Authentication: datamodel.AuthConfig{ + Git: datamodel.GitAuthConfig{ + PAT: map[string]datamodel.SecretConfig{ + "dev.azure.com": { + Secret: "secret-store1", + }, + }, + }, + }, + }, + }, + }, + metadata: &recipes.ResourceMetadata{ + Name: testRecipeName, + Parameters: resourceParams, + EnvironmentID: "/planes/radius/local/resourceGroups/test-group/providers/Applications.Environments/testEnv/env", + ApplicationID: "/planes/radius/local/resourceGroups/test-group/providers/Applications.Applications/testApp/app", + ResourceID: "/planes/radius/local/resourceGroups/test-group/providers/Applications.Datastores/redisCaches/redis", + }, + expectedConfigFile: "testdata/module-private-git-repo.tf.json", + }, } for _, tc := range configTests { t.Run(tc.desc, func(t *testing.T) { workingDir := t.TempDir() - tfconfig, _ := New(context.Background(), testRecipeName, tc.envdef, tc.metadata, nil) + tfconfig, err := New(context.Background(), testRecipeName, tc.envdef, tc.metadata, tc.envConfig) + require.NoError(t, err) // validate generated config - err := tfconfig.Save(testcontext.New(t), workingDir) + err = tfconfig.Save(testcontext.New(t), workingDir) require.NoError(t, err) actualConfig, err := os.ReadFile(getMainConfigFilePath(workingDir)) require.NoError(t, err) @@ -273,9 +307,9 @@ func Test_AddRecipeContext(t *testing.T) { ctx := testcontext.New(t) workingDir := t.TempDir() - tfconfig, _ := New(context.Background(), testRecipeName, tc.envdef, tc.metadata, nil) - - err := tfconfig.AddRecipeContext(ctx, tc.moduleName, tc.recipeContext) + tfconfig, err := New(context.Background(), testRecipeName, tc.envdef, tc.metadata, nil) + require.NoError(t, err) + err = tfconfig.AddRecipeContext(ctx, tc.moduleName, tc.recipeContext) if tc.err == "" { require.NoError(t, err) } else { @@ -417,14 +451,15 @@ func Test_AddProviders(t *testing.T) { ctx := testcontext.New(t) workingDir := t.TempDir() - tfconfig, _ := New(context.Background(), testRecipeName, &envRecipe, &resourceRecipe, nil) + tfconfig, err := New(context.Background(), testRecipeName, &envRecipe, &resourceRecipe, nil) + require.NoError(t, err) for _, p := range tc.expectedProviders { mProvider.EXPECT().BuildConfig(ctx, &tc.envConfig).Times(1).Return(p, nil) } if tc.Err != nil { mProvider.EXPECT().BuildConfig(ctx, &tc.envConfig).Times(1).Return(nil, tc.Err) } - err := tfconfig.AddProviders(ctx, tc.requiredProviders, supportedProviders, &tc.envConfig) + err = tfconfig.AddProviders(ctx, tc.requiredProviders, supportedProviders, &tc.envConfig) if tc.Err != nil { require.ErrorContains(t, err, tc.Err.Error()) return @@ -477,9 +512,10 @@ func Test_AddOutputs(t *testing.T) { for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { - tfconfig, _ := New(context.Background(), testRecipeName, &envRecipe, &resourceRecipe, nil) + tfconfig, err := New(context.Background(), testRecipeName, &envRecipe, &resourceRecipe, nil) + require.NoError(t, err) - err := tfconfig.AddOutputs(tc.moduleName) + err = tfconfig.AddOutputs(tc.moduleName) if tc.expectedErr { require.Error(t, err) require.Nil(t, tfconfig.Output) @@ -506,9 +542,10 @@ func Test_Save_overwrite(t *testing.T) { ctx := testcontext.New(t) testDir := t.TempDir() envRecipe, resourceRecipe := getTestInputs() - tfconfig, _ := New(context.Background(), testRecipeName, &envRecipe, &resourceRecipe, nil) + tfconfig, err := New(context.Background(), testRecipeName, &envRecipe, &resourceRecipe, nil) + require.NoError(t, err) - err := tfconfig.Save(ctx, testDir) + err = tfconfig.Save(ctx, testDir) require.NoError(t, err) err = tfconfig.Save(ctx, testDir) @@ -518,10 +555,11 @@ func Test_Save_overwrite(t *testing.T) { func Test_Save_ConfigFileReadOnly(t *testing.T) { testDir := t.TempDir() envRecipe, resourceRecipe := getTestInputs() - tfconfig, _ := New(context.Background(), testRecipeName, &envRecipe, &resourceRecipe, nil) + tfconfig, err := New(context.Background(), testRecipeName, &envRecipe, &resourceRecipe, nil) + require.NoError(t, err) // Create a test configuration file with read only permission. - err := os.WriteFile(getMainConfigFilePath(testDir), []byte(`{"module":{}}`), 0400) + err = os.WriteFile(getMainConfigFilePath(testDir), []byte(`{"module":{}}`), 0400) require.NoError(t, err) // Assert that Save returns an error. @@ -534,9 +572,10 @@ func Test_Save_InvalidWorkingDir(t *testing.T) { testDir := filepath.Join("invalid", uuid.New().String()) envRecipe, resourceRecipe := getTestInputs() - tfconfig, _ := New(context.Background(), testRecipeName, &envRecipe, &resourceRecipe, nil) + tfconfig, err := New(context.Background(), testRecipeName, &envRecipe, &resourceRecipe, nil) + require.NoError(t, err) - err := tfconfig.Save(testcontext.New(t), testDir) + err = tfconfig.Save(testcontext.New(t), testDir) require.Error(t, err) require.Equal(t, fmt.Sprintf("error creating file: open %s/main.tf.json: no such file or directory", testDir), err.Error()) } diff --git a/pkg/recipes/terraform/config/testdata/module-private-git-repo.tf.json b/pkg/recipes/terraform/config/testdata/module-private-git-repo.tf.json new file mode 100644 index 0000000000..2f006e57ca --- /dev/null +++ b/pkg/recipes/terraform/config/testdata/module-private-git-repo.tf.json @@ -0,0 +1,11 @@ +{ + "terraform": null, + "module": { + "redis-azure": { + "redis_cache_name": "redis-test", + "resource_group_name": "test-rg", + "sku": "P", + "source": "git::https://env-app-redis-dev.azure.com/project/module" + } + } +} \ No newline at end of file diff --git a/pkg/recipes/types.go b/pkg/recipes/types.go index 9d1c505b6b..e607129dba 100644 --- a/pkg/recipes/types.go +++ b/pkg/recipes/types.go @@ -142,6 +142,7 @@ func (ro *RecipeOutput) PrepareRecipeResponse(resultValue map[string]any) error return nil } +// GetSecretStoreID returns secretstore resource ID associated with git private terraform repository source. func GetSecretStoreID(envConfig Configuration, templatePath string) (string, error) { url, err := GetGitURL(templatePath) if err != nil { @@ -151,6 +152,7 @@ func GetSecretStoreID(envConfig Configuration, templatePath string) (string, err return envConfig.RecipeConfig.Terraform.Authentication.Git.PAT[strings.TrimPrefix(url.Hostname(), "www.")].Secret, nil } +// GetGitURL returns git url from generic git module source. func GetGitURL(templatePath string) (*url.URL, error) { paths := strings.Split(templatePath, "git::") gitUrl := paths[len(paths)-1] @@ -162,18 +164,22 @@ func GetGitURL(templatePath string) (*url.URL, error) { return url, nil } +// GetEnvAppResourceNames returns the application, environment and resource names. func GetEnvAppResourceNames(resourceMetadata *ResourceMetadata) (string, string, string, error) { app, err := resources.ParseResource(resourceMetadata.ApplicationID) if err != nil { return "", "", "", err } + env, err := resources.ParseResource(resourceMetadata.EnvironmentID) if err != nil { return "", "", "", err } + resource, err := resources.ParseResource(resourceMetadata.ResourceID) if err != nil { return "", "", "", err } + return env.Name(), app.Name(), resource.Name(), nil } From 4db184f7848145fba7ef4578bb580f13a8966767 Mon Sep 17 00:00:00 2001 From: Vishwanath Hiremath Date: Wed, 21 Feb 2024 14:37:11 -0800 Subject: [PATCH 12/22] adding secret loader Signed-off-by: Vishwanath Hiremath --- pkg/recipes/configloader/secrets.go | 50 ++++++++++++ pkg/recipes/controllerconfig/config.go | 1 + pkg/recipes/driver/terraform.go | 108 +------------------------ pkg/recipes/driver/types.go | 63 +++++++++++++++ pkg/recipes/engine/engine.go | 20 +++++ 5 files changed, 138 insertions(+), 104 deletions(-) create mode 100644 pkg/recipes/configloader/secrets.go diff --git a/pkg/recipes/configloader/secrets.go b/pkg/recipes/configloader/secrets.go new file mode 100644 index 0000000000..f76528d9cd --- /dev/null +++ b/pkg/recipes/configloader/secrets.go @@ -0,0 +1,50 @@ +/* +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 configloader + +import ( + "context" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" + aztoken "github.com/radius-project/radius/pkg/azure/tokencredentials" + "github.com/radius-project/radius/pkg/corerp/api/v20231001preview" + "github.com/radius-project/radius/pkg/ucp/resources" +) + +func NewSecretStoreLoader(armOptions *arm.ClientOptions) SecretsLoader { + return SecretsLoader{ArmClientOptions: armOptions} +} + +type SecretsLoader struct { + ArmClientOptions *arm.ClientOptions +} + +func (e *SecretsLoader) LoadSecrets(ctx context.Context, secretStore string) (v20231001preview.SecretStoresClientListSecretsResponse, error) { + secretStoreID, err := resources.ParseResource(secretStore) + if err != nil { + return v20231001preview.SecretStoresClientListSecretsResponse{}, err + } + + client, err := v20231001preview.NewSecretStoresClient(secretStoreID.RootScope(), &aztoken.AnonymousCredential{}, e.ArmClientOptions) + if err != nil { + return v20231001preview.SecretStoresClientListSecretsResponse{}, err + } + + secrets, err := client.ListSecrets(ctx, secretStoreID.Name(), map[string]any{}, nil) + if err != nil { + return v20231001preview.SecretStoresClientListSecretsResponse{}, err + } + + return secrets, nil +} diff --git a/pkg/recipes/controllerconfig/config.go b/pkg/recipes/controllerconfig/config.go index 2c4252b7c1..1fe08f6d8b 100644 --- a/pkg/recipes/controllerconfig/config.go +++ b/pkg/recipes/controllerconfig/config.go @@ -88,6 +88,7 @@ func New(options hostoptions.HostOptions) (*RecipeControllerConfig, error) { cfg.ConfigLoader = configloader.NewEnvironmentLoader(clientOptions) cfg.Engine = engine.NewEngine(engine.Options{ + SecretsLoader: configloader.NewSecretStoreLoader(clientOptions), ConfigurationLoader: cfg.ConfigLoader, Drivers: map[string]driver.Driver{ recipes.TemplateKindBicep: driver.NewBicepDriver( diff --git a/pkg/recipes/driver/terraform.go b/pkg/recipes/driver/terraform.go index c17d6b1145..85ae3c11d9 100644 --- a/pkg/recipes/driver/terraform.go +++ b/pkg/recipes/driver/terraform.go @@ -21,7 +21,6 @@ import ( "errors" "fmt" "os" - "os/exec" "path/filepath" "reflect" "strings" @@ -36,7 +35,6 @@ import ( "github.com/radius-project/radius/pkg/recipes" - aztoken "github.com/radius-project/radius/pkg/azure/tokencredentials" "github.com/radius-project/radius/pkg/recipes/terraform" recipes_util "github.com/radius-project/radius/pkg/recipes/util" "github.com/radius-project/radius/pkg/sdk" @@ -96,9 +94,8 @@ func (d *terraformDriver) Execute(ctx context.Context, opts ExecuteOptions) (*re logger.Info("simulated environment is set to true, skipping deployment") return nil, nil } - secrets, err := configureSecretsForModuleSource(ctx, &opts.Configuration, &opts.Recipe, opts.Definition.TemplatePath, d.ArmClientOptions) - if err != nil { - return nil, err + if !reflect.DeepEqual(opts.BaseOptions.Secrets, v20231001preview.SecretStoresClientListSecretsResponse{}) { + addSecretsToGitConfig(opts.BaseOptions.Secrets, &opts.Recipe, opts.Definition.TemplatePath) } tfState, err := d.terraformExecutor.Deploy(ctx, terraform.Options{ @@ -108,8 +105,8 @@ func (d *terraformDriver) Execute(ctx context.Context, opts ExecuteOptions) (*re EnvRecipe: &opts.Definition, }) - if !reflect.DeepEqual(secrets, v20231001preview.SecretStoresClientListSecretsResponse{}) { - unsetError := unsetSecretsFromGitConfig(secrets, opts.Definition.TemplatePath) + if !reflect.DeepEqual(opts.BaseOptions.Secrets, v20231001preview.SecretStoresClientListSecretsResponse{}) { + unsetError := unsetSecretsFromGitConfig(opts.BaseOptions.Secrets, opts.Definition.TemplatePath) if unsetError != nil { return nil, unsetError } @@ -127,103 +124,6 @@ func (d *terraformDriver) Execute(ctx context.Context, opts ExecuteOptions) (*re return recipeOutputs, nil } -func configureSecretsForModuleSource(ctx context.Context, envConfig *recipes.Configuration, recipeMetadata *recipes.ResourceMetadata, templatePath string, armOptions *arm.ClientOptions) (v20231001preview.SecretStoresClientListSecretsResponse, error) { - if strings.HasPrefix(templatePath, "git::") { - secretStore, err := recipes.GetSecretStoreID(*envConfig, templatePath) - if err != nil { - return v20231001preview.SecretStoresClientListSecretsResponse{}, err - } - - if secretStore == "" { - return v20231001preview.SecretStoresClientListSecretsResponse{}, nil - } - - secrets, err := getSecrets(ctx, secretStore, armOptions) - if err != nil { - return v20231001preview.SecretStoresClientListSecretsResponse{}, err - } - - err = addSecretsToGitConfig(secrets, recipeMetadata, templatePath) - if err != nil { - return v20231001preview.SecretStoresClientListSecretsResponse{}, err - } - - return secrets, nil - } - return v20231001preview.SecretStoresClientListSecretsResponse{}, nil -} -func getURLConfigKeyValue(secrets v20231001preview.SecretStoresClientListSecretsResponse, templatePath string) (string, string, error) { - url, err := recipes.GetGitURL(templatePath) - if err != nil { - return "", "", err - } - - var username, pat *string - path := "https://" - user, ok := secrets.Data["username"] - if ok { - username = user.Value - path += fmt.Sprintf("%s:", *username) - } - - token, ok := secrets.Data["pat"] - if ok { - pat = token.Value - path += *pat - } - - path += fmt.Sprintf("@%s", url.Hostname()) - return fmt.Sprintf("url.%s.insteadOf", path), url.Hostname(), nil -} -func addSecretsToGitConfig(secrets v20231001preview.SecretStoresClientListSecretsResponse, recipeMetadata *recipes.ResourceMetadata, templatePath string) error { - urlConfigKey, urlConfigValue, err := getURLConfigKeyValue(secrets, templatePath) - if err != nil { - return err - } - env, app, resource, err := recipes.GetEnvAppResourceNames(recipeMetadata) - if err != nil { - return err - } - urlConfigValue = fmt.Sprintf("https://%s-%s-%s-%s", env, app, resource, urlConfigValue) - cmd := exec.Command("git", "config", "--global", urlConfigKey, urlConfigValue) - _, err = cmd.Output() - if err != nil { - return errors.New("failed to add git config") - } - - return err -} - -func unsetSecretsFromGitConfig(secrets v20231001preview.SecretStoresClientListSecretsResponse, templatePath string) error { - urlConfigKey, _, err := getURLConfigKeyValue(secrets, templatePath) - if err != nil { - return err - } - - cmd := exec.Command("git", "config", "--global", "--unset", urlConfigKey) - _, err = cmd.Output() - if err != nil { - return errors.New("failed to unset git config") - } - - return err -} -func getSecrets(ctx context.Context, secretStore string, armOptions *arm.ClientOptions) (v20231001preview.SecretStoresClientListSecretsResponse, error) { - secretStoreID, err := resources.ParseResource(secretStore) - if err != nil { - return v20231001preview.SecretStoresClientListSecretsResponse{}, err - } - client, err := v20231001preview.NewSecretStoresClient(secretStoreID.RootScope(), &aztoken.AnonymousCredential{}, armOptions) - if err != nil { - return v20231001preview.SecretStoresClientListSecretsResponse{}, err - } - secrets, err := client.ListSecrets(ctx, secretStoreID.Name(), map[string]any{}, nil) - if err != nil { - return v20231001preview.SecretStoresClientListSecretsResponse{}, err - } - return secrets, nil -} - // Delete returns an error if called as it is not yet implemented. func (d *terraformDriver) Delete(ctx context.Context, opts DeleteOptions) error { logger := ucplog.FromContextOrDiscard(ctx) diff --git a/pkg/recipes/driver/types.go b/pkg/recipes/driver/types.go index ba9088768a..ee4b268f15 100644 --- a/pkg/recipes/driver/types.go +++ b/pkg/recipes/driver/types.go @@ -18,7 +18,11 @@ package driver import ( "context" + "errors" + "fmt" + "os/exec" + "github.com/radius-project/radius/pkg/corerp/api/v20231001preview" "github.com/radius-project/radius/pkg/recipes" rpv1 "github.com/radius-project/radius/pkg/rp/v1" ) @@ -51,6 +55,8 @@ type BaseOptions struct { // Definition is the environment definition for the recipe. Definition recipes.EnvironmentDefinition + + Secrets v20231001preview.SecretStoresClientListSecretsResponse } // ExecuteOptions is the options for the Execute method. @@ -67,3 +73,60 @@ type DeleteOptions struct { // OutputResources is the list of output resources for the recipe. OutputResources []rpv1.OutputResource } + +func getURLConfigKeyValue(secrets v20231001preview.SecretStoresClientListSecretsResponse, templatePath string) (string, string, error) { + url, err := recipes.GetGitURL(templatePath) + if err != nil { + return "", "", err + } + + var username, pat *string + path := "https://" + user, ok := secrets.Data["username"] + if ok { + username = user.Value + path += fmt.Sprintf("%s:", *username) + } + + token, ok := secrets.Data["pat"] + if ok { + pat = token.Value + path += *pat + } + + path += fmt.Sprintf("@%s", url.Hostname()) + return fmt.Sprintf("url.%s.insteadOf", path), url.Hostname(), nil +} +func addSecretsToGitConfig(secrets v20231001preview.SecretStoresClientListSecretsResponse, recipeMetadata *recipes.ResourceMetadata, templatePath string) error { + urlConfigKey, urlConfigValue, err := getURLConfigKeyValue(secrets, templatePath) + if err != nil { + return err + } + env, app, resource, err := recipes.GetEnvAppResourceNames(recipeMetadata) + if err != nil { + return err + } + urlConfigValue = fmt.Sprintf("https://%s-%s-%s-%s", env, app, resource, urlConfigValue) + cmd := exec.Command("git", "config", "--global", urlConfigKey, urlConfigValue) + _, err = cmd.Output() + if err != nil { + return errors.New("failed to add git config") + } + + return err +} + +func unsetSecretsFromGitConfig(secrets v20231001preview.SecretStoresClientListSecretsResponse, templatePath string) error { + urlConfigKey, _, err := getURLConfigKeyValue(secrets, templatePath) + if err != nil { + return err + } + + cmd := exec.Command("git", "config", "--global", "--unset", urlConfigKey) + _, err = cmd.Output() + if err != nil { + return errors.New("failed to unset git config") + } + + return err +} diff --git a/pkg/recipes/engine/engine.go b/pkg/recipes/engine/engine.go index c875cb8750..01ba86f5ec 100644 --- a/pkg/recipes/engine/engine.go +++ b/pkg/recipes/engine/engine.go @@ -19,8 +19,10 @@ package engine import ( "context" "fmt" + "strings" "time" + "github.com/radius-project/radius/pkg/corerp/api/v20231001preview" "github.com/radius-project/radius/pkg/metrics" "github.com/radius-project/radius/pkg/recipes" "github.com/radius-project/radius/pkg/recipes/configloader" @@ -40,6 +42,7 @@ var _ Engine = (*engine)(nil) type Options struct { ConfigurationLoader configloader.ConfigurationLoader Drivers map[string]recipedriver.Driver + SecretsLoader configloader.SecretsLoader } type engine struct { @@ -81,11 +84,28 @@ func (e *engine) executeCore(ctx context.Context, recipe recipes.ResourceMetadat return nil, definition, recipes.NewRecipeError(recipes.RecipeConfigurationFailure, err.Error(), util.RecipeSetupError, recipes.GetErrorDetails(err)) } + secrets := v20231001preview.SecretStoresClientListSecretsResponse{} + if strings.HasPrefix(definition.TemplatePath, "git::") { + secretStore, err := recipes.GetSecretStoreID(*configuration, definition.TemplatePath) + if err != nil { + return nil, nil, err + } + + if secretStore != "" { + secrets, err = e.options.SecretsLoader.LoadSecrets(ctx, secretStore) + if err != nil { + return nil, nil, err + } + + } + } + res, err := driver.Execute(ctx, recipedriver.ExecuteOptions{ BaseOptions: recipedriver.BaseOptions{ Configuration: *configuration, Recipe: recipe, Definition: *definition, + Secrets: secrets, }, PrevState: prevState, }) From ec388f7859c2f7ff066d244cd73ddf646ba97e6f Mon Sep 17 00:00:00 2001 From: Vishwanath Hiremath Date: Wed, 21 Feb 2024 19:24:52 -0800 Subject: [PATCH 13/22] addressing comments Signed-off-by: Vishwanath Hiremath --- pkg/recipes/configloader/secrets.go | 2 ++ pkg/recipes/driver/terraform.go | 7 ++-- pkg/recipes/driver/terraform_test.go | 5 --- pkg/recipes/driver/types.go | 13 +++++-- pkg/recipes/engine/engine.go | 18 ++++------ pkg/recipes/terraform/config/config.go | 10 +++--- pkg/recipes/types.go | 24 ++++++++++--- pkg/recipes/types_test.go | 49 ++++++++++++++++++++++++++ 8 files changed, 98 insertions(+), 30 deletions(-) diff --git a/pkg/recipes/configloader/secrets.go b/pkg/recipes/configloader/secrets.go index f76528d9cd..65bf8f560f 100644 --- a/pkg/recipes/configloader/secrets.go +++ b/pkg/recipes/configloader/secrets.go @@ -22,10 +22,12 @@ import ( "github.com/radius-project/radius/pkg/ucp/resources" ) +// NewSecretStoreLoader creates a new SecretsLoader instance with the given ARM Client Options. func NewSecretStoreLoader(armOptions *arm.ClientOptions) SecretsLoader { return SecretsLoader{ArmClientOptions: armOptions} } +// SecretsLoader struct provides functionality to get secret information from secret stores. type SecretsLoader struct { ArmClientOptions *arm.ClientOptions } diff --git a/pkg/recipes/driver/terraform.go b/pkg/recipes/driver/terraform.go index 85ae3c11d9..d597bf6a6b 100644 --- a/pkg/recipes/driver/terraform.go +++ b/pkg/recipes/driver/terraform.go @@ -94,7 +94,9 @@ func (d *terraformDriver) Execute(ctx context.Context, opts ExecuteOptions) (*re logger.Info("simulated environment is set to true, skipping deployment") return nil, nil } - if !reflect.DeepEqual(opts.BaseOptions.Secrets, v20231001preview.SecretStoresClientListSecretsResponse{}) { + + // Add credential information to .gitconfig if module source is of type git. + if strings.HasPrefix(opts.Definition.TemplatePath, "git::") && !reflect.DeepEqual(opts.BaseOptions.Secrets, v20231001preview.SecretStoresClientListSecretsResponse{}) { addSecretsToGitConfig(opts.BaseOptions.Secrets, &opts.Recipe, opts.Definition.TemplatePath) } @@ -105,7 +107,8 @@ func (d *terraformDriver) Execute(ctx context.Context, opts ExecuteOptions) (*re EnvRecipe: &opts.Definition, }) - if !reflect.DeepEqual(opts.BaseOptions.Secrets, v20231001preview.SecretStoresClientListSecretsResponse{}) { + // Unset credential information from .gitconfig if module source is of type git. + if strings.HasPrefix(opts.Definition.TemplatePath, "git::") && !reflect.DeepEqual(opts.BaseOptions.Secrets, v20231001preview.SecretStoresClientListSecretsResponse{}) { unsetError := unsetSecretsFromGitConfig(opts.BaseOptions.Secrets, opts.Definition.TemplatePath) if unsetError != nil { return nil, unsetError diff --git a/pkg/recipes/driver/terraform_test.go b/pkg/recipes/driver/terraform_test.go index a269f502b0..b47970a17d 100644 --- a/pkg/recipes/driver/terraform_test.go +++ b/pkg/recipes/driver/terraform_test.go @@ -880,11 +880,6 @@ func TestUnsetConfig(t *testing.T) { } func withGlobalGitConfigFile(tmpdir string, content string) func() { - //tmpdir := t.TempDir() - // tmpdir := os.TempDir(workingDir) - // if err != nil { - // panic(err) - // } tmpGitConfigFile := filepath.Join(tmpdir, ".gitconfig") diff --git a/pkg/recipes/driver/types.go b/pkg/recipes/driver/types.go index ee4b268f15..9253244ba2 100644 --- a/pkg/recipes/driver/types.go +++ b/pkg/recipes/driver/types.go @@ -56,6 +56,7 @@ type BaseOptions struct { // Definition is the environment definition for the recipe. Definition recipes.EnvironmentDefinition + // Secrets specifies the module authentication information stored in the secret store. Secrets v20231001preview.SecretStoresClientListSecretsResponse } @@ -74,6 +75,7 @@ type DeleteOptions struct { OutputResources []rpv1.OutputResource } +// getURLConfigKeyValue is used to get the key and value details of the url config. func getURLConfigKeyValue(secrets v20231001preview.SecretStoresClientListSecretsResponse, templatePath string) (string, string, error) { url, err := recipes.GetGitURL(templatePath) if err != nil { @@ -95,18 +97,23 @@ func getURLConfigKeyValue(secrets v20231001preview.SecretStoresClientListSecrets } path += fmt.Sprintf("@%s", url.Hostname()) + return fmt.Sprintf("url.%s.insteadOf", path), url.Hostname(), nil } + +// Add the git credentials information to .gitconfig by running +// git config --global url.insteadOf func addSecretsToGitConfig(secrets v20231001preview.SecretStoresClientListSecretsResponse, recipeMetadata *recipes.ResourceMetadata, templatePath string) error { urlConfigKey, urlConfigValue, err := getURLConfigKeyValue(secrets, templatePath) if err != nil { return err } - env, app, resource, err := recipes.GetEnvAppResourceNames(recipeMetadata) + + prefix, err := recipes.GetURLPrefix(recipeMetadata) if err != nil { return err } - urlConfigValue = fmt.Sprintf("https://%s-%s-%s-%s", env, app, resource, urlConfigValue) + urlConfigValue = fmt.Sprintf("%s%s", prefix, urlConfigValue) cmd := exec.Command("git", "config", "--global", urlConfigKey, urlConfigValue) _, err = cmd.Output() if err != nil { @@ -116,6 +123,8 @@ func addSecretsToGitConfig(secrets v20231001preview.SecretStoresClientListSecret return err } +// Unset the git credentials information from .gitconfig by running +// git config --global --unset url.insteadOf func unsetSecretsFromGitConfig(secrets v20231001preview.SecretStoresClientListSecretsResponse, templatePath string) error { urlConfigKey, _, err := getURLConfigKeyValue(secrets, templatePath) if err != nil { diff --git a/pkg/recipes/engine/engine.go b/pkg/recipes/engine/engine.go index 01ba86f5ec..fed1bc05d8 100644 --- a/pkg/recipes/engine/engine.go +++ b/pkg/recipes/engine/engine.go @@ -19,7 +19,6 @@ package engine import ( "context" "fmt" - "strings" "time" "github.com/radius-project/radius/pkg/corerp/api/v20231001preview" @@ -85,19 +84,16 @@ func (e *engine) executeCore(ctx context.Context, recipe recipes.ResourceMetadat } secrets := v20231001preview.SecretStoresClientListSecretsResponse{} - if strings.HasPrefix(definition.TemplatePath, "git::") { - secretStore, err := recipes.GetSecretStoreID(*configuration, definition.TemplatePath) + secretStore, err := recipes.GetSecretStoreID(*configuration, definition.TemplatePath) + if err != nil { + return nil, nil, err + } + + if secretStore != "" { + secrets, err = e.options.SecretsLoader.LoadSecrets(ctx, secretStore) if err != nil { return nil, nil, err } - - if secretStore != "" { - secrets, err = e.options.SecretsLoader.LoadSecrets(ctx, secretStore) - if err != nil { - return nil, nil, err - } - - } } res, err := driver.Execute(ctx, recipedriver.ExecuteOptions{ diff --git a/pkg/recipes/terraform/config/config.go b/pkg/recipes/terraform/config/config.go index 9170c0ab1c..a17a48b7ba 100644 --- a/pkg/recipes/terraform/config/config.go +++ b/pkg/recipes/terraform/config/config.go @@ -41,14 +41,14 @@ const ( // Parameters are populated from environment recipe and resource recipe metadata. func New(ctx context.Context, moduleName string, envRecipe *recipes.EnvironmentDefinition, resourceRecipe *recipes.ResourceMetadata, envConfig *recipes.Configuration) (*TerraformConfig, error) { path := envRecipe.TemplatePath - if strings.HasPrefix(envRecipe.TemplatePath, "git::") { + + if envConfig != nil { secretStore, err := recipes.GetSecretStoreID(*envConfig, envRecipe.TemplatePath) if err != nil { return nil, err } - if secretStore != "" { - env, app, resource, err := recipes.GetEnvAppResourceNames(resourceRecipe) + prefix, err := recipes.GetURLPrefix(resourceRecipe) if err != nil { return nil, err } @@ -56,10 +56,10 @@ func New(ctx context.Context, moduleName string, envRecipe *recipes.EnvironmentD if err != nil { return nil, err } - path = fmt.Sprintf("git::https://%s-%s-%s-%s", env, app, resource, strings.TrimPrefix(url.String(), "https://")) + path = fmt.Sprintf("git::%s%s", prefix, strings.TrimPrefix(url.String(), "https://")) } - } + // Resource parameter gets precedence over environment level parameter, // if same parameter is defined in both environment and resource recipe metadata. moduleData := newModuleConfig(path, envRecipe.TemplateVersion, envRecipe.Parameters, resourceRecipe.Parameters) diff --git a/pkg/recipes/types.go b/pkg/recipes/types.go index e607129dba..7b3da8892a 100644 --- a/pkg/recipes/types.go +++ b/pkg/recipes/types.go @@ -144,15 +144,20 @@ func (ro *RecipeOutput) PrepareRecipeResponse(resultValue map[string]any) error // GetSecretStoreID returns secretstore resource ID associated with git private terraform repository source. func GetSecretStoreID(envConfig Configuration, templatePath string) (string, error) { - url, err := GetGitURL(templatePath) - if err != nil { - return "", err + if strings.HasPrefix(templatePath, "git::") { + url, err := GetGitURL(templatePath) + if err != nil { + return "", err + } + + // get the secret store id associated with the git domain of the template path. + return envConfig.RecipeConfig.Terraform.Authentication.Git.PAT[strings.TrimPrefix(url.Hostname(), "www.")].Secret, nil } - - return envConfig.RecipeConfig.Terraform.Authentication.Git.PAT[strings.TrimPrefix(url.Hostname(), "www.")].Secret, nil + return "", nil } // GetGitURL returns git url from generic git module source. +// git::https://exmaple.com/project/module -> https://exmaple.com/project/module func GetGitURL(templatePath string) (*url.URL, error) { paths := strings.Split(templatePath, "git::") gitUrl := paths[len(paths)-1] @@ -183,3 +188,12 @@ func GetEnvAppResourceNames(resourceMetadata *ResourceMetadata) (string, string, return env.Name(), app.Name(), resource.Name(), nil } + +// GetURLPrefix returns the url prefix to be added to the template path before adding it to the .gitconfig and terraform config. +func GetURLPrefix(resourceRecipe *ResourceMetadata) (string, error) { + env, app, resource, err := GetEnvAppResourceNames(resourceRecipe) + if err != nil { + return "", err + } + return fmt.Sprintf("https://%s-%s-%s-", env, app, resource), nil +} diff --git a/pkg/recipes/types_test.go b/pkg/recipes/types_test.go index 1cfba2733c..fbfe6b790f 100644 --- a/pkg/recipes/types_test.go +++ b/pkg/recipes/types_test.go @@ -256,3 +256,52 @@ func Test_GetSecretStoreID(t *testing.T) { }) } } + +func Test_GetURLPrefix(t *testing.T) { + tests := []struct { + desc string + metadata ResourceMetadata + expectedPrefix string + expectedErr bool + }{ + { + desc: "success", + metadata: ResourceMetadata{ + Name: "redis-azure", + ApplicationID: "/planes/radius/local/resourcegroups/test-rg/providers/applications.core/applications/app1", + EnvironmentID: "/planes/radius/local/resourcegroups/test-rg/providers/applications.core/environments/env1", + ResourceID: "/planes/radius/local/resourceGroups/test-rg/providers/applications.datastores/rediscaches/redis", + Parameters: map[string]any{ + "redis_cache_name": "redis-test", + }, + }, + expectedPrefix: "https://env1-app1-redis-", + expectedErr: false, + }, + { + desc: "success", + metadata: ResourceMetadata{ + Name: "redis-azure", + ApplicationID: "//planes/radius/local/resourcegroups/test-rg/providers/applications.core/applications/app1", + EnvironmentID: "/planes/radius/local/resourcegroups/test-rg/providers/applications.core/environments/env1", + ResourceID: "/planes/radius/local/resourceGroups/test-rg/providers/applications.datastores/rediscaches/redis", + Parameters: map[string]any{ + "redis_cache_name": "redis-test", + }, + }, + expectedPrefix: "", + expectedErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + ss, err := GetURLPrefix(&tt.metadata) + if !tt.expectedErr { + require.NoError(t, err) + require.Equal(t, ss, tt.expectedPrefix) + } else { + require.Error(t, err) + } + }) + } +} From 89c32fa7d7392607848cdc6abe31275ce2a0a2da Mon Sep 17 00:00:00 2001 From: Vishwanath Hiremath Date: Thu, 22 Feb 2024 12:21:54 -0800 Subject: [PATCH 14/22] addressing comments Signed-off-by: Vishwanath Hiremath --- .../2023-10-01-preview/types.json | 2 +- .../api/radapp.io/v1alpha3/recipe_types.go | 33 +++++++ .../v1alpha3/zz_generated.deepcopy.go | 85 +++++++++++++++++++ .../environment_conversion.go | 27 +++--- .../environment_conversion_test.go | 21 ++--- .../v20231001preview/zz_generated_models.go | 7 +- pkg/corerp/datamodel/environment.go | 33 +------ pkg/recipes/configloader/environment.go | 5 +- pkg/recipes/configloader/environment_test.go | 29 ++++--- pkg/recipes/controllerconfig/config.go | 2 +- pkg/recipes/engine/engine.go | 2 +- pkg/recipes/terraform/config/config_test.go | 11 +-- pkg/recipes/terraform/execute.go | 9 +- pkg/recipes/terraform/execute_test.go | 6 +- pkg/recipes/types.go | 3 +- pkg/recipes/types_test.go | 14 +-- .../preview/2023-10-01-preview/openapi.json | 4 +- typespec/Applications.Core/environments.tsp | 4 +- 18 files changed, 196 insertions(+), 101 deletions(-) diff --git a/hack/bicep-types-radius/generated/applications/applications.core/2023-10-01-preview/types.json b/hack/bicep-types-radius/generated/applications/applications.core/2023-10-01-preview/types.json index 524622e785..ecb3ce7b51 100644 --- a/hack/bicep-types-radius/generated/applications/applications.core/2023-10-01-preview/types.json +++ b/hack/bicep-types-radius/generated/applications/applications.core/2023-10-01-preview/types.json @@ -1 +1 @@ -[{"1":{"Kind":1}},{"1":{"Kind":2}},{"1":{"Kind":3}},{"1":{"Kind":4}},{"1":{"Kind":5}},{"1":{"Kind":6}},{"1":{"Kind":7}},{"1":{"Kind":8}},{"6":{"Value":"Applications.Core/applications"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/applications","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":8,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":9,"Flags":10,"Description":"The resource api version"},"properties":{"Type":11,"Flags":1,"Description":"Application properties"},"tags":{"Type":46,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ApplicationProperties","Properties":{"provisioningState":{"Type":19,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"environment":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"extensions":{"Type":34,"Flags":0,"Description":"The application extension."},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[12,13,14,15,16,17,18]}},{"7":{"Name":"Extension","Discriminator":"kind","BaseProperties":{},"Elements":{"daprSidecar":21,"kubernetesMetadata":26,"kubernetesNamespace":30,"manualScaling":32}}},{"2":{"Name":"DaprSidecarExtension","Properties":{"appPort":{"Type":3,"Flags":0,"Description":"The Dapr appPort. Specifies the internal listening port for the application to handle requests from the Dapr sidecar."},"appId":{"Type":4,"Flags":1,"Description":"The Dapr appId. Specifies the identifier used by Dapr for service invocation."},"config":{"Type":4,"Flags":0,"Description":"Specifies the Dapr configuration to use for the resource."},"protocol":{"Type":24,"Flags":0,"Description":"The Dapr sidecar extension protocol"},"kind":{"Type":25,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"http"}},{"6":{"Value":"grpc"}},{"5":{"Elements":[22,23]}},{"6":{"Value":"daprSidecar"}},{"2":{"Name":"KubernetesMetadataExtension","Properties":{"annotations":{"Type":27,"Flags":0,"Description":"Annotations to be applied to the Kubernetes resources output by the resource"},"labels":{"Type":28,"Flags":0,"Description":"Labels to be applied to the Kubernetes resources output by the resource"},"kind":{"Type":29,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"2":{"Name":"KubernetesMetadataExtensionAnnotations","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"KubernetesMetadataExtensionLabels","Properties":{},"AdditionalProperties":4}},{"6":{"Value":"kubernetesMetadata"}},{"2":{"Name":"KubernetesNamespaceExtension","Properties":{"namespace":{"Type":4,"Flags":1,"Description":"The namespace of the application environment."},"kind":{"Type":31,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"kubernetesNamespace"}},{"2":{"Name":"ManualScalingExtension","Properties":{"replicas":{"Type":3,"Flags":1,"Description":"Replica count."},"kind":{"Type":33,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"manualScaling"}},{"3":{"ItemType":20}},{"2":{"Name":"ResourceStatus","Properties":{"compute":{"Type":36,"Flags":0,"Description":"Represents backing compute resource"},"recipe":{"Type":43,"Flags":2,"Description":"Recipe status at deployment time for a resource."},"outputResources":{"Type":45,"Flags":0,"Description":"Properties of an output resource"}}}},{"7":{"Name":"EnvironmentCompute","Discriminator":"kind","BaseProperties":{"resourceId":{"Type":4,"Flags":0,"Description":"The resource id of the compute resource for application environment."},"identity":{"Type":37,"Flags":0,"Description":"IdentitySettings is the external identity setting."}},"Elements":{"kubernetes":41}}},{"2":{"Name":"IdentitySettings","Properties":{"kind":{"Type":40,"Flags":1,"Description":"IdentitySettingKind is the kind of supported external identity setting"},"oidcIssuer":{"Type":4,"Flags":0,"Description":"The URI for your compute platform's OIDC issuer"},"resource":{"Type":4,"Flags":0,"Description":"The resource ID of the provisioned identity"}}}},{"6":{"Value":"undefined"}},{"6":{"Value":"azure.com.workload"}},{"5":{"Elements":[38,39]}},{"2":{"Name":"KubernetesCompute","Properties":{"namespace":{"Type":4,"Flags":1,"Description":"The namespace to use for the environment."},"kind":{"Type":42,"Flags":1,"Description":"Discriminator property for EnvironmentCompute."}}}},{"6":{"Value":"kubernetes"}},{"2":{"Name":"RecipeStatus","Properties":{"templateKind":{"Type":4,"Flags":1,"Description":"TemplateKind is the kind of the recipe template used by the portable resource upon deployment."},"templatePath":{"Type":4,"Flags":1,"Description":"TemplatePath is the path of the recipe consumed by the portable resource upon deployment."},"templateVersion":{"Type":4,"Flags":0,"Description":"TemplateVersion is the version number of the template."}}}},{"2":{"Name":"OutputResource","Properties":{"localId":{"Type":4,"Flags":0,"Description":"The logical identifier scoped to the owning Radius resource. This is only needed or used when a resource has a dependency relationship. LocalIDs do not have any particular format or meaning beyond being compared to determine dependency relationships."},"id":{"Type":4,"Flags":0,"Description":"The UCP resource ID of the underlying resource."},"radiusManaged":{"Type":2,"Flags":0,"Description":"Determines whether Radius manages the lifecycle of the underlying resource."}}}},{"3":{"ItemType":44}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"SystemData","Properties":{"createdBy":{"Type":4,"Flags":0,"Description":"The identity that created the resource."},"createdByType":{"Type":52,"Flags":0,"Description":"The type of identity that created the resource."},"createdAt":{"Type":4,"Flags":0,"Description":"The timestamp of resource creation (UTC)."},"lastModifiedBy":{"Type":4,"Flags":0,"Description":"The identity that last modified the resource."},"lastModifiedByType":{"Type":57,"Flags":0,"Description":"The type of identity that created the resource."},"lastModifiedAt":{"Type":4,"Flags":0,"Description":"The timestamp of resource last modification (UTC)"}}}},{"6":{"Value":"User"}},{"6":{"Value":"Application"}},{"6":{"Value":"ManagedIdentity"}},{"6":{"Value":"Key"}},{"5":{"Elements":[48,49,50,51]}},{"6":{"Value":"User"}},{"6":{"Value":"Application"}},{"6":{"Value":"ManagedIdentity"}},{"6":{"Value":"Key"}},{"5":{"Elements":[53,54,55,56]}},{"4":{"Name":"Applications.Core/applications@2023-10-01-preview","ScopeType":0,"Body":10}},{"6":{"Value":"Applications.Core/containers"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/containers","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":59,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":60,"Flags":10,"Description":"The resource api version"},"properties":{"Type":62,"Flags":1,"Description":"Container properties"},"tags":{"Type":122,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ContainerProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":70,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"container":{"Type":71,"Flags":1,"Description":"Definition of a container"},"connections":{"Type":108,"Flags":0,"Description":"Specifies a connection to another resource."},"identity":{"Type":37,"Flags":0,"Description":"IdentitySettings is the external identity setting."},"extensions":{"Type":109,"Flags":0,"Description":"Extensions spec of the resource"},"resourceProvisioning":{"Type":112,"Flags":0,"Description":"Specifies how the underlying service/resource is provisioned and managed. Available values are 'internal', where Radius manages the lifecycle of the resource internally, and 'manual', where a user manages the resource."},"resources":{"Type":114,"Flags":0,"Description":"A collection of references to resources associated with the container"},"restartPolicy":{"Type":118,"Flags":0,"Description":"Restart policy for the container"},"runtimes":{"Type":119,"Flags":0,"Description":"The properties for runtime configuration"}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[63,64,65,66,67,68,69]}},{"2":{"Name":"Container","Properties":{"image":{"Type":4,"Flags":1,"Description":"The registry and image to download and run in your container"},"imagePullPolicy":{"Type":75,"Flags":0,"Description":"The image pull policy for the container"},"env":{"Type":76,"Flags":0,"Description":"environment"},"ports":{"Type":81,"Flags":0,"Description":"container ports"},"readinessProbe":{"Type":82,"Flags":0,"Description":"Properties for readiness/liveness probe"},"livenessProbe":{"Type":82,"Flags":0,"Description":"Properties for readiness/liveness probe"},"volumes":{"Type":101,"Flags":0,"Description":"container volumes"},"command":{"Type":102,"Flags":0,"Description":"Entrypoint array. Overrides the container image's ENTRYPOINT"},"args":{"Type":103,"Flags":0,"Description":"Arguments to the entrypoint. Overrides the container image's CMD"},"workingDir":{"Type":4,"Flags":0,"Description":"Working directory for the container"}}}},{"6":{"Value":"Always"}},{"6":{"Value":"IfNotPresent"}},{"6":{"Value":"Never"}},{"5":{"Elements":[72,73,74]}},{"2":{"Name":"ContainerEnv","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"ContainerPortProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"protocol":{"Type":80,"Flags":0,"Description":"The protocol in use by the port"},"provides":{"Type":4,"Flags":0,"Description":"Specifies a route provided by this port"},"scheme":{"Type":4,"Flags":0,"Description":"Specifies the URL scheme of the communication protocol. Consumers can use the scheme to construct a URL. The value defaults to 'http' or 'https' depending on the port value"},"port":{"Type":3,"Flags":0,"Description":"Specifies the port that will be exposed by this container. Must be set when value different from containerPort is desired"}}}},{"6":{"Value":"TCP"}},{"6":{"Value":"UDP"}},{"5":{"Elements":[78,79]}},{"2":{"Name":"ContainerPorts","Properties":{},"AdditionalProperties":77}},{"7":{"Name":"HealthProbeProperties","Discriminator":"kind","BaseProperties":{"initialDelaySeconds":{"Type":3,"Flags":0,"Description":"Initial delay in seconds before probing for readiness/liveness"},"failureThreshold":{"Type":3,"Flags":0,"Description":"Threshold number of times the probe fails after which a failure would be reported"},"periodSeconds":{"Type":3,"Flags":0,"Description":"Interval for the readiness/liveness probe in seconds"},"timeoutSeconds":{"Type":3,"Flags":0,"Description":"Number of seconds after which the readiness/liveness probe times out. Defaults to 5 seconds"}},"Elements":{"exec":83,"httpGet":85,"tcp":88}}},{"2":{"Name":"ExecHealthProbeProperties","Properties":{"command":{"Type":4,"Flags":1,"Description":"Command to execute to probe readiness/liveness"},"kind":{"Type":84,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"6":{"Value":"exec"}},{"2":{"Name":"HttpGetHealthProbeProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"path":{"Type":4,"Flags":1,"Description":"The route to make the HTTP request on"},"headers":{"Type":86,"Flags":0,"Description":"Custom HTTP headers to add to the get request"},"kind":{"Type":87,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"2":{"Name":"HttpGetHealthProbePropertiesHeaders","Properties":{},"AdditionalProperties":4}},{"6":{"Value":"httpGet"}},{"2":{"Name":"TcpHealthProbeProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"kind":{"Type":89,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"6":{"Value":"tcp"}},{"7":{"Name":"Volume","Discriminator":"kind","BaseProperties":{"mountPath":{"Type":4,"Flags":0,"Description":"The path where the volume is mounted"}},"Elements":{"ephemeral":91,"persistent":96}}},{"2":{"Name":"EphemeralVolume","Properties":{"managedStore":{"Type":94,"Flags":1,"Description":"The managed store for the ephemeral volume"},"kind":{"Type":95,"Flags":1,"Description":"Discriminator property for Volume."}}}},{"6":{"Value":"memory"}},{"6":{"Value":"disk"}},{"5":{"Elements":[92,93]}},{"6":{"Value":"ephemeral"}},{"2":{"Name":"PersistentVolume","Properties":{"permission":{"Type":99,"Flags":0,"Description":"The persistent volume permission"},"source":{"Type":4,"Flags":1,"Description":"The source of the volume"},"kind":{"Type":100,"Flags":1,"Description":"Discriminator property for Volume."}}}},{"6":{"Value":"read"}},{"6":{"Value":"write"}},{"5":{"Elements":[97,98]}},{"6":{"Value":"persistent"}},{"2":{"Name":"ContainerVolumes","Properties":{},"AdditionalProperties":90}},{"3":{"ItemType":4}},{"3":{"ItemType":4}},{"2":{"Name":"ConnectionProperties","Properties":{"source":{"Type":4,"Flags":1,"Description":"The source of the connection"},"disableDefaultEnvVars":{"Type":2,"Flags":0,"Description":"default environment variable override"},"iam":{"Type":105,"Flags":0,"Description":"IAM properties"}}}},{"2":{"Name":"IamProperties","Properties":{"kind":{"Type":106,"Flags":1,"Description":"The kind of IAM provider to configure"},"roles":{"Type":107,"Flags":0,"Description":"RBAC permissions to be assigned on the source resource"}}}},{"6":{"Value":"azure"}},{"3":{"ItemType":4}},{"2":{"Name":"ContainerPropertiesConnections","Properties":{},"AdditionalProperties":104}},{"3":{"ItemType":20}},{"6":{"Value":"internal"}},{"6":{"Value":"manual"}},{"5":{"Elements":[110,111]}},{"2":{"Name":"ResourceReference","Properties":{"id":{"Type":4,"Flags":1,"Description":"Resource id of an existing resource"}}}},{"3":{"ItemType":113}},{"6":{"Value":"Always"}},{"6":{"Value":"OnFailure"}},{"6":{"Value":"Never"}},{"5":{"Elements":[115,116,117]}},{"2":{"Name":"RuntimesProperties","Properties":{"kubernetes":{"Type":120,"Flags":0,"Description":"The runtime configuration properties for Kubernetes"}}}},{"2":{"Name":"KubernetesRuntimeProperties","Properties":{"base":{"Type":4,"Flags":0,"Description":"The serialized YAML manifest which represents the base Kubernetes resources to deploy, such as Deployment, Service, ServiceAccount, Secrets, and ConfigMaps."},"pod":{"Type":121,"Flags":0,"Description":"A strategic merge patch that will be applied to the PodSpec object when this container is being deployed."}}}},{"2":{"Name":"KubernetesPodSpec","Properties":{},"AdditionalProperties":0}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/containers@2023-10-01-preview","ScopeType":0,"Body":61}},{"6":{"Value":"Applications.Core/environments"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/environments","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":124,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":125,"Flags":10,"Description":"The resource api version"},"properties":{"Type":127,"Flags":1,"Description":"Environment properties"},"tags":{"Type":153,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"EnvironmentProperties","Properties":{"provisioningState":{"Type":135,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"compute":{"Type":36,"Flags":1,"Description":"Represents backing compute resource"},"providers":{"Type":136,"Flags":0,"Description":"The Cloud providers configuration"},"simulated":{"Type":2,"Flags":0,"Description":"Simulated environment."},"recipes":{"Type":145,"Flags":0,"Description":"Specifies Recipes linked to the Environment."},"recipeConfig":{"Type":146,"Flags":0,"Description":"Configuration for Recipes. Defines how each type of Recipe should be configured and run."},"extensions":{"Type":152,"Flags":0,"Description":"The environment extension."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[128,129,130,131,132,133,134]}},{"2":{"Name":"Providers","Properties":{"azure":{"Type":137,"Flags":0,"Description":"The Azure cloud provider definition"},"aws":{"Type":138,"Flags":0,"Description":"The AWS cloud provider definition"}}}},{"2":{"Name":"ProvidersAzure","Properties":{"scope":{"Type":4,"Flags":1,"Description":"Target scope for Azure resources to be deployed into. For example: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup'"}}}},{"2":{"Name":"ProvidersAws","Properties":{"scope":{"Type":4,"Flags":1,"Description":"Target scope for AWS resources to be deployed into. For example: '/planes/aws/aws/accounts/000000000000/regions/us-west-2'"}}}},{"7":{"Name":"RecipeProperties","Discriminator":"templateKind","BaseProperties":{"templatePath":{"Type":4,"Flags":1,"Description":"Path to the template provided by the recipe. Currently only link to Azure Container Registry is supported."},"parameters":{"Type":0,"Flags":0,"Description":"Any object"}},"Elements":{"bicep":140,"terraform":142}}},{"2":{"Name":"BicepRecipeProperties","Properties":{"plainHttp":{"Type":2,"Flags":0,"Description":"Connect to the Bicep registry using HTTP (not-HTTPS). This should be used when the registry is known not to support HTTPS, for example in a locally-hosted registry. Defaults to false (use HTTPS/TLS)."},"templateKind":{"Type":141,"Flags":1,"Description":"Discriminator property for RecipeProperties."}}}},{"6":{"Value":"bicep"}},{"2":{"Name":"TerraformRecipeProperties","Properties":{"templateVersion":{"Type":4,"Flags":0,"Description":"Version of the template to deploy. For Terraform recipes using a module registry this is required, but must be omitted for other module sources."},"templateKind":{"Type":143,"Flags":1,"Description":"Discriminator property for RecipeProperties."}}}},{"6":{"Value":"terraform"}},{"2":{"Name":"DictionaryOfRecipeProperties","Properties":{},"AdditionalProperties":139}},{"2":{"Name":"EnvironmentPropertiesRecipes","Properties":{},"AdditionalProperties":144}},{"2":{"Name":"RecipeConfigProperties","Properties":{"terraform":{"Type":147,"Flags":0,"Description":"Configuration for Terraform Recipes. Controls how Terraform plans and applies templates as part of Recipe deployment."}}}},{"2":{"Name":"TerraformConfigProperties","Properties":{"authentication":{"Type":148,"Flags":0,"Description":"Authentication information used to access private Terraform module sources. Supported module sources: Git."}}}},{"2":{"Name":"AuthConfig","Properties":{"git":{"Type":149,"Flags":0,"Description":"Authentication information used to access private Terraform modules from Git repository sources."}}}},{"2":{"Name":"GitAuthConfig","Properties":{"pat":{"Type":151,"Flags":0,"Description":"Personal Access Token (PAT) configuration used to authenticate to Git platforms."}}}},{"2":{"Name":"SecretConfig","Properties":{"secret":{"Type":4,"Flags":0,"Description":"The resource id for the Applications.Core/SecretStore resource containing credentials. Secret names required: 'pat', and 'username' is optional"}}}},{"2":{"Name":"GitAuthConfigPat","Properties":{},"AdditionalProperties":150}},{"3":{"ItemType":20}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/environments@2023-10-01-preview","ScopeType":0,"Body":126}},{"6":{"Value":"Applications.Core/extenders"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/extenders","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":155,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":156,"Flags":10,"Description":"The resource api version"},"properties":{"Type":158,"Flags":1,"Description":"ExtenderResource portable resource properties"},"tags":{"Type":171,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ExtenderProperties","Properties":{"environment":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the environment that the portable resource is linked to"},"application":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the application that the portable resource is consumed by (if applicable)"},"provisioningState":{"Type":166,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"secrets":{"Type":0,"Flags":0,"Description":"Any object"},"recipe":{"Type":167,"Flags":0,"Description":"The recipe used to automatically deploy underlying infrastructure for a portable resource"},"resourceProvisioning":{"Type":170,"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."}},"AdditionalProperties":0}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[159,160,161,162,163,164,165]}},{"2":{"Name":"Recipe","Properties":{"name":{"Type":4,"Flags":1,"Description":"The name of the recipe within the environment to use"},"parameters":{"Type":0,"Flags":0,"Description":"Any object"}}}},{"6":{"Value":"recipe"}},{"6":{"Value":"manual"}},{"5":{"Elements":[168,169]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/extenders@2023-10-01-preview","ScopeType":0,"Body":157}},{"6":{"Value":"Applications.Core/gateways"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/gateways","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":173,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":174,"Flags":10,"Description":"The resource api version"},"properties":{"Type":176,"Flags":1,"Description":"Gateway properties"},"tags":{"Type":192,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"GatewayProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":184,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"internal":{"Type":2,"Flags":0,"Description":"Sets Gateway to not be exposed externally (no public IP address associated). Defaults to false (exposed to internet)."},"hostname":{"Type":185,"Flags":0,"Description":"Declare hostname information for the Gateway. Leaving the hostname empty auto-assigns one: mygateway.myapp.PUBLICHOSTNAMEORIP.nip.io."},"routes":{"Type":187,"Flags":1,"Description":"Routes attached to this Gateway"},"tls":{"Type":188,"Flags":0,"Description":"TLS configuration definition for Gateway resource."},"url":{"Type":4,"Flags":2,"Description":"URL of the gateway resource. Readonly"}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[177,178,179,180,181,182,183]}},{"2":{"Name":"GatewayHostname","Properties":{"prefix":{"Type":4,"Flags":0,"Description":"Specify a prefix for the hostname: myhostname.myapp.PUBLICHOSTNAMEORIP.nip.io. Mutually exclusive with 'fullyQualifiedHostname' and will be overridden if both are defined."},"fullyQualifiedHostname":{"Type":4,"Flags":0,"Description":"Specify a fully-qualified domain name: myapp.mydomain.com. Mutually exclusive with 'prefix' and will take priority if both are defined."}}}},{"2":{"Name":"GatewayRoute","Properties":{"path":{"Type":4,"Flags":0,"Description":"The path to match the incoming request path on. Ex - /myservice."},"destination":{"Type":4,"Flags":0,"Description":"The HttpRoute to route to. Ex - myserviceroute.id."},"replacePrefix":{"Type":4,"Flags":0,"Description":"Optionally update the prefix when sending the request to the service. Ex - replacePrefix: '/' and path: '/myservice' will transform '/myservice/myroute' to '/myroute'"}}}},{"3":{"ItemType":186}},{"2":{"Name":"GatewayTls","Properties":{"sslPassthrough":{"Type":2,"Flags":0,"Description":"If true, gateway lets the https traffic sslPassthrough to the backend servers for decryption."},"minimumProtocolVersion":{"Type":191,"Flags":0,"Description":"Tls Minimum versions for Gateway resource."},"certificateFrom":{"Type":4,"Flags":0,"Description":"The resource id for the secret containing the TLS certificate and key for the gateway."}}}},{"6":{"Value":"1.2"}},{"6":{"Value":"1.3"}},{"5":{"Elements":[189,190]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/gateways@2023-10-01-preview","ScopeType":0,"Body":175}},{"6":{"Value":"Applications.Core/httpRoutes"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/httpRoutes","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":194,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":195,"Flags":10,"Description":"The resource api version"},"properties":{"Type":197,"Flags":1,"Description":"HTTPRoute properties"},"tags":{"Type":206,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"HttpRouteProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":205,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"hostname":{"Type":4,"Flags":0,"Description":"The internal hostname accepting traffic for the HTTP Route. Readonly."},"port":{"Type":3,"Flags":0,"Description":"The port number for the HTTP Route. Defaults to 80. Readonly."},"scheme":{"Type":4,"Flags":2,"Description":"The scheme used for traffic. Readonly."},"url":{"Type":4,"Flags":2,"Description":"A stable URL that that can be used to route traffic to a resource. Readonly."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[198,199,200,201,202,203,204]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/httpRoutes@2023-10-01-preview","ScopeType":0,"Body":196}},{"6":{"Value":"Applications.Core/secretStores"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/secretStores","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":208,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":209,"Flags":10,"Description":"The resource api version"},"properties":{"Type":211,"Flags":1,"Description":"The properties of SecretStore"},"tags":{"Type":229,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"SecretStoreProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":219,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"type":{"Type":222,"Flags":0,"Description":"The type of SecretStore data"},"data":{"Type":228,"Flags":1,"Description":"An object to represent key-value type secrets"},"resource":{"Type":4,"Flags":0,"Description":"The resource id of external secret store."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[212,213,214,215,216,217,218]}},{"6":{"Value":"generic"}},{"6":{"Value":"certificate"}},{"5":{"Elements":[220,221]}},{"2":{"Name":"SecretValueProperties","Properties":{"encoding":{"Type":226,"Flags":0,"Description":"The type of SecretValue Encoding"},"value":{"Type":4,"Flags":0,"Description":"The value of secret."},"valueFrom":{"Type":227,"Flags":0,"Description":"The Secret value source properties"}}}},{"6":{"Value":"raw"}},{"6":{"Value":"base64"}},{"5":{"Elements":[224,225]}},{"2":{"Name":"ValueFromProperties","Properties":{"name":{"Type":4,"Flags":1,"Description":"The name of the referenced secret."},"version":{"Type":4,"Flags":0,"Description":"The version of the referenced secret."}}}},{"2":{"Name":"SecretStorePropertiesData","Properties":{},"AdditionalProperties":223}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/secretStores@2023-10-01-preview","ScopeType":0,"Body":210}},{"6":{"Value":"Applications.Core/volumes"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/volumes","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":231,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":232,"Flags":10,"Description":"The resource api version"},"properties":{"Type":234,"Flags":1,"Description":"Volume properties"},"tags":{"Type":266,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"7":{"Name":"VolumeProperties","Discriminator":"kind","BaseProperties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":242,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."}},"Elements":{"azure.com.keyvault":243}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[235,236,237,238,239,240,241]}},{"2":{"Name":"AzureKeyVaultVolumeProperties","Properties":{"certificates":{"Type":256,"Flags":0,"Description":"The KeyVault certificates that this volume exposes"},"keys":{"Type":258,"Flags":0,"Description":"The KeyVault keys that this volume exposes"},"resource":{"Type":4,"Flags":1,"Description":"The ID of the keyvault to use for this volume resource"},"secrets":{"Type":264,"Flags":0,"Description":"The KeyVault secrets that this volume exposes"},"kind":{"Type":265,"Flags":1,"Description":"Discriminator property for VolumeProperties."}}}},{"2":{"Name":"CertificateObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"encoding":{"Type":248,"Flags":0,"Description":"Represents secret encodings"},"format":{"Type":251,"Flags":0,"Description":"Represents certificate formats"},"name":{"Type":4,"Flags":1,"Description":"The name of the certificate"},"certType":{"Type":255,"Flags":0,"Description":"Represents certificate types"},"version":{"Type":4,"Flags":0,"Description":"Certificate version"}}}},{"6":{"Value":"utf-8"}},{"6":{"Value":"hex"}},{"6":{"Value":"base64"}},{"5":{"Elements":[245,246,247]}},{"6":{"Value":"pem"}},{"6":{"Value":"pfx"}},{"5":{"Elements":[249,250]}},{"6":{"Value":"certificate"}},{"6":{"Value":"privatekey"}},{"6":{"Value":"publickey"}},{"5":{"Elements":[252,253,254]}},{"2":{"Name":"AzureKeyVaultVolumePropertiesCertificates","Properties":{},"AdditionalProperties":244}},{"2":{"Name":"KeyObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"name":{"Type":4,"Flags":1,"Description":"The name of the key"},"version":{"Type":4,"Flags":0,"Description":"Key version"}}}},{"2":{"Name":"AzureKeyVaultVolumePropertiesKeys","Properties":{},"AdditionalProperties":257}},{"2":{"Name":"SecretObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"encoding":{"Type":263,"Flags":0,"Description":"Represents secret encodings"},"name":{"Type":4,"Flags":1,"Description":"The name of the secret"},"version":{"Type":4,"Flags":0,"Description":"secret version"}}}},{"6":{"Value":"utf-8"}},{"6":{"Value":"hex"}},{"6":{"Value":"base64"}},{"5":{"Elements":[260,261,262]}},{"2":{"Name":"AzureKeyVaultVolumePropertiesSecrets","Properties":{},"AdditionalProperties":259}},{"6":{"Value":"azure.com.keyvault"}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/volumes@2023-10-01-preview","ScopeType":0,"Body":233}},{"8":{"Name":"listSecrets","ResourceType":"Applications.Core/extenders","ApiVersion":"2023-10-01-preview","Output":0,"Input":0}},{"2":{"Name":"SecretStoreListSecretsResult","Properties":{"type":{"Type":272,"Flags":2,"Description":"The type of SecretStore data"},"data":{"Type":273,"Flags":2,"Description":"An object to represent key-value type secrets"}}}},{"6":{"Value":"generic"}},{"6":{"Value":"certificate"}},{"5":{"Elements":[270,271]}},{"2":{"Name":"SecretStoreListSecretsResultData","Properties":{},"AdditionalProperties":223}},{"8":{"Name":"listSecrets","ResourceType":"Applications.Core/secretStores","ApiVersion":"2023-10-01-preview","Output":269,"Input":0}}] \ No newline at end of file +[{"1":{"Kind":1}},{"1":{"Kind":2}},{"1":{"Kind":3}},{"1":{"Kind":4}},{"1":{"Kind":5}},{"1":{"Kind":6}},{"1":{"Kind":7}},{"1":{"Kind":8}},{"6":{"Value":"Applications.Core/applications"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/applications","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":8,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":9,"Flags":10,"Description":"The resource api version"},"properties":{"Type":11,"Flags":1,"Description":"Application properties"},"tags":{"Type":46,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ApplicationProperties","Properties":{"provisioningState":{"Type":19,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"environment":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"extensions":{"Type":34,"Flags":0,"Description":"The application extension."},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[12,13,14,15,16,17,18]}},{"7":{"Name":"Extension","Discriminator":"kind","BaseProperties":{},"Elements":{"daprSidecar":21,"kubernetesMetadata":26,"kubernetesNamespace":30,"manualScaling":32}}},{"2":{"Name":"DaprSidecarExtension","Properties":{"appPort":{"Type":3,"Flags":0,"Description":"The Dapr appPort. Specifies the internal listening port for the application to handle requests from the Dapr sidecar."},"appId":{"Type":4,"Flags":1,"Description":"The Dapr appId. Specifies the identifier used by Dapr for service invocation."},"config":{"Type":4,"Flags":0,"Description":"Specifies the Dapr configuration to use for the resource."},"protocol":{"Type":24,"Flags":0,"Description":"The Dapr sidecar extension protocol"},"kind":{"Type":25,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"http"}},{"6":{"Value":"grpc"}},{"5":{"Elements":[22,23]}},{"6":{"Value":"daprSidecar"}},{"2":{"Name":"KubernetesMetadataExtension","Properties":{"annotations":{"Type":27,"Flags":0,"Description":"Annotations to be applied to the Kubernetes resources output by the resource"},"labels":{"Type":28,"Flags":0,"Description":"Labels to be applied to the Kubernetes resources output by the resource"},"kind":{"Type":29,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"2":{"Name":"KubernetesMetadataExtensionAnnotations","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"KubernetesMetadataExtensionLabels","Properties":{},"AdditionalProperties":4}},{"6":{"Value":"kubernetesMetadata"}},{"2":{"Name":"KubernetesNamespaceExtension","Properties":{"namespace":{"Type":4,"Flags":1,"Description":"The namespace of the application environment."},"kind":{"Type":31,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"kubernetesNamespace"}},{"2":{"Name":"ManualScalingExtension","Properties":{"replicas":{"Type":3,"Flags":1,"Description":"Replica count."},"kind":{"Type":33,"Flags":1,"Description":"Discriminator property for Extension."}}}},{"6":{"Value":"manualScaling"}},{"3":{"ItemType":20}},{"2":{"Name":"ResourceStatus","Properties":{"compute":{"Type":36,"Flags":0,"Description":"Represents backing compute resource"},"recipe":{"Type":43,"Flags":2,"Description":"Recipe status at deployment time for a resource."},"outputResources":{"Type":45,"Flags":0,"Description":"Properties of an output resource"}}}},{"7":{"Name":"EnvironmentCompute","Discriminator":"kind","BaseProperties":{"resourceId":{"Type":4,"Flags":0,"Description":"The resource id of the compute resource for application environment."},"identity":{"Type":37,"Flags":0,"Description":"IdentitySettings is the external identity setting."}},"Elements":{"kubernetes":41}}},{"2":{"Name":"IdentitySettings","Properties":{"kind":{"Type":40,"Flags":1,"Description":"IdentitySettingKind is the kind of supported external identity setting"},"oidcIssuer":{"Type":4,"Flags":0,"Description":"The URI for your compute platform's OIDC issuer"},"resource":{"Type":4,"Flags":0,"Description":"The resource ID of the provisioned identity"}}}},{"6":{"Value":"undefined"}},{"6":{"Value":"azure.com.workload"}},{"5":{"Elements":[38,39]}},{"2":{"Name":"KubernetesCompute","Properties":{"namespace":{"Type":4,"Flags":1,"Description":"The namespace to use for the environment."},"kind":{"Type":42,"Flags":1,"Description":"Discriminator property for EnvironmentCompute."}}}},{"6":{"Value":"kubernetes"}},{"2":{"Name":"RecipeStatus","Properties":{"templateKind":{"Type":4,"Flags":1,"Description":"TemplateKind is the kind of the recipe template used by the portable resource upon deployment."},"templatePath":{"Type":4,"Flags":1,"Description":"TemplatePath is the path of the recipe consumed by the portable resource upon deployment."},"templateVersion":{"Type":4,"Flags":0,"Description":"TemplateVersion is the version number of the template."}}}},{"2":{"Name":"OutputResource","Properties":{"localId":{"Type":4,"Flags":0,"Description":"The logical identifier scoped to the owning Radius resource. This is only needed or used when a resource has a dependency relationship. LocalIDs do not have any particular format or meaning beyond being compared to determine dependency relationships."},"id":{"Type":4,"Flags":0,"Description":"The UCP resource ID of the underlying resource."},"radiusManaged":{"Type":2,"Flags":0,"Description":"Determines whether Radius manages the lifecycle of the underlying resource."}}}},{"3":{"ItemType":44}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"SystemData","Properties":{"createdBy":{"Type":4,"Flags":0,"Description":"The identity that created the resource."},"createdByType":{"Type":52,"Flags":0,"Description":"The type of identity that created the resource."},"createdAt":{"Type":4,"Flags":0,"Description":"The timestamp of resource creation (UTC)."},"lastModifiedBy":{"Type":4,"Flags":0,"Description":"The identity that last modified the resource."},"lastModifiedByType":{"Type":57,"Flags":0,"Description":"The type of identity that created the resource."},"lastModifiedAt":{"Type":4,"Flags":0,"Description":"The timestamp of resource last modification (UTC)"}}}},{"6":{"Value":"User"}},{"6":{"Value":"Application"}},{"6":{"Value":"ManagedIdentity"}},{"6":{"Value":"Key"}},{"5":{"Elements":[48,49,50,51]}},{"6":{"Value":"User"}},{"6":{"Value":"Application"}},{"6":{"Value":"ManagedIdentity"}},{"6":{"Value":"Key"}},{"5":{"Elements":[53,54,55,56]}},{"4":{"Name":"Applications.Core/applications@2023-10-01-preview","ScopeType":0,"Body":10}},{"6":{"Value":"Applications.Core/containers"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/containers","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":59,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":60,"Flags":10,"Description":"The resource api version"},"properties":{"Type":62,"Flags":1,"Description":"Container properties"},"tags":{"Type":122,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ContainerProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":70,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"container":{"Type":71,"Flags":1,"Description":"Definition of a container"},"connections":{"Type":108,"Flags":0,"Description":"Specifies a connection to another resource."},"identity":{"Type":37,"Flags":0,"Description":"IdentitySettings is the external identity setting."},"extensions":{"Type":109,"Flags":0,"Description":"Extensions spec of the resource"},"resourceProvisioning":{"Type":112,"Flags":0,"Description":"Specifies how the underlying service/resource is provisioned and managed. Available values are 'internal', where Radius manages the lifecycle of the resource internally, and 'manual', where a user manages the resource."},"resources":{"Type":114,"Flags":0,"Description":"A collection of references to resources associated with the container"},"restartPolicy":{"Type":118,"Flags":0,"Description":"Restart policy for the container"},"runtimes":{"Type":119,"Flags":0,"Description":"The properties for runtime configuration"}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[63,64,65,66,67,68,69]}},{"2":{"Name":"Container","Properties":{"image":{"Type":4,"Flags":1,"Description":"The registry and image to download and run in your container"},"imagePullPolicy":{"Type":75,"Flags":0,"Description":"The image pull policy for the container"},"env":{"Type":76,"Flags":0,"Description":"environment"},"ports":{"Type":81,"Flags":0,"Description":"container ports"},"readinessProbe":{"Type":82,"Flags":0,"Description":"Properties for readiness/liveness probe"},"livenessProbe":{"Type":82,"Flags":0,"Description":"Properties for readiness/liveness probe"},"volumes":{"Type":101,"Flags":0,"Description":"container volumes"},"command":{"Type":102,"Flags":0,"Description":"Entrypoint array. Overrides the container image's ENTRYPOINT"},"args":{"Type":103,"Flags":0,"Description":"Arguments to the entrypoint. Overrides the container image's CMD"},"workingDir":{"Type":4,"Flags":0,"Description":"Working directory for the container"}}}},{"6":{"Value":"Always"}},{"6":{"Value":"IfNotPresent"}},{"6":{"Value":"Never"}},{"5":{"Elements":[72,73,74]}},{"2":{"Name":"ContainerEnv","Properties":{},"AdditionalProperties":4}},{"2":{"Name":"ContainerPortProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"protocol":{"Type":80,"Flags":0,"Description":"The protocol in use by the port"},"provides":{"Type":4,"Flags":0,"Description":"Specifies a route provided by this port"},"scheme":{"Type":4,"Flags":0,"Description":"Specifies the URL scheme of the communication protocol. Consumers can use the scheme to construct a URL. The value defaults to 'http' or 'https' depending on the port value"},"port":{"Type":3,"Flags":0,"Description":"Specifies the port that will be exposed by this container. Must be set when value different from containerPort is desired"}}}},{"6":{"Value":"TCP"}},{"6":{"Value":"UDP"}},{"5":{"Elements":[78,79]}},{"2":{"Name":"ContainerPorts","Properties":{},"AdditionalProperties":77}},{"7":{"Name":"HealthProbeProperties","Discriminator":"kind","BaseProperties":{"initialDelaySeconds":{"Type":3,"Flags":0,"Description":"Initial delay in seconds before probing for readiness/liveness"},"failureThreshold":{"Type":3,"Flags":0,"Description":"Threshold number of times the probe fails after which a failure would be reported"},"periodSeconds":{"Type":3,"Flags":0,"Description":"Interval for the readiness/liveness probe in seconds"},"timeoutSeconds":{"Type":3,"Flags":0,"Description":"Number of seconds after which the readiness/liveness probe times out. Defaults to 5 seconds"}},"Elements":{"exec":83,"httpGet":85,"tcp":88}}},{"2":{"Name":"ExecHealthProbeProperties","Properties":{"command":{"Type":4,"Flags":1,"Description":"Command to execute to probe readiness/liveness"},"kind":{"Type":84,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"6":{"Value":"exec"}},{"2":{"Name":"HttpGetHealthProbeProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"path":{"Type":4,"Flags":1,"Description":"The route to make the HTTP request on"},"headers":{"Type":86,"Flags":0,"Description":"Custom HTTP headers to add to the get request"},"kind":{"Type":87,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"2":{"Name":"HttpGetHealthProbePropertiesHeaders","Properties":{},"AdditionalProperties":4}},{"6":{"Value":"httpGet"}},{"2":{"Name":"TcpHealthProbeProperties","Properties":{"containerPort":{"Type":3,"Flags":1,"Description":"The listening port number"},"kind":{"Type":89,"Flags":1,"Description":"Discriminator property for HealthProbeProperties."}}}},{"6":{"Value":"tcp"}},{"7":{"Name":"Volume","Discriminator":"kind","BaseProperties":{"mountPath":{"Type":4,"Flags":0,"Description":"The path where the volume is mounted"}},"Elements":{"ephemeral":91,"persistent":96}}},{"2":{"Name":"EphemeralVolume","Properties":{"managedStore":{"Type":94,"Flags":1,"Description":"The managed store for the ephemeral volume"},"kind":{"Type":95,"Flags":1,"Description":"Discriminator property for Volume."}}}},{"6":{"Value":"memory"}},{"6":{"Value":"disk"}},{"5":{"Elements":[92,93]}},{"6":{"Value":"ephemeral"}},{"2":{"Name":"PersistentVolume","Properties":{"permission":{"Type":99,"Flags":0,"Description":"The persistent volume permission"},"source":{"Type":4,"Flags":1,"Description":"The source of the volume"},"kind":{"Type":100,"Flags":1,"Description":"Discriminator property for Volume."}}}},{"6":{"Value":"read"}},{"6":{"Value":"write"}},{"5":{"Elements":[97,98]}},{"6":{"Value":"persistent"}},{"2":{"Name":"ContainerVolumes","Properties":{},"AdditionalProperties":90}},{"3":{"ItemType":4}},{"3":{"ItemType":4}},{"2":{"Name":"ConnectionProperties","Properties":{"source":{"Type":4,"Flags":1,"Description":"The source of the connection"},"disableDefaultEnvVars":{"Type":2,"Flags":0,"Description":"default environment variable override"},"iam":{"Type":105,"Flags":0,"Description":"IAM properties"}}}},{"2":{"Name":"IamProperties","Properties":{"kind":{"Type":106,"Flags":1,"Description":"The kind of IAM provider to configure"},"roles":{"Type":107,"Flags":0,"Description":"RBAC permissions to be assigned on the source resource"}}}},{"6":{"Value":"azure"}},{"3":{"ItemType":4}},{"2":{"Name":"ContainerPropertiesConnections","Properties":{},"AdditionalProperties":104}},{"3":{"ItemType":20}},{"6":{"Value":"internal"}},{"6":{"Value":"manual"}},{"5":{"Elements":[110,111]}},{"2":{"Name":"ResourceReference","Properties":{"id":{"Type":4,"Flags":1,"Description":"Resource id of an existing resource"}}}},{"3":{"ItemType":113}},{"6":{"Value":"Always"}},{"6":{"Value":"OnFailure"}},{"6":{"Value":"Never"}},{"5":{"Elements":[115,116,117]}},{"2":{"Name":"RuntimesProperties","Properties":{"kubernetes":{"Type":120,"Flags":0,"Description":"The runtime configuration properties for Kubernetes"}}}},{"2":{"Name":"KubernetesRuntimeProperties","Properties":{"base":{"Type":4,"Flags":0,"Description":"The serialized YAML manifest which represents the base Kubernetes resources to deploy, such as Deployment, Service, ServiceAccount, Secrets, and ConfigMaps."},"pod":{"Type":121,"Flags":0,"Description":"A strategic merge patch that will be applied to the PodSpec object when this container is being deployed."}}}},{"2":{"Name":"KubernetesPodSpec","Properties":{},"AdditionalProperties":0}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/containers@2023-10-01-preview","ScopeType":0,"Body":61}},{"6":{"Value":"Applications.Core/environments"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/environments","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":124,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":125,"Flags":10,"Description":"The resource api version"},"properties":{"Type":127,"Flags":1,"Description":"Environment properties"},"tags":{"Type":153,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"EnvironmentProperties","Properties":{"provisioningState":{"Type":135,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"compute":{"Type":36,"Flags":1,"Description":"Represents backing compute resource"},"providers":{"Type":136,"Flags":0,"Description":"The Cloud providers configuration"},"simulated":{"Type":2,"Flags":0,"Description":"Simulated environment."},"recipes":{"Type":145,"Flags":0,"Description":"Specifies Recipes linked to the Environment."},"recipeConfig":{"Type":146,"Flags":0,"Description":"Configuration for Recipes. Defines how each type of Recipe should be configured and run."},"extensions":{"Type":152,"Flags":0,"Description":"The environment extension."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[128,129,130,131,132,133,134]}},{"2":{"Name":"Providers","Properties":{"azure":{"Type":137,"Flags":0,"Description":"The Azure cloud provider definition"},"aws":{"Type":138,"Flags":0,"Description":"The AWS cloud provider definition"}}}},{"2":{"Name":"ProvidersAzure","Properties":{"scope":{"Type":4,"Flags":1,"Description":"Target scope for Azure resources to be deployed into. For example: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup'"}}}},{"2":{"Name":"ProvidersAws","Properties":{"scope":{"Type":4,"Flags":1,"Description":"Target scope for AWS resources to be deployed into. For example: '/planes/aws/aws/accounts/000000000000/regions/us-west-2'"}}}},{"7":{"Name":"RecipeProperties","Discriminator":"templateKind","BaseProperties":{"templatePath":{"Type":4,"Flags":1,"Description":"Path to the template provided by the recipe. Currently only link to Azure Container Registry is supported."},"parameters":{"Type":0,"Flags":0,"Description":"Any object"}},"Elements":{"bicep":140,"terraform":142}}},{"2":{"Name":"BicepRecipeProperties","Properties":{"plainHttp":{"Type":2,"Flags":0,"Description":"Connect to the Bicep registry using HTTP (not-HTTPS). This should be used when the registry is known not to support HTTPS, for example in a locally-hosted registry. Defaults to false (use HTTPS/TLS)."},"templateKind":{"Type":141,"Flags":1,"Description":"Discriminator property for RecipeProperties."}}}},{"6":{"Value":"bicep"}},{"2":{"Name":"TerraformRecipeProperties","Properties":{"templateVersion":{"Type":4,"Flags":0,"Description":"Version of the template to deploy. For Terraform recipes using a module registry this is required, but must be omitted for other module sources."},"templateKind":{"Type":143,"Flags":1,"Description":"Discriminator property for RecipeProperties."}}}},{"6":{"Value":"terraform"}},{"2":{"Name":"DictionaryOfRecipeProperties","Properties":{},"AdditionalProperties":139}},{"2":{"Name":"EnvironmentPropertiesRecipes","Properties":{},"AdditionalProperties":144}},{"2":{"Name":"RecipeConfigProperties","Properties":{"terraform":{"Type":147,"Flags":0,"Description":"Configuration for Terraform Recipes. Controls how Terraform plans and applies templates as part of Recipe deployment."}}}},{"2":{"Name":"TerraformConfigProperties","Properties":{"authentication":{"Type":148,"Flags":0,"Description":"Authentication information used to access private Terraform module sources. Supported module sources: Git."}}}},{"2":{"Name":"AuthConfig","Properties":{"git":{"Type":149,"Flags":0,"Description":"Authentication information used to access private Terraform modules from Git repository sources."}}}},{"2":{"Name":"GitAuthConfig","Properties":{"pat":{"Type":151,"Flags":0,"Description":"Personal Access Token (PAT) configuration used to authenticate to Git platforms."}}}},{"2":{"Name":"SecretConfig","Properties":{"secret":{"Type":4,"Flags":0,"Description":"The ID of an Applications.Core/SecretStore resource containing the Git platform personal access token (PAT). The secret store must have a secret named 'pat', containing the PAT value. A secret named 'username' is optional, containing the username associated with the pat. By default no username is specified."}}}},{"2":{"Name":"GitAuthConfigPat","Properties":{},"AdditionalProperties":150}},{"3":{"ItemType":20}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/environments@2023-10-01-preview","ScopeType":0,"Body":126}},{"6":{"Value":"Applications.Core/extenders"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/extenders","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":155,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":156,"Flags":10,"Description":"The resource api version"},"properties":{"Type":158,"Flags":1,"Description":"ExtenderResource portable resource properties"},"tags":{"Type":171,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"ExtenderProperties","Properties":{"environment":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the environment that the portable resource is linked to"},"application":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the application that the portable resource is consumed by (if applicable)"},"provisioningState":{"Type":166,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"secrets":{"Type":0,"Flags":0,"Description":"Any object"},"recipe":{"Type":167,"Flags":0,"Description":"The recipe used to automatically deploy underlying infrastructure for a portable resource"},"resourceProvisioning":{"Type":170,"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."}},"AdditionalProperties":0}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[159,160,161,162,163,164,165]}},{"2":{"Name":"Recipe","Properties":{"name":{"Type":4,"Flags":1,"Description":"The name of the recipe within the environment to use"},"parameters":{"Type":0,"Flags":0,"Description":"Any object"}}}},{"6":{"Value":"recipe"}},{"6":{"Value":"manual"}},{"5":{"Elements":[168,169]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/extenders@2023-10-01-preview","ScopeType":0,"Body":157}},{"6":{"Value":"Applications.Core/gateways"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/gateways","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":173,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":174,"Flags":10,"Description":"The resource api version"},"properties":{"Type":176,"Flags":1,"Description":"Gateway properties"},"tags":{"Type":192,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"GatewayProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":184,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"internal":{"Type":2,"Flags":0,"Description":"Sets Gateway to not be exposed externally (no public IP address associated). Defaults to false (exposed to internet)."},"hostname":{"Type":185,"Flags":0,"Description":"Declare hostname information for the Gateway. Leaving the hostname empty auto-assigns one: mygateway.myapp.PUBLICHOSTNAMEORIP.nip.io."},"routes":{"Type":187,"Flags":1,"Description":"Routes attached to this Gateway"},"tls":{"Type":188,"Flags":0,"Description":"TLS configuration definition for Gateway resource."},"url":{"Type":4,"Flags":2,"Description":"URL of the gateway resource. Readonly"}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[177,178,179,180,181,182,183]}},{"2":{"Name":"GatewayHostname","Properties":{"prefix":{"Type":4,"Flags":0,"Description":"Specify a prefix for the hostname: myhostname.myapp.PUBLICHOSTNAMEORIP.nip.io. Mutually exclusive with 'fullyQualifiedHostname' and will be overridden if both are defined."},"fullyQualifiedHostname":{"Type":4,"Flags":0,"Description":"Specify a fully-qualified domain name: myapp.mydomain.com. Mutually exclusive with 'prefix' and will take priority if both are defined."}}}},{"2":{"Name":"GatewayRoute","Properties":{"path":{"Type":4,"Flags":0,"Description":"The path to match the incoming request path on. Ex - /myservice."},"destination":{"Type":4,"Flags":0,"Description":"The HttpRoute to route to. Ex - myserviceroute.id."},"replacePrefix":{"Type":4,"Flags":0,"Description":"Optionally update the prefix when sending the request to the service. Ex - replacePrefix: '/' and path: '/myservice' will transform '/myservice/myroute' to '/myroute'"}}}},{"3":{"ItemType":186}},{"2":{"Name":"GatewayTls","Properties":{"sslPassthrough":{"Type":2,"Flags":0,"Description":"If true, gateway lets the https traffic sslPassthrough to the backend servers for decryption."},"minimumProtocolVersion":{"Type":191,"Flags":0,"Description":"Tls Minimum versions for Gateway resource."},"certificateFrom":{"Type":4,"Flags":0,"Description":"The resource id for the secret containing the TLS certificate and key for the gateway."}}}},{"6":{"Value":"1.2"}},{"6":{"Value":"1.3"}},{"5":{"Elements":[189,190]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/gateways@2023-10-01-preview","ScopeType":0,"Body":175}},{"6":{"Value":"Applications.Core/httpRoutes"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/httpRoutes","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":194,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":195,"Flags":10,"Description":"The resource api version"},"properties":{"Type":197,"Flags":1,"Description":"HTTPRoute properties"},"tags":{"Type":206,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"HttpRouteProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":205,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"hostname":{"Type":4,"Flags":0,"Description":"The internal hostname accepting traffic for the HTTP Route. Readonly."},"port":{"Type":3,"Flags":0,"Description":"The port number for the HTTP Route. Defaults to 80. Readonly."},"scheme":{"Type":4,"Flags":2,"Description":"The scheme used for traffic. Readonly."},"url":{"Type":4,"Flags":2,"Description":"A stable URL that that can be used to route traffic to a resource. Readonly."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[198,199,200,201,202,203,204]}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/httpRoutes@2023-10-01-preview","ScopeType":0,"Body":196}},{"6":{"Value":"Applications.Core/secretStores"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/secretStores","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":208,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":209,"Flags":10,"Description":"The resource api version"},"properties":{"Type":211,"Flags":1,"Description":"The properties of SecretStore"},"tags":{"Type":229,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"2":{"Name":"SecretStoreProperties","Properties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":219,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."},"type":{"Type":222,"Flags":0,"Description":"The type of SecretStore data"},"data":{"Type":228,"Flags":1,"Description":"An object to represent key-value type secrets"},"resource":{"Type":4,"Flags":0,"Description":"The resource id of external secret store."}}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[212,213,214,215,216,217,218]}},{"6":{"Value":"generic"}},{"6":{"Value":"certificate"}},{"5":{"Elements":[220,221]}},{"2":{"Name":"SecretValueProperties","Properties":{"encoding":{"Type":226,"Flags":0,"Description":"The type of SecretValue Encoding"},"value":{"Type":4,"Flags":0,"Description":"The value of secret."},"valueFrom":{"Type":227,"Flags":0,"Description":"The Secret value source properties"}}}},{"6":{"Value":"raw"}},{"6":{"Value":"base64"}},{"5":{"Elements":[224,225]}},{"2":{"Name":"ValueFromProperties","Properties":{"name":{"Type":4,"Flags":1,"Description":"The name of the referenced secret."},"version":{"Type":4,"Flags":0,"Description":"The version of the referenced secret."}}}},{"2":{"Name":"SecretStorePropertiesData","Properties":{},"AdditionalProperties":223}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/secretStores@2023-10-01-preview","ScopeType":0,"Body":210}},{"6":{"Value":"Applications.Core/volumes"}},{"6":{"Value":"2023-10-01-preview"}},{"2":{"Name":"Applications.Core/volumes","Properties":{"id":{"Type":4,"Flags":10,"Description":"The resource id"},"name":{"Type":4,"Flags":9,"Description":"The resource name"},"type":{"Type":231,"Flags":10,"Description":"The resource type"},"apiVersion":{"Type":232,"Flags":10,"Description":"The resource api version"},"properties":{"Type":234,"Flags":1,"Description":"Volume properties"},"tags":{"Type":266,"Flags":0,"Description":"Resource tags."},"location":{"Type":4,"Flags":1,"Description":"The geo-location where the resource lives"},"systemData":{"Type":47,"Flags":2,"Description":"Metadata pertaining to creation and last modification of the resource."}}}},{"7":{"Name":"VolumeProperties","Discriminator":"kind","BaseProperties":{"environment":{"Type":4,"Flags":0,"Description":"Fully qualified resource ID for the environment that the application is linked to"},"application":{"Type":4,"Flags":1,"Description":"Fully qualified resource ID for the application"},"provisioningState":{"Type":242,"Flags":2,"Description":"Provisioning state of the resource at the time the operation was called"},"status":{"Type":35,"Flags":2,"Description":"Status of a resource."}},"Elements":{"azure.com.keyvault":243}}},{"6":{"Value":"Succeeded"}},{"6":{"Value":"Failed"}},{"6":{"Value":"Canceled"}},{"6":{"Value":"Provisioning"}},{"6":{"Value":"Updating"}},{"6":{"Value":"Deleting"}},{"6":{"Value":"Accepted"}},{"5":{"Elements":[235,236,237,238,239,240,241]}},{"2":{"Name":"AzureKeyVaultVolumeProperties","Properties":{"certificates":{"Type":256,"Flags":0,"Description":"The KeyVault certificates that this volume exposes"},"keys":{"Type":258,"Flags":0,"Description":"The KeyVault keys that this volume exposes"},"resource":{"Type":4,"Flags":1,"Description":"The ID of the keyvault to use for this volume resource"},"secrets":{"Type":264,"Flags":0,"Description":"The KeyVault secrets that this volume exposes"},"kind":{"Type":265,"Flags":1,"Description":"Discriminator property for VolumeProperties."}}}},{"2":{"Name":"CertificateObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"encoding":{"Type":248,"Flags":0,"Description":"Represents secret encodings"},"format":{"Type":251,"Flags":0,"Description":"Represents certificate formats"},"name":{"Type":4,"Flags":1,"Description":"The name of the certificate"},"certType":{"Type":255,"Flags":0,"Description":"Represents certificate types"},"version":{"Type":4,"Flags":0,"Description":"Certificate version"}}}},{"6":{"Value":"utf-8"}},{"6":{"Value":"hex"}},{"6":{"Value":"base64"}},{"5":{"Elements":[245,246,247]}},{"6":{"Value":"pem"}},{"6":{"Value":"pfx"}},{"5":{"Elements":[249,250]}},{"6":{"Value":"certificate"}},{"6":{"Value":"privatekey"}},{"6":{"Value":"publickey"}},{"5":{"Elements":[252,253,254]}},{"2":{"Name":"AzureKeyVaultVolumePropertiesCertificates","Properties":{},"AdditionalProperties":244}},{"2":{"Name":"KeyObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"name":{"Type":4,"Flags":1,"Description":"The name of the key"},"version":{"Type":4,"Flags":0,"Description":"Key version"}}}},{"2":{"Name":"AzureKeyVaultVolumePropertiesKeys","Properties":{},"AdditionalProperties":257}},{"2":{"Name":"SecretObjectProperties","Properties":{"alias":{"Type":4,"Flags":0,"Description":"File name when written to disk"},"encoding":{"Type":263,"Flags":0,"Description":"Represents secret encodings"},"name":{"Type":4,"Flags":1,"Description":"The name of the secret"},"version":{"Type":4,"Flags":0,"Description":"secret version"}}}},{"6":{"Value":"utf-8"}},{"6":{"Value":"hex"}},{"6":{"Value":"base64"}},{"5":{"Elements":[260,261,262]}},{"2":{"Name":"AzureKeyVaultVolumePropertiesSecrets","Properties":{},"AdditionalProperties":259}},{"6":{"Value":"azure.com.keyvault"}},{"2":{"Name":"TrackedResourceTags","Properties":{},"AdditionalProperties":4}},{"4":{"Name":"Applications.Core/volumes@2023-10-01-preview","ScopeType":0,"Body":233}},{"8":{"Name":"listSecrets","ResourceType":"Applications.Core/extenders","ApiVersion":"2023-10-01-preview","Output":0,"Input":0}},{"2":{"Name":"SecretStoreListSecretsResult","Properties":{"type":{"Type":272,"Flags":2,"Description":"The type of SecretStore data"},"data":{"Type":273,"Flags":2,"Description":"An object to represent key-value type secrets"}}}},{"6":{"Value":"generic"}},{"6":{"Value":"certificate"}},{"5":{"Elements":[270,271]}},{"2":{"Name":"SecretStoreListSecretsResultData","Properties":{},"AdditionalProperties":223}},{"8":{"Name":"listSecrets","ResourceType":"Applications.Core/secretStores","ApiVersion":"2023-10-01-preview","Output":269,"Input":0}}] \ No newline at end of file diff --git a/pkg/controller/api/radapp.io/v1alpha3/recipe_types.go b/pkg/controller/api/radapp.io/v1alpha3/recipe_types.go index 2f39fc3be6..467e6c7a5f 100644 --- a/pkg/controller/api/radapp.io/v1alpha3/recipe_types.go +++ b/pkg/controller/api/radapp.io/v1alpha3/recipe_types.go @@ -146,3 +146,36 @@ type RecipeList struct { func init() { SchemeBuilder.Register(&Recipe{}, &RecipeList{}) } + +// RecipeConfigProperties - Configuration for Recipes. Defines how each type of Recipe should be configured and run. +type RecipeConfigProperties struct { + // Configuration for Terraform Recipes. Controls how Terraform plans and applies templates as part of Recipe deployment. + Terraform TerraformConfigProperties `json:"terraform,omitempty"` +} + +// TerraformConfigProperties - Configuration for Terraform Recipes. Controls how Terraform plans and applies templates as +// part of Recipe deployment. +type TerraformConfigProperties struct { + // Authentication information used to access private Terraform module sources. Supported module sources: Git. + Authentication AuthConfig `json:"authentication,omitempty"` +} + +// AuthConfig - Authentication information used to access private Terraform module sources. Supported module sources: Git. +type AuthConfig struct { + // Authentication information used to access private Terraform modules from Git repository sources. + Git GitAuthConfig `json:"git,omitempty"` +} + +// GitAuthConfig - Authentication information used to access private Terraform modules from Git repository sources. +type GitAuthConfig struct { + // Personal Access Token (PAT) configuration used to authenticate to Git platforms. + PAT map[string]SecretConfig `json:"pat,omitempty"` +} + +// SecretConfig - Personal Access Token (PAT) configuration used to authenticate to Git platforms. +type SecretConfig struct { + // The ID of an Applications.Core/SecretStore resource containing the Git platform personal access token (PAT). The secret + // store must have a secret named 'pat', containing the PAT value. A secret named + // 'username' is optional, containing the username associated with the pat. By default no username is specified. + Secret string `json:"secret,omitempty"` +} diff --git a/pkg/controller/api/radapp.io/v1alpha3/zz_generated.deepcopy.go b/pkg/controller/api/radapp.io/v1alpha3/zz_generated.deepcopy.go index 9da58abdb9..1147268477 100644 --- a/pkg/controller/api/radapp.io/v1alpha3/zz_generated.deepcopy.go +++ b/pkg/controller/api/radapp.io/v1alpha3/zz_generated.deepcopy.go @@ -25,6 +25,44 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AuthConfig) DeepCopyInto(out *AuthConfig) { + *out = *in + in.Git.DeepCopyInto(&out.Git) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AuthConfig. +func (in *AuthConfig) DeepCopy() *AuthConfig { + if in == nil { + return nil + } + out := new(AuthConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GitAuthConfig) DeepCopyInto(out *GitAuthConfig) { + *out = *in + if in.PAT != nil { + in, out := &in.PAT, &out.PAT + *out = make(map[string]SecretConfig, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitAuthConfig. +func (in *GitAuthConfig) DeepCopy() *GitAuthConfig { + if in == nil { + return nil + } + out := new(GitAuthConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Recipe) DeepCopyInto(out *Recipe) { *out = *in @@ -52,6 +90,22 @@ func (in *Recipe) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RecipeConfigProperties) DeepCopyInto(out *RecipeConfigProperties) { + *out = *in + in.Terraform.DeepCopyInto(&out.Terraform) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RecipeConfigProperties. +func (in *RecipeConfigProperties) DeepCopy() *RecipeConfigProperties { + if in == nil { + return nil + } + out := new(RecipeConfigProperties) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RecipeList) DeepCopyInto(out *RecipeList) { *out = *in @@ -134,3 +188,34 @@ func (in *ResourceOperation) DeepCopy() *ResourceOperation { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SecretConfig) DeepCopyInto(out *SecretConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretConfig. +func (in *SecretConfig) DeepCopy() *SecretConfig { + if in == nil { + return nil + } + out := new(SecretConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TerraformConfigProperties) DeepCopyInto(out *TerraformConfigProperties) { + *out = *in + in.Authentication.DeepCopyInto(&out.Authentication) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TerraformConfigProperties. +func (in *TerraformConfigProperties) DeepCopy() *TerraformConfigProperties { + if in == nil { + return nil + } + out := new(TerraformConfigProperties) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/corerp/api/v20231001preview/environment_conversion.go b/pkg/corerp/api/v20231001preview/environment_conversion.go index 23ee1fde0c..4a349e5fdc 100644 --- a/pkg/corerp/api/v20231001preview/environment_conversion.go +++ b/pkg/corerp/api/v20231001preview/environment_conversion.go @@ -22,6 +22,7 @@ import ( "strings" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/controller/api/radapp.io/v1alpha3" "github.com/radius-project/radius/pkg/corerp/datamodel" "github.com/radius-project/radius/pkg/kubernetes" types "github.com/radius-project/radius/pkg/recipes" @@ -183,20 +184,20 @@ func (dst *EnvironmentResource) ConvertFrom(src v1.DataModelInterface) error { return nil } -func toRecipeConfigDatamodel(config *RecipeConfigProperties) datamodel.RecipeConfigProperties { +func toRecipeConfigDatamodel(config *RecipeConfigProperties) v1alpha3.RecipeConfigProperties { if config != nil { - recipeConfig := datamodel.RecipeConfigProperties{} + recipeConfig := v1alpha3.RecipeConfigProperties{} if config.Terraform != nil { - recipeConfig.Terraform = datamodel.TerraformConfigProperties{} + recipeConfig.Terraform = v1alpha3.TerraformConfigProperties{} if config.Terraform.Authentication != nil { - recipeConfig.Terraform.Authentication = datamodel.AuthConfig{} + recipeConfig.Terraform.Authentication = v1alpha3.AuthConfig{} gitConfig := config.Terraform.Authentication.Git if gitConfig != nil { - recipeConfig.Terraform.Authentication.Git = datamodel.GitAuthConfig{} + recipeConfig.Terraform.Authentication.Git = v1alpha3.GitAuthConfig{} if gitConfig.Pat != nil { - p := map[string]datamodel.SecretConfig{} + p := map[string]v1alpha3.SecretConfig{} for k, v := range gitConfig.Pat { - p[k] = datamodel.SecretConfig{ + p[k] = v1alpha3.SecretConfig{ Secret: to.String(v.Secret), } } @@ -208,17 +209,17 @@ func toRecipeConfigDatamodel(config *RecipeConfigProperties) datamodel.RecipeCon } return recipeConfig } - return datamodel.RecipeConfigProperties{} + return v1alpha3.RecipeConfigProperties{} } -func fromRecipeConfigDatamodel(config datamodel.RecipeConfigProperties) *RecipeConfigProperties { - if !reflect.DeepEqual(config, datamodel.RecipeConfigProperties{}) { +func fromRecipeConfigDatamodel(config v1alpha3.RecipeConfigProperties) *RecipeConfigProperties { + if !reflect.DeepEqual(config, v1alpha3.RecipeConfigProperties{}) { recipeConfig := &RecipeConfigProperties{} - if !reflect.DeepEqual(config.Terraform, datamodel.TerraformConfigProperties{}) { + if !reflect.DeepEqual(config.Terraform, v1alpha3.TerraformConfigProperties{}) { recipeConfig.Terraform = &TerraformConfigProperties{} - if !reflect.DeepEqual(config.Terraform.Authentication, datamodel.AuthConfig{}) { + if !reflect.DeepEqual(config.Terraform.Authentication, v1alpha3.AuthConfig{}) { recipeConfig.Terraform.Authentication = &AuthConfig{} - if !reflect.DeepEqual(config.Terraform.Authentication.Git, datamodel.GitAuthConfig{}) { + if !reflect.DeepEqual(config.Terraform.Authentication.Git, v1alpha3.GitAuthConfig{}) { recipeConfig.Terraform.Authentication.Git = &GitAuthConfig{} if config.Terraform.Authentication.Git.PAT != nil { recipeConfig.Terraform.Authentication.Git.Pat = map[string]*SecretConfig{} diff --git a/pkg/corerp/api/v20231001preview/environment_conversion_test.go b/pkg/corerp/api/v20231001preview/environment_conversion_test.go index 45b9f95b48..4fa74e2513 100644 --- a/pkg/corerp/api/v20231001preview/environment_conversion_test.go +++ b/pkg/corerp/api/v20231001preview/environment_conversion_test.go @@ -22,6 +22,7 @@ import ( "testing" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/controller/api/radapp.io/v1alpha3" "github.com/radius-project/radius/pkg/corerp/datamodel" dapr_ctrl "github.com/radius-project/radius/pkg/daprrp/frontend/controller" ds_ctrl "github.com/radius-project/radius/pkg/datastoresrp/frontend/controller" @@ -73,11 +74,11 @@ func TestConvertVersionedToDataModel(t *testing.T) { Scope: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup", }, }, - RecipeConfig: datamodel.RecipeConfigProperties{ - Terraform: datamodel.TerraformConfigProperties{ - Authentication: datamodel.AuthConfig{ - Git: datamodel.GitAuthConfig{ - PAT: map[string]datamodel.SecretConfig{}, + RecipeConfig: v1alpha3.RecipeConfigProperties{ + Terraform: v1alpha3.TerraformConfigProperties{ + Authentication: v1alpha3.AuthConfig{ + Git: v1alpha3.GitAuthConfig{ + PAT: map[string]v1alpha3.SecretConfig{}, }, }, }, @@ -126,11 +127,11 @@ func TestConvertVersionedToDataModel(t *testing.T) { Scope: "/planes/aws/aws/accounts/140313373712/regions/us-west-2", }, }, - RecipeConfig: datamodel.RecipeConfigProperties{ - Terraform: datamodel.TerraformConfigProperties{ - Authentication: datamodel.AuthConfig{ - Git: datamodel.GitAuthConfig{ - PAT: map[string]datamodel.SecretConfig{ + RecipeConfig: v1alpha3.RecipeConfigProperties{ + Terraform: v1alpha3.TerraformConfigProperties{ + Authentication: v1alpha3.AuthConfig{ + Git: v1alpha3.GitAuthConfig{ + PAT: map[string]v1alpha3.SecretConfig{ "dev.azure.com": { Secret: "/planes/radius/local/resourcegroups/default/providers/Applications.Core/secretStores/github", }, diff --git a/pkg/corerp/api/v20231001preview/zz_generated_models.go b/pkg/corerp/api/v20231001preview/zz_generated_models.go index 6868ef548c..e3ac24d2cc 100644 --- a/pkg/corerp/api/v20231001preview/zz_generated_models.go +++ b/pkg/corerp/api/v20231001preview/zz_generated_models.go @@ -1400,7 +1400,7 @@ type Recipe struct { // RecipeConfigProperties - Configuration for Recipes. Defines how each type of Recipe should be configured and run. type RecipeConfigProperties struct { - // Specifies the terraform config properties + // Configuration for Terraform Recipes. Controls how Terraform plans and applies templates as part of Recipe deployment. Terraform *TerraformConfigProperties } @@ -1525,8 +1525,9 @@ type RuntimesProperties struct { // SecretConfig - Personal Access Token (PAT) configuration used to authenticate to Git platforms. type SecretConfig struct { - // The resource id for the Applications.Core/SecretStore resource containing credentials. Secret names required: 'pat', and -// 'username' is optional + // The ID of an Applications.Core/SecretStore resource containing the Git platform personal access token (PAT). The secret +// store must have a secret named 'pat', containing the PAT value. A secret named +// 'username' is optional, containing the username associated with the pat. By default no username is specified. Secret *string } diff --git a/pkg/corerp/datamodel/environment.go b/pkg/corerp/datamodel/environment.go index ed044ae40d..4b6b130ddb 100644 --- a/pkg/corerp/datamodel/environment.go +++ b/pkg/corerp/datamodel/environment.go @@ -18,6 +18,7 @@ package datamodel import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/controller/api/radapp.io/v1alpha3" rpv1 "github.com/radius-project/radius/pkg/rp/v1" ) @@ -41,41 +42,11 @@ type EnvironmentProperties struct { Compute rpv1.EnvironmentCompute `json:"compute,omitempty"` Recipes map[string]map[string]EnvironmentRecipeProperties `json:"recipes,omitempty"` Providers Providers `json:"providers,omitempty"` - RecipeConfig RecipeConfigProperties `json:"recipeConfig,omitempty"` + RecipeConfig v1alpha3.RecipeConfigProperties `json:"recipeConfig,omitempty"` Extensions []Extension `json:"extensions,omitempty"` Simulated bool `json:"simulated,omitempty"` } -// RecipeConfigProperties - Specifies recipe configurations needed for the recipes. -type RecipeConfigProperties struct { - // Terraform specifies the terraform config properties - Terraform TerraformConfigProperties `json:"terraform,omitempty"` -} - -// TerraformConfigProperties - Specifies the terraform config properties -type TerraformConfigProperties struct { - // Authentication specifies authentication information needed to use private terraform module repositories. - Authentication AuthConfig `json:"authentication,omitempty"` -} - -// AuthConfig - Specifies authentication information needed to use private terraform module repositories. -type AuthConfig struct { - // Git specifies authentication information needed to use private terraform module repositories from git module source - Git GitAuthConfig `json:"git,omitempty"` -} - -// GitAuthConfig - Specifies authentication information needed to use private terraform module repositories from git module source -type GitAuthConfig struct { - // GitAuthConfig specifies the secret details of type personal access token for each different git platforms - PAT map[string]SecretConfig `json:"pat,omitempty"` -} - -// SecretConfig - Specifies the secret details of type personal access token for each different git platforms -type SecretConfig struct { - // Secret represent the resource id for the secret store containing credentials - Secret string `json:"secret,omitempty"` -} - // EnvironmentRecipeProperties represents the properties of environment's recipe. type EnvironmentRecipeProperties struct { TemplateKind string `json:"templateKind"` diff --git a/pkg/recipes/configloader/environment.go b/pkg/recipes/configloader/environment.go index 2c8e2fbb33..1087990cec 100644 --- a/pkg/recipes/configloader/environment.go +++ b/pkg/recipes/configloader/environment.go @@ -22,6 +22,7 @@ import ( "fmt" "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" + "github.com/radius-project/radius/pkg/controller/api/radapp.io/v1alpha3" "github.com/radius-project/radius/pkg/corerp/api/v20231001preview" "github.com/radius-project/radius/pkg/corerp/datamodel" "github.com/radius-project/radius/pkg/recipes" @@ -73,7 +74,7 @@ func getConfiguration(environment *v20231001preview.EnvironmentResource, applica config := recipes.Configuration{ Runtime: recipes.RuntimeConfiguration{}, Providers: datamodel.Providers{}, - RecipeConfig: datamodel.RecipeConfigProperties{}, + RecipeConfig: v1alpha3.RecipeConfigProperties{}, } switch environment.Properties.Compute.(type) { @@ -106,10 +107,12 @@ func getConfiguration(environment *v20231001preview.EnvironmentResource, applica if err != nil { return nil, err } + envDatamodel := env.(*datamodel.Environment) if environment.Properties.Providers != nil { config.Providers = envDatamodel.Properties.Providers } + if environment.Properties.RecipeConfig != nil { config.RecipeConfig = envDatamodel.Properties.RecipeConfig } diff --git a/pkg/recipes/configloader/environment_test.go b/pkg/recipes/configloader/environment_test.go index cd9a3b635a..f5e8a91a91 100644 --- a/pkg/recipes/configloader/environment_test.go +++ b/pkg/recipes/configloader/environment_test.go @@ -19,6 +19,7 @@ package configloader import ( "testing" + "github.com/radius-project/radius/pkg/controller/api/radapp.io/v1alpha3" model "github.com/radius-project/radius/pkg/corerp/api/v20231001preview" "github.com/radius-project/radius/pkg/corerp/datamodel" "github.com/radius-project/radius/pkg/recipes" @@ -68,8 +69,8 @@ func TestGetConfiguration(t *testing.T) { Authentication: &model.AuthConfig{ Git: &model.GitAuthConfig{ Pat: map[string]*model.SecretConfig{ - "dev.azure.com": &model.SecretConfig{ - Secret: to.Ptr("secretStoreID"), + "dev.azure.com": { + Secret: to.Ptr("/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/secretStores/secret"), }, }, }, @@ -87,13 +88,13 @@ func TestGetConfiguration(t *testing.T) { }, }, Providers: createAzureProvider(), - RecipeConfig: datamodel.RecipeConfigProperties{ - Terraform: datamodel.TerraformConfigProperties{ - Authentication: datamodel.AuthConfig{ - Git: datamodel.GitAuthConfig{ - PAT: map[string]datamodel.SecretConfig{ - "dev.azure.com": datamodel.SecretConfig{ - Secret: "secretStoreID", + RecipeConfig: v1alpha3.RecipeConfigProperties{ + Terraform: v1alpha3.TerraformConfigProperties{ + Authentication: v1alpha3.AuthConfig{ + Git: v1alpha3.GitAuthConfig{ + PAT: map[string]v1alpha3.SecretConfig{ + "dev.azure.com": { + Secret: "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/secretStores/secret", }, }, }, @@ -135,11 +136,11 @@ func TestGetConfiguration(t *testing.T) { EnvironmentNamespace: envNamespace, }, }, - RecipeConfig: datamodel.RecipeConfigProperties{ - Terraform: datamodel.TerraformConfigProperties{ - Authentication: datamodel.AuthConfig{ - Git: datamodel.GitAuthConfig{ - PAT: map[string]datamodel.SecretConfig{}, + RecipeConfig: v1alpha3.RecipeConfigProperties{ + Terraform: v1alpha3.TerraformConfigProperties{ + Authentication: v1alpha3.AuthConfig{ + Git: v1alpha3.GitAuthConfig{ + PAT: map[string]v1alpha3.SecretConfig{}, }, }, }, diff --git a/pkg/recipes/controllerconfig/config.go b/pkg/recipes/controllerconfig/config.go index 1fe08f6d8b..e94009728d 100644 --- a/pkg/recipes/controllerconfig/config.go +++ b/pkg/recipes/controllerconfig/config.go @@ -88,8 +88,8 @@ func New(options hostoptions.HostOptions) (*RecipeControllerConfig, error) { cfg.ConfigLoader = configloader.NewEnvironmentLoader(clientOptions) cfg.Engine = engine.NewEngine(engine.Options{ - SecretsLoader: configloader.NewSecretStoreLoader(clientOptions), ConfigurationLoader: cfg.ConfigLoader, + SecretsLoader: configloader.NewSecretStoreLoader(clientOptions), Drivers: map[string]driver.Driver{ recipes.TemplateKindBicep: driver.NewBicepDriver( clientOptions, diff --git a/pkg/recipes/engine/engine.go b/pkg/recipes/engine/engine.go index fed1bc05d8..0921c8cf67 100644 --- a/pkg/recipes/engine/engine.go +++ b/pkg/recipes/engine/engine.go @@ -40,8 +40,8 @@ var _ Engine = (*engine)(nil) // Options represents the configuration loader and type of driver used to deploy recipe. type Options struct { ConfigurationLoader configloader.ConfigurationLoader - Drivers map[string]recipedriver.Driver SecretsLoader configloader.SecretsLoader + Drivers map[string]recipedriver.Driver } type engine struct { diff --git a/pkg/recipes/terraform/config/config_test.go b/pkg/recipes/terraform/config/config_test.go index a9f1f90174..3b4ca55628 100644 --- a/pkg/recipes/terraform/config/config_test.go +++ b/pkg/recipes/terraform/config/config_test.go @@ -28,6 +28,7 @@ import ( "github.com/google/uuid" "github.com/stretchr/testify/require" + "github.com/radius-project/radius/pkg/controller/api/radapp.io/v1alpha3" "github.com/radius-project/radius/pkg/corerp/datamodel" "github.com/radius-project/radius/pkg/recipes" "github.com/radius-project/radius/pkg/recipes/recipecontext" @@ -184,11 +185,11 @@ func Test_NewConfig(t *testing.T) { Parameters: envParams, }, envConfig: &recipes.Configuration{ - RecipeConfig: datamodel.RecipeConfigProperties{ - Terraform: datamodel.TerraformConfigProperties{ - Authentication: datamodel.AuthConfig{ - Git: datamodel.GitAuthConfig{ - PAT: map[string]datamodel.SecretConfig{ + RecipeConfig: v1alpha3.RecipeConfigProperties{ + Terraform: v1alpha3.TerraformConfigProperties{ + Authentication: v1alpha3.AuthConfig{ + Git: v1alpha3.GitAuthConfig{ + PAT: map[string]v1alpha3.SecretConfig{ "dev.azure.com": { Secret: "secret-store1", }, diff --git a/pkg/recipes/terraform/execute.go b/pkg/recipes/terraform/execute.go index cda0e3450d..248d4fa1c7 100644 --- a/pkg/recipes/terraform/execute.go +++ b/pkg/recipes/terraform/execute.go @@ -22,7 +22,6 @@ import ( "fmt" "time" - "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" install "github.com/hashicorp/hc-install" "github.com/hashicorp/terraform-exec/tfexec" tfjson "github.com/hashicorp/terraform-json" @@ -62,8 +61,6 @@ type executor struct { // k8sClientSet is the Kubernetes client. k8sClientSet kubernetes.Interface - - armOptions *arm.ClientOptions } // Deploy installs Terraform, creates a working directory, generates a config, and runs Terraform init and @@ -182,7 +179,7 @@ func (e *executor) GetRecipeMetadata(ctx context.Context, options Options) (map[ return nil, err } - _, err = getTerraformConfig(ctx, tf.WorkingDir(), options, e.armOptions) + _, err = getTerraformConfig(ctx, tf.WorkingDir(), options) if err != nil { return nil, err } @@ -202,7 +199,7 @@ func (e *executor) generateConfig(ctx context.Context, tf *tfexec.Terraform, opt logger := ucplog.FromContextOrDiscard(ctx) workingDir := tf.WorkingDir() - tfConfig, err := getTerraformConfig(ctx, workingDir, options, e.armOptions) + tfConfig, err := getTerraformConfig(ctx, workingDir, options) if err != nil { return "", err } @@ -294,7 +291,7 @@ func downloadAndInspect(ctx context.Context, tf *tfexec.Terraform, options Optio } // getTerraformConfig initializes the Terraform json config with provided module source and saves it -func getTerraformConfig(ctx context.Context, workingDir string, options Options, armOptions *arm.ClientOptions) (*config.TerraformConfig, error) { +func getTerraformConfig(ctx context.Context, workingDir string, options Options) (*config.TerraformConfig, error) { // Generate Terraform json config in the working directory // Use recipe name as a local reference to the module. // Modules are downloaded in a subdirectory in the working directory. Name of the module specified in the diff --git a/pkg/recipes/terraform/execute_test.go b/pkg/recipes/terraform/execute_test.go index 4e953ecea8..87e54e8e37 100644 --- a/pkg/recipes/terraform/execute_test.go +++ b/pkg/recipes/terraform/execute_test.go @@ -78,7 +78,7 @@ func Test_GetTerraformConfig(t *testing.T) { Module: map[string]config.TFModuleConfig{ "test-recipe": {"source": "test/module/source"}}, } - tfConfig, err := getTerraformConfig(testcontext.New(t), testDir, options, nil) + tfConfig, err := getTerraformConfig(testcontext.New(t), testDir, options) require.NoError(t, err) require.Equal(t, &expectedConfig, tfConfig) } @@ -95,7 +95,7 @@ func Test_GetTerraformConfig_EmptyRecipeName(t *testing.T) { ResourceRecipe: &recipes.ResourceMetadata{}, } - _, err := getTerraformConfig(testcontext.New(t), testDir, options, nil) + _, err := getTerraformConfig(testcontext.New(t), testDir, options) require.Error(t, err) require.Equal(t, err, ErrRecipeNameEmpty) } @@ -110,7 +110,7 @@ func Test_GetTerraformConfig_InvalidDirectory(t *testing.T) { ResourceRecipe: &recipes.ResourceMetadata{}, } - _, err := getTerraformConfig(testcontext.New(t), workingDir, options, nil) + _, err := getTerraformConfig(testcontext.New(t), workingDir, options) require.Error(t, err) require.Contains(t, err.Error(), "error creating file: open invalid-directory/main.tf.json: no such file or directory") } diff --git a/pkg/recipes/types.go b/pkg/recipes/types.go index 7b3da8892a..2f0a083993 100644 --- a/pkg/recipes/types.go +++ b/pkg/recipes/types.go @@ -23,6 +23,7 @@ import ( "net/url" "strings" + "github.com/radius-project/radius/pkg/controller/api/radapp.io/v1alpha3" "github.com/radius-project/radius/pkg/corerp/datamodel" rpv1 "github.com/radius-project/radius/pkg/rp/v1" "github.com/radius-project/radius/pkg/ucp/resources" @@ -37,7 +38,7 @@ type Configuration struct { // Simulated represents whether the environment is simulated or not. Simulated bool - RecipeConfig datamodel.RecipeConfigProperties + RecipeConfig v1alpha3.RecipeConfigProperties } // RuntimeConfiguration represents Kubernetes Runtime configuration for the environment. diff --git a/pkg/recipes/types_test.go b/pkg/recipes/types_test.go index fbfe6b790f..da42c77aef 100644 --- a/pkg/recipes/types_test.go +++ b/pkg/recipes/types_test.go @@ -19,7 +19,7 @@ package recipes import ( "testing" - "github.com/radius-project/radius/pkg/corerp/datamodel" + "github.com/radius-project/radius/pkg/controller/api/radapp.io/v1alpha3" rpv1 "github.com/radius-project/radius/pkg/rp/v1" "github.com/stretchr/testify/require" ) @@ -213,12 +213,12 @@ func Test_GetSecretStoreID(t *testing.T) { { desc: "success", envConfig: Configuration{ - RecipeConfig: datamodel.RecipeConfigProperties{ - Terraform: datamodel.TerraformConfigProperties{ - Authentication: datamodel.AuthConfig{ - Git: datamodel.GitAuthConfig{ - PAT: map[string]datamodel.SecretConfig{ - "dev.azure.com": datamodel.SecretConfig{ + RecipeConfig: v1alpha3.RecipeConfigProperties{ + Terraform: v1alpha3.TerraformConfigProperties{ + Authentication: v1alpha3.AuthConfig{ + Git: v1alpha3.GitAuthConfig{ + PAT: map[string]v1alpha3.SecretConfig{ + "dev.azure.com": v1alpha3.SecretConfig{ Secret: "secret-store1", }, }, diff --git a/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/openapi.json b/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/openapi.json index 8811d3b18a..2e4936598c 100644 --- a/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/openapi.json +++ b/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/openapi.json @@ -4773,7 +4773,7 @@ "properties": { "terraform": { "$ref": "#/definitions/TerraformConfigProperties", - "description": "Specifies the terraform config properties" + "description": "Configuration for Terraform Recipes. Controls how Terraform plans and applies templates as part of Recipe deployment." } } }, @@ -5017,7 +5017,7 @@ "properties": { "secret": { "type": "string", - "description": "The resource id for the Applications.Core/SecretStore resource containing credentials. Secret names required: 'pat', and 'username' is optional" + "description": "The ID of an Applications.Core/SecretStore resource containing the Git platform personal access token (PAT). The secret store must have a secret named 'pat', containing the PAT value. A secret named 'username' is optional, containing the username associated with the pat. By default no username is specified." } } }, diff --git a/typespec/Applications.Core/environments.tsp b/typespec/Applications.Core/environments.tsp index cab440f810..9bb025b9bc 100644 --- a/typespec/Applications.Core/environments.tsp +++ b/typespec/Applications.Core/environments.tsp @@ -75,7 +75,7 @@ model EnvironmentProperties { @doc("Configuration for Recipes. Defines how each type of Recipe should be configured and run.") model RecipeConfigProperties { - @doc("Specifies the terraform config properties") + @doc("Configuration for Terraform Recipes. Controls how Terraform plans and applies templates as part of Recipe deployment.") terraform?: TerraformConfigProperties; } @@ -99,7 +99,7 @@ model GitAuthConfig{ @doc("Personal Access Token (PAT) configuration used to authenticate to Git platforms.") model SecretConfig { - @doc("The resource id for the Applications.Core/SecretStore resource containing credentials. Secret names required: 'pat', and 'username' is optional") + @doc("The ID of an Applications.Core/SecretStore resource containing the Git platform personal access token (PAT). The secret store must have a secret named 'pat', containing the PAT value. A secret named 'username' is optional, containing the username associated with the pat. By default no username is specified.") secret?: string; } From c3b89df1762dcef23cac34f1cd6242e924b8e897 Mon Sep 17 00:00:00 2001 From: Vishwanath Hiremath Date: Thu, 22 Feb 2024 14:28:36 -0800 Subject: [PATCH 15/22] addressing comments Signed-off-by: Vishwanath Hiremath --- .../api/radapp.io/v1alpha3/recipe_types.go | 33 ---- .../v1alpha3/zz_generated.deepcopy.go | 85 ---------- .../environment_conversion.go | 27 ++- .../environment_conversion_test.go | 21 ++- pkg/corerp/datamodel/environment.go | 3 +- pkg/corerp/datamodel/recipe_types.go | 50 ++++++ pkg/recipes/configloader/environment.go | 3 +- pkg/recipes/configloader/environment_test.go | 21 ++- pkg/recipes/driver/gitconfig.go | 96 +++++++++++ pkg/recipes/driver/gitconfig_test.go | 159 ++++++++++++++++++ pkg/recipes/driver/terraform_test.go | 134 --------------- pkg/recipes/driver/types.go | 68 -------- pkg/recipes/terraform/config/config_test.go | 11 +- pkg/recipes/types.go | 3 +- pkg/recipes/types_test.go | 14 +- 15 files changed, 353 insertions(+), 375 deletions(-) create mode 100644 pkg/corerp/datamodel/recipe_types.go create mode 100644 pkg/recipes/driver/gitconfig.go create mode 100644 pkg/recipes/driver/gitconfig_test.go diff --git a/pkg/controller/api/radapp.io/v1alpha3/recipe_types.go b/pkg/controller/api/radapp.io/v1alpha3/recipe_types.go index 467e6c7a5f..2f39fc3be6 100644 --- a/pkg/controller/api/radapp.io/v1alpha3/recipe_types.go +++ b/pkg/controller/api/radapp.io/v1alpha3/recipe_types.go @@ -146,36 +146,3 @@ type RecipeList struct { func init() { SchemeBuilder.Register(&Recipe{}, &RecipeList{}) } - -// RecipeConfigProperties - Configuration for Recipes. Defines how each type of Recipe should be configured and run. -type RecipeConfigProperties struct { - // Configuration for Terraform Recipes. Controls how Terraform plans and applies templates as part of Recipe deployment. - Terraform TerraformConfigProperties `json:"terraform,omitempty"` -} - -// TerraformConfigProperties - Configuration for Terraform Recipes. Controls how Terraform plans and applies templates as -// part of Recipe deployment. -type TerraformConfigProperties struct { - // Authentication information used to access private Terraform module sources. Supported module sources: Git. - Authentication AuthConfig `json:"authentication,omitempty"` -} - -// AuthConfig - Authentication information used to access private Terraform module sources. Supported module sources: Git. -type AuthConfig struct { - // Authentication information used to access private Terraform modules from Git repository sources. - Git GitAuthConfig `json:"git,omitempty"` -} - -// GitAuthConfig - Authentication information used to access private Terraform modules from Git repository sources. -type GitAuthConfig struct { - // Personal Access Token (PAT) configuration used to authenticate to Git platforms. - PAT map[string]SecretConfig `json:"pat,omitempty"` -} - -// SecretConfig - Personal Access Token (PAT) configuration used to authenticate to Git platforms. -type SecretConfig struct { - // The ID of an Applications.Core/SecretStore resource containing the Git platform personal access token (PAT). The secret - // store must have a secret named 'pat', containing the PAT value. A secret named - // 'username' is optional, containing the username associated with the pat. By default no username is specified. - Secret string `json:"secret,omitempty"` -} diff --git a/pkg/controller/api/radapp.io/v1alpha3/zz_generated.deepcopy.go b/pkg/controller/api/radapp.io/v1alpha3/zz_generated.deepcopy.go index 1147268477..9da58abdb9 100644 --- a/pkg/controller/api/radapp.io/v1alpha3/zz_generated.deepcopy.go +++ b/pkg/controller/api/radapp.io/v1alpha3/zz_generated.deepcopy.go @@ -25,44 +25,6 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *AuthConfig) DeepCopyInto(out *AuthConfig) { - *out = *in - in.Git.DeepCopyInto(&out.Git) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AuthConfig. -func (in *AuthConfig) DeepCopy() *AuthConfig { - if in == nil { - return nil - } - out := new(AuthConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *GitAuthConfig) DeepCopyInto(out *GitAuthConfig) { - *out = *in - if in.PAT != nil { - in, out := &in.PAT, &out.PAT - *out = make(map[string]SecretConfig, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitAuthConfig. -func (in *GitAuthConfig) DeepCopy() *GitAuthConfig { - if in == nil { - return nil - } - out := new(GitAuthConfig) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Recipe) DeepCopyInto(out *Recipe) { *out = *in @@ -90,22 +52,6 @@ func (in *Recipe) DeepCopyObject() runtime.Object { return nil } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RecipeConfigProperties) DeepCopyInto(out *RecipeConfigProperties) { - *out = *in - in.Terraform.DeepCopyInto(&out.Terraform) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RecipeConfigProperties. -func (in *RecipeConfigProperties) DeepCopy() *RecipeConfigProperties { - if in == nil { - return nil - } - out := new(RecipeConfigProperties) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RecipeList) DeepCopyInto(out *RecipeList) { *out = *in @@ -188,34 +134,3 @@ func (in *ResourceOperation) DeepCopy() *ResourceOperation { in.DeepCopyInto(out) return out } - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SecretConfig) DeepCopyInto(out *SecretConfig) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretConfig. -func (in *SecretConfig) DeepCopy() *SecretConfig { - if in == nil { - return nil - } - out := new(SecretConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TerraformConfigProperties) DeepCopyInto(out *TerraformConfigProperties) { - *out = *in - in.Authentication.DeepCopyInto(&out.Authentication) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TerraformConfigProperties. -func (in *TerraformConfigProperties) DeepCopy() *TerraformConfigProperties { - if in == nil { - return nil - } - out := new(TerraformConfigProperties) - in.DeepCopyInto(out) - return out -} diff --git a/pkg/corerp/api/v20231001preview/environment_conversion.go b/pkg/corerp/api/v20231001preview/environment_conversion.go index 4a349e5fdc..23ee1fde0c 100644 --- a/pkg/corerp/api/v20231001preview/environment_conversion.go +++ b/pkg/corerp/api/v20231001preview/environment_conversion.go @@ -22,7 +22,6 @@ import ( "strings" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" - "github.com/radius-project/radius/pkg/controller/api/radapp.io/v1alpha3" "github.com/radius-project/radius/pkg/corerp/datamodel" "github.com/radius-project/radius/pkg/kubernetes" types "github.com/radius-project/radius/pkg/recipes" @@ -184,20 +183,20 @@ func (dst *EnvironmentResource) ConvertFrom(src v1.DataModelInterface) error { return nil } -func toRecipeConfigDatamodel(config *RecipeConfigProperties) v1alpha3.RecipeConfigProperties { +func toRecipeConfigDatamodel(config *RecipeConfigProperties) datamodel.RecipeConfigProperties { if config != nil { - recipeConfig := v1alpha3.RecipeConfigProperties{} + recipeConfig := datamodel.RecipeConfigProperties{} if config.Terraform != nil { - recipeConfig.Terraform = v1alpha3.TerraformConfigProperties{} + recipeConfig.Terraform = datamodel.TerraformConfigProperties{} if config.Terraform.Authentication != nil { - recipeConfig.Terraform.Authentication = v1alpha3.AuthConfig{} + recipeConfig.Terraform.Authentication = datamodel.AuthConfig{} gitConfig := config.Terraform.Authentication.Git if gitConfig != nil { - recipeConfig.Terraform.Authentication.Git = v1alpha3.GitAuthConfig{} + recipeConfig.Terraform.Authentication.Git = datamodel.GitAuthConfig{} if gitConfig.Pat != nil { - p := map[string]v1alpha3.SecretConfig{} + p := map[string]datamodel.SecretConfig{} for k, v := range gitConfig.Pat { - p[k] = v1alpha3.SecretConfig{ + p[k] = datamodel.SecretConfig{ Secret: to.String(v.Secret), } } @@ -209,17 +208,17 @@ func toRecipeConfigDatamodel(config *RecipeConfigProperties) v1alpha3.RecipeConf } return recipeConfig } - return v1alpha3.RecipeConfigProperties{} + return datamodel.RecipeConfigProperties{} } -func fromRecipeConfigDatamodel(config v1alpha3.RecipeConfigProperties) *RecipeConfigProperties { - if !reflect.DeepEqual(config, v1alpha3.RecipeConfigProperties{}) { +func fromRecipeConfigDatamodel(config datamodel.RecipeConfigProperties) *RecipeConfigProperties { + if !reflect.DeepEqual(config, datamodel.RecipeConfigProperties{}) { recipeConfig := &RecipeConfigProperties{} - if !reflect.DeepEqual(config.Terraform, v1alpha3.TerraformConfigProperties{}) { + if !reflect.DeepEqual(config.Terraform, datamodel.TerraformConfigProperties{}) { recipeConfig.Terraform = &TerraformConfigProperties{} - if !reflect.DeepEqual(config.Terraform.Authentication, v1alpha3.AuthConfig{}) { + if !reflect.DeepEqual(config.Terraform.Authentication, datamodel.AuthConfig{}) { recipeConfig.Terraform.Authentication = &AuthConfig{} - if !reflect.DeepEqual(config.Terraform.Authentication.Git, v1alpha3.GitAuthConfig{}) { + if !reflect.DeepEqual(config.Terraform.Authentication.Git, datamodel.GitAuthConfig{}) { recipeConfig.Terraform.Authentication.Git = &GitAuthConfig{} if config.Terraform.Authentication.Git.PAT != nil { recipeConfig.Terraform.Authentication.Git.Pat = map[string]*SecretConfig{} diff --git a/pkg/corerp/api/v20231001preview/environment_conversion_test.go b/pkg/corerp/api/v20231001preview/environment_conversion_test.go index 4fa74e2513..45b9f95b48 100644 --- a/pkg/corerp/api/v20231001preview/environment_conversion_test.go +++ b/pkg/corerp/api/v20231001preview/environment_conversion_test.go @@ -22,7 +22,6 @@ import ( "testing" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" - "github.com/radius-project/radius/pkg/controller/api/radapp.io/v1alpha3" "github.com/radius-project/radius/pkg/corerp/datamodel" dapr_ctrl "github.com/radius-project/radius/pkg/daprrp/frontend/controller" ds_ctrl "github.com/radius-project/radius/pkg/datastoresrp/frontend/controller" @@ -74,11 +73,11 @@ func TestConvertVersionedToDataModel(t *testing.T) { Scope: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup", }, }, - RecipeConfig: v1alpha3.RecipeConfigProperties{ - Terraform: v1alpha3.TerraformConfigProperties{ - Authentication: v1alpha3.AuthConfig{ - Git: v1alpha3.GitAuthConfig{ - PAT: map[string]v1alpha3.SecretConfig{}, + RecipeConfig: datamodel.RecipeConfigProperties{ + Terraform: datamodel.TerraformConfigProperties{ + Authentication: datamodel.AuthConfig{ + Git: datamodel.GitAuthConfig{ + PAT: map[string]datamodel.SecretConfig{}, }, }, }, @@ -127,11 +126,11 @@ func TestConvertVersionedToDataModel(t *testing.T) { Scope: "/planes/aws/aws/accounts/140313373712/regions/us-west-2", }, }, - RecipeConfig: v1alpha3.RecipeConfigProperties{ - Terraform: v1alpha3.TerraformConfigProperties{ - Authentication: v1alpha3.AuthConfig{ - Git: v1alpha3.GitAuthConfig{ - PAT: map[string]v1alpha3.SecretConfig{ + RecipeConfig: datamodel.RecipeConfigProperties{ + Terraform: datamodel.TerraformConfigProperties{ + Authentication: datamodel.AuthConfig{ + Git: datamodel.GitAuthConfig{ + PAT: map[string]datamodel.SecretConfig{ "dev.azure.com": { Secret: "/planes/radius/local/resourcegroups/default/providers/Applications.Core/secretStores/github", }, diff --git a/pkg/corerp/datamodel/environment.go b/pkg/corerp/datamodel/environment.go index 4b6b130ddb..7e3c7d8ba1 100644 --- a/pkg/corerp/datamodel/environment.go +++ b/pkg/corerp/datamodel/environment.go @@ -18,7 +18,6 @@ package datamodel import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" - "github.com/radius-project/radius/pkg/controller/api/radapp.io/v1alpha3" rpv1 "github.com/radius-project/radius/pkg/rp/v1" ) @@ -42,7 +41,7 @@ type EnvironmentProperties struct { Compute rpv1.EnvironmentCompute `json:"compute,omitempty"` Recipes map[string]map[string]EnvironmentRecipeProperties `json:"recipes,omitempty"` Providers Providers `json:"providers,omitempty"` - RecipeConfig v1alpha3.RecipeConfigProperties `json:"recipeConfig,omitempty"` + RecipeConfig RecipeConfigProperties `json:"recipeConfig,omitempty"` Extensions []Extension `json:"extensions,omitempty"` Simulated bool `json:"simulated,omitempty"` } diff --git a/pkg/corerp/datamodel/recipe_types.go b/pkg/corerp/datamodel/recipe_types.go new file mode 100644 index 0000000000..79f8923b5f --- /dev/null +++ b/pkg/corerp/datamodel/recipe_types.go @@ -0,0 +1,50 @@ +/* +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 + +// RecipeConfigProperties - Configuration for Recipes. Defines how each type of Recipe should be configured and run. +type RecipeConfigProperties struct { + // Configuration for Terraform Recipes. Controls how Terraform plans and applies templates as part of Recipe deployment. + Terraform TerraformConfigProperties `json:"terraform,omitempty"` +} + +// TerraformConfigProperties - Configuration for Terraform Recipes. Controls how Terraform plans and applies templates as +// part of Recipe deployment. +type TerraformConfigProperties struct { + // Authentication information used to access private Terraform module sources. Supported module sources: Git. + Authentication AuthConfig `json:"authentication,omitempty"` +} + +// AuthConfig - Authentication information used to access private Terraform module sources. Supported module sources: Git. +type AuthConfig struct { + // Authentication information used to access private Terraform modules from Git repository sources. + Git GitAuthConfig `json:"git,omitempty"` +} + +// GitAuthConfig - Authentication information used to access private Terraform modules from Git repository sources. +type GitAuthConfig struct { + // Personal Access Token (PAT) configuration used to authenticate to Git platforms. + PAT map[string]SecretConfig `json:"pat,omitempty"` +} + +// SecretConfig - Personal Access Token (PAT) configuration used to authenticate to Git platforms. +type SecretConfig struct { + // The ID of an Applications.Core/SecretStore resource containing the Git platform personal access token (PAT). The secret + // store must have a secret named 'pat', containing the PAT value. A secret named + // 'username' is optional, containing the username associated with the pat. By default no username is specified. + Secret string `json:"secret,omitempty"` +} diff --git a/pkg/recipes/configloader/environment.go b/pkg/recipes/configloader/environment.go index 1087990cec..66ae542f21 100644 --- a/pkg/recipes/configloader/environment.go +++ b/pkg/recipes/configloader/environment.go @@ -22,7 +22,6 @@ import ( "fmt" "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" - "github.com/radius-project/radius/pkg/controller/api/radapp.io/v1alpha3" "github.com/radius-project/radius/pkg/corerp/api/v20231001preview" "github.com/radius-project/radius/pkg/corerp/datamodel" "github.com/radius-project/radius/pkg/recipes" @@ -74,7 +73,7 @@ func getConfiguration(environment *v20231001preview.EnvironmentResource, applica config := recipes.Configuration{ Runtime: recipes.RuntimeConfiguration{}, Providers: datamodel.Providers{}, - RecipeConfig: v1alpha3.RecipeConfigProperties{}, + RecipeConfig: datamodel.RecipeConfigProperties{}, } switch environment.Properties.Compute.(type) { diff --git a/pkg/recipes/configloader/environment_test.go b/pkg/recipes/configloader/environment_test.go index f5e8a91a91..d6de288e44 100644 --- a/pkg/recipes/configloader/environment_test.go +++ b/pkg/recipes/configloader/environment_test.go @@ -19,7 +19,6 @@ package configloader import ( "testing" - "github.com/radius-project/radius/pkg/controller/api/radapp.io/v1alpha3" model "github.com/radius-project/radius/pkg/corerp/api/v20231001preview" "github.com/radius-project/radius/pkg/corerp/datamodel" "github.com/radius-project/radius/pkg/recipes" @@ -88,11 +87,11 @@ func TestGetConfiguration(t *testing.T) { }, }, Providers: createAzureProvider(), - RecipeConfig: v1alpha3.RecipeConfigProperties{ - Terraform: v1alpha3.TerraformConfigProperties{ - Authentication: v1alpha3.AuthConfig{ - Git: v1alpha3.GitAuthConfig{ - PAT: map[string]v1alpha3.SecretConfig{ + RecipeConfig: datamodel.RecipeConfigProperties{ + Terraform: datamodel.TerraformConfigProperties{ + Authentication: datamodel.AuthConfig{ + Git: datamodel.GitAuthConfig{ + PAT: map[string]datamodel.SecretConfig{ "dev.azure.com": { Secret: "/planes/radius/local/resourceGroups/testGroup/providers/Applications.Core/secretStores/secret", }, @@ -136,11 +135,11 @@ func TestGetConfiguration(t *testing.T) { EnvironmentNamespace: envNamespace, }, }, - RecipeConfig: v1alpha3.RecipeConfigProperties{ - Terraform: v1alpha3.TerraformConfigProperties{ - Authentication: v1alpha3.AuthConfig{ - Git: v1alpha3.GitAuthConfig{ - PAT: map[string]v1alpha3.SecretConfig{}, + RecipeConfig: datamodel.RecipeConfigProperties{ + Terraform: datamodel.TerraformConfigProperties{ + Authentication: datamodel.AuthConfig{ + Git: datamodel.GitAuthConfig{ + PAT: map[string]datamodel.SecretConfig{}, }, }, }, diff --git a/pkg/recipes/driver/gitconfig.go b/pkg/recipes/driver/gitconfig.go new file mode 100644 index 0000000000..d733fa398a --- /dev/null +++ b/pkg/recipes/driver/gitconfig.go @@ -0,0 +1,96 @@ +/* +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 driver + +import ( + "errors" + "fmt" + "os/exec" + + "github.com/radius-project/radius/pkg/corerp/api/v20231001preview" + "github.com/radius-project/radius/pkg/recipes" +) + +// getURLConfigKeyValue is used to get the key and value details of the url config. +// get the secret values pat and username from secrets and create a git url in +// the format : https://:@.com and adds it to gitconfig +func getURLConfigKeyValue(secrets v20231001preview.SecretStoresClientListSecretsResponse, templatePath string) (string, string, error) { + url, err := recipes.GetGitURL(templatePath) + if err != nil { + return "", "", err + } + + //accessing the secret values and creating the git url with secret information. + var username, pat *string + path := "https://" + user, ok := secrets.Data["username"] + if ok { + username = user.Value + path += fmt.Sprintf("%s:", *username) + } + + token, ok := secrets.Data["pat"] + if ok { + pat = token.Value + path += *pat + } + + path += fmt.Sprintf("@%s", url.Hostname()) + + // git config key will be in the format of url..insteadOf + // and value returned will the original git url domain, e.g github.com + return fmt.Sprintf("url.%s.insteadOf", path), url.Hostname(), nil +} + +// Add the git credentials information to .gitconfig by running +// git config --global url.insteadOf +func addSecretsToGitConfig(secrets v20231001preview.SecretStoresClientListSecretsResponse, recipeMetadata *recipes.ResourceMetadata, templatePath string) error { + urlConfigKey, urlConfigValue, err := getURLConfigKeyValue(secrets, templatePath) + if err != nil { + return err + } + + prefix, err := recipes.GetURLPrefix(recipeMetadata) + if err != nil { + return err + } + urlConfigValue = fmt.Sprintf("%s%s", prefix, urlConfigValue) + cmd := exec.Command("git", "config", "--global", urlConfigKey, urlConfigValue) + _, err = cmd.Output() + if err != nil { + return errors.New("failed to add git config") + } + + return err +} + +// Unset the git credentials information from .gitconfig by running +// git config --global --unset url.insteadOf +func unsetSecretsFromGitConfig(secrets v20231001preview.SecretStoresClientListSecretsResponse, templatePath string) error { + urlConfigKey, _, err := getURLConfigKeyValue(secrets, templatePath) + if err != nil { + return err + } + + cmd := exec.Command("git", "config", "--global", "--unset", urlConfigKey) + _, err = cmd.Output() + if err != nil { + return errors.New("failed to unset git config") + } + + return err +} diff --git a/pkg/recipes/driver/gitconfig_test.go b/pkg/recipes/driver/gitconfig_test.go new file mode 100644 index 0000000000..b5389adea1 --- /dev/null +++ b/pkg/recipes/driver/gitconfig_test.go @@ -0,0 +1,159 @@ +/* +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 driver + +import ( + "errors" + "os" + "path/filepath" + "testing" + + "github.com/radius-project/radius/pkg/corerp/api/v20231001preview" + "github.com/radius-project/radius/pkg/to" + "github.com/stretchr/testify/require" +) + +func TestAddConfig(t *testing.T) { + tests := []struct { + desc string + templatePath string + expectedResponse string + expectedErr error + }{ + { + desc: "success", + templatePath: "git::https://github.com/project/module", + expectedResponse: "[url \"https://test-user:ghp_token@github.com\"]\n\tinsteadOf = https://env1-app1-test-redis-recipe-github.com\n", + expectedErr: nil, + }, + { + desc: "invalid git url", + templatePath: "git::https://gith ub.com/project/module", + expectedErr: errors.New("failed to parse git url"), + }, + { + desc: "invalid resource id", + templatePath: "git::https://github.com/project/module", + expectedErr: errors.New(" is not a valid resource id"), + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + tmpdir := t.TempDir() + config := withGlobalGitConfigFile(tmpdir, ``) + defer config() + _, recipeMetadata, _ := buildTestInputs() + if tt.desc == "invalid resource id" { + recipeMetadata.EnvironmentID = "//planes/radius/local/resourceGroups/r1/providers/Applications.Core/environments/env" + } + err := addSecretsToGitConfig(getSecretList(), &recipeMetadata, tt.templatePath) + if tt.expectedErr == nil { + require.NoError(t, err) + fileContent, err := os.ReadFile(filepath.Join(tmpdir, ".gitconfig")) + require.NoError(t, err) + require.Contains(t, string(fileContent), tt.expectedResponse) + } else { + require.Error(t, err) + require.Contains(t, err.Error(), tt.expectedErr.Error()) + } + }) + } + +} +func TestUnsetConfig(t *testing.T) { + tests := []struct { + desc string + templatePath string + fileContent string + expectedResponse string + expectedErr error + }{ + { + desc: "success", + templatePath: "git::https://github.com/project/module", + fileContent: ` + [url "https://test-user:ghp_token@github.com"] + insteadOf = https://env1-app1-test-redis-recipe-github.com + `, + expectedErr: nil, + }, + { + desc: "invalid url", + templatePath: "git::https://git hub.com/project/module", + fileContent: ` + [url "https://test-user:ghp_token@github.com"] + insteadOf = https://env1-app1-test-redis-recipe-github.com + `, + expectedErr: errors.New("failed to parse git url"), + }, + { + desc: "empty config file", + templatePath: "git::https://github.com/project/module", + fileContent: "", + expectedErr: errors.New("failed to unset git config"), + }, + } + for _, tt := range tests { + tmpdir := t.TempDir() + config := withGlobalGitConfigFile(tmpdir, tt.fileContent) + defer config() + err := unsetSecretsFromGitConfig(getSecretList(), tt.templatePath) + if tt.expectedErr == nil { + require.NoError(t, err) + contents, err := os.ReadFile(filepath.Join(tmpdir, ".gitconfig")) + require.NoError(t, err) + require.NotContains(t, string(contents), tt.fileContent) + } else { + require.Error(t, err) + require.Contains(t, err.Error(), tt.expectedErr.Error()) + } + } +} + +func withGlobalGitConfigFile(tmpdir string, content string) func() { + + tmpGitConfigFile := filepath.Join(tmpdir, ".gitconfig") + + os.WriteFile( + tmpGitConfigFile, + []byte(content), + 0777, + ) + + prevGitConfigEnv := os.Getenv("HOME") + os.Setenv("HOME", tmpdir) + + return func() { + os.Setenv("HOME", prevGitConfigEnv) + } +} + +func getSecretList() v20231001preview.SecretStoresClientListSecretsResponse { + secrets := v20231001preview.SecretStoresClientListSecretsResponse{ + SecretStoreListSecretsResult: v20231001preview.SecretStoreListSecretsResult{ + Data: map[string]*v20231001preview.SecretValueProperties{ + "username": { + Value: to.Ptr("test-user"), + }, + "pat": { + Value: to.Ptr("ghp_token"), + }, + }, + }, + } + return secrets +} diff --git a/pkg/recipes/driver/terraform_test.go b/pkg/recipes/driver/terraform_test.go index b47970a17d..3e595c9ff1 100644 --- a/pkg/recipes/driver/terraform_test.go +++ b/pkg/recipes/driver/terraform_test.go @@ -21,7 +21,6 @@ import ( "encoding/json" "errors" "os" - "path/filepath" "strings" "testing" @@ -29,11 +28,9 @@ import ( "github.com/google/uuid" tfjson "github.com/hashicorp/terraform-json" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" - "github.com/radius-project/radius/pkg/corerp/api/v20231001preview" "github.com/radius-project/radius/pkg/corerp/datamodel" "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/pkg/recipes/terraform" "github.com/radius-project/radius/test/testcontext" @@ -781,134 +778,3 @@ func Test_Terraform_PrepareRecipeResponse(t *testing.T) { }) } } - -func TestAddConfig(t *testing.T) { - tests := []struct { - desc string - templatePath string - expectedResponse string - expectedErr error - }{ - { - desc: "success", - templatePath: "git::https://github.com/project/module", - expectedResponse: "[url \"https://test-user:ghp_token@github.com\"]\n\tinsteadOf = https://env1-app1-test-redis-recipe-github.com\n", - expectedErr: nil, - }, - { - desc: "invalid git url", - templatePath: "git::https://gith ub.com/project/module", - expectedErr: errors.New("failed to parse git url"), - }, - { - desc: "invalid resource id", - templatePath: "git::https://github.com/project/module", - expectedErr: errors.New(" is not a valid resource id"), - }, - } - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - tmpdir := t.TempDir() - config := withGlobalGitConfigFile(tmpdir, ``) - defer config() - _, recipeMetadata, _ := buildTestInputs() - if tt.desc == "invalid resource id" { - recipeMetadata.EnvironmentID = "//planes/radius/local/resourceGroups/r1/providers/Applications.Core/environments/env" - } - err := addSecretsToGitConfig(getSecretList(), &recipeMetadata, tt.templatePath) - if tt.expectedErr == nil { - require.NoError(t, err) - fileContent, err := os.ReadFile(filepath.Join(tmpdir, ".gitconfig")) - require.NoError(t, err) - require.Contains(t, string(fileContent), tt.expectedResponse) - } else { - require.Error(t, err) - require.Contains(t, err.Error(), tt.expectedErr.Error()) - } - }) - } - -} -func TestUnsetConfig(t *testing.T) { - tests := []struct { - desc string - templatePath string - fileContent string - expectedResponse string - expectedErr error - }{ - { - desc: "success", - templatePath: "git::https://github.com/project/module", - fileContent: ` - [url "https://test-user:ghp_token@github.com"] - insteadOf = https://env1-app1-test-redis-recipe-github.com - `, - expectedErr: nil, - }, - { - desc: "invalid url", - templatePath: "git::https://git hub.com/project/module", - fileContent: ` - [url "https://test-user:ghp_token@github.com"] - insteadOf = https://env1-app1-test-redis-recipe-github.com - `, - expectedErr: errors.New("failed to parse git url"), - }, - { - desc: "empty config file", - templatePath: "git::https://github.com/project/module", - fileContent: "", - expectedErr: errors.New("failed to unset git config"), - }, - } - for _, tt := range tests { - tmpdir := t.TempDir() - config := withGlobalGitConfigFile(tmpdir, tt.fileContent) - defer config() - err := unsetSecretsFromGitConfig(getSecretList(), tt.templatePath) - if tt.expectedErr == nil { - require.NoError(t, err) - contents, err := os.ReadFile(filepath.Join(tmpdir, ".gitconfig")) - require.NoError(t, err) - require.NotContains(t, string(contents), tt.fileContent) - } else { - require.Error(t, err) - require.Contains(t, err.Error(), tt.expectedErr.Error()) - } - } -} - -func withGlobalGitConfigFile(tmpdir string, content string) func() { - - tmpGitConfigFile := filepath.Join(tmpdir, ".gitconfig") - - os.WriteFile( - tmpGitConfigFile, - []byte(content), - 0777, - ) - - prevGitConfigEnv := os.Getenv("HOME") - os.Setenv("HOME", tmpdir) - - return func() { - os.Setenv("HOME", prevGitConfigEnv) - } -} - -func getSecretList() v20231001preview.SecretStoresClientListSecretsResponse { - secrets := v20231001preview.SecretStoresClientListSecretsResponse{ - SecretStoreListSecretsResult: v20231001preview.SecretStoreListSecretsResult{ - Data: map[string]*v20231001preview.SecretValueProperties{ - "username": { - Value: to.Ptr("test-user"), - }, - "pat": { - Value: to.Ptr("ghp_token"), - }, - }, - }, - } - return secrets -} diff --git a/pkg/recipes/driver/types.go b/pkg/recipes/driver/types.go index 9253244ba2..0b981d6827 100644 --- a/pkg/recipes/driver/types.go +++ b/pkg/recipes/driver/types.go @@ -18,9 +18,6 @@ package driver import ( "context" - "errors" - "fmt" - "os/exec" "github.com/radius-project/radius/pkg/corerp/api/v20231001preview" "github.com/radius-project/radius/pkg/recipes" @@ -74,68 +71,3 @@ type DeleteOptions struct { // OutputResources is the list of output resources for the recipe. OutputResources []rpv1.OutputResource } - -// getURLConfigKeyValue is used to get the key and value details of the url config. -func getURLConfigKeyValue(secrets v20231001preview.SecretStoresClientListSecretsResponse, templatePath string) (string, string, error) { - url, err := recipes.GetGitURL(templatePath) - if err != nil { - return "", "", err - } - - var username, pat *string - path := "https://" - user, ok := secrets.Data["username"] - if ok { - username = user.Value - path += fmt.Sprintf("%s:", *username) - } - - token, ok := secrets.Data["pat"] - if ok { - pat = token.Value - path += *pat - } - - path += fmt.Sprintf("@%s", url.Hostname()) - - return fmt.Sprintf("url.%s.insteadOf", path), url.Hostname(), nil -} - -// Add the git credentials information to .gitconfig by running -// git config --global url.insteadOf -func addSecretsToGitConfig(secrets v20231001preview.SecretStoresClientListSecretsResponse, recipeMetadata *recipes.ResourceMetadata, templatePath string) error { - urlConfigKey, urlConfigValue, err := getURLConfigKeyValue(secrets, templatePath) - if err != nil { - return err - } - - prefix, err := recipes.GetURLPrefix(recipeMetadata) - if err != nil { - return err - } - urlConfigValue = fmt.Sprintf("%s%s", prefix, urlConfigValue) - cmd := exec.Command("git", "config", "--global", urlConfigKey, urlConfigValue) - _, err = cmd.Output() - if err != nil { - return errors.New("failed to add git config") - } - - return err -} - -// Unset the git credentials information from .gitconfig by running -// git config --global --unset url.insteadOf -func unsetSecretsFromGitConfig(secrets v20231001preview.SecretStoresClientListSecretsResponse, templatePath string) error { - urlConfigKey, _, err := getURLConfigKeyValue(secrets, templatePath) - if err != nil { - return err - } - - cmd := exec.Command("git", "config", "--global", "--unset", urlConfigKey) - _, err = cmd.Output() - if err != nil { - return errors.New("failed to unset git config") - } - - return err -} diff --git a/pkg/recipes/terraform/config/config_test.go b/pkg/recipes/terraform/config/config_test.go index 3b4ca55628..a9f1f90174 100644 --- a/pkg/recipes/terraform/config/config_test.go +++ b/pkg/recipes/terraform/config/config_test.go @@ -28,7 +28,6 @@ import ( "github.com/google/uuid" "github.com/stretchr/testify/require" - "github.com/radius-project/radius/pkg/controller/api/radapp.io/v1alpha3" "github.com/radius-project/radius/pkg/corerp/datamodel" "github.com/radius-project/radius/pkg/recipes" "github.com/radius-project/radius/pkg/recipes/recipecontext" @@ -185,11 +184,11 @@ func Test_NewConfig(t *testing.T) { Parameters: envParams, }, envConfig: &recipes.Configuration{ - RecipeConfig: v1alpha3.RecipeConfigProperties{ - Terraform: v1alpha3.TerraformConfigProperties{ - Authentication: v1alpha3.AuthConfig{ - Git: v1alpha3.GitAuthConfig{ - PAT: map[string]v1alpha3.SecretConfig{ + RecipeConfig: datamodel.RecipeConfigProperties{ + Terraform: datamodel.TerraformConfigProperties{ + Authentication: datamodel.AuthConfig{ + Git: datamodel.GitAuthConfig{ + PAT: map[string]datamodel.SecretConfig{ "dev.azure.com": { Secret: "secret-store1", }, diff --git a/pkg/recipes/types.go b/pkg/recipes/types.go index 2f0a083993..7b3da8892a 100644 --- a/pkg/recipes/types.go +++ b/pkg/recipes/types.go @@ -23,7 +23,6 @@ import ( "net/url" "strings" - "github.com/radius-project/radius/pkg/controller/api/radapp.io/v1alpha3" "github.com/radius-project/radius/pkg/corerp/datamodel" rpv1 "github.com/radius-project/radius/pkg/rp/v1" "github.com/radius-project/radius/pkg/ucp/resources" @@ -38,7 +37,7 @@ type Configuration struct { // Simulated represents whether the environment is simulated or not. Simulated bool - RecipeConfig v1alpha3.RecipeConfigProperties + RecipeConfig datamodel.RecipeConfigProperties } // RuntimeConfiguration represents Kubernetes Runtime configuration for the environment. diff --git a/pkg/recipes/types_test.go b/pkg/recipes/types_test.go index da42c77aef..fbfe6b790f 100644 --- a/pkg/recipes/types_test.go +++ b/pkg/recipes/types_test.go @@ -19,7 +19,7 @@ package recipes import ( "testing" - "github.com/radius-project/radius/pkg/controller/api/radapp.io/v1alpha3" + "github.com/radius-project/radius/pkg/corerp/datamodel" rpv1 "github.com/radius-project/radius/pkg/rp/v1" "github.com/stretchr/testify/require" ) @@ -213,12 +213,12 @@ func Test_GetSecretStoreID(t *testing.T) { { desc: "success", envConfig: Configuration{ - RecipeConfig: v1alpha3.RecipeConfigProperties{ - Terraform: v1alpha3.TerraformConfigProperties{ - Authentication: v1alpha3.AuthConfig{ - Git: v1alpha3.GitAuthConfig{ - PAT: map[string]v1alpha3.SecretConfig{ - "dev.azure.com": v1alpha3.SecretConfig{ + RecipeConfig: datamodel.RecipeConfigProperties{ + Terraform: datamodel.TerraformConfigProperties{ + Authentication: datamodel.AuthConfig{ + Git: datamodel.GitAuthConfig{ + PAT: map[string]datamodel.SecretConfig{ + "dev.azure.com": datamodel.SecretConfig{ Secret: "secret-store1", }, }, From b843bc77929679770f97326c0831b59607f09fb6 Mon Sep 17 00:00:00 2001 From: Vishwanath Hiremath Date: Thu, 22 Feb 2024 16:47:08 -0800 Subject: [PATCH 16/22] addressing comments Signed-off-by: Vishwanath Hiremath --- .../environment_conversion.go | 1 - pkg/recipes/configloader/secrets.go | 2 +- pkg/recipes/controllerconfig/config.go | 2 +- pkg/recipes/driver/gitconfig.go | 34 ++++++++++++------- pkg/recipes/driver/terraform.go | 10 +++--- pkg/recipes/driver/terraform_test.go | 2 +- pkg/recipes/engine/engine.go | 7 ++-- pkg/recipes/terraform/config/config.go | 9 +++++ 8 files changed, 44 insertions(+), 23 deletions(-) diff --git a/pkg/corerp/api/v20231001preview/environment_conversion.go b/pkg/corerp/api/v20231001preview/environment_conversion.go index 23ee1fde0c..a5d2ff2d6f 100644 --- a/pkg/corerp/api/v20231001preview/environment_conversion.go +++ b/pkg/corerp/api/v20231001preview/environment_conversion.go @@ -202,7 +202,6 @@ func toRecipeConfigDatamodel(config *RecipeConfigProperties) datamodel.RecipeCon } recipeConfig.Terraform.Authentication.Git.PAT = p } - } } } diff --git a/pkg/recipes/configloader/secrets.go b/pkg/recipes/configloader/secrets.go index 65bf8f560f..9c2707e070 100644 --- a/pkg/recipes/configloader/secrets.go +++ b/pkg/recipes/configloader/secrets.go @@ -27,7 +27,7 @@ func NewSecretStoreLoader(armOptions *arm.ClientOptions) SecretsLoader { return SecretsLoader{ArmClientOptions: armOptions} } -// SecretsLoader struct provides functionality to get secret information from secret stores. +// SecretsLoader struct provides functionality to get secret information from Application.Core/SecretStore resource. type SecretsLoader struct { ArmClientOptions *arm.ClientOptions } diff --git a/pkg/recipes/controllerconfig/config.go b/pkg/recipes/controllerconfig/config.go index e94009728d..6ddeb1ef19 100644 --- a/pkg/recipes/controllerconfig/config.go +++ b/pkg/recipes/controllerconfig/config.go @@ -103,7 +103,7 @@ func New(options hostoptions.HostOptions) (*RecipeControllerConfig, error) { recipes.TemplateKindTerraform: driver.NewTerraformDriver(options.UCPConnection, provider.NewSecretProvider(options.Config.SecretProvider), driver.TerraformOptions{ Path: options.Config.Terraform.Path, - }, cfg.K8sClients.ClientSet, clientOptions), + }, cfg.K8sClients.ClientSet), }, }) diff --git a/pkg/recipes/driver/gitconfig.go b/pkg/recipes/driver/gitconfig.go index d733fa398a..0358aabfba 100644 --- a/pkg/recipes/driver/gitconfig.go +++ b/pkg/recipes/driver/gitconfig.go @@ -19,21 +19,15 @@ package driver import ( "errors" "fmt" + "net/url" "os/exec" "github.com/radius-project/radius/pkg/corerp/api/v20231001preview" "github.com/radius-project/radius/pkg/recipes" ) -// getURLConfigKeyValue is used to get the key and value details of the url config. -// get the secret values pat and username from secrets and create a git url in -// the format : https://:@.com and adds it to gitconfig -func getURLConfigKeyValue(secrets v20231001preview.SecretStoresClientListSecretsResponse, templatePath string) (string, string, error) { - url, err := recipes.GetGitURL(templatePath) - if err != nil { - return "", "", err - } - +// getGitURLWithSecrets returns the git URL with secrets information added. +func getGitURLWithSecrets(secrets v20231001preview.SecretStoresClientListSecretsResponse, url *url.URL) string { //accessing the secret values and creating the git url with secret information. var username, pat *string path := "https://" @@ -48,16 +42,32 @@ func getURLConfigKeyValue(secrets v20231001preview.SecretStoresClientListSecrets pat = token.Value path += *pat } - path += fmt.Sprintf("@%s", url.Hostname()) + return path +} + +// getURLConfigKeyValue is used to get the key and value details of the url config. +// get the secret values pat and username from secrets and create a git url in +// the format : https://:@.com and adds it to gitconfig +func getURLConfigKeyValue(secrets v20231001preview.SecretStoresClientListSecretsResponse, templatePath string) (string, string, error) { + url, err := recipes.GetGitURL(templatePath) + if err != nil { + return "", "", err + } + + path := getGitURLWithSecrets(secrets, url) + // git config key will be in the format of url..insteadOf // and value returned will the original git url domain, e.g github.com return fmt.Sprintf("url.%s.insteadOf", path), url.Hostname(), nil } -// Add the git credentials information to .gitconfig by running -// git config --global url.insteadOf +// Updates the global Git configuration with credentials for a recipe template path and prefixes the path with environment, application, and resource name to make the entry unique to each recipe execution operation. +// +// Retrieves the git credentials from the provided secrets object +// and adds them to the Git config by running +// git config --global url.insteadOf . func addSecretsToGitConfig(secrets v20231001preview.SecretStoresClientListSecretsResponse, recipeMetadata *recipes.ResourceMetadata, templatePath string) error { urlConfigKey, urlConfigValue, err := getURLConfigKeyValue(secrets, templatePath) if err != nil { diff --git a/pkg/recipes/driver/terraform.go b/pkg/recipes/driver/terraform.go index d597bf6a6b..1800a2952a 100644 --- a/pkg/recipes/driver/terraform.go +++ b/pkg/recipes/driver/terraform.go @@ -25,7 +25,6 @@ import ( "reflect" "strings" - "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" "github.com/google/uuid" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" "github.com/radius-project/radius/pkg/corerp/api/v20231001preview" @@ -51,9 +50,8 @@ import ( var _ Driver = (*terraformDriver)(nil) // NewTerraformDriver creates a new instance of driver to execute a Terraform recipe. -func NewTerraformDriver(ucpConn sdk.Connection, secretProvider *ucp_provider.SecretProvider, options TerraformOptions, k8sClientSet kubernetes.Interface, armOptions *arm.ClientOptions) Driver { +func NewTerraformDriver(ucpConn sdk.Connection, secretProvider *ucp_provider.SecretProvider, options TerraformOptions, k8sClientSet kubernetes.Interface) Driver { return &terraformDriver{ - ArmClientOptions: armOptions, terraformExecutor: terraform.NewExecutor(ucpConn, secretProvider, k8sClientSet), options: options, } @@ -67,7 +65,6 @@ type TerraformOptions struct { // terraformDriver represents a driver to interact with Terraform Recipe - deploy recipe, delete resources, etc. type terraformDriver struct { - ArmClientOptions *arm.ClientOptions // terraformExecutor is used to execute Terraform commands - deploy, destroy, etc. terraformExecutor terraform.TerraformExecutor @@ -97,7 +94,10 @@ func (d *terraformDriver) Execute(ctx context.Context, opts ExecuteOptions) (*re // Add credential information to .gitconfig if module source is of type git. if strings.HasPrefix(opts.Definition.TemplatePath, "git::") && !reflect.DeepEqual(opts.BaseOptions.Secrets, v20231001preview.SecretStoresClientListSecretsResponse{}) { - addSecretsToGitConfig(opts.BaseOptions.Secrets, &opts.Recipe, opts.Definition.TemplatePath) + err := addSecretsToGitConfig(opts.BaseOptions.Secrets, &opts.Recipe, opts.Definition.TemplatePath) + if err != nil { + return nil, err + } } tfState, err := d.terraformExecutor.Deploy(ctx, terraform.Options{ diff --git a/pkg/recipes/driver/terraform_test.go b/pkg/recipes/driver/terraform_test.go index 3e595c9ff1..3e42acdcb8 100644 --- a/pkg/recipes/driver/terraform_test.go +++ b/pkg/recipes/driver/terraform_test.go @@ -41,7 +41,7 @@ func setup(t *testing.T) (terraform.MockTerraformExecutor, terraformDriver) { ctrl := gomock.NewController(t) tfExecutor := terraform.NewMockTerraformExecutor(ctrl) - driver := terraformDriver{nil, tfExecutor, TerraformOptions{Path: t.TempDir()}} + driver := terraformDriver{tfExecutor, TerraformOptions{Path: t.TempDir()}} return *tfExecutor, driver } diff --git a/pkg/recipes/engine/engine.go b/pkg/recipes/engine/engine.go index 0921c8cf67..2571c2f2f9 100644 --- a/pkg/recipes/engine/engine.go +++ b/pkg/recipes/engine/engine.go @@ -83,16 +83,19 @@ func (e *engine) executeCore(ctx context.Context, recipe recipes.ResourceMetadat return nil, definition, recipes.NewRecipeError(recipes.RecipeConfigurationFailure, err.Error(), util.RecipeSetupError, recipes.GetErrorDetails(err)) } - secrets := v20231001preview.SecretStoresClientListSecretsResponse{} + // Retrieves the secret store id from the recipes configuration for the terraform module source of type git. + // secretStoreID returned will be an empty string for other types. secretStore, err := recipes.GetSecretStoreID(*configuration, definition.TemplatePath) if err != nil { return nil, nil, err } + // Retrieves the secret values from the secret store ID provided. + secrets := v20231001preview.SecretStoresClientListSecretsResponse{} if secretStore != "" { secrets, err = e.options.SecretsLoader.LoadSecrets(ctx, secretStore) if err != nil { - return nil, nil, err + return nil, nil, fmt.Errorf("failed to fetch secrets from the secret store resource id %s for Terraform recipe %s deployment: %w", secretStore, definition.TemplatePath, err) } } diff --git a/pkg/recipes/terraform/config/config.go b/pkg/recipes/terraform/config/config.go index a17a48b7ba..30ff5691a4 100644 --- a/pkg/recipes/terraform/config/config.go +++ b/pkg/recipes/terraform/config/config.go @@ -43,19 +43,28 @@ func New(ctx context.Context, moduleName string, envRecipe *recipes.EnvironmentD path := envRecipe.TemplatePath if envConfig != nil { + // Retrieving the secret store with associated with the template path. + // appends an URL prefix to the templatePath if secret store exists. secretStore, err := recipes.GetSecretStoreID(*envConfig, envRecipe.TemplatePath) if err != nil { return nil, err } + if secretStore != "" { + // Retrieving the URL prefix, prefix will be in the format of https://--- prefix, err := recipes.GetURLPrefix(resourceRecipe) if err != nil { return nil, err } + url, err := recipes.GetGitURL(envRecipe.TemplatePath) if err != nil { return nil, err } + + // Adding URL prefix to the template path. + // Adding the prefix helps to access the the right credential information for git across environments. + // Updated template path will be added to the terraform config. path = fmt.Sprintf("git::%s%s", prefix, strings.TrimPrefix(url.String(), "https://")) } } From 60232024c7a8ae3d9c94eed06e1fe709cd86d348 Mon Sep 17 00:00:00 2001 From: Vishwanath Hiremath Date: Fri, 23 Feb 2024 00:16:00 -0800 Subject: [PATCH 17/22] addressing comments Signed-off-by: Vishwanath Hiremath --- .../api/v20231001preview/environment_conversion_test.go | 4 +--- .../environmentresource-with-workload-identity.json | 4 +--- ...environmentresourcedatamodel-with-workload-identity.json | 4 +--- pkg/recipes/driver/gitconfig.go | 6 +++--- pkg/recipes/types_test.go | 2 ++ 5 files changed, 8 insertions(+), 12 deletions(-) diff --git a/pkg/corerp/api/v20231001preview/environment_conversion_test.go b/pkg/corerp/api/v20231001preview/environment_conversion_test.go index 45b9f95b48..68d61c7050 100644 --- a/pkg/corerp/api/v20231001preview/environment_conversion_test.go +++ b/pkg/corerp/api/v20231001preview/environment_conversion_test.go @@ -76,9 +76,7 @@ func TestConvertVersionedToDataModel(t *testing.T) { RecipeConfig: datamodel.RecipeConfigProperties{ Terraform: datamodel.TerraformConfigProperties{ Authentication: datamodel.AuthConfig{ - Git: datamodel.GitAuthConfig{ - PAT: map[string]datamodel.SecretConfig{}, - }, + Git: datamodel.GitAuthConfig{}, }, }, }, diff --git a/pkg/corerp/api/v20231001preview/testdata/environmentresource-with-workload-identity.json b/pkg/corerp/api/v20231001preview/testdata/environmentresource-with-workload-identity.json index 3522684771..11ce3dc424 100644 --- a/pkg/corerp/api/v20231001preview/testdata/environmentresource-with-workload-identity.json +++ b/pkg/corerp/api/v20231001preview/testdata/environmentresource-with-workload-identity.json @@ -21,9 +21,7 @@ "recipeConfig": { "terraform": { "authentication": { - "git": { - "pat": {} - } + "git": {} } } }, diff --git a/pkg/corerp/api/v20231001preview/testdata/environmentresourcedatamodel-with-workload-identity.json b/pkg/corerp/api/v20231001preview/testdata/environmentresourcedatamodel-with-workload-identity.json index d2c054b9d5..c66221ed4b 100644 --- a/pkg/corerp/api/v20231001preview/testdata/environmentresourcedatamodel-with-workload-identity.json +++ b/pkg/corerp/api/v20231001preview/testdata/environmentresourcedatamodel-with-workload-identity.json @@ -34,9 +34,7 @@ "recipeConfig": { "terraform": { "authentication": { - "git": { - "pat": {} - } + "git": {} } } }, diff --git a/pkg/recipes/driver/gitconfig.go b/pkg/recipes/driver/gitconfig.go index 0358aabfba..f8a00b949e 100644 --- a/pkg/recipes/driver/gitconfig.go +++ b/pkg/recipes/driver/gitconfig.go @@ -28,7 +28,7 @@ import ( // getGitURLWithSecrets returns the git URL with secrets information added. func getGitURLWithSecrets(secrets v20231001preview.SecretStoresClientListSecretsResponse, url *url.URL) string { - //accessing the secret values and creating the git url with secret information. + // accessing the secret values and creating the git url with secret information. var username, pat *string path := "https://" user, ok := secrets.Data["username"] @@ -85,7 +85,7 @@ func addSecretsToGitConfig(secrets v20231001preview.SecretStoresClientListSecret return errors.New("failed to add git config") } - return err + return nil } // Unset the git credentials information from .gitconfig by running @@ -102,5 +102,5 @@ func unsetSecretsFromGitConfig(secrets v20231001preview.SecretStoresClientListSe return errors.New("failed to unset git config") } - return err + return nil } diff --git a/pkg/recipes/types_test.go b/pkg/recipes/types_test.go index fbfe6b790f..38dfab59be 100644 --- a/pkg/recipes/types_test.go +++ b/pkg/recipes/types_test.go @@ -170,6 +170,7 @@ func Test_GetEnvAppResourceNames(t *testing.T) { }) } } + func Test_GetGitURL(t *testing.T) { tests := []struct { desc string @@ -202,6 +203,7 @@ func Test_GetGitURL(t *testing.T) { } } + func Test_GetSecretStoreID(t *testing.T) { tests := []struct { desc string From 7f89d772122f54b8eaef08ec1da6502607caca2b Mon Sep 17 00:00:00 2001 From: Vishwanath Hiremath Date: Fri, 23 Feb 2024 10:57:29 -0800 Subject: [PATCH 18/22] adding typespec examples Signed-off-by: Vishwanath Hiremath --- .../examples/Environments_CreateOrUpdate.json | 13 +++++++++++++ .../examples/Environments_GetEnv0.json | 13 +++++++++++++ .../examples/Environments_List.json | 13 +++++++++++++ .../examples/Environments_PatchEnv0.json | 13 +++++++++++++ .../Environments_CreateOrUpdate.json | 13 +++++++++++++ .../2023-10-01-preview/Environments_GetEnv0.json | 13 +++++++++++++ .../2023-10-01-preview/Environments_List.json | 13 +++++++++++++ .../2023-10-01-preview/Environments_PatchEnv0.json | 13 +++++++++++++ 8 files changed, 104 insertions(+) diff --git a/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/examples/Environments_CreateOrUpdate.json b/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/examples/Environments_CreateOrUpdate.json index 9998a5dd1f..6de0e8d45f 100644 --- a/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/examples/Environments_CreateOrUpdate.json +++ b/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/examples/Environments_CreateOrUpdate.json @@ -17,6 +17,19 @@ "oidcIssuer": "https://oidcissuer/oidc" } }, + "recipeConfig": { + "terraform": { + "authentication": { + "git": { + "pat": { + "dev.azure.com":{ + "secret":"/planes/radius/local/resourcegroups/default/providers/Applications.Core/secretStores/github" + } + } + } + } + } + }, "recipes": { "Applications.Datastores/mongoDatabases":{ "cosmos-recipe": { diff --git a/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/examples/Environments_GetEnv0.json b/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/examples/Environments_GetEnv0.json index 8e7c297726..667d7ed7fe 100644 --- a/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/examples/Environments_GetEnv0.json +++ b/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/examples/Environments_GetEnv0.json @@ -28,6 +28,19 @@ "scope":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup" } }, + "recipeConfig": { + "terraform": { + "authentication": { + "git": { + "pat": { + "dev.azure.com":{ + "secret":"/planes/radius/local/resourcegroups/default/providers/Applications.Core/secretStores/github" + } + } + } + } + } + }, "recipes": { "Applications.Datastores/mongoDatabases":{ "cosmos-recipe": { diff --git a/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/examples/Environments_List.json b/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/examples/Environments_List.json index 4d7e7fc258..576046456f 100644 --- a/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/examples/Environments_List.json +++ b/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/examples/Environments_List.json @@ -29,6 +29,19 @@ "scope":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup" } }, + "recipeConfig": { + "terraform": { + "authentication": { + "git": { + "pat": { + "dev.azure.com":{ + "secret":"/planes/radius/local/resourcegroups/default/providers/Applications.Core/secretStores/github" + } + } + } + } + } + }, "recipes": { "Applications.Datastores/mongoDatabases":{ "cosmos-recipe": { diff --git a/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/examples/Environments_PatchEnv0.json b/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/examples/Environments_PatchEnv0.json index a193b84236..169fedb9f7 100644 --- a/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/examples/Environments_PatchEnv0.json +++ b/swagger/specification/applications/resource-manager/Applications.Core/preview/2023-10-01-preview/examples/Environments_PatchEnv0.json @@ -54,6 +54,19 @@ } } }, + "recipeConfig": { + "terraform": { + "authentication": { + "git": { + "pat": { + "dev.azure.com":{ + "secret":"/planes/radius/local/resourcegroups/default/providers/Applications.Core/secretStores/github" + } + } + } + } + } + }, "extensions": [ { "kind": "kubernetesMetadata", diff --git a/typespec/Applications.Core/examples/2023-10-01-preview/Environments_CreateOrUpdate.json b/typespec/Applications.Core/examples/2023-10-01-preview/Environments_CreateOrUpdate.json index 9998a5dd1f..6de0e8d45f 100644 --- a/typespec/Applications.Core/examples/2023-10-01-preview/Environments_CreateOrUpdate.json +++ b/typespec/Applications.Core/examples/2023-10-01-preview/Environments_CreateOrUpdate.json @@ -17,6 +17,19 @@ "oidcIssuer": "https://oidcissuer/oidc" } }, + "recipeConfig": { + "terraform": { + "authentication": { + "git": { + "pat": { + "dev.azure.com":{ + "secret":"/planes/radius/local/resourcegroups/default/providers/Applications.Core/secretStores/github" + } + } + } + } + } + }, "recipes": { "Applications.Datastores/mongoDatabases":{ "cosmos-recipe": { diff --git a/typespec/Applications.Core/examples/2023-10-01-preview/Environments_GetEnv0.json b/typespec/Applications.Core/examples/2023-10-01-preview/Environments_GetEnv0.json index 8e7c297726..667d7ed7fe 100644 --- a/typespec/Applications.Core/examples/2023-10-01-preview/Environments_GetEnv0.json +++ b/typespec/Applications.Core/examples/2023-10-01-preview/Environments_GetEnv0.json @@ -28,6 +28,19 @@ "scope":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup" } }, + "recipeConfig": { + "terraform": { + "authentication": { + "git": { + "pat": { + "dev.azure.com":{ + "secret":"/planes/radius/local/resourcegroups/default/providers/Applications.Core/secretStores/github" + } + } + } + } + } + }, "recipes": { "Applications.Datastores/mongoDatabases":{ "cosmos-recipe": { diff --git a/typespec/Applications.Core/examples/2023-10-01-preview/Environments_List.json b/typespec/Applications.Core/examples/2023-10-01-preview/Environments_List.json index 4d7e7fc258..576046456f 100644 --- a/typespec/Applications.Core/examples/2023-10-01-preview/Environments_List.json +++ b/typespec/Applications.Core/examples/2023-10-01-preview/Environments_List.json @@ -29,6 +29,19 @@ "scope":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup" } }, + "recipeConfig": { + "terraform": { + "authentication": { + "git": { + "pat": { + "dev.azure.com":{ + "secret":"/planes/radius/local/resourcegroups/default/providers/Applications.Core/secretStores/github" + } + } + } + } + } + }, "recipes": { "Applications.Datastores/mongoDatabases":{ "cosmos-recipe": { diff --git a/typespec/Applications.Core/examples/2023-10-01-preview/Environments_PatchEnv0.json b/typespec/Applications.Core/examples/2023-10-01-preview/Environments_PatchEnv0.json index a193b84236..169fedb9f7 100644 --- a/typespec/Applications.Core/examples/2023-10-01-preview/Environments_PatchEnv0.json +++ b/typespec/Applications.Core/examples/2023-10-01-preview/Environments_PatchEnv0.json @@ -54,6 +54,19 @@ } } }, + "recipeConfig": { + "terraform": { + "authentication": { + "git": { + "pat": { + "dev.azure.com":{ + "secret":"/planes/radius/local/resourcegroups/default/providers/Applications.Core/secretStores/github" + } + } + } + } + } + }, "extensions": [ { "kind": "kubernetesMetadata", From 2ada35fcc2620b91e0d660286d3bfed73f9d72d4 Mon Sep 17 00:00:00 2001 From: Vishwanath Hiremath Date: Fri, 23 Feb 2024 12:05:12 -0800 Subject: [PATCH 19/22] Trigger Build Signed-off-by: Vishwanath Hiremath From fcdc510c3c34d7696475986d23dc732c3d914ec3 Mon Sep 17 00:00:00 2001 From: Vishwanath Hiremath Date: Fri, 23 Feb 2024 12:09:28 -0800 Subject: [PATCH 20/22] Trigger Build Signed-off-by: Vishwanath Hiremath From 8c178ca0ab75708c91b308cacfa6406c6f61b4c5 Mon Sep 17 00:00:00 2001 From: Vishwanath Hiremath Date: Fri, 23 Feb 2024 12:16:42 -0800 Subject: [PATCH 21/22] adding error for write file Signed-off-by: Vishwanath Hiremath --- pkg/recipes/driver/gitconfig_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/recipes/driver/gitconfig_test.go b/pkg/recipes/driver/gitconfig_test.go index b5389adea1..657753ddb9 100644 --- a/pkg/recipes/driver/gitconfig_test.go +++ b/pkg/recipes/driver/gitconfig_test.go @@ -128,12 +128,13 @@ func withGlobalGitConfigFile(tmpdir string, content string) func() { tmpGitConfigFile := filepath.Join(tmpdir, ".gitconfig") - os.WriteFile( + err:=os.WriteFile( tmpGitConfigFile, []byte(content), 0777, ) + require.NoError(t,err) prevGitConfigEnv := os.Getenv("HOME") os.Setenv("HOME", tmpdir) From c7091f015c98da618265453b67e3a34323b52f1d Mon Sep 17 00:00:00 2001 From: Vishwanath Hiremath Date: Fri, 23 Feb 2024 12:24:02 -0800 Subject: [PATCH 22/22] fixing linter error Signed-off-by: Vishwanath Hiremath --- pkg/recipes/driver/gitconfig_test.go | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/pkg/recipes/driver/gitconfig_test.go b/pkg/recipes/driver/gitconfig_test.go index 657753ddb9..f7a11329a3 100644 --- a/pkg/recipes/driver/gitconfig_test.go +++ b/pkg/recipes/driver/gitconfig_test.go @@ -54,13 +54,14 @@ func TestAddConfig(t *testing.T) { for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { tmpdir := t.TempDir() - config := withGlobalGitConfigFile(tmpdir, ``) + config, err := withGlobalGitConfigFile(tmpdir, ``) + require.NoError(t, err) defer config() _, recipeMetadata, _ := buildTestInputs() if tt.desc == "invalid resource id" { recipeMetadata.EnvironmentID = "//planes/radius/local/resourceGroups/r1/providers/Applications.Core/environments/env" } - err := addSecretsToGitConfig(getSecretList(), &recipeMetadata, tt.templatePath) + err = addSecretsToGitConfig(getSecretList(), &recipeMetadata, tt.templatePath) if tt.expectedErr == nil { require.NoError(t, err) fileContent, err := os.ReadFile(filepath.Join(tmpdir, ".gitconfig")) @@ -109,9 +110,10 @@ func TestUnsetConfig(t *testing.T) { } for _, tt := range tests { tmpdir := t.TempDir() - config := withGlobalGitConfigFile(tmpdir, tt.fileContent) + config, err := withGlobalGitConfigFile(tmpdir, tt.fileContent) + require.NoError(t, err) defer config() - err := unsetSecretsFromGitConfig(getSecretList(), tt.templatePath) + err = unsetSecretsFromGitConfig(getSecretList(), tt.templatePath) if tt.expectedErr == nil { require.NoError(t, err) contents, err := os.ReadFile(filepath.Join(tmpdir, ".gitconfig")) @@ -124,23 +126,25 @@ func TestUnsetConfig(t *testing.T) { } } -func withGlobalGitConfigFile(tmpdir string, content string) func() { +func withGlobalGitConfigFile(tmpdir string, content string) (func(), error) { tmpGitConfigFile := filepath.Join(tmpdir, ".gitconfig") - err:=os.WriteFile( + err := os.WriteFile( tmpGitConfigFile, []byte(content), 0777, ) - require.NoError(t,err) + if err != nil { + return func() {}, err + } prevGitConfigEnv := os.Getenv("HOME") os.Setenv("HOME", tmpdir) return func() { os.Setenv("HOME", prevGitConfigEnv) - } + }, nil } func getSecretList() v20231001preview.SecretStoresClientListSecretsResponse {