From 9d19b27959a2ef04ab46d0b2251ffca197f6d5b9 Mon Sep 17 00:00:00 2001 From: lakshmimsft Date: Wed, 11 Dec 2024 02:08:51 -0800 Subject: [PATCH] draft 3 Signed-off-by: lakshmimsft --- cmd/ucpd/cmd/root.go | 23 +- pkg/ucp/manifestservice/service.go | 209 ++++++++++++ pkg/ucp/server/server.go | 3 + pkg/ucp/ucpclient/registermanifests.go | 64 ++-- pkg/ucp/ucpclient/registermanifests_test.go | 314 ++++++++++++++++++ .../ucpclient/resourceproviders_fakes_test.go | 2 +- pkg/ucp/ucpclient/resourcetypes_fake_test.go | 2 +- pkg/ucp/ucpclient/testdata/test1.yaml | 6 +- pkg/ucp/ucpclient/testdata/test2.yaml | 6 +- pkg/ucp/ucpclient/ucpclient.go | 61 ++-- pkg/ucp/ucpclient/ucpclient_test.go | 205 ++---------- .../ucpclient_test_deletethisfile.go | 206 ++++++++++++ 12 files changed, 860 insertions(+), 241 deletions(-) create mode 100644 pkg/ucp/manifestservice/service.go create mode 100644 pkg/ucp/ucpclient/ucpclient_test_deletethisfile.go diff --git a/cmd/ucpd/cmd/root.go b/cmd/ucpd/cmd/root.go index f5baa49ee9..aa1ad71738 100644 --- a/cmd/ucpd/cmd/root.go +++ b/cmd/ucpd/cmd/root.go @@ -19,6 +19,7 @@ package cmd import ( "context" "fmt" + "os" "time" "github.com/go-logr/logr" @@ -68,24 +69,30 @@ var rootCmd = &cobra.Command{ if err != nil { return err } - /* - err = backend.RegisterManifests(cmd.Context()) - if err != nil { - return err - } - */ // Discuss if there is a better way to check if the server is listening.. // Start RegisterManifests in a goroutine after 15 seconds go func() { time.Sleep(15 * time.Second) // Register manifests - ucpclient, err := ucpclient.NewUCPClient(&options) + manifestDir := options.Config.Manifests.ManifestDirectory + if _, err := os.Stat(manifestDir); os.IsNotExist(err) { + logger.Error(err, "Manifest directory does not exist", "directory", manifestDir) + return + } else if err != nil { + logger.Error(err, "Error checking manifest directory", "directory", manifestDir) + return + } + + ucpclient, err := ucpclient.NewUCPClient(options.UCPConnection) if err != nil { logger.Error(err, "Failed to create UCP client") } - if err := ucpclient.RegisterManifests(cmd.Context()); err != nil { + + if err := ucpclient.RegisterManifests(cmd.Context(), manifestDir); err != nil { logger.Error(err, "Failed to register manifests") + } else { + logger.Info("Successfully registered manifests", "directory", manifestDir) } }() diff --git a/pkg/ucp/manifestservice/service.go b/pkg/ucp/manifestservice/service.go new file mode 100644 index 0000000000..034a720102 --- /dev/null +++ b/pkg/ucp/manifestservice/service.go @@ -0,0 +1,209 @@ +package manifestservice + +// Import your logger, server, ucpclient, etc. +// "github.com/yourorg/yourproject/logger" +// "github.com/yourorg/yourproject/server" +// "github.com/yourorg/yourproject/ucpclient" + +/* +// Service is a service to run AsyncReqeustProcessWorker. +type Service struct { + worker.Service + + config ucpoptions.UCPConfig +} + +// NewService creates new service instance to run AsyncRequestProcessWorker. +func NewService(options hostoptions.HostOptions, config ucpoptions.UCPConfig) *Service { + return &Service{ + Service: worker.Service{ + ProviderName: UCPProviderName, + Options: options, + }, + config: config, + } +} + +// Name returns a string containing the UCPProviderName and the text "async worker". +func (w *Service) Name() string { + return fmt.Sprintf("%s async worker", UCPProviderName) +} + +// Run starts the service and worker. It initializes the service and sets the worker options based on the configuration, +// then starts the service with the given worker options. It returns an error if the initialization fails. +func (w *Service) Run(ctx context.Context) error { + if err := w.Init(ctx); err != nil { + return err + } + + workerOpts := worker.Options{} + if w.Options.Config.WorkerServer != nil { + if w.Options.Config.WorkerServer.MaxOperationConcurrency != nil { + workerOpts.MaxOperationConcurrency = *w.Options.Config.WorkerServer.MaxOperationConcurrency + } + if w.Options.Config.WorkerServer.MaxOperationRetryCount != nil { + workerOpts.MaxOperationRetryCount = *w.Options.Config.WorkerServer.MaxOperationRetryCount + } + } + + opts := ctrl.Options{ + DataProvider: w.StorageProvider, + } + + defaultDownstream, err := url.Parse(w.config.Routing.DefaultDownstreamEndpoint) + if err != nil { + return err + } + + transport := otelhttp.NewTransport(http.DefaultTransport) + err = RegisterControllers(ctx, w.Controllers, w.Options.UCPConnection, transport, opts, defaultDownstream) + if err != nil { + return err + } + + return w.Start(ctx, workerOpts) +} + +// RegisterControllers registers the controllers for the UCP backend. +func RegisterControllers(ctx context.Context, registry *worker.ControllerRegistry, connection sdk.Connection, transport http.RoundTripper, opts ctrl.Options, defaultDownstream *url.URL) error { + // Tracked resources + err := errors.Join(nil, registry.Register(ctx, v20231001preview.ResourceType, v1.OperationMethod(datamodel.OperationProcess), func(opts ctrl.Options) (ctrl.Controller, error) { + return resourcegroups.NewTrackedResourceProcessController(opts, transport, defaultDownstream) + }, opts)) + + // Resource providers and related types + err = errors.Join(err, registry.Register(ctx, datamodel.ResourceProviderResourceType, v1.OperationPut, func(opts ctrl.Options) (ctrl.Controller, error) { + return &resourceproviders.ResourceProviderPutController{BaseController: ctrl.NewBaseAsyncController(opts)}, nil + }, opts)) + err = errors.Join(err, registry.Register(ctx, datamodel.ResourceProviderResourceType, v1.OperationDelete, func(opts ctrl.Options) (ctrl.Controller, error) { + return &resourceproviders.ResourceProviderDeleteController{ + BaseController: ctrl.NewBaseAsyncController(opts), + Connection: connection, + }, nil + }, opts)) + err = errors.Join(err, registry.Register(ctx, datamodel.ResourceTypeResourceType, v1.OperationPut, func(opts ctrl.Options) (ctrl.Controller, error) { + return &resourceproviders.ResourceTypePutController{BaseController: ctrl.NewBaseAsyncController(opts)}, nil + }, opts)) + err = errors.Join(err, registry.Register(ctx, datamodel.ResourceTypeResourceType, v1.OperationDelete, func(opts ctrl.Options) (ctrl.Controller, error) { + return &resourceproviders.ResourceTypeDeleteController{ + BaseController: ctrl.NewBaseAsyncController(opts), + Connection: connection, + }, nil + }, opts)) + err = errors.Join(err, registry.Register(ctx, datamodel.APIVersionResourceType, v1.OperationPut, func(opts ctrl.Options) (ctrl.Controller, error) { + return &resourceproviders.APIVersionPutController{BaseController: ctrl.NewBaseAsyncController(opts)}, nil + }, opts)) + err = errors.Join(err, registry.Register(ctx, datamodel.APIVersionResourceType, v1.OperationDelete, func(opts ctrl.Options) (ctrl.Controller, error) { + return &resourceproviders.APIVersionDeleteController{BaseController: ctrl.NewBaseAsyncController(opts)}, nil + }, opts)) + err = errors.Join(err, registry.Register(ctx, datamodel.LocationResourceType, v1.OperationPut, func(opts ctrl.Options) (ctrl.Controller, error) { + return &resourceproviders.LocationPutController{BaseController: ctrl.NewBaseAsyncController(opts)}, nil + }, opts)) + err = errors.Join(err, registry.Register(ctx, datamodel.LocationResourceType, v1.OperationDelete, func(opts ctrl.Options) (ctrl.Controller, error) { + return &resourceproviders.LocationDeleteController{BaseController: ctrl.NewBaseAsyncController(opts)}, nil + }, opts)) + + if err != nil { + return err + } + + return nil +} + +func waitForServer(ctx context.Context, host, port string, retryInterval time.Duration, timeout time.Duration) error { + address := net.JoinHostPort(host, port) + deadline := time.Now().Add(timeout) + + for { + select { + case <-ctx.Done(): + return fmt.Errorf("connection attempts canceled or timed out: %w", ctx.Err()) + default: + conn, err := net.DialTimeout("tcp", address, retryInterval) + if err == nil { + conn.Close() + return nil + } + + if time.Now().After(deadline) { + return fmt.Errorf("failed to connect to %s after %v: %w", address, timeout, err) + } + + time.Sleep(retryInterval) + } + } +} + +func main() { + + // Start the server in a separate goroutine + go func() { + if err := host.Start(); err != nil { + logger.Error(err, "Server failed to start") + os.Exit(1) + } + }() + + // Set up signal handling for graceful shutdown + stopChan := make(chan os.Signal, 1) + signal.Notify(stopChan, syscall.SIGINT, syscall.SIGTERM) + + // Start the manifest registration in a goroutine + go func() { + // Define connection parameters + hostName := host.HostName() // Replace with actual method + port := host.Port() // Replace with actual method + + // Define context with timeout for the connection attempts + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + // Attempt to connect to the server + err := waitForServer(ctx, hostName, port, 500*time.Millisecond, 30*time.Second) + if err != nil { + logger.Error(err, "Server is not available for manifest registration") + return + } + + // Server is up, proceed to register manifests + manifestDir := options.Config.Manifests.ManifestDirectory + if _, err := os.Stat(manifestDir); os.IsNotExist(err) { + logger.Error(err, "Manifest directory does not exist", "directory", manifestDir) + return + } else if err != nil { + logger.Error(err, "Error checking manifest directory", "directory", manifestDir) + return + } + + ucpclient, err := ucpclient.NewUCPClient(options.UCPConnection) + if err != nil { + logger.Error(err, "Failed to create UCP client") + return + } + + // Proceed with registering manifests + if err := ucpclient.RegisterManifests(ctx, manifestDir); err != nil { + logger.Error(err, "Failed to register manifests") + return + } + + logger.Info("Successfully registered manifests", "directory", manifestDir) + }() + + // Wait for a termination signal + <-stopChan + logger.Info("Received termination signal. Shutting down...") + + // Gracefully shut down the server + shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 10*time.Second) + defer shutdownCancel() + + if err := host.Shutdown(shutdownCtx); err != nil { + logger.Error(err, "Server shutdown failed") + } else { + logger.Info("Server gracefully stopped") + } + + // Additional cleanup if necessary +} +*/ diff --git a/pkg/ucp/server/server.go b/pkg/ucp/server/server.go index 83a1e9cd8b..6d9d0cb068 100644 --- a/pkg/ucp/server/server.go +++ b/pkg/ucp/server/server.go @@ -38,6 +38,8 @@ import ( "github.com/radius-project/radius/pkg/ucp/frontend/api" "github.com/radius-project/radius/pkg/ucp/hosting" "github.com/radius-project/radius/pkg/ucp/hostoptions" + + //"github.com/radius-project/radius/pkg/ucp/manifestservice" qprovider "github.com/radius-project/radius/pkg/ucp/queue/provider" "github.com/radius-project/radius/pkg/ucp/rest" "github.com/radius-project/radius/pkg/ucp/secret/provider" @@ -204,6 +206,7 @@ func NewServer(options *Options) (*hosting.Host, error) { options.TracerProviderOptions.ServiceName = "ucp" hostingServices = append(hostingServices, &trace.Service{Options: options.TracerProviderOptions}) + //hostingServices = append(hostingServices, &manifestservice.Service{Options: options.LoggingOptions}) return &hosting.Host{ Services: hostingServices, }, nil diff --git a/pkg/ucp/ucpclient/registermanifests.go b/pkg/ucp/ucpclient/registermanifests.go index 46ab0073fd..e86f60fefe 100644 --- a/pkg/ucp/ucpclient/registermanifests.go +++ b/pkg/ucp/ucpclient/registermanifests.go @@ -14,48 +14,68 @@ package ucpclient import ( "context" "errors" + "fmt" "os" "path/filepath" "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/policy" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + aztoken "github.com/radius-project/radius/pkg/azure/tokencredentials" "github.com/radius-project/radius/pkg/cli/manifest" "github.com/radius-project/radius/pkg/sdk" "github.com/radius-project/radius/pkg/to" "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" - "github.com/radius-project/radius/pkg/ucp/server" ) type UCPClientImpl struct { - ManifestDirectory string - Options *policy.ClientOptions + Options *policy.ClientOptions } -func NewUCPClient(options *server.Options) (UCPClient, error) { - if options.UCPConnection == nil { +func NewUCPClient(UCPConnection sdk.Connection) (*UCPClient, error) { + if UCPConnection == nil { return nil, errors.New("UCP connection is nil") } - clientOptions := sdk.NewClientOptions(options.UCPConnection) + clientOptions := sdk.NewClientOptions(UCPConnection) - return &UCPClientImpl{ - ManifestDirectory: options.Config.Manifests.ManifestDirectory, - Options: clientOptions, + resourceProvidersClient, err := v20231001preview.NewResourceProvidersClient(&aztoken.AnonymousCredential{}, clientOptions) + if err != nil { + return nil, fmt.Errorf("failed to create ResourceProvidersClient: %w", err) + } + + resourceTypesClient, err := v20231001preview.NewResourceTypesClient(&aztoken.AnonymousCredential{}, clientOptions) + if err != nil { + return nil, fmt.Errorf("failed to create ResourceTypeResource: %w", err) + } + + apiVersionsClient, err := v20231001preview.NewAPIVersionsClient(&aztoken.AnonymousCredential{}, clientOptions) + if err != nil { + return nil, fmt.Errorf("failed to create APIVersionResource: %w", err) + } + + locationsClient, err := v20231001preview.NewLocationsClient(&aztoken.AnonymousCredential{}, clientOptions) + if err != nil { + return nil, fmt.Errorf("failed to create LocationResource: %w", err) + } + + return &UCPClient{ + ResourceProvidersClient: resourceProvidersClient, + ResourceTypesClient: resourceTypesClient, + APIVersionsClient: apiVersionsClient, + LocationsClient: locationsClient, }, nil } -var _ UCPClient = (*UCPClientImpl)(nil) - -func (u *UCPClientImpl) RegisterManifests(ctx context.Context) error { +func (u *UCPClient) RegisterManifests(ctx context.Context, manifestDirectory string) error { // loop thru files in the directory //manifestDirectory := u.Options.Config.Manifests.ManifestDirectory - if u.ManifestDirectory == "" { - return errors.New("manifest directory path is empty") + if manifestDirectory == "" { + return fmt.Errorf("invalid manifest directory") } // List all files in the manifestDirectory - files, err := os.ReadDir(u.ManifestDirectory) + files, err := os.ReadDir(manifestDirectory) if err != nil { return err } @@ -63,16 +83,16 @@ func (u *UCPClientImpl) RegisterManifests(ctx context.Context) error { // Iterate over each file in the directory for _, fileInfo := range files { if fileInfo.IsDir() { - continue // Skip directories + continue // Skip directories - check if want to include subdirectories } - filePath := filepath.Join(u.ManifestDirectory, fileInfo.Name()) + filePath := filepath.Join(manifestDirectory, fileInfo.Name()) // Read the manifest file resourceProvider, err := manifest.ReadFile(filePath) if err != nil { return err } - _, err = u.CreateOrUpdateResourceProvider(ctx, "local", resourceProvider.Name, &v20231001preview.ResourceProviderResource{ + _, err = u.CreateOrUpdateResourceProvider(ctx, planeName, resourceProvider.Name, &v20231001preview.ResourceProviderResource{ Location: to.Ptr(v1.LocationGlobal), Properties: &v20231001preview.ResourceProviderProperties{}, }) @@ -89,7 +109,7 @@ func (u *UCPClientImpl) RegisterManifests(ctx context.Context) error { } for resourceTypeName, resourceType := range resourceProvider.Types { - _, err := u.CreateOrUpdateResourceType(ctx, "local", resourceProvider.Name, resourceTypeName, &v20231001preview.ResourceTypeResource{ + _, err := u.CreateOrUpdateResourceType(ctx, planeName, resourceProvider.Name, resourceTypeName, &v20231001preview.ResourceTypeResource{ Properties: &v20231001preview.ResourceTypeProperties{ DefaultAPIVersion: resourceType.DefaultAPIVersion, }, @@ -103,7 +123,7 @@ func (u *UCPClientImpl) RegisterManifests(ctx context.Context) error { } for apiVersionName := range resourceType.APIVersions { - _, err := u.CreateOrUpdateAPIVersion(ctx, "local", resourceProvider.Name, resourceTypeName, apiVersionName, &v20231001preview.APIVersionResource{ + _, err := u.CreateOrUpdateAPIVersion(ctx, planeName, resourceProvider.Name, resourceTypeName, apiVersionName, &v20231001preview.APIVersionResource{ Properties: &v20231001preview.APIVersionProperties{}, }) if err != nil { @@ -116,12 +136,12 @@ func (u *UCPClientImpl) RegisterManifests(ctx context.Context) error { locationResource.Properties.ResourceTypes[resourceTypeName] = locationResourceType } - _, err = u.CreateOrUpdateLocation(ctx, "local", resourceProvider.Name, v1.LocationGlobal, &locationResource) + _, err = u.CreateOrUpdateLocation(ctx, planeName, resourceProvider.Name, v1.LocationGlobal, &locationResource) if err != nil { return err } - _, err = u.GetResourceProvider(ctx, "local", resourceProvider.Name) + _, err = u.GetResourceProvider(ctx, planeName, resourceProvider.Name) if err != nil { return err } diff --git a/pkg/ucp/ucpclient/registermanifests_test.go b/pkg/ucp/ucpclient/registermanifests_test.go index 25084d960d..d27a23a754 100644 --- a/pkg/ucp/ucpclient/registermanifests_test.go +++ b/pkg/ucp/ucpclient/registermanifests_test.go @@ -44,3 +44,317 @@ func TestRegisterManifests(t *testing.T) { } */ +/* +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. +*/ + +import ( + "context" + "fmt" + "net/http" + "testing" + + // armpolicy "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/policy" + armpolicy "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/policy" + azfake "github.com/Azure/azure-sdk-for-go/sdk/azcore/fake" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" + "github.com/radius-project/radius/pkg/to" + "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" + ucpfake "github.com/radius-project/radius/pkg/ucp/api/v20231001preview/fake" + "github.com/stretchr/testify/require" +) + +type FakeUCPServer struct { + ResourceProvidersServer *ucpfake.ResourceProvidersServer + ResourceTypesServer *ucpfake.ResourceTypesServer + APIVersionsServer *ucpfake.APIVersionsServer + LocationsServer *ucpfake.LocationsServer +} + +/* +type MultiTransport struct { + transports map[string]http.RoundTripper +} + +func (mt *MultiTransport) RoundTrip(req *http.Request) (*http.Response, error) { + for prefix, transport := range mt.transports { + if strings.HasPrefix(req.URL.Path, prefix) { + return transport.RoundTrip(req) + } + } + return nil, fmt.Errorf("no transport found for request path: %s", req.URL.Path) +}*/ + +func NewFakeUCPServer() (*FakeUCPServer, error) { + + // Create fake servers for each client + resourceProvidersServer := &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 + }, + Get: func( + ctx context.Context, + planeName string, + resourceProviderName string, + options *v20231001preview.ResourceProvidersClientGetOptions, + ) (resp azfake.Responder[v20231001preview.ResourceProvidersClientGetResponse], errResp azfake.ErrorResponder) { + response := v20231001preview.ResourceProvidersClientGetResponse{ + ResourceProviderResource: v20231001preview.ResourceProviderResource{ + Name: to.Ptr(resourceProviderName), + }, + } + resp.SetResponse(http.StatusOK, response, nil) + return + }, + } + + // Create other fake servers similarly + resourceTypesServer := &ucpfake.ResourceTypesServer{ + BeginCreateOrUpdate: func( + ctx context.Context, + planeName string, + resourceProviderName string, + resourceTypeName string, + resource v20231001preview.ResourceTypeResource, + options *v20231001preview.ResourceTypesClientBeginCreateOrUpdateOptions, + ) (resp azfake.PollerResponder[v20231001preview.ResourceTypesClientCreateOrUpdateResponse], errResp azfake.ErrorResponder) { + result := v20231001preview.ResourceTypesClientCreateOrUpdateResponse{ + ResourceTypeResource: resource, + } + + resp.AddNonTerminalResponse(http.StatusCreated, nil) + resp.SetTerminalResponse(http.StatusOK, result, nil) + + return + }, + Get: func( + ctx context.Context, + planeName string, + resourceProviderName string, + resourceTypeName string, + options *v20231001preview.ResourceTypesClientGetOptions, + ) (resp azfake.Responder[v20231001preview.ResourceTypesClientGetResponse], errResp azfake.ErrorResponder) { + response := v20231001preview.ResourceTypesClientGetResponse{ + ResourceTypeResource: v20231001preview.ResourceTypeResource{ + Name: to.Ptr(resourceTypeName), + }, + } + resp.SetResponse(http.StatusOK, response, nil) + return + }, + } + + apiVersionsServer := &ucpfake.APIVersionsServer{ + BeginCreateOrUpdate: func( + ctx context.Context, + planeName string, + resourceProviderName string, + resourceTypeName string, + apiVersionName string, // Added missing parameter + resource v20231001preview.APIVersionResource, + options *v20231001preview.APIVersionsClientBeginCreateOrUpdateOptions, + ) (resp azfake.PollerResponder[v20231001preview.APIVersionsClientCreateOrUpdateResponse], errResp azfake.ErrorResponder) { + // Simulate successful creation + result := v20231001preview.APIVersionsClientCreateOrUpdateResponse{ + APIVersionResource: resource, + } + resp.AddNonTerminalResponse(http.StatusCreated, nil) + resp.SetTerminalResponse(http.StatusOK, result, nil) + return + }, + } + + locationsServer := &ucpfake.LocationsServer{ + BeginCreateOrUpdate: func( + ctx context.Context, + planeName string, + resourceProviderName string, + locationName string, + resource v20231001preview.LocationResource, + options *v20231001preview.LocationsClientBeginCreateOrUpdateOptions, + ) (resp azfake.PollerResponder[v20231001preview.LocationsClientCreateOrUpdateResponse], errResp azfake.ErrorResponder) { + // Simulate successful creation + result := v20231001preview.LocationsClientCreateOrUpdateResponse{ + LocationResource: resource, + } + resp.AddNonTerminalResponse(http.StatusCreated, nil) + resp.SetTerminalResponse(http.StatusOK, result, nil) + + return + }, + } + + // Return the FakeUCPClient instance + return &FakeUCPServer{ + ResourceProvidersServer: resourceProvidersServer, + ResourceTypesServer: resourceTypesServer, + APIVersionsServer: apiVersionsServer, + LocationsServer: locationsServer, + /*UCPClient: ucpClient, + + ResourceProvidersClient: *resourceProvidersClient, + ResourceTypesClient: *resourceTypesClient, + APIVersionsClient: *apiVersionsClient, + LocationsClient: *locationsClient, + */ + }, nil +} + +func NewTestUCPClient(server *FakeUCPServer) (*UCPClient, error) { + + // Create individual transports for each fake server + resourceProvidersTransport := ucpfake.NewResourceProvidersServerTransport(server.ResourceProvidersServer) + resourceTypesTransport := ucpfake.NewResourceTypesServerTransport(server.ResourceTypesServer) + apiVersionsTransport := ucpfake.NewAPIVersionsServerTransport(server.APIVersionsServer) + locationsTransport := ucpfake.NewLocationsServerTransport(server.LocationsServer) + + /* + // Create the ServerFactory + serverFactory := &ucpfake.ServerFactory{} + + // Register your fake servers with the ServerFactory + serverFactory.ResourceProvidersServer = *resourceProvidersServer + serverFactory.ResourceTypesServer = *resourceTypesServer + serverFactory.APIVersionsServer = *apiVersionsServer + serverFactory.LocationsServer = *locationsServer + //locTransporter := ucpfake.ServerFactoryTransport(locationsServer) + + serverFactoryTransport := ucpfake.NewServerFactoryTransport(serverFactory) + */ + + //clientOptions := &policy.ClientOptions{Transport: serverFactoryTransport} + + // Assuming you have a UCPConnection or can use a fake one for testing + //ucpConnection, err := sdk.NewDirectConnection("") // Replace with a valid implementation + /* + // Create the server.Options instance + serverOptions := &server.Options{ + UCPConnection: ucpConnection, + } + + httpClient:= http.Client{ + Transport: serverFactoryTransport, + } + + ucpConnection.Client() = httpClient + */ + // Instantiate UCPClientImpl with the unified ClientOptions + //ucpClient, err := NewUCPClient(ucpConnection) + /*if err != nil { + return nil, fmt.Errorf("failed to create UCP client: %w", err) + }*/ + + // Configure client options with respective transports + resourceProvidersOptions := &armpolicy.ClientOptions{ + ClientOptions: policy.ClientOptions{ + Transport: resourceProvidersTransport, + }, + } + + resourceTypesOptions := &armpolicy.ClientOptions{ + ClientOptions: policy.ClientOptions{ + Transport: resourceTypesTransport, + }, + } + + apiVersionsOptions := &armpolicy.ClientOptions{ + ClientOptions: policy.ClientOptions{ + Transport: apiVersionsTransport, + }, + } + + locationsOptions := &armpolicy.ClientOptions{ + ClientOptions: policy.ClientOptions{ + Transport: locationsTransport, + }, + } + + credential := &azfake.TokenCredential{} + + resourceProvidersClient, err := v20231001preview.NewResourceProvidersClient(credential, resourceProvidersOptions) + if err != nil { + return nil, fmt.Errorf("failed to create fake ResourceProvidersClient: %w", err) + } + + resourceTypesClient, err := v20231001preview.NewResourceTypesClient(credential, resourceTypesOptions) + if err != nil { + return nil, fmt.Errorf("failed to create fake ResourceTypesClient: %w", err) + } + + apiVersionsClient, err := v20231001preview.NewAPIVersionsClient(credential, apiVersionsOptions) + if err != nil { + return nil, fmt.Errorf("failed to create fake APIVersionsClient: %w", err) + } + + locationsClient, err := v20231001preview.NewLocationsClient(credential, locationsOptions) + if err != nil { + return nil, fmt.Errorf("failed to create fake LocationsClient: %w", err) + } + + return &UCPClient{ + ResourceProvidersClient: resourceProvidersClient, + ResourceTypesClient: resourceTypesClient, + APIVersionsClient: apiVersionsClient, + LocationsClient: locationsClient, + }, nil +} + +func TestRegisterManifests_Success(t *testing.T) { + ctx := context.Background() + expectedResourceProvider := "MyCompany2.CompanyName2" + + // Setup fake UCP server + server, err := NewFakeUCPServer() + require.NoError(t, err) + + // Setup UCP client with fake servers + client, err := NewTestUCPClient(server) + require.NoError(t, err) + + err = client.RegisterManifests(ctx, "testdata") + require.NoError(t, err) + + // Verify resource provider was created + rp, err := client.GetResourceProvider(ctx, planeName, expectedResourceProvider) + require.NoError(t, err) + require.Equal(t, to.Ptr(expectedResourceProvider), rp.Name) + +} + +func TestRegisterManifests_InvalidParameters(t *testing.T) { + ctx := context.Background() + + // Setup fake UCP server + server, err := NewFakeUCPServer() + require.NoError(t, err) + + // Setup UCP client with fake servers + client, err := NewTestUCPClient(server) + require.NoError(t, err) + + // Pass invalid manifest directory + err = client.RegisterManifests(ctx, "") + require.Error(t, err) + require.Contains(t, err.Error(), "invalid manifest directory") +} diff --git a/pkg/ucp/ucpclient/resourceproviders_fakes_test.go b/pkg/ucp/ucpclient/resourceproviders_fakes_test.go index 23a1e25638..0e08277d44 100644 --- a/pkg/ucp/ucpclient/resourceproviders_fakes_test.go +++ b/pkg/ucp/ucpclient/resourceproviders_fakes_test.go @@ -114,7 +114,7 @@ func TestResourceProvidersServer(t *testing.T) { require.NoError(t, err) ctx := context.Background() - planeName := "local" + planeName := planeName resourceProviderName := "testResourceProvider" resource := v20231001preview.ResourceProviderResource{ Name: to.Ptr(resourceProviderName), diff --git a/pkg/ucp/ucpclient/resourcetypes_fake_test.go b/pkg/ucp/ucpclient/resourcetypes_fake_test.go index fab4bbba60..cb06d7eaf2 100644 --- a/pkg/ucp/ucpclient/resourcetypes_fake_test.go +++ b/pkg/ucp/ucpclient/resourcetypes_fake_test.go @@ -108,7 +108,7 @@ func TestResourceTypesServer(t *testing.T) { require.NoError(t, err) ctx := context.Background() - planeName := "local" + planeName := planeName resourceProviderName := "testResourceProvider" resourceTypeName := "testResourceType" resource := v20231001preview.ResourceTypeResource{ diff --git a/pkg/ucp/ucpclient/testdata/test1.yaml b/pkg/ucp/ucpclient/testdata/test1.yaml index 16740fb5d3..b76f369047 100644 --- a/pkg/ucp/ucpclient/testdata/test1.yaml +++ b/pkg/ucp/ucpclient/testdata/test1.yaml @@ -1,11 +1,11 @@ -name: Applications.Core +name: MyCompany.CompanyName types: - containers: + testResource1: apiVersions: "2025-01-01-preview": schema: {} capabilities: [] - applications: + testResource2: apiVersions: "2025-01-01-preview": schema: {} diff --git a/pkg/ucp/ucpclient/testdata/test2.yaml b/pkg/ucp/ucpclient/testdata/test2.yaml index 8fe9cf02de..3ef2b3ffcc 100644 --- a/pkg/ucp/ucpclient/testdata/test2.yaml +++ b/pkg/ucp/ucpclient/testdata/test2.yaml @@ -1,11 +1,11 @@ -name: Applications.Dapr +name: MyCompany2.CompanyName2 types: - configurationStores: + testResource3: apiVersions: "2025-01-01-preview": schema: {} capabilities: ["Recipes"] - pubSubBrokers: + testResource4: apiVersions: "2025-01-01-preview": schema: {} diff --git a/pkg/ucp/ucpclient/ucpclient.go b/pkg/ucp/ucpclient/ucpclient.go index 2303d96484..03de4be03e 100644 --- a/pkg/ucp/ucpclient/ucpclient.go +++ b/pkg/ucp/ucpclient/ucpclient.go @@ -15,10 +15,10 @@ import ( "context" "fmt" - aztoken "github.com/radius-project/radius/pkg/azure/tokencredentials" ucpv20231001 "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" ) +/* // UCPClient is a client for interacting with the UCP API. type UCPClient interface { // CreateOrUpdateResourceProvider creates or updates a resource provider in the configured scope. @@ -40,15 +40,28 @@ type UCPClient interface { // ... Add other methods as needed ... } +*/ + +const planeName = "local" + +// UCPClient holds instances of each specific client. +type UCPClient struct { + ResourceProvidersClient *ucpv20231001.ResourceProvidersClient + ResourceTypesClient *ucpv20231001.ResourceTypesClient + APIVersionsClient *ucpv20231001.APIVersionsClient + LocationsClient *ucpv20231001.LocationsClient + // Add other clients as needed +} + +//var _ UCPClient = (*UCPClientFactory)(nil) // CreateOrUpdateResourceProvider creates or updates a resource provider in the configured scope. -func (u *UCPClientImpl) CreateOrUpdateResourceProvider(ctx context.Context, planeName string, resourceProviderName string, resource *ucpv20231001.ResourceProviderResource) (ucpv20231001.ResourceProviderResource, error) { - client, err := ucpv20231001.NewResourceProvidersClient(&aztoken.AnonymousCredential{}, u.Options) - if err != nil { - return ucpv20231001.ResourceProviderResource{}, fmt.Errorf("failed to create ResourceProvidersClient: %w", err) +func (u *UCPClient) CreateOrUpdateResourceProvider(ctx context.Context, planeName string, resourceProviderName string, resource *ucpv20231001.ResourceProviderResource) (ucpv20231001.ResourceProviderResource, error) { + if u.ResourceProvidersClient == nil { + return ucpv20231001.ResourceProviderResource{}, fmt.Errorf("ResourceProvidersClient is nil") } - poller, err := client.BeginCreateOrUpdate(ctx, planeName, resourceProviderName, *resource, &ucpv20231001.ResourceProvidersClientBeginCreateOrUpdateOptions{}) + poller, err := u.ResourceProvidersClient.BeginCreateOrUpdate(ctx, planeName, resourceProviderName, *resource, &ucpv20231001.ResourceProvidersClientBeginCreateOrUpdateOptions{}) if err != nil { return ucpv20231001.ResourceProviderResource{}, fmt.Errorf("begin create or update failed: %w", err) } @@ -62,13 +75,12 @@ func (u *UCPClientImpl) CreateOrUpdateResourceProvider(ctx context.Context, plan } // CreateOrUpdateResourceType creates or updates a resource type in the configured scope. -func (u *UCPClientImpl) CreateOrUpdateResourceType(ctx context.Context, planeName string, resourceProviderName string, resourceTypeName string, resource *ucpv20231001.ResourceTypeResource) (ucpv20231001.ResourceTypeResource, error) { - client, err := ucpv20231001.NewResourceTypesClient(&aztoken.AnonymousCredential{}, u.Options) - if err != nil { - return ucpv20231001.ResourceTypeResource{}, fmt.Errorf("failed to create ResourceTypeResource: %w", err) +func (u *UCPClient) CreateOrUpdateResourceType(ctx context.Context, planeName string, resourceProviderName string, resourceTypeName string, resource *ucpv20231001.ResourceTypeResource) (ucpv20231001.ResourceTypeResource, error) { + if u.ResourceTypesClient == nil { + return ucpv20231001.ResourceTypeResource{}, fmt.Errorf("ResourceTypeResource is nil") } - poller, err := client.BeginCreateOrUpdate(ctx, planeName, resourceProviderName, resourceTypeName, *resource, &ucpv20231001.ResourceTypesClientBeginCreateOrUpdateOptions{}) + poller, err := u.ResourceTypesClient.BeginCreateOrUpdate(ctx, planeName, resourceProviderName, resourceTypeName, *resource, &ucpv20231001.ResourceTypesClientBeginCreateOrUpdateOptions{}) if err != nil { return ucpv20231001.ResourceTypeResource{}, err } @@ -82,13 +94,12 @@ func (u *UCPClientImpl) CreateOrUpdateResourceType(ctx context.Context, planeNam } // CreateOrUpdateAPIVersion creates or updates an API version in the configured scope. -func (u *UCPClientImpl) CreateOrUpdateAPIVersion(ctx context.Context, planeName string, resourceProviderName string, resourceTypeName string, apiVersionName string, resource *ucpv20231001.APIVersionResource) (ucpv20231001.APIVersionResource, error) { - client, err := ucpv20231001.NewAPIVersionsClient(&aztoken.AnonymousCredential{}, u.Options) - if err != nil { - return ucpv20231001.APIVersionResource{}, fmt.Errorf("failed to create APIVersionResource: %w", err) +func (u *UCPClient) CreateOrUpdateAPIVersion(ctx context.Context, planeName string, resourceProviderName string, resourceTypeName string, apiVersionName string, resource *ucpv20231001.APIVersionResource) (ucpv20231001.APIVersionResource, error) { + if u.APIVersionsClient == nil { + return ucpv20231001.APIVersionResource{}, fmt.Errorf("APIVersionResource is nil") } - poller, err := client.BeginCreateOrUpdate(ctx, planeName, resourceProviderName, resourceTypeName, apiVersionName, *resource, &ucpv20231001.APIVersionsClientBeginCreateOrUpdateOptions{}) + poller, err := u.APIVersionsClient.BeginCreateOrUpdate(ctx, planeName, resourceProviderName, resourceTypeName, apiVersionName, *resource, &ucpv20231001.APIVersionsClientBeginCreateOrUpdateOptions{}) if err != nil { return ucpv20231001.APIVersionResource{}, err } @@ -102,13 +113,12 @@ func (u *UCPClientImpl) CreateOrUpdateAPIVersion(ctx context.Context, planeName } // CreateOrUpdateLocation creates or updates a resource provider location in the configured scope. -func (u *UCPClientImpl) CreateOrUpdateLocation(ctx context.Context, planeName string, resourceProviderName string, locationName string, resource *ucpv20231001.LocationResource) (ucpv20231001.LocationResource, error) { - client, err := ucpv20231001.NewLocationsClient(&aztoken.AnonymousCredential{}, u.Options) - if err != nil { - return ucpv20231001.LocationResource{}, fmt.Errorf("failed to create LocationResource: %w", err) +func (u *UCPClient) CreateOrUpdateLocation(ctx context.Context, planeName string, resourceProviderName string, locationName string, resource *ucpv20231001.LocationResource) (ucpv20231001.LocationResource, error) { + if u.LocationsClient == nil { + return ucpv20231001.LocationResource{}, fmt.Errorf("LocationResource is nil") } - poller, err := client.BeginCreateOrUpdate(ctx, planeName, resourceProviderName, locationName, *resource, &ucpv20231001.LocationsClientBeginCreateOrUpdateOptions{}) + poller, err := u.LocationsClient.BeginCreateOrUpdate(ctx, planeName, resourceProviderName, locationName, *resource, &ucpv20231001.LocationsClientBeginCreateOrUpdateOptions{}) if err != nil { return ucpv20231001.LocationResource{}, err } @@ -122,13 +132,12 @@ func (u *UCPClientImpl) CreateOrUpdateLocation(ctx context.Context, planeName st } // GetResourceProvider gets the resource provider with the specified name in the configured scope. -func (u *UCPClientImpl) GetResourceProvider(ctx context.Context, planeName string, resourceProviderName string) (ucpv20231001.ResourceProviderResource, error) { - client, err := ucpv20231001.NewResourceProvidersClient(&aztoken.AnonymousCredential{}, u.Options) - if err != nil { - return ucpv20231001.ResourceProviderResource{}, fmt.Errorf("failed to create ResourceProvidersClient: %w", err) +func (u *UCPClient) GetResourceProvider(ctx context.Context, planeName string, resourceProviderName string) (ucpv20231001.ResourceProviderResource, error) { + if u.ResourceProvidersClient == nil { + return ucpv20231001.ResourceProviderResource{}, fmt.Errorf("ResourceProvidersClient is nil") } - response, err := client.Get(ctx, planeName, resourceProviderName, &ucpv20231001.ResourceProvidersClientGetOptions{}) + response, err := u.ResourceProvidersClient.Get(ctx, planeName, resourceProviderName, &ucpv20231001.ResourceProvidersClientGetOptions{}) if err != nil { return ucpv20231001.ResourceProviderResource{}, err } diff --git a/pkg/ucp/ucpclient/ucpclient_test.go b/pkg/ucp/ucpclient/ucpclient_test.go index 09b5a54bba..43a100cf1a 100644 --- a/pkg/ucp/ucpclient/ucpclient_test.go +++ b/pkg/ucp/ucpclient/ucpclient_test.go @@ -13,193 +13,44 @@ package ucpclient import ( "context" - "fmt" - "net/http" + "testing" - armpolicy "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/policy" - azfake "github.com/Azure/azure-sdk-for-go/sdk/azcore/fake" - "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" "github.com/radius-project/radius/pkg/to" - "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" - ucpfake "github.com/radius-project/radius/pkg/ucp/api/v20231001preview/fake" + ucpv20231001 "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" + "github.com/stretchr/testify/require" ) -type FakeUCPClient struct { - ResourceProvidersClient v20231001preview.ResourceProvidersClient - ResourceTypesClient v20231001preview.ResourceTypesClient - APIVersionsClient v20231001preview.APIVersionsClient - LocationsClient v20231001preview.LocationsClient -} - -func NewFakeUCPClient() (*FakeUCPClient, error) { - // Create fake servers for each client - resourceProvidersServer := &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.StatusAccepted, nil) - resp.SetTerminalResponse(http.StatusOK, result, nil) - - return - }, - Get: func( - ctx context.Context, - planeName string, - resourceProviderName string, - options *v20231001preview.ResourceProvidersClientGetOptions, - ) (resp azfake.Responder[v20231001preview.ResourceProvidersClientGetResponse], errResp azfake.ErrorResponder) { - response := v20231001preview.ResourceProvidersClientGetResponse{ - ResourceProviderResource: v20231001preview.ResourceProviderResource{ - Name: to.Ptr(resourceProviderName), - }, - } - resp.SetResponse(http.StatusOK, response, nil) - return - }, +func TestCreateOrUpdateResourceProvider_Success(t *testing.T) { + ctx := context.Background() + resourceProviderName := "MyCompany2.CompanyName2" + resource := &ucpv20231001.ResourceProviderResource{ + Name: to.Ptr(resourceProviderName), } - // Create other fake servers similarly - resourceTypesServer := &ucpfake.ResourceTypesServer{ - BeginCreateOrUpdate: func( - ctx context.Context, - planeName string, - resourceProviderName string, - resourceTypeName string, - resource v20231001preview.ResourceTypeResource, - options *v20231001preview.ResourceTypesClientBeginCreateOrUpdateOptions, - ) (resp azfake.PollerResponder[v20231001preview.ResourceTypesClientCreateOrUpdateResponse], errResp azfake.ErrorResponder) { - result := v20231001preview.ResourceTypesClientCreateOrUpdateResponse{ - ResourceTypeResource: resource, - } - - // Create a PollerResponder to simulate a long-running operation (LRO) - resp.SetTerminalResponse(http.StatusOK, result, nil) + // Setup fake UCP server + server, err := NewFakeUCPServer() + require.NoError(t, err) - return - }, - Get: func( - ctx context.Context, - planeName string, - resourceProviderName string, - resourceTypeName string, - options *v20231001preview.ResourceTypesClientGetOptions, - ) (resp azfake.Responder[v20231001preview.ResourceTypesClientGetResponse], errResp azfake.ErrorResponder) { - response := v20231001preview.ResourceTypesClientGetResponse{ - ResourceTypeResource: v20231001preview.ResourceTypeResource{ - Name: to.Ptr(resourceTypeName), - }, - } - resp.SetResponse(http.StatusOK, response, nil) - return - }, - } - - apiVersionsServer := &ucpfake.APIVersionsServer{ - BeginCreateOrUpdate: func( - ctx context.Context, - planeName string, - resourceProviderName string, - resourceTypeName string, - apiVersionName string, // Added missing parameter - resource v20231001preview.APIVersionResource, - options *v20231001preview.APIVersionsClientBeginCreateOrUpdateOptions, - ) (resp azfake.PollerResponder[v20231001preview.APIVersionsClientCreateOrUpdateResponse], errResp azfake.ErrorResponder) { - // Simulate successful creation - result := v20231001preview.APIVersionsClientCreateOrUpdateResponse{ - APIVersionResource: resource, - } - resp.AddNonTerminalResponse(http.StatusAccepted, nil) - resp.SetTerminalResponse(http.StatusOK, result, nil) - return - }, - } - - locationsServer := &ucpfake.LocationsServer{ - BeginCreateOrUpdate: func( - ctx context.Context, - planeName string, - resourceProviderName string, - locationName string, - resource v20231001preview.LocationResource, - options *v20231001preview.LocationsClientBeginCreateOrUpdateOptions, - ) (resp azfake.PollerResponder[v20231001preview.LocationsClientCreateOrUpdateResponse], errResp azfake.ErrorResponder) { - // Simulate successful creation - result := v20231001preview.LocationsClientCreateOrUpdateResponse{ - LocationResource: resource, - } - resp.AddNonTerminalResponse(http.StatusAccepted, nil) - resp.SetTerminalResponse(http.StatusOK, result, nil) - - return - }, - } - - // Create individual transports for each fake server - resourceProvidersTransport := ucpfake.NewResourceProvidersServerTransport(resourceProvidersServer) - resourceTypesTransport := ucpfake.NewResourceTypesServerTransport(resourceTypesServer) - apiVersionsTransport := ucpfake.NewAPIVersionsServerTransport(apiVersionsServer) - locationsTransport := ucpfake.NewLocationsServerTransport(locationsServer) - - // Configure client options with respective transports - resourceProvidersOptions := &armpolicy.ClientOptions{ - ClientOptions: policy.ClientOptions{ - Transport: resourceProvidersTransport, - }, - } + // Setup UCP client with fake servers + client, err := NewTestUCPClient(server) + require.NoError(t, err) - resourceTypesOptions := &armpolicy.ClientOptions{ - ClientOptions: policy.ClientOptions{ - Transport: resourceTypesTransport, - }, - } - - apiVersionsOptions := &armpolicy.ClientOptions{ - ClientOptions: policy.ClientOptions{ - Transport: apiVersionsTransport, - }, - } - - locationsOptions := &armpolicy.ClientOptions{ - ClientOptions: policy.ClientOptions{ - Transport: locationsTransport, - }, - } - - credential := &azfake.TokenCredential{} - - resourceProvidersClient, err := v20231001preview.NewResourceProvidersClient(credential, resourceProvidersOptions) - if err != nil { - return nil, fmt.Errorf("failed to create fake ResourceProvidersClient: %w", err) - } - - resourceTypesClient, err := v20231001preview.NewResourceTypesClient(credential, resourceTypesOptions) - if err != nil { - return nil, fmt.Errorf("failed to create fake ResourceTypesClient: %w", err) - } + updatedResource, err := client.CreateOrUpdateResourceProvider(ctx, planeName, resourceProviderName, resource) + require.NoError(t, err) + require.Equal(t, resourceProviderName, *updatedResource.Name) +} - apiVersionsClient, err := v20231001preview.NewAPIVersionsClient(credential, apiVersionsOptions) - if err != nil { - return nil, fmt.Errorf("failed to create fake APIVersionsClient: %w", err) +func TestCreateOrUpdateResourceProvider_Failure(t *testing.T) { + ctx := context.Background() + resourceProviderName := "MyCompany2.CompanyName2" + resource := &ucpv20231001.ResourceProviderResource{ + Name: to.Ptr(resourceProviderName), } - locationsClient, err := v20231001preview.NewLocationsClient(credential, locationsOptions) - if err != nil { - return nil, fmt.Errorf("failed to create fake LocationsClient: %w", err) - } + // Setup UCP client with fake servers + client := UCPClient{} - // Return the FakeUCPClient instance - return &FakeUCPClient{ - ResourceProvidersClient: *resourceProvidersClient, - ResourceTypesClient: *resourceTypesClient, - APIVersionsClient: *apiVersionsClient, - LocationsClient: *locationsClient, - }, nil + _, err := client.CreateOrUpdateResourceProvider(ctx, planeName, resourceProviderName, resource) + require.Error(t, err) + require.ErrorContains(t, err, "ResourceProvidersClient is nil") } diff --git a/pkg/ucp/ucpclient/ucpclient_test_deletethisfile.go b/pkg/ucp/ucpclient/ucpclient_test_deletethisfile.go new file mode 100644 index 0000000000..61ba93a7e6 --- /dev/null +++ b/pkg/ucp/ucpclient/ucpclient_test_deletethisfile.go @@ -0,0 +1,206 @@ +/* +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 ucpclient + +import ( + "context" + "fmt" + "net/http" + + armpolicy "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/policy" + azfake "github.com/Azure/azure-sdk-for-go/sdk/azcore/fake" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" + "github.com/radius-project/radius/pkg/to" + "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" + ucpfake "github.com/radius-project/radius/pkg/ucp/api/v20231001preview/fake" +) + +type FakeUCPClient struct { + ResourceProvidersClient v20231001preview.ResourceProvidersClient + ResourceTypesClient v20231001preview.ResourceTypesClient + APIVersionsClient v20231001preview.APIVersionsClient + LocationsClient v20231001preview.LocationsClient +} + +func NewFakeUCPClient() (*FakeUCPClient, error) { + + // Create fake servers for each client + resourceProvidersServer := &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.StatusAccepted, nil) + resp.SetTerminalResponse(http.StatusOK, result, nil) + + return + }, + Get: func( + ctx context.Context, + planeName string, + resourceProviderName string, + options *v20231001preview.ResourceProvidersClientGetOptions, + ) (resp azfake.Responder[v20231001preview.ResourceProvidersClientGetResponse], errResp azfake.ErrorResponder) { + response := v20231001preview.ResourceProvidersClientGetResponse{ + ResourceProviderResource: v20231001preview.ResourceProviderResource{ + Name: to.Ptr(resourceProviderName), + }, + } + resp.SetResponse(http.StatusOK, response, nil) + return + }, + } + + // Create other fake servers similarly + resourceTypesServer := &ucpfake.ResourceTypesServer{ + BeginCreateOrUpdate: func( + ctx context.Context, + planeName string, + resourceProviderName string, + resourceTypeName string, + resource v20231001preview.ResourceTypeResource, + options *v20231001preview.ResourceTypesClientBeginCreateOrUpdateOptions, + ) (resp azfake.PollerResponder[v20231001preview.ResourceTypesClientCreateOrUpdateResponse], errResp azfake.ErrorResponder) { + result := v20231001preview.ResourceTypesClientCreateOrUpdateResponse{ + ResourceTypeResource: resource, + } + + // Create a PollerResponder to simulate a long-running operation (LRO) + resp.SetTerminalResponse(http.StatusOK, result, nil) + + return + }, + Get: func( + ctx context.Context, + planeName string, + resourceProviderName string, + resourceTypeName string, + options *v20231001preview.ResourceTypesClientGetOptions, + ) (resp azfake.Responder[v20231001preview.ResourceTypesClientGetResponse], errResp azfake.ErrorResponder) { + response := v20231001preview.ResourceTypesClientGetResponse{ + ResourceTypeResource: v20231001preview.ResourceTypeResource{ + Name: to.Ptr(resourceTypeName), + }, + } + resp.SetResponse(http.StatusOK, response, nil) + return + }, + } + + apiVersionsServer := &ucpfake.APIVersionsServer{ + BeginCreateOrUpdate: func( + ctx context.Context, + planeName string, + resourceProviderName string, + resourceTypeName string, + apiVersionName string, // Added missing parameter + resource v20231001preview.APIVersionResource, + options *v20231001preview.APIVersionsClientBeginCreateOrUpdateOptions, + ) (resp azfake.PollerResponder[v20231001preview.APIVersionsClientCreateOrUpdateResponse], errResp azfake.ErrorResponder) { + // Simulate successful creation + result := v20231001preview.APIVersionsClientCreateOrUpdateResponse{ + APIVersionResource: resource, + } + resp.AddNonTerminalResponse(http.StatusAccepted, nil) + resp.SetTerminalResponse(http.StatusOK, result, nil) + return + }, + } + + locationsServer := &ucpfake.LocationsServer{ + BeginCreateOrUpdate: func( + ctx context.Context, + planeName string, + resourceProviderName string, + locationName string, + resource v20231001preview.LocationResource, + options *v20231001preview.LocationsClientBeginCreateOrUpdateOptions, + ) (resp azfake.PollerResponder[v20231001preview.LocationsClientCreateOrUpdateResponse], errResp azfake.ErrorResponder) { + // Simulate successful creation + result := v20231001preview.LocationsClientCreateOrUpdateResponse{ + LocationResource: resource, + } + resp.AddNonTerminalResponse(http.StatusAccepted, nil) + resp.SetTerminalResponse(http.StatusOK, result, nil) + + return + }, + } + + // Create individual transports for each fake server + resourceProvidersTransport := ucpfake.NewResourceProvidersServerTransport(resourceProvidersServer) + resourceTypesTransport := ucpfake.NewResourceTypesServerTransport(resourceTypesServer) + apiVersionsTransport := ucpfake.NewAPIVersionsServerTransport(apiVersionsServer) + locationsTransport := ucpfake.NewLocationsServerTransport(locationsServer) + + // Configure client options with respective transports + resourceProvidersOptions := &armpolicy.ClientOptions{ + ClientOptions: policy.ClientOptions{ + Transport: resourceProvidersTransport, + }, + } + + resourceTypesOptions := &armpolicy.ClientOptions{ + ClientOptions: policy.ClientOptions{ + Transport: resourceTypesTransport, + }, + } + + apiVersionsOptions := &armpolicy.ClientOptions{ + ClientOptions: policy.ClientOptions{ + Transport: apiVersionsTransport, + }, + } + + locationsOptions := &armpolicy.ClientOptions{ + ClientOptions: policy.ClientOptions{ + Transport: locationsTransport, + }, + } + + credential := &azfake.TokenCredential{} + + resourceProvidersClient, err := v20231001preview.NewResourceProvidersClient(credential, resourceProvidersOptions) + if err != nil { + return nil, fmt.Errorf("failed to create fake ResourceProvidersClient: %w", err) + } + + resourceTypesClient, err := v20231001preview.NewResourceTypesClient(credential, resourceTypesOptions) + if err != nil { + return nil, fmt.Errorf("failed to create fake ResourceTypesClient: %w", err) + } + + apiVersionsClient, err := v20231001preview.NewAPIVersionsClient(credential, apiVersionsOptions) + if err != nil { + return nil, fmt.Errorf("failed to create fake APIVersionsClient: %w", err) + } + + locationsClient, err := v20231001preview.NewLocationsClient(credential, locationsOptions) + if err != nil { + return nil, fmt.Errorf("failed to create fake LocationsClient: %w", err) + } + + // Return the FakeUCPClient instance + return &FakeUCPClient{ + ResourceProvidersClient: *resourceProvidersClient, + ResourceTypesClient: *resourceTypesClient, + APIVersionsClient: *apiVersionsClient, + LocationsClient: *locationsClient, + }, nil +}