Skip to content

Commit

Permalink
Add capabilities to resource type API (#8182)
Browse files Browse the repository at this point in the history
# Description

This change adds the 'capabilities' concept to the resource type API.

- Capabilities enable resource types to indicate the schema and
behaviors they support.
- Capabilities enable clients like the `rad` CLI to understand the
behaviors of resource types dynamically.

For example, we're adding the `SupportsRecipes` capability.

- All resource types that support recipes should declare this
capability. This is how a UDT will opt-in to recipe functionality during
provisioning.
- The `rad` CLI functionality for `rad recipe register` can introspect
the resource type to validate recipe support, rather than hardcoding
which types have the support and which don't.

----

Description of the changes:

- The manifests previously supported capabilities as part of the API
version, we're moving this to the resource type for a simplification.
- The manifest entry for capabilities wasn't sent to the server. Now it
is.
- Updated API, converters, and UCP functionality.


## Type of change

- This pull request adds or changes features of Radius and has an
approved issue (issue link required).


Part of: #6688

## Contributor checklist
Please verify that the PR meets the following requirements, where
applicable:

- [ ] An overview of proposed schema changes is included in a linked
GitHub issue.
- [ ] A design document PR is created in the [design-notes
repository](https://github.com/radius-project/design-notes/), if new
APIs are being introduced.
- [ ] If applicable, design document has been reviewed and approved by
Radius maintainers/approvers.
- [ ] A PR for the [samples
repository](https://github.com/radius-project/samples) is created, if
existing samples are affected by the changes in this PR.
- [ ] A PR for the [documentation
repository](https://github.com/radius-project/docs) is created, if the
changes in this PR affect the documentation or any user facing updates
are made.
- [ ] A PR for the [recipes
repository](https://github.com/radius-project/recipes) is created, if
existing recipes are affected by the changes in this PR.

Signed-off-by: Ryan Nowak <[email protected]>
  • Loading branch information
rynowak authored Jan 4, 2025
1 parent 46dc40d commit 75d093a
Show file tree
Hide file tree
Showing 41 changed files with 191 additions and 41 deletions.
14 changes: 7 additions & 7 deletions deploy/manifest/built-in-providers/applications_core.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,34 @@ types:
apiVersions:
"2025-01-01-preview":
schema: {}
capabilities: []
capabilities: []
applications:
apiVersions:
"2025-01-01-preview":
schema: {}
capabilities: []
capabilities: []
environments:
apiVersions:
"2025-01-01-preview":
schema: {}
capabilities: []
capabilities: []
gateways:
apiVersions:
"2025-01-01-preview":
schema: {}
capabilities: []
capabilities: []
secretStores:
apiVersions:
"2025-01-01-preview":
schema: {}
capabilities: []
capabilities: []
extenders:
apiVersions:
"2025-01-01-preview":
schema: {}
capabilities: ["Recipes"]
capabilities: ["SupportsRecipes"]
volumes:
apiVersions:
"2025-01-01-preview":
schema: {}
capabilities: []
capabilities: []
8 changes: 4 additions & 4 deletions deploy/manifest/built-in-providers/applications_dapr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@ types:
apiVersions:
"2025-01-01-preview":
schema: {}
capabilities: ["Recipes"]
capabilities: ["SupportsRecipes"]
pubSubBrokers:
apiVersions:
"2025-01-01-preview":
schema: {}
capabilities: ["Recipes"]
capabilities: ["SupportsRecipes"]
secretStores:
apiVersions:
"2025-01-01-preview":
schema: {}
capabilities: ["Recipes"]
capabilities: ["SupportsRecipes"]
stateStores:
apiVersions:
"2025-01-01-preview":
schema: {}
capabilities: ["Recipes"]
capabilities: ["SupportsRecipes"]
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ types:
apiVersions:
"2025-01-01-preview":
schema: {}
capabilities: ["Recipes"]
capabilities: ["SupportsRecipes"]
sqlDatabases:
apiVersions:
"2025-01-01-preview":
schema: {}
capabilities: ["Recipes"]
capabilities: ["SupportsRecipes"]
redisCaches:
apiVersions:
"2025-01-01-preview":
schema: {}
capabilities: ["Recipes"]
capabilities: ["SupportsRecipes"]
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ types:
apiVersions:
"2025-01-01-preview":
schema: {}
capabilities: ["Recipes"]
capabilities: ["SupportsRecipes"]
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ types:
apiVersions:
"2025-01-01-preview":
schema: {}
capabilities: []
capabilities: []

Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ types:
apiVersions:
'2025-01-01-preview':
schema: {}
capabilities: ["Recipes"]
capabilities: ["SupportsRecipes"]
2 changes: 1 addition & 1 deletion pkg/cli/cmd/resourceprovider/create/testdata/valid.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ types:
apiVersions:
'2025-01-01-preview':
schema: {}
capabilities: ["Recipes"]
capabilities: ["SupportsRecipes"]
6 changes: 3 additions & 3 deletions pkg/cli/manifest/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ type ResourceProvider struct {

// ResourceType represents a resource type in a resource provider manifest.
type ResourceType struct {
// Capabilities is a list of capabilities for the resource type.
Capabilities []string `yaml:"capabilities" validate:"dive,capability"`

// DefaultAPIVersion is the default API version for the resource type.
DefaultAPIVersion *string `yaml:"defaultApiVersion,omitempty" validate:"omitempty,apiVersion"`

Expand All @@ -40,7 +43,4 @@ type ResourceTypeAPIVersion struct {
// TODO: this allows anything right now, and will be ignored. We'll improve this in
// a future pull-request.
Schema any `yaml:"schema" validate:"required"`

// Capabilities is a list of capabilities for the resource type.
Capabilities []string `yaml:"capabilities" validate:"dive,capability"`
}
8 changes: 4 additions & 4 deletions pkg/cli/manifest/manifest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ func TestReadFileYAML(t *testing.T) {
"testResources": {
APIVersions: map[string]*ResourceTypeAPIVersion{
"2025-01-01-preview": {
Schema: map[string]any{},
Capabilities: []string{"Recipes"},
Schema: map[string]any{},
},
},
Capabilities: []string{"SupportsRecipes"},
},
},
}
Expand Down Expand Up @@ -70,10 +70,10 @@ func TestReadFileJSON(t *testing.T) {
"testResources": {
APIVersions: map[string]*ResourceTypeAPIVersion{
"2025-01-01-preview": {
Schema: map[string]any{},
Capabilities: []string{"Recipes"},
Schema: map[string]any{},
},
},
Capabilities: []string{"SupportsRecipes"},
},
},
}
Expand Down
1 change: 1 addition & 0 deletions pkg/cli/manifest/registermanifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ func RegisterFile(ctx context.Context, clientFactory *v20231001preview.ClientFac
logIfEnabled(logger, "Creating resource type %s/%s", resourceProvider.Name, resourceTypeName)
resourceTypePoller, err := clientFactory.NewResourceTypesClient().BeginCreateOrUpdate(ctx, planeName, resourceProvider.Name, resourceTypeName, v20231001preview.ResourceTypeResource{
Properties: &v20231001preview.ResourceTypeProperties{
Capabilities: to.SliceOfPtrs(resourceType.Capabilities...),
DefaultAPIVersion: resourceType.DefaultAPIVersion,
},
}, nil)
Expand Down
4 changes: 2 additions & 2 deletions pkg/cli/manifest/testdata/duplicate-key.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ types:
apiVersions:
'2025-01-01-preview':
schema: {}
capabilities: ["Recipes"]
capabilities: ["SupportsRecipes"]
types:
testResources:
apiVersions:
'2025-01-01-preview':
schema: {}
capabilities: ["Recipes"]
capabilities: ["SupportsRecipes"]
2 changes: 1 addition & 1 deletion pkg/cli/manifest/testdata/invalid-yaml.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ types:
apiVersions:
'2025-01-01-preview':
schema: {}
capabilities: ["Recipes"]
capabilities: ["SupportsRecipes"]
2 changes: 1 addition & 1 deletion pkg/cli/manifest/testdata/missing-required-field.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ types:
apiVersions:
'2025-01-01-preview':
schema: {}
capabilities: ["Recipes"]
capabilities: ["SupportsRecipes"]
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ types:
apiVersions:
"2025-01-01-preview":
schema: {}
capabilities: []
capabilities: []
testResource2:
apiVersions:
"2025-01-01-preview":
schema: {}
capabilities: []
capabilities: []
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ types:
apiVersions:
"2025-01-01-preview":
schema: {}
capabilities: ["Recipes"]
capabilities: ["SupportsRecipes"]
testResource4:
apiVersions:
"2025-01-01-preview":
schema: {}
capabilities: ["Recipes"]
capabilities: ["SupportsRecipes"]
6 changes: 3 additions & 3 deletions pkg/cli/manifest/testdata/valid.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
"testResources": {
"apiVersions": {
"2025-01-01-preview": {
"schema": {},
"capabilities": ["Recipes"]
"schema": {}
}
}
},
"capabilities": ["SupportsRecipes"]
}
}
}
2 changes: 1 addition & 1 deletion pkg/cli/manifest/testdata/valid.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ types:
apiVersions:
'2025-01-01-preview':
schema: {}
capabilities: ["Recipes"]
capabilities: ["SupportsRecipes"]
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ func (dst *ResourceProviderSummary) ConvertFrom(src v1.DataModelInterface) error
dst.ResourceTypes = map[string]*ResourceProviderSummaryResourceType{}
for resourceTypeName, resourceType := range dm.Properties.ResourceTypes {
dst.ResourceTypes[resourceTypeName] = &ResourceProviderSummaryResourceType{
Capabilities: to.SliceOfPtrs(resourceType.Capabilities...),
DefaultAPIVersion: resourceType.DefaultAPIVersion,
APIVersions: map[string]map[string]any{},
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ func Test_ResourceProviderSummary_DataModelToVersioned(t *testing.T) {
},
ResourceTypes: map[string]*ResourceProviderSummaryResourceType{
"testResources": {
Capabilities: []*string{to.Ptr("SupportsRecipes")},
DefaultAPIVersion: to.Ptr("2025-01-01"),
APIVersions: map[string]map[string]any{
"2025-01-01": {},
Expand Down
26 changes: 26 additions & 0 deletions pkg/ucp/api/v20231001preview/resourcetype_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ limitations under the License.
package v20231001preview

import (
"fmt"

v1 "github.com/radius-project/radius/pkg/armrpc/api/v1"
"github.com/radius-project/radius/pkg/to"
"github.com/radius-project/radius/pkg/ucp/datamodel"
Expand All @@ -39,7 +41,18 @@ func (src *ResourceTypeResource) ConvertTo() (v1.DataModelInterface, error) {
},
}

capabilities := []string{}
for _, capability := range src.Properties.Capabilities {
err := validateCapability(capability)
if err != nil {
return nil, err
}

capabilities = append(capabilities, *capability)
}

dst.Properties = datamodel.ResourceTypeProperties{
Capabilities: capabilities,
DefaultAPIVersion: src.Properties.DefaultAPIVersion,
}

Expand All @@ -61,8 +74,21 @@ func (dst *ResourceTypeResource) ConvertFrom(src v1.DataModelInterface) error {

dst.Properties = &ResourceTypeProperties{
ProvisioningState: to.Ptr(ProvisioningState(dm.InternalMetadata.AsyncProvisioningState)),
Capabilities: to.SliceOfPtrs(dm.Properties.Capabilities...),
DefaultAPIVersion: dm.Properties.DefaultAPIVersion,
}

return nil
}

func validateCapability(input *string) error {
if input == nil {
return v1.NewClientErrInvalidRequest("capability cannot be null")
}

if *input == datamodel.CapabilitySupportsRecipes {
return nil
}

return v1.NewClientErrInvalidRequest(fmt.Sprintf("capability %q is not recognized. Supported capabilities: %s", *input, datamodel.CapabilitySupportsRecipes))
}
37 changes: 37 additions & 0 deletions pkg/ucp/api/v20231001preview/resourcetype_conversion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ func Test_ResourceType_VersionedToDataModel(t *testing.T) {
},
},
Properties: datamodel.ResourceTypeProperties{
Capabilities: []string{"SupportsRecipes"},
DefaultAPIVersion: to.Ptr("2025-01-01"),
},
},
Expand Down Expand Up @@ -87,6 +88,7 @@ func Test_ResourceType_DataModelToVersioned(t *testing.T) {
Name: to.Ptr("testResources"),
Properties: &ResourceTypeProperties{
ProvisioningState: to.Ptr(ProvisioningStateSucceeded),
Capabilities: []*string{to.Ptr("SupportsRecipes")},
DefaultAPIVersion: to.Ptr("2025-01-01"),
},
},
Expand All @@ -113,3 +115,38 @@ func Test_ResourceType_DataModelToVersioned(t *testing.T) {
})
}
}

func Test_validateCapability(t *testing.T) {
tests := []struct {
name string
input *string
expectedErr error
}{
{
name: "valid capability",
input: to.Ptr(datamodel.CapabilitySupportsRecipes),
},
{
name: "invalid capability",
input: to.Ptr("InvalidCapability"),
expectedErr: v1.NewClientErrInvalidRequest("capability \"InvalidCapability\" is not recognized. Supported capabilities: SupportsRecipes"),
},
{
name: "nil capability",
input: nil,
expectedErr: v1.NewClientErrInvalidRequest("capability cannot be null"),
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validateCapability(tt.input)
if tt.expectedErr != nil {
require.Error(t, err)
require.Equal(t, tt.expectedErr, err)
} else {
require.NoError(t, err)
}
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
},
"resourceTypes": {
"testResources": {
"capabilities": ["SupportsRecipes"],
"defaultApiVersion": "2025-01-01",
"apiVersions": {
"2025-01-01": {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"type": "System.Resources/resourceProviders/resourceTypes",
"provisioningState": "Succeeded",
"properties": {
"capabilities": ["SupportsRecipes"],
"defaultApiVersion": "2025-01-01"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"id": "/planes/radius/local/providers/System.Resources/resourceProviders/Applications.Test/resourceTypes/testResources",
"name": "testResources",
"properties": {
"capabilities": ["SupportsRecipes"],
"defaultApiVersion": "2025-01-01"
}
}
Loading

0 comments on commit 75d093a

Please sign in to comment.