Skip to content

Commit

Permalink
Adding commands for user-defined-types (radius-project#7699)
Browse files Browse the repository at this point in the history
This commit adds new commands and capabilities for working with
user-defined-types in the CLI.

Some new commands:

- `rad resourceprovider *`: CRUDL lifecycle management for a resource
provider.
- `rad resourceprovider new`: Scaffolding a template for a resource
provider.
- `rad resourcetype [show|list]`: RL lifecycle management for resource
types (read-only).
- `rad resource create`: CU lifecycle management for any resource type.

Also updated `rad resource delete` and similar commands to work with
user-defined-types. Many commands validate a fixed list of resource
types, this update allows commands to work with an arbitrary resource
type.

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

Part of: radius-project#6688

Signed-off-by: Ryan Nowak <[email protected]>
  • Loading branch information
rynowak committed Nov 5, 2024
1 parent 67be079 commit 5e488af
Show file tree
Hide file tree
Showing 40 changed files with 3,861 additions and 20 deletions.
34 changes: 34 additions & 0 deletions cmd/rad/cmd/resourceprovider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
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: "resourceprovider",
Short: "Manage resource providers",
Long: `Manage resource providers`,
}
}
34 changes: 34 additions & 0 deletions cmd/rad/cmd/resourcetype.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
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: "resourcetype",
Short: "Manage resource types",
Long: `Manage resource types`,
}
}
46 changes: 40 additions & 6 deletions cmd/rad/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,17 @@ 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_create "github.com/radius-project/radius/pkg/cli/cmd/resourceprovider/create"
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_new "github.com/radius-project/radius/pkg/cli/cmd/resourceprovider/new"
resourceprovider_show "github.com/radius-project/radius/pkg/cli/cmd/resourceprovider/show"
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 +108,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 +221,38 @@ 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)
resourceListCommand, _ := resource_list.NewCommand(framework)
resourceCmd.AddCommand(resourceListCommand)

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

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

resourceProviderNewCmd, _ := resourceprovider_new.NewCommand(framework)
resourceProviderCmd.AddCommand(resourceProviderNewCmd)

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

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

resourceProviderCreateCmd, _ := resourceprovider_create.NewCommand(framework)
resourceProviderCmd.AddCommand(resourceProviderCreateCmd)

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

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

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

listRecipeCmd, _ := recipe_list.NewCommand(framework)
recipeCmd.AddCommand(listRecipeCmd)
Expand Down
21 changes: 21 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,24 @@ 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 list 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)
}

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

Expand Down Expand Up @@ -196,6 +197,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 +682,125 @@ 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 _, resourceGroup := range page.Value {
results = append(results, *resourceGroup)
}
}

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, providerNamespace string) (ucpv20231001.ResourceProviderResource, error) {
client, err := amc.createResourceProviderClient()
if err != nil {
return ucpv20231001.ResourceProviderResource{}, err
}

response, err := client.Get(ctx, planeName, providerNamespace, &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, providerNamespace string, resource *ucpv20231001.ResourceProviderResource) (ucpv20231001.ResourceProviderResource, error) {
client, err := amc.createResourceProviderClient()
if err != nil {
return ucpv20231001.ResourceProviderResource{}, err
}

poller, err := client.BeginCreateOrUpdate(ctx, planeName, providerNamespace, *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, providerNamespace 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, providerNamespace, &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, providerNamespace string) (ucpv20231001.ResourceProviderSummary, error) {
client, err := amc.createResourceProviderClient()
if err != nil {
return ucpv20231001.ResourceProviderSummary{}, err
}

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

return response.ResourceProviderSummary, 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 +836,14 @@ 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) extractScopeAndName(nameOrID string) (string, string, error) {
if strings.HasPrefix(nameOrID, resources.SegmentSeparator) {
// Treat this as a resource id.
Expand Down
12 changes: 11 additions & 1 deletion pkg/cli/clients/management_mocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import (
// Because these interfaces are non-exported, they MUST be defined in their own file
// and we MUST use -source on mockgen to generate mocks for them.

//go:generate mockgen -typed -source=./management_mocks.go -destination=./mock_management_wrapped_clients.go -package=clients -self_package github.com/radius-project/radius/pkg/cli/clients github.com/radius-project/radius/pkg/cli/clients genericResourceClient,applicationResourceClient,environmentResourceClient,resourceGroupClient
//go:generate mockgen -typed -source=./management_mocks.go -destination=./mock_management_wrapped_clients.go -package=clients -self_package github.com/radius-project/radius/pkg/cli/clients github.com/radius-project/radius/pkg/cli/clients genericResourceClient,applicationResourceClient,environmentResourceClient,resourceGroupClient,resourceProviderClient

// genericResourceClient is an interface for mocking the generated SDK client for any resource.
type genericResourceClient interface {
Expand Down Expand Up @@ -71,3 +71,13 @@ type resourceGroupClient interface {
Get(ctx context.Context, planeName string, resourceGroupName string, options *ucpv20231001.ResourceGroupsClientGetOptions) (ucpv20231001.ResourceGroupsClientGetResponse, error)
NewListPager(planeName string, options *ucpv20231001.ResourceGroupsClientListOptions) *runtime.Pager[ucpv20231001.ResourceGroupsClientListResponse]
}

// resourceProviderClient is an interface for mocking the generated SDK client for resource providers.
type resourceProviderClient interface {
BeginCreateOrUpdate(ctx context.Context, planeName string, resourceProviderName string, resource ucpv20231001.ResourceProviderResource, options *ucpv20231001.ResourceProvidersClientBeginCreateOrUpdateOptions) (*runtime.Poller[ucpv20231001.ResourceProvidersClientCreateOrUpdateResponse], error)
BeginDelete(ctx context.Context, planeName string, resourceProviderName string, options *ucpv20231001.ResourceProvidersClientBeginDeleteOptions) (*runtime.Poller[ucpv20231001.ResourceProvidersClientDeleteResponse], error)
Get(ctx context.Context, planeName string, resourceProviderName string, options *ucpv20231001.ResourceProvidersClientGetOptions) (ucpv20231001.ResourceProvidersClientGetResponse, error)
NewListPager(planeName string, options *ucpv20231001.ResourceProvidersClientListOptions) *runtime.Pager[ucpv20231001.ResourceProvidersClientListResponse]
GetProviderSummary(ctx context.Context, planeName string, resourceProviderName string, options *ucpv20231001.ResourceProvidersClientGetProviderSummaryOptions) (ucpv20231001.ResourceProvidersClientGetProviderSummaryResponse, error)
NewListProviderSummariesPager(planeName string, options *ucpv20231001.ResourceProvidersClientListProviderSummariesOptions) *runtime.Pager[ucpv20231001.ResourceProvidersClientListProviderSummariesResponse]
}
Loading

0 comments on commit 5e488af

Please sign in to comment.