Skip to content

Commit

Permalink
[UDT PR 4 of N] Add basic commands for UDT support (#8035)
Browse files Browse the repository at this point in the history
# Description

This change adds the most basic commands for interacting with resource
providers and resource types. Also added some flexibility to existing
commands for resources so they can work with UDTs.

- `rad resourceprovider`
  - `list`
  - `show`
  - `delete`

- `rad resourcetype`
  - `list`
  - `show`

Also added `rad resource create`. We never implemented anything like
this, it was just missing.

Also enhanced the `rad resource` family of commands to support any
fully-qualified resource type. These commands now how to validate and
work with the set of built-in types in Radius today using short names
like `containers`.

This set of commands are the easy decisions, and already work well using
the existing functionality in the implementation. We'll add some of the
more complex commands after a design discussion.

## 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 Nov 12, 2024
1 parent 9e41b41 commit 7e4d565
Show file tree
Hide file tree
Showing 30 changed files with 3,517 additions and 16 deletions.
35 changes: 35 additions & 0 deletions cmd/rad/cmd/resourceprovider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
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 cmd

import (
"github.com/spf13/cobra"
)

func init() {
RootCmd.AddCommand(resourceProviderCmd)
resourceProviderCmd.PersistentFlags().StringP("workspace", "w", "", "The workspace name")
}

func NewResourceProviderCommand() *cobra.Command {
return &cobra.Command{
Use: "resource-provider",
Aliases: []string{"rp"},
Short: "Manage resource providers",
Long: `Manage resource providers`,
}
}
35 changes: 35 additions & 0 deletions cmd/rad/cmd/resourcetype.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
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 cmd

import (
"github.com/spf13/cobra"
)

func init() {
RootCmd.AddCommand(resourceTypeCmd)
resourceTypeCmd.PersistentFlags().StringP("workspace", "w", "", "The workspace name")
}

func NewResourceTypeCommand() *cobra.Command {
return &cobra.Command{
Use: "resource-type",
Aliases: []string{"rt"},
Short: "Manage resource types",
Long: `Manage resource types`,
}
}
42 changes: 36 additions & 6 deletions cmd/rad/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,16 @@ import (
recipe_register "github.com/radius-project/radius/pkg/cli/cmd/recipe/register"
recipe_show "github.com/radius-project/radius/pkg/cli/cmd/recipe/show"
recipe_unregister "github.com/radius-project/radius/pkg/cli/cmd/recipe/unregister"
resource_create "github.com/radius-project/radius/pkg/cli/cmd/resource/create"
resource_delete "github.com/radius-project/radius/pkg/cli/cmd/resource/delete"
resource_list "github.com/radius-project/radius/pkg/cli/cmd/resource/list"
resource_show "github.com/radius-project/radius/pkg/cli/cmd/resource/show"
resourceprovider_delete "github.com/radius-project/radius/pkg/cli/cmd/resourceprovider/delete"
resourceprovider_list "github.com/radius-project/radius/pkg/cli/cmd/resourceprovider/list"
resourceprovider_show "github.com/radius-project/radius/pkg/cli/cmd/resourceprovider/show"
resourcetype_delete "github.com/radius-project/radius/pkg/cli/cmd/resourcetype/delete"
resourcetype_list "github.com/radius-project/radius/pkg/cli/cmd/resourcetype/list"
resourcetype_show "github.com/radius-project/radius/pkg/cli/cmd/resourcetype/show"
"github.com/radius-project/radius/pkg/cli/cmd/run"
"github.com/radius-project/radius/pkg/cli/cmd/uninstall"
uninstall_kubernetes "github.com/radius-project/radius/pkg/cli/cmd/uninstall/kubernetes"
Expand Down Expand Up @@ -100,6 +107,8 @@ const (

var applicationCmd = NewAppCommand()
var resourceCmd = NewResourceCommand()
var resourceProviderCmd = NewResourceProviderCommand()
var resourceTypeCmd = NewResourceTypeCommand()
var recipeCmd = NewRecipeCommand()
var envCmd = NewEnvironmentCommand()
var workspaceCmd = NewWorkspaceCommand()
Expand Down Expand Up @@ -211,14 +220,35 @@ func initSubCommands() {
runCmd, _ := run.NewCommand(framework)
RootCmd.AddCommand(runCmd)

showCmd, _ := resource_show.NewCommand(framework)
resourceCmd.AddCommand(showCmd)
resourceShowCmd, _ := resource_show.NewCommand(framework)
resourceCmd.AddCommand(resourceShowCmd)

listCmd, _ := resource_list.NewCommand(framework)
resourceCmd.AddCommand(listCmd)
resourceListCmd, _ := resource_list.NewCommand(framework)
resourceCmd.AddCommand(resourceListCmd)

deleteCmd, _ := resource_delete.NewCommand(framework)
resourceCmd.AddCommand(deleteCmd)
resourceCreateCmd, _ := resource_create.NewCommand(framework)
resourceCmd.AddCommand(resourceCreateCmd)

resourceDeleteCmd, _ := resource_delete.NewCommand(framework)
resourceCmd.AddCommand(resourceDeleteCmd)

resourceProviderShowCmd, _ := resourceprovider_show.NewCommand(framework)
resourceProviderCmd.AddCommand(resourceProviderShowCmd)

resourceProviderListCmd, _ := resourceprovider_list.NewCommand(framework)
resourceProviderCmd.AddCommand(resourceProviderListCmd)

resourceProviderDeleteCmd, _ := resourceprovider_delete.NewCommand(framework)
resourceProviderCmd.AddCommand(resourceProviderDeleteCmd)

resourceTypeShowCmd, _ := resourcetype_show.NewCommand(framework)
resourceTypeCmd.AddCommand(resourceTypeShowCmd)

resourceTypeListCmd, _ := resourcetype_list.NewCommand(framework)
resourceTypeCmd.AddCommand(resourceTypeListCmd)

resourceTypeDeleteCmd, _ := resourcetype_delete.NewCommand(framework)
resourceTypeCmd.AddCommand(resourceTypeDeleteCmd)

listRecipeCmd, _ := recipe_list.NewCommand(framework)
recipeCmd.AddCommand(listRecipeCmd)
Expand Down
24 changes: 24 additions & 0 deletions pkg/cli/clients/clients.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,9 @@ type ApplicationsManagementClient interface {
// GetResource retrieves a resource by its type and name (or id).
GetResource(ctx context.Context, resourceType string, resourceNameOrID string) (generated.GenericResource, error)

// CreateOrUpdateResource creates or updates a resource using its type name (or id).
CreateOrUpdateResource(ctx context.Context, resourceType string, resourceNameOrID string, resource *generated.GenericResource) (generated.GenericResource, error)

// DeleteResource deletes a resource by its type and name (or id).
DeleteResource(ctx context.Context, resourceType string, resourceNameOrID string) (bool, error)

Expand Down Expand Up @@ -205,6 +208,27 @@ type ApplicationsManagementClient interface {

// DeleteResourceGroup deletes a resource group by its name.
DeleteResourceGroup(ctx context.Context, planeName string, resourceGroupName string) (bool, error)

// ListResourceProviders lists all resource providers in the configured scope.
ListResourceProviders(ctx context.Context, planeName string) ([]ucp_v20231001preview.ResourceProviderResource, error)

// GetResourceProvider gets the resource provider with the specified name in the configured scope.
GetResourceProvider(ctx context.Context, planeName string, providerNamespace string) (ucp_v20231001preview.ResourceProviderResource, error)

// CreateOrUpdateResourceProvider creates or updates a resource provider in the configured scope.
CreateOrUpdateResourceProvider(ctx context.Context, planeName string, providerNamespace string, resource *ucp_v20231001preview.ResourceProviderResource) (ucp_v20231001preview.ResourceProviderResource, error)

// DeleteResourceProvider deletes a resource provider in the configured scope.
DeleteResourceProvider(ctx context.Context, planeName string, providerNamespace string) (bool, error)

// ListResourceProviderSummaries lists the summary data of all resource providers in the configured scope.
ListResourceProviderSummaries(ctx context.Context, planeName string) ([]ucp_v20231001preview.ResourceProviderSummary, error)

// GetResourceProviderSummary gets the resource provider summary with the specified name in the configured scope.
GetResourceProviderSummary(ctx context.Context, planeName string, providerNamespace string) (ucp_v20231001preview.ResourceProviderSummary, error)

// DeleteResourceType deletes a resource type in the configured scope.
DeleteResourceType(ctx context.Context, planeName string, providerNamespace string, resourceTypeName string) (bool, error)
}

// ShallowCopy creates a shallow copy of the DeploymentParameters object by iterating through the original object and
Expand Down
185 changes: 185 additions & 0 deletions pkg/cli/clients/management.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ type UCPApplicationsManagementClient struct {
applicationResourceClientFactory func(scope string) (applicationResourceClient, error)
environmentResourceClientFactory func(scope string) (environmentResourceClient, error)
resourceGroupClientFactory func() (resourceGroupClient, error)
resourceProviderClientFactory func() (resourceProviderClient, error)
resourceTypeClientFactory func() (resourceTypeClient, error)
capture func(ctx context.Context, capture **http.Response) context.Context
}

Expand Down Expand Up @@ -196,6 +198,31 @@ func (amc *UCPApplicationsManagementClient) GetResource(ctx context.Context, res
return getResponse.GenericResource, nil
}

// CreateOrUpdateResource creates or updates a resource using its type name (or id).
func (amc *UCPApplicationsManagementClient) CreateOrUpdateResource(ctx context.Context, resourceType string, resourceNameOrID string, resource *generated.GenericResource) (generated.GenericResource, error) {
scope, name, err := amc.extractScopeAndName(resourceNameOrID)
if err != nil {
return generated.GenericResource{}, err
}

client, err := amc.createGenericClient(scope, resourceType)
if err != nil {
return generated.GenericResource{}, err
}

poller, err := client.BeginCreateOrUpdate(ctx, name, *resource, &generated.GenericResourcesClientBeginCreateOrUpdateOptions{})
if err != nil {
return generated.GenericResource{}, err
}

response, err := poller.PollUntilDone(ctx, nil)
if err != nil {
return generated.GenericResource{}, err
}

return response.GenericResource, nil
}

// DeleteResource deletes a resource by its type and name (or id).
func (amc *UCPApplicationsManagementClient) DeleteResource(ctx context.Context, resourceType string, resourceNameOrID string) (bool, error) {
scope, name, err := amc.extractScopeAndName(resourceNameOrID)
Expand Down Expand Up @@ -656,6 +683,148 @@ func (amc *UCPApplicationsManagementClient) DeleteResourceGroup(ctx context.Cont
return response.StatusCode != 204, nil
}

// ListResourceProviders lists all resource providers in the configured scope.
func (amc *UCPApplicationsManagementClient) ListResourceProviders(ctx context.Context, planeName string) ([]ucpv20231001.ResourceProviderResource, error) {
client, err := amc.createResourceProviderClient()
if err != nil {
return nil, err
}

results := []ucpv20231001.ResourceProviderResource{}
pager := client.NewListPager(planeName, &ucpv20231001.ResourceProvidersClientListOptions{})
for pager.More() {
page, err := pager.NextPage(ctx)
if err != nil {
return nil, err
}

for _, resourceProvider := range page.Value {
results = append(results, *resourceProvider)
}
}

return results, nil
}

// GetResourceProvider gets the resource provider with the specified name in the configured scope.
func (amc *UCPApplicationsManagementClient) GetResourceProvider(ctx context.Context, planeName string, resourceProviderName string) (ucpv20231001.ResourceProviderResource, error) {
client, err := amc.createResourceProviderClient()
if err != nil {
return ucpv20231001.ResourceProviderResource{}, err
}

response, err := client.Get(ctx, planeName, resourceProviderName, &ucpv20231001.ResourceProvidersClientGetOptions{})
if err != nil {
return ucpv20231001.ResourceProviderResource{}, err
}

return response.ResourceProviderResource, nil
}

// CreateOrUpdateResourceProvider creates or updates a resource provider in the configured scope.
func (amc *UCPApplicationsManagementClient) CreateOrUpdateResourceProvider(ctx context.Context, planeName string, resourceProviderName string, resource *ucpv20231001.ResourceProviderResource) (ucpv20231001.ResourceProviderResource, error) {
client, err := amc.createResourceProviderClient()
if err != nil {
return ucpv20231001.ResourceProviderResource{}, err
}

poller, err := client.BeginCreateOrUpdate(ctx, planeName, resourceProviderName, *resource, &ucpv20231001.ResourceProvidersClientBeginCreateOrUpdateOptions{})
if err != nil {
return ucpv20231001.ResourceProviderResource{}, err
}

response, err := poller.PollUntilDone(ctx, nil)
if err != nil {
return ucpv20231001.ResourceProviderResource{}, err
}

return response.ResourceProviderResource, nil
}

// DeleteResourceProvider deletes a resource provider in the configured scope.
func (amc *UCPApplicationsManagementClient) DeleteResourceProvider(ctx context.Context, planeName string, resourceProviderName string) (bool, error) {
client, err := amc.createResourceProviderClient()
if err != nil {
return false, err
}

var response *http.Response
ctx = amc.captureResponse(ctx, &response)

poller, err := client.BeginDelete(ctx, planeName, resourceProviderName, &ucpv20231001.ResourceProvidersClientBeginDeleteOptions{})
if err != nil {
return false, err
}

_, err = poller.PollUntilDone(ctx, nil)
if err != nil {
return false, err
}

return response.StatusCode != 204, nil
}

// ListResourceProviderSummaries lists all resource provider summaries in the configured scope.
func (amc *UCPApplicationsManagementClient) ListResourceProviderSummaries(ctx context.Context, planeName string) ([]ucpv20231001.ResourceProviderSummary, error) {
client, err := amc.createResourceProviderClient()
if err != nil {
return nil, err
}

results := []ucpv20231001.ResourceProviderSummary{}
pager := client.NewListProviderSummariesPager(planeName, &ucpv20231001.ResourceProvidersClientListProviderSummariesOptions{})
for pager.More() {
page, err := pager.NextPage(ctx)
if err != nil {
return nil, err
}

for _, summary := range page.Value {
results = append(results, *summary)
}
}

return results, nil
}

// GetResourceProvider gets the resource provider summary with the specified name in the configured scope.
func (amc *UCPApplicationsManagementClient) GetResourceProviderSummary(ctx context.Context, planeName string, resourceProviderName string) (ucpv20231001.ResourceProviderSummary, error) {
client, err := amc.createResourceProviderClient()
if err != nil {
return ucpv20231001.ResourceProviderSummary{}, err
}

response, err := client.GetProviderSummary(ctx, planeName, resourceProviderName, &ucpv20231001.ResourceProvidersClientGetProviderSummaryOptions{})
if err != nil {
return ucpv20231001.ResourceProviderSummary{}, err
}

return response.ResourceProviderSummary, nil
}

// DeleteResourceType deletes a resource type in the configured scope.
func (amc *UCPApplicationsManagementClient) DeleteResourceType(ctx context.Context, planeName string, resourceProviderName string, resourceTypeName string) (bool, error) {
client, err := amc.createResourceTypeClient()
if err != nil {
return false, err
}

var response *http.Response
ctx = amc.captureResponse(ctx, &response)

poller, err := client.BeginDelete(ctx, planeName, resourceProviderName, resourceTypeName, &ucpv20231001.ResourceTypesClientBeginDeleteOptions{})
if err != nil {
return false, err
}

_, err = poller.PollUntilDone(ctx, nil)
if err != nil {
return false, err
}

return response.StatusCode != 204, nil
}

func (amc *UCPApplicationsManagementClient) createApplicationClient(scope string) (applicationResourceClient, error) {
if amc.applicationResourceClientFactory == nil {
// Generated client doesn't like the leading '/' in the scope.
Expand Down Expand Up @@ -691,6 +860,22 @@ func (amc *UCPApplicationsManagementClient) createResourceGroupClient() (resourc
return amc.resourceGroupClientFactory()
}

func (amc *UCPApplicationsManagementClient) createResourceProviderClient() (resourceProviderClient, error) {
if amc.resourceProviderClientFactory == nil {
return ucpv20231001.NewResourceProvidersClient(&aztoken.AnonymousCredential{}, amc.ClientOptions)
}

return amc.resourceProviderClientFactory()
}

func (amc *UCPApplicationsManagementClient) createResourceTypeClient() (resourceTypeClient, error) {
if amc.resourceTypeClientFactory == nil {
return ucpv20231001.NewResourceTypesClient(&aztoken.AnonymousCredential{}, amc.ClientOptions)
}

return amc.resourceTypeClientFactory()
}

func (amc *UCPApplicationsManagementClient) extractScopeAndName(nameOrID string) (string, string, error) {
if strings.HasPrefix(nameOrID, resources.SegmentSeparator) {
// Treat this as a resource id.
Expand Down
Loading

0 comments on commit 7e4d565

Please sign in to comment.