From 73ed52d270866587e31d23a7b1910a7176b1527a Mon Sep 17 00:00:00 2001 From: Nithya Subramanian <98416062+nithyatsu@users.noreply.github.com> Date: Wed, 8 Jan 2025 15:27:03 -0800 Subject: [PATCH] [UDT] add create resource type command (#8104) # Description add rad resource-type create ## Type of change - This pull request adds or changes features of Radius and has an approved issue (issue link required). Partially Fixes: #issue_number ## Contributor checklist Please verify that the PR meets the following requirements, where applicable: - [ NA] An overview of proposed schema changes is included in a linked GitHub issue. - [ NA] A design document PR is created in the [design-notes repository](https://github.com/radius-project/design-notes/), if new APIs are being introduced. - [NA ] If applicable, design document has been reviewed and approved by Radius maintainers/approvers. - [NA ] 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. - [ NA] 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. - [ NA] 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. --- cmd/rad/cmd/root.go | 4 + pkg/cli/clivalidation.go | 25 +++ pkg/cli/cmd/resourceprovider/create/create.go | 1 - .../resourceprovider/create/create_test.go | 2 +- pkg/cli/cmd/resourcetype/create/create.go | 188 ++++++++++++++++++ .../cmd/resourcetype/create/create_test.go | 156 +++++++++++++++ .../resourcetype/create/testdata/valid.yaml | 12 ++ pkg/cli/manifest/registermanifest.go | 72 ++++++- pkg/cli/manifest/registermanifest_test.go | 4 +- pkg/cli/manifest/testclientfactory.go | 138 +++++++++++-- 10 files changed, 574 insertions(+), 28 deletions(-) create mode 100644 pkg/cli/cmd/resourcetype/create/create.go create mode 100644 pkg/cli/cmd/resourcetype/create/create_test.go create mode 100644 pkg/cli/cmd/resourcetype/create/testdata/valid.yaml diff --git a/cmd/rad/cmd/root.go b/cmd/rad/cmd/root.go index 87c9f9b563..e360d5ba00 100644 --- a/cmd/rad/cmd/root.go +++ b/cmd/rad/cmd/root.go @@ -62,6 +62,7 @@ import ( 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_create "github.com/radius-project/radius/pkg/cli/cmd/resourcetype/create" 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" @@ -273,6 +274,9 @@ func initSubCommands() { resourceTypeDeleteCmd, _ := resourcetype_delete.NewCommand(framework) resourceTypeCmd.AddCommand(resourceTypeDeleteCmd) + resourceTypeCreateCmd, _ := resourcetype_create.NewCommand(framework) + resourceTypeCmd.AddCommand(resourceTypeCreateCmd) + listRecipeCmd, _ := recipe_list.NewCommand(framework) recipeCmd.AddCommand(listRecipeCmd) diff --git a/pkg/cli/clivalidation.go b/pkg/cli/clivalidation.go index 76addbe5ec..da8dcc03a3 100644 --- a/pkg/cli/clivalidation.go +++ b/pkg/cli/clivalidation.go @@ -454,6 +454,31 @@ func ReadResourceGroupNameArgs(cmd *cobra.Command, args []string) (string, error return name, err } +// RequireResourceTypeNameArgs is used by commands that require specifying a type name using positional args. +// + +// RequireResourceTypeNameArgs reads the resource type name from the command line arguments and returns an error if the name +// is not provided or is empty. It also handles any errors that may occur while reading the resource type name. +func RequireResourceTypeNameArgs(cmd *cobra.Command, args []string) (string, error) { + resourceType, err := ReadResourceTypeNameArgs(cmd, args) + if err != nil { + return "", err + } + if resourceType == "" { + return "", fmt.Errorf("resource type name is not provided or is empty") + } + + return resourceType, nil +} + +// ReadResourceTypeNameArgs is used to get the resource type name that is supplied as the first argument +func ReadResourceTypeNameArgs(cmd *cobra.Command, args []string) (string, error) { + if len(args) < 1 { + return "", errors.New("resource type name is not provided") + } + return args[0], nil +} + // RequireWorkspaceArgs is used by commands that require an existing workspace either set as the default, // or specified as a positional arg, or specified using the 'workspace' flag. // diff --git a/pkg/cli/cmd/resourceprovider/create/create.go b/pkg/cli/cmd/resourceprovider/create/create.go index 162bef3c76..97333b0c08 100644 --- a/pkg/cli/cmd/resourceprovider/create/create.go +++ b/pkg/cli/cmd/resourceprovider/create/create.go @@ -136,7 +136,6 @@ func (r *Runner) Run(ctx context.Context) error { return err } - // Add a blank line before printing the result. r.Output.LogInfo("") err = r.Output.WriteFormatted(r.Format, response, common.GetResourceProviderTableFormat()) diff --git a/pkg/cli/cmd/resourceprovider/create/create_test.go b/pkg/cli/cmd/resourceprovider/create/create_test.go index d74123e709..4f4f6015de 100644 --- a/pkg/cli/cmd/resourceprovider/create/create_test.go +++ b/pkg/cli/cmd/resourceprovider/create/create_test.go @@ -76,7 +76,7 @@ func Test_Run(t *testing.T) { expectedResourceType := "testResources" expectedAPIVersion := "2025-01-01-preview" - clientFactory, err := manifest.NewTestClientFactory() + clientFactory, err := manifest.NewTestClientFactory(manifest.WithResourceProviderServerNoError) require.NoError(t, err) var logBuffer bytes.Buffer diff --git a/pkg/cli/cmd/resourcetype/create/create.go b/pkg/cli/cmd/resourcetype/create/create.go new file mode 100644 index 0000000000..d8a24c3ac0 --- /dev/null +++ b/pkg/cli/cmd/resourcetype/create/create.go @@ -0,0 +1,188 @@ +/* +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 create + +import ( + "context" + "strings" + + "github.com/radius-project/radius/pkg/cli" + "github.com/radius-project/radius/pkg/cli/clients" + "github.com/radius-project/radius/pkg/cli/clierrors" + "github.com/radius-project/radius/pkg/cli/cmd/commonflags" + "github.com/radius-project/radius/pkg/cli/cmd/resourcetype/common" + "github.com/radius-project/radius/pkg/cli/framework" + "github.com/radius-project/radius/pkg/cli/manifest" + "github.com/radius-project/radius/pkg/cli/output" + "github.com/radius-project/radius/pkg/cli/workspaces" + "github.com/radius-project/radius/pkg/sdk" + "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" + "github.com/spf13/cobra" + + aztoken "github.com/radius-project/radius/pkg/azure/tokencredentials" +) + +const ( + fakeServerResourceProviderNotFoundResponse = "unexpected status code 404. acceptable values are http.StatusOK" +) + +// NewCommand creates an instance of the `rad resource-type create` command and runner. +func NewCommand(factory framework.Factory) (*cobra.Command, framework.Runner) { + runner := NewRunner(factory) + + cmd := &cobra.Command{ + Use: "create [input]", + Short: "Create or update a resource type", + Long: `Create or update a resource type from a resource provider manifest. + + Resource types are user defined types such as 'Mycompany.Messaging/plaid'. + + Creating a resource type defines a new type that can be used in applications. + + Input can be passed in using a JSON or YAML file using the --from-file option. + `, + Example: ` +# Create a resource type from YAML file +rad resource-type create myType --from-file /path/to/input.yaml + +# Create a resource type from JSON file +rad resource-type create myType --from-file /path/to/input.json +`, + Args: cobra.ExactArgs(1), + RunE: framework.RunCommand(runner), + } + + commonflags.AddOutputFlag(cmd) + commonflags.AddWorkspaceFlag(cmd) + commonflags.AddFromFileFlagVar(cmd, &runner.ResourceProviderManifestFilePath) + + return cmd, runner +} + +// Runner is the Runner implementation for the `rad resource-type create` command. +type Runner struct { + UCPClientFactory *v20231001preview.ClientFactory + ConfigHolder *framework.ConfigHolder + Output output.Interface + Format string + Workspace *workspaces.Workspace + + ResourceProviderManifestFilePath string + ResourceProvider *manifest.ResourceProvider + ResourceTypeName string + Logger func(format string, args ...any) +} + +// NewRunner creates an instance of the runner for the `rad resource-type create` command. +func NewRunner(factory framework.Factory) *Runner { + return &Runner{ + ConfigHolder: factory.GetConfigHolder(), + Output: factory.GetOutput(), + Logger: func(format string, args ...any) { + output.LogInfo(format, args...) + }, + } +} + +// Validate runs validation for the `rad resource-type create` command. +func (r *Runner) Validate(cmd *cobra.Command, args []string) error { + resourceTypeName, err := cli.RequireResourceTypeNameArgs(cmd, args) + if err != nil { + return err + } + r.ResourceTypeName = resourceTypeName + + workspace, err := cli.RequireWorkspace(cmd, r.ConfigHolder.Config, r.ConfigHolder.DirectoryConfig) + if err != nil { + return err + } + r.Workspace = workspace + + format, err := cli.RequireOutput(cmd) + if err != nil { + return err + } + r.Format = format + + r.ResourceProvider, err = manifest.ReadFile(r.ResourceProviderManifestFilePath) + if err != nil { + return err + } + + resourcesTypes := r.ResourceProvider.Types + if _, ok := resourcesTypes[r.ResourceTypeName]; !ok { + return clierrors.Message("Resource type %q not found in the manifest", r.ResourceTypeName) + } + + return nil +} + +// Run runs the `rad resource-type create` command. +func (r *Runner) Run(ctx context.Context) error { + // Initialize the client factory if it hasn't been set externally. + // This allows for flexibility where a test UCPClientFactory can be set externally during testing. + if r.UCPClientFactory == nil { + err := r.initializeClientFactory(ctx, r.Workspace) + if err != nil { + return err + } + } + + response, err := r.UCPClientFactory.NewResourceProvidersClient().Get(ctx, "local", r.ResourceProvider.Name, nil) + if err != nil { + // The second clause is required for testing purpose since fake server returns a different type of error. + if clients.Is404Error(err) || strings.Contains(err.Error(), fakeServerResourceProviderNotFoundResponse) { + r.Output.LogInfo("Resource provider %q not found.", r.ResourceProvider.Name) + if registerErr := manifest.RegisterFile(ctx, r.UCPClientFactory, "local", r.ResourceProviderManifestFilePath, r.Logger); err != nil { + return registerErr + } + } else { + return err + } + } else { + r.Output.LogInfo("Resource provider %q found. Registering resource type %q.", r.ResourceProvider.Name, r.ResourceTypeName) + if registerErr := manifest.RegisterType(ctx, r.UCPClientFactory, "local", r.ResourceProviderManifestFilePath, r.ResourceTypeName, r.Logger); err != nil { + return registerErr + } + } + + r.Output.LogInfo("") + + err = r.Output.WriteFormatted(r.Format, response, common.GetResourceTypeTableFormat()) + if err != nil { + return err + } + + return nil +} + +func (r *Runner) initializeClientFactory(ctx context.Context, workspace *workspaces.Workspace) error { + connection, err := workspace.Connect(ctx) + if err != nil { + return err + } + + clientOptions := sdk.NewClientOptions(connection) + + clientFactory, err := v20231001preview.NewClientFactory(&aztoken.AnonymousCredential{}, clientOptions) + if err != nil { + return err + } + + r.UCPClientFactory = clientFactory + return nil +} diff --git a/pkg/cli/cmd/resourcetype/create/create_test.go b/pkg/cli/cmd/resourcetype/create/create_test.go new file mode 100644 index 0000000000..8d22ada7d8 --- /dev/null +++ b/pkg/cli/cmd/resourcetype/create/create_test.go @@ -0,0 +1,156 @@ +/* +Copyright 2023 The Radius Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package create + +import ( + "bytes" + "context" + "fmt" + "testing" + + "github.com/radius-project/radius/pkg/cli/framework" + "github.com/radius-project/radius/pkg/cli/manifest" + "github.com/radius-project/radius/pkg/cli/output" + "github.com/radius-project/radius/pkg/cli/workspaces" + "github.com/radius-project/radius/test/radcli" + "github.com/stretchr/testify/require" +) + +func Test_CommandValidation(t *testing.T) { + radcli.SharedCommandValidation(t, NewCommand) +} + +func Test_Validate(t *testing.T) { + config := radcli.LoadConfigWithWorkspace(t) + + testcases := []radcli.ValidateInput{ + { + Name: "Valid", + Input: []string{"coolResources", "--from-file", "testdata/valid.yaml"}, + ExpectedValid: true, + ConfigHolder: framework.ConfigHolder{Config: config}, + }, + { + Name: "Invalid: resource type not present in manifest", + Input: []string{"myResources", "--from-file", "testdata/valid.yaml"}, + ExpectedValid: false, + ConfigHolder: framework.ConfigHolder{Config: config}, + }, + { + Name: "Invalid: missing resource type as argument", + Input: []string{"--from-file", "testdata/valid.yaml"}, + ExpectedValid: false, + ConfigHolder: framework.ConfigHolder{Config: config}, + }, + } + + radcli.SharedValidateValidation(t, NewCommand, testcases) +} + +func Test_Run(t *testing.T) { + t.Run("Success: resource type created", func(t *testing.T) { + resourceProviderData, err := manifest.ReadFile("testdata/valid.yaml") + require.NoError(t, err) + + expectedResourceType := "testResources" + + clientFactory, err := manifest.NewTestClientFactory(manifest.WithResourceProviderServerNoError) + require.NoError(t, err) + + var logBuffer bytes.Buffer + logger := func(format string, args ...any) { + fmt.Fprintf(&logBuffer, format+"\n", args...) + } + + runner := &Runner{ + UCPClientFactory: clientFactory, + Output: &output.MockOutput{}, + Workspace: &workspaces.Workspace{}, + ResourceProvider: resourceProviderData, + Format: "table", + Logger: logger, + ResourceProviderManifestFilePath: "testdata/valid.yaml", + ResourceTypeName: expectedResourceType, + } + + err = runner.Run(context.Background()) + require.NoError(t, err) + + logOutput := logBuffer.String() + require.NotContains(t, logOutput, fmt.Sprintf("Creating resource provider %s", runner.ResourceProvider.Name)) + require.Contains(t, logOutput, fmt.Sprintf("Resource type %s/%s created successfully", resourceProviderData.Name, expectedResourceType)) + }) + t.Run("Resource provider does not exist", func(t *testing.T) { + resourceProviderData, err := manifest.ReadFile("testdata/valid.yaml") + require.NoError(t, err) + + expectedResourceType := "testResources" + + clientFactory, err := manifest.NewTestClientFactory(manifest.WithResourceProviderServerNotFoundError) + require.NoError(t, err) + + var logBuffer bytes.Buffer + logger := func(format string, args ...any) { + fmt.Fprintf(&logBuffer, format+"\n", args...) + } + + runner := &Runner{ + UCPClientFactory: clientFactory, + Output: &output.MockOutput{}, + Workspace: &workspaces.Workspace{}, + ResourceProvider: resourceProviderData, + Format: "table", + Logger: logger, + ResourceProviderManifestFilePath: "testdata/valid.yaml", + ResourceTypeName: expectedResourceType, + } + + _ = runner.Run(context.Background()) + logOutput := logBuffer.String() + require.Contains(t, logOutput, fmt.Sprintf("Creating resource provider %s", runner.ResourceProvider.Name)) + + }) + t.Run("Get Resource provider Internal Error", func(t *testing.T) { + resourceProviderData, err := manifest.ReadFile("testdata/valid.yaml") + require.NoError(t, err) + + expectedResourceType := "testResources" + + clientFactory, err := manifest.NewTestClientFactory(manifest.WithResourceProviderServerInternalError) + require.NoError(t, err) + + var logBuffer bytes.Buffer + logger := func(format string, args ...any) { + fmt.Fprintf(&logBuffer, format+"\n", args...) + } + + runner := &Runner{ + UCPClientFactory: clientFactory, + Output: &output.MockOutput{}, + Workspace: &workspaces.Workspace{}, + ResourceProvider: resourceProviderData, + Format: "table", + Logger: logger, + ResourceProviderManifestFilePath: "testdata/valid.yaml", + ResourceTypeName: expectedResourceType, + } + + err = runner.Run(context.Background()) + require.Error(t, err) + require.Contains(t, err.Error(), "unexpected status code 500.") + }) +} diff --git a/pkg/cli/cmd/resourcetype/create/testdata/valid.yaml b/pkg/cli/cmd/resourcetype/create/testdata/valid.yaml new file mode 100644 index 0000000000..220799cc23 --- /dev/null +++ b/pkg/cli/cmd/resourcetype/create/testdata/valid.yaml @@ -0,0 +1,12 @@ +name: CoolCompany.Resources +types: + testResources: + apiVersions: + '2023-10-01-preview': + schema: {} + capabilities: ["Recipes"] + coolResources: + apiVersions: + '2023-10-01-preview': + schema: {} + capabilities: ["Recipes"] \ No newline at end of file diff --git a/pkg/cli/manifest/registermanifest.go b/pkg/cli/manifest/registermanifest.go index 9ac98c6d7f..2c2a54fb72 100644 --- a/pkg/cli/manifest/registermanifest.go +++ b/pkg/cli/manifest/registermanifest.go @@ -29,12 +29,10 @@ import ( // RegisterFile registers a manifest file func RegisterFile(ctx context.Context, clientFactory *v20231001preview.ClientFactory, planeName string, filePath string, logger func(format string, args ...any)) error { - // Check for valid file path if filePath == "" { return fmt.Errorf("invalid manifest file path") } - // Read the manifest file resourceProvider, err := ReadFile(filePath) if err != nil { return err @@ -124,7 +122,6 @@ func RegisterFile(ctx context.Context, clientFactory *v20231001preview.ClientFac // RegisterDirectory registers all manifest files in a directory func RegisterDirectory(ctx context.Context, clientFactory *v20231001preview.ClientFactory, planeName string, directoryPath string, logger func(format string, args ...any)) error { - // Check for valid directory path if directoryPath == "" { return fmt.Errorf("invalid manifest directory") } @@ -138,16 +135,14 @@ func RegisterDirectory(ctx context.Context, clientFactory *v20231001preview.Clie return fmt.Errorf("manifest path %s is not a directory", directoryPath) } - // List all files in the manifestDirectory files, err := os.ReadDir(directoryPath) if err != nil { return err } - // Iterate over each file in the directory for _, fileInfo := range files { if fileInfo.IsDir() { - continue // Skip directories + continue } filePath := filepath.Join(directoryPath, fileInfo.Name()) @@ -161,6 +156,71 @@ func RegisterDirectory(ctx context.Context, clientFactory *v20231001preview.Clie return nil } +// RegisterType registers a type specified in a manifest file +func RegisterType(ctx context.Context, clientFactory *v20231001preview.ClientFactory, planeName string, filePath string, typeName string, logger func(format string, args ...any)) error { + if filePath == "" { + return fmt.Errorf("invalid manifest file path") + } + + resourceProvider, err := ReadFile(filePath) + if err != nil { + return err + } + + resourceType, ok := resourceProvider.Types[typeName] + if !ok { + return fmt.Errorf("Type %s not found in manifest file %s", typeName, filePath) + } + + logIfEnabled(logger, "Creating resource type %s/%s", resourceProvider.Name, typeName) + resourceTypePoller, err := clientFactory.NewResourceTypesClient().BeginCreateOrUpdate(ctx, planeName, resourceProvider.Name, typeName, v20231001preview.ResourceTypeResource{ + Properties: &v20231001preview.ResourceTypeProperties{ + DefaultAPIVersion: resourceProvider.Types[typeName].DefaultAPIVersion, + }, + }, nil) + if err != nil { + return err + } + + _, err = resourceTypePoller.PollUntilDone(ctx, nil) + if err != nil { + return err + } + + // get the existing location resource and update it with new resource type. We have to revisit this code once schema is finalized and validated. + locationResourceGetResponse, err := clientFactory.NewLocationsClient().Get(ctx, planeName, resourceProvider.Name, v1.LocationGlobal, nil) + if err != nil { + return err + } + + var defaultAPIVersion string + if resourceType.DefaultAPIVersion == nil { + defaultAPIVersion = v20231001preview.Version //hardcoded for now, since we don't have a default API version in the manifest but it should be made mandatory as part of schema validation + } else { + defaultAPIVersion = *resourceType.DefaultAPIVersion + } + locationResource := locationResourceGetResponse.LocationResource + locationResource.Properties.ResourceTypes[typeName] = &v20231001preview.LocationResourceType{ + APIVersions: map[string]map[string]any{ + defaultAPIVersion: {}, + }, + } + + logIfEnabled(logger, "Updating location %s/%s with new resource type", resourceProvider.Name, v1.LocationGlobal) + locationPoller, err := clientFactory.NewLocationsClient().BeginCreateOrUpdate(ctx, planeName, resourceProvider.Name, v1.LocationGlobal, locationResource, nil) + if err != nil { + return err + } + + _, err = locationPoller.PollUntilDone(ctx, nil) + if err != nil { + return err + } + + logIfEnabled(logger, "Resource type %s/%s created successfully", resourceProvider.Name, typeName) + return nil +} + // Define an optional logger to prevent nil pointer dereference func logIfEnabled(logger func(format string, args ...any), format string, args ...any) { if logger != nil { diff --git a/pkg/cli/manifest/registermanifest_test.go b/pkg/cli/manifest/registermanifest_test.go index cc2c1ec525..810fddce9a 100644 --- a/pkg/cli/manifest/registermanifest_test.go +++ b/pkg/cli/manifest/registermanifest_test.go @@ -73,7 +73,7 @@ func TestRegisterDirectory(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - clientFactory, err := NewTestClientFactory() + clientFactory, err := NewTestClientFactory(WithResourceProviderServerNoError) require.NoError(t, err) err = RegisterDirectory(context.Background(), clientFactory, tt.planeName, tt.directoryPath, nil) @@ -129,7 +129,7 @@ func TestRegisterFile(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - clientFactory, err := NewTestClientFactory() + clientFactory, err := NewTestClientFactory(WithResourceProviderServerNoError) require.NoError(t, err, "Failed to create client factory") var logBuffer bytes.Buffer diff --git a/pkg/cli/manifest/testclientfactory.go b/pkg/cli/manifest/testclientfactory.go index 85e9117d1f..3e91b789ed 100644 --- a/pkg/cli/manifest/testclientfactory.go +++ b/pkg/cli/manifest/testclientfactory.go @@ -30,8 +30,31 @@ import ( ) // NewTestClientFactory creates a new client factory for testing purposes. -func NewTestClientFactory() (*v20231001preview.ClientFactory, error) { - // Create fake servers for each client +func NewTestClientFactory(resourceProvidersServer func() ucpfake.ResourceProvidersServer) (*v20231001preview.ClientFactory, error) { + serverFactory := ucpfake.ServerFactory{ + ResourceProvidersServer: resourceProvidersServer(), + ResourceTypesServer: WithResourceTypeServerNoError(), + APIVersionsServer: WithAPIVersionServerNoError(), + LocationsServer: WithLocationServerNoError(), + } + + serverFactoryTransport := ucpfake.NewServerFactoryTransport(&serverFactory) + + clientOptions := &armpolicy.ClientOptions{ + ClientOptions: policy.ClientOptions{ + Transport: serverFactoryTransport, + }, + } + + clientFactory, err := v20231001preview.NewClientFactory(&azfake.TokenCredential{}, clientOptions) + if err != nil { + return nil, err + } + + return clientFactory, err +} + +func WithResourceProviderServerNoError() ucpfake.ResourceProvidersServer { resourceProvidersServer := ucpfake.ResourceProvidersServer{ BeginCreateOrUpdate: func( ctx context.Context, @@ -64,7 +87,10 @@ func NewTestClientFactory() (*v20231001preview.ClientFactory, error) { return }, } + return resourceProvidersServer +} +func WithResourceTypeServerNoError() ucpfake.ResourceTypesServer { resourceTypesServer := ucpfake.ResourceTypesServer{ BeginCreateOrUpdate: func( ctx context.Context, @@ -99,7 +125,10 @@ func NewTestClientFactory() (*v20231001preview.ClientFactory, error) { return }, } + return resourceTypesServer +} +func WithAPIVersionServerNoError() ucpfake.APIVersionsServer { apiVersionsServer := ucpfake.APIVersionsServer{ BeginCreateOrUpdate: func( ctx context.Context, @@ -119,7 +148,10 @@ func NewTestClientFactory() (*v20231001preview.ClientFactory, error) { return }, } + return apiVersionsServer +} +func WithLocationServerNoError() ucpfake.LocationsServer { locationsServer := ucpfake.LocationsServer{ BeginCreateOrUpdate: func( ctx context.Context, @@ -138,27 +170,97 @@ func NewTestClientFactory() (*v20231001preview.ClientFactory, error) { return }, + Get: func( + ctx context.Context, + planeName string, + resourceProviderName string, + locationName string, + options *v20231001preview.LocationsClientGetOptions, + ) (resp azfake.Responder[v20231001preview.LocationsClientGetResponse], errResp azfake.ErrorResponder) { + response := v20231001preview.LocationsClientGetResponse{ + LocationResource: v20231001preview.LocationResource{ + Name: to.Ptr(locationName), + ID: to.Ptr("id"), + Properties: &v20231001preview.LocationProperties{ + ResourceTypes: map[string]*v20231001preview.LocationResourceType{}, + }, + }, + } + resp.SetResponse(http.StatusOK, response, nil) + return + }, } + return locationsServer +} - serverFactory := ucpfake.ServerFactory{ - ResourceProvidersServer: resourceProvidersServer, - ResourceTypesServer: resourceTypesServer, - APIVersionsServer: apiVersionsServer, - LocationsServer: locationsServer, - } - - serverFactoryTransport := ucpfake.NewServerFactoryTransport(&serverFactory) +func WithResourceProviderServerNotFoundError() ucpfake.ResourceProvidersServer { + resourceProvidersNotFoundServer := ucpfake.ResourceProvidersServer{ + BeginCreateOrUpdate: func( + ctx context.Context, + planeName string, + resourceProviderName string, + resource v20231001preview.ResourceProviderResource, + options *v20231001preview.ResourceProvidersClientBeginCreateOrUpdateOptions, + ) (resp azfake.PollerResponder[v20231001preview.ResourceProvidersClientCreateOrUpdateResponse], errResp azfake.ErrorResponder) { + // Simulate successful creation + result := v20231001preview.ResourceProvidersClientCreateOrUpdateResponse{ + ResourceProviderResource: resource, + } + resp.AddNonTerminalResponse(http.StatusCreated, nil) + resp.SetTerminalResponse(http.StatusOK, result, nil) - clientOptions := &armpolicy.ClientOptions{ - ClientOptions: policy.ClientOptions{ - Transport: serverFactoryTransport, + return + }, + Get: func( + ctx context.Context, + planeName string, + resourceProviderName string, + options *v20231001preview.ResourceProvidersClientGetOptions, // Add this parameter + ) (resp azfake.Responder[v20231001preview.ResourceProvidersClientGetResponse], errResp azfake.ErrorResponder) { + response := v20231001preview.ResourceProvidersClientGetResponse{ + ResourceProviderResource: v20231001preview.ResourceProviderResource{ + Name: to.Ptr(resourceProviderName), + }, + } + resp.SetResponse(http.StatusNotFound, response, nil) + return }, } + return resourceProvidersNotFoundServer +} - clientFactory, err := v20231001preview.NewClientFactory(&azfake.TokenCredential{}, clientOptions) - if err != nil { - return nil, err - } +func WithResourceProviderServerInternalError() ucpfake.ResourceProvidersServer { + resourceProvidersServerInternalError := ucpfake.ResourceProvidersServer{ + BeginCreateOrUpdate: func( + ctx context.Context, + planeName string, + resourceProviderName string, + resource v20231001preview.ResourceProviderResource, + options *v20231001preview.ResourceProvidersClientBeginCreateOrUpdateOptions, + ) (resp azfake.PollerResponder[v20231001preview.ResourceProvidersClientCreateOrUpdateResponse], errResp azfake.ErrorResponder) { + // Simulate successful creation + result := v20231001preview.ResourceProvidersClientCreateOrUpdateResponse{ + ResourceProviderResource: resource, + } + resp.AddNonTerminalResponse(http.StatusCreated, nil) + resp.SetTerminalResponse(http.StatusOK, result, nil) - return clientFactory, err + return + }, + Get: func( + ctx context.Context, + planeName string, + resourceProviderName string, + options *v20231001preview.ResourceProvidersClientGetOptions, // Add this parameter + ) (resp azfake.Responder[v20231001preview.ResourceProvidersClientGetResponse], errResp azfake.ErrorResponder) { + response := v20231001preview.ResourceProvidersClientGetResponse{ + ResourceProviderResource: v20231001preview.ResourceProviderResource{ + Name: to.Ptr(resourceProviderName), + }, + } + resp.SetResponse(http.StatusInternalServerError, response, nil) + return + }, + } + return resourceProvidersServerInternalError }