From 9b876858b465222b126a59e44b9f37f54b5aaed9 Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Thu, 12 Dec 2024 09:41:01 -0800 Subject: [PATCH 01/37] Remove CosmosDB database provider (#8116) # Description This is part of a refactor of the data layer to simplify the design. The CosmosDB implementation is complicated and unused so we're removing it. ## Type of change - This pull request is a minor refactor, code cleanup, test improvement, or other maintenance task and doesn't change the functionality of Radius (issue link optional). ## Contributor checklist Please verify that the PR meets the following requirements, where applicable: - [ ] An overview of proposed schema changes is included in a linked GitHub issue. - [ ] A design document PR is created in the [design-notes repository](https://github.com/radius-project/design-notes/), if new APIs are being introduced. - [ ] If applicable, design document has been reviewed and approved by Radius maintainers/approvers. - [ ] A PR for the [samples repository](https://github.com/radius-project/samples) is created, if existing samples are affected by the changes in this PR. - [ ] A PR for the [documentation repository](https://github.com/radius-project/docs) is created, if the changes in this PR affect the documentation or any user facing updates are made. - [ ] A PR for the [recipes repository](https://github.com/radius-project/recipes) is created, if existing recipes are affected by the changes in this PR. Signed-off-by: Ryan Nowak Co-authored-by: Yetkin Timocin --- cmd/applications-rp/radius-cloud.yaml | 56 -- .../configSettings.md | 11 +- .../first-commit-05-running-tests/index.md | 1 - go.mod | 2 - go.sum | 7 - pkg/armrpc/hostoptions/hostoptions.go | 10 - pkg/corerp/README.md | 9 +- pkg/ucp/dataprovider/factory.go | 22 - pkg/ucp/dataprovider/types.go | 3 - .../store/cosmosdb/cosmosdbstorageclient.go | 460 ---------- .../cosmosdb/cosmosdbstorageclient_test.go | 786 ------------------ pkg/ucp/store/cosmosdb/options.go | 52 -- pkg/ucp/store/cosmosdb/util.go | 154 ---- pkg/ucp/store/cosmosdb/util_test.go | 199 ----- 14 files changed, 4 insertions(+), 1768 deletions(-) delete mode 100644 cmd/applications-rp/radius-cloud.yaml delete mode 100644 pkg/ucp/store/cosmosdb/cosmosdbstorageclient.go delete mode 100644 pkg/ucp/store/cosmosdb/cosmosdbstorageclient_test.go delete mode 100644 pkg/ucp/store/cosmosdb/options.go delete mode 100644 pkg/ucp/store/cosmosdb/util.go delete mode 100644 pkg/ucp/store/cosmosdb/util_test.go diff --git a/cmd/applications-rp/radius-cloud.yaml b/cmd/applications-rp/radius-cloud.yaml deleted file mode 100644 index acbcbe2401..0000000000 --- a/cmd/applications-rp/radius-cloud.yaml +++ /dev/null @@ -1,56 +0,0 @@ -# This is an example of configuration file. -environment: - name: AzureCloud - roleLocation: West US -identity: # 1P AAD APP authentication - clientId: "PLACEHOLDER" - instance: "https://login.windows.net" - tenantId: "common" - armEndpoint: "https://management.azure.com:443" - audience: "https://management.core.windows.net" - pemCertPath: "/var/certs/rp-aad-app.pem" -storageProvider: - provider: "cosmosdb" - cosmosdb: - # Create your own SQL API Cosmos DB account and set url in this configuration or to RADIUS_STORAGEPROVIDER_COSMOSDB_URL environment variable - url: https://radius-eastus-test.documents.azure.com:443/ - database: applicationscore - # Set primary key to in this configuration or to RADIUS_STORAGEPROVIDER_COSMOSDB_MASTERKEY environment variable - masterKey: set-me-in-a-different-way -queueProvider: - provider: inmemory - name: radius -profilerProvider: - enabled: true - port: 6060 -secretProvider: - provider: etcd - etcd: - inmemory: true -server: - host: "0.0.0.0" - port: 8080 - authType: "ClientCertificate" - enableArmAuth: true - armMetadataEndpoint: "https://admin.api-dogfood.resources.windows-int.net/metadata/authentication?api-version=2015-01-01" -workerServer: - maxOperationConcurrency: 10 - maxOperationRetryCount: 2 -metricsProvider: - prometheus: - enabled: true - path: "/metrics" - port: 9090 -featureFlags: - - "PLACEHOLDER" -ucp: - kind: kubernetes - # Logging configuration -logging: - level: "info" - json: false -bicep: - deleteRetryCount: 20 - deleteRetryDelaySeconds: 60 -terraform: - path: "/terraform" diff --git a/docs/contributing/contributing-code/contributing-code-control-plane/configSettings.md b/docs/contributing/contributing-code/contributing-code-control-plane/configSettings.md index c350891370..e54edf127a 100644 --- a/docs/contributing/contributing-code/contributing-code-control-plane/configSettings.md +++ b/docs/contributing/contributing-code/contributing-code-control-plane/configSettings.md @@ -65,8 +65,7 @@ The following are properties that can be specified for UCP: | Key | Description | Example | |-----|-------------|---------| | provider | The type of storage provider | `apiServer` | -| apiServer | Object containing properties for Kubernetes APIServer store | [**See below**](#apiserver) | -| cosmosdb | Object containing properties for CosmosDB | [**See below**](#cosmosdb) | +| apiServer | Object containing properties for Kubernetes APIServer store | [**See below**](#apiserver) | | etcd | Object containing properties for ETCD store | [**See below**](#etcd)| ### queueProvider @@ -159,14 +158,6 @@ ucp: |-----|-------------|---------| | inMemory | Configures the etcd store to run in-memory with the resource provider (must be `true`/`false`) | `true` | -### cosmosdb -| Key | Description | Example | -|-----|-------------|---------| -| url | URL of CosmosDB account | `https://radius-eastus-test.documents.azure.com:443/` | -| database | Name of the database in account | `applicationscore` | -| masterKey | All access key token for database resources | `your-master-key` | -| CollectionThroughput | Throughput of database | `400` | - ## Plane properties | Key | Description | Example | diff --git a/docs/contributing/contributing-code/contributing-code-first-commit/first-commit-05-running-tests/index.md b/docs/contributing/contributing-code/contributing-code-first-commit/first-commit-05-running-tests/index.md index 18f9532339..f55098ea20 100644 --- a/docs/contributing/contributing-code/contributing-code-first-commit/first-commit-05-running-tests/index.md +++ b/docs/contributing/contributing-code/contributing-code-first-commit/first-commit-05-running-tests/index.md @@ -24,7 +24,6 @@ ok github.com/radius-project/radius/pkg/cli 0.250s ? github.com/radius-project/radius/pkg/azure/radclient [no test files] ? github.com/radius-project/radius/pkg/renderers [no test files] ok github.com/radius-project/radius/pkg/renderers/containerv1alpha3 -ok github.com/radius-project/radius/pkg/renderers/cosmosdbmongov1alpha3 ok github.com/radius-project/radius/pkg/renderers/dapr ok github.com/radius-project/radius/pkg/renderers/daprpubsubv1alpha3 ok github.com/radius-project/radius/pkg/renderers/daprstatestorev1alpha3 diff --git a/go.mod b/go.mod index ea24574e14..73e3159556 100644 --- a/go.mod +++ b/go.mod @@ -61,13 +61,11 @@ require ( github.com/opencontainers/image-spec v1.1.0 github.com/projectcontour/contour v1.30.1 github.com/prometheus/client_golang v1.20.5 - github.com/spaolacci/murmur3 v1.1.0 github.com/spf13/cobra v1.8.1 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.19.0 github.com/stern/stern v1.31.0 github.com/stretchr/testify v1.10.0 - github.com/vippsas/go-cosmosdb v0.0.0-20230118095602-f4e4b9f1c352 github.com/wI2L/jsondiff v0.6.1 go.etcd.io/etcd/client/v3 v3.5.17 go.etcd.io/etcd/server/v3 v3.5.17 diff --git a/go.sum b/go.sum index 205064fcc5..5004b94645 100644 --- a/go.sum +++ b/go.sum @@ -265,7 +265,6 @@ github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7l github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/agnivade/levenshtein v1.2.0 h1:U9L4IOT0Y3i0TIlUIDJ7rVUziKi/zPbrJGaFrtYH3SY= github.com/agnivade/levenshtein v1.2.0/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU= -github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= @@ -549,7 +548,6 @@ github.com/goccy/go-yaml v1.15.7/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7Lk github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= -github.com/gofrs/uuid v3.1.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= @@ -759,7 +757,6 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs= @@ -947,8 +944,6 @@ github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= -github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= @@ -997,8 +992,6 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1 github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= -github.com/vippsas/go-cosmosdb v0.0.0-20230118095602-f4e4b9f1c352 h1:N4BwZihfs9zOPo1R+aSIyLmM2qRYoN2gEuleOHUhjBA= -github.com/vippsas/go-cosmosdb v0.0.0-20230118095602-f4e4b9f1c352/go.mod h1:MC5grluKkU7tz2VMDXi7AOj2N4spGFebM5w//g2WpyU= github.com/wI2L/jsondiff v0.6.1 h1:ISZb9oNWbP64LHnu4AUhsMF5W0FIj5Ok3Krip9Shqpw= github.com/wI2L/jsondiff v0.6.1/go.mod h1:KAEIojdQq66oJiHhDyQez2x+sRit0vIzC9KeK0yizxM= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= diff --git a/pkg/armrpc/hostoptions/hostoptions.go b/pkg/armrpc/hostoptions/hostoptions.go index 83aaea0922..f669c93f92 100644 --- a/pkg/armrpc/hostoptions/hostoptions.go +++ b/pkg/armrpc/hostoptions/hostoptions.go @@ -124,16 +124,6 @@ func loadConfig(configPath string) (*ProviderConfig, error) { return nil, fmt.Errorf("failed to load yaml: %w", err) } - cosmosdbUrl := os.Getenv("RADIUS_STORAGEPROVIDER_COSMOSDB_URL") - if cosmosdbUrl != "" { - conf.StorageProvider.CosmosDB.Url = cosmosdbUrl - } - - cosmosDBKey := os.Getenv("RADIUS_STORAGEPROVIDER_COSMOSDB_MASTERKEY") - if cosmosDBKey != "" { - conf.StorageProvider.CosmosDB.MasterKey = cosmosDBKey - } - return conf, nil } diff --git a/pkg/corerp/README.md b/pkg/corerp/README.md index cd0e516e77..76cbc8ad2b 100644 --- a/pkg/corerp/README.md +++ b/pkg/corerp/README.md @@ -24,10 +24,9 @@ ## How to Run and Test Core RP -1. Update StorageProvider section of `cmd/applications-rp/radius-dev.yaml` by adding your Cosmos DB URL and key 1. With `cmd/applications-rp/main.go` file open, go to `Run And Debug` view in VS Code and click `Run` -1. You should have the service up and running at `localhost:8080` now -1. To create or update an environment, here is an example curl command: +2. You should have the service up and running at `localhost:8080` now +3. To create or update an environment, here is an example curl command: ``` curl --location --request PUT 'http://localhost:8080/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/environments/env0?api-version=2023-10-01-preview' \ @@ -43,14 +42,12 @@ }' ``` -1. To get information about an environment, here is an example curl command: +4. To get information about an environment, here is an example curl command: ``` curl --location --request GET 'http://localhost:8080/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/environments/?api-version=2023-10-01-preview' ``` -1. You should also be able to see all changes in Cosmos DB - ## References * [ARM RPC v1.0 Specification](https://github.com/Azure/azure-resource-manager-rpc) diff --git a/pkg/ucp/dataprovider/factory.go b/pkg/ucp/dataprovider/factory.go index 14696633fc..1d0580336a 100644 --- a/pkg/ucp/dataprovider/factory.go +++ b/pkg/ucp/dataprovider/factory.go @@ -27,7 +27,6 @@ import ( store "github.com/radius-project/radius/pkg/ucp/store" "github.com/radius-project/radius/pkg/ucp/store/apiserverstore" ucpv1alpha1 "github.com/radius-project/radius/pkg/ucp/store/apiserverstore/api/ucp.dev/v1alpha1" - "github.com/radius-project/radius/pkg/ucp/store/cosmosdb" "github.com/radius-project/radius/pkg/ucp/store/etcdstore" "github.com/radius-project/radius/pkg/ucp/store/inmemory" "github.com/radius-project/radius/pkg/ucp/store/postgres" @@ -41,7 +40,6 @@ type storageFactoryFunc func(context.Context, StorageProviderOptions, string) (s var storageClientFactory = map[StorageProviderType]storageFactoryFunc{ TypeAPIServer: initAPIServerClient, - TypeCosmosDB: initCosmosDBClient, TypeETCD: InitETCDClient, TypeInMemory: initInMemoryClient, TypePostgreSQL: initPostgreSQLClient, @@ -83,26 +81,6 @@ func initAPIServerClient(ctx context.Context, opt StorageProviderOptions, _ stri return client, nil } -func initCosmosDBClient(ctx context.Context, opt StorageProviderOptions, collectionName string) (store.StorageClient, error) { - sopt := &cosmosdb.ConnectionOptions{ - Url: opt.CosmosDB.Url, - DatabaseName: opt.CosmosDB.Database, - CollectionName: collectionName, - MasterKey: opt.CosmosDB.MasterKey, - CollectionThroughput: opt.CosmosDB.CollectionThroughput, - } - dbclient, err := cosmosdb.NewCosmosDBStorageClient(sopt) - if err != nil { - return nil, fmt.Errorf("failed to create CosmosDB client - configuration may be invalid: %w", err) - } - - if err = dbclient.Init(ctx); err != nil { - return nil, fmt.Errorf("failed to initialize CosmosDB client - configuration may be invalid: %w", err) - } - - return dbclient, nil -} - // InitETCDClient checks if the ETCD client is in memory and if the client is not nil, then it initializes the storage // client and returns an ETCDClient. If either of these conditions are not met, an error is returned. func InitETCDClient(ctx context.Context, opt StorageProviderOptions, _ string) (store.StorageClient, error) { diff --git a/pkg/ucp/dataprovider/types.go b/pkg/ucp/dataprovider/types.go index 543ef8dfd8..b6f9e8ce9b 100644 --- a/pkg/ucp/dataprovider/types.go +++ b/pkg/ucp/dataprovider/types.go @@ -29,9 +29,6 @@ const ( // TypeAPIServer represents the Kubernetes APIServer provider. TypeAPIServer StorageProviderType = "apiserver" - // TypeCosmosDB represents CosmosDB provider. - TypeCosmosDB StorageProviderType = "cosmosdb" - // TypeETCD represents the etcd provider. TypeETCD StorageProviderType = "etcd" diff --git a/pkg/ucp/store/cosmosdb/cosmosdbstorageclient.go b/pkg/ucp/store/cosmosdb/cosmosdbstorageclient.go deleted file mode 100644 index 0291235fa0..0000000000 --- a/pkg/ucp/store/cosmosdb/cosmosdbstorageclient.go +++ /dev/null @@ -1,460 +0,0 @@ -/* -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 cosmosdb - -import ( - "context" - "fmt" - "strings" - - "github.com/radius-project/radius/pkg/ucp/resources" - resources_azure "github.com/radius-project/radius/pkg/ucp/resources/azure" - "github.com/radius-project/radius/pkg/ucp/store" - "github.com/vippsas/go-cosmosdb/cosmosapi" -) - -const ( - // PartitionKeyName is the property used for partitioning. - PartitionKeyName = "/partitionKey" - - // go-cosmosdb does not return the error response code. Comparing error message is the only way to check the errors. - // Once we move to official Go SDK, we can have the better error handling. - // TODO: Switch to the official cosmosdb SDK - https://github.com/radius-project/radius/issues/2225 - // 1. Repalce github.com/vippsas/go-cosmosdb/cosmosapi with the official sdk when it supports query api. - // 2. Improve error handling using response code instead of string match. - errResourceNotFoundMsg = "Resource that no longer exists" - errIDConflictMsg = "The ID provided has been taken by an existing resource" - errEtagPreconditionMsgPrefix = "The operation specified an eTag" -) - -var _ store.StorageClient = (*CosmosDBStorageClient)(nil) - -// ResourceEntity represents the default envelope model to store resource metadata. -type ResourceEntity struct { - // CosmosDB system-related properties. - // ID represents the primary key. - ID string `json:"id"` - // ETag represents an etag required for optimistic concurrency control. - ETag string `json:"_etag"` - // Self represents the unique addressable URI for the resource. - Self string `json:"_self"` - // Timestamp represents the last updated timestamp of the resource. - UpdatedTime int `json:"_ts"` - - // ResourceID represents fully qualified resource id. - ResourceID string `json:"resourceId"` - // RootScope represents root scope such as subscription id. - RootScope string `json:"rootScope"` - // ResourceGroup represents fully qualified resource scope. - ResourceGroup string `json:"resourceGroup"` - // PartitionKey represents the key used for partitioning. - PartitionKey string `json:"partitionKey"` - // Entity represents the resource metadata. - Entity any `json:"entity"` -} - -// CosmosDBStorageClient implements CosmosDB stroage client. -type CosmosDBStorageClient struct { - client *cosmosapi.Client - options *ConnectionOptions -} - -// NewCosmosDBStorageClient creates a new CosmosDBStorageClient instance using the provided ConnectionOptions and returns -// it, or an error if the ConnectionOptions are invalid. -func NewCosmosDBStorageClient(options *ConnectionOptions) (*CosmosDBStorageClient, error) { - if err := options.load(); err != nil { - return nil, err - } - - cfg := cosmosapi.Config{ - MasterKey: options.MasterKey, - MaxRetries: 5, - } - - client := cosmosapi.New(options.Url, cfg, nil, nil) - - return &CosmosDBStorageClient{ - client: client, - options: options, - }, nil -} - -// Init checks if the database and collection exist, and if not, creates them. It returns an error if -// either of the checks or creations fail. -func (c *CosmosDBStorageClient) Init(ctx context.Context) error { - if err := c.createDatabaseIfNotExists(ctx); err != nil { - return err - } - if err := c.createCollectionIfNotExists(ctx); err != nil { - return err - } - return nil -} - -func (c *CosmosDBStorageClient) createDatabaseIfNotExists(ctx context.Context) error { - _, err := c.client.GetDatabase(ctx, c.options.DatabaseName, nil) - if err == nil { - return nil - } - if !strings.EqualFold(err.Error(), errResourceNotFoundMsg) { - return err - } - - _, err = c.client.CreateDatabase(ctx, c.options.DatabaseName, nil) - if err != nil && strings.EqualFold(err.Error(), errIDConflictMsg) { - return nil - } - - return err -} - -func (c *CosmosDBStorageClient) createCollectionIfNotExists(ctx context.Context) error { - _, err := c.client.GetCollection(ctx, c.options.DatabaseName, c.options.CollectionName) - if err == nil { - return nil - } - if !strings.EqualFold(err.Error(), errResourceNotFoundMsg) { - return err - } - opt := cosmosapi.CreateCollectionOptions{ - Id: c.options.CollectionName, - IndexingPolicy: &cosmosapi.IndexingPolicy{ - IndexingMode: cosmosapi.IndexingMode("consistent"), - Automatic: true, - Included: []cosmosapi.IncludedPath{ - { - Path: "/*", - Indexes: []cosmosapi.Index{ - { - Kind: cosmosapi.Range, - DataType: cosmosapi.StringType, - Precision: -1, - }, - { - Kind: cosmosapi.Range, - DataType: cosmosapi.NumberType, - Precision: -1, - }, - }, - }, - }, - }, - PartitionKey: &cosmosapi.PartitionKey{ - Paths: []string{ - PartitionKeyName, - }, - Kind: "Hash", - }, - } - - // CollectionThroughput needs to be set only if radius uses Provioned throughput mode. - if c.options.CollectionThroughput > 0 { - opt.OfferThroughput = cosmosapi.OfferThroughput(c.options.CollectionThroughput) - } - - _, err = c.client.CreateCollection(context.Background(), c.options.DatabaseName, opt) - - if err != nil && strings.EqualFold(err.Error(), errIDConflictMsg) { - return nil - } - - return err -} - -func constructCosmosDBQuery(query store.Query) (*cosmosapi.Query, error) { - if query.RoutingScopePrefix != "" { - return nil, &store.ErrInvalid{Message: "RoutingScopePrefix is not supported."} - } - - if query.RootScope == "" { - return nil, &store.ErrInvalid{Message: "RootScope can not be empty."} - } - - queryString := "SELECT * FROM c WHERE " - whereParam := "" - queryParams := []cosmosapi.QueryParam{} - - if query.ScopeRecursive { - whereParam = whereParam + "STARTSWITH(c.rootScope, @rootScope, true)" - queryParams = append(queryParams, cosmosapi.QueryParam{ - Name: "@rootScope", - Value: strings.ToLower(query.RootScope), - }) - } else { - whereParam = whereParam + "c.rootScope = @rootScope" - queryParams = append(queryParams, cosmosapi.QueryParam{ - Name: "@rootScope", - Value: strings.ToLower(query.RootScope), - }) - } - - if query.ResourceType != "" { - if whereParam != "" { - whereParam += " and " - } - whereParam += "STRINGEQUALS(c.entity.type, @rtype, true)" - queryParams = append(queryParams, cosmosapi.QueryParam{ - Name: "@rtype", - Value: query.ResourceType, - }) - } - - for i, filter := range query.Filters { - if whereParam != "" { - whereParam += " and " - } - filterParam := fmt.Sprintf("filter%d", i) - whereParam += fmt.Sprintf("STRINGEQUALS(c.entity.%s, @%s, true)", filter.Field, filterParam) - queryParams = append(queryParams, cosmosapi.QueryParam{ - Name: "@" + filterParam, - Value: filter.Value, - }) - } - - if whereParam == "" { - return nil, &store.ErrInvalid{Message: "invalid Query parameters"} - } - - return &cosmosapi.Query{Query: queryString + whereParam, Params: queryParams}, nil -} - -// Query builds and executes a CosmosDB query based on the provided store.Query and returns the results. -func (c *CosmosDBStorageClient) Query(ctx context.Context, query store.Query, opts ...store.QueryOptions) (*store.ObjectQueryResult, error) { - if ctx == nil { - return nil, &store.ErrInvalid{Message: "invalid argument. 'ctx' is required"} - } - err := query.Validate() - if err != nil { - return nil, &store.ErrInvalid{Message: fmt.Sprintf("invalid argument. Query is invalid: %s", err.Error())} - } - - cfg := store.NewQueryConfig(opts...) - - resourceID, err := resources.ParseScope(query.RootScope) - if err != nil { - return nil, err - } - - qry, err := constructCosmosDBQuery(query) - if err != nil { - return nil, err - } - - entities := []ResourceEntity{} - - maxItemCount := c.options.DefaultQueryItemCount - if cfg.MaxQueryItemCount > 0 { - maxItemCount = cfg.MaxQueryItemCount - } - - qops := cosmosapi.QueryDocumentsOptions{ - IsQuery: true, - ContentType: cosmosapi.QUERY_CONTENT_TYPE, - MaxItemCount: maxItemCount, - EnableCrossPartition: true, - ConsistencyLevel: cosmosapi.ConsistencyLevelEventual, - } - - partitionKey, err := GetPartitionKey(resourceID) - if err != nil { - return nil, err - } - - if partitionKey != "" { - qops.PartitionKeyValue = partitionKey - qops.EnableCrossPartition = false - } - - if cfg.PaginationToken != "" { - qops.Continuation = cfg.PaginationToken - } - - resp, err := c.client.QueryDocuments(ctx, c.options.DatabaseName, c.options.CollectionName, *qry, &entities, qops) - if err != nil { - return nil, err - } - - output := []store.Object{} - for _, entity := range entities { - output = append(output, store.Object{ - Metadata: store.Metadata{ - ID: entity.ResourceID, - ETag: entity.ETag, - }, - Data: entity.Entity, - }) - } - - return &store.ObjectQueryResult{ - PaginationToken: resp.Continuation, - Items: output, - }, nil -} - -// Get retrieves an object using CosmosDBStorageClient using the provided ID and optional GetOptions. It returns an error -// if the object is not found or if an error occurs while retrieving the object. -func (c *CosmosDBStorageClient) Get(ctx context.Context, id string, opts ...store.GetOptions) (*store.Object, error) { - parsedID, err := resources.Parse(id) - if err != nil { - return nil, err - } - - partitionKey, err := GetPartitionKey(parsedID) - if err != nil { - return nil, err - } - - ops := cosmosapi.GetDocumentOptions{ - PartitionKeyValue: partitionKey, - } - - docID, err := GenerateCosmosDBKey(parsedID) - if err != nil { - return nil, err - } - - entity := &ResourceEntity{} - _, err = c.client.GetDocument(ctx, c.options.DatabaseName, c.options.CollectionName, docID, ops, entity) - - if err != nil && strings.EqualFold(err.Error(), errResourceNotFoundMsg) { - return nil, &store.ErrNotFound{ID: id} - } - - obj := &store.Object{ - Metadata: store.Metadata{ - ID: entity.ResourceID, - ETag: entity.ETag, - }, - Data: entity.Entity, - } - - return obj, err -} - -// Delete parses the given ID, gets the partition key, generates the CosmosDB key, and deletes the document from the -// CosmosDB collection. It returns an error if the document is not found. -func (c *CosmosDBStorageClient) Delete(ctx context.Context, id string, opts ...store.DeleteOptions) error { - parsedID, err := resources.Parse(id) - if err != nil { - return err - } - - partitionKey, err := GetPartitionKey(parsedID) - if err != nil { - return err - } - - ops := cosmosapi.DeleteDocumentOptions{ - PartitionKeyValue: partitionKey, - } - - docID, err := GenerateCosmosDBKey(parsedID) - if err != nil { - return err - } - - _, err = c.client.DeleteDocument(ctx, c.options.DatabaseName, c.options.CollectionName, docID, ops) - if err != nil && strings.EqualFold(err.Error(), errResourceNotFoundMsg) { - return &store.ErrNotFound{ID: id} - } - - return err -} - -// Save saves an object to the CosmosDB storage, returning an error if one occurs. If an ETag is provided, an error is -// returned if the ETag does not match the existing ETag. -func (c *CosmosDBStorageClient) Save(ctx context.Context, obj *store.Object, opts ...store.SaveOptions) error { - if ctx == nil { - return &store.ErrInvalid{Message: "invalid argument. 'ctx' is required"} - } - if obj == nil { - return &store.ErrInvalid{Message: "invalid argument. 'obj' is required"} - } - - cfg := store.NewSaveConfig(opts...) - - parsed, err := resources.Parse(obj.ID) - if err != nil { - return err - } - - docID, err := GenerateCosmosDBKey(parsed) - if err != nil { - return err - } - - partitionKey, err := GetPartitionKey(parsed) - if err != nil { - return err - } - - entity := &ResourceEntity{ - ID: docID, - ResourceID: strings.ToLower(parsed.String()), - RootScope: strings.ToLower(parsed.RootScope()), - PartitionKey: partitionKey, - Entity: obj.Data, - } - - ifMatch := cfg.ETag - if ifMatch == "" && obj.ETag != "" { - ifMatch = obj.ETag - } - - var resp *cosmosapi.Resource - if ifMatch == "" { - op := cosmosapi.CreateDocumentOptions{ - PartitionKeyValue: partitionKey, - IsUpsert: true, - } - resp, _, err = c.client.CreateDocument(ctx, c.options.DatabaseName, c.options.CollectionName, entity, op) - } else { - op := cosmosapi.ReplaceDocumentOptions{ - PartitionKeyValue: partitionKey, - IfMatch: ifMatch, - } - resp, _, err = c.client.ReplaceDocument(ctx, c.options.DatabaseName, c.options.CollectionName, entity.ID, entity, op) - - // TODO: use the response code when switching to official SDK. - if err != nil && strings.HasPrefix(err.Error(), errEtagPreconditionMsgPrefix) { - return &store.ErrConcurrency{} - } - } - - if err != nil { - return err - } - - obj.ETag = resp.Etag - - return nil -} - -// GetPartitionKey returns a partition key based on the given ID, normalizing the subscription ID and normalizing the -// plane namespace if the ID is UCP-qualified. -// Examples: -// /planes/radius/local/... - Partition Key: radius/local -// subscriptions/{guid}/... - Partition Key: {guid} -func GetPartitionKey(id resources.ID) (string, error) { - partitionKey := NormalizeSubscriptionID(id.FindScope(resources_azure.ScopeSubscriptions)) - - if id.IsUCPQualified() { - partitionKey = NormalizeLetterOrDigitToUpper(id.PlaneNamespace()) - } - - return partitionKey, nil -} diff --git a/pkg/ucp/store/cosmosdb/cosmosdbstorageclient_test.go b/pkg/ucp/store/cosmosdb/cosmosdbstorageclient_test.go deleted file mode 100644 index 08a4d5340d..0000000000 --- a/pkg/ucp/store/cosmosdb/cosmosdbstorageclient_test.go +++ /dev/null @@ -1,786 +0,0 @@ -/* -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 cosmosdb - -import ( - "context" - "fmt" - "os" - "strconv" - "strings" - "testing" - - "github.com/google/uuid" - v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" - "github.com/radius-project/radius/pkg/corerp/datamodel" - rpv1 "github.com/radius-project/radius/pkg/rp/v1" - "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/store" - "github.com/stretchr/testify/require" - "github.com/vippsas/go-cosmosdb/cosmosapi" -) - -var randomSubscriptionIDs = []string{ - "eaf9116d-84e7-4720-a841-67ca2b67f888", - "7826d962-510f-407a-92a2-5aeb37aa7b6e", - "b2c7913e-e1fe-4c1d-a843-212159d07e46", -} -var randomResourceGroups = []string{ - "red-group", - "blue-group", - "radius-lala", -} -var randomPlanes = []string{ - "local", - "k8s", - "azure", -} - -var ( - // To run this test, you need to specify the below environment variable before running the test. - dBUrl = os.Getenv("TEST_COSMOSDB_URL") - masterKey = os.Getenv("TEST_COSMOSDB_MASTERKEY") - - dbName = "applicationscore" - dbCollectionName = "functional-test-environments" - - testLocation = "test-location" - environmentResourceType = "applications.core/environments" -) - -func getTestEnvironmentModel(rootScope string, resourceName string) *datamodel.Environment { - testID := rootScope + "/providers/applications.core/environments/" + resourceName - - env := &datamodel.Environment{ - BaseResource: v1.BaseResource{ - TrackedResource: v1.TrackedResource{ - ID: testID, - Name: resourceName, - Type: environmentResourceType, - Location: testLocation, - }, - InternalMetadata: v1.InternalMetadata{}, - }, - Properties: datamodel.EnvironmentProperties{ - Compute: rpv1.EnvironmentCompute{ - Kind: rpv1.KubernetesComputeKind, - KubernetesCompute: rpv1.KubernetesComputeProperties{ - ResourceID: "/subscriptions/00000000-0000-0000-1000-000000000001/resourceGroups/testGroup/providers/Microsoft.ContainerService/managedClusters/radiusTestCluster", - Namespace: "default", - }, - }, - }, - } - - env.InternalMetadata.CreatedAPIVersion = "2023-10-01-preview" - env.InternalMetadata.UpdatedAPIVersion = "2023-10-01-preview" - - return env -} - -var dbClient *CosmosDBStorageClient - -func mustGetTestClient(t *testing.T) *CosmosDBStorageClient { - if dBUrl == "" || masterKey == "" { - t.Skip("TEST_COSMOSDB_URL and TEST_COSMOSDB_MASTERKEY are not set.") - } - - if dbClient != nil { - return dbClient - } - - var err error - dbClient, err = NewCosmosDBStorageClient(&ConnectionOptions{ - Url: dBUrl, - DatabaseName: dbName, - CollectionName: dbCollectionName, - MasterKey: masterKey, - }) - if err != nil { - panic(err) - } - - if err := dbClient.Init(context.Background()); err != nil { - panic(err) - } - - return dbClient -} - -func TestConstructCosmosDBQuery(t *testing.T) { - tests := []struct { - desc string - storeQuery store.Query - queryString string - params []cosmosapi.QueryParam - err error - }{ - { - desc: "invalid-query-parameters", - storeQuery: store.Query{}, - err: &store.ErrInvalid{Message: "RootScope can not be empty."}, - }, - { - desc: "scope-recursive-and-routing-scope-prefix", - storeQuery: store.Query{RootScope: "/subscriptions/00000000-0000-0000-1000-000000000001", RoutingScopePrefix: "prefix"}, - err: &store.ErrInvalid{Message: "RoutingScopePrefix is not supported."}, - }, - { - desc: "root-scope-subscription-id", - storeQuery: store.Query{RootScope: "/subscriptions/00000000-0000-0000-1000-000000000001", ScopeRecursive: true}, - queryString: "SELECT * FROM c WHERE STARTSWITH(c.rootScope, @rootScope, true)", - params: []cosmosapi.QueryParam{{ - Name: "@rootScope", - Value: "/subscriptions/00000000-0000-0000-1000-000000000001", - }}, - err: nil, - }, - { - desc: "root-scope-plane", - storeQuery: store.Query{RootScope: "/planes/radius/local", ScopeRecursive: true}, - queryString: "SELECT * FROM c WHERE STARTSWITH(c.rootScope, @rootScope, true)", - params: []cosmosapi.QueryParam{{ - Name: "@rootScope", - Value: "/planes/radius/local", - }}, - err: nil, - }, - { - desc: "root-scope-subscription-id-and-resource-group", - storeQuery: store.Query{RootScope: "/subscriptions/00000000-0000-0000-1000-000000000001/resourcegroups/testgroup", ScopeRecursive: false}, - queryString: "SELECT * FROM c WHERE c.rootScope = @rootScope", - params: []cosmosapi.QueryParam{{ - Name: "@rootScope", - Value: "/subscriptions/00000000-0000-0000-1000-000000000001/resourcegroups/testgroup", - }}, - err: nil, - }, - - { - desc: "root-scope-plane-and-resource-group", - storeQuery: store.Query{RootScope: "/planes/radius/local/resourcegroups/testgroup", ScopeRecursive: false}, - queryString: "SELECT * FROM c WHERE c.rootScope = @rootScope", - params: []cosmosapi.QueryParam{{ - Name: "@rootScope", - Value: "/planes/radius/local/resourcegroups/testgroup", - }}, - err: nil, - }, - { - storeQuery: store.Query{ - RootScope: "/subscriptions/00000000-0000-0000-1000-000000000001/resourcegroups/testgroup", - ResourceType: "applications.core/environments", - }, - queryString: "SELECT * FROM c WHERE c.rootScope = @rootScope and STRINGEQUALS(c.entity.type, @rtype, true)", - params: []cosmosapi.QueryParam{{ - Name: "@rootScope", - Value: "/subscriptions/00000000-0000-0000-1000-000000000001/resourcegroups/testgroup", - }, { - Name: "@rtype", - Value: "applications.core/environments", - }}, - err: nil, - }, - { - storeQuery: store.Query{ - RootScope: "/subscriptions/00000000-0000-0000-1000-000000000001/resourcegroups/testgroup", - ResourceType: "applications.core/environments", - Filters: []store.QueryFilter{ - { - Field: "properties.environment", - Value: "/subscriptions/00000000-0000-0000-1000-000000000001/resourcegroups/testgroup/providers/applications.core/environments/env0", - }, - { - Field: "properties.application", - Value: "/subscriptions/00000000-0000-0000-1000-000000000001/resourcegroups/testgroup/providers/applications.core/applications/app0", - }, - }, - }, - queryString: "SELECT * FROM c WHERE c.rootScope = @rootScope and STRINGEQUALS(c.entity.type, @rtype, true) and STRINGEQUALS(c.entity.properties.environment, @filter0, true) and STRINGEQUALS(c.entity.properties.application, @filter1, true)", - params: []cosmosapi.QueryParam{{ - Name: "@rootScope", - Value: "/subscriptions/00000000-0000-0000-1000-000000000001/resourcegroups/testgroup", - }, { - Name: "@rtype", - Value: "applications.core/environments", - }, { - Name: "@filter0", - Value: "/subscriptions/00000000-0000-0000-1000-000000000001/resourcegroups/testgroup/providers/applications.core/environments/env0", - }, { - Name: "@filter1", - Value: "/subscriptions/00000000-0000-0000-1000-000000000001/resourcegroups/testgroup/providers/applications.core/applications/app0", - }}, - err: nil, - }, - } - for _, tc := range tests { - t.Run(tc.desc, func(t *testing.T) { - qry, err := constructCosmosDBQuery(tc.storeQuery) - if tc.err != nil { - require.ErrorIs(t, tc.err, err) - } else { - require.Equal(t, tc.queryString, qry.Query) - require.ElementsMatch(t, tc.params, qry.Params) - } - }) - } -} - -func TestGetNotFound(t *testing.T) { - ctx := context.Background() - client := mustGetTestClient(t) - - resourceID := "/subscriptions/00000000-0000-0000-1000-000000000001/resourceGroups/testGroup/providers/applications.core/environments/notfound" - _, err := client.Get(ctx, resourceID) - require.ErrorIs(t, &store.ErrNotFound{ID: resourceID}, err) -} - -func TestDeleteNotFound(t *testing.T) { - ctx := context.Background() - client := mustGetTestClient(t) - - resourceID := "/subscriptions/00000000-0000-0000-1000-000000000001/resourceGroups/testGroup/providers/applications.core/environments/notfound" - err := client.Delete(ctx, resourceID) - require.ErrorIs(t, &store.ErrNotFound{ID: resourceID}, err) -} - -func TestSave(t *testing.T) { - ctx := context.Background() - client := mustGetTestClient(t) - - ucpRootScope := fmt.Sprintf("/planes/radius/local/resourcegroups/%s", randomResourceGroups[0]) - ucpResource := getTestEnvironmentModel(ucpRootScope, "test-UCP-resource") - - armResourceRootScope := fmt.Sprintf("/subscriptions/%s/resourcegroups/%s", randomSubscriptionIDs[0], randomResourceGroups[0]) - armResource := getTestEnvironmentModel(armResourceRootScope, "test-Resource") - - setupTest := func(tb testing.TB, resource *datamodel.Environment) (func(tb testing.TB), *store.Object) { - // Prepare DB object - obj := &store.Object{ - Metadata: store.Metadata{ - ID: resource.ID, - }, - Data: resource, - } - - // Save the object - err := client.Save(ctx, obj) - require.NoError(tb, err) - require.NotEmpty(tb, obj.ETag) - - // Return teardown func and the object - return func(tb testing.TB) { - // Delete object if it exists - err = client.Delete(ctx, resource.ID) - require.NoError(tb, err) - }, obj - } - - // useObjEtag lets you use the existing object etag - tests := map[string]struct { - resource *datamodel.Environment - useObjEtag bool - etag string - useOpts bool - err error - }{ - "upsert-ucp-resource-without-etag": { - resource: ucpResource, - useObjEtag: false, - etag: "", - useOpts: false, - err: nil, - }, - "upsert-arm-resource-without-etag": { - resource: armResource, - useObjEtag: false, - etag: "", - useOpts: false, - err: nil, - }, - "upsert-ucp-resource-with-valid-etag": { - resource: ucpResource, - useObjEtag: true, - etag: "", - useOpts: false, - err: nil, - }, - "upsert-arm-resource-with-valid-etag": { - resource: armResource, - useObjEtag: true, - etag: "", - useOpts: false, - err: nil, - }, - "upsert-ucp-resource-with-options": { - resource: ucpResource, - useObjEtag: false, - etag: "", - useOpts: true, - err: nil, - }, - "upsert-arm-resource-with-options": { - resource: armResource, - useObjEtag: false, - etag: "", - useOpts: true, - err: nil, - }, - "upsert-ucp-resource-with-invalid-etag": { - resource: ucpResource, - useObjEtag: false, - etag: "invalid-etag", - useOpts: false, - err: &store.ErrConcurrency{}, - }, - "upsert-arm-resource-with-invalid-etag": { - resource: armResource, - useObjEtag: false, - etag: "invalid-etag", - useOpts: false, - err: &store.ErrConcurrency{}, - }, - } - for name, tc := range tests { - t.Run(name, func(t *testing.T) { - teardownTest, obj := setupTest(t, tc.resource) - defer teardownTest(t) - - // Update the etag - if !tc.useObjEtag { - obj.ETag = tc.etag - } - - // Upsert the object - var err error - if tc.useOpts { - err = client.Save(ctx, obj, store.WithETag(obj.ETag)) - } else { - err = client.Save(ctx, obj) - } - - // Error checking - if tc.err != nil { - require.ErrorIs(t, err, tc.err) - } else { - require.NoError(t, err) - } - }) - } -} - -// TestQuery tests the following scenarios: -// - Query records by subscription -// - Query records by plane -// - Query records by subscription and resource group -// - Query records by plane and resource group -// - Query records by subscription and resource type -// - Query records by subscription, resource group, and resource type -// - Query records by subscription, resource group, and custom filter -// - Query records by resource type and custom filter (across subscription) -// - Use case - this will be used when environment queries all linked applications and links. -func TestQuery(t *testing.T) { - ctx := context.Background() - client := mustGetTestClient(t) - - ucpResources := []string{} - armResources := []string{} - - // TODO: UCP doesn't check for the plane type - // Ex: /planes/radius/azure/resourcegroups/rg/.../environments/env - // is equal to /planes/radius/local/resourcegroups/rg/.../environments/env - - setupTest := func() func(tb testing.TB) { - // Reset arrays each time - ucpResources = []string{} - armResources = []string{} - - // Creates ucp resources under 3 different planes and 3 different resource groups. - // Total makes 9 ucp resources - for _, plane := range randomPlanes { - for _, resourceGroup := range randomResourceGroups { - // Create and Save a UCP Resource - ucpRootScope := fmt.Sprintf("/planes/radius/%s/resourcegroups/%s", plane, resourceGroup) - ucpEnv := buildAndSaveTestModel(ctx, t, ucpRootScope, uuid.New().String()) - ucpResources = append(ucpResources, ucpEnv.ID) - } - } - - // Creates ARM resources under 3 different subscriptions and 3 different resource groups. - // Total makes 9 ARM resources - for _, subscriptionID := range randomSubscriptionIDs { - for idx, resourceGroup := range randomResourceGroups { - // Create and Save an ARM Resource - armResourceRootScope := fmt.Sprintf("/subscriptions/%s/resourcegroups/%s", subscriptionID, resourceGroup) - armEnv := buildAndSaveTestModel(ctx, t, armResourceRootScope, fmt.Sprintf("test-env-%d", idx)) - armResources = append(armResources, armEnv.ID) - } - } - - // Return teardown - return func(tb testing.TB) { - // Delete all UCP resources after each test - for i := 0; i < len(ucpResources); i++ { - ucpResourceID := ucpResources[i] - err := client.Delete(ctx, ucpResourceID) - require.NoError(tb, err) - } - - // Delete all ARM resources after each test - for i := 0; i < len(armResources); i++ { - armResourceID := armResources[i] - err := client.Delete(ctx, armResourceID) - require.NoError(tb, err) - } - } - } - - queryTest := func(resourceID string, resourceType string, filters []store.QueryFilter, itemsLen int) { - parsedID, err := resources.Parse(resourceID) - require.NoError(t, err) - - // Build the query for testing - query := store.Query{ - RootScope: parsedID.RootScope(), - } - if resourceType != "" { - query.ResourceType = resourceType - } - if len(filters) > 0 { - query.Filters = filters - } - - results, err := client.Query(ctx, query) - require.NoError(t, err) - require.NotNil(t, results) - require.NotNil(t, results.Items) - require.Equal(t, itemsLen, len(results.Items)) - } - - // Query with subscriptionID + resourceGroup - tests := map[string]struct { - resourceType string - filters []store.QueryFilter - expected int - }{ - "just-root-scope": { - resourceType: "", - filters: []store.QueryFilter{}, - expected: 1, - }, - "root-scope-with-resource-type": { - resourceType: environmentResourceType, - filters: []store.QueryFilter{}, - expected: 1, - }, - "root-scope-resource-type-location-filter": { - resourceType: environmentResourceType, - filters: []store.QueryFilter{ - { - Field: "location", - Value: testLocation, - }, - }, - expected: 1, - }, - "root-scope-resource-type-wrong-location-filter": { - resourceType: environmentResourceType, - filters: []store.QueryFilter{ - { - Field: "location", - Value: "wrong-location", - }, - }, - expected: 0, - }, - } - for name, tc := range tests { - t.Run(name, func(t *testing.T) { - teardownTest := setupTest() - defer teardownTest(t) - - // Query each ucp resource - for i := 0; i < len(ucpResources); i++ { - ucpResource := ucpResources[i] - queryTest(ucpResource, tc.resourceType, tc.filters, tc.expected) - } - - // Query each ARM resource - for i := 0; i < len(armResources); i++ { - armResource := armResources[i] - queryTest(armResource, tc.resourceType, tc.filters, tc.expected) - } - }) - } - - // Query with subscriptionID or plane - These are recursive queries - subscriptionIDCases := map[string]struct { - rootScope string - resourceType string - filters []store.QueryFilter - expected int - }{ - "arm-resource-subscription-id": { - rootScope: fmt.Sprintf("/subscriptions/%s", randomSubscriptionIDs[0]), - resourceType: "", - filters: []store.QueryFilter{}, - expected: 3, - }, - "ucp-resource-subscription-id": { - rootScope: "/planes/radius/local", - resourceType: "", - filters: []store.QueryFilter{}, - expected: 3, - }, - "arm-resource-subscription-id-with-resource-type": { - rootScope: fmt.Sprintf("/subscriptions/%s", randomSubscriptionIDs[0]), - resourceType: environmentResourceType, - filters: []store.QueryFilter{}, - expected: 3, - }, - "ucp-resource-subscription-id-with-resource-type": { - rootScope: "/planes/radius/local", - resourceType: environmentResourceType, - filters: []store.QueryFilter{}, - expected: 3, - }, - "arm-resource-subscription-id-with-resource-type-with-filter": { - rootScope: fmt.Sprintf("/subscriptions/%s", randomSubscriptionIDs[0]), - resourceType: environmentResourceType, - filters: []store.QueryFilter{ - { - Field: "location", - Value: testLocation, - }, - }, - expected: 3, - }, - "ucp-resource-subscription-id-with-resource-type-with-filter": { - rootScope: "/planes/radius/local", - resourceType: environmentResourceType, - filters: []store.QueryFilter{ - { - Field: "location", - Value: testLocation, - }, - }, - expected: 3, - }, - "arm-resource-subscription-id-with-resource-type-with-invalid-filter": { - rootScope: fmt.Sprintf("/subscriptions/%s", randomSubscriptionIDs[0]), - resourceType: environmentResourceType, - filters: []store.QueryFilter{ - { - Field: "location", - Value: "wrong-location", - }, - }, - expected: 0, - }, - "ucp-resource-subscription-id-with-resource-type-with-invalid-filter": { - rootScope: "/planes/radius/local", - resourceType: environmentResourceType, - filters: []store.QueryFilter{ - { - Field: "location", - Value: "wrong-location", - }, - }, - expected: 0, - }, - } - for name, tc := range subscriptionIDCases { - t.Run(name, func(t *testing.T) { - teardownTest := setupTest() - defer teardownTest(t) - - // Build the query for testing - query := store.Query{ - RootScope: tc.rootScope, - ScopeRecursive: true, - } - if tc.resourceType != "" { - query.ResourceType = tc.resourceType - } - if len(tc.filters) > 0 { - query.Filters = tc.filters - } - - results, err := client.Query(ctx, query) - require.NoError(t, err) - require.NotNil(t, results) - require.NotNil(t, results.Items) - require.Equal(t, tc.expected, len(results.Items)) - - for _, item := range results.Items { - if !strings.HasPrefix(item.Metadata.ID, tc.rootScope) { - require.Failf(t, "Matched an item that doesn't include the rootscope %s", item.ID) - } - } - }) - } -} - -// TestPaginationTokenAndQueryItemCount tests the pagination scenario using continuation token and query item count. -func TestPaginationTokenAndQueryItemCount(t *testing.T) { - ctx := context.Background() - client := mustGetTestClient(t) - - ucpResources := []string{} - armResources := []string{} - - ucpRootScope := "/planes/radius/local/resourcegroups/test-RG" - armResourceRootScope := "/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/test-RG" - - setupTest := func() func(tb testing.TB) { - // 50 UCP - 50 ARM - for i := 0; i < 50; i++ { - ucpEnv := buildAndSaveTestModel(ctx, t, ucpRootScope, fmt.Sprintf("ucp-env-%d", i)) - ucpResources = append(ucpResources, ucpEnv.ID) - - armEnv := buildAndSaveTestModel(ctx, t, armResourceRootScope, fmt.Sprintf("test-ENV-%d", i)) - armResources = append(armResources, armEnv.ID) - } - - // Return teardown - return func(tb testing.TB) { - for i := 0; i < 50; i++ { - ucpResourceID := ucpResources[i] - err := client.Delete(ctx, ucpResourceID) - require.NoError(tb, err) - - armResourceID := armResources[i] - err = client.Delete(ctx, armResourceID) - require.NoError(tb, err) - } - } - } - - tests := map[string]struct { - rootScope string - itemCount string - }{ - "ucp-resource-default-query-item-count": { - rootScope: ucpRootScope, - itemCount: "", - }, - "arm-resource-default-query-item-count": { - rootScope: armResourceRootScope, - itemCount: "", - }, - "ucp-resource-10-query-item-count": { - rootScope: strings.ToLower(ucpRootScope), // case-insensitive query - itemCount: "10", - }, - "arm-resource-10-query-item-count": { - rootScope: armResourceRootScope, - itemCount: "10", - }, - } - for name, tc := range tests { - t.Run(name, func(t *testing.T) { - teardownTest := setupTest() - defer teardownTest(t) - - remaining := 50 - queryItemCount := defaultQueryItemCount - paginationToken := "" - - for remaining > 0 { - // Build query options - queryOptions := []store.QueryOptions{} - if tc.itemCount != "" { - ic, err := strconv.Atoi(tc.itemCount) - require.NoError(t, err) - queryOptions = append(queryOptions, store.WithMaxQueryItemCount(ic)) - queryItemCount = ic - } - if paginationToken != "" { - queryOptions = append(queryOptions, store.WithPaginationToken(paginationToken)) - } - - if remaining < queryItemCount { - queryItemCount = remaining - } - - results, err := client.Query(ctx, store.Query{RootScope: tc.rootScope}, queryOptions...) - require.NoError(t, err) - require.Equal(t, queryItemCount, len(results.Items)) - - remaining -= queryItemCount - - if remaining > 0 { - require.NotEmpty(t, results.PaginationToken) - paginationToken = results.PaginationToken - } else { - require.Empty(t, results.PaginationToken) - } - } - }) - } -} - -func TestGetPartitionKey(t *testing.T) { - cases := []struct { - desc string - fullID string - out string - }{ - { - "env-partition-key", - "/subscriptions/00000000-0000-0000-1000-000000000001/resourcegroups/testGroup/providers/applications.core/environments/env0", - "00000000000000001000000000000001", - }, - { - "env-no-subscription-partition-key", - "/resourcegroups/testGroup/providers/applications.core/environments/env0", - "", - }, - { - "ucp-resource-partition-key-radius-local", - "/planes/radius/local/resourcegroups/testGroup/providers/applications.core/environments/env0", - "RADIUSLOCAL", - }, - { - "ucp-resource-partition-key-radius-k8s", - "/planes/radius/k8s/resourcegroups/testGroup/providers/applications.core/environments/env0", - "RADIUSK8S", - }, - } - for _, tc := range cases { - t.Run(tc.desc, func(t *testing.T) { - testID, err := resources.Parse(tc.fullID) - require.NoError(t, err) - key, err := GetPartitionKey(testID) - require.NoError(t, err) - require.Equal(t, tc.out, key) - }) - } -} - -func buildAndSaveTestModel(ctx context.Context, t *testing.T, rootScope string, resourceName string) *datamodel.Environment { - model := getTestEnvironmentModel(rootScope, resourceName) - obj := &store.Object{ - Metadata: store.Metadata{ - ID: model.ID, - }, - Data: model, - } - err := dbClient.Save(ctx, obj) - require.NoError(t, err) - return model -} diff --git a/pkg/ucp/store/cosmosdb/options.go b/pkg/ucp/store/cosmosdb/options.go deleted file mode 100644 index a0dc279aca..0000000000 --- a/pkg/ucp/store/cosmosdb/options.go +++ /dev/null @@ -1,52 +0,0 @@ -/* -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 cosmosdb - -import "github.com/radius-project/radius/pkg/ucp/store" - -const ( - defaultQueryItemCount = 20 -) - -// ConnectionOptions represents connection info to connect CosmosDB -type ConnectionOptions struct { - // Url represents the url of cosmosdb endpoint. - Url string - // DatabaseName represents the database name to connect. - DatabaseName string - // CollectionName represents the collection name in DataBaseName - CollectionName string - // DefaultQueryItemCount represents the maximum number of items for query. - DefaultQueryItemCount int - // CollectionThroughput represents shared throughput database share the throughput (RU/s) allocated to that database. - CollectionThroughput int - - // MasterKey is the key string for CosmosDB connection. - MasterKey string -} - -func (c *ConnectionOptions) load() error { - if c.MasterKey == "" { - return &store.ErrInvalid{Message: "unset MasterKey"} - } - - if c.DefaultQueryItemCount == 0 { - c.DefaultQueryItemCount = defaultQueryItemCount - } - - return nil -} diff --git a/pkg/ucp/store/cosmosdb/util.go b/pkg/ucp/store/cosmosdb/util.go deleted file mode 100644 index 4af73d0ea3..0000000000 --- a/pkg/ucp/store/cosmosdb/util.go +++ /dev/null @@ -1,154 +0,0 @@ -/* -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 cosmosdb - -import ( - "errors" - "fmt" - "strings" - "unicode" - - "github.com/radius-project/radius/pkg/ucp/resources" - resources_azure "github.com/radius-project/radius/pkg/ucp/resources/azure" - "github.com/radius-project/radius/pkg/ucp/store" - "github.com/spaolacci/murmur3" -) - -const ( - keyDelimiter = "-" - - // StorageKeyTrimPaddingLen is the length of the padding when key is trimed. - StorageKeyTrimPaddingLen = 17 - // The resource group name storage key length. - ResourceGroupNameMaxStorageKeyLen = 64 - // The resource identifier storage key limit. - ResourceIdMaxStorageKeyLen = 157 -) - -var ( - ErrInvalidKey = errors.New("key includes invalid character") -) - -var escapedStorageKeys = []string{ - ":00", ":01", ":02", ":03", ":04", ":05", ":06", ":07", ":08", ":09", ":0A", ":0B", ":0C", ":0D", ":0E", ":0F", - ":10", ":11", ":12", ":13", ":14", ":15", ":16", ":17", ":18", ":19", ":1A", ":1B", ":1C", ":1D", ":1E", ":1F", - ":20", ":21", ":22", ":23", ":24", ":25", ":26", ":27", ":28", ":29", ":2A", ":2B", ":2C", ":2D", ":2E", ":2F", - "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":3A", ":3B", ":3C", ":3D", ":3E", ":3F", - ":40", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", - "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", ":5B", ":5C", ":5D", ":5E", ":5F", - ":60", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", - "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", ":7B", ":7C", ":7D", ":7E", ":7F", -} - -// NormalizeLetterOrDigitToUpper takes in a string and returns a new string with all letters and digits converted to uppercase. -func NormalizeLetterOrDigitToUpper(s string) string { - if s == "" { - return s - } - - sb := strings.Builder{} - for _, ch := range s { - if unicode.IsDigit(ch) || unicode.IsLetter(ch) { - sb.WriteRune(ch) - } - } - - return strings.ToUpper(sb.String()) -} - -// NormalizeSubscriptionID normalizes subscription id. -func NormalizeSubscriptionID(subscriptionID string) string { - return NormalizeLetterOrDigitToUpper(subscriptionID) -} - -// EscapedStorageKey escapes a string so that it can be used as a storage key. -func EscapedStorageKey(key string) string { - sb := strings.Builder{} - for _, ch := range key { - if ch < 128 { - sb.WriteString(escapedStorageKeys[ch]) - } else if unicode.IsDigit(ch) || unicode.IsLetter(ch) { - sb.WriteRune(ch) - } else if ch < 0x100 { - sb.WriteRune(':') - sb.WriteString(fmt.Sprintf("%02d", ch)) - } else { - sb.WriteRune(':') - sb.WriteRune(':') - sb.WriteString(fmt.Sprintf("%04d", ch)) - } - } - return sb.String() -} - -// CombineStorageKeys combines multiple storage keys into one, returning an error if any of the keys contain the key delimiter. -func CombineStorageKeys(keys ...string) (string, error) { - for _, key := range keys { - if strings.Contains(key, keyDelimiter) { - return "", ErrInvalidKey - } - } - - return strings.Join(keys, keyDelimiter), nil -} - -// TrimStorageKey checks if the storage key is too short, contains invalid characters, or exceeds the maximum length, and -// returns a trimmed version of the key or an error if any of these conditions are met. -func TrimStorageKey(storageKey string, maxLength int) (string, error) { - if maxLength < StorageKeyTrimPaddingLen { - return "", &store.ErrInvalid{Message: "storage key is too short"} - } - if strings.Contains(storageKey, "|") { - return "", &store.ErrInvalid{Message: "storage key is not properly encoded"} - } - if len(storageKey) > maxLength { - // Use murmur hash to generate unique key if the length of key exceeds maxLenth - storageKey = fmt.Sprintf("%s|%16X", storageKey[:(maxLength-StorageKeyTrimPaddingLen)], murmur3.Sum64([]byte(storageKey))) - } - return storageKey, nil -} - -// NormalizeStorageKey takes a storage key string and a maximum length and returns a normalized string with -// the maximum length, or an error if the maximum length is exceeded. -func NormalizeStorageKey(storageKey string, maxLength int) (string, error) { - upper := strings.ToUpper(storageKey) - return TrimStorageKey(EscapedStorageKey(upper), maxLength) -} - -// GenerateCosmosDBKey takes in an ID object and returns a string and an error if the resource group or resource type and -// name fail to normalize. -func GenerateCosmosDBKey(id resources.ID) (string, error) { - storageKeys := []string{NormalizeSubscriptionID(id.FindScope(resources_azure.ScopeSubscriptions))} - - resourceGroup := id.FindScope(resources_azure.ScopeResourceGroups) - - if resourceGroup != "" { - uniqueResourceGroup, err := NormalizeStorageKey(resourceGroup, ResourceGroupNameMaxStorageKeyLen) - if err != nil { - return "", err - } - storageKeys = append(storageKeys, uniqueResourceGroup) - } - - resourceTypeAndName, err := NormalizeStorageKey(id.RoutingScope(), ResourceIdMaxStorageKeyLen) - if err != nil { - return "", err - } - storageKeys = append(storageKeys, resourceTypeAndName) - - return CombineStorageKeys(storageKeys...) -} diff --git a/pkg/ucp/store/cosmosdb/util_test.go b/pkg/ucp/store/cosmosdb/util_test.go deleted file mode 100644 index 2a70fa46eb..0000000000 --- a/pkg/ucp/store/cosmosdb/util_test.go +++ /dev/null @@ -1,199 +0,0 @@ -/* -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 cosmosdb - -import ( - "testing" - - "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/store" - "github.com/stretchr/testify/require" -) - -func TestNormalizeLetterOrDigitToUpper(t *testing.T) { - testStrings := []struct { - in string - out string - }{ - {"00000000-0000-0000-1000-000000000001", "00000000000000001000000000000001"}, - {"test-GROUp", "TESTGROUP"}, - {"WEST US", "WESTUS"}, - } - - for _, tc := range testStrings { - t.Run(tc.in, func(t *testing.T) { - result := NormalizeLetterOrDigitToUpper(tc.in) - require.Equal(t, tc.out, result) - }) - } -} - -func TestSubscriptionID(t *testing.T) { - testStrings := []struct { - in string - out string - }{ - {"00000000-0000-0000-1000-000000000001", "00000000000000001000000000000001"}, - {"eaf9116d-84e7-4720-a841-67ca2b67f888", "EAF9116D84E74720A84167CA2B67F888"}, - {"b2c7913e-e1fe-4c1d-a843-212159d07e46", "B2C7913EE1FE4C1DA843212159D07E46"}, - } - - for _, tc := range testStrings { - t.Run(tc.in, func(t *testing.T) { - result := NormalizeSubscriptionID(tc.in) - require.Equal(t, tc.out, result) - }) - } -} - -func TestEscapedStorageKey(t *testing.T) { - escapedTests := []struct { - in string - out string - }{ - {"testgroup", "testgroup"}, - {"test-group", "test:2Dgroup"}, - {"/subscriptions/sub/resourceGroups/rgname", ":2Fsubscriptions:2Fsub:2FresourceGroups:2Frgname"}, - } - - for _, tc := range escapedTests { - t.Run(tc.in, func(t *testing.T) { - escaped := EscapedStorageKey(tc.in) - require.Equal(t, tc.out, escaped) - }) - } -} - -func TestTrimStorageKey(t *testing.T) { - trimTests := []struct { - in string - len int - out string - err error - }{ - {"subscripti", 10, "", &store.ErrInvalid{Message: "storage key is too short"}}, - {"subscriptions|0000000000000000|testGroup", StorageKeyTrimPaddingLen, "", &store.ErrInvalid{Message: "storage key is not properly encoded"}}, - {"subscriptions/0000000000000000/testGroup", StorageKeyTrimPaddingLen, "|DCE4A54F0A69CD0F", nil}, - {"subscriptions/00000000000000001000000000000001/resourceGroups/testGroup", 20, "sub|DB99FE979E7C972C", nil}, - {"subscriptions/00000000000000001000000000000001/resourceGroups/testGroup", 80, "subscriptions/00000000000000001000000000000001/resourceGroups/testGroup", nil}, - } - - for _, tc := range trimTests { - t.Run(tc.in, func(t *testing.T) { - trimed, err := TrimStorageKey(tc.in, tc.len) - require.ErrorIs(t, err, tc.err) - require.Equal(t, tc.out, trimed) - }) - } -} - -func TestNormalizeStorageKey(t *testing.T) { - trimTests := []struct { - in string - len int - out string - err error - }{ - {"subscripti", 10, "", &store.ErrInvalid{Message: "storage key is too short"}}, - {"subscriptions/0000000000000000/testGroup", StorageKeyTrimPaddingLen, "|7A4B44E13072BE17", nil}, - {"subscriptions/00000000000000001000000000000001/resourceGroups/testGroup", 20, "SUB|10844510550A50BD", nil}, - {"subscriptions/00000000000000001000000000000001/resourceGroups/testGroup", 80, "SUBSCRIPTIONS:2F00000000000000001000000000000001:2FRESOURCEGROUPS:2FTESTGROUP", nil}, - } - - for _, tc := range trimTests { - t.Run(tc.in, func(t *testing.T) { - trimed, err := NormalizeStorageKey(tc.in, tc.len) - require.ErrorIs(t, err, tc.err) - require.Equal(t, tc.out, trimed) - }) - } -} - -func TestGenerateCosmosDBKey(t *testing.T) { - cases := []struct { - desc string - fullID string - out string - err error - }{ - { - "env-success-1", - "/subscriptions/00000000-0000-0000-1000-000000000001/resourcegroups/testGroup/providers/applications.core/environments/env0", - "00000000000000001000000000000001-TESTGROUP-APPLICATIONS:2ECORE:2FENVIRONMENTS:2FENV0", - nil, - }, - { - "env-success-2", - "/subscriptions/eaf9116d-84e7-4720-a841-67ca2b67f888/resourcegroups/testGroup/providers/Applications.Core/environments/appenv", - "EAF9116D84E74720A84167CA2B67F888-TESTGROUP-APPLICATIONS:2ECORE:2FENVIRONMENTS:2FAPPENV", - nil, - }, - { - "env-no-rg-success", - "/subscriptions/00000000-0000-0000-1000-000000000001/providers/Applications.Core/environments/env0", - "00000000000000001000000000000001-APPLICATIONS:2ECORE:2FENVIRONMENTS:2FENV0", - nil, - }, - { - "os-success", - "/subscriptions/00000000-0000-0000-1000-000000000001/providers/Applications.Core/locations/westus/operationStatuses/os1", - "00000000000000001000000000000001-APPLICATIONS:2ECORE:2FLOCATIONS:2FWESTUS:2FOPERATIONSTATUSES:2FOS1", - nil, - }, - { - "app-success", - "/subscriptions/7826d962-510f-407a-92a2-5aeb37aa7b6e/resourcegroups/radius-westus/providers/Applications.Core/applications/todoapp", - "7826D962510F407A92A25AEB37AA7B6E-RADIUS:2DWESTUS-APPLICATIONS:2ECORE:2FAPPLICATIONS:2FTODOAPP", - nil, - }, - { - "app-long-name-success", - "/subscriptions/7826d962-510f-407a-92a2-5aeb37aa7b6e/resourcegroups/radius-westus/providers/Applications.Core/applications/longapplicationname1longapplicationname1longapplicationname1longapplicationname1longapplicationname1longapplicationname1longapplicationname1longapplicationname1longapplicationname1longapplicationname1longapplicationname1longapplicationname1longapplicationname1longapplicationname1", - "7826D962510F407A92A25AEB37AA7B6E-RADIUS:2DWESTUS-APPLICATIONS:2ECORE:2FAPPLICATIONS:2FLONGAPPLICATIONNAME1LONGAPPLICATIONNAME1LONGAPPLICATIONNAME1LONGAPPLICATIONNAME1LONGAPPLICATIONNAME1LON|651E511DBBDDC783", - nil, - }, - { - "app-long-resource-name-success", - "/subscriptions/7826d962-510f-407a-92a2-5aeb37aa7b6e/resourcegroups/radius-westus/providers/Applications.Core/longresourcename0longresourcename0longresourcename0longresourcename0longresourcename0longresourcename0longresourcename0longresourcename0/longapplicationname1longapplicationname1longapplicationname1longapplicationname1longapplicationname1longapplicationname1longapplicationname1longapplicationname1longapplicationname1longapplicationname1longapplicationname1longapplicationname1longapplicationname1longapplicationname1", - "7826D962510F407A92A25AEB37AA7B6E-RADIUS:2DWESTUS-APPLICATIONS:2ECORE:2FLONGRESOURCENAME0LONGRESOURCENAME0LONGRESOURCENAME0LONGRESOURCENAME0LONGRESOURCENAME0LONGRESOURCENAME0LONGRESOURCENAME|279366913EF52FC7", - nil, - }, - { - "app-long-rg-app-names-success", - "/subscriptions/7826d962-510f-407a-92a2-5aeb37aa7b6e/resourcegroups/longresourcegroup0longresourcegroup0longresourcegroup0longresourcegroup0longresourcegroup0longresourcegroup0longresourcegroup0longresourcegroup0longresourcegroup0longresourcegroup0longresourcegroup0longresourcegroup0/providers/Applications.Core/longresourcename0longresourcename0longresourcename0longresourcename0longresourcename0longresourcename0longresourcename0longresourcename0/longapplicationname1longapplicationname1longapplicationname1longapplicationname1longapplicationname1longapplicationname1longapplicationname1longapplicationname1longapplicationname1longapplicationname1longapplicationname1longapplicationname1longapplicationname1longapplicationname1", - "7826D962510F407A92A25AEB37AA7B6E-LONGRESOURCEGROUP0LONGRESOURCEGROUP0LONGRESOURC|EF662FD5E8286859-APPLICATIONS:2ECORE:2FLONGRESOURCENAME0LONGRESOURCENAME0LONGRESOURCENAME0LONGRESOURCENAME0LONGRESOURCENAME0LONGRESOURCENAME0LONGRESOURCENAME|279366913EF52FC7", - nil, - }, - { - "ucp-success", - "/planes/radius/local/resourcegroups/radius-westus/providers/Applications.Core/applications/todoapp", - "-RADIUS:2DWESTUS-APPLICATIONS:2ECORE:2FAPPLICATIONS:2FTODOAPP", - nil, - }, - } - - for _, tc := range cases { - t.Run(tc.desc, func(t *testing.T) { - testID, err := resources.Parse(tc.fullID) - require.NoError(t, err) - key, err := GenerateCosmosDBKey(testID) - require.ErrorIs(t, err, tc.err) - require.Equal(t, tc.out, key) - require.LessOrEqual(t, len(key), 255) - }) - } -} From dcf5ad6071bf9d93c7110a645e7f2684d16ed587 Mon Sep 17 00:00:00 2001 From: Nithya Subramanian <98416062+nithyatsu@users.noreply.github.com> Date: Thu, 12 Dec 2024 10:13:45 -0800 Subject: [PATCH 02/37] enable irsa on cicd (#8052) # Description This PR enabled IRSA for functional tests and removes the static credentials. ## Type of change - This pull request is a minor refactor, code cleanup, test improvement, or other maintenance task and doesn't change the functionality of Radius (issue link optional). 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. --- .github/workflows/functional-test-cloud.yaml | 28 +++++++++++++------- test/validation/aws.go | 21 +++------------ 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/.github/workflows/functional-test-cloud.yaml b/.github/workflows/functional-test-cloud.yaml index 7017dc7c11..2042fa807b 100644 --- a/.github/workflows/functional-test-cloud.yaml +++ b/.github/workflows/functional-test-cloud.yaml @@ -549,7 +549,17 @@ jobs: - uses: azure/setup-helm@v4 with: version: ${{ env.HELM_VER }} + + # The role-to-assume is the role that the github action will assume to execute aws commands and + # construct cloud control client in test code. + - name: configure aws credentials using assumed role + uses: aws-actions/configure-aws-credentials@v1.7.0 + with: + role-to-assume: ${{ secrets.AWS_GH_ACTIONS_ROLE }} + role-session-name: GitHub_to_AWS_via_FederatedOIDC + aws-region: ${{ env.AWS_REGION }} + # create kind cluster with OIDC provider. - name: Create KinD cluster run: | curl -sSLo "kind" "https://github.com/kubernetes-sigs/kind/releases/download/${{ env.KIND_VER }}/kind-linux-amd64" @@ -632,7 +642,6 @@ jobs: append: true message: | :x: Test tool installation for ${{ matrix.name }} failed. Please check [the logs](${{ env.ACTION_LINK }}) for more details - - name: Install Radius run: | export PATH=$GITHUB_WORKSPACE/bin:$PATH @@ -646,8 +655,9 @@ jobs: --set controller.image=${{ env.CONTAINER_REGISTRY }}/controller,controller.tag=${{ env.REL_VERSION }} \ --set ucp.image=${{ env.CONTAINER_REGISTRY }}/ucpd,ucp.tag=${{ env.REL_VERSION }} \ --set de.image=${{ env.DE_IMAGE }},de.tag=${{ env.DE_TAG }} \ - --set global.azureWorkloadIdentity.enabled=true - + --set global.azureWorkloadIdentity.enabled=true \ + --set global.aws.irsa.enabled=true + echo "*** Create workspace, group and environment for test ***" rad workspace create kubernetes rad group create kind-radius @@ -666,9 +676,10 @@ jobs: echo "*** Configuring AWS provider ***" rad env update kind-radius --aws-region ${{ env.AWS_REGION }} --aws-account-id ${{ secrets.FUNCTEST_AWS_ACCOUNT_ID }} - rad credential register aws access-key \ - --access-key-id ${{ secrets.FUNCTEST_AWS_ACCESS_KEY_ID }} --secret-access-key ${{ secrets.FUNCTEST_AWS_SECRET_ACCESS_KEY }} - + + rad credential register aws irsa \ + --iam-role ${{ secrets.FUNC_TEST_RAD_IRSA_ROLE }} + - uses: marocchino/sticky-pull-request-comment@v2 if: failure() && env.PR_NUMBER != '' continue-on-error: true @@ -726,9 +737,8 @@ jobs: DOCKER_REGISTRY: ${{ env.CONTAINER_REGISTRY }} TEST_TIMEOUT: ${{ env.FUNCTIONALTEST_TIMEOUT }} RADIUS_CONTAINER_LOG_PATH: ${{ github.workspace }}/${{ env.RADIUS_CONTAINER_LOG_BASE }} - AWS_ACCESS_KEY_ID: ${{ secrets.FUNCTEST_AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.FUNCTEST_AWS_SECRET_ACCESS_KEY }} AWS_REGION: ${{ env.AWS_REGION }} + AWS_ACCOUNT_ID: ${{ secrets.FUNCTEST_AWS_ACCOUNT_ID }} RADIUS_SAMPLES_REPO_ROOT: ${{ github.workspace }}/samples # Test_MongoDB_Recipe_Parameters is using the following environment variable. INTEGRATION_TEST_RESOURCE_GROUP_NAME: ${{ env.AZURE_TEST_RESOURCE_GROUP }} @@ -919,4 +929,4 @@ jobs: title: `Scheduled functional test failed - Run ID: ${context.runId}`, labels: ['bug', 'test-failure'], body: `## Bug information \n\nThis bug is generated automatically if the scheduled functional test fails at least ${process.env.ISSUE_CREATE_THRESHOLD} times in a row. The Radius functional test operates on a schedule of every 4 hours during weekdays and every 12 hours over the weekend. It's important to understand that the test may fail due to workflow infrastructure issues, like network problems, rather than the flakiness of the test itself. For the further investigation, please visit [here](${process.env.ACTION_LINK}).` - }) + }) \ No newline at end of file diff --git a/test/validation/aws.go b/test/validation/aws.go index 32bc3e71f0..805ace9e4b 100644 --- a/test/validation/aws.go +++ b/test/validation/aws.go @@ -25,9 +25,7 @@ import ( "github.com/radius-project/radius/pkg/to" - "github.com/aws/aws-sdk-go-v2/credentials" "github.com/aws/aws-sdk-go-v2/service/cloudcontrol" - "github.com/aws/aws-sdk-go-v2/service/sts" awsclient "github.com/radius-project/radius/pkg/ucp/aws" "github.com/radius-project/radius/pkg/ucp/resources" resources_aws "github.com/radius-project/radius/pkg/ucp/resources/aws" @@ -158,23 +156,12 @@ func IsAWSResourceNotFound(ctx context.Context, resource *AWSResource, client aw // GetResourceIdentifier retrieves the identifier of a resource from the environment variables and the context. func GetResourceIdentifier(ctx context.Context, resourceType string, name string) (string, error) { - accessKey := os.Getenv("AWS_ACCESS_KEY_ID") - secretAccessKey := os.Getenv("AWS_SECRET_ACCESS_KEY") - sessionToken := "" + accountID := os.Getenv("AWS_ACCOUNT_ID") region := os.Getenv("AWS_REGION") - - credentialsProvider := credentials.NewStaticCredentialsProvider(accessKey, secretAccessKey, sessionToken) - - stsClient := sts.New(sts.Options{ - Region: region, - Credentials: credentialsProvider, - }) - result, err := stsClient.GetCallerIdentity(ctx, &sts.GetCallerIdentityInput{}) - if err != nil { - return "", err + if region == "" || accountID == "" { + return "", fmt.Errorf("AWS_REGION or AWS_ACCOUNT_ID is not set") } - - return "/planes/aws/aws/accounts/" + *result.Account + "/regions/" + region + "/providers/" + resourceType + "/" + name, nil + return "/planes/aws/aws/accounts/" + accountID + "/regions/" + region + "/providers/" + resourceType + "/" + name, nil } // GetResourceTypeName retrieves the AWS resource type name from the resource identifier and context. It returns an From 13df9da0c403e14633e7f014debf68607e57499d Mon Sep 17 00:00:00 2001 From: Karishma Chawla Date: Thu, 12 Dec 2024 10:49:03 -0800 Subject: [PATCH 03/37] Update Notification Condition for Scheduled Test Runs (#8115) # Description Updating scheduled test run notification logic. With this update every scheduled functional/long running test run failure will log a new issue. ## Type of change - This pull request fixes a bug in Radius and has an approved issue (issue link required). Fixes: https://github.com/radius-project/radius/issues/7998 ## Contributor checklist Please verify that the PR meets the following requirements, where applicable: - [ ] An overview of proposed schema changes is included in a linked GitHub issue. -- N/A - [ ] A design document PR is created in the [design-notes repository](https://github.com/radius-project/design-notes/), if new APIs are being introduced. -- N/A - [ ] If applicable, design document has been reviewed and approved by Radius maintainers/approvers. -- N/A - [ ] 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. -- N/A - [ ] 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. -- N/A - [ ] 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. -- N/A Signed-off-by: karishma-chawla <74574173+karishma-chawla@users.noreply.github.com> Co-authored-by: karishma-chawla <74574173+karishma-chawla@users.noreply.github.com> --- .github/workflows/functional-test-cloud.yaml | 31 ++----------------- .../workflows/functional-test-noncloud.yaml | 30 +----------------- .github/workflows/long-running-azure.yaml | 29 +---------------- 3 files changed, 4 insertions(+), 86 deletions(-) diff --git a/.github/workflows/functional-test-cloud.yaml b/.github/workflows/functional-test-cloud.yaml index 2042fa807b..bc58f8b2aa 100644 --- a/.github/workflows/functional-test-cloud.yaml +++ b/.github/workflows/functional-test-cloud.yaml @@ -85,8 +85,6 @@ env: FUNCTIONAL_TEST_APP_ID: 425843 # Private Git repository where terraform module for testing is stored. TF_RECIPE_PRIVATE_GIT_SOURCE: "git::https://github.com/radius-project/terraform-private-modules//kubernetes-redis" - # The number of failed tests to report. - ISSUE_CREATE_THRESHOLD: 2 # bicep-types ACR url for uploading Radius Bicep types BICEP_TYPES_REGISTRY: 'biceptypes.azurecr.io' @@ -894,33 +892,8 @@ jobs: runs-on: ubuntu-latest if: failure() && github.event_name == 'schedule' && github.repository == 'radius-project/radius' steps: - - name: Count recently failed tests - id: count_failures - uses: actions/github-script@v7 - with: - script: | - response = await github.rest.actions.listWorkflowRuns({ - owner: context.repo.owner, - repo: context.repo.repo, - workflow_id: 'functional-test-cloud.yaml', - event: 'schedule', - per_page: 10 - }); - - failureCount = 1; - for (const run of response.data.workflow_runs) { - if (run.conclusion === 'failure') { - failureCount++; - } else { - break; - } - } - return failureCount; - - name: Create failure issue for failing scheduled run uses: actions/github-script@v7 - # Only create an issue if there are (env.ISSUE_CREATE_THRESHOLD) failures of the recent tests. - if: steps.count_failures.outputs.result >= env.ISSUE_CREATE_THRESHOLD with: github-token: ${{ secrets.GH_RAD_CI_BOT_PAT }} script: | @@ -928,5 +901,5 @@ jobs: ...context.repo, title: `Scheduled functional test failed - Run ID: ${context.runId}`, labels: ['bug', 'test-failure'], - body: `## Bug information \n\nThis bug is generated automatically if the scheduled functional test fails at least ${process.env.ISSUE_CREATE_THRESHOLD} times in a row. The Radius functional test operates on a schedule of every 4 hours during weekdays and every 12 hours over the weekend. It's important to understand that the test may fail due to workflow infrastructure issues, like network problems, rather than the flakiness of the test itself. For the further investigation, please visit [here](${process.env.ACTION_LINK}).` - }) \ No newline at end of file + body: `## Bug information \n\nThis issue is automatically generated if the scheduled functional test fails. The Radius functional test operates on a schedule of every 4 hours during weekdays and every 12 hours over the weekend. It's important to understand that the test may fail due to workflow infrastructure issues, like network problems, rather than the flakiness of the test itself. For the further investigation, please visit [here](${process.env.ACTION_LINK}).` + }) diff --git a/.github/workflows/functional-test-noncloud.yaml b/.github/workflows/functional-test-noncloud.yaml index 7560b7aa38..870dde5d84 100644 --- a/.github/workflows/functional-test-noncloud.yaml +++ b/.github/workflows/functional-test-noncloud.yaml @@ -66,10 +66,6 @@ env: TF_RECIPE_MODULE_SERVER_URL: "http://tf-module-server.radius-test-tf-module-server.svc.cluster.local" # Private Git repository where terraform module for testing is stored. TF_RECIPE_PRIVATE_GIT_SOURCE: "git::https://github.com/radius-project/terraform-private-modules//kubernetes-redis" - - # The number of failed tests to report. - ISSUE_CREATE_THRESHOLD: 2 - # Local Docker registry name LOCAL_REGISTRY_NAME: "radius-registry" # Local Docker registry server @@ -453,32 +449,8 @@ jobs: runs-on: ubuntu-latest if: failure() && github.event_name == 'schedule' && github.repository == 'radius-project/radius' steps: - - name: Count recently failed tests - id: count_failures - uses: actions/github-script@v7 - with: - script: | - response = await github.rest.actions.listWorkflowRuns({ - owner: context.repo.owner, - repo: context.repo.repo, - workflow_id: 'functional-test-noncloud.yaml', - event: 'schedule', - per_page: 10 - }); - - failureCount = 1; - for (const run of response.data.workflow_runs) { - if (run.conclusion === 'failure') { - failureCount++; - } else { - break; - } - } - return failureCount; - name: Create failure issue for failing scheduled run uses: actions/github-script@v7 - # Only create an issue if there are (env.ISSUE_CREATE_THRESHOLD) failures of the recent tests. - if: steps.count_failures.outputs.result >= env.ISSUE_CREATE_THRESHOLD with: github-token: ${{ secrets.GH_RAD_CI_BOT_PAT }} script: | @@ -486,5 +458,5 @@ jobs: ...context.repo, title: `Scheduled functional test (noncloud) failed - Run ID: ${context.runId}`, labels: ['bug', 'test-failure'], - body: `## Bug information \n\nThis bug is generated automatically if the scheduled functional test fails at least ${process.env.ISSUE_CREATE_THRESHOLD} times in a row. The Radius functional test operates on a schedule of every 4 hours during weekdays and every 12 hours over the weekend. It's important to understand that the test may fail due to workflow infrastructure issues, like network problems, rather than the flakiness of the test itself. For the further investigation, please visit [here](${process.env.ACTION_LINK}).` + body: `## Bug information \n\nThis issue is automatically generated if the scheduled functional test fails. The Radius functional test operates on a schedule of every 4 hours during weekdays and every 12 hours over the weekend. It's important to understand that the test may fail due to workflow infrastructure issues, like network problems, rather than the flakiness of the test itself. For the further investigation, please visit [here](${process.env.ACTION_LINK}).` }) diff --git a/.github/workflows/long-running-azure.yaml b/.github/workflows/long-running-azure.yaml index ce30023e82..94f2e38cbe 100644 --- a/.github/workflows/long-running-azure.yaml +++ b/.github/workflows/long-running-azure.yaml @@ -95,9 +95,6 @@ env: # The current GitHub action link ACTION_LINK: "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" - # The number of failed tests to report. - ISSUE_CREATE_THRESHOLD: 2 - jobs: build: name: Build Radius for test @@ -522,32 +519,8 @@ jobs: runs-on: ubuntu-latest if: failure() && github.repository == 'radius-project/radius' && github.event_name == 'schedule' steps: - - name: Count recently failed tests - id: count_failures - uses: actions/github-script@v7 - with: - script: | - response = await github.rest.actions.listWorkflowRuns({ - owner: context.repo.owner, - repo: context.repo.repo, - workflow_id: 'long-running-azure.yaml', - event: 'schedule', - per_page: 10 - }); - - failureCount = 1; - for (const run of response.data.workflow_runs) { - if (run.conclusion === 'failure') { - failureCount++; - } else { - break; - } - } - return failureCount; - name: Create failure issue for failing long running test run uses: actions/github-script@v7 - # Only create an issue if there are (env.ISSUE_CREATE_THRESHOLD) failures of the recent tests. - if: steps.count_failures.outputs.result >= env.ISSUE_CREATE_THRESHOLD with: github-token: ${{ secrets.GH_RAD_CI_BOT_PAT }} script: | @@ -555,5 +528,5 @@ jobs: ...context.repo, title: `Scheduled long running test failed - Run ID: ${context.runId}`, labels: ['bug', 'test-failure'], - body: `## Bug information \n\nThis bug is generated automatically if the scheduled long running test fails at least ${process.env.ISSUE_CREATE_THRESHOLD} times in a row. The Radius long running test operates on a schedule of every 2 hours everyday. It's important to understand that the test may fail due to workflow infrastructure issues, like network problems, rather than the flakiness of the test itself. For the further investigation, please visit [here](${process.env.ACTION_LINK}).` + body: `## Bug information \n\nThis issue is automatically generated if the scheduled long running test fails. The Radius long running test operates on a schedule of every 2 hours everyday. It's important to understand that the test may fail due to workflow infrastructure issues, like network problems, rather than the flakiness of the test itself. For the further investigation, please visit [here](${process.env.ACTION_LINK}).` }) From ae4056055909227c9f5128d9cd11ed2bcd99ca62 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 12 Dec 2024 16:45:05 -0800 Subject: [PATCH 04/37] Bump golang.org/x/crypto from 0.28.0 to 0.31.0 in the go_modules group (#8124) Bumps the go_modules group with 1 update: [golang.org/x/crypto](https://github.com/golang/crypto). Updates `golang.org/x/crypto` from 0.28.0 to 0.31.0
Commits
  • b4f1988 ssh: make the public key cache a 1-entry FIFO cache
  • 7042ebc openpgp/clearsign: just use rand.Reader in tests
  • 3e90321 go.mod: update golang.org/x dependencies
  • 8c4e668 x509roots/fallback: update bundle
  • 6018723 go.mod: update golang.org/x dependencies
  • 71ed71b README: don't recommend go get
  • 750a45f sha3: add MarshalBinary, AppendBinary, and UnmarshalBinary
  • 36b1725 sha3: avoid trailing permutation
  • 80ea76e sha3: fix padding for long cSHAKE parameters
  • c17aa50 sha3: avoid buffer copy
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=golang.org/x/crypto&package-manager=go_modules&previous-version=0.28.0&new-version=0.31.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/radius-project/radius/network/alerts).
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Yetkin Timocin --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 73e3159556..be2cafaee6 100644 --- a/go.mod +++ b/go.mod @@ -311,13 +311,13 @@ require ( go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.starlark.net v0.0.0-20240925182052-1207426daebd // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.28.0 // indirect + golang.org/x/crypto v0.31.0 // indirect golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 golang.org/x/mod v0.21.0 // indirect golang.org/x/net v0.30.0 // indirect golang.org/x/oauth2 v0.23.0 // indirect - golang.org/x/sys v0.27.0 // indirect - golang.org/x/term v0.25.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/term v0.27.0 // indirect golang.org/x/time v0.7.0 // indirect google.golang.org/genproto v0.0.0-20240624140628-dc46fd24d27d // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240624140628-dc46fd24d27d // indirect diff --git a/go.sum b/go.sum index 5004b94645..ec77548b7f 100644 --- a/go.sum +++ b/go.sum @@ -1103,8 +1103,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= -golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1316,13 +1316,13 @@ golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= -golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= From 338369290443cbd398cb9e77bbd24603faec2a08 Mon Sep 17 00:00:00 2001 From: Yetkin Timocin Date: Fri, 13 Dec 2024 10:11:29 -0800 Subject: [PATCH 05/37] Changing permissions of UCP - removing unused permissions (#8080) # Description Changing permissions of UCP - removing unused permissions. ## Type of change - This pull request is a minor refactor, code cleanup, test improvement, or other maintenance task and doesn't change the functionality of Radius (issue link optional). Fixes: #8082 ## Contributor checklist Please verify that the PR meets the following requirements, where applicable: - [ ] An overview of proposed schema changes is included in a linked GitHub issue. - [ ] A design document PR is created in the [design-notes repository](https://github.com/radius-project/design-notes/), if new APIs are being introduced. - [ ] If applicable, design document has been reviewed and approved by Radius maintainers/approvers. - [ ] A PR for the [samples repository](https://github.com/radius-project/samples) is created, if existing samples are affected by the changes in this PR. - [ ] A PR for the [documentation repository](https://github.com/radius-project/docs) is created, if the changes in this PR affect the documentation or any user facing updates are made. - [ ] A PR for the [recipes repository](https://github.com/radius-project/recipes) is created, if existing recipes are affected by the changes in this PR. --------- Signed-off-by: ytimocin --- deploy/Chart/templates/ucp/rbac.yaml | 84 ++++++++++++---------------- 1 file changed, 35 insertions(+), 49 deletions(-) diff --git a/deploy/Chart/templates/ucp/rbac.yaml b/deploy/Chart/templates/ucp/rbac.yaml index e41c1d6fe8..d1425f4c45 100644 --- a/deploy/Chart/templates/ucp/rbac.yaml +++ b/deploy/Chart/templates/ucp/rbac.yaml @@ -6,52 +6,38 @@ metadata: app.kubernetes.io/name: ucp app.kubernetes.io/part-of: radius rules: -- apiGroups: - - "" - resources: - - configmaps - - secrets - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - resources: - - deployments - - statefulsets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - ucp.dev - resources: - - resources - - queuemessages - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - api.ucp.dev - resources: - - '*' - verbs: - - '*' + - apiGroups: + - "" + resources: + - secrets + verbs: + - create + - delete + - get + - update + + - apiGroups: + - ucp.dev + resources: + - resources + - queuemessages + verbs: + - create + - delete + - get + - list + - update + + - apiGroups: + - api.ucp.dev + resources: + - "*" + verbs: + - create + - delete + - get + - list + - update --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -65,6 +51,6 @@ roleRef: kind: ClusterRole name: ucp subjects: -- kind: ServiceAccount - name: ucp - namespace: {{ .Release.Namespace }} \ No newline at end of file + - kind: ServiceAccount + name: ucp + namespace: {{ .Release.Namespace }} From 186800bd63a35336f1201bbc06b0d23edf7203a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Dec 2024 14:11:56 -0800 Subject: [PATCH 06/37] Bump bicep-types from `c86fc24` to `7c34fe6` (#8134) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [bicep-types](https://github.com/Azure/bicep-types) from `c86fc24` to `7c34fe6`.
Commits
  • 7c34fe6 Bump Nerdbank.GitVersioning from 3.6.146 to 3.7.112 (#568)
  • 3fec89c Bump @​types/node from 22.10.1 to 22.10.2 in /src/bicep-types (#567)
  • 7ffa7d5 Bump nerdbank-gitversioning from 3.6.146 to 3.7.112 in /src/bicep-types (#565)
  • See full diff in compare view

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- bicep-types | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bicep-types b/bicep-types index c86fc24d81..7c34fe65c7 160000 --- a/bicep-types +++ b/bicep-types @@ -1 +1 @@ -Subproject commit c86fc24d81c562edba3035eb453772b4c2275c9a +Subproject commit 7c34fe65c70469beda773d603805da0a6224ccc3 From 4f2f16ed170469b9603cbe00b8b04b17753a97d7 Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Mon, 16 Dec 2024 15:53:01 -0800 Subject: [PATCH 07/37] Simplify database interface (#8126) # Description This change simplifes our database provider interface by removing the 'name' parameter. Prior to this change, each database client was used to store and retrieve a single resource type's data. Every piece of code had to be aware of the resource type associated with the client, and make sure to get a different client for each resource type it interacted with. This was complicated further by the fact that not all of our database providers implemented this restriction. --- After removing this restriction, our code can be simplified all over the place. A lot of code that used to need the `dataprovider.DataStoreProvider` now just needs the `store.StorageClient`. This change pushes the simplification as far as possible. I also added validation to places where we initialize controllers, because it was too easy to initialize a controller with missing data. ## Type of change - This pull request is a minor refactor, code cleanup, test improvement, or other maintenance task and doesn't change the functionality of Radius (issue link optional). ## Contributor checklist Please verify that the PR meets the following requirements, where applicable: - [ ] An overview of proposed schema changes is included in a linked GitHub issue. - [ ] A design document PR is created in the [design-notes repository](https://github.com/radius-project/design-notes/), if new APIs are being introduced. - [ ] If applicable, design document has been reviewed and approved by Radius maintainers/approvers. - [ ] A PR for the [samples repository](https://github.com/radius-project/samples) is created, if existing samples are affected by the changes in this PR. - [ ] A PR for the [documentation repository](https://github.com/radius-project/docs) is created, if the changes in this PR affect the documentation or any user facing updates are made. - [ ] A PR for the [recipes repository](https://github.com/radius-project/recipes) is created, if existing recipes are affected by the changes in this PR. Signed-off-by: Ryan Nowak --- .../asyncoperation/controller/controller.go | 26 ++-- .../statusmanager/statusmanager.go | 42 ++--- .../statusmanager/statusmanager_test.go | 14 +- pkg/armrpc/asyncoperation/worker/registry.go | 38 ++--- .../asyncoperation/worker/registry_test.go | 39 ++--- pkg/armrpc/asyncoperation/worker/service.go | 15 +- pkg/armrpc/asyncoperation/worker/worker.go | 2 +- .../worker/worker_runoperation_test.go | 45 ++---- pkg/armrpc/builder/builder.go | 2 +- pkg/armrpc/builder/builder_test.go | 54 +++---- pkg/armrpc/frontend/controller/controller.go | 32 ++-- .../frontend/controller/controller_test.go | 74 +++++++++ pkg/armrpc/frontend/controller/operation.go | 6 - .../defaultoperation/getoperationresult.go | 9 +- .../getoperationresult_test.go | 29 +--- pkg/armrpc/frontend/server/handler.go | 18 +-- pkg/armrpc/frontend/server/handler_test.go | 32 ++-- pkg/armrpc/frontend/server/service.go | 12 +- pkg/armrpc/rpctest/controllers.go | 4 +- .../backend/deployment/deploymentprocessor.go | 20 +-- .../deployment/deploymentprocessor_test.go | 141 +++++++---------- .../controller/applications/updatefilter.go | 2 +- .../applications/updatefilter_test.go | 6 - pkg/corerp/setup/setup_test.go | 23 ++- pkg/daprrp/setup/setup_test.go | 23 ++- pkg/datastoresrp/setup/setup_test.go | 23 ++- pkg/dynamicrp/options.go | 11 +- pkg/messagingrp/setup/setup_test.go | 23 ++- pkg/rp/kube/resources.go | 11 +- pkg/rp/kube/resources_test.go | 7 +- pkg/rp/util/datastore.go | 9 +- pkg/server/apiservice.go | 8 +- pkg/server/asyncworker.go | 11 +- pkg/ucp/backend/service.go | 29 ++-- pkg/ucp/dataprovider/factory.go | 10 +- .../dataprovider/mock_datastorage_provider.go | 80 ---------- pkg/ucp/dataprovider/storageprovider.go | 146 ++++++++++++------ pkg/ucp/dataprovider/storageprovider_test.go | 129 ++++++++++++++++ pkg/ucp/dataprovider/types.go | 14 -- pkg/ucp/frontend/api/routes.go | 16 +- pkg/ucp/frontend/api/routes_test.go | 23 ++- pkg/ucp/frontend/api/server.go | 13 +- pkg/ucp/frontend/aws/routes.go | 25 ++- pkg/ucp/frontend/aws/routes_test.go | 12 +- pkg/ucp/frontend/azure/routes.go | 17 +- pkg/ucp/frontend/azure/routes_test.go | 12 +- pkg/ucp/frontend/modules/types.go | 2 +- pkg/ucp/frontend/radius/routes.go | 7 +- pkg/ucp/frontend/radius/routes_test.go | 9 +- pkg/ucp/integrationtests/radius/proxy_test.go | 2 +- pkg/ucp/integrationtests/testrp/async.go | 15 +- pkg/ucp/integrationtests/testrp/sync.go | 24 ++- .../integrationtests/testserver/testserver.go | 21 ++- pkg/ucp/secret/provider/factory.go | 2 +- 54 files changed, 775 insertions(+), 644 deletions(-) delete mode 100644 pkg/ucp/dataprovider/mock_datastorage_provider.go create mode 100644 pkg/ucp/dataprovider/storageprovider_test.go diff --git a/pkg/armrpc/asyncoperation/controller/controller.go b/pkg/armrpc/asyncoperation/controller/controller.go index 3081b69511..1a1ce535b9 100644 --- a/pkg/armrpc/asyncoperation/controller/controller.go +++ b/pkg/armrpc/asyncoperation/controller/controller.go @@ -18,9 +18,9 @@ package controller import ( "context" + "errors" "github.com/radius-project/radius/pkg/corerp/backend/deployment" - "github.com/radius-project/radius/pkg/ucp/dataprovider" "github.com/radius-project/radius/pkg/ucp/store" runtimeclient "sigs.k8s.io/controller-runtime/pkg/client" @@ -31,9 +31,6 @@ type Options struct { // StorageClient is the data storage client. StorageClient store.StorageClient - // DataProvider is the data storage provider. - DataProvider dataprovider.DataStorageProvider - // KubeClient is the Kubernetes controller runtime client. KubeClient runtimeclient.Client @@ -44,6 +41,22 @@ type Options struct { GetDeploymentProcessor func() deployment.DeploymentProcessor } +// Validate validates that required fields are set on the options. +func (o Options) Validate() error { + var err error + if o.StorageClient == nil { + err = errors.Join(err, errors.New("StorageClient is required")) + } + if o.ResourceType == "" { + err = errors.Join(err, errors.New("ResourceType is required")) + } + + // KubeClient and GetDeploymentProcessor are not used by the majority of the code, so they + // are not validated here. + + return err +} + // Controller is an interface to implement async operation controller. type Controller interface { // Run runs async request operation. @@ -68,11 +81,6 @@ func (b *BaseController) StorageClient() store.StorageClient { return b.options.StorageClient } -// DataProvider gets data storage provider for this controller. -func (b *BaseController) DataProvider() dataprovider.DataStorageProvider { - return b.options.DataProvider -} - // KubeClient gets Kubernetes client for this controller. func (b *BaseController) KubeClient() runtimeclient.Client { return b.options.KubeClient diff --git a/pkg/armrpc/asyncoperation/statusmanager/statusmanager.go b/pkg/armrpc/asyncoperation/statusmanager/statusmanager.go index 7b51764390..e87c5f71c1 100644 --- a/pkg/armrpc/asyncoperation/statusmanager/statusmanager.go +++ b/pkg/armrpc/asyncoperation/statusmanager/statusmanager.go @@ -27,7 +27,6 @@ import ( ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" "github.com/radius-project/radius/pkg/metrics" "github.com/radius-project/radius/pkg/trace" - "github.com/radius-project/radius/pkg/ucp/dataprovider" queue "github.com/radius-project/radius/pkg/ucp/queue/client" "github.com/radius-project/radius/pkg/ucp/resources" "github.com/radius-project/radius/pkg/ucp/store" @@ -37,7 +36,7 @@ import ( // statusManager includes the necessary functions to manage asynchronous operations. type statusManager struct { - storeProvider dataprovider.DataStorageProvider + storageClient store.StorageClient queue queue.Client location string } @@ -65,9 +64,9 @@ type StatusManager interface { } // New creates statusManager instance. -func New(dataProvider dataprovider.DataStorageProvider, q queue.Client, location string) StatusManager { +func New(storageClient store.StorageClient, q queue.Client, location string) StatusManager { return &statusManager{ - storeProvider: dataProvider, + storageClient: storageClient, queue: q, location: location, } @@ -78,10 +77,6 @@ func (aom *statusManager) operationStatusResourceID(id resources.ID, operationID return fmt.Sprintf("%s/providers/%s/locations/%s/operationstatuses/%s", id.PlaneScope(), strings.ToLower(id.ProviderNamespace()), aom.location, operationID) } -func (aom *statusManager) getClient(ctx context.Context, id resources.ID) (store.StorageClient, error) { - return aom.storeProvider.GetStorageClient(ctx, id.ProviderNamespace()+"/operationstatuses") -} - // QueueAsyncOperation creates and saves a new status resource with the given parameters in datastore, and queues // a request message. If an error occurs, the status is deleted using the storeClient. func (aom *statusManager) QueueAsyncOperation(ctx context.Context, sCtx *v1.ARMRequestContext, options QueueOperationOptions) error { @@ -111,12 +106,7 @@ func (aom *statusManager) QueueAsyncOperation(ctx context.Context, sCtx *v1.ARMR ClientObjectID: sCtx.ClientObjectID, } - storeClient, err := aom.getClient(ctx, sCtx.ResourceID) - if err != nil { - return err - } - - err = storeClient.Save(ctx, &store.Object{ + err := aom.storageClient.Save(ctx, &store.Object{ Metadata: store.Metadata{ID: opID}, Data: aos, }) @@ -126,7 +116,7 @@ func (aom *statusManager) QueueAsyncOperation(ctx context.Context, sCtx *v1.ARMR } if err = aom.queueRequestMessage(ctx, sCtx, aos, options.OperationTimeout); err != nil { - delErr := storeClient.Delete(ctx, opID) + delErr := aom.storageClient.Delete(ctx, opID) if delErr != nil { return delErr } @@ -140,12 +130,7 @@ func (aom *statusManager) QueueAsyncOperation(ctx context.Context, sCtx *v1.ARMR // Get gets a status object from the datastore or an error if the retrieval fails. func (aom *statusManager) Get(ctx context.Context, id resources.ID, operationID uuid.UUID) (*Status, error) { - storeClient, err := aom.getClient(ctx, id) - if err != nil { - return nil, err - } - - obj, err := storeClient.Get(ctx, aom.operationStatusResourceID(id, operationID)) + obj, err := aom.storageClient.Get(ctx, aom.operationStatusResourceID(id, operationID)) if err != nil { return nil, err } @@ -162,12 +147,7 @@ func (aom *statusManager) Get(ctx context.Context, id resources.ID, operationID // given parameters, and saves it back to the store. func (aom *statusManager) Update(ctx context.Context, id resources.ID, operationID uuid.UUID, state v1.ProvisioningState, endTime *time.Time, opError *v1.ErrorDetails) error { opID := aom.operationStatusResourceID(id, operationID) - storeClient, err := aom.getClient(ctx, id) - if err != nil { - return err - } - - obj, err := storeClient.Get(ctx, opID) + obj, err := aom.storageClient.Get(ctx, opID) if err != nil { return err } @@ -190,17 +170,13 @@ func (aom *statusManager) Update(ctx context.Context, id resources.ID, operation obj.Data = s - return storeClient.Save(ctx, obj, store.WithETag(obj.ETag)) + return aom.storageClient.Save(ctx, obj, store.WithETag(obj.ETag)) } // Delete deletes the operation status resource associated with the given ID and // operationID, and returns an error if unsuccessful. func (aom *statusManager) Delete(ctx context.Context, id resources.ID, operationID uuid.UUID) error { - storeClient, err := aom.getClient(ctx, id) - if err != nil { - return err - } - return storeClient.Delete(ctx, aom.operationStatusResourceID(id, operationID)) + return aom.storageClient.Delete(ctx, aom.operationStatusResourceID(id, operationID)) } // queueRequestMessage function is to put the async operation message to the queue to be worked on. diff --git a/pkg/armrpc/asyncoperation/statusmanager/statusmanager_test.go b/pkg/armrpc/asyncoperation/statusmanager/statusmanager_test.go index 7f0c330939..c61872cf3d 100644 --- a/pkg/armrpc/asyncoperation/statusmanager/statusmanager_test.go +++ b/pkg/armrpc/asyncoperation/statusmanager/statusmanager_test.go @@ -26,7 +26,6 @@ import ( "github.com/google/uuid" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" "github.com/radius-project/radius/pkg/armrpc/rpctest" - "github.com/radius-project/radius/pkg/ucp/dataprovider" queue "github.com/radius-project/radius/pkg/ucp/queue/client" "github.com/radius-project/radius/pkg/ucp/resources" "github.com/radius-project/radius/pkg/ucp/store" @@ -35,10 +34,9 @@ import ( ) type asyncOperationsManagerTest struct { - manager StatusManager - storeProvider *dataprovider.MockDataStorageProvider - storeClient *store.MockStorageClient - queue *queue.MockClient + manager StatusManager + storeClient *store.MockStorageClient + queue *queue.MockClient } const ( @@ -54,12 +52,10 @@ const ( func setup(tb testing.TB) (asyncOperationsManagerTest, *gomock.Controller) { ctrl := gomock.NewController(tb) - dp := dataprovider.NewMockDataStorageProvider(ctrl) sc := store.NewMockStorageClient(ctrl) - dp.EXPECT().GetStorageClient(gomock.Any(), "Applications.Core/operationstatuses").Return(sc, nil) enq := queue.NewMockClient(ctrl) - aom := New(dp, enq, "test-location") - return asyncOperationsManagerTest{manager: aom, storeProvider: dp, storeClient: sc, queue: enq}, ctrl + aom := New(sc, enq, "test-location") + return asyncOperationsManagerTest{manager: aom, storeClient: sc, queue: enq}, ctrl } var reqCtx = &v1.ARMRequestContext{ diff --git a/pkg/armrpc/asyncoperation/worker/registry.go b/pkg/armrpc/asyncoperation/worker/registry.go index 00dbe7e82a..eb21ec1042 100644 --- a/pkg/armrpc/asyncoperation/worker/registry.go +++ b/pkg/armrpc/asyncoperation/worker/registry.go @@ -17,12 +17,11 @@ limitations under the License. package worker import ( - "context" + "fmt" "sync" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" - "github.com/radius-project/radius/pkg/ucp/dataprovider" ) // ControllerFactoryFunc is a factory function to create a controller. @@ -32,40 +31,38 @@ type ControllerFactoryFunc func(opts ctrl.Options) (ctrl.Controller, error) type ControllerRegistry struct { ctrlMap map[string]ctrl.Controller ctrlMapMu sync.RWMutex - sp dataprovider.DataStorageProvider defaultFactory ControllerFactoryFunc defaultOpts ctrl.Options } // NewControllerRegistry creates an ControllerRegistry instance. -func NewControllerRegistry(sp dataprovider.DataStorageProvider) *ControllerRegistry { +func NewControllerRegistry() *ControllerRegistry { return &ControllerRegistry{ ctrlMap: map[string]ctrl.Controller{}, - sp: sp, } } // Register registers a controller for a specific resource type and operation method. // // Controllers registered using Register will be cached by the registry and the same instance will be reused. -func (h *ControllerRegistry) Register(ctx context.Context, resourceType string, method v1.OperationMethod, factoryFn ControllerFactoryFunc, opts ctrl.Options) error { +func (h *ControllerRegistry) Register(resourceType string, method v1.OperationMethod, factoryFn ControllerFactoryFunc, opts ctrl.Options) error { h.ctrlMapMu.Lock() defer h.ctrlMapMu.Unlock() - ot := v1.OperationType{Type: resourceType, Method: method} - storageClient, err := h.sp.GetStorageClient(ctx, resourceType) + opts.ResourceType = resourceType + + err := opts.Validate() if err != nil { - return err + return fmt.Errorf("invalid controller options: %w", err) } - opts.StorageClient = storageClient - opts.ResourceType = resourceType ctrl, err := factoryFn(opts) if err != nil { return err } + ot := v1.OperationType{Type: resourceType, Method: method} h.ctrlMap[ot.String()] = ctrl return nil } @@ -74,17 +71,22 @@ func (h *ControllerRegistry) Register(ctx context.Context, resourceType string, // // The default controller will be used when Get is called with an operation type that has no registered controller. // The default controller will not be cached by the registry. -func (h *ControllerRegistry) RegisterDefault(ctx context.Context, factoryFn ControllerFactoryFunc, opts ctrl.Options) error { +func (h *ControllerRegistry) RegisterDefault(factoryFn ControllerFactoryFunc, opts ctrl.Options) error { h.ctrlMapMu.Lock() defer h.ctrlMapMu.Unlock() + // Note: we can't call opts.Validate() here because we don't know the resource type yet. + if opts.StorageClient == nil { + return fmt.Errorf("invalid controller options: .StorageClient is required") + } + h.defaultFactory = factoryFn h.defaultOpts = opts return nil } // Get gets the registered async controller instance. -func (h *ControllerRegistry) Get(ctx context.Context, operationType v1.OperationType) (ctrl.Controller, error) { +func (h *ControllerRegistry) Get(operationType v1.OperationType) (ctrl.Controller, error) { h.ctrlMapMu.RLock() defer h.ctrlMapMu.RUnlock() @@ -92,23 +94,17 @@ func (h *ControllerRegistry) Get(ctx context.Context, operationType v1.Operation return h, nil } - return h.getDefault(ctx, operationType) + return h.getDefault(operationType) } -func (h *ControllerRegistry) getDefault(ctx context.Context, operationType v1.OperationType) (ctrl.Controller, error) { +func (h *ControllerRegistry) getDefault(operationType v1.OperationType) (ctrl.Controller, error) { if h.defaultFactory == nil { return nil, nil } - storageClient, err := h.sp.GetStorageClient(ctx, operationType.Type) - if err != nil { - return nil, err - } - // Copy the options so we can update it. opts := h.defaultOpts - opts.StorageClient = storageClient opts.ResourceType = operationType.Type return h.defaultFactory(opts) diff --git a/pkg/armrpc/asyncoperation/worker/registry_test.go b/pkg/armrpc/asyncoperation/worker/registry_test.go index 8ceb457448..c3078e6dcb 100644 --- a/pkg/armrpc/asyncoperation/worker/registry_test.go +++ b/pkg/armrpc/asyncoperation/worker/registry_test.go @@ -23,7 +23,7 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" "github.com/radius-project/radius/pkg/corerp/backend/deployment" - "github.com/radius-project/radius/pkg/ucp/dataprovider" + "github.com/radius-project/radius/pkg/ucp/store/inmemory" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" ) @@ -32,21 +32,17 @@ func TestRegister_Get(t *testing.T) { mctrl := gomock.NewController(t) defer mctrl.Finish() - mockSP := dataprovider.NewMockDataStorageProvider(mctrl) - mockSP.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() - - registry := NewControllerRegistry(mockSP) + registry := NewControllerRegistry() opGet := v1.OperationType{Type: "Applications.Core/environments", Method: v1.OperationGet} opPut := v1.OperationType{Type: "Applications.Core/environments", Method: v1.OperationPut} ctrlOpts := ctrl.Options{ - StorageClient: nil, - DataProvider: mockSP, + StorageClient: inmemory.NewClient(), GetDeploymentProcessor: func() deployment.DeploymentProcessor { return nil }, } - err := registry.Register(context.TODO(), opGet.Type, opGet.Method, func(opts ctrl.Options) (ctrl.Controller, error) { + err := registry.Register(opGet.Type, opGet.Method, func(opts ctrl.Options) (ctrl.Controller, error) { return &testAsyncController{ BaseController: ctrl.NewBaseAsyncController(ctrlOpts), fn: func(ctx context.Context) (ctrl.Result, error) { @@ -56,45 +52,38 @@ func TestRegister_Get(t *testing.T) { }, ctrlOpts) require.NoError(t, err) - err = registry.Register(context.TODO(), opPut.Type, opPut.Method, func(opts ctrl.Options) (ctrl.Controller, error) { + err = registry.Register(opPut.Type, opPut.Method, func(opts ctrl.Options) (ctrl.Controller, error) { return &testAsyncController{ BaseController: ctrl.NewBaseAsyncController(ctrlOpts), }, nil }, ctrlOpts) require.NoError(t, err) - ctrl, err := registry.Get(context.Background(), opGet) + ctrl, err := registry.Get(opGet) require.NoError(t, err) require.NotNil(t, ctrl) - ctrl, err = registry.Get(context.Background(), opPut) + ctrl, err = registry.Get(opPut) require.NoError(t, err) require.NotNil(t, ctrl) // Getting a controller that is not registered should return nil by default. - ctrl, err = registry.Get(context.Background(), v1.OperationType{Type: "Applications.Core/unknown", Method: v1.OperationGet}) + ctrl, err = registry.Get(v1.OperationType{Type: "Applications.Core/unknown", Method: v1.OperationGet}) require.NoError(t, err) require.Nil(t, ctrl) } func TestRegister_Get_WithDefault(t *testing.T) { - mctrl := gomock.NewController(t) - defer mctrl.Finish() - - mockSP := dataprovider.NewMockDataStorageProvider(mctrl) - mockSP.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() - - registry := NewControllerRegistry(mockSP) + registry := NewControllerRegistry() opGet := v1.OperationType{Type: "Applications.Core/environments", Method: v1.OperationGet} ctrlOpts := ctrl.Options{ - StorageClient: nil, - DataProvider: mockSP, + StorageClient: inmemory.NewClient(), GetDeploymentProcessor: func() deployment.DeploymentProcessor { return nil }, } - err := registry.Register(context.TODO(), opGet.Type, opGet.Method, func(opts ctrl.Options) (ctrl.Controller, error) { + err := registry.Register(opGet.Type, opGet.Method, func(opts ctrl.Options) (ctrl.Controller, error) { return &testAsyncController{ BaseController: ctrl.NewBaseAsyncController(ctrlOpts), fn: func(ctx context.Context) (ctrl.Result, error) { @@ -104,19 +93,19 @@ func TestRegister_Get_WithDefault(t *testing.T) { }, ctrlOpts) require.NoError(t, err) - err = registry.RegisterDefault(context.TODO(), func(opts ctrl.Options) (ctrl.Controller, error) { + err = registry.RegisterDefault(func(opts ctrl.Options) (ctrl.Controller, error) { return &testAsyncController{ BaseController: ctrl.NewBaseAsyncController(ctrlOpts), }, nil }, ctrlOpts) require.NoError(t, err) - ctrl, err := registry.Get(context.Background(), opGet) + ctrl, err := registry.Get(opGet) require.NoError(t, err) require.NotNil(t, ctrl) // Getting a controller that is not registered should default the default - ctrl, err = registry.Get(context.Background(), v1.OperationType{Type: "Applications.Core/unknown", Method: v1.OperationGet}) + ctrl, err = registry.Get(v1.OperationType{Type: "Applications.Core/unknown", Method: v1.OperationGet}) require.NoError(t, err) require.NotNil(t, ctrl) } diff --git a/pkg/armrpc/asyncoperation/worker/service.go b/pkg/armrpc/asyncoperation/worker/service.go index 4a04a19384..a70b98778e 100644 --- a/pkg/armrpc/asyncoperation/worker/service.go +++ b/pkg/armrpc/asyncoperation/worker/service.go @@ -34,7 +34,7 @@ type Service struct { // Options is the server hosting options. Options hostoptions.HostOptions // StorageProvider is the provider of storage client. - StorageProvider dataprovider.DataStorageProvider + StorageProvider *dataprovider.DataStorageProvider // OperationStatusManager is the manager of the operation status. OperationStatusManager manager.StatusManager // Controllers is the registry of the async operation controllers. @@ -46,15 +46,22 @@ type Service struct { // Init initializes worker service - it initializes the StorageProvider, RequestQueue, OperationStatusManager, Controllers, KubeClient and // returns an error if any of these operations fail. func (s *Service) Init(ctx context.Context) error { - s.StorageProvider = dataprovider.NewStorageProvider(s.Options.Config.StorageProvider) + s.StorageProvider = dataprovider.DataStorageProviderFromOptions(s.Options.Config.StorageProvider) qp := qprovider.New(s.Options.Config.QueueProvider) + var err error + storageClient, err := s.StorageProvider.GetClient(ctx) + if err != nil { + return err + } + s.RequestQueue, err = qp.GetClient(ctx) if err != nil { return err } - s.OperationStatusManager = manager.New(s.StorageProvider, s.RequestQueue, s.Options.Config.Env.RoleLocation) - s.Controllers = NewControllerRegistry(s.StorageProvider) + + s.OperationStatusManager = manager.New(storageClient, s.RequestQueue, s.Options.Config.Env.RoleLocation) + s.Controllers = NewControllerRegistry() return nil } diff --git a/pkg/armrpc/asyncoperation/worker/worker.go b/pkg/armrpc/asyncoperation/worker/worker.go index e00c767170..0539576109 100644 --- a/pkg/armrpc/asyncoperation/worker/worker.go +++ b/pkg/armrpc/asyncoperation/worker/worker.go @@ -168,7 +168,7 @@ func (w *AsyncRequestProcessWorker) Start(ctx context.Context) error { } reqCtx = v1.WithARMRequestContext(reqCtx, armReqCtx) - asyncCtrl, err := w.registry.Get(reqCtx, armReqCtx.OperationType) + asyncCtrl, err := w.registry.Get(armReqCtx.OperationType) if err != nil { opLogger.Error(err, "failed to get async controller.") if err := w.requestQueue.FinishMessage(reqCtx, msgreq); err != nil { diff --git a/pkg/armrpc/asyncoperation/worker/worker_runoperation_test.go b/pkg/armrpc/asyncoperation/worker/worker_runoperation_test.go index 3e7d6280ab..9ff93b40b1 100644 --- a/pkg/armrpc/asyncoperation/worker/worker_runoperation_test.go +++ b/pkg/armrpc/asyncoperation/worker/worker_runoperation_test.go @@ -29,11 +29,11 @@ import ( ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" manager "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" "github.com/radius-project/radius/pkg/corerp/backend/deployment" - "github.com/radius-project/radius/pkg/ucp/dataprovider" queue "github.com/radius-project/radius/pkg/ucp/queue/client" "github.com/radius-project/radius/pkg/ucp/queue/inmemory" "github.com/radius-project/radius/pkg/ucp/resources" "github.com/radius-project/radius/pkg/ucp/store" + inmemorystore "github.com/radius-project/radius/pkg/ucp/store/inmemory" "github.com/stretchr/testify/require" "go.uber.org/atomic" "go.uber.org/mock/gomock" @@ -77,7 +77,6 @@ type testContext struct { ctx context.Context mockSC *store.MockStorageClient mockSM *manager.MockStatusManager - mockSP *dataprovider.MockDataStorageProvider testQueue *inmemory.Client internalQ *inmemory.InmemQueue @@ -115,11 +114,11 @@ func (c *testContext) cancellable(timeout time.Duration) (context.Context, conte func newTestContext(t *testing.T, lockTime time.Duration) (*testContext, *gomock.Controller) { mctrl := gomock.NewController(t) inmemQ := inmemory.NewInMemQueue(lockTime) + storageClient := store.NewMockStorageClient(mctrl) return &testContext{ ctx: context.Background(), - mockSC: store.NewMockStorageClient(mctrl), + mockSC: storageClient, mockSM: manager.NewMockStatusManager(mctrl), - mockSP: dataprovider.NewMockDataStorageProvider(mctrl), internalQ: inmemQ, testQueue: inmemory.New(inmemQ), }, mctrl @@ -147,17 +146,11 @@ func TestStart_UnknownOperation(t *testing.T) { tCtx, mctrl := newTestContext(t, defaultTestLockTime) defer mctrl.Finish() - registry := NewControllerRegistry(tCtx.mockSP) + registry := NewControllerRegistry() worker := New(Options{DequeueIntervalDuration: defaultTestDequeueInterval}, nil, tCtx.testQueue, registry) - tCtx.mockSP.EXPECT(). - GetStorageClient(gomock.Any(), gomock.Any()). - Return(tCtx.mockSC, nil). - Times(1) - opts := ctrl.Options{ StorageClient: tCtx.mockSC, - DataProvider: tCtx.mockSP, GetDeploymentProcessor: func() deployment.DeploymentProcessor { return deployment.NewMockDeploymentProcessor(mctrl) }, @@ -174,7 +167,6 @@ func TestStart_UnknownOperation(t *testing.T) { ctx, cancel := tCtx.cancellable(time.Duration(0)) err := registry.Register( - ctx, testResourceType, "UNDEFINED", func(opts ctrl.Options) (ctrl.Controller, error) { return testCtrl, nil @@ -215,16 +207,14 @@ func TestStart_MaxDequeueCount(t *testing.T) { }).AnyTimes() tCtx.mockSC.EXPECT().Save(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1) tCtx.mockSM.EXPECT().Update(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Eq(v1.ProvisioningStateFailed), gomock.Any(), gomock.Any()).Return(nil).Times(1) - tCtx.mockSP.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Return(store.StorageClient(tCtx.mockSC), nil).Times(1) expectedDequeueCount := 2 - registry := NewControllerRegistry(tCtx.mockSP) + registry := NewControllerRegistry() worker := New(Options{MaxOperationRetryCount: expectedDequeueCount, DequeueIntervalDuration: defaultTestDequeueInterval}, tCtx.mockSM, tCtx.testQueue, registry) opts := ctrl.Options{ StorageClient: tCtx.mockSC, - DataProvider: tCtx.mockSP, } testCtrl := &testAsyncController{ @@ -236,12 +226,11 @@ func TestStart_MaxDequeueCount(t *testing.T) { ctx, cancel := tCtx.cancellable(0) err := registry.Register( - ctx, testResourceType, v1.OperationPut, func(opts ctrl.Options) (ctrl.Controller, error) { return testCtrl, nil }, ctrl.Options{ - DataProvider: tCtx.mockSP, + StorageClient: tCtx.mockSC, }) require.NoError(t, err) @@ -279,14 +268,12 @@ func TestStart_MaxConcurrency(t *testing.T) { tCtx.mockSC.EXPECT().Save(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() tCtx.mockSM.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(testOperationStatus, nil).AnyTimes() tCtx.mockSM.EXPECT().Update(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() - tCtx.mockSP.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Return(store.StorageClient(tCtx.mockSC), nil).AnyTimes() - registry := NewControllerRegistry(tCtx.mockSP) + registry := NewControllerRegistry() worker := New(Options{DequeueIntervalDuration: defaultTestDequeueInterval}, tCtx.mockSM, tCtx.testQueue, registry) opts := ctrl.Options{ StorageClient: tCtx.mockSC, - DataProvider: tCtx.mockSP, GetDeploymentProcessor: func() deployment.DeploymentProcessor { return deployment.NewMockDeploymentProcessor(mctrl) }, @@ -309,7 +296,6 @@ func TestStart_MaxConcurrency(t *testing.T) { } ctx, cancel := tCtx.cancellable(time.Duration(0)) err := registry.Register( - ctx, testResourceType, v1.OperationPut, func(opts ctrl.Options) (ctrl.Controller, error) { @@ -358,14 +344,12 @@ func TestStart_RunOperation(t *testing.T) { tCtx.mockSC.EXPECT().Save(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() tCtx.mockSM.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(testOperationStatus, nil).AnyTimes() tCtx.mockSM.EXPECT().Update(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() - tCtx.mockSP.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Return(store.StorageClient(tCtx.mockSC), nil).AnyTimes() - registry := NewControllerRegistry(tCtx.mockSP) + registry := NewControllerRegistry() worker := New(Options{}, tCtx.mockSM, tCtx.testQueue, registry) opts := ctrl.Options{ StorageClient: tCtx.mockSC, - DataProvider: tCtx.mockSP, GetDeploymentProcessor: func() deployment.DeploymentProcessor { return deployment.NewMockDeploymentProcessor(mctrl) }, @@ -385,7 +369,6 @@ func TestStart_RunOperation(t *testing.T) { ctx, cancel := tCtx.cancellable(time.Duration(0)) err := registry.Register( - ctx, testResourceType, v1.OperationPut, func(opts ctrl.Options) (ctrl.Controller, error) { return testCtrl, nil @@ -437,7 +420,6 @@ func TestRunOperation_Successfully(t *testing.T) { opts := ctrl.Options{ StorageClient: tCtx.mockSC, - DataProvider: tCtx.mockSP, GetDeploymentProcessor: func() deployment.DeploymentProcessor { return deployment.NewMockDeploymentProcessor(mctrl) }, @@ -477,7 +459,6 @@ func TestRunOperation_ExtendMessageLock(t *testing.T) { opts := ctrl.Options{ StorageClient: tCtx.mockSC, - DataProvider: tCtx.mockSP, GetDeploymentProcessor: func() deployment.DeploymentProcessor { return deployment.NewMockDeploymentProcessor(mctrl) }, @@ -513,9 +494,13 @@ func TestRunOperation_CancelContext(t *testing.T) { worker := New(Options{}, nil, tCtx.testQueue, nil) + // This test has a race condition with the worker loop trying to make an operation status + // as failed. We can't use a mock because that might happen after the mock is destroyed (on test completion). + // + // Instead we use the in-memory store. + opts := ctrl.Options{ - StorageClient: nil, - DataProvider: tCtx.mockSP, + StorageClient: inmemorystore.NewClient(), GetDeploymentProcessor: func() deployment.DeploymentProcessor { return nil }, @@ -571,7 +556,6 @@ func TestRunOperation_Timeout(t *testing.T) { opts := ctrl.Options{ StorageClient: tCtx.mockSC, - DataProvider: tCtx.mockSP, GetDeploymentProcessor: func() deployment.DeploymentProcessor { return deployment.NewMockDeploymentProcessor(mctrl) }, @@ -606,7 +590,6 @@ func TestRunOperation_PanicController(t *testing.T) { opts := ctrl.Options{ StorageClient: tCtx.mockSC, - DataProvider: tCtx.mockSP, GetDeploymentProcessor: func() deployment.DeploymentProcessor { return nil }, diff --git a/pkg/armrpc/builder/builder.go b/pkg/armrpc/builder/builder.go index 0120d77f22..24f17263c2 100644 --- a/pkg/armrpc/builder/builder.go +++ b/pkg/armrpc/builder/builder.go @@ -172,7 +172,7 @@ func (b *Builder) ApplyAsyncHandler(ctx context.Context, registry *worker.Contro } if h.AsyncController != nil { - err := registry.Register(ctx, h.ResourceType, h.Method, h.AsyncController, ctrlOpts) + err := registry.Register(h.ResourceType, h.Method, h.AsyncController, ctrlOpts) if err != nil { return err } diff --git a/pkg/armrpc/builder/builder_test.go b/pkg/armrpc/builder/builder_test.go index c0c0f20654..90695a363c 100644 --- a/pkg/armrpc/builder/builder_test.go +++ b/pkg/armrpc/builder/builder_test.go @@ -23,12 +23,12 @@ import ( "github.com/go-chi/chi/v5" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" - asyncctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" + backendctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" + "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" "github.com/radius-project/radius/pkg/armrpc/asyncoperation/worker" apictrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rpctest" - "github.com/radius-project/radius/pkg/ucp/dataprovider" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/store/inmemory" "github.com/radius-project/radius/test/testcontext" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -167,28 +167,19 @@ var defaultHandlerTests = []rpctest.HandlerTestSpec{ }, } -func setup(t *testing.T) (*dataprovider.MockDataStorageProvider, *store.MockStorageClient) { - mctrl := gomock.NewController(t) - - mockSP := dataprovider.NewMockDataStorageProvider(mctrl) - mockSC := store.NewMockStorageClient(mctrl) - - mockSC.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(&store.Object{}, nil).AnyTimes() - mockSC.EXPECT().Save(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() - mockSC.EXPECT().Delete(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() - mockSC.EXPECT().Query(gomock.Any(), gomock.Any(), gomock.Any()).Return(&store.ObjectQueryResult{}, nil).AnyTimes() - mockSP.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Return(store.StorageClient(mockSC), nil).AnyTimes() - - return mockSP, mockSC -} - func TestApplyAPIHandlers(t *testing.T) { - mockSP, _ := setup(t) - runTests := func(t *testing.T, testSpecs []rpctest.HandlerTestSpec, b *Builder) { rpctest.AssertRequests(t, testSpecs, "/api.ucp.dev", "/planes/radius/local", func(ctx context.Context) (chi.Router, error) { r := chi.NewRouter() - return r, b.ApplyAPIHandlers(ctx, r, apictrl.Options{PathBase: "/api.ucp.dev", DataProvider: mockSP}) + + options := apictrl.Options{ + Address: "localhost:8080", + PathBase: "/api.ucp.dev", + StorageClient: inmemory.NewClient(), + StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), + } + + return r, b.ApplyAPIHandlers(ctx, r, options) }) } @@ -218,7 +209,6 @@ func TestApplyAPIHandlers(t *testing.T) { } func TestApplyAPIHandlers_AvailableOperations(t *testing.T) { - mockSP, _ := setup(t) ns := newTestNamespace(t) ns.SetAvailableOperations([]v1.Operation{ @@ -237,17 +227,27 @@ func TestApplyAPIHandlers_AvailableOperations(t *testing.T) { builder := ns.GenerateBuilder() rpctest.AssertRequests(t, handlerTests, "/api.ucp.dev", "/planes/radius/local", func(ctx context.Context) (chi.Router, error) { r := chi.NewRouter() - return r, builder.ApplyAPIHandlers(ctx, r, apictrl.Options{PathBase: "/api.ucp.dev", DataProvider: mockSP}) + options := apictrl.Options{ + Address: "localhost:8080", + PathBase: "/api.ucp.dev", + StorageClient: inmemory.NewClient(), + StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), + } + return r, builder.ApplyAPIHandlers(ctx, r, options) }) } func TestApplyAsyncHandler(t *testing.T) { - mockSP, _ := setup(t) ns := newTestNamespace(t) builder := ns.GenerateBuilder() - registry := worker.NewControllerRegistry(mockSP) + registry := worker.NewControllerRegistry() ctx := testcontext.New(t) - err := builder.ApplyAsyncHandler(ctx, registry, asyncctrl.Options{}) + + options := backendctrl.Options{ + StorageClient: inmemory.NewClient(), + } + + err := builder.ApplyAsyncHandler(ctx, registry, options) require.NoError(t, err) expectedOperations := []v1.OperationType{ @@ -261,7 +261,7 @@ func TestApplyAsyncHandler(t *testing.T) { } for _, op := range expectedOperations { - jobCtrl, err := registry.Get(context.Background(), op) + jobCtrl, err := registry.Get(op) require.NoError(t, err) require.NotNil(t, jobCtrl) } diff --git a/pkg/armrpc/frontend/controller/controller.go b/pkg/armrpc/frontend/controller/controller.go index d7c8bab554..bbda7f3537 100644 --- a/pkg/armrpc/frontend/controller/controller.go +++ b/pkg/armrpc/frontend/controller/controller.go @@ -18,6 +18,7 @@ package controller import ( "context" + "errors" "net/http" "time" @@ -25,7 +26,6 @@ import ( sm "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" "github.com/radius-project/radius/pkg/armrpc/hostoptions" "github.com/radius-project/radius/pkg/armrpc/rest" - "github.com/radius-project/radius/pkg/ucp/dataprovider" "github.com/radius-project/radius/pkg/ucp/store" runtimeclient "sigs.k8s.io/controller-runtime/pkg/client" @@ -55,9 +55,6 @@ type Options struct { // StorageClient is the data storage client. StorageClient store.StorageClient - // DataProvider is the data storage provider. - DataProvider dataprovider.DataStorageProvider - // KubeClient is the Kubernetes controller runtime client. KubeClient runtimeclient.Client @@ -69,6 +66,28 @@ type Options struct { StatusManager sm.StatusManager } +func (o Options) Validate() error { + var err error + if o.Address == "" { + err = errors.Join(err, errors.New(".Address is required")) + } + if o.StorageClient == nil { + err = errors.Join(err, errors.New(".StorageClient is required")) + } + if o.ResourceType == "" { + err = errors.Join(err, errors.New(".ResourceType is required")) + } + if o.StatusManager == nil { + err = errors.Join(err, errors.New(".StatusManager is required")) + } + + // PathBase is usually empty, so it is not validated here. + // + // KubeClient is not used by the majority of the code, so it is not validated here. + + return err +} + // ResourceOptions represents the options and filters for resource. type ResourceOptions[T any] struct { // RequestConverter is the request converter. @@ -122,11 +141,6 @@ func (b *BaseController) StorageClient() store.StorageClient { return b.options.StorageClient } -// DataProvider gets data storage provider for this controller. -func (b *BaseController) DataProvider() dataprovider.DataStorageProvider { - return b.options.DataProvider -} - // KubeClient gets Kubernetes client for this controller. func (b *BaseController) KubeClient() runtimeclient.Client { return b.options.KubeClient diff --git a/pkg/armrpc/frontend/controller/controller_test.go b/pkg/armrpc/frontend/controller/controller_test.go index d8435643e4..af31d927d6 100644 --- a/pkg/armrpc/frontend/controller/controller_test.go +++ b/pkg/armrpc/frontend/controller/controller_test.go @@ -20,6 +20,8 @@ import ( "testing" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" + "github.com/radius-project/radius/pkg/ucp/store" "github.com/stretchr/testify/require" ) @@ -99,3 +101,75 @@ func TestUpdateSystemData(t *testing.T) { }) } } + +func TestOptionsValidate(t *testing.T) { + tests := []struct { + name string + options Options + wantErr bool + errMsg string + }{ + { + name: "valid options", + options: Options{ + Address: "localhost:8080", + StorageClient: &store.MockStorageClient{}, + ResourceType: "testResource", + StatusManager: &statusmanager.MockStatusManager{}, + }, + wantErr: false, + }, + { + name: "missing address", + options: Options{ + StorageClient: &store.MockStorageClient{}, + ResourceType: "testResource", + StatusManager: &statusmanager.MockStatusManager{}, + }, + wantErr: true, + errMsg: ".Address is required", + }, + { + name: "missing storage client", + options: Options{ + Address: "localhost:8080", + ResourceType: "testResource", + StatusManager: &statusmanager.MockStatusManager{}, + }, + wantErr: true, + errMsg: ".StorageClient is required", + }, + { + name: "missing resource type", + options: Options{ + Address: "localhost:8080", + StorageClient: &store.MockStorageClient{}, + StatusManager: &statusmanager.MockStatusManager{}, + }, + wantErr: true, + errMsg: ".ResourceType is required", + }, + { + name: "missing status manager", + options: Options{ + Address: "localhost:8080", + StorageClient: &store.MockStorageClient{}, + ResourceType: "testResource", + }, + wantErr: true, + errMsg: ".StatusManager is required", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.options.Validate() + if tt.wantErr { + require.Error(t, err) + require.Contains(t, err.Error(), tt.errMsg) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/pkg/armrpc/frontend/controller/operation.go b/pkg/armrpc/frontend/controller/operation.go index db6d510148..2d720c1f47 100644 --- a/pkg/armrpc/frontend/controller/operation.go +++ b/pkg/armrpc/frontend/controller/operation.go @@ -26,7 +26,6 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" sm "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" "github.com/radius-project/radius/pkg/armrpc/rest" - "github.com/radius-project/radius/pkg/ucp/dataprovider" "github.com/radius-project/radius/pkg/ucp/resources" "github.com/radius-project/radius/pkg/ucp/store" ) @@ -64,11 +63,6 @@ func (b *Operation[P, T]) StorageClient() store.StorageClient { return b.options.StorageClient } -// DataProvider gets the DataStorageProvider. -func (b *Operation[P, T]) DataProvider() dataprovider.DataStorageProvider { - return b.options.DataProvider -} - // ResourceType gets the resource type for this controller. func (b *Operation[P, T]) ResourceType() string { return b.options.ResourceType diff --git a/pkg/armrpc/frontend/defaultoperation/getoperationresult.go b/pkg/armrpc/frontend/defaultoperation/getoperationresult.go index e5f64effd8..473884ab24 100644 --- a/pkg/armrpc/frontend/defaultoperation/getoperationresult.go +++ b/pkg/armrpc/frontend/defaultoperation/getoperationresult.go @@ -56,14 +56,7 @@ func (e *GetOperationResult) Run(ctx context.Context, w http.ResponseWriter, req return rest.NewBadRequestResponse(err.Error()), nil } - // Avoid using GetResource or e.StorageClient since they will use a different - // storage client than the one we want. - storageClient, err := e.DataProvider().GetStorageClient(ctx, id.ProviderNamespace()+"/operationstatuses") - if err != nil { - return nil, err - } - - obj, err := storageClient.Get(ctx, id.String()) + obj, err := e.StorageClient().Get(ctx, id.String()) if errors.Is(&store.ErrNotFound{ID: id.String()}, err) { return rest.NewNotFoundResponse(id), nil } diff --git a/pkg/armrpc/frontend/defaultoperation/getoperationresult_test.go b/pkg/armrpc/frontend/defaultoperation/getoperationresult_test.go index f4ad86b1d6..9434bfed4a 100644 --- a/pkg/armrpc/frontend/defaultoperation/getoperationresult_test.go +++ b/pkg/armrpc/frontend/defaultoperation/getoperationresult_test.go @@ -28,7 +28,6 @@ import ( manager "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rpctest" - "github.com/radius-project/radius/pkg/ucp/dataprovider" "github.com/radius-project/radius/pkg/ucp/store" "github.com/radius-project/radius/test/testcontext" "github.com/radius-project/radius/test/testutil" @@ -51,21 +50,14 @@ func TestGetOperationResultRun(t *testing.T) { t.Run("get non-existing resource", func(t *testing.T) { mctrl := gomock.NewController(t) - operationResultStoreClient := store.NewMockStorageClient(mctrl) - operationStatusStoreClient := store.NewMockStorageClient(mctrl) - - dataProvider := dataprovider.NewMockDataStorageProvider(mctrl) - dataProvider.EXPECT(). - GetStorageClient(gomock.Any(), "Applications.Core/operationstatuses"). - Return(operationStatusStoreClient, nil). - Times(1) + storageClient := store.NewMockStorageClient(mctrl) w := httptest.NewRecorder() req, err := rpctest.NewHTTPRequestFromJSON(testcontext.New(t), http.MethodGet, operationStatusTestHeaderFile, nil) require.NoError(t, err) ctx := rpctest.NewARMRequestContext(req) - operationStatusStoreClient. + storageClient. EXPECT(). Get(gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { @@ -73,8 +65,7 @@ func TestGetOperationResultRun(t *testing.T) { }) ctl, err := NewGetOperationResult(ctrl.Options{ - DataProvider: dataProvider, - StorageClient: operationResultStoreClient, // Will not be used. + StorageClient: storageClient, }) require.NoError(t, err) @@ -125,14 +116,7 @@ func TestGetOperationResultRun(t *testing.T) { for _, tt := range opResTestCases { t.Run(tt.desc, func(t *testing.T) { mctrl := gomock.NewController(t) - operationResultStoreClient := store.NewMockStorageClient(mctrl) - operationStatusStoreClient := store.NewMockStorageClient(mctrl) - - dataProvider := dataprovider.NewMockDataStorageProvider(mctrl) - dataProvider.EXPECT(). - GetStorageClient(gomock.Any(), "Applications.Core/operationstatuses"). - Return(operationStatusStoreClient, nil). - Times(1) + storageClient := store.NewMockStorageClient(mctrl) w := httptest.NewRecorder() req, err := rpctest.NewHTTPRequestFromJSON(testcontext.New(t), http.MethodGet, operationStatusTestHeaderFile, nil) @@ -142,7 +126,7 @@ func TestGetOperationResultRun(t *testing.T) { osDataModel.Status = tt.provisioningState osDataModel.RetryAfter = time.Second * 5 - operationStatusStoreClient. + storageClient. EXPECT(). Get(gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { @@ -153,8 +137,7 @@ func TestGetOperationResultRun(t *testing.T) { }) ctl, err := NewGetOperationResult(ctrl.Options{ - DataProvider: dataProvider, - StorageClient: operationResultStoreClient, // Will not be used. + StorageClient: storageClient, }) require.NoError(t, err) diff --git a/pkg/armrpc/frontend/server/handler.go b/pkg/armrpc/frontend/server/handler.go index d6a8354cac..e5d3c47b3d 100644 --- a/pkg/armrpc/frontend/server/handler.go +++ b/pkg/armrpc/frontend/server/handler.go @@ -130,14 +130,13 @@ func HandlerForController(controller ctrl.Controller, operationType v1.Operation // CreateHandler creates an http.Handler for the given resource type and operation method. func CreateHandler(ctx context.Context, resourceType string, operationMethod v1.OperationMethod, opts ctrl.Options, factory ControllerFactoryFunc) (http.HandlerFunc, error) { - storageClient, err := opts.DataProvider.GetStorageClient(ctx, resourceType) + opts.ResourceType = resourceType + + err := opts.Validate() if err != nil { - return nil, err + return nil, fmt.Errorf("invalid controller options: %w", err) } - opts.StorageClient = storageClient - opts.ResourceType = resourceType - ctrl, err := factory(opts) if err != nil { return nil, err @@ -155,14 +154,13 @@ func RegisterHandler(ctx context.Context, opts HandlerOptions, ctrlOpts ctrl.Opt return ErrInvalidOperationTypeOption } - storageClient, err := ctrlOpts.DataProvider.GetStorageClient(ctx, opts.ResourceType) + ctrlOpts.ResourceType = opts.ResourceType + + err := ctrlOpts.Validate() if err != nil { - return err + return fmt.Errorf("invalid controller options: %w", err) } - ctrlOpts.StorageClient = storageClient - ctrlOpts.ResourceType = opts.ResourceType - ctrl, err := opts.ControllerFactory(ctrlOpts) if err != nil { return err diff --git a/pkg/armrpc/frontend/server/handler_test.go b/pkg/armrpc/frontend/server/handler_test.go index f635c3f125..107c8b6877 100644 --- a/pkg/armrpc/frontend/server/handler_test.go +++ b/pkg/armrpc/frontend/server/handler_test.go @@ -28,11 +28,12 @@ import ( "github.com/go-chi/chi/v5" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rest" "github.com/radius-project/radius/pkg/armrpc/rpctest" "github.com/radius-project/radius/pkg/middleware" - "github.com/radius-project/radius/pkg/ucp/dataprovider" + "github.com/radius-project/radius/pkg/ucp/store/inmemory" "github.com/radius-project/radius/test/testcontext" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -73,18 +74,17 @@ func Test_NewSubrouter(t *testing.T) { } func Test_RegisterHandler_DeplicatedRoutes(t *testing.T) { - mctrl := gomock.NewController(t) - - mockSP := dataprovider.NewMockDataStorageProvider(mctrl) - mockSP.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() ctrlOpts := ctrl.Options{ - DataProvider: mockSP, + Address: "localhost:8080", + ResourceType: "Applications.Test/testResources", + StorageClient: inmemory.NewClient(), + StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), } p := chi.NewRouter() opts := HandlerOptions{ ParentRouter: p, - ResourceType: "Applications.Test", + ResourceType: "Applications.Test/testResources", Method: http.MethodGet, ControllerFactory: func(ctrl.Options) (ctrl.Controller, error) { return nil, nil }, Middlewares: chi.Middlewares{middleware.NormalizePath}, @@ -112,7 +112,7 @@ func Test_RegisterHandler(t *testing.T) { name: "valid route with resource type and method", opts: HandlerOptions{ ParentRouter: p, - ResourceType: "Applications.Test", + ResourceType: "Applications.Test/testResources", Method: http.MethodGet, ControllerFactory: func(ctrl.Options) (ctrl.Controller, error) { return nil, nil }, Middlewares: chi.Middlewares{middleware.NormalizePath}, @@ -124,7 +124,7 @@ func Test_RegisterHandler(t *testing.T) { opts: HandlerOptions{ ParentRouter: p, Path: "/test", - ResourceType: "Applications.Test", + ResourceType: "Applications.Test/testResources", Method: http.MethodGet, ControllerFactory: func(ctrl.Options) (ctrl.Controller, error) { return nil, nil }, Middlewares: chi.Middlewares{middleware.NormalizePath}, @@ -136,7 +136,8 @@ func Test_RegisterHandler(t *testing.T) { name: "valid route with operation type", opts: HandlerOptions{ ParentRouter: p, - OperationType: &v1.OperationType{Type: "Applications.Test", Method: "GET"}, + ResourceType: "Applications.Test/testResources", + OperationType: &v1.OperationType{Type: "Applications.Test/testResources", Method: "GET"}, ControllerFactory: func(ctrl.Options) (ctrl.Controller, error) { return nil, nil }, Middlewares: chi.Middlewares{middleware.NormalizePath}, }, @@ -148,7 +149,8 @@ func Test_RegisterHandler(t *testing.T) { opts: HandlerOptions{ ParentRouter: p, Path: "/*", - OperationType: &v1.OperationType{Type: "Applications.Test", Method: "PROXY"}, + ResourceType: "Applications.Test/testResources", + OperationType: &v1.OperationType{Type: "Applications.Test/testResources", Method: "PROXY"}, ControllerFactory: func(ctrl.Options) (ctrl.Controller, error) { return nil, nil }, }, validMethod: []string{http.MethodGet, http.MethodPost}, @@ -166,12 +168,10 @@ func Test_RegisterHandler(t *testing.T) { }, } - mctrl := gomock.NewController(t) - - mockSP := dataprovider.NewMockDataStorageProvider(mctrl) - mockSP.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() ctrlOpts := ctrl.Options{ - DataProvider: mockSP, + Address: "localhost:8080", + StorageClient: inmemory.NewClient(), + StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), } for _, tc := range tests { diff --git a/pkg/armrpc/frontend/server/service.go b/pkg/armrpc/frontend/server/service.go index f46244cfbe..6d31f1c958 100644 --- a/pkg/armrpc/frontend/server/service.go +++ b/pkg/armrpc/frontend/server/service.go @@ -40,7 +40,7 @@ type Service struct { Options hostoptions.HostOptions // StorageProvider is the provider of storage client. - StorageProvider dataprovider.DataStorageProvider + StorageProvider *dataprovider.DataStorageProvider // OperationStatusManager is the manager of the operation status. OperationStatusManager manager.StatusManager @@ -57,13 +57,19 @@ type Service struct { func (s *Service) Init(ctx context.Context) error { logger := ucplog.FromContextOrDiscard(ctx) - s.StorageProvider = dataprovider.NewStorageProvider(s.Options.Config.StorageProvider) + s.StorageProvider = dataprovider.DataStorageProviderFromOptions(s.Options.Config.StorageProvider) qp := qprovider.New(s.Options.Config.QueueProvider) + + storageClient, err := s.StorageProvider.GetClient(ctx) + if err != nil { + return err + } + reqQueueClient, err := qp.GetClient(ctx) if err != nil { return err } - s.OperationStatusManager = manager.New(s.StorageProvider, reqQueueClient, s.Options.Config.Env.RoleLocation) + s.OperationStatusManager = manager.New(storageClient, reqQueueClient, s.Options.Config.Env.RoleLocation) s.KubeClient, err = kubeutil.NewRuntimeClient(s.Options.K8sConfig) if err != nil { return err diff --git a/pkg/armrpc/rpctest/controllers.go b/pkg/armrpc/rpctest/controllers.go index 2c3c7d4cdf..80610ea8f4 100644 --- a/pkg/armrpc/rpctest/controllers.go +++ b/pkg/armrpc/rpctest/controllers.go @@ -22,7 +22,6 @@ import ( "testing" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" - "github.com/radius-project/radius/pkg/ucp/dataprovider" "github.com/radius-project/radius/pkg/ucp/store" "go.uber.org/mock/gomock" ) @@ -32,17 +31,16 @@ type ControllerContext struct { Ctx context.Context MCtrl *gomock.Controller MockSC *store.MockStorageClient - MockSP *dataprovider.MockDataStorageProvider } // NewControllerContext creates a new ControllerContext for testing. func NewControllerContext(t *testing.T) *ControllerContext { mctrl := gomock.NewController(t) + return &ControllerContext{ Ctx: context.Background(), MCtrl: mctrl, MockSC: store.NewMockStorageClient(mctrl), - MockSP: dataprovider.NewMockDataStorageProvider(mctrl), } } diff --git a/pkg/corerp/backend/deployment/deploymentprocessor.go b/pkg/corerp/backend/deployment/deploymentprocessor.go index 2ebf21a3e3..80392e48bc 100644 --- a/pkg/corerp/backend/deployment/deploymentprocessor.go +++ b/pkg/corerp/backend/deployment/deploymentprocessor.go @@ -40,7 +40,6 @@ import ( msg_dm "github.com/radius-project/radius/pkg/messagingrp/datamodel" msg_ctrl "github.com/radius-project/radius/pkg/messagingrp/frontend/controller" "github.com/radius-project/radius/pkg/portableresources" - "github.com/radius-project/radius/pkg/ucp/dataprovider" "github.com/radius-project/radius/pkg/ucp/resources" "github.com/radius-project/radius/pkg/ucp/store" "github.com/radius-project/radius/pkg/ucp/ucplog" @@ -60,15 +59,15 @@ type DeploymentProcessor interface { } // NewDeploymentProcessor creates a new instance of the DeploymentProcessor struct with the given parameters. -func NewDeploymentProcessor(appmodel model.ApplicationModel, sp dataprovider.DataStorageProvider, k8sClient controller_runtime.Client, k8sClientSet kubernetes.Interface) DeploymentProcessor { - return &deploymentProcessor{appmodel: appmodel, sp: sp, k8sClient: k8sClient, k8sClientSet: k8sClientSet} +func NewDeploymentProcessor(appmodel model.ApplicationModel, storageClient store.StorageClient, k8sClient controller_runtime.Client, k8sClientSet kubernetes.Interface) DeploymentProcessor { + return &deploymentProcessor{appmodel: appmodel, storageClient: storageClient, k8sClient: k8sClient, k8sClientSet: k8sClientSet} } var _ DeploymentProcessor = (*deploymentProcessor)(nil) type deploymentProcessor struct { - appmodel model.ApplicationModel - sp dataprovider.DataStorageProvider + appmodel model.ApplicationModel + storageClient store.StorageClient // k8sClient is the Kubernetes controller runtime client. k8sClient controller_runtime.Client // k8sClientSet is the Kubernetes client. @@ -226,14 +225,14 @@ func (dp *deploymentProcessor) getApplicationAndEnvironmentForResourceID(ctx con // 2. fetch the application properties from the DB app := &corerp_dm.Application{} - err = rp_util.FetchScopeResource(ctx, dp.sp, res.AppID.String(), app) + err = rp_util.FetchScopeResource(ctx, dp.storageClient, res.AppID.String(), app) if err != nil { return nil, nil, err } // 3. fetch the environment resource from the db to get the Namespace env := &corerp_dm.Environment{} - err = rp_util.FetchScopeResource(ctx, dp.sp, app.Properties.Environment, env) + err = rp_util.FetchScopeResource(ctx, dp.storageClient, app.Properties.Environment, env) if err != nil { return nil, nil, err } @@ -481,12 +480,7 @@ func (dp *deploymentProcessor) getAppOptions(appProp *corerp_dm.ApplicationPrope // getResourceDataByID fetches resource for the provided id from the data store func (dp *deploymentProcessor) getResourceDataByID(ctx context.Context, resourceID resources.ID) (ResourceData, error) { errMsg := "failed to fetch the resource %q. Err: %w" - sc, err := dp.sp.GetStorageClient(ctx, resourceID.Type()) - if err != nil { - return ResourceData{}, fmt.Errorf(errMsg, resourceID.String(), err) - } - - resource, err := sc.Get(ctx, resourceID.String()) + resource, err := dp.storageClient.Get(ctx, resourceID.String()) if err != nil { if errors.Is(&store.ErrNotFound{ID: resourceID.String()}, err) { return ResourceData{}, v1.NewClientErrInvalidRequest(fmt.Sprintf("resource %q does not exist", resourceID.String())) diff --git a/pkg/corerp/backend/deployment/deploymentprocessor_test.go b/pkg/corerp/backend/deployment/deploymentprocessor_test.go index 4a5cb748ef..0f2c4816e8 100644 --- a/pkg/corerp/backend/deployment/deploymentprocessor_test.go +++ b/pkg/corerp/backend/deployment/deploymentprocessor_test.go @@ -37,7 +37,6 @@ import ( "github.com/radius-project/radius/pkg/resourcemodel" rpv1 "github.com/radius-project/radius/pkg/rp/v1" "github.com/radius-project/radius/pkg/to" - "github.com/radius-project/radius/pkg/ucp/dataprovider" "github.com/radius-project/radius/pkg/ucp/resources" resources_azure "github.com/radius-project/radius/pkg/ucp/resources/azure" resources_kubernetes "github.com/radius-project/radius/pkg/ucp/resources/kubernetes" @@ -51,8 +50,7 @@ import ( type SharedMocks struct { model model.ApplicationModel - db *store.MockStorageClient - dbProvider *dataprovider.MockDataStorageProvider + storageClient *store.MockStorageClient resourceHandler *handlers.MockResourceHandler renderer *renderers.MockRenderer mctrl *gomock.Controller @@ -131,10 +129,10 @@ func setup(t *testing.T) SharedMocks { }, } + storageClient := store.NewMockStorageClient(ctrl) return SharedMocks{ model: model, - db: store.NewMockStorageClient(ctrl), - dbProvider: dataprovider.NewMockDataStorageProvider(ctrl), + storageClient: storageClient, resourceHandler: resourceHandler, renderer: renderer, mctrl: ctrl, @@ -303,7 +301,7 @@ func Test_Render(t *testing.T) { t.Run("verify render success", func(t *testing.T) { mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.dbProvider, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} testResource := getTestResource() testRendererOutput := getTestRendererOutput() @@ -314,7 +312,6 @@ func Test_Render(t *testing.T) { mocks.renderer.EXPECT().Render(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(testRendererOutput, nil) mocks.renderer.EXPECT().GetDependencyIDs(gomock.Any(), gomock.Any()).Times(1).Return(requiredResources, nil, nil) - mocks.dbProvider.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).AnyTimes().Return(mocks.db, nil) cr := store.Object{ Metadata: store.Metadata{ @@ -322,7 +319,7 @@ func Test_Render(t *testing.T) { }, Data: testResource, } - mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) + mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) application := datamodel.Application{ BaseResource: v1.BaseResource{ TrackedResource: v1.TrackedResource{ @@ -341,14 +338,14 @@ func Test_Render(t *testing.T) { }, Data: application, } - mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&ar, nil) + mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&ar, nil) er := store.Object{ Metadata: store.Metadata{ ID: env.ID, }, Data: env, } - mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&er, nil) + mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&er, nil) mongoResource := dsrp_dm.MongoDatabase{ BaseResource: v1.BaseResource{ @@ -369,7 +366,7 @@ func Test_Render(t *testing.T) { Data: mongoResource, } - mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&mr, nil) + mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&mr, nil) rendererOutput, err := dp.Render(ctx, resourceID, &testResource) require.NoError(t, err) @@ -378,7 +375,7 @@ func Test_Render(t *testing.T) { t.Run("verify render success lowercase resourcetype", func(t *testing.T) { mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.dbProvider, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} testResource := getLowerCaseTestResource() testRendererOutput := getTestRendererOutput() @@ -386,7 +383,6 @@ func Test_Render(t *testing.T) { mocks.renderer.EXPECT().Render(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(testRendererOutput, nil) mocks.renderer.EXPECT().GetDependencyIDs(gomock.Any(), gomock.Any()).Times(1).Return([]resources.ID{}, nil, nil) - mocks.dbProvider.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).AnyTimes().Return(mocks.db, nil) cr := store.Object{ Metadata: store.Metadata{ @@ -394,7 +390,7 @@ func Test_Render(t *testing.T) { }, Data: testResource, } - mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) + mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) application := datamodel.Application{ BaseResource: v1.BaseResource{ TrackedResource: v1.TrackedResource{ @@ -413,14 +409,14 @@ func Test_Render(t *testing.T) { }, Data: application, } - mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&ar, nil) + mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&ar, nil) er := store.Object{ Metadata: store.Metadata{ ID: env.ID, }, Data: env, } - mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&er, nil) + mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&er, nil) rendererOutput, err := dp.Render(ctx, resourceID, &testResource) require.NoError(t, err) @@ -429,7 +425,7 @@ func Test_Render(t *testing.T) { t.Run("verify render success uppercase resourcetype", func(t *testing.T) { mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.dbProvider, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} testResource := getUpperCaseTestResource() testRendererOutput := getTestRendererOutput() @@ -437,7 +433,6 @@ func Test_Render(t *testing.T) { mocks.renderer.EXPECT().Render(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(testRendererOutput, nil) mocks.renderer.EXPECT().GetDependencyIDs(gomock.Any(), gomock.Any()).Times(1).Return([]resources.ID{}, nil, nil) - mocks.dbProvider.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).AnyTimes().Return(mocks.db, nil) cr := store.Object{ Metadata: store.Metadata{ @@ -445,7 +440,7 @@ func Test_Render(t *testing.T) { }, Data: testResource, } - mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) + mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) application := datamodel.Application{ BaseResource: v1.BaseResource{ TrackedResource: v1.TrackedResource{ @@ -464,14 +459,14 @@ func Test_Render(t *testing.T) { }, Data: application, } - mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&ar, nil) + mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&ar, nil) er := store.Object{ Metadata: store.Metadata{ ID: env.ID, }, Data: env, } - mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&er, nil) + mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&er, nil) rendererOutput, err := dp.Render(ctx, resourceID, &testResource) require.NoError(t, err) @@ -480,13 +475,12 @@ func Test_Render(t *testing.T) { t.Run("verify render error", func(t *testing.T) { mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.dbProvider, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} testResource := getTestResource() resourceID := getTestResourceID(testResource.ID) mocks.renderer.EXPECT().GetDependencyIDs(gomock.Any(), gomock.Any()).Times(1).Return([]resources.ID{}, nil, nil) - mocks.dbProvider.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).AnyTimes().Return(mocks.db, nil) mocks.renderer.EXPECT().Render(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(renderers.RendererOutput{}, errors.New("failed to render the resource")) cr := store.Object{ @@ -495,7 +489,7 @@ func Test_Render(t *testing.T) { }, Data: testResource, } - mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) + mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) application := datamodel.Application{ BaseResource: v1.BaseResource{ TrackedResource: v1.TrackedResource{ @@ -514,43 +508,27 @@ func Test_Render(t *testing.T) { }, Data: application, } - mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&ar, nil) + mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&ar, nil) er := store.Object{ Metadata: store.Metadata{ ID: env.ID, }, Data: env, } - mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&er, nil) + mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&er, nil) _, err := dp.Render(ctx, resourceID, &testResource) require.Error(t, err, "failed to render the resource") }) - t.Run("Failure to get storage client", func(t *testing.T) { - mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.dbProvider, nil, nil} - - testResource := getTestResource() - resourceID := getTestResourceID(testResource.ID) - - mocks.dbProvider.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Times(1).Return(nil, errors.New("unsupported storage provider")) - - _, err := dp.Render(ctx, resourceID, &testResource) - require.Error(t, err) - require.Equal(t, "failed to fetch the resource \"/subscriptions/test-subscription/resourceGroups/test-resource-group/providers/Applications.Core/containers/test-resource\". Err: unsupported storage provider", err.Error()) - }) - t.Run("Resource not found in data store", func(t *testing.T) { mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.dbProvider, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} testResource := getTestResource() resourceID := getTestResourceID(testResource.ID) - mocks.dbProvider.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Times(1).Return(mocks.db, nil) - - mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&store.Object{}, &store.ErrNotFound{ID: testResource.ID}) + mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&store.Object{}, &store.ErrNotFound{ID: testResource.ID}) _, err := dp.Render(ctx, resourceID, &testResource) require.Error(t, err) @@ -560,14 +538,12 @@ func Test_Render(t *testing.T) { t.Run("Data store access error", func(t *testing.T) { mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.dbProvider, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} testResource := getTestResource() resourceID := getTestResourceID(testResource.ID) - mocks.dbProvider.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Times(1).Return(mocks.db, nil) - - mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&store.Object{}, errors.New("failed to connect to data store")) + mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&store.Object{}, errors.New("failed to connect to data store")) _, err := dp.Render(ctx, resourceID, &testResource) require.Error(t, err) @@ -576,7 +552,7 @@ func Test_Render(t *testing.T) { t.Run("Invalid resource type", func(t *testing.T) { mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.dbProvider, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} testInvalidResourceID := "/subscriptions/test-sub/resourceGroups/test-group/providers/Applications.foo/foo/foo" testResource := getTestResource() @@ -588,21 +564,19 @@ func Test_Render(t *testing.T) { t.Run("Invalid application id", func(t *testing.T) { mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.dbProvider, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} testResource := getTestResource() resourceID := getTestResourceID(testResource.ID) testResource.Properties.Application = "invalid-app-id" - mocks.dbProvider.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Times(1).Return(mocks.db, nil) - cr := store.Object{ Metadata: store.Metadata{ ID: testResource.ID, }, Data: testResource, } - mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) + mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) _, err := dp.Render(ctx, resourceID, &testResource) require.Error(t, err) @@ -612,21 +586,19 @@ func Test_Render(t *testing.T) { t.Run("Missing application id", func(t *testing.T) { mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.dbProvider, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} testResource := getTestResource() resourceID := getTestResourceID(testResource.ID) testResource.Properties.Application = "" - mocks.dbProvider.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Times(1).Return(mocks.db, nil) - cr := store.Object{ Metadata: store.Metadata{ ID: testResource.ID, }, Data: testResource, } - mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) + mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) _, err := dp.Render(ctx, resourceID, &testResource) require.Error(t, err) @@ -635,21 +607,19 @@ func Test_Render(t *testing.T) { t.Run("Invalid application resource type", func(t *testing.T) { mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.dbProvider, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} testResource := getTestResource() resourceID := getTestResourceID(testResource.ID) testResource.Properties.Application = "/subscriptions/test-subscription/resourceGroups/test-resource-group/providers/Applications.Core/app/test-application" - mocks.dbProvider.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Times(1).Return(mocks.db, nil) - cr := store.Object{ Metadata: store.Metadata{ ID: testResource.ID, }, Data: testResource, } - mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) + mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) _, err := dp.Render(ctx, resourceID, &testResource) require.Error(t, err) @@ -659,7 +629,7 @@ func Test_Render(t *testing.T) { t.Run("Missing output resource provider", func(t *testing.T) { mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.dbProvider, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} testResource := getTestResource() testRendererOutput := getTestRendererOutput() @@ -669,7 +639,6 @@ func Test_Render(t *testing.T) { mocks.renderer.EXPECT().Render(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(testRendererOutput, nil) mocks.renderer.EXPECT().GetDependencyIDs(gomock.Any(), gomock.Any()).Times(1).Return([]resources.ID{}, nil, nil) - mocks.dbProvider.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).AnyTimes().Return(mocks.db, nil) cr := store.Object{ Metadata: store.Metadata{ @@ -677,7 +646,7 @@ func Test_Render(t *testing.T) { }, Data: testResource, } - mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) + mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) application := datamodel.Application{ BaseResource: v1.BaseResource{ TrackedResource: v1.TrackedResource{ @@ -696,14 +665,14 @@ func Test_Render(t *testing.T) { }, Data: application, } - mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&ar, nil) + mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&ar, nil) er := store.Object{ Metadata: store.Metadata{ ID: env.ID, }, Data: env, } - mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&er, nil) + mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&er, nil) _, err := dp.Render(ctx, resourceID, &testResource) require.Error(t, err, "output resource \"Deployment\" does not have a provider specified") @@ -711,7 +680,7 @@ func Test_Render(t *testing.T) { t.Run("Unsupported output resource provider", func(t *testing.T) { mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.dbProvider, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} testResource := getTestResource() testRendererOutput := getTestRendererOutput() @@ -720,7 +689,6 @@ func Test_Render(t *testing.T) { testRendererOutput.Resources[0].CreateResource.ResourceType.Provider = "unknown" mocks.renderer.EXPECT().GetDependencyIDs(gomock.Any(), gomock.Any()).Times(1).Return([]resources.ID{}, nil, nil) - mocks.dbProvider.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).AnyTimes().Return(mocks.db, nil) cr := store.Object{ Metadata: store.Metadata{ @@ -728,7 +696,7 @@ func Test_Render(t *testing.T) { }, Data: testResource, } - mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) + mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) application := datamodel.Application{ BaseResource: v1.BaseResource{ TrackedResource: v1.TrackedResource{ @@ -747,14 +715,14 @@ func Test_Render(t *testing.T) { }, Data: application, } - mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&ar, nil) + mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&ar, nil) er := store.Object{ Metadata: store.Metadata{ ID: env.ID, }, Data: env, } - mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&er, nil) + mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&er, nil) mocks.renderer.EXPECT().Render(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(testRendererOutput, nil) @@ -765,14 +733,13 @@ func Test_Render(t *testing.T) { func setupDeployMocks(mocks SharedMocks, simulated bool) { testResource := getTestResource() - mocks.dbProvider.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).AnyTimes().Return(mocks.db, nil) cr := store.Object{ Metadata: store.Metadata{ ID: testResource.ID, }, Data: testResource, } - mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) + mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) app := datamodel.Application{ BaseResource: v1.BaseResource{ @@ -793,7 +760,7 @@ func setupDeployMocks(mocks SharedMocks, simulated bool) { }, Data: app, } - mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&ar, nil) + mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&ar, nil) env := datamodel.Environment{ BaseResource: v1.BaseResource{ @@ -823,14 +790,14 @@ func setupDeployMocks(mocks SharedMocks, simulated bool) { }, Data: env, } - mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&er, nil) + mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&er, nil) } func Test_Deploy(t *testing.T) { t.Run("Verify deploy success", func(t *testing.T) { ctx := testcontext.New(t) mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.dbProvider, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} testResource := getTestResource() testRendererOutput := getTestRendererOutput() @@ -870,7 +837,7 @@ func Test_Deploy(t *testing.T) { t.Run("Verify deploy success with simulated env", func(t *testing.T) { ctx := testcontext.New(t) mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.dbProvider, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} testResource := getTestResource() testRendererOutput := getTestRendererOutput() @@ -888,7 +855,7 @@ func Test_Deploy(t *testing.T) { t.Run("Verify deploy failure", func(t *testing.T) { ctx := testcontext.New(t) mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.dbProvider, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} testResource := getTestResource() testRendererOutput := getTestRendererOutput() @@ -905,7 +872,7 @@ func Test_Deploy(t *testing.T) { t.Run("Output resource dependency missing local ID", func(t *testing.T) { ctx := testcontext.New(t) mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.dbProvider, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} testResource := getTestResource() testRendererOutput := getTestRendererOutput() @@ -923,7 +890,7 @@ func Test_Deploy(t *testing.T) { t.Run("Invalid output resource type", func(t *testing.T) { ctx := testcontext.New(t) mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.dbProvider, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} testResource := getTestResource() testRendererOutput := getTestRendererOutput() @@ -941,7 +908,7 @@ func Test_Deploy(t *testing.T) { t.Run("Missing output resource identity", func(t *testing.T) { ctx := testcontext.New(t) mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.dbProvider, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} testResource := getTestResource() testRendererOutput := getTestRendererOutput() @@ -967,7 +934,7 @@ func Test_Delete(t *testing.T) { t.Run("Verify delete success", func(t *testing.T) { ctx := testcontext.New(t) mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.dbProvider, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} testResource := getTestResource() resourceID := getTestResourceID(testResource.ID) @@ -981,7 +948,7 @@ func Test_Delete(t *testing.T) { t.Run("Verify delete failure", func(t *testing.T) { ctx := testcontext.New(t) mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.dbProvider, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} testResource := getTestResource() resourceID := getTestResourceID(testResource.ID) @@ -995,7 +962,7 @@ func Test_Delete(t *testing.T) { t.Run("Verify delete with no output resources", func(t *testing.T) { ctx := testcontext.New(t) mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.dbProvider, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} testResource := getTestResource() resourceID := getTestResourceID(testResource.ID) @@ -1070,11 +1037,9 @@ func Test_getEnvOptions_PublicEndpointOverride(t *testing.T) { func Test_getResourceDataByID(t *testing.T) { ctx := testcontext.New(t) mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.dbProvider, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} t.Run("Get recipe data from connected mongoDB resources", func(t *testing.T) { - mocks.dbProvider.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Times(1).Return(mocks.db, nil) - depId, _ := resources.ParseResource("/subscriptions/test-subscription/resourceGroups/test-resource-group/providers/Applications.Datastores/mongoDatabases/test-mongo") mongoResource := buildMongoDBWithRecipe() mongoResource.PortableResourceMetadata.RecipeData = portableresources.RecipeData{} @@ -1085,7 +1050,7 @@ func Test_getResourceDataByID(t *testing.T) { Data: mongoResource, } - mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&mr, nil) + mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&mr, nil) resourceData, err := dp.getResourceDataByID(ctx, depId) require.NoError(t, err) diff --git a/pkg/corerp/frontend/controller/applications/updatefilter.go b/pkg/corerp/frontend/controller/applications/updatefilter.go index 620b5a97c4..2c254f98aa 100644 --- a/pkg/corerp/frontend/controller/applications/updatefilter.go +++ b/pkg/corerp/frontend/controller/applications/updatefilter.go @@ -63,7 +63,7 @@ func CreateAppScopedNamespace(ctx context.Context, newResource, oldResource *dat kubeNamespace = ext.KubernetesNamespace.Namespace } else { // Construct namespace using the namespace specified by environment resource. - envNamespace, err := rp_kube.FindNamespaceByEnvID(ctx, opt.DataProvider, newResource.Properties.Environment) + envNamespace, err := rp_kube.FindNamespaceByEnvID(ctx, opt.StorageClient, newResource.Properties.Environment) if err != nil { return rest.NewBadRequestResponse(fmt.Sprintf("Environment %s could not be constructed: %s", newResource.Properties.Environment, err.Error())), nil diff --git a/pkg/corerp/frontend/controller/applications/updatefilter_test.go b/pkg/corerp/frontend/controller/applications/updatefilter_test.go index 1f6de70971..0e33bbc401 100644 --- a/pkg/corerp/frontend/controller/applications/updatefilter_test.go +++ b/pkg/corerp/frontend/controller/applications/updatefilter_test.go @@ -45,7 +45,6 @@ func TestCreateAppScopedNamespace_valid_namespace(t *testing.T) { opts := ctrl.Options{ StorageClient: tCtx.MockSC, - DataProvider: tCtx.MockSP, KubeClient: k8sutil.NewFakeKubeClient(nil), } @@ -111,8 +110,6 @@ func TestCreateAppScopedNamespace_valid_namespace(t *testing.T) { }, nil }).Times(2) - tCtx.MockSP.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Return(tCtx.MockSC, nil).Times(1) - envdm := &datamodel.Environment{ Properties: datamodel.EnvironmentProperties{ Compute: rpv1.EnvironmentCompute{ @@ -155,7 +152,6 @@ func TestCreateAppScopedNamespace_invalid_property(t *testing.T) { opts := ctrl.Options{ StorageClient: tCtx.MockSC, - DataProvider: tCtx.MockSP, KubeClient: k8sutil.NewFakeKubeClient(nil), } @@ -163,8 +159,6 @@ func TestCreateAppScopedNamespace_invalid_property(t *testing.T) { longAppID := "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/applications.core/applications/this-is-a-very-long-application-name-that-is-invalid" longEnvID := "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/applications.core/environments/this-is-a-very-long-environment-name-that-is-invalid" - tCtx.MockSP.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Return(tCtx.MockSC, nil).Times(1) - envdm := &datamodel.Environment{ Properties: datamodel.EnvironmentProperties{ Compute: rpv1.EnvironmentCompute{ diff --git a/pkg/corerp/setup/setup_test.go b/pkg/corerp/setup/setup_test.go index 109761d95d..203ef7681a 100644 --- a/pkg/corerp/setup/setup_test.go +++ b/pkg/corerp/setup/setup_test.go @@ -26,13 +26,13 @@ import ( "go.uber.org/mock/gomock" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" "github.com/radius-project/radius/pkg/armrpc/builder" apictrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rpctest" "github.com/radius-project/radius/pkg/recipes/controllerconfig" "github.com/radius-project/radius/pkg/sdk" - "github.com/radius-project/radius/pkg/ucp/dataprovider" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/store/inmemory" app_ctrl "github.com/radius-project/radius/pkg/corerp/frontend/controller/applications" ctr_ctrl "github.com/radius-project/radius/pkg/corerp/frontend/controller/containers" @@ -211,15 +211,6 @@ var handlerTests = []rpctest.HandlerTestSpec{ } func TestRouter(t *testing.T) { - mctrl := gomock.NewController(t) - - mockSP := dataprovider.NewMockDataStorageProvider(mctrl) - mockSC := store.NewMockStorageClient(mctrl) - - mockSC.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(&store.Object{}, nil).AnyTimes() - mockSC.EXPECT().Save(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() - mockSP.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Return(store.StorageClient(mockSC), nil).AnyTimes() - conn, err := sdk.NewDirectConnection("http://localhost:9000/apis/api.ucp.dev/v1alpha3") require.NoError(t, err) cfg := &controllerconfig.RecipeControllerConfig{ @@ -232,6 +223,14 @@ func TestRouter(t *testing.T) { r := chi.NewRouter() validator, err := builder.NewOpenAPIValidator(ctx, "/api.ucp.dev", "applications.core") require.NoError(t, err) - return r, nsBuilder.ApplyAPIHandlers(ctx, r, apictrl.Options{PathBase: "/api.ucp.dev", DataProvider: mockSP}, validator) + + options := apictrl.Options{ + Address: "localhost:9000", + PathBase: "/api.ucp.dev", + StorageClient: inmemory.NewClient(), + StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), + } + + return r, nsBuilder.ApplyAPIHandlers(ctx, r, options, validator) }) } diff --git a/pkg/daprrp/setup/setup_test.go b/pkg/daprrp/setup/setup_test.go index 9bcdc2efb6..b55cd010d6 100644 --- a/pkg/daprrp/setup/setup_test.go +++ b/pkg/daprrp/setup/setup_test.go @@ -26,13 +26,13 @@ import ( "go.uber.org/mock/gomock" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" "github.com/radius-project/radius/pkg/armrpc/builder" apictrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rpctest" dapr_ctrl "github.com/radius-project/radius/pkg/daprrp/frontend/controller" "github.com/radius-project/radius/pkg/recipes/controllerconfig" - "github.com/radius-project/radius/pkg/ucp/dataprovider" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/store/inmemory" ) var handlerTests = []rpctest.HandlerTestSpec{ @@ -137,15 +137,6 @@ var handlerTests = []rpctest.HandlerTestSpec{ } func TestRouter(t *testing.T) { - mctrl := gomock.NewController(t) - - mockSP := dataprovider.NewMockDataStorageProvider(mctrl) - mockSC := store.NewMockStorageClient(mctrl) - - mockSC.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(&store.Object{}, nil).AnyTimes() - mockSC.EXPECT().Save(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() - mockSP.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Return(store.StorageClient(mockSC), nil).AnyTimes() - cfg := &controllerconfig.RecipeControllerConfig{} ns := SetupNamespace(cfg) nsBuilder := ns.GenerateBuilder() @@ -154,6 +145,14 @@ func TestRouter(t *testing.T) { r := chi.NewRouter() validator, err := builder.NewOpenAPIValidator(ctx, "/api.ucp.dev", "applications.dapr") require.NoError(t, err) - return r, nsBuilder.ApplyAPIHandlers(ctx, r, apictrl.Options{PathBase: "/api.ucp.dev", DataProvider: mockSP}, validator) + + options := apictrl.Options{ + Address: "localhost:9000", + PathBase: "/api.ucp.dev", + StorageClient: inmemory.NewClient(), + StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), + } + + return r, nsBuilder.ApplyAPIHandlers(ctx, r, options, validator) }) } diff --git a/pkg/datastoresrp/setup/setup_test.go b/pkg/datastoresrp/setup/setup_test.go index 82e994989c..d834cc6fbe 100644 --- a/pkg/datastoresrp/setup/setup_test.go +++ b/pkg/datastoresrp/setup/setup_test.go @@ -26,13 +26,13 @@ import ( "go.uber.org/mock/gomock" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" "github.com/radius-project/radius/pkg/armrpc/builder" apictrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rpctest" ds_ctrl "github.com/radius-project/radius/pkg/datastoresrp/frontend/controller" "github.com/radius-project/radius/pkg/recipes/controllerconfig" - "github.com/radius-project/radius/pkg/ucp/dataprovider" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/store/inmemory" ) var handlerTests = []rpctest.HandlerTestSpec{ @@ -124,15 +124,6 @@ var handlerTests = []rpctest.HandlerTestSpec{ } func TestRouter(t *testing.T) { - mctrl := gomock.NewController(t) - - mockSP := dataprovider.NewMockDataStorageProvider(mctrl) - mockSC := store.NewMockStorageClient(mctrl) - - mockSC.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(&store.Object{}, nil).AnyTimes() - mockSC.EXPECT().Save(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() - mockSP.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Return(store.StorageClient(mockSC), nil).AnyTimes() - cfg := &controllerconfig.RecipeControllerConfig{} ns := SetupNamespace(cfg) nsBuilder := ns.GenerateBuilder() @@ -141,6 +132,14 @@ func TestRouter(t *testing.T) { r := chi.NewRouter() validator, err := builder.NewOpenAPIValidator(ctx, "/api.ucp.dev", "applications.datastores") require.NoError(t, err) - return r, nsBuilder.ApplyAPIHandlers(ctx, r, apictrl.Options{PathBase: "/api.ucp.dev", DataProvider: mockSP}, validator) + + options := apictrl.Options{ + Address: "localhost:9000", + PathBase: "/api.ucp.dev", + StorageClient: inmemory.NewClient(), + StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), + } + + return r, nsBuilder.ApplyAPIHandlers(ctx, r, options, validator) }) } diff --git a/pkg/dynamicrp/options.go b/pkg/dynamicrp/options.go index ce18389617..109c1a00e8 100644 --- a/pkg/dynamicrp/options.go +++ b/pkg/dynamicrp/options.go @@ -50,7 +50,7 @@ type Options struct { StatusManager statusmanager.StatusManager // StorageProvider provides access to the data storage system. - StorageProvider dataprovider.DataStorageProvider + StorageProvider *dataprovider.DataStorageProvider // UCP is the connection to UCP UCP sdk.Connection @@ -65,14 +65,19 @@ func NewOptions(ctx context.Context, config *Config) (*Options, error) { options.QueueProvider = queueprovider.New(config.Queue) options.SecretProvider = secretprovider.NewSecretProvider(config.Secrets) - options.StorageProvider = dataprovider.NewStorageProvider(config.Storage) + options.StorageProvider = dataprovider.DataStorageProviderFromOptions(config.Storage) + + storageClient, err := options.StorageProvider.GetClient(ctx) + if err != nil { + return nil, err + } queueClient, err := options.QueueProvider.GetClient(ctx) if err != nil { return nil, err } - options.StatusManager = statusmanager.New(options.StorageProvider, queueClient, config.Environment.RoleLocation) + options.StatusManager = statusmanager.New(storageClient, queueClient, config.Environment.RoleLocation) var cfg *kube_rest.Config cfg, err = kubeutil.NewClientConfig(&kubeutil.ConfigOptions{ diff --git a/pkg/messagingrp/setup/setup_test.go b/pkg/messagingrp/setup/setup_test.go index ebcc1e5a34..0aa5af07bb 100644 --- a/pkg/messagingrp/setup/setup_test.go +++ b/pkg/messagingrp/setup/setup_test.go @@ -23,13 +23,13 @@ import ( "go.uber.org/mock/gomock" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" "github.com/radius-project/radius/pkg/armrpc/builder" apictrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rpctest" msg_ctrl "github.com/radius-project/radius/pkg/messagingrp/frontend/controller" "github.com/radius-project/radius/pkg/recipes/controllerconfig" - "github.com/radius-project/radius/pkg/ucp/dataprovider" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/store/inmemory" ) var handlerTests = []rpctest.HandlerTestSpec{ @@ -66,15 +66,6 @@ var handlerTests = []rpctest.HandlerTestSpec{ } func TestRouter(t *testing.T) { - mctrl := gomock.NewController(t) - - mockSP := dataprovider.NewMockDataStorageProvider(mctrl) - mockSC := store.NewMockStorageClient(mctrl) - - mockSC.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(&store.Object{}, nil).AnyTimes() - mockSC.EXPECT().Save(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() - mockSP.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Return(store.StorageClient(mockSC), nil).AnyTimes() - cfg := &controllerconfig.RecipeControllerConfig{} ns := SetupNamespace(cfg) nsBuilder := ns.GenerateBuilder() @@ -83,6 +74,14 @@ func TestRouter(t *testing.T) { r := chi.NewRouter() validator, err := builder.NewOpenAPIValidator(ctx, "/api.ucp.dev", "applications.messaging") require.NoError(t, err) - return r, nsBuilder.ApplyAPIHandlers(ctx, r, apictrl.Options{PathBase: "/api.ucp.dev", DataProvider: mockSP}, validator) + + options := apictrl.Options{ + Address: "localhost:9000", + PathBase: "/api.ucp.dev", + StorageClient: inmemory.NewClient(), + StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), + } + + return r, nsBuilder.ApplyAPIHandlers(ctx, r, options, validator) }) } diff --git a/pkg/rp/kube/resources.go b/pkg/rp/kube/resources.go index 81d3edab5e..e55cf914ed 100644 --- a/pkg/rp/kube/resources.go +++ b/pkg/rp/kube/resources.go @@ -25,13 +25,13 @@ import ( "github.com/radius-project/radius/pkg/corerp/api/v20231001preview" cdm "github.com/radius-project/radius/pkg/corerp/datamodel" rpv1 "github.com/radius-project/radius/pkg/rp/v1" - "github.com/radius-project/radius/pkg/ucp/dataprovider" "github.com/radius-project/radius/pkg/ucp/resources" + "github.com/radius-project/radius/pkg/ucp/store" ) // FindNamespaceByEnvID finds the environment-scope Kubernetes namespace. If the environment ID is invalid or the environment is not a Kubernetes // environment, an error is returned. -func FindNamespaceByEnvID(ctx context.Context, sp dataprovider.DataStorageProvider, envID string) (namespace string, err error) { +func FindNamespaceByEnvID(ctx context.Context, storageClient store.StorageClient, envID string) (namespace string, err error) { id, err := resources.ParseResource(envID) if err != nil { return @@ -43,12 +43,7 @@ func FindNamespaceByEnvID(ctx context.Context, sp dataprovider.DataStorageProvid } env := &cdm.Environment{} - client, err := sp.GetStorageClient(ctx, id.Type()) - if err != nil { - return - } - - res, err := client.Get(ctx, id.String()) + res, err := storageClient.Get(ctx, id.String()) if err != nil { return } diff --git a/pkg/rp/kube/resources_test.go b/pkg/rp/kube/resources_test.go index fc3e7d8bfb..738cd318e3 100644 --- a/pkg/rp/kube/resources_test.go +++ b/pkg/rp/kube/resources_test.go @@ -26,7 +26,6 @@ import ( "github.com/radius-project/radius/pkg/corerp/datamodel" rpv1 "github.com/radius-project/radius/pkg/rp/v1" "github.com/radius-project/radius/pkg/to" - "github.com/radius-project/radius/pkg/ucp/dataprovider" "github.com/radius-project/radius/pkg/ucp/store" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -87,12 +86,10 @@ func TestFindNamespaceByEnvID(t *testing.T) { }, } - mockSP := dataprovider.NewMockDataStorageProvider(mctrl) mockSC := store.NewMockStorageClient(mctrl) - - mockSP.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Return(store.StorageClient(mockSC), nil).Times(1) mockSC.EXPECT().Get(gomock.Any(), tc.id, gomock.Any()).Return(fakeStoreObject(envdm), nil).Times(1) - ns, err := FindNamespaceByEnvID(context.Background(), mockSP, testEnvID) + + ns, err := FindNamespaceByEnvID(context.Background(), mockSC, testEnvID) require.NoError(t, err) require.Equal(t, tc.out, ns) }) diff --git a/pkg/rp/util/datastore.go b/pkg/rp/util/datastore.go index 5aa2306221..e0ca7f317b 100644 --- a/pkg/rp/util/datastore.go +++ b/pkg/rp/util/datastore.go @@ -23,14 +23,13 @@ import ( "strings" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" - "github.com/radius-project/radius/pkg/ucp/dataprovider" resources "github.com/radius-project/radius/pkg/ucp/resources" "github.com/radius-project/radius/pkg/ucp/store" ) // FetchScopeResource checks if the given scopeID is a valid resource ID for the given resource type, fetches the resource // from the storage client and returns an error if the resource does not exist. -func FetchScopeResource(ctx context.Context, sp dataprovider.DataStorageProvider, scopeID string, resource v1.DataModelInterface) error { +func FetchScopeResource(ctx context.Context, storageClient store.StorageClient, scopeID string, resource v1.DataModelInterface) error { id, err := resources.ParseResource(scopeID) if err != nil { return v1.NewClientErrInvalidRequest(fmt.Sprintf("%s is not a valid resource id for %s.", scopeID, resource.ResourceTypeName())) @@ -39,12 +38,8 @@ func FetchScopeResource(ctx context.Context, sp dataprovider.DataStorageProvider if !strings.EqualFold(id.Type(), resource.ResourceTypeName()) { return v1.NewClientErrInvalidRequest(fmt.Sprintf("linked %q has invalid %s resource type.", scopeID, resource.ResourceTypeName())) } - sc, err := sp.GetStorageClient(ctx, id.Type()) - if err != nil { - return err - } - res, err := sc.Get(ctx, id.String()) + res, err := storageClient.Get(ctx, id.String()) if errors.Is(&store.ErrNotFound{ID: id.String()}, err) { return v1.NewClientErrInvalidRequest(fmt.Sprintf("linked resource %s does not exist", scopeID)) } diff --git a/pkg/server/apiservice.go b/pkg/server/apiservice.go index b92d6fe2f2..698f935ba8 100644 --- a/pkg/server/apiservice.go +++ b/pkg/server/apiservice.go @@ -57,6 +57,11 @@ func (s *APIService) Run(ctx context.Context) error { return err } + storageClient, err := s.StorageProvider.GetClient(ctx) + if err != nil { + return err + } + address := fmt.Sprintf("%s:%d", s.Options.Config.Server.Host, s.Options.Config.Server.Port) return s.Start(ctx, server.Options{ Location: s.Options.Config.Env.RoleLocation, @@ -65,8 +70,9 @@ func (s *APIService) Run(ctx context.Context) error { Configure: func(r chi.Router) error { for _, b := range s.handlerBuilder { opts := apictrl.Options{ + Address: address, PathBase: s.Options.Config.Server.PathBase, - DataProvider: s.StorageProvider, + StorageClient: storageClient, KubeClient: s.KubeClient, StatusManager: s.OperationStatusManager, } diff --git a/pkg/server/asyncworker.go b/pkg/server/asyncworker.go index e5703885ec..fd8a162c3e 100644 --- a/pkg/server/asyncworker.go +++ b/pkg/server/asyncworker.go @@ -68,12 +68,17 @@ func (w *AsyncWorker) Run(ctx context.Context) error { return fmt.Errorf("failed to initialize application model: %w", err) } + storageClient, err := w.StorageProvider.GetClient(ctx) + if err != nil { + return err + } + for _, b := range w.handlerBuilder { opts := ctrl.Options{ - DataProvider: w.StorageProvider, - KubeClient: k8s.RuntimeClient, + StorageClient: storageClient, + KubeClient: k8s.RuntimeClient, GetDeploymentProcessor: func() deployment.DeploymentProcessor { - return deployment.NewDeploymentProcessor(appModel, w.StorageProvider, k8s.RuntimeClient, k8s.ClientSet) + return deployment.NewDeploymentProcessor(appModel, storageClient, k8s.RuntimeClient, k8s.ClientSet) }, } diff --git a/pkg/ucp/backend/service.go b/pkg/ucp/backend/service.go index fc6ecc1270..708dca726b 100644 --- a/pkg/ucp/backend/service.go +++ b/pkg/ucp/backend/service.go @@ -80,8 +80,13 @@ func (w *Service) Run(ctx context.Context) error { } } + storageClient, err := w.StorageProvider.GetClient(ctx) + if err != nil { + return err + } + opts := ctrl.Options{ - DataProvider: w.StorageProvider, + StorageClient: storageClient, } defaultDownstream, err := url.Parse(w.config.Routing.DefaultDownstreamEndpoint) @@ -90,7 +95,7 @@ func (w *Service) Run(ctx context.Context) error { } transport := otelhttp.NewTransport(http.DefaultTransport) - err = RegisterControllers(ctx, w.Controllers, w.Options.UCPConnection, transport, opts, defaultDownstream) + err = RegisterControllers(w.Controllers, w.Options.UCPConnection, transport, opts, defaultDownstream) if err != nil { return err } @@ -99,41 +104,41 @@ func (w *Service) Run(ctx context.Context) error { } // 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 { +func RegisterControllers(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) { + err := errors.Join(nil, registry.Register(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) { + err = errors.Join(err, registry.Register(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) { + err = errors.Join(err, registry.Register(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) { + err = errors.Join(err, registry.Register(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) { + err = errors.Join(err, registry.Register(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) { + err = errors.Join(err, registry.Register(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) { + err = errors.Join(err, registry.Register(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) { + err = errors.Join(err, registry.Register(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) { + err = errors.Join(err, registry.Register(datamodel.LocationResourceType, v1.OperationDelete, func(opts ctrl.Options) (ctrl.Controller, error) { return &resourceproviders.LocationDeleteController{BaseController: ctrl.NewBaseAsyncController(opts)}, nil }, opts)) diff --git a/pkg/ucp/dataprovider/factory.go b/pkg/ucp/dataprovider/factory.go index 1d0580336a..e879339d2b 100644 --- a/pkg/ucp/dataprovider/factory.go +++ b/pkg/ucp/dataprovider/factory.go @@ -36,7 +36,7 @@ import ( runtimeclient "sigs.k8s.io/controller-runtime/pkg/client" ) -type storageFactoryFunc func(context.Context, StorageProviderOptions, string) (store.StorageClient, error) +type storageFactoryFunc func(ctx context.Context, options StorageProviderOptions) (store.StorageClient, error) var storageClientFactory = map[StorageProviderType]storageFactoryFunc{ TypeAPIServer: initAPIServerClient, @@ -45,7 +45,7 @@ var storageClientFactory = map[StorageProviderType]storageFactoryFunc{ TypePostgreSQL: initPostgreSQLClient, } -func initAPIServerClient(ctx context.Context, opt StorageProviderOptions, _ string) (store.StorageClient, error) { +func initAPIServerClient(ctx context.Context, opt StorageProviderOptions) (store.StorageClient, error) { if opt.APIServer.Namespace == "" { return nil, errors.New("failed to initialize APIServer client: namespace is required") } @@ -83,7 +83,7 @@ func initAPIServerClient(ctx context.Context, opt StorageProviderOptions, _ stri // InitETCDClient checks if the ETCD client is in memory and if the client is not nil, then it initializes the storage // client and returns an ETCDClient. If either of these conditions are not met, an error is returned. -func InitETCDClient(ctx context.Context, opt StorageProviderOptions, _ string) (store.StorageClient, error) { +func InitETCDClient(ctx context.Context, opt StorageProviderOptions) (store.StorageClient, error) { if !opt.ETCD.InMemory { return nil, errors.New("failed to initialize etcd client: inmemory is the only supported mode for now") } @@ -102,12 +102,12 @@ func InitETCDClient(ctx context.Context, opt StorageProviderOptions, _ string) ( } // initInMemoryClient creates a new in-memory store client. -func initInMemoryClient(ctx context.Context, opt StorageProviderOptions, _ string) (store.StorageClient, error) { +func initInMemoryClient(ctx context.Context, opt StorageProviderOptions) (store.StorageClient, error) { return inmemory.NewClient(), nil } // initPostgreSQLClient creates a new PostgreSQL store client. -func initPostgreSQLClient(ctx context.Context, opt StorageProviderOptions, _ string) (store.StorageClient, error) { +func initPostgreSQLClient(ctx context.Context, opt StorageProviderOptions) (store.StorageClient, error) { if opt.PostgreSQL.URL == "" { return nil, errors.New("failed to initialize PostgreSQL client: URL is required") } diff --git a/pkg/ucp/dataprovider/mock_datastorage_provider.go b/pkg/ucp/dataprovider/mock_datastorage_provider.go deleted file mode 100644 index e270701f4a..0000000000 --- a/pkg/ucp/dataprovider/mock_datastorage_provider.go +++ /dev/null @@ -1,80 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: github.com/radius-project/radius/pkg/ucp/dataprovider (interfaces: DataStorageProvider) -// -// Generated by this command: -// -// mockgen -typed -destination=./mock_datastorage_provider.go -package=dataprovider -self_package github.com/radius-project/radius/pkg/ucp/dataprovider github.com/radius-project/radius/pkg/ucp/dataprovider DataStorageProvider -// - -// Package dataprovider is a generated GoMock package. -package dataprovider - -import ( - context "context" - reflect "reflect" - - store "github.com/radius-project/radius/pkg/ucp/store" - gomock "go.uber.org/mock/gomock" -) - -// MockDataStorageProvider is a mock of DataStorageProvider interface. -type MockDataStorageProvider struct { - ctrl *gomock.Controller - recorder *MockDataStorageProviderMockRecorder -} - -// MockDataStorageProviderMockRecorder is the mock recorder for MockDataStorageProvider. -type MockDataStorageProviderMockRecorder struct { - mock *MockDataStorageProvider -} - -// NewMockDataStorageProvider creates a new mock instance. -func NewMockDataStorageProvider(ctrl *gomock.Controller) *MockDataStorageProvider { - mock := &MockDataStorageProvider{ctrl: ctrl} - mock.recorder = &MockDataStorageProviderMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockDataStorageProvider) EXPECT() *MockDataStorageProviderMockRecorder { - return m.recorder -} - -// GetStorageClient mocks base method. -func (m *MockDataStorageProvider) GetStorageClient(arg0 context.Context, arg1 string) (store.StorageClient, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetStorageClient", arg0, arg1) - ret0, _ := ret[0].(store.StorageClient) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetStorageClient indicates an expected call of GetStorageClient. -func (mr *MockDataStorageProviderMockRecorder) GetStorageClient(arg0, arg1 any) *MockDataStorageProviderGetStorageClientCall { - mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageClient", reflect.TypeOf((*MockDataStorageProvider)(nil).GetStorageClient), arg0, arg1) - return &MockDataStorageProviderGetStorageClientCall{Call: call} -} - -// MockDataStorageProviderGetStorageClientCall wrap *gomock.Call -type MockDataStorageProviderGetStorageClientCall struct { - *gomock.Call -} - -// Return rewrite *gomock.Call.Return -func (c *MockDataStorageProviderGetStorageClientCall) Return(arg0 store.StorageClient, arg1 error) *MockDataStorageProviderGetStorageClientCall { - c.Call = c.Call.Return(arg0, arg1) - return c -} - -// Do rewrite *gomock.Call.Do -func (c *MockDataStorageProviderGetStorageClientCall) Do(f func(context.Context, string) (store.StorageClient, error)) *MockDataStorageProviderGetStorageClientCall { - c.Call = c.Call.Do(f) - return c -} - -// DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockDataStorageProviderGetStorageClientCall) DoAndReturn(f func(context.Context, string) (store.StorageClient, error)) *MockDataStorageProviderGetStorageClientCall { - c.Call = c.Call.DoAndReturn(f) - return c -} diff --git a/pkg/ucp/dataprovider/storageprovider.go b/pkg/ucp/dataprovider/storageprovider.go index ce90641bd5..0fb89234b7 100644 --- a/pkg/ucp/dataprovider/storageprovider.go +++ b/pkg/ucp/dataprovider/storageprovider.go @@ -18,71 +18,119 @@ package dataprovider import ( "context" - "errors" + "fmt" "sync" "github.com/radius-project/radius/pkg/ucp/store" - "github.com/radius-project/radius/pkg/ucp/util" ) -var ( - ErrUnsupportedStorageProvider = errors.New("unsupported storage provider") - ErrStorageNotFound = errors.New("storage provider not found") -) +// DataStorageProvider acts as a factory for storage clients. +// +// Do not use construct this directly: +// +// - Use DataStorageProviderFromOptions instead for production use. +// - Use DataStorageProviderFromMemory or DataStorageProviderFromClient for testing. +type DataStorageProvider struct { + // options configures the settings of the storage provider. + options StorageProviderOptions + + // factory is factory function to create a new storage client. Can be overridden for testing. + factory storageFactoryFunc + + // init is used to guarantee single-initialization of the storage provider. + init sync.RWMutex -var _ DataStorageProvider = (*storageProvider)(nil) + // result is result of invoking the factory (cached). + result result +} + +type result struct { + client store.StorageClient + err error +} -type storageProvider struct { - clients map[string]store.StorageClient - clientsMu sync.RWMutex - options StorageProviderOptions +// DataStorageProviderFromOptions creates a new instance of the DataStorageProvider struct with the given options. +// +// This will used the known factory functions to instantiate the storage client. +func DataStorageProviderFromOptions(options StorageProviderOptions) *DataStorageProvider { + return &DataStorageProvider{options: options} } -// NewStorageProvider creates a new instance of the "storageProvider" struct with the given -// "StorageProviderOptions" and returns it. -func NewStorageProvider(opts StorageProviderOptions) DataStorageProvider { - return &storageProvider{ - clients: map[string]store.StorageClient{}, - options: opts, +// DataStorageProviderFromMemory creates a new instance of the DataStorageProvider struct using the in-memory client. +// +// This will use the ephemeral in-memory storage client. +func DataStorageProviderFromMemory() *DataStorageProvider { + return &DataStorageProvider{options: StorageProviderOptions{Provider: TypeInMemory}} +} + +// DataStorageProviderFromClient creates a new instance of the DataStorageProvider struct with the given client. +// +// This will always return the given client and will not attempt to create a new one. This can be used for testing +// with mocks. +func DataStorageProviderFromClient(client store.StorageClient) *DataStorageProvider { + return &DataStorageProvider{result: result{client: client}} +} + +// GetStorageClient returns a storage client for the given resource type. +func (p *DataStorageProvider) GetClient(ctx context.Context) (store.StorageClient, error) { + // Guarantee single initialization. + p.init.RLock() + result := p.result + p.init.RUnlock() + + if result.client == nil && result.err == nil { + result = p.initialize(ctx) } + + // Invariant, either result.err is set or result.client is set. + if result.err != nil { + return nil, result.err + } + + if result.client == nil { + panic("invariant violated: p.result.client is nil") + } + + return result.client, nil } -// GetStorageClient checks if a StorageClient for the given resourceType already exists in the map, and -// if so, returns it. If not, it creates a new StorageClient using the storageClientFactory and adds it to the map, -// returning it. If an error occurs, it returns an error. -func (p *storageProvider) GetStorageClient(ctx context.Context, resourceType string) (store.StorageClient, error) { - cn := util.NormalizeStringToLower(resourceType) - - p.clientsMu.RLock() - c, ok := p.clients[cn] - p.clientsMu.RUnlock() - if ok { - return c, nil +func (p *DataStorageProvider) initialize(ctx context.Context) result { + p.init.Lock() + defer p.init.Unlock() + + // Invariant: p.result is set when this function exits. + // Invariant: p.result.client is nil or p.result.err is nil when this function exits. + // Invariant: p.result is returned to the caller, so they don't need to retake the lock. + + // Note: this is a double-checked locking pattern. + // + // It's possible that result was set by another goroutine before we acquired the lock. + if p.result.client != nil || p.result.err != nil { + return p.result } - var err error - if fn, ok := storageClientFactory[p.options.Provider]; ok { - // This write lock ensure that storage init function executes one by one and write client - // to map safely. - // CosmosDBStorageClient Init() calls database and collection creation control plane APIs. - // Ideally, such control plane APIs must be idempotent, but we could see unexpected failures - // by calling control plane API concurrently. Even if such issue rarely happens during release - // time, it could make the short-term downtime of the service. - // We expect that GetStorageClient() will be called during the start time. Thus, having a lock won't - // hurt any runtime performance. - p.clientsMu.Lock() - defer p.clientsMu.Unlock() - - if c, ok := p.clients[cn]; ok { - return c, nil - } + // If we get here we have the exclusive lock and need to initialize the storage client. - if c, err = fn(ctx, p.options, cn); err == nil { - p.clients[cn] = c + factory := p.factory + if factory == nil { + fn, ok := storageClientFactory[p.options.Provider] + if !ok { + p.result = result{nil, fmt.Errorf("unsupported storage provider: %s", p.options.Provider)} + return p.result } - } else { - err = ErrUnsupportedStorageProvider + + factory = fn + } + + client, err := factory(ctx, p.options) + if err != nil { + p.result = result{nil, fmt.Errorf("failed to initialize storage client: %w", err)} + return p.result + } else if client == nil { + p.result = result{nil, fmt.Errorf("failed to initialize storage client: provider returned nil")} + return p.result } - return c, err + p.result = result{client, nil} + return p.result } diff --git a/pkg/ucp/dataprovider/storageprovider_test.go b/pkg/ucp/dataprovider/storageprovider_test.go new file mode 100644 index 0000000000..984c73597f --- /dev/null +++ b/pkg/ucp/dataprovider/storageprovider_test.go @@ -0,0 +1,129 @@ +/* +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 dataprovider + +import ( + "context" + "errors" + "testing" + + "github.com/radius-project/radius/pkg/ucp/store" + "github.com/stretchr/testify/require" +) + +func Test_DataStorageProviderFromOptions(t *testing.T) { + options := StorageProviderOptions{Provider: TypeInMemory} + provider := DataStorageProviderFromOptions(options) + + require.NotNil(t, provider) + require.Equal(t, options, provider.options) + + client, err := provider.GetClient(context.Background()) + require.NoError(t, err) + require.NotNil(t, client) +} + +func Test_DataStorageProviderFromMemory(t *testing.T) { + provider := DataStorageProviderFromMemory() + + require.NotNil(t, provider) + require.Equal(t, TypeInMemory, provider.options.Provider) + + client, err := provider.GetClient(context.Background()) + require.NoError(t, err) + require.NotNil(t, client) +} + +func Test_DataStorageProviderFromClient(t *testing.T) { + mockClient := &store.MockStorageClient{} + provider := DataStorageProviderFromClient(mockClient) + + require.NotNil(t, provider) + require.Same(t, mockClient, provider.result.client) + + client, err := provider.GetClient(context.Background()) + require.NoError(t, err) + require.Same(t, client, mockClient) +} + +func Test_GetClient_CachedClient(t *testing.T) { + mockClient := &store.MockStorageClient{} + provider := DataStorageProviderFromOptions(StorageProviderOptions{Provider: "Test"}) + + callCount := 0 + provider.factory = storageFactoryFunc(func(ctx context.Context, options StorageProviderOptions) (store.StorageClient, error) { + callCount++ + return mockClient, nil + }) + + client, err := provider.GetClient(context.Background()) + require.NoError(t, err) + require.Same(t, mockClient, client) + + // Do it twice to ensure the client is cached. + client, err = provider.GetClient(context.Background()) + require.NoError(t, err) + require.Same(t, mockClient, client) + + require.Equal(t, 1, callCount) +} + +func Test_GetClient_CachedError(t *testing.T) { + provider := DataStorageProviderFromOptions(StorageProviderOptions{Provider: "Test"}) + + expectedErr := errors.New("oh noes!") + + callCount := 0 + provider.factory = storageFactoryFunc(func(ctx context.Context, options StorageProviderOptions) (store.StorageClient, error) { + callCount++ + return nil, expectedErr + }) + + client, err := provider.GetClient(context.Background()) + require.Error(t, err) + require.Equal(t, "failed to initialize storage client: oh noes!", err.Error()) + require.Nil(t, client) + + // Do it twice to ensure the client is cached. + client, err = provider.GetClient(context.Background()) + require.Error(t, err) + require.Equal(t, "failed to initialize storage client: oh noes!", err.Error()) + require.Nil(t, client) + + require.Equal(t, 1, callCount) +} + +func TestGetClient_UnsupportedProvider(t *testing.T) { + options := StorageProviderOptions{Provider: "unsupported"} + provider := DataStorageProviderFromOptions(options) + + client, err := provider.GetClient(context.Background()) + + require.Error(t, err) + require.Nil(t, client) + require.Equal(t, "unsupported storage provider: unsupported", err.Error()) +} + +func TestInitialize(t *testing.T) { + options := StorageProviderOptions{Provider: TypeInMemory} + provider := DataStorageProviderFromOptions(options) + + result := provider.initialize(context.Background()) + + require.NoError(t, result.err) + require.NotNil(t, result.client) +} diff --git a/pkg/ucp/dataprovider/types.go b/pkg/ucp/dataprovider/types.go index b6f9e8ce9b..cc577b0d63 100644 --- a/pkg/ucp/dataprovider/types.go +++ b/pkg/ucp/dataprovider/types.go @@ -16,12 +16,6 @@ limitations under the License. package dataprovider -import ( - "context" - - "github.com/radius-project/radius/pkg/ucp/store" -) - // StorageProviderType represents types of storage provider. type StorageProviderType string @@ -38,11 +32,3 @@ const ( // TypePostgreSQL represents the PostgreSQL provider. TypePostgreSQL StorageProviderType = "postgresql" ) - -//go:generate mockgen -typed -destination=./mock_datastorage_provider.go -package=dataprovider -self_package github.com/radius-project/radius/pkg/ucp/dataprovider github.com/radius-project/radius/pkg/ucp/dataprovider DataStorageProvider - -// DataStorageProvider is an interfae to provide storage client. -type DataStorageProvider interface { - // GetStorageClient creates or gets storage client. - GetStorageClient(context.Context, string) (store.StorageClient, error) -} diff --git a/pkg/ucp/frontend/api/routes.go b/pkg/ucp/frontend/api/routes.go index f18a628628..fd84d51aa5 100644 --- a/pkg/ucp/frontend/api/routes.go +++ b/pkg/ucp/frontend/api/routes.go @@ -97,6 +97,7 @@ func Register(ctx context.Context, router chi.Router, planeModules []modules.Ini ParentRouter: router, Path: "/openapi/v2", OperationType: &v1.OperationType{Type: OperationTypeKubernetesOpenAPIV2Doc, Method: v1.OperationGet}, + ResourceType: OperationTypeKubernetesOpenAPIV2Doc, Method: v1.OperationGet, ControllerFactory: kubernetes_ctrl.NewOpenAPIv2Doc, }, @@ -104,6 +105,7 @@ func Register(ctx context.Context, router chi.Router, planeModules []modules.Ini ParentRouter: router, Path: "/openapi/v3", OperationType: &v1.OperationType{Type: OperationTypeKubernetesOpenAPIV3Doc, Method: v1.OperationGet}, + ResourceType: OperationTypeKubernetesOpenAPIV3Doc, Method: v1.OperationGet, ControllerFactory: kubernetes_ctrl.NewOpenAPIv3Doc, }, @@ -111,6 +113,7 @@ func Register(ctx context.Context, router chi.Router, planeModules []modules.Ini ParentRouter: router, Path: options.PathBase, OperationType: &v1.OperationType{Type: OperationTypeKubernetesDiscoveryDoc, Method: v1.OperationGet}, + ResourceType: OperationTypeKubernetesDiscoveryDoc, Method: v1.OperationGet, ControllerFactory: kubernetes_ctrl.NewDiscoveryDoc, }, @@ -134,14 +137,21 @@ func Register(ctx context.Context, router chi.Router, planeModules []modules.Ini ParentRouter: planeCollectionRouter, Method: v1.OperationList, OperationType: &v1.OperationType{Type: OperationTypePlanes, Method: v1.OperationList}, + ResourceType: OperationTypePlanes, ControllerFactory: planes_ctrl.NewListPlanes, }, }...) + storageClient, err := options.DataProvider.GetClient(ctx) + if err != nil { + return err + } + ctrlOptions := controller.Options{ - Address: options.Address, - PathBase: options.PathBase, - DataProvider: options.DataProvider, + Address: options.Address, + PathBase: options.PathBase, + StorageClient: storageClient, + StatusManager: options.StatusManager, } for _, h := range handlerOptions { diff --git a/pkg/ucp/frontend/api/routes_test.go b/pkg/ucp/frontend/api/routes_test.go index d0a7fdc849..32fc581a22 100644 --- a/pkg/ucp/frontend/api/routes_test.go +++ b/pkg/ucp/frontend/api/routes_test.go @@ -23,6 +23,7 @@ import ( "github.com/go-chi/chi/v5" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" "github.com/radius-project/radius/pkg/armrpc/rpctest" "github.com/radius-project/radius/pkg/ucp/dataprovider" "github.com/radius-project/radius/pkg/ucp/frontend/modules" @@ -78,14 +79,11 @@ func Test_Routes(t *testing.T) { }, } - ctrl := gomock.NewController(t) - dataProvider := dataprovider.NewMockDataStorageProvider(ctrl) - dataProvider.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() - options := modules.Options{ - Address: "localhost", - PathBase: pathBase, - DataProvider: dataProvider, + Address: "localhost", + PathBase: pathBase, + DataProvider: dataprovider.DataStorageProviderFromMemory(), + StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), } rpctest.AssertRouters(t, tests, pathBase, "", func(ctx context.Context) (chi.Router, error) { @@ -97,14 +95,11 @@ func Test_Routes(t *testing.T) { func Test_Route_ToModule(t *testing.T) { pathBase := "/some-path-base" - ctrl := gomock.NewController(t) - dataProvider := dataprovider.NewMockDataStorageProvider(ctrl) - dataProvider.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() - options := modules.Options{ - Address: "localhost", - PathBase: pathBase, - DataProvider: dataProvider, + Address: "localhost", + PathBase: pathBase, + DataProvider: dataprovider.DataStorageProviderFromMemory(), + StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), } r := chi.NewRouter() diff --git a/pkg/ucp/frontend/api/server.go b/pkg/ucp/frontend/api/server.go index 8a957c2d89..4f121582c7 100644 --- a/pkg/ucp/frontend/api/server.go +++ b/pkg/ucp/frontend/api/server.go @@ -84,7 +84,7 @@ type ServiceOptions struct { // Service implements the hosting.Service interface for the UCP frontend API. type Service struct { options ServiceOptions - storageProvider dataprovider.DataStorageProvider + storageProvider *dataprovider.DataStorageProvider queueProvider *queueprovider.QueueProvider secretProvider *secretprovider.SecretProvider } @@ -118,7 +118,7 @@ func (s *Service) Name() string { func (s *Service) Initialize(ctx context.Context) (*http.Server, error) { r := chi.NewRouter() - s.storageProvider = dataprovider.NewStorageProvider(s.options.StorageProviderOptions) + s.storageProvider = dataprovider.DataStorageProviderFromOptions(s.options.StorageProviderOptions) s.queueProvider = queueprovider.New(s.options.QueueProviderOptions) s.secretProvider = secretprovider.NewSecretProvider(s.options.SecretProviderOptions) @@ -127,12 +127,17 @@ func (s *Service) Initialize(ctx context.Context) (*http.Server, error) { return nil, err } + storageClient, err := s.storageProvider.GetClient(ctx) + if err != nil { + return nil, err + } + queueClient, err := s.queueProvider.GetClient(ctx) if err != nil { return nil, err } - statusManager := statusmanager.New(s.storageProvider, queueClient, s.options.Location) + statusManager := statusmanager.New(storageClient, queueClient, s.options.Location) moduleOptions := modules.Options{ Address: s.options.Address, @@ -217,7 +222,7 @@ func (s *Service) createPlane(ctx context.Context, plane rest.Plane) error { return fmt.Errorf("invalid plane ID: %s", plane.ID) } - db, err := s.storageProvider.GetStorageClient(ctx, "ucp") + db, err := s.storageProvider.GetClient(ctx) if err != nil { return err } diff --git a/pkg/ucp/frontend/aws/routes.go b/pkg/ucp/frontend/aws/routes.go index 6a3154f23d..6713157d50 100644 --- a/pkg/ucp/frontend/aws/routes.go +++ b/pkg/ucp/frontend/aws/routes.go @@ -102,6 +102,7 @@ func (m *Module) Initialize(ctx context.Context) (http.Handler, error) { ParentRouter: planeCollectionRouter, Method: v1.OperationList, OperationType: &v1.OperationType{Type: datamodel.AWSPlaneResourceType, Method: v1.OperationList}, + ResourceType: datamodel.AWSPlaneResourceType, ControllerFactory: func(opts controller.Options) (controller.Controller, error) { return &planes_ctrl.ListPlanesByType[*datamodel.AWSPlane, datamodel.AWSPlane]{ Operation: controller.NewOperation(opts, planeResourceOptions), @@ -112,6 +113,7 @@ func (m *Module) Initialize(ctx context.Context) (http.Handler, error) { ParentRouter: planeResourceRouter, Method: v1.OperationGet, OperationType: &v1.OperationType{Type: datamodel.AWSPlaneResourceType, Method: v1.OperationGet}, + ResourceType: datamodel.AWSPlaneResourceType, ControllerFactory: func(opts controller.Options) (controller.Controller, error) { return defaultoperation.NewGetResource(opts, planeResourceOptions) }, @@ -120,6 +122,7 @@ func (m *Module) Initialize(ctx context.Context) (http.Handler, error) { ParentRouter: planeResourceRouter, Method: v1.OperationPut, OperationType: &v1.OperationType{Type: datamodel.AWSPlaneResourceType, Method: v1.OperationPut}, + ResourceType: datamodel.AWSPlaneResourceType, ControllerFactory: func(opts controller.Options) (controller.Controller, error) { return defaultoperation.NewDefaultSyncPut(opts, planeResourceOptions) }, @@ -128,6 +131,7 @@ func (m *Module) Initialize(ctx context.Context) (http.Handler, error) { ParentRouter: planeResourceRouter, Method: v1.OperationDelete, OperationType: &v1.OperationType{Type: datamodel.AWSPlaneResourceType, Method: v1.OperationDelete}, + ResourceType: datamodel.AWSPlaneResourceType, ControllerFactory: func(opts controller.Options) (controller.Controller, error) { return defaultoperation.NewDefaultSyncDelete(opts, planeResourceOptions) }, @@ -137,6 +141,7 @@ func (m *Module) Initialize(ctx context.Context) (http.Handler, error) { ParentRouter: server.NewSubrouter(baseRouter, operationResultsPath), Method: v1.OperationGet, OperationType: &v1.OperationType{Type: OperationResultsResourceType, Method: v1.OperationGet}, + ResourceType: OperationResultsResourceType, ControllerFactory: func(opt controller.Options) (controller.Controller, error) { return awsproxy_ctrl.NewGetAWSOperationResults(opt, m.AWSClients) }, @@ -146,6 +151,7 @@ func (m *Module) Initialize(ctx context.Context) (http.Handler, error) { ParentRouter: server.NewSubrouter(baseRouter, operationStatusesPath), Method: v1.OperationGet, OperationType: &v1.OperationType{Type: OperationStatusResourceType, Method: v1.OperationGet}, + ResourceType: OperationStatusResourceType, ControllerFactory: func(opts controller.Options) (controller.Controller, error) { return awsproxy_ctrl.NewGetAWSOperationStatuses(opts, m.AWSClients) }, @@ -159,6 +165,7 @@ func (m *Module) Initialize(ctx context.Context) (http.Handler, error) { ParentRouter: resourceCollectionRouter, Method: v1.OperationList, OperationType: &v1.OperationType{Type: OperationTypeAWSResource, Method: v1.OperationList}, + ResourceType: OperationTypeAWSResource, ControllerFactory: func(opts controller.Options) (controller.Controller, error) { return awsproxy_ctrl.NewListAWSResources(opts, m.AWSClients) }, @@ -168,6 +175,7 @@ func (m *Module) Initialize(ctx context.Context) (http.Handler, error) { Path: "/{resourceName}", Method: v1.OperationPut, OperationType: &v1.OperationType{Type: OperationTypeAWSResource, Method: v1.OperationPut}, + ResourceType: OperationTypeAWSResource, ControllerFactory: func(opts controller.Options) (controller.Controller, error) { return awsproxy_ctrl.NewCreateOrUpdateAWSResource(opts, m.AWSClients) }, @@ -177,6 +185,7 @@ func (m *Module) Initialize(ctx context.Context) (http.Handler, error) { Path: "/{resourceName}", Method: v1.OperationDelete, OperationType: &v1.OperationType{Type: OperationTypeAWSResource, Method: v1.OperationDelete}, + ResourceType: OperationTypeAWSResource, ControllerFactory: func(opts controller.Options) (controller.Controller, error) { return awsproxy_ctrl.NewDeleteAWSResource(opts, m.AWSClients) }, @@ -186,6 +195,7 @@ func (m *Module) Initialize(ctx context.Context) (http.Handler, error) { Path: "/{resourceName}", Method: v1.OperationGet, OperationType: &v1.OperationType{Type: OperationTypeAWSResource, Method: v1.OperationGet}, + ResourceType: OperationTypeAWSResource, ControllerFactory: func(opt controller.Options) (controller.Controller, error) { return awsproxy_ctrl.NewGetAWSResource(opt, m.AWSClients) }, @@ -203,6 +213,7 @@ func (m *Module) Initialize(ctx context.Context) (http.Handler, error) { Path: "/:put", Method: v1.OperationPutImperative, OperationType: &v1.OperationType{Type: OperationTypeAWSResource, Method: v1.OperationPutImperative}, + ResourceType: OperationTypeAWSResource, ControllerFactory: func(opt controller.Options) (controller.Controller, error) { return awsproxy_ctrl.NewCreateOrUpdateAWSResourceWithPost(opt, m.AWSClients) }, @@ -212,6 +223,7 @@ func (m *Module) Initialize(ctx context.Context) (http.Handler, error) { Path: "/:get", Method: v1.OperationGetImperative, OperationType: &v1.OperationType{Type: OperationTypeAWSResource, Method: v1.OperationGetImperative}, + ResourceType: OperationTypeAWSResource, ControllerFactory: func(opt controller.Options) (controller.Controller, error) { return awsproxy_ctrl.NewGetAWSResourceWithPost(opt, m.AWSClients) }, @@ -221,6 +233,7 @@ func (m *Module) Initialize(ctx context.Context) (http.Handler, error) { Path: "/:delete", Method: v1.OperationDeleteImperative, OperationType: &v1.OperationType{Type: OperationTypeAWSResource, Method: v1.OperationDeleteImperative}, + ResourceType: OperationTypeAWSResource, ControllerFactory: func(opts controller.Options) (controller.Controller, error) { return awsproxy_ctrl.NewDeleteAWSResourceWithPost(opts, m.AWSClients) }, @@ -279,10 +292,16 @@ func (m *Module) Initialize(ctx context.Context) (http.Handler, error) { }, }...) + storageClient, err := m.options.DataProvider.GetClient(ctx) + if err != nil { + return nil, err + } + ctrlOpts := controller.Options{ - Address: m.options.Address, - PathBase: m.options.PathBase, - DataProvider: m.options.DataProvider, + Address: m.options.Address, + PathBase: m.options.PathBase, + StorageClient: storageClient, + StatusManager: m.options.StatusManager, } for _, h := range handlerOptions { diff --git a/pkg/ucp/frontend/aws/routes_test.go b/pkg/ucp/frontend/aws/routes_test.go index aacd25032a..5311a646d8 100644 --- a/pkg/ucp/frontend/aws/routes_test.go +++ b/pkg/ucp/frontend/aws/routes_test.go @@ -25,6 +25,7 @@ import ( "go.uber.org/mock/gomock" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" "github.com/radius-project/radius/pkg/armrpc/rpctest" "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" "github.com/radius-project/radius/pkg/ucp/datamodel" @@ -111,8 +112,6 @@ func Test_Routes(t *testing.T) { } ctrl := gomock.NewController(t) - dataProvider := dataprovider.NewMockDataStorageProvider(ctrl) - dataProvider.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() secretClient := secret.NewMockClient(ctrl) secretProvider := secretprovider.NewSecretProvider(secretprovider.SecretProviderOptions{}) @@ -122,13 +121,18 @@ func Test_Routes(t *testing.T) { Address: "localhost", PathBase: pathBase, Config: &hostoptions.UCPConfig{}, - DataProvider: dataProvider, + DataProvider: dataprovider.DataStorageProviderFromMemory(), SecretProvider: secretProvider, + StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), } rpctest.AssertRouters(t, tests, pathBase, "", func(ctx context.Context) (chi.Router, error) { module := NewModule(options) handler, err := module.Initialize(ctx) - return handler.(chi.Router), err + if err != nil { + return nil, err + } + + return handler.(chi.Router), nil }) } diff --git a/pkg/ucp/frontend/azure/routes.go b/pkg/ucp/frontend/azure/routes.go index ca06e44f61..c54cd05fb5 100644 --- a/pkg/ucp/frontend/azure/routes.go +++ b/pkg/ucp/frontend/azure/routes.go @@ -74,6 +74,7 @@ func (m *Module) Initialize(ctx context.Context) (http.Handler, error) { ParentRouter: planeCollectionRouter, Method: v1.OperationList, OperationType: &v1.OperationType{Type: datamodel.AzurePlaneResourceType, Method: v1.OperationList}, + ResourceType: datamodel.AzurePlaneResourceType, ControllerFactory: func(opts controller.Options) (controller.Controller, error) { return &planes_ctrl.ListPlanesByType[*datamodel.AzurePlane, datamodel.AzurePlane]{ Operation: controller.NewOperation(opts, planeResourceOptions), @@ -84,6 +85,7 @@ func (m *Module) Initialize(ctx context.Context) (http.Handler, error) { ParentRouter: planeResourceRouter, Method: v1.OperationGet, OperationType: &v1.OperationType{Type: datamodel.AzurePlaneResourceType, Method: v1.OperationGet}, + ResourceType: datamodel.AzurePlaneResourceType, ControllerFactory: func(opts controller.Options) (controller.Controller, error) { return defaultoperation.NewGetResource(opts, planeResourceOptions) }, @@ -92,6 +94,7 @@ func (m *Module) Initialize(ctx context.Context) (http.Handler, error) { ParentRouter: planeResourceRouter, Method: v1.OperationPut, OperationType: &v1.OperationType{Type: datamodel.AzurePlaneResourceType, Method: v1.OperationPut}, + ResourceType: datamodel.AzurePlaneResourceType, ControllerFactory: func(opts controller.Options) (controller.Controller, error) { return defaultoperation.NewDefaultSyncPut(opts, planeResourceOptions) }, @@ -100,6 +103,7 @@ func (m *Module) Initialize(ctx context.Context) (http.Handler, error) { ParentRouter: planeResourceRouter, Method: v1.OperationDelete, OperationType: &v1.OperationType{Type: datamodel.AzurePlaneResourceType, Method: v1.OperationDelete}, + ResourceType: datamodel.AzurePlaneResourceType, ControllerFactory: func(opts controller.Options) (controller.Controller, error) { return defaultoperation.NewDefaultSyncDelete(opts, planeResourceOptions) }, @@ -156,14 +160,21 @@ func (m *Module) Initialize(ctx context.Context) (http.Handler, error) { ParentRouter: planeResourceRouter, Path: server.CatchAllPath, OperationType: &v1.OperationType{Type: OperationTypeUCPAzureProxy, Method: v1.OperationProxy}, + ResourceType: OperationTypeUCPAzureProxy, ControllerFactory: planes_ctrl.NewProxyController, }, } + storageClient, err := m.options.DataProvider.GetClient(ctx) + if err != nil { + return nil, err + } + ctrlOpts := controller.Options{ - Address: m.options.Address, - PathBase: m.options.PathBase, - DataProvider: m.options.DataProvider, + Address: m.options.Address, + PathBase: m.options.PathBase, + StorageClient: storageClient, + StatusManager: m.options.StatusManager, } for _, h := range handlerOptions { diff --git a/pkg/ucp/frontend/azure/routes_test.go b/pkg/ucp/frontend/azure/routes_test.go index 8690166085..1e0e3a5f8c 100644 --- a/pkg/ucp/frontend/azure/routes_test.go +++ b/pkg/ucp/frontend/azure/routes_test.go @@ -25,6 +25,7 @@ import ( "go.uber.org/mock/gomock" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" "github.com/radius-project/radius/pkg/armrpc/rpctest" "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" "github.com/radius-project/radius/pkg/ucp/datamodel" @@ -85,8 +86,6 @@ func Test_Routes(t *testing.T) { } ctrl := gomock.NewController(t) - dataProvider := dataprovider.NewMockDataStorageProvider(ctrl) - dataProvider.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() secretClient := secret.NewMockClient(ctrl) secretProvider := secretprovider.NewSecretProvider(secretprovider.SecretProviderOptions{}) @@ -96,13 +95,18 @@ func Test_Routes(t *testing.T) { Address: "localhost", PathBase: pathBase, Config: &hostoptions.UCPConfig{}, - DataProvider: dataProvider, + DataProvider: dataprovider.DataStorageProviderFromMemory(), SecretProvider: secretProvider, + StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), } rpctest.AssertRouters(t, tests, pathBase, "", func(ctx context.Context) (chi.Router, error) { module := NewModule(options) handler, err := module.Initialize(ctx) - return handler.(chi.Router), err + if err != nil { + return nil, err + } + + return handler.(chi.Router), nil }) } diff --git a/pkg/ucp/frontend/modules/types.go b/pkg/ucp/frontend/modules/types.go index 6b0a0d6e9a..87abadc2c4 100644 --- a/pkg/ucp/frontend/modules/types.go +++ b/pkg/ucp/frontend/modules/types.go @@ -61,7 +61,7 @@ type Options struct { Location string // DataProvider is the data storage provider. - DataProvider dataprovider.DataStorageProvider + DataProvider *dataprovider.DataStorageProvider // QeueueProvider provides access to the queue for async operations. QueueProvider *queueprovider.QueueProvider diff --git a/pkg/ucp/frontend/radius/routes.go b/pkg/ucp/frontend/radius/routes.go index 2631bd9afd..cee401c79b 100644 --- a/pkg/ucp/frontend/radius/routes.go +++ b/pkg/ucp/frontend/radius/routes.go @@ -64,10 +64,15 @@ func (m *Module) Initialize(ctx context.Context) (http.Handler, error) { return handler } + storageClient, err := m.options.DataProvider.GetClient(ctx) + if err != nil { + return nil, err + } + ctrlOptions := controller.Options{ Address: m.options.Address, PathBase: m.options.PathBase, - DataProvider: m.options.DataProvider, + StorageClient: storageClient, StatusManager: m.options.StatusManager, } diff --git a/pkg/ucp/frontend/radius/routes_test.go b/pkg/ucp/frontend/radius/routes_test.go index c47bb9ab1a..6e26e16adf 100644 --- a/pkg/ucp/frontend/radius/routes_test.go +++ b/pkg/ucp/frontend/radius/routes_test.go @@ -185,8 +185,7 @@ func Test_Routes(t *testing.T) { } ctrl := gomock.NewController(t) - dataProvider := dataprovider.NewMockDataStorageProvider(ctrl) - dataProvider.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() + dataProvider := dataprovider.DataStorageProviderFromMemory() secretClient := secret.NewMockClient(ctrl) secretProvider := secretprovider.NewSecretProvider(secretprovider.SecretProviderOptions{}) @@ -203,6 +202,10 @@ func Test_Routes(t *testing.T) { rpctest.AssertRouters(t, tests, pathBase, "", func(ctx context.Context) (chi.Router, error) { module := NewModule(options) handler, err := module.Initialize(ctx) - return handler.(chi.Router), err + if err != nil { + return nil, err + } + + return handler.(chi.Router), nil }) } diff --git a/pkg/ucp/integrationtests/radius/proxy_test.go b/pkg/ucp/integrationtests/radius/proxy_test.go index 371a79af0a..0cd69ea5fc 100644 --- a/pkg/ucp/integrationtests/radius/proxy_test.go +++ b/pkg/ucp/integrationtests/radius/proxy_test.go @@ -176,7 +176,7 @@ func Test_RadiusPlane_ResourceAsync(t *testing.T) { return result, nil } - client, err := ucp.Clients.StorageProvider.GetStorageClient(ctx, "System.Test/testResources") + client, err := ucp.Clients.StorageProvider.GetClient(ctx) require.NoError(t, err) err = client.Delete(ctx, testResourceID) require.NoError(t, err) diff --git a/pkg/ucp/integrationtests/testrp/async.go b/pkg/ucp/integrationtests/testrp/async.go index 144b598c8c..61043b1092 100644 --- a/pkg/ucp/integrationtests/testrp/async.go +++ b/pkg/ucp/integrationtests/testrp/async.go @@ -60,7 +60,7 @@ func AsyncResource(t *testing.T, ts *testserver.TestServer, rootScope string, pu resourceType := "System.Test/testResources" // We can share the storage provider with the test server. - _, err := ts.Clients.StorageProvider.GetStorageClient(ctx, "System.Test/operationStatuses") + storageClient, err := ts.Clients.StorageProvider.GetClient(ctx) require.NoError(t, err) // Do not share the queue. @@ -73,19 +73,19 @@ func AsyncResource(t *testing.T, ts *testserver.TestServer, rootScope string, pu queueClient, err := queueProvider.GetClient(ctx) require.NoError(t, err) - statusManager := statusmanager.New(ts.Clients.StorageProvider, queueClient, v1.LocationGlobal) + statusManager := statusmanager.New(storageClient, queueClient, v1.LocationGlobal) backendOpts := backend_ctrl.Options{ - DataProvider: ts.Clients.StorageProvider, + StorageClient: storageClient, } - registry := worker.NewControllerRegistry(ts.Clients.StorageProvider) - err = registry.Register(ctx, resourceType, v1.OperationPut, func(opts backend_ctrl.Options) (backend_ctrl.Controller, error) { + registry := worker.NewControllerRegistry() + err = registry.Register(resourceType, v1.OperationPut, func(opts backend_ctrl.Options) (backend_ctrl.Controller, error) { return &BackendFuncController{BaseController: backend_ctrl.NewBaseAsyncController(opts), Func: put}, nil }, backendOpts) require.NoError(t, err) - err = registry.Register(ctx, resourceType, v1.OperationDelete, func(opts backend_ctrl.Options) (backend_ctrl.Controller, error) { + err = registry.Register(resourceType, v1.OperationDelete, func(opts backend_ctrl.Options) (backend_ctrl.Controller, error) { return &BackendFuncController{BaseController: backend_ctrl.NewBaseAsyncController(opts), Func: delete}, nil }, backendOpts) require.NoError(t, err) @@ -100,7 +100,8 @@ func AsyncResource(t *testing.T, ts *testserver.TestServer, rootScope string, pu }() frontendOpts := frontend_ctrl.Options{ - DataProvider: ts.Clients.StorageProvider, + Address: "localhost:8080", + StorageClient: storageClient, StatusManager: statusManager, } diff --git a/pkg/ucp/integrationtests/testrp/sync.go b/pkg/ucp/integrationtests/testrp/sync.go index 53ca8b4438..f720a319ed 100644 --- a/pkg/ucp/integrationtests/testrp/sync.go +++ b/pkg/ucp/integrationtests/testrp/sync.go @@ -23,12 +23,14 @@ import ( "github.com/go-chi/chi/v5" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" frontend_ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/frontend/defaultoperation" "github.com/radius-project/radius/pkg/armrpc/frontend/server" "github.com/radius-project/radius/pkg/armrpc/servicecontext" "github.com/radius-project/radius/pkg/middleware" "github.com/radius-project/radius/pkg/ucp/integrationtests/testserver" + queueprovider "github.com/radius-project/radius/pkg/ucp/queue/provider" "github.com/radius-project/radius/test/testcontext" "github.com/stretchr/testify/require" ) @@ -41,11 +43,29 @@ func SyncResource(t *testing.T, ts *testserver.TestServer, rootScope string) fun r := chi.NewRouter() r.Use(servicecontext.ARMRequestCtx("", v1.LocationGlobal), middleware.LowercaseURLPath) + // We can share the storage provider with the test server. + storageClient, err := ts.Clients.StorageProvider.GetClient(ctx) + require.NoError(t, err) + + // Do not share the queue. + queueOptions := queueprovider.QueueProviderOptions{ + Provider: queueprovider.TypeInmemory, + InMemory: &queueprovider.InMemoryQueueOptions{}, + Name: "System.Test", + } + queueProvider := queueprovider.New(queueOptions) + queueClient, err := queueProvider.GetClient(ctx) + require.NoError(t, err) + + statusManager := statusmanager.New(storageClient, queueClient, v1.LocationGlobal) + ctrlOpts := frontend_ctrl.Options{ - DataProvider: ts.Clients.StorageProvider, + Address: "localhost:8080", + StatusManager: statusManager, + StorageClient: storageClient, } - err := server.ConfigureDefaultHandlers(ctx, r, rootScope, false, "System.Test", nil, ctrlOpts) + err = server.ConfigureDefaultHandlers(ctx, r, rootScope, false, "System.Test", nil, ctrlOpts) require.NoError(t, err) resourceType := "System.Test/testResources" diff --git a/pkg/ucp/integrationtests/testserver/testserver.go b/pkg/ucp/integrationtests/testserver/testserver.go index f1eba83023..218181b40c 100644 --- a/pkg/ucp/integrationtests/testserver/testserver.go +++ b/pkg/ucp/integrationtests/testserver/testserver.go @@ -105,7 +105,7 @@ type TestServerClients struct { SecretProvider *secretprovider.SecretProvider // StorageProvider is the storage client provider. - StorageProvider dataprovider.DataStorageProvider + StorageProvider *dataprovider.DataStorageProvider } // TestServerMocks provides access to mock instances created by the TestServer. @@ -150,11 +150,7 @@ func StartWithMocks(t *testing.T, configureModules func(options modules.Options) ctrl := gomock.NewController(t) dataClient := store.NewMockStorageClient(ctrl) - dataProvider := dataprovider.NewMockDataStorageProvider(ctrl) - dataProvider.EXPECT(). - GetStorageClient(gomock.Any(), gomock.Any()). - Return(dataClient, nil). - AnyTimes() + dataProvider := dataprovider.DataStorageProviderFromClient(dataClient) queueClient := queue.NewMockClient(ctrl) queueProvider := queueprovider.New(queueprovider.QueueProviderOptions{Name: "System.Resources"}) @@ -180,7 +176,7 @@ func StartWithMocks(t *testing.T, configureModules func(options modules.Options) require.NoError(t, err, "failed to load OpenAPI spec") options := modules.Options{ - Address: server.URL, + Address: "localhost:9999", // Will be dynamically populated when server is started PathBase: pathBase, Config: &hostoptions.UCPConfig{}, DataProvider: dataProvider, @@ -275,7 +271,7 @@ func StartWithETCD(t *testing.T, configureModules func(options modules.Options) // Generate a random base path to ensure we're handling it correctly. pathBase := "/" + uuid.New().String() - dataProvider := dataprovider.NewStorageProvider(storageOptions) + dataProvider := dataprovider.DataStorageProviderFromOptions(storageOptions) secretProvider := secretprovider.NewSecretProvider(secretOptions) queueProvider := queueprovider.New(queueOptions) @@ -296,7 +292,10 @@ func StartWithETCD(t *testing.T, configureModules func(options modules.Options) connection, err := sdk.NewDirectConnection(address + pathBase) require.NoError(t, err) - statusManager := statusmanager.New(dataProvider, queueClient, v1.LocationGlobal) + storageClient, err := dataProvider.GetClient(ctx) + require.NoError(t, err) + + statusManager := statusmanager.New(storageClient, queueClient, v1.LocationGlobal) specLoader, err := validator.LoadSpec(ctx, "ucp", swagger.SpecFilesUCP, []string{pathBase}, "") require.NoError(t, err, "failed to load OpenAPI spec") @@ -327,8 +326,8 @@ func StartWithETCD(t *testing.T, configureModules func(options modules.Options) defaultDownstream, err := url.Parse(options.Config.Routing.DefaultDownstreamEndpoint) require.NoError(t, err) - registry := worker.NewControllerRegistry(dataProvider) - err = backend.RegisterControllers(ctx, registry, connection, http.DefaultTransport, backend_ctrl.Options{DataProvider: dataProvider}, defaultDownstream) + registry := worker.NewControllerRegistry() + err = backend.RegisterControllers(registry, connection, http.DefaultTransport, backend_ctrl.Options{StorageClient: storageClient}, defaultDownstream) require.NoError(t, err) w := worker.New(worker.Options{}, statusManager, queueClient, registry) diff --git a/pkg/ucp/secret/provider/factory.go b/pkg/ucp/secret/provider/factory.go index 5608171b52..839fb7b9fa 100644 --- a/pkg/ucp/secret/provider/factory.go +++ b/pkg/ucp/secret/provider/factory.go @@ -44,7 +44,7 @@ func initETCDSecretClient(ctx context.Context, opts SecretProviderOptions) (secr // data provider already creates an etcd process which can be re-used instead of a new process for secret. client, err := dataprovider.InitETCDClient(ctx, dataprovider.StorageProviderOptions{ ETCD: opts.ETCD, - }, "") + }) if err != nil { return nil, err } From 5549e1e739cba01e2df310ac371432b73ce3d785 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Dec 2024 16:52:59 -0800 Subject: [PATCH 08/37] Bump aws-actions/configure-aws-credentials from 1.7.0 to 4.0.2 in the all group (#8136) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the all group with 1 update: [aws-actions/configure-aws-credentials](https://github.com/aws-actions/configure-aws-credentials). Updates `aws-actions/configure-aws-credentials` from 1.7.0 to 4.0.2
Release notes

Sourced from aws-actions/configure-aws-credentials's releases.

v4.0.2

See the changelog for details about the changes included in this release.

v4.0.1

See the changelog for details about the changes included in this release.

v4.0.0

See the changelog for details about the changes included in this release.

v4

This tag tracks the latest v4.x.x release

v3.0.2

See the changelog for details about the changes included in this release.

v3.0.1

See the changelog for details about the changes included in this release.

v3.0.0

See the changelog for details about the changes included in this release.

v3

This tag tracks the latest v3.x.x release

v2.2.0

See the changelog for details about the changes included in this release.

v2.1.0

See the changelog for details about the changes included in this release.

v2.0.0

See the changelog for details about the changes included in this release.

v2

This tag tracks the latest v2.x.x release.

Changelog

Sourced from aws-actions/configure-aws-credentials's changelog.

4.0.2 (2024-02-09)

  • Revert 4.0.1 to remove warning

4.0.1 (2023-10-03)

Documentation

  • Throw a warning when customers use long-term credentials.

4.0.0 (2023-09-11)

  • Upgraded runtime to node20 from node16

3.0.2 (2023-09-07)

Bug Fixes

3.0.1 (2023-08-24)

Features

  • Can configure special-characters-workaround to keep retrying credentials if the returned credentials have special characters (Fixes #599)

Bug Fixes

Changes to existing functionality

  • Special characters are now allowed in returned credential variables unless you configure the special-characters-workaround option

3.0.0 (2023-08-21)

Features

  • Can configure max-retries and disable-retry to modify retry functionality when the assume role call fails
  • Set returned credentials as step outputs with output-credentials
  • Clear AWS related environment variables at the start of the action with unset-current-credentials
  • Unique role identifier is now printed in the workflow logs

Bug Fixes

  • Can't use credentials if they contain a special character
  • Retry functionality added when generating the JWT fails
  • Can now use webIdentityTokenFile option
  • Branch name validation too strict
  • JS SDK v2 deprecation warning in workflow logs

Changes to existing functionality

  • Default session duration is now 1 hour in all cases (from 6 hours in some cases)
  • Account ID will not be masked by default in logs

... (truncated)

Commits
  • e3dd6a4 chore: Bump @​types/jest from 29.5.11 to 29.5.12 (#1000)
  • c6c400f chore: Bump @​types/node from 20.11.5 to 20.11.16 (#999)
  • c38ab41 chore: Bump prettier from 3.2.4 to 3.2.5 (#998)
  • 2071ebe chore: Bump @​types/node from 20.11.3 to 20.11.5 (#986)
  • 44112af chore: Update dist
  • 492c455 chore: Bump @​aws-sdk/client-sts from 3.490.0 to 3.496.0 (#982)
  • 13e074e chore: Update dist
  • 5a676ce chore: Bump @​smithy/property-provider from 2.0.17 to 2.1.1 (#985)
  • e43a696 chore: Bump ts-jest from 29.1.1 to 29.1.2 (#983)
  • eb98af5 chore: Bump prettier from 3.2.2 to 3.2.4 (#981)
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=aws-actions/configure-aws-credentials&package-manager=github_actions&previous-version=1.7.0&new-version=4.0.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/functional-test-cloud.yaml | 2 +- .github/workflows/purge-aws-test-resources.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/functional-test-cloud.yaml b/.github/workflows/functional-test-cloud.yaml index bc58f8b2aa..9b7d89faa1 100644 --- a/.github/workflows/functional-test-cloud.yaml +++ b/.github/workflows/functional-test-cloud.yaml @@ -551,7 +551,7 @@ jobs: # The role-to-assume is the role that the github action will assume to execute aws commands and # construct cloud control client in test code. - name: configure aws credentials using assumed role - uses: aws-actions/configure-aws-credentials@v1.7.0 + uses: aws-actions/configure-aws-credentials@v4.0.2 with: role-to-assume: ${{ secrets.AWS_GH_ACTIONS_ROLE }} role-session-name: GitHub_to_AWS_via_FederatedOIDC diff --git a/.github/workflows/purge-aws-test-resources.yaml b/.github/workflows/purge-aws-test-resources.yaml index 32dc3a2359..67e947eaae 100644 --- a/.github/workflows/purge-aws-test-resources.yaml +++ b/.github/workflows/purge-aws-test-resources.yaml @@ -36,7 +36,7 @@ jobs: - name: Checkout the repository uses: actions/checkout@v4 - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v4 + uses: aws-actions/configure-aws-credentials@v4.0.2 with: aws-access-key-id: ${{ secrets.FUNCTEST_AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.FUNCTEST_AWS_SECRET_ACCESS_KEY }} From c4c1c0031d272fa66023ad51ea903c03e0663f5b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Dec 2024 09:23:53 -0800 Subject: [PATCH 09/37] Bump the all group across 1 directory with 21 updates (#8142) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the all group with 14 updates in the / directory: | Package | From | To | | --- | --- | --- | | [github.com/aws/aws-sdk-go-v2/service/ec2](https://github.com/aws/aws-sdk-go-v2) | `1.195.0` | `1.198.0` | | [github.com/charmbracelet/x/ansi](https://github.com/charmbracelet/x) | `0.5.2` | `0.6.0` | | [github.com/go-chi/chi/v5](https://github.com/go-chi/chi) | `5.1.0` | `5.2.0` | | [github.com/goccy/go-yaml](https://github.com/goccy/go-yaml) | `1.15.7` | `1.15.10` | | [go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp](https://github.com/open-telemetry/opentelemetry-go-contrib) | `0.57.0` | `0.58.0` | | [go.opentelemetry.io/contrib/instrumentation/runtime](https://github.com/open-telemetry/opentelemetry-go-contrib) | `0.57.0` | `0.58.0` | | [go.opentelemetry.io/otel/exporters/prometheus](https://github.com/open-telemetry/opentelemetry-go) | `0.54.0` | `0.55.0` | | [go.opentelemetry.io/otel/exporters/zipkin](https://github.com/open-telemetry/opentelemetry-go) | `1.32.0` | `1.33.0` | | [helm.sh/helm/v3](https://github.com/helm/helm) | `3.16.3` | `3.16.4` | | [k8s.io/api](https://github.com/kubernetes/api) | `0.31.3` | `0.32.0` | | [k8s.io/apiextensions-apiserver](https://github.com/kubernetes/apiextensions-apiserver) | `0.31.3` | `0.32.0` | | [k8s.io/cli-runtime](https://github.com/kubernetes/cli-runtime) | `0.31.3` | `0.32.0` | | [k8s.io/kubectl](https://github.com/kubernetes/kubectl) | `0.31.3` | `0.32.0` | | [github.com/hashicorp/terraform-json](https://github.com/hashicorp/terraform-json) | `0.23.0` | `0.24.0` | Updates `github.com/aws/aws-sdk-go-v2/service/ec2` from 1.195.0 to 1.198.0
Commits

Updates `github.com/charmbracelet/x/ansi` from 0.5.2 to 0.6.0
Commits
  • f4f373c fix(ansi): add mouse release with modifiers test
  • e41b682 feat(ansi): TruncateLeft (#301)
  • 7755dca fix(ansi): whitespace handling on the last line (#287)
  • d2b9686 chore: merge pull request #300 from charmbracelet/x/input2
  • 8ea7470 fix(teatest): lint issues (#302)
  • 9ac5034 feat(ansi): define invalid mouse button behavior and add tests
  • 67128e2 feat(input): use ansi.MouseButton for mouse buttons
  • d2630b9 feat(ansi): add mouse button type and its encoding
  • af8254c chore(input): update godoc comments
  • 88394e1 chore(input): go mod tidy
  • Additional commits viewable in compare view

Updates `github.com/go-chi/chi/v5` from 5.1.0 to 5.2.0
Release notes

Sourced from github.com/go-chi/chi/v5's releases.

v5.2.0

What's Changed

New Contributors

Full Changelog: https://github.com/go-chi/chi/compare/v5.1.0...v5.2.0

Commits

Updates `github.com/goccy/go-yaml` from 1.15.7 to 1.15.10
Release notes

Sourced from github.com/goccy/go-yaml's releases.

1.15.10

What's Changed

New Contributors

Full Changelog: https://github.com/goccy/go-yaml/compare/v1.15.9...v1.15.10

1.15.9

What's Changed

New Contributors

Full Changelog: https://github.com/goccy/go-yaml/compare/v1.15.8...v1.15.9

1.15.8

What's Changed

Full Changelog: https://github.com/goccy/go-yaml/compare/v1.15.7...v1.15.8

Commits

Updates `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp` from 0.57.0 to 0.58.0
Release notes

Sourced from go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp's releases.

Release v1.33.0/v0.58.0/v0.27.0/v0.13.0/v0.8.0/v0.6.0/v0.5.0

Overview

Added

  • Added support for providing endpoint, pollingIntervalMs and initialSamplingRate using environment variable OTEL_TRACES_SAMPLER_ARG in go.opentelemetry.io/contrib/samples/jaegerremote. (#6310)
  • Added support exporting logs via OTLP over gRPC in go.opentelemetry.io/contrib/config. (#6340)
  • The go.opentelemetry.io/contrib/bridges/otellogr module. This module provides an OpenTelemetry logging bridge for github.com/go-logr/logr. (#6386)
  • Added SNS instrumentation in go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws. (#6388)

Changed

  • Change the span name to be GET /path so it complies with the OTel HTTP semantic conventions in go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho. (#6365)
  • Record errors instead of setting the gin.errors attribute in go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin. (#6346)
  • The go.opentelemetry.io/contrib/config now supports multiple schemas in subdirectories (i.e. go.opentelemetry.io/contrib/config/v0.3.0) for easier migration. (#6412)

Fixed

  • Fix broken AWS presigned URLs when using instrumentation in go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws. (#5975)
  • Fixed the value for configuring the OTLP exporter to use grpc instead of grpc/protobuf in go.opentelemetry.io/contrib/config. (#6338)
  • Allow marshaling types in go.opentelemetry.io/contrib/config. (#6347)
  • Removed the redundant handling of panic from the HTML function in go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin. (#6373)
  • The code.function attribute emitted by go.opentelemetry.io/contrib/bridges/otelslog now stores just the function name instead the package path-qualified function name. The code.namespace attribute now stores the package path. (#6415)
  • The code.function attribute emitted by go.opentelemetry.io/contrib/bridges/otelzap now stores just the function name instead the package path-qualified function name. The code.namespace attribute now stores the package path. (#6423)

What's Changed

... (truncated)

Changelog

Sourced from go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp's changelog.

[1.33.0/0.58.0/0.27.0/0.13.0/0.8.0/0.6.0/0.5.0] - 2024-12-12

Added

  • Added support for providing endpoint, pollingIntervalMs and initialSamplingRate using environment variable OTEL_TRACES_SAMPLER_ARG in go.opentelemetry.io/contrib/samples/jaegerremote. (#6310)
  • Added support exporting logs via OTLP over gRPC in go.opentelemetry.io/contrib/config. (#6340)
  • The go.opentelemetry.io/contrib/bridges/otellogr module. This module provides an OpenTelemetry logging bridge for github.com/go-logr/logr. (#6386)
  • Added SNS instrumentation in go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws. (#6388)
  • Use a sync.Pool for metric options in go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp. (#6394)
  • Added support for configuring Certificate field when configuring OTLP exporters in go.opentelemetry.io/contrib/config. (#6376)

Changed

  • Change the span name to be GET /path so it complies with the OTel HTTP semantic conventions in go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho. (#6365)
  • Record errors instead of setting the gin.errors attribute in go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin. (#6346)
  • The go.opentelemetry.io/contrib/config now supports multiple schemas in subdirectories (i.e. go.opentelemetry.io/contrib/config/v0.3.0) for easier migration. (#6412)

Fixed

  • Fix broken AWS presigned URLs when using instrumentation in go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws. (#5975)
  • Fixed the value for configuring the OTLP exporter to use grpc instead of grpc/protobuf in go.opentelemetry.io/contrib/config. (#6338)
  • Allow marshaling types in go.opentelemetry.io/contrib/config. (#6347)
  • Removed the redundant handling of panic from the HTML function in go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin. (#6373)
  • The code.function attribute emitted by go.opentelemetry.io/contrib/bridges/otelslog now stores just the function name instead the package path-qualified function name. The code.namespace attribute now stores the package path. (#6415)
  • The code.function attribute emitted by go.opentelemetry.io/contrib/bridges/otelzap now stores just the function name instead the package path-qualified function name. The code.namespace attribute now stores the package path. (#6423)
  • Return an error for nil values when unmarshaling NameStringValuePair in go.opentelemetry.io/contrib/config. (#6425)
Commits
  • f6667f6 Release v1.33.0/v0.58.0/v0.27.0/v0.13.0/v0.8.0/v0.6.0/v0.5.0 (#6435)
  • 741c472 config: add support for certificate configuration (#6376)
  • 0c0b385 Add method and benchmarks for pooling metric options (#6394)
  • 378d704 config: fix panic on nil value in headers name/value pair (#6425)
  • 1606707 chore(deps): update module github.com/goccy/go-json to v0.10.4 (#6430)
  • 3160376 fix(deps): update module github.com/labstack/echo/v4 to v4.13.2 (#6433)
  • 271162e chore(deps): update module sigs.k8s.io/structured-merge-diff/v4 to v4.5.0 (#6...
  • dc41d0e chore(deps): update k8s.io/kube-openapi digest to 5ad02ce (#6432)
  • a71bdc8 otelzap: Split code attributes (#6423)
  • cb458f1 chore(deps): update module golang.org/x/crypto to v0.31.0 (#6426)
  • Additional commits viewable in compare view

Updates `go.opentelemetry.io/contrib/instrumentation/runtime` from 0.57.0 to 0.58.0
Release notes

Sourced from go.opentelemetry.io/contrib/instrumentation/runtime's releases.

Release v1.33.0/v0.58.0/v0.27.0/v0.13.0/v0.8.0/v0.6.0/v0.5.0

Overview

Added

  • Added support for providing endpoint, pollingIntervalMs and initialSamplingRate using environment variable OTEL_TRACES_SAMPLER_ARG in go.opentelemetry.io/contrib/samples/jaegerremote. (#6310)
  • Added support exporting logs via OTLP over gRPC in go.opentelemetry.io/contrib/config. (#6340)
  • The go.opentelemetry.io/contrib/bridges/otellogr module. This module provides an OpenTelemetry logging bridge for github.com/go-logr/logr. (#6386)
  • Added SNS instrumentation in go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws. (#6388)

Changed

  • Change the span name to be GET /path so it complies with the OTel HTTP semantic conventions in go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho. (#6365)
  • Record errors instead of setting the gin.errors attribute in go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin. (#6346)
  • The go.opentelemetry.io/contrib/config now supports multiple schemas in subdirectories (i.e. go.opentelemetry.io/contrib/config/v0.3.0) for easier migration. (#6412)

Fixed

  • Fix broken AWS presigned URLs when using instrumentation in go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws. (#5975)
  • Fixed the value for configuring the OTLP exporter to use grpc instead of grpc/protobuf in go.opentelemetry.io/contrib/config. (#6338)
  • Allow marshaling types in go.opentelemetry.io/contrib/config. (#6347)
  • Removed the redundant handling of panic from the HTML function in go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin. (#6373)
  • The code.function attribute emitted by go.opentelemetry.io/contrib/bridges/otelslog now stores just the function name instead the package path-qualified function name. The code.namespace attribute now stores the package path. (#6415)
  • The code.function attribute emitted by go.opentelemetry.io/contrib/bridges/otelzap now stores just the function name instead the package path-qualified function name. The code.namespace attribute now stores the package path. (#6423)

What's Changed

... (truncated)

Changelog

Sourced from go.opentelemetry.io/contrib/instrumentation/runtime's changelog.

[1.33.0/0.58.0/0.27.0/0.13.0/0.8.0/0.6.0/0.5.0] - 2024-12-12

Added

  • Added support for providing endpoint, pollingIntervalMs and initialSamplingRate using environment variable OTEL_TRACES_SAMPLER_ARG in go.opentelemetry.io/contrib/samples/jaegerremote. (#6310)
  • Added support exporting logs via OTLP over gRPC in go.opentelemetry.io/contrib/config. (#6340)
  • The go.opentelemetry.io/contrib/bridges/otellogr module. This module provides an OpenTelemetry logging bridge for github.com/go-logr/logr. (#6386)
  • Added SNS instrumentation in go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws. (#6388)
  • Use a sync.Pool for metric options in go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp. (#6394)
  • Added support for configuring Certificate field when configuring OTLP exporters in go.opentelemetry.io/contrib/config. (#6376)

Changed

  • Change the span name to be GET /path so it complies with the OTel HTTP semantic conventions in go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho. (#6365)
  • Record errors instead of setting the gin.errors attribute in go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin. (#6346)
  • The go.opentelemetry.io/contrib/config now supports multiple schemas in subdirectories (i.e. go.opentelemetry.io/contrib/config/v0.3.0) for easier migration. (#6412)

Fixed

  • Fix broken AWS presigned URLs when using instrumentation in go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws. (#5975)
  • Fixed the value for configuring the OTLP exporter to use grpc instead of grpc/protobuf in go.opentelemetry.io/contrib/config. (#6338)
  • Allow marshaling types in go.opentelemetry.io/contrib/config. (#6347)
  • Removed the redundant handling of panic from the HTML function in go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin. (#6373)
  • The code.function attribute emitted by go.opentelemetry.io/contrib/bridges/otelslog now stores just the function name instead the package path-qualified function name. The code.namespace attribute now stores the package path. (#6415)
  • The code.function attribute emitted by go.opentelemetry.io/contrib/bridges/otelzap now stores just the function name instead the package path-qualified function name. The code.namespace attribute now stores the package path. (#6423)
  • Return an error for nil values when unmarshaling NameStringValuePair in go.opentelemetry.io/contrib/config. (#6425)
Commits
  • f6667f6 Release v1.33.0/v0.58.0/v0.27.0/v0.13.0/v0.8.0/v0.6.0/v0.5.0 (#6435)
  • 741c472 config: add support for certificate configuration (#6376)
  • 0c0b385 Add method and benchmarks for pooling metric options (#6394)
  • 378d704 config: fix panic on nil value in headers name/value pair (#6425)
  • 1606707 chore(deps): update module github.com/goccy/go-json to v0.10.4 (#6430)
  • 3160376 fix(deps): update module github.com/labstack/echo/v4 to v4.13.2 (#6433)
  • 271162e chore(deps): update module sigs.k8s.io/structured-merge-diff/v4 to v4.5.0 (#6...
  • dc41d0e chore(deps): update k8s.io/kube-openapi digest to 5ad02ce (#6432)
  • a71bdc8 otelzap: Split code attributes (#6423)
  • cb458f1 chore(deps): update module golang.org/x/crypto to v0.31.0 (#6426)
  • Additional commits viewable in compare view

Updates `go.opentelemetry.io/otel` from 1.32.0 to 1.33.0
Changelog

Sourced from go.opentelemetry.io/otel's changelog.

[1.33.0/0.55.0/0.9.0/0.0.12] 2024-12-12

Added

  • Add Reset method to SpanRecorder in go.opentelemetry.io/otel/sdk/trace/tracetest. (#5994)
  • Add EnabledInstrument interface in go.opentelemetry.io/otel/sdk/metric/internal/x. This is an experimental interface that is implemented by synchronous instruments provided by go.opentelemetry.io/otel/sdk/metric. Users can use it to avoid performing computationally expensive operations when recording measurements. It does not fall within the scope of the OpenTelemetry Go versioning and stability policy and it may be changed in backwards incompatible ways or removed in feature releases. (#6016)

Changed

  • The default global API now supports full auto-instrumentation from the go.opentelemetry.io/auto package. See that package for more information. (#5920)
  • Propagate non-retryable error messages to client in go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp. (#5929)
  • Propagate non-retryable error messages to client in go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp. (#5929)
  • Propagate non-retryable error messages to client in go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp. (#5929)
  • Performance improvements for attribute value AsStringSlice, AsFloat64Slice, AsInt64Slice, AsBoolSlice. (#6011)
  • Change EnabledParameters to have a Severity field instead of a getter and setter in go.opentelemetry.io/otel/log. (#6009)

Fixed

  • Fix inconsistent request body closing in go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp. (#5954)
  • Fix inconsistent request body closing in go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp. (#5954)
  • Fix inconsistent request body closing in go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp. (#5954)
  • Fix invalid exemplar keys in go.opentelemetry.io/otel/exporters/prometheus. (#5995)
  • Fix attribute value truncation in go.opentelemetry.io/otel/sdk/trace. (#5997)
  • Fix attribute value truncation in go.opentelemetry.io/otel/sdk/log. (#6032)
Commits
  • 8c38f80 Release v1.33.0 (#6035)
  • aa95895 Fix sdk/log record attr value limit (#6032)
  • 58fdf2a Cache successful requests in lychee (#6030)
  • ac386f3 fix(deps): update golang.org/x/exp digest to 1829a12 (#6031)
  • dd83cac chore(deps): update googleapis to e6fa225 (#6028)
  • de4ff31 fix(deps): update github.com/opentracing-contrib/go-grpc/test digest to ca80a...
  • 0598dae sdk/metric: Add experimental Enabled method to synchronous instruments (#6016)
  • 3bb224b chore(deps): update google.golang.org/genproto/googleapis/rpc digest to a4fef...
  • 13da554 chore(deps): update codecov/codecov-action action to v5.1.1 (#6026)
  • b4a91a2 chore(deps): update module go.opentelemetry.io/auto/sdk to v1.1.0 (#6025)
  • Additional commits viewable in compare view

Updates `go.opentelemetry.io/otel/exporters/prometheus` from 0.54.0 to 0.55.0
Release notes

Sourced from go.opentelemetry.io/o... _Description has been truncated_ Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 80 +++++++++++++------------- go.sum | 174 ++++++++++++++++++++++++++++----------------------------- 2 files changed, 125 insertions(+), 129 deletions(-) diff --git a/go.mod b/go.mod index be2cafaee6..deb3f77454 100644 --- a/go.mod +++ b/go.mod @@ -20,18 +20,18 @@ require ( github.com/aws/aws-sdk-go-v2/credentials v1.17.47 github.com/aws/aws-sdk-go-v2/service/cloudcontrol v1.23.2 github.com/aws/aws-sdk-go-v2/service/cloudformation v1.56.1 - github.com/aws/aws-sdk-go-v2/service/ec2 v1.195.0 + github.com/aws/aws-sdk-go-v2/service/ec2 v1.198.0 github.com/aws/aws-sdk-go-v2/service/ecr v1.36.7 github.com/aws/aws-sdk-go-v2/service/sts v1.33.2 github.com/aws/smithy-go v1.22.1 github.com/charmbracelet/bubbles v0.20.0 github.com/charmbracelet/bubbletea v1.2.4 github.com/charmbracelet/lipgloss v1.0.0 - github.com/charmbracelet/x/ansi v0.5.2 + github.com/charmbracelet/x/ansi v0.6.0 github.com/charmbracelet/x/exp/teatest v0.0.0-20240408110044-525ba71bb562 github.com/dimchansky/utfbom v1.1.1 github.com/fatih/color v1.18.0 - github.com/go-chi/chi/v5 v5.1.0 + github.com/go-chi/chi/v5 v5.2.0 github.com/go-git/go-git/v5 v5.12.0 github.com/go-logr/logr v1.4.2 github.com/go-logr/zapr v1.3.0 @@ -44,7 +44,7 @@ require ( github.com/go-playground/locales v0.14.1 github.com/go-playground/universal-translator v0.18.1 github.com/go-playground/validator/v10 v10.23.0 - github.com/goccy/go-yaml v1.15.7 + github.com/goccy/go-yaml v1.15.10 github.com/gofrs/flock v0.12.1 github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.6.0 @@ -69,28 +69,28 @@ require ( github.com/wI2L/jsondiff v0.6.1 go.etcd.io/etcd/client/v3 v3.5.17 go.etcd.io/etcd/server/v3 v3.5.17 - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 - go.opentelemetry.io/contrib/instrumentation/runtime v0.57.0 - go.opentelemetry.io/otel v1.32.0 - go.opentelemetry.io/otel/exporters/prometheus v0.54.0 - go.opentelemetry.io/otel/exporters/zipkin v1.32.0 - go.opentelemetry.io/otel/metric v1.32.0 - go.opentelemetry.io/otel/sdk v1.32.0 - go.opentelemetry.io/otel/sdk/metric v1.32.0 - go.opentelemetry.io/otel/trace v1.32.0 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 + go.opentelemetry.io/contrib/instrumentation/runtime v0.58.0 + go.opentelemetry.io/otel v1.33.0 + go.opentelemetry.io/otel/exporters/prometheus v0.55.0 + go.opentelemetry.io/otel/exporters/zipkin v1.33.0 + go.opentelemetry.io/otel/metric v1.33.0 + go.opentelemetry.io/otel/sdk v1.33.0 + go.opentelemetry.io/otel/sdk/metric v1.33.0 + go.opentelemetry.io/otel/trace v1.33.0 go.uber.org/atomic v1.11.0 go.uber.org/mock v0.5.0 go.uber.org/zap v1.27.0 golang.org/x/sync v0.10.0 golang.org/x/text v0.21.0 gopkg.in/yaml.v3 v3.0.1 - helm.sh/helm/v3 v3.16.3 - k8s.io/api v0.31.3 - k8s.io/apiextensions-apiserver v0.31.3 - k8s.io/apimachinery v0.31.3 - k8s.io/cli-runtime v0.31.3 - k8s.io/client-go v0.31.3 - k8s.io/kubectl v0.31.3 + helm.sh/helm/v3 v3.16.4 + k8s.io/api v0.32.0 + k8s.io/apiextensions-apiserver v0.32.0 + k8s.io/apimachinery v0.32.0 + k8s.io/cli-runtime v0.32.0 + k8s.io/client-go v0.32.0 + k8s.io/kubectl v0.32.0 oras.land/oras-go/v2 v2.5.0 sigs.k8s.io/controller-runtime v0.19.3 sigs.k8s.io/secrets-store-csi-driver v1.4.7 @@ -152,7 +152,8 @@ require ( github.com/xanzy/ssh-agent v0.3.3 // indirect go.mongodb.org/mongo-driver v1.15.1 // indirect go.opencensus.io v0.24.0 // indirect - golang.org/x/tools v0.25.0 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + golang.org/x/tools v0.26.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/api v0.185.0 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect @@ -237,9 +238,8 @@ require ( github.com/hashicorp/go-version v1.7.0 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect github.com/hashicorp/hcl/v2 v2.21.0 // indirect - github.com/hashicorp/terraform-json v0.23.0 + github.com/hashicorp/terraform-json v0.24.0 github.com/huandu/xstrings v1.5.0 // indirect - github.com/imdario/mergo v0.3.16 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmoiron/sqlx v1.4.0 // indirect @@ -263,7 +263,7 @@ require ( github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/moby/locker v1.0.1 // indirect - github.com/moby/spdystream v0.4.0 // indirect + github.com/moby/spdystream v0.5.0 // indirect github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -280,7 +280,7 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.60.1 // indirect + github.com/prometheus/common v0.61.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/rubenv/sql-migrate v1.7.0 // indirect @@ -298,7 +298,7 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510 // indirect github.com/xlab/treeprint v1.2.0 // indirect - github.com/zclconf/go-cty v1.15.0 // indirect + github.com/zclconf/go-cty v1.15.1 // indirect go.etcd.io/bbolt v1.3.11 // indirect go.etcd.io/etcd/api/v3 v3.5.17 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.17 // indirect @@ -309,34 +309,32 @@ require ( go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect - go.starlark.net v0.0.0-20240925182052-1207426daebd // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.31.0 // indirect golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 golang.org/x/mod v0.21.0 // indirect - golang.org/x/net v0.30.0 // indirect - golang.org/x/oauth2 v0.23.0 // indirect + golang.org/x/net v0.32.0 // indirect + golang.org/x/oauth2 v0.24.0 // indirect golang.org/x/sys v0.28.0 // indirect golang.org/x/term v0.27.0 // indirect golang.org/x/time v0.7.0 // indirect google.golang.org/genproto v0.0.0-20240624140628-dc46fd24d27d // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240624140628-dc46fd24d27d // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 // indirect google.golang.org/grpc v1.65.1 // indirect - google.golang.org/protobuf v1.35.1 // indirect + google.golang.org/protobuf v1.35.2 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect - k8s.io/apiserver v0.31.3 // indirect - k8s.io/component-base v0.31.3 // indirect + k8s.io/apiserver v0.32.0 // indirect + k8s.io/component-base v0.32.0 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20240903163716-9e1beecbcb38 // indirect - k8s.io/utils v0.0.0-20240921022957-49e7df575cb6 // indirect + k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect + k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect oras.land/oras-go v1.2.5 // indirect - sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/kustomize/api v0.17.3 // indirect - sigs.k8s.io/kustomize/kyaml v0.17.2 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect + sigs.k8s.io/kustomize/api v0.18.0 // indirect + sigs.k8s.io/kustomize/kyaml v0.18.1 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/go.sum b/go.sum index ec77548b7f..1aec1ae6f9 100644 --- a/go.sum +++ b/go.sum @@ -301,8 +301,8 @@ github.com/aws/aws-sdk-go-v2/service/cloudcontrol v1.23.2 h1:xUD/6aoYwDsMmVl6J6U github.com/aws/aws-sdk-go-v2/service/cloudcontrol v1.23.2/go.mod h1:NQSFnuiS7N4Leys2Mx/N0UMIWkMsXHBs3HEI4ElCSV8= github.com/aws/aws-sdk-go-v2/service/cloudformation v1.56.1 h1:EqRhsrEoXFFyzcNuqQCF1g9rG9EA8K2EiUj6/eWClgk= github.com/aws/aws-sdk-go-v2/service/cloudformation v1.56.1/go.mod h1:75rrfzgrN4Ol0m9Xo4+8S09KBoGAd1t6eafFHMt5wDI= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.195.0 h1:F3pFi50sK30DZ4IkkNpHwTLGeal5c3nlKuvTgv7xec4= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.195.0/go.mod h1:00zqVNJFK6UASrTnuvjJHJuaqUdkVz5tW8Ip+VhzuNg= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.198.0 h1:ivPJXmGlzAjgy0jLO9naExUWE8IM8lLRcRKLPBEx6Q0= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.198.0/go.mod h1:00zqVNJFK6UASrTnuvjJHJuaqUdkVz5tW8Ip+VhzuNg= github.com/aws/aws-sdk-go-v2/service/ecr v1.36.7 h1:R+5XKIJga2K9Dkj0/iQ6fD/MBGo02oxGGFTc512lK/Q= github.com/aws/aws-sdk-go-v2/service/ecr v1.36.7/go.mod h1:fDPQV/6ONOQOjvtKhtypIy1wcGLcKYtoK/lvZ9fyDGQ= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y= @@ -353,8 +353,8 @@ github.com/charmbracelet/bubbletea v1.2.4 h1:KN8aCViA0eps9SCOThb2/XPIlea3ANJLUkv github.com/charmbracelet/bubbletea v1.2.4/go.mod h1:Qr6fVQw+wX7JkWWkVyXYk/ZUQ92a6XNekLXa3rR18MM= github.com/charmbracelet/lipgloss v1.0.0 h1:O7VkGDvqEdGi93X+DeqsQ7PKHDgtQfF8j8/O2qFMQNg= github.com/charmbracelet/lipgloss v1.0.0/go.mod h1:U5fy9Z+C38obMs+T+tJqst9VGzlOYGj4ri9reL3qUlo= -github.com/charmbracelet/x/ansi v0.5.2 h1:dEa1x2qdOZXD/6439s+wF7xjV+kZLu/iN00GuXXrU9E= -github.com/charmbracelet/x/ansi v0.5.2/go.mod h1:KBUFw1la39nl0dLl10l5ORDAqGXaeurTQmwyyVKse/Q= +github.com/charmbracelet/x/ansi v0.6.0 h1:qOznutrb93gx9oMiGf7caF7bqqubh6YIM0SWKyA08pA= +github.com/charmbracelet/x/ansi v0.6.0/go.mod h1:KBUFw1la39nl0dLl10l5ORDAqGXaeurTQmwyyVKse/Q= github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b h1:MnAMdlwSltxJyULnrYbkZpp4k58Co7Tah3ciKhSNo0Q= github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= github.com/charmbracelet/x/exp/teatest v0.0.0-20240408110044-525ba71bb562 h1:8ePTYvWHJvthAosOpbQ6LO3Oxn/eWl8QZ9s5OTF1jdU= @@ -476,8 +476,8 @@ github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSe github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE= github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8= -github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= -github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-chi/chi/v5 v5.2.0 h1:Aj1EtB0qR2Rdo2dG4O94RIU35w2lvQSj6BRA4+qwFL0= +github.com/go-chi/chi/v5 v5.2.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk= github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= @@ -543,8 +543,8 @@ github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= -github.com/goccy/go-yaml v1.15.7 h1:L7XuKpd/A66X4w/dlk08lVfiIADdy79a1AzRoIefC98= -github.com/goccy/go-yaml v1.15.7/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= +github.com/goccy/go-yaml v1.15.10 h1:9exV2CDYm/FWHPptIIgcDiPQS+X/4uTR+HEl+GF9xJU= +github.com/goccy/go-yaml v1.15.10/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= @@ -639,8 +639,8 @@ github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 h1:5iH8iuqE5apketRbSFBy+X1V0o+l+8NF1avt4HWl7cA= -github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= @@ -720,14 +720,12 @@ github.com/hashicorp/terraform-config-inspect v0.0.0-20240607080351-271db412dbcb github.com/hashicorp/terraform-config-inspect v0.0.0-20240607080351-271db412dbcb/go.mod h1:Gz/z9Hbn+4KSp8A2FBtNszfLSdT2Tn/uAKGuVqqWmDI= github.com/hashicorp/terraform-exec v0.21.0 h1:uNkLAe95ey5Uux6KJdua6+cv8asgILFVWkd/RG0D2XQ= github.com/hashicorp/terraform-exec v0.21.0/go.mod h1:1PPeMYou+KDUSSeRE9szMZ/oHf4fYUmB923Wzbq1ICg= -github.com/hashicorp/terraform-json v0.23.0 h1:sniCkExU4iKtTADReHzACkk8fnpQXrdD2xoR+lppBkI= -github.com/hashicorp/terraform-json v0.23.0/go.mod h1:MHdXbBAbSg0GvzuWazEGKAn/cyNfIB7mN6y7KJN6y2c= +github.com/hashicorp/terraform-json v0.24.0 h1:rUiyF+x1kYawXeRth6fKFm/MdfBS6+lW4NbeATsYz8Q= +github.com/hashicorp/terraform-json v0.24.0/go.mod h1:Nfj5ubo9xbu9uiAoZVBsNOjvNKB66Oyrvtit74kC7ow= github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= -github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= @@ -824,8 +822,8 @@ github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zx github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= -github.com/moby/spdystream v0.4.0 h1:Vy79D6mHeJJjiPdFEL2yku1kl0chZpJfZcPpb16BRl8= -github.com/moby/spdystream v0.4.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= +github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU= +github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78= github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= @@ -857,10 +855,10 @@ github.com/novln/docker-parser v1.0.0/go.mod h1:oCeM32fsoUwkwByB5wVjsrsVQySzPWkl github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4= -github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag= -github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8= -github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc= +github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= +github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= +github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= +github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= @@ -901,8 +899,8 @@ github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc= -github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= +github.com/prometheus/common v0.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFSRQQ= +github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbpeNH7pAjb/s= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= @@ -915,8 +913,8 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/rubenv/sql-migrate v1.7.0 h1:HtQq1xyTN2ISmQDggnh0c9U3JlP8apWh8YO2jzlXpTI= github.com/rubenv/sql-migrate v1.7.0/go.mod h1:S4wtDEG1CKn+0ShpTtzWhFpHHI5PvCUtiGI+C+Z2THE= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= @@ -1021,8 +1019,8 @@ github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMzt github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= -github.com/zclconf/go-cty v1.15.0 h1:tTCRWxsexYUmtt/wVxgDClUe+uQusuI443uL6e+5sXQ= -github.com/zclconf/go-cty v1.15.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= +github.com/zclconf/go-cty v1.15.1 h1:RgQYm4j2EvoBRXOPxhUvxPzRrGDo1eCOhHXuGfrj5S0= +github.com/zclconf/go-cty v1.15.1/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0= @@ -1052,35 +1050,35 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 h1:9G6E0TXzGFVfTnawRzrPl83iHOAV7L8NJiR8RSGYV1g= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0/go.mod h1:azvtTADFQJA8mX80jIH/akaE7h+dbm/sVuaHqN13w74= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 h1:DheMAlT6POBP+gh8RUH19EOTnQIor5QE0uSRPtzCpSw= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0/go.mod h1:wZcGmeVO9nzP67aYSLDqXNWK87EZWhi7JWj1v7ZXf94= -go.opentelemetry.io/contrib/instrumentation/runtime v0.57.0 h1:kJB5wMVorwre8QzEodzTAbzm9FOOah0zvG+V4abNlEE= -go.opentelemetry.io/contrib/instrumentation/runtime v0.57.0/go.mod h1:Nup4TgnOyEJWmVq9sf/ASH3ZJiAXwWHd5xZCHG7Sg9M= -go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= -go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= +go.opentelemetry.io/contrib/instrumentation/runtime v0.58.0 h1:GrcF8ABgnBHQFgp4zu5/jTSqLkoJ9uiDz2e7eKkjq+w= +go.opentelemetry.io/contrib/instrumentation/runtime v0.58.0/go.mod h1:+kxR5prZLoFAJVXJWZKWO2e4PY2dYyXIRNklBuOyzpM= +go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= +go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= -go.opentelemetry.io/otel/exporters/prometheus v0.54.0 h1:rFwzp68QMgtzu9PgP3jm9XaMICI6TsofWWPcBDKwlsU= -go.opentelemetry.io/otel/exporters/prometheus v0.54.0/go.mod h1:QyjcV9qDP6VeK5qPyKETvNjmaaEc7+gqjh4SS0ZYzDU= -go.opentelemetry.io/otel/exporters/zipkin v1.32.0 h1:6O8HgLHPXtXE9QEKEWkBImL9mEKCGEl+m+OncVO53go= -go.opentelemetry.io/otel/exporters/zipkin v1.32.0/go.mod h1:+MFvorlowjy0iWnsKaNxC1kzczSxe71mw85h4p8yEvg= -go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= -go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= -go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= -go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= -go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= -go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= -go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= -go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= +go.opentelemetry.io/otel/exporters/prometheus v0.55.0 h1:sSPw658Lk2NWAv74lkD3B/RSDb+xRFx46GjkrL3VUZo= +go.opentelemetry.io/otel/exporters/prometheus v0.55.0/go.mod h1:nC00vyCmQixoeaxF6KNyP42II/RHa9UdruK02qBmHvI= +go.opentelemetry.io/otel/exporters/zipkin v1.33.0 h1:aFexjEJIw5kVz6vQwnsqCG/nTV/UpsZh7MtQwGmH1eI= +go.opentelemetry.io/otel/exporters/zipkin v1.33.0/go.mod h1:aYsOzr/SZwZXJM6DJmSP/ST2P7MYxuc0R9RewkFVp9s= +go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ= +go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M= +go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM= +go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM= +go.opentelemetry.io/otel/sdk/metric v1.33.0 h1:Gs5VK9/WUJhNXZgn8MR6ITatvAmKeIuCtNbsP3JkNqU= +go.opentelemetry.io/otel/sdk/metric v1.33.0/go.mod h1:dL5ykHZmm1B1nVRk9dDjChwDmt81MjVp3gLkQRwKf/Q= +go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= +go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= -go.starlark.net v0.0.0-20240925182052-1207426daebd h1:S+EMisJOHklQxnS3kqsY8jl2y5aF0FDEdcLnOw3q22E= -go.starlark.net v0.0.0-20240925182052-1207426daebd/go.mod h1:YKMCv9b1WrfWmeqdV5MAuEHWsu5iC+fe6kYl2sQjdI8= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= @@ -1197,8 +1195,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= -golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= +golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1224,8 +1222,8 @@ golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A= -golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= -golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= +golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1395,8 +1393,8 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE= -golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg= +golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1570,10 +1568,10 @@ google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= google.golang.org/genproto v0.0.0-20240624140628-dc46fd24d27d h1:PksQg4dV6Sem3/HkBX+Ltq8T0ke0PKIRBNBatoDTVls= google.golang.org/genproto v0.0.0-20240624140628-dc46fd24d27d/go.mod h1:s7iA721uChleev562UJO2OYB0PPT9CMFjV+Ce7VJH5M= -google.golang.org/genproto/googleapis/api v0.0.0-20240624140628-dc46fd24d27d h1:Aqf0fiIdUQEj0Gn9mKFFXoQfTTEaNopWpfVyYADxiSg= -google.golang.org/genproto/googleapis/api v0.0.0-20240624140628-dc46fd24d27d/go.mod h1:Od4k8V1LQSizPRUK4OzZ7TBE/20k+jPczUDAEyvn69Y= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7 h1:YcyjlL1PRr2Q17/I0dPk2JmYS5CDXfcdb2Z3YRioEbw= +google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:OCdP9MfskevB/rbYvHTsXTtKC+3bHWajPdoKgjcYkfo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 h1:2035KHhUv+EpyB+hWgJnaWKJOdX1E95w2S8Rr4uWKTs= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1627,8 +1625,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= -google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= +google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1659,8 +1657,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= -helm.sh/helm/v3 v3.16.3 h1:kb8bSxMeRJ+knsK/ovvlaVPfdis0X3/ZhYCSFRP+YmY= -helm.sh/helm/v3 v3.16.3/go.mod h1:zeVWGDR4JJgiRbT3AnNsjYaX8OTJlIE9zC+Q7F7iUSU= +helm.sh/helm/v3 v3.16.4 h1:rBn/h9MACw+QlhxQTjpl8Ifx+VTWaYsw3rguGBYBzr0= +helm.sh/helm/v3 v3.16.4/go.mod h1:k8QPotUt57wWbi90w3LNmg3/MWcLPigVv+0/X4B8BzA= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1668,28 +1666,28 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.31.3 h1:umzm5o8lFbdN/hIXbrK9oRpOproJO62CV1zqxXrLgk8= -k8s.io/api v0.31.3/go.mod h1:UJrkIp9pnMOI9K2nlL6vwpxRzzEX5sWgn8kGQe92kCE= -k8s.io/apiextensions-apiserver v0.31.3 h1:+GFGj2qFiU7rGCsA5o+p/rul1OQIq6oYpQw4+u+nciE= -k8s.io/apiextensions-apiserver v0.31.3/go.mod h1:2DSpFhUZZJmn/cr/RweH1cEVVbzFw9YBu4T+U3mf1e4= -k8s.io/apimachinery v0.31.3 h1:6l0WhcYgasZ/wk9ktLq5vLaoXJJr5ts6lkaQzgeYPq4= -k8s.io/apimachinery v0.31.3/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= -k8s.io/apiserver v0.31.3 h1:+1oHTtCB+OheqFEz375D0IlzHZ5VeQKX1KGXnx+TTuY= -k8s.io/apiserver v0.31.3/go.mod h1:PrxVbebxrxQPFhJk4powDISIROkNMKHibTg9lTRQ0Qg= -k8s.io/cli-runtime v0.31.3 h1:fEQD9Xokir78y7pVK/fCJN090/iYNrLHpFbGU4ul9TI= -k8s.io/cli-runtime v0.31.3/go.mod h1:Q2jkyTpl+f6AtodQvgDI8io3jrfr+Z0LyQBPJJ2Btq8= -k8s.io/client-go v0.31.3 h1:CAlZuM+PH2cm+86LOBemaJI/lQ5linJ6UFxKX/SoG+4= -k8s.io/client-go v0.31.3/go.mod h1:2CgjPUTpv3fE5dNygAr2NcM8nhHzXvxB8KL5gYc3kJs= -k8s.io/component-base v0.31.3 h1:DMCXXVx546Rfvhj+3cOm2EUxhS+EyztH423j+8sOwhQ= -k8s.io/component-base v0.31.3/go.mod h1:xME6BHfUOafRgT0rGVBGl7TuSg8Z9/deT7qq6w7qjIU= +k8s.io/api v0.32.0 h1:OL9JpbvAU5ny9ga2fb24X8H6xQlVp+aJMFlgtQjR9CE= +k8s.io/api v0.32.0/go.mod h1:4LEwHZEf6Q/cG96F3dqR965sYOfmPM7rq81BLgsE0p0= +k8s.io/apiextensions-apiserver v0.32.0 h1:S0Xlqt51qzzqjKPxfgX1xh4HBZE+p8KKBq+k2SWNOE0= +k8s.io/apiextensions-apiserver v0.32.0/go.mod h1:86hblMvN5yxMvZrZFX2OhIHAuFIMJIZ19bTvzkP+Fmw= +k8s.io/apimachinery v0.32.0 h1:cFSE7N3rmEEtv4ei5X6DaJPHHX0C+upp+v5lVPiEwpg= +k8s.io/apimachinery v0.32.0/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= +k8s.io/apiserver v0.32.0 h1:VJ89ZvQZ8p1sLeiWdRJpRD6oLozNZD2+qVSLi+ft5Qs= +k8s.io/apiserver v0.32.0/go.mod h1:HFh+dM1/BE/Hm4bS4nTXHVfN6Z6tFIZPi649n83b4Ag= +k8s.io/cli-runtime v0.32.0 h1:dP+OZqs7zHPpGQMCGAhectbHU2SNCuZtIimRKTv2T1c= +k8s.io/cli-runtime v0.32.0/go.mod h1:Mai8ht2+esoDRK5hr861KRy6z0zHsSTYttNVJXgP3YQ= +k8s.io/client-go v0.32.0 h1:DimtMcnN/JIKZcrSrstiwvvZvLjG0aSxy8PxN8IChp8= +k8s.io/client-go v0.32.0/go.mod h1:boDWvdM1Drk4NJj/VddSLnx59X3OPgwrOo0vGbtq9+8= +k8s.io/component-base v0.32.0 h1:d6cWHZkCiiep41ObYQS6IcgzOUQUNpywm39KVYaUqzU= +k8s.io/component-base v0.32.0/go.mod h1:JLG2W5TUxUu5uDyKiH2R/7NnxJo1HlPoRIIbVLkK5eM= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20240903163716-9e1beecbcb38 h1:1dWzkmJrrprYvjGwh9kEUxmcUV/CtNU8QM7h1FLWQOo= -k8s.io/kube-openapi v0.0.0-20240903163716-9e1beecbcb38/go.mod h1:coRQXBK9NxO98XUv3ZD6AK3xzHCxV6+b7lrquKwaKzA= -k8s.io/kubectl v0.31.3 h1:3r111pCjPsvnR98oLLxDMwAeM6OPGmPty6gSKaLTQes= -k8s.io/kubectl v0.31.3/go.mod h1:lhMECDCbJN8He12qcKqs2QfmVo9Pue30geovBVpH5fs= -k8s.io/utils v0.0.0-20240921022957-49e7df575cb6 h1:MDF6h2H/h4tbzmtIKTuctcwZmY0tY9mD9fNT47QO6HI= -k8s.io/utils v0.0.0-20240921022957-49e7df575cb6/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y= +k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4= +k8s.io/kubectl v0.32.0 h1:rpxl+ng9qeG79YA4Em9tLSfX0G8W0vfaiPVrc/WR7Xw= +k8s.io/kubectl v0.32.0/go.mod h1:qIjSX+QgPQUgdy8ps6eKsYNF+YmFOAO3WygfucIqFiE= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= oras.land/oras-go v1.2.5 h1:XpYuAwAb0DfQsunIyMfeET92emK8km3W4yEzZvUbsTo= oras.land/oras-go v1.2.5/go.mod h1:PuAwRShRZCsZb7g8Ar3jKKQR/2A/qN+pkYxIOd/FAoo= oras.land/oras-go/v2 v2.5.0 h1:o8Me9kLY74Vp5uw07QXPiitjsw7qNXi8Twd+19Zf02c= @@ -1699,15 +1697,15 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/controller-runtime v0.19.3 h1:XO2GvC9OPftRst6xWCpTgBZO04S2cbp0Qqkj8bX1sPw= sigs.k8s.io/controller-runtime v0.19.3/go.mod h1:j4j87DqtsThvwTv5/Tc5NFRyyF/RF0ip4+62tbTSIUM= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/kustomize/api v0.17.3 h1:6GCuHSsxq7fN5yhF2XrC+AAr8gxQwhexgHflOAD/JJU= -sigs.k8s.io/kustomize/api v0.17.3/go.mod h1:TuDH4mdx7jTfK61SQ/j1QZM/QWR+5rmEiNjvYlhzFhc= -sigs.k8s.io/kustomize/kyaml v0.17.2 h1:+AzvoJUY0kq4QAhH/ydPHHMRLijtUKiyVyh7fOSshr0= -sigs.k8s.io/kustomize/kyaml v0.17.2/go.mod h1:9V0mCjIEYjlXuCdYsSXvyoy2BTsLESH7TlGV81S282U= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= +sigs.k8s.io/kustomize/api v0.18.0 h1:hTzp67k+3NEVInwz5BHyzc9rGxIauoXferXyjv5lWPo= +sigs.k8s.io/kustomize/api v0.18.0/go.mod h1:f8isXnX+8b+SGLHQ6yO4JG1rdkZlvhaCf/uZbLVMb0U= +sigs.k8s.io/kustomize/kyaml v0.18.1 h1:WvBo56Wzw3fjS+7vBjN6TeivvpbW9GmRaWZ9CIVmt4E= +sigs.k8s.io/kustomize/kyaml v0.18.1/go.mod h1:C3L2BFVU1jgcddNBE1TxuVLgS46TjObMwW5FT9FcjYo= sigs.k8s.io/secrets-store-csi-driver v1.4.7 h1:AyuwmPTW2GoPD2RjyVD3OrH1J9cdPZx+0h2qJvzbGXs= sigs.k8s.io/secrets-store-csi-driver v1.4.7/go.mod h1:0/wMVOv8qLx7YNVMGU+Sh7S4D6TH6GhyEpouo28OTUU= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA= +sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= From 6281a2f7770adf3836039a690762d43ebd3dbfd7 Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Tue, 17 Dec 2024 10:05:46 -0800 Subject: [PATCH 10/37] Rename database APIs (#8143) # Description This change renames the database APIs to have clearer names. ## Type of change - This pull request is a minor refactor, code cleanup, test improvement, or other maintenance task and doesn't change the functionality of Radius (issue link optional). ## Contributor checklist Please verify that the PR meets the following requirements, where applicable: - [ ] An overview of proposed schema changes is included in a linked GitHub issue. - [ ] A design document PR is created in the [design-notes repository](https://github.com/radius-project/design-notes/), if new APIs are being introduced. - [ ] If applicable, design document has been reviewed and approved by Radius maintainers/approvers. - [ ] A PR for the [samples repository](https://github.com/radius-project/samples) is created, if existing samples are affected by the changes in this PR. - [ ] A PR for the [documentation repository](https://github.com/radius-project/docs) is created, if the changes in this PR affect the documentation or any user facing updates are made. - [ ] A PR for the [recipes repository](https://github.com/radius-project/recipes) is created, if existing recipes are affected by the changes in this PR. Signed-off-by: Ryan Nowak --- build/generate.mk | 4 +- cmd/applications-rp/cmd/root.go | 8 +- cmd/ucpd/cmd/root.go | 8 +- .../asyncoperation/controller/controller.go | 22 +- .../statusmanager/statusmanager.go | 32 +-- .../statusmanager/statusmanager_test.go | 38 ++-- pkg/armrpc/asyncoperation/worker/registry.go | 4 +- .../asyncoperation/worker/registry_test.go | 6 +- pkg/armrpc/asyncoperation/worker/service.go | 14 +- pkg/armrpc/asyncoperation/worker/worker.go | 20 +- .../worker/worker_runoperation_test.go | 46 ++-- .../asyncoperation/worker/worker_test.go | 16 +- pkg/armrpc/builder/builder_test.go | 20 +- pkg/armrpc/builder/namespace_test.go | 4 +- pkg/armrpc/frontend/controller/controller.go | 28 +-- .../frontend/controller/controller_test.go | 32 +-- pkg/armrpc/frontend/controller/operation.go | 24 +- .../defaultasyncdelete_test.go | 12 +- .../defaultoperation/defaultasyncput_test.go | 38 ++-- .../defaultoperation/defaultsyncdelete.go | 6 +- .../defaultsyncdelete_test.go | 14 +- .../defaultoperation/defaultsyncput_test.go | 32 +-- .../defaultoperation/getoperationresult.go | 6 +- .../getoperationresult_test.go | 24 +- .../defaultoperation/getoperationstatus.go | 4 +- .../getoperationstatus_test.go | 22 +- .../defaultoperation/getresource_test.go | 22 +- .../defaultoperation/listresources.go | 8 +- .../defaultoperation/listresources_test.go | 30 +-- .../defaultoperation/resource_test.go | 6 +- pkg/armrpc/frontend/server/handler.go | 7 - pkg/armrpc/frontend/server/handler_test.go | 16 +- pkg/armrpc/frontend/server/service.go | 18 +- pkg/armrpc/hostoptions/hostoptions.go | 2 +- pkg/armrpc/hostoptions/providerconfig.go | 12 +- pkg/armrpc/rpctest/controllers.go | 10 +- .../controller/createorupdateresource.go | 16 +- .../controller/createorupdateresource_test.go | 30 +-- .../backend/controller/deleteresource.go | 8 +- .../backend/controller/deleteresource_test.go | 12 +- .../backend/deployment/deploymentprocessor.go | 18 +- .../deployment/deploymentprocessor_test.go | 212 ++++++++--------- .../controller/applications/getgraph_test.go | 10 +- .../controller/applications/updatefilter.go | 6 +- .../applications/updatefilter_test.go | 40 ++-- .../environments/createorupdateenvironment.go | 2 +- .../createorupdateenvironment_test.go | 112 ++++----- .../environments/getrecipemetadata_test.go | 52 ++--- .../extenders/listsecretsextender_test.go | 30 +-- .../controller/secretstores/kubernetes.go | 6 +- .../secretstores/kubernetes_test.go | 54 ++--- .../secretstores/listsecrets_test.go | 36 +-- pkg/corerp/frontend/controller/util/query.go | 10 +- pkg/corerp/setup/setup_test.go | 10 +- pkg/daprrp/setup/setup_test.go | 10 +- .../listsecretsmongodatabase_test.go | 48 ++-- .../rediscaches/listsecretsrediscache.go | 4 +- .../rediscaches/listsecretsrediscache_test.go | 48 ++-- .../listsecretssqldatabase_test.go | 48 ++-- pkg/datastoresrp/setup/setup_test.go | 10 +- pkg/dynamicrp/backend/service.go | 8 +- pkg/dynamicrp/config.go | 12 +- pkg/dynamicrp/frontend/service.go | 2 +- pkg/dynamicrp/options.go | 18 +- pkg/dynamicrp/server/server.go | 8 +- .../listsecretsrabbitmq_test.go | 28 +-- pkg/messagingrp/setup/setup_test.go | 10 +- .../controller/createorupdateresource.go | 18 +- .../controller/createorupdateresource_test.go | 20 +- .../backend/controller/deleteresource.go | 8 +- .../backend/controller/deleteresource_test.go | 12 +- pkg/recipes/controllerconfig/config.go | 4 +- pkg/recipes/driver/terraform.go | 2 +- pkg/recipes/terraform/config/providers/aws.go | 2 +- .../terraform/config/providers/azure.go | 2 +- .../terraform/config/providers/types.go | 2 +- pkg/recipes/terraform/execute.go | 2 +- pkg/rp/kube/resources.go | 6 +- pkg/rp/kube/resources_test.go | 8 +- pkg/rp/util/datastore.go | 10 +- pkg/server/apiservice.go | 12 +- pkg/server/asyncworker.go | 8 +- .../resourcegroups/trackedresourceprocess.go | 12 +- .../trackedresourceprocess_test.go | 62 ++--- .../resourceproviders/apiversion_delete.go | 4 +- .../resourceproviders/apiversion_put.go | 2 +- .../resourceproviders/location_delete.go | 4 +- .../resourceproviders/location_put.go | 2 +- .../resourceprovider_delete.go | 8 +- .../resourceproviders/resourceprovider_put.go | 2 +- .../resourceproviders/resourcetype_delete.go | 4 +- .../resourceproviders/resourcetype_put.go | 4 +- .../controller/resourceproviders/util.go | 20 +- .../controller/resourceproviders/util_test.go | 14 +- pkg/ucp/backend/service.go | 4 +- pkg/ucp/credentials/aws.go | 6 +- pkg/ucp/credentials/azure.go | 6 +- .../api/ucp.dev/v1alpha1/groupversion_info.go | 0 .../ucp.dev/v1alpha1/queuemessage_types.go | 0 .../api/ucp.dev/v1alpha1/resource_types.go | 0 .../ucp.dev/v1alpha1/zz_generated.deepcopy.go | 0 .../apiserverstore/apiserverclient.go | 92 ++++---- .../apiserverstore/apiserverclient_test.go | 40 ++-- pkg/ucp/{store => database}/client.go | 14 +- pkg/ucp/{store => database}/client_test.go | 2 +- .../databaseutil}/doc.go | 4 +- .../storeutil => database/databaseutil}/id.go | 6 +- .../databaseutil}/id_test.go | 36 +-- pkg/ucp/{store => database}/err.go | 2 +- .../etcdstore/etcdclient.go | 84 +++---- .../etcdstore/etcdclient_test.go | 0 pkg/ucp/{store => database}/filter.go | 2 +- pkg/ucp/{store => database}/filter_test.go | 2 +- .../{store => database}/inmemory/client.go | 92 ++++---- .../inmemory/client_test.go | 0 pkg/ucp/{store => database}/inmemory/doc.go | 0 pkg/ucp/{store => database}/map.go | 2 +- pkg/ucp/{store => database}/map_test.go | 2 +- pkg/ucp/database/mock_client.go | 214 ++++++++++++++++++ pkg/ucp/{store => database}/object.go | 8 +- pkg/ucp/{store => database}/options.go | 44 ++-- .../postgres/postgresclient.go | 112 ++++----- .../postgres/postgresclient_test.go | 0 pkg/ucp/{store => database}/resources.go | 2 +- .../factory.go | 30 +-- .../options.go | 21 +- .../storageprovider.go | 62 ++--- .../storageprovider_test.go | 44 ++-- .../types.go | 14 +- pkg/ucp/frontend/api/routes.go | 10 +- pkg/ucp/frontend/api/routes_test.go | 18 +- pkg/ucp/frontend/api/server.go | 48 ++-- pkg/ucp/frontend/aws/routes.go | 10 +- pkg/ucp/frontend/aws/routes_test.go | 16 +- pkg/ucp/frontend/azure/routes.go | 10 +- pkg/ucp/frontend/azure/routes_test.go | 16 +- .../controller/awsproxy/awsproxytest.go | 10 +- .../createorupdateawsresource_test.go | 8 +- .../createorupdateawsresourcewithpost_test.go | 12 +- .../awsproxy/deleteawsresource_test.go | 4 +- .../deleteawsresourcewithpost_test.go | 6 +- .../awsproxy/getawsoperationresults_test.go | 4 +- .../awsproxy/getawsoperationstatuses_test.go | 6 +- .../awsproxy/getawsresource_test.go | 8 +- .../awsproxy/getawsresourcewithpost_test.go | 10 +- .../awsproxy/listawsresources_test.go | 8 +- .../aws/createorupdateawscredential_test.go | 50 ++-- .../credentials/aws/deleteawscredential.go | 6 +- .../aws/deleteawscredential_test.go | 54 ++--- .../createorupdateazurecredential_test.go | 50 ++-- .../azure/deleteazurecredential.go | 6 +- .../azure/deleteazurecredential_test.go | 54 ++--- .../frontend/controller/planes/listplanes.go | 12 +- .../controller/planes/listplanes_test.go | 22 +- .../controller/planes/listplanesbytype.go | 10 +- .../planes/listplanesbytype_test.go | 14 +- pkg/ucp/frontend/controller/radius/proxy.go | 14 +- .../frontend/controller/radius/proxy_test.go | 84 +++---- .../resourcegroups/listresourcegroups.go | 10 +- .../resourcegroups/listresourcegroups_test.go | 16 +- .../resourcegroups/listresources.go | 12 +- .../resourcegroups/listresources_test.go | 40 ++-- .../controller/resourcegroups/util.go | 30 +-- .../controller/resourcegroups/util_test.go | 118 +++++----- .../getresourceprovidersummary.go | 12 +- .../listresourceprovidersummaries.go | 12 +- pkg/ucp/frontend/modules/types.go | 10 +- pkg/ucp/frontend/radius/routes.go | 12 +- pkg/ucp/frontend/radius/routes_test.go | 16 +- pkg/ucp/hostoptions/providerconfig.go | 12 +- pkg/ucp/integrationtests/aws/awstest.go | 6 +- pkg/ucp/integrationtests/radius/proxy_test.go | 2 +- pkg/ucp/integrationtests/testrp/async.go | 16 +- pkg/ucp/integrationtests/testrp/sync.go | 14 +- .../integrationtests/testserver/testserver.go | 82 +++---- pkg/ucp/queue/apiserver/client.go | 2 +- pkg/ucp/queue/apiserver/client_test.go | 2 +- .../{provider => queueprovider}/factory.go | 4 +- .../{provider => queueprovider}/options.go | 6 +- .../{provider => queueprovider}/provider.go | 6 +- .../provider_test.go | 4 +- .../{provider => queueprovider}/types.go | 2 +- .../{provider => secretprovider}/factory.go | 8 +- .../{provider => secretprovider}/options.go | 6 +- .../{provider => secretprovider}/provider.go | 2 +- .../provider_test.go | 2 +- .../{provider => secretprovider}/types.go | 2 +- pkg/ucp/server/server.go | 48 ++-- pkg/ucp/store/mock_storageClient.go | 214 ------------------ pkg/ucp/trackedresource/update.go | 26 +-- pkg/ucp/trackedresource/update_test.go | 86 +++---- test/ucp/kubeenv/testenv.go | 2 +- test/ucp/storetest/shared.go | 108 ++++----- 193 files changed, 2123 insertions(+), 2141 deletions(-) rename pkg/ucp/{store => database}/apiserverstore/api/ucp.dev/v1alpha1/groupversion_info.go (100%) rename pkg/ucp/{store => database}/apiserverstore/api/ucp.dev/v1alpha1/queuemessage_types.go (100%) rename pkg/ucp/{store => database}/apiserverstore/api/ucp.dev/v1alpha1/resource_types.go (100%) rename pkg/ucp/{store => database}/apiserverstore/api/ucp.dev/v1alpha1/zz_generated.deepcopy.go (100%) rename pkg/ucp/{store => database}/apiserverstore/apiserverclient.go (83%) rename pkg/ucp/{store => database}/apiserverstore/apiserverclient_test.go (97%) rename pkg/ucp/{store => database}/client.go (91%) rename pkg/ucp/{store => database}/client_test.go (99%) rename pkg/ucp/{store/storeutil => database/databaseutil}/doc.go (84%) rename pkg/ucp/{store/storeutil => database/databaseutil}/id.go (97%) rename pkg/ucp/{store/storeutil => database/databaseutil}/id_test.go (95%) rename pkg/ucp/{store => database}/err.go (99%) rename pkg/ucp/{store => database}/etcdstore/etcdclient.go (76%) rename pkg/ucp/{store => database}/etcdstore/etcdclient_test.go (100%) rename pkg/ucp/{store => database}/filter.go (99%) rename pkg/ucp/{store => database}/filter_test.go (99%) rename pkg/ucp/{store => database}/inmemory/client.go (63%) rename pkg/ucp/{store => database}/inmemory/client_test.go (100%) rename pkg/ucp/{store => database}/inmemory/doc.go (100%) rename pkg/ucp/{store => database}/map.go (98%) rename pkg/ucp/{store => database}/map_test.go (99%) create mode 100644 pkg/ucp/database/mock_client.go rename pkg/ucp/{store => database}/object.go (87%) rename pkg/ucp/{store => database}/options.go (77%) rename pkg/ucp/{store => database}/postgres/postgresclient.go (70%) rename pkg/ucp/{store => database}/postgres/postgresclient_test.go (100%) rename pkg/ucp/{store => database}/resources.go (97%) rename pkg/ucp/{dataprovider => databaseprovider}/factory.go (74%) rename pkg/ucp/{dataprovider => databaseprovider}/options.go (81%) rename pkg/ucp/{dataprovider => databaseprovider}/storageprovider.go (51%) rename pkg/ucp/{dataprovider => databaseprovider}/storageprovider_test.go (63%) rename pkg/ucp/{dataprovider => databaseprovider}/types.go (71%) rename pkg/ucp/queue/{provider => queueprovider}/factory.go (95%) rename pkg/ucp/queue/{provider => queueprovider}/options.go (92%) rename pkg/ucp/queue/{provider => queueprovider}/provider.go (92%) rename pkg/ucp/queue/{provider => queueprovider}/provider_test.go (94%) rename pkg/ucp/queue/{provider => queueprovider}/types.go (97%) rename pkg/ucp/secret/{provider => secretprovider}/factory.go (91%) rename pkg/ucp/secret/{provider => secretprovider}/options.go (86%) rename pkg/ucp/secret/{provider => secretprovider}/provider.go (98%) rename pkg/ucp/secret/{provider => secretprovider}/provider_test.go (97%) rename pkg/ucp/secret/{provider => secretprovider}/types.go (97%) delete mode 100644 pkg/ucp/store/mock_storageClient.go diff --git a/build/generate.mk b/build/generate.mk index e0a969f997..13c4fc0a53 100644 --- a/build/generate.mk +++ b/build/generate.mk @@ -62,8 +62,8 @@ generate-controller-gen-installed: .PHONY: generate-ucp-crd generate-ucp-crd: generate-controller-gen-installed ## Generates the CRDs for UCP APIServer store. @echo "$(ARROW) Generating CRDs for ucp.dev..." - controller-gen object:headerFile=./boilerplate.go.txt paths=./pkg/ucp/store/apiserverstore/api/ucp.dev/v1alpha1/... - controller-gen crd paths=./pkg/ucp/store/apiserverstore/api/ucp.dev/v1alpha1/... output:crd:dir=./deploy/Chart/crds/ucpd + controller-gen object:headerFile=./boilerplate.go.txt paths=./pkg/ucp/database/apiserverstore/api/ucp.dev/v1alpha1/... + controller-gen crd paths=./pkg/ucp/database/apiserverstore/api/ucp.dev/v1alpha1/... output:crd:dir=./deploy/Chart/crds/ucpd .PHONY: generate-controller generate-controller: generate-controller-gen-installed ## Generates the CRDs for the Radius controller. diff --git a/cmd/applications-rp/cmd/root.go b/cmd/applications-rp/cmd/root.go index 88650ee966..75241ea968 100644 --- a/cmd/applications-rp/cmd/root.go +++ b/cmd/applications-rp/cmd/root.go @@ -34,7 +34,7 @@ import ( "github.com/radius-project/radius/pkg/trace" "github.com/radius-project/radius/pkg/ucp/data" - "github.com/radius-project/radius/pkg/ucp/dataprovider" + "github.com/radius-project/radius/pkg/ucp/databaseprovider" "github.com/radius-project/radius/pkg/ucp/hosting" "github.com/radius-project/radius/pkg/ucp/ucplog" @@ -79,14 +79,14 @@ var rootCmd = &cobra.Command{ // Must set the logger before using controller-runtime. runtimelog.SetLogger(logger) - if options.Config.StorageProvider.Provider == dataprovider.TypeETCD && - options.Config.StorageProvider.ETCD.InMemory { + if options.Config.DatabaseProvider.Provider == databaseprovider.TypeETCD && + options.Config.DatabaseProvider.ETCD.InMemory { // For in-memory etcd we need to register another service to manage its lifecycle. // // The client will be initialized asynchronously. logger.Info("Enabled in-memory etcd") client := hosting.NewAsyncValue[etcdclient.Client]() - options.Config.StorageProvider.ETCD.Client = client + options.Config.DatabaseProvider.ETCD.Client = client options.Config.SecretProvider.ETCD.Client = client hostingSvc = append(hostingSvc, data.NewEmbeddedETCDService(data.EmbeddedETCDServiceOptions{ClientConfigSink: client})) diff --git a/cmd/ucpd/cmd/root.go b/cmd/ucpd/cmd/root.go index c6672ea03f..c11ec85178 100644 --- a/cmd/ucpd/cmd/root.go +++ b/cmd/ucpd/cmd/root.go @@ -26,7 +26,7 @@ import ( runtimelog "sigs.k8s.io/controller-runtime/pkg/log" "github.com/radius-project/radius/pkg/armrpc/hostoptions" - "github.com/radius-project/radius/pkg/ucp/dataprovider" + "github.com/radius-project/radius/pkg/ucp/databaseprovider" "github.com/radius-project/radius/pkg/ucp/hosting" "github.com/radius-project/radius/pkg/ucp/server" "github.com/radius-project/radius/pkg/ucp/ucplog" @@ -52,13 +52,13 @@ var rootCmd = &cobra.Command{ // Must set the logger before using controller-runtime. runtimelog.SetLogger(logger) - if options.StorageProviderOptions.Provider == dataprovider.TypeETCD && - options.StorageProviderOptions.ETCD.InMemory { + if options.DatabaseProviderOptions.Provider == databaseprovider.TypeETCD && + options.DatabaseProviderOptions.ETCD.InMemory { // For in-memory etcd we need to register another service to manage its lifecycle. // // The client will be initialized asynchronously. clientconfigSource := hosting.NewAsyncValue[etcdclient.Client]() - options.StorageProviderOptions.ETCD.Client = clientconfigSource + options.DatabaseProviderOptions.ETCD.Client = clientconfigSource options.SecretProviderOptions.ETCD.Client = clientconfigSource } diff --git a/pkg/armrpc/asyncoperation/controller/controller.go b/pkg/armrpc/asyncoperation/controller/controller.go index 1a1ce535b9..a0a89b0720 100644 --- a/pkg/armrpc/asyncoperation/controller/controller.go +++ b/pkg/armrpc/asyncoperation/controller/controller.go @@ -21,15 +21,15 @@ import ( "errors" "github.com/radius-project/radius/pkg/corerp/backend/deployment" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/database" runtimeclient "sigs.k8s.io/controller-runtime/pkg/client" ) // Options represents controller options. type Options struct { - // StorageClient is the data storage client. - StorageClient store.StorageClient + // DatabaseClient is the database client. + DatabaseClient database.Client // KubeClient is the Kubernetes controller runtime client. KubeClient runtimeclient.Client @@ -44,11 +44,11 @@ type Options struct { // Validate validates that required fields are set on the options. func (o Options) Validate() error { var err error - if o.StorageClient == nil { - err = errors.Join(err, errors.New("StorageClient is required")) + if o.DatabaseClient == nil { + err = errors.Join(err, errors.New(".DatabaseClient is required")) } if o.ResourceType == "" { - err = errors.Join(err, errors.New("ResourceType is required")) + err = errors.Join(err, errors.New(".ResourceType is required")) } // KubeClient and GetDeploymentProcessor are not used by the majority of the code, so they @@ -62,8 +62,8 @@ type Controller interface { // Run runs async request operation. Run(ctx context.Context, request *Request) (Result, error) - // StorageClient gets the storage client for resource type. - StorageClient() store.StorageClient + // DatabaseClient gets the database client for resource type. + DatabaseClient() database.Client } // BaseController is the base struct of async operation controller. @@ -76,9 +76,9 @@ func NewBaseAsyncController(options Options) BaseController { return BaseController{options} } -// StorageClient gets storage client for this controller. -func (b *BaseController) StorageClient() store.StorageClient { - return b.options.StorageClient +// DatabaseClient gets database client for this controller. +func (b *BaseController) DatabaseClient() database.Client { + return b.options.DatabaseClient } // KubeClient gets Kubernetes client for this controller. diff --git a/pkg/armrpc/asyncoperation/statusmanager/statusmanager.go b/pkg/armrpc/asyncoperation/statusmanager/statusmanager.go index e87c5f71c1..bc9b2acc1a 100644 --- a/pkg/armrpc/asyncoperation/statusmanager/statusmanager.go +++ b/pkg/armrpc/asyncoperation/statusmanager/statusmanager.go @@ -27,18 +27,18 @@ import ( ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" "github.com/radius-project/radius/pkg/metrics" "github.com/radius-project/radius/pkg/trace" + "github.com/radius-project/radius/pkg/ucp/database" queue "github.com/radius-project/radius/pkg/ucp/queue/client" "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/store" "github.com/google/uuid" ) // statusManager includes the necessary functions to manage asynchronous operations. type statusManager struct { - storageClient store.StorageClient - queue queue.Client - location string + databaseClient database.Client + queue queue.Client + location string } // QueueOperationOptions is the options type provided when queueing an async operation. @@ -64,11 +64,11 @@ type StatusManager interface { } // New creates statusManager instance. -func New(storageClient store.StorageClient, q queue.Client, location string) StatusManager { +func New(databaseClient database.Client, queueClient queue.Client, location string) StatusManager { return &statusManager{ - storageClient: storageClient, - queue: q, - location: location, + databaseClient: databaseClient, + queue: queueClient, + location: location, } } @@ -78,7 +78,7 @@ func (aom *statusManager) operationStatusResourceID(id resources.ID, operationID } // QueueAsyncOperation creates and saves a new status resource with the given parameters in datastore, and queues -// a request message. If an error occurs, the status is deleted using the storeClient. +// a request message. If an error occurs, the status is deleted using the databaseClient. func (aom *statusManager) QueueAsyncOperation(ctx context.Context, sCtx *v1.ARMRequestContext, options QueueOperationOptions) error { ctx, span := trace.StartProducerSpan(ctx, "statusmanager.QueueAsyncOperation publish", trace.FrontendTracerName) defer span.End() @@ -106,8 +106,8 @@ func (aom *statusManager) QueueAsyncOperation(ctx context.Context, sCtx *v1.ARMR ClientObjectID: sCtx.ClientObjectID, } - err := aom.storageClient.Save(ctx, &store.Object{ - Metadata: store.Metadata{ID: opID}, + err := aom.databaseClient.Save(ctx, &database.Object{ + Metadata: database.Metadata{ID: opID}, Data: aos, }) @@ -116,7 +116,7 @@ func (aom *statusManager) QueueAsyncOperation(ctx context.Context, sCtx *v1.ARMR } if err = aom.queueRequestMessage(ctx, sCtx, aos, options.OperationTimeout); err != nil { - delErr := aom.storageClient.Delete(ctx, opID) + delErr := aom.databaseClient.Delete(ctx, opID) if delErr != nil { return delErr } @@ -130,7 +130,7 @@ func (aom *statusManager) QueueAsyncOperation(ctx context.Context, sCtx *v1.ARMR // Get gets a status object from the datastore or an error if the retrieval fails. func (aom *statusManager) Get(ctx context.Context, id resources.ID, operationID uuid.UUID) (*Status, error) { - obj, err := aom.storageClient.Get(ctx, aom.operationStatusResourceID(id, operationID)) + obj, err := aom.databaseClient.Get(ctx, aom.operationStatusResourceID(id, operationID)) if err != nil { return nil, err } @@ -147,7 +147,7 @@ func (aom *statusManager) Get(ctx context.Context, id resources.ID, operationID // given parameters, and saves it back to the store. func (aom *statusManager) Update(ctx context.Context, id resources.ID, operationID uuid.UUID, state v1.ProvisioningState, endTime *time.Time, opError *v1.ErrorDetails) error { opID := aom.operationStatusResourceID(id, operationID) - obj, err := aom.storageClient.Get(ctx, opID) + obj, err := aom.databaseClient.Get(ctx, opID) if err != nil { return err } @@ -170,13 +170,13 @@ func (aom *statusManager) Update(ctx context.Context, id resources.ID, operation obj.Data = s - return aom.storageClient.Save(ctx, obj, store.WithETag(obj.ETag)) + return aom.databaseClient.Save(ctx, obj, database.WithETag(obj.ETag)) } // Delete deletes the operation status resource associated with the given ID and // operationID, and returns an error if unsuccessful. func (aom *statusManager) Delete(ctx context.Context, id resources.ID, operationID uuid.UUID) error { - return aom.storageClient.Delete(ctx, aom.operationStatusResourceID(id, operationID)) + return aom.databaseClient.Delete(ctx, aom.operationStatusResourceID(id, operationID)) } // queueRequestMessage function is to put the async operation message to the queue to be worked on. diff --git a/pkg/armrpc/asyncoperation/statusmanager/statusmanager_test.go b/pkg/armrpc/asyncoperation/statusmanager/statusmanager_test.go index c61872cf3d..c1de8a5bd6 100644 --- a/pkg/armrpc/asyncoperation/statusmanager/statusmanager_test.go +++ b/pkg/armrpc/asyncoperation/statusmanager/statusmanager_test.go @@ -26,17 +26,17 @@ import ( "github.com/google/uuid" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" "github.com/radius-project/radius/pkg/armrpc/rpctest" + "github.com/radius-project/radius/pkg/ucp/database" queue "github.com/radius-project/radius/pkg/ucp/queue/client" "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/store" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" ) type asyncOperationsManagerTest struct { - manager StatusManager - storeClient *store.MockStorageClient - queue *queue.MockClient + manager StatusManager + databaseClient *database.MockClient + queueClient *queue.MockClient } const ( @@ -52,10 +52,10 @@ const ( func setup(tb testing.TB) (asyncOperationsManagerTest, *gomock.Controller) { ctrl := gomock.NewController(tb) - sc := store.NewMockStorageClient(ctrl) + sc := database.NewMockClient(ctrl) enq := queue.NewMockClient(ctrl) aom := New(sc, enq, "test-location") - return asyncOperationsManagerTest{manager: aom, storeClient: sc, queue: enq}, ctrl + return asyncOperationsManagerTest{manager: aom, databaseClient: sc, queueClient: enq}, ctrl } var reqCtx = &v1.ARMRequestContext{ @@ -151,16 +151,16 @@ func TestCreateAsyncOperationStatus(t *testing.T) { aomTest, mctrl := setup(t) defer mctrl.Finish() - aomTest.storeClient.EXPECT().Save(gomock.Any(), gomock.Any(), gomock.Any()).Return(tt.SaveErr) + aomTest.databaseClient.EXPECT().Save(gomock.Any(), gomock.Any(), gomock.Any()).Return(tt.SaveErr) // We can't expect an async operation to be queued if it is not saved to the DB. if tt.SaveErr == nil { - aomTest.queue.EXPECT().Enqueue(gomock.Any(), gomock.Any(), gomock.Any()).Return(tt.EnqueueErr) + aomTest.queueClient.EXPECT().Enqueue(gomock.Any(), gomock.Any(), gomock.Any()).Return(tt.EnqueueErr) } // If there is an error when enqueuing the message, the async operation should be deleted. if tt.EnqueueErr != nil { - aomTest.storeClient.EXPECT().Delete(gomock.Any(), gomock.Any(), gomock.Any()).Return(tt.DeleteErr) + aomTest.databaseClient.EXPECT().Delete(gomock.Any(), gomock.Any(), gomock.Any()).Return(tt.DeleteErr) } options := QueueOperationOptions{ @@ -208,7 +208,7 @@ func TestDeleteAsyncOperationStatus(t *testing.T) { aomTest, mctrl := setup(t) defer mctrl.Finish() - aomTest.storeClient.EXPECT().Delete(gomock.Any(), gomock.Any(), gomock.Any()).Return(tt.DeleteErr) + aomTest.databaseClient.EXPECT().Delete(gomock.Any(), gomock.Any(), gomock.Any()).Return(tt.DeleteErr) rid, err := resources.ParseResource(azureEnvResourceID) require.NoError(t, err) err = aomTest.manager.Delete(context.TODO(), rid, uuid.New()) @@ -226,13 +226,13 @@ func TestGetAsyncOperationStatus(t *testing.T) { getCases := []struct { Desc string GetErr error - Obj *store.Object + Obj *database.Object }{ { Desc: "get_success", GetErr: nil, - Obj: &store.Object{ - Metadata: store.Metadata{ID: opID.String(), ETag: "etag"}, + Obj: &database.Object{ + Metadata: database.Metadata{ID: opID.String(), ETag: "etag"}, Data: testAos, }, }, @@ -248,7 +248,7 @@ func TestGetAsyncOperationStatus(t *testing.T) { aomTest, mctrl := setup(t) defer mctrl.Finish() - aomTest.storeClient. + aomTest.databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any(), gomock.Any()). Return(tt.Obj, tt.GetErr) @@ -275,14 +275,14 @@ func TestUpdateAsyncOperationStatus(t *testing.T) { updateCases := []struct { Desc string GetErr error - Obj *store.Object + Obj *database.Object SaveErr error }{ { Desc: "update_success", GetErr: nil, - Obj: &store.Object{ - Metadata: store.Metadata{ID: opID.String(), ETag: "etag"}, + Obj: &database.Object{ + Metadata: database.Metadata{ID: opID.String(), ETag: "etag"}, Data: testAos, }, SaveErr: nil, @@ -294,13 +294,13 @@ func TestUpdateAsyncOperationStatus(t *testing.T) { aomTest, mctrl := setup(t) defer mctrl.Finish() - aomTest.storeClient. + aomTest.databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any(), gomock.Any()). Return(tt.Obj, tt.GetErr) if tt.GetErr == nil { - aomTest.storeClient. + aomTest.databaseClient. EXPECT(). Save(gomock.Any(), gomock.Any(), gomock.Any()). Return(tt.SaveErr) diff --git a/pkg/armrpc/asyncoperation/worker/registry.go b/pkg/armrpc/asyncoperation/worker/registry.go index eb21ec1042..ddef93093d 100644 --- a/pkg/armrpc/asyncoperation/worker/registry.go +++ b/pkg/armrpc/asyncoperation/worker/registry.go @@ -76,8 +76,8 @@ func (h *ControllerRegistry) RegisterDefault(factoryFn ControllerFactoryFunc, op defer h.ctrlMapMu.Unlock() // Note: we can't call opts.Validate() here because we don't know the resource type yet. - if opts.StorageClient == nil { - return fmt.Errorf("invalid controller options: .StorageClient is required") + if opts.DatabaseClient == nil { + return fmt.Errorf("invalid controller options: .DatabaseClient is required") } h.defaultFactory = factoryFn diff --git a/pkg/armrpc/asyncoperation/worker/registry_test.go b/pkg/armrpc/asyncoperation/worker/registry_test.go index c3078e6dcb..3df0c2ac5e 100644 --- a/pkg/armrpc/asyncoperation/worker/registry_test.go +++ b/pkg/armrpc/asyncoperation/worker/registry_test.go @@ -23,7 +23,7 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" "github.com/radius-project/radius/pkg/corerp/backend/deployment" - "github.com/radius-project/radius/pkg/ucp/store/inmemory" + "github.com/radius-project/radius/pkg/ucp/database/inmemory" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" ) @@ -38,7 +38,7 @@ func TestRegister_Get(t *testing.T) { opPut := v1.OperationType{Type: "Applications.Core/environments", Method: v1.OperationPut} ctrlOpts := ctrl.Options{ - StorageClient: inmemory.NewClient(), + DatabaseClient: inmemory.NewClient(), GetDeploymentProcessor: func() deployment.DeploymentProcessor { return nil }, } @@ -79,7 +79,7 @@ func TestRegister_Get_WithDefault(t *testing.T) { opGet := v1.OperationType{Type: "Applications.Core/environments", Method: v1.OperationGet} ctrlOpts := ctrl.Options{ - StorageClient: inmemory.NewClient(), + DatabaseClient: inmemory.NewClient(), GetDeploymentProcessor: func() deployment.DeploymentProcessor { return nil }, } diff --git a/pkg/armrpc/asyncoperation/worker/service.go b/pkg/armrpc/asyncoperation/worker/service.go index a70b98778e..7a2440266c 100644 --- a/pkg/armrpc/asyncoperation/worker/service.go +++ b/pkg/armrpc/asyncoperation/worker/service.go @@ -21,9 +21,9 @@ import ( manager "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" "github.com/radius-project/radius/pkg/armrpc/hostoptions" - "github.com/radius-project/radius/pkg/ucp/dataprovider" + "github.com/radius-project/radius/pkg/ucp/databaseprovider" queue "github.com/radius-project/radius/pkg/ucp/queue/client" - qprovider "github.com/radius-project/radius/pkg/ucp/queue/provider" + "github.com/radius-project/radius/pkg/ucp/queue/queueprovider" "github.com/radius-project/radius/pkg/ucp/ucplog" ) @@ -33,8 +33,8 @@ type Service struct { ProviderName string // Options is the server hosting options. Options hostoptions.HostOptions - // StorageProvider is the provider of storage client. - StorageProvider *dataprovider.DataStorageProvider + // DatabaseProvider is the provider of the database client. + DatabaseProvider *databaseprovider.DatabaseProvider // OperationStatusManager is the manager of the operation status. OperationStatusManager manager.StatusManager // Controllers is the registry of the async operation controllers. @@ -46,11 +46,11 @@ type Service struct { // Init initializes worker service - it initializes the StorageProvider, RequestQueue, OperationStatusManager, Controllers, KubeClient and // returns an error if any of these operations fail. func (s *Service) Init(ctx context.Context) error { - s.StorageProvider = dataprovider.DataStorageProviderFromOptions(s.Options.Config.StorageProvider) - qp := qprovider.New(s.Options.Config.QueueProvider) + s.DatabaseProvider = databaseprovider.FromOptions(s.Options.Config.DatabaseProvider) + qp := queueprovider.New(s.Options.Config.QueueProvider) var err error - storageClient, err := s.StorageProvider.GetClient(ctx) + storageClient, err := s.DatabaseProvider.GetClient(ctx) if err != nil { return err } diff --git a/pkg/armrpc/asyncoperation/worker/worker.go b/pkg/armrpc/asyncoperation/worker/worker.go index 0539576109..d10d862a8a 100644 --- a/pkg/armrpc/asyncoperation/worker/worker.go +++ b/pkg/armrpc/asyncoperation/worker/worker.go @@ -31,9 +31,9 @@ import ( "github.com/radius-project/radius/pkg/logging" "github.com/radius-project/radius/pkg/metrics" "github.com/radius-project/radius/pkg/trace" + "github.com/radius-project/radius/pkg/ucp/database" queue "github.com/radius-project/radius/pkg/ucp/queue/client" "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/store" "github.com/radius-project/radius/pkg/ucp/ucplog" "github.com/google/uuid" @@ -192,7 +192,7 @@ func (w *AsyncRequestProcessWorker) Start(ctx context.Context) error { Code: v1.CodeInternal, Message: errMsg, }) - w.completeOperation(reqCtx, msgreq, failed, asyncCtrl.StorageClient()) + w.completeOperation(reqCtx, msgreq, failed, asyncCtrl.DatabaseClient()) return } @@ -210,7 +210,7 @@ func (w *AsyncRequestProcessWorker) Start(ctx context.Context) error { return } - if err = w.updateResourceAndOperationStatus(reqCtx, asyncCtrl.StorageClient(), op, v1.ProvisioningStateUpdating, nil); err != nil { + if err = w.updateResourceAndOperationStatus(reqCtx, asyncCtrl.DatabaseClient(), op, v1.ProvisioningStateUpdating, nil); err != nil { return } @@ -274,7 +274,7 @@ func (w *AsyncRequestProcessWorker) runOperation(ctx context.Context, message *q // 2. When parent context is canceled or done, we need to requeue the operation to reprocess the request. // Such cases should not call w.completeOperation. if !errors.Is(asyncReqCtx.Err(), context.Canceled) { - w.completeOperation(ctx, message, result, asyncCtrl.StorageClient()) + w.completeOperation(ctx, message, result, asyncCtrl.DatabaseClient()) } trace.SetAsyncResultStatus(result, span) }() @@ -300,7 +300,7 @@ func (w *AsyncRequestProcessWorker) runOperation(ctx context.Context, message *q errMessage := fmt.Sprintf("Operation (%s) has timed out because it was processing longer than %d s.", asyncReq.OperationType, int(asyncReq.Timeout().Seconds())) result := ctrl.NewCanceledResult(errMessage) result.Error.Target = asyncReq.ResourceID - w.completeOperation(ctx, message, result, asyncCtrl.StorageClient()) + w.completeOperation(ctx, message, result, asyncCtrl.DatabaseClient()) return case <-ctx.Done(): @@ -326,7 +326,7 @@ func extractError(err error) v1.ErrorDetails { } } -func (w *AsyncRequestProcessWorker) completeOperation(ctx context.Context, message *queue.Message, result ctrl.Result, sc store.StorageClient) { +func (w *AsyncRequestProcessWorker) completeOperation(ctx context.Context, message *queue.Message, result ctrl.Result, sc database.Client) { logger := ucplog.FromContextOrDiscard(ctx) req := &ctrl.Request{} if err := json.Unmarshal(message.Data, req); err != nil { @@ -350,7 +350,7 @@ func (w *AsyncRequestProcessWorker) completeOperation(ctx context.Context, messa metrics.DefaultAsyncOperationMetrics.RecordAsyncOperation(ctx, req, &result) } -func (w *AsyncRequestProcessWorker) updateResourceAndOperationStatus(ctx context.Context, sc store.StorageClient, req *ctrl.Request, state v1.ProvisioningState, opErr *v1.ErrorDetails) error { +func (w *AsyncRequestProcessWorker) updateResourceAndOperationStatus(ctx context.Context, sc database.Client, req *ctrl.Request, state v1.ProvisioningState, opErr *v1.ErrorDetails) error { logger := ucplog.FromContextOrDiscard(ctx) rID, err := resources.ParseResource(req.ResourceID) @@ -360,7 +360,7 @@ func (w *AsyncRequestProcessWorker) updateResourceAndOperationStatus(ctx context } err = updateResourceState(ctx, sc, rID.String(), state) - if errors.Is(err, &store.ErrNotFound{}) { + if errors.Is(err, &database.ErrNotFound{}) { logger.Info("failed to update the provisioningState in resource because it no longer exists.") } else if err != nil { logger.Error(err, "failed to update the provisioningState in resource.") @@ -408,7 +408,7 @@ func (w *AsyncRequestProcessWorker) getMessageExtendDuration(visibleAt time.Time return d } -func updateResourceState(ctx context.Context, sc store.StorageClient, id string, state v1.ProvisioningState) error { +func updateResourceState(ctx context.Context, sc database.Client, id string, state v1.ProvisioningState) error { obj, err := sc.Get(ctx, id) if err != nil { return err @@ -425,7 +425,7 @@ func updateResourceState(ctx context.Context, sc store.StorageClient, id string, objmap["provisioningState"] = string(state) - err = sc.Save(ctx, obj, store.WithETag(obj.ETag)) + err = sc.Save(ctx, obj, database.WithETag(obj.ETag)) if err != nil { return err } diff --git a/pkg/armrpc/asyncoperation/worker/worker_runoperation_test.go b/pkg/armrpc/asyncoperation/worker/worker_runoperation_test.go index 9ff93b40b1..9359186c6d 100644 --- a/pkg/armrpc/asyncoperation/worker/worker_runoperation_test.go +++ b/pkg/armrpc/asyncoperation/worker/worker_runoperation_test.go @@ -29,11 +29,11 @@ import ( ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" manager "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" "github.com/radius-project/radius/pkg/corerp/backend/deployment" + "github.com/radius-project/radius/pkg/ucp/database" + inmemorystore "github.com/radius-project/radius/pkg/ucp/database/inmemory" queue "github.com/radius-project/radius/pkg/ucp/queue/client" "github.com/radius-project/radius/pkg/ucp/queue/inmemory" "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/store" - inmemorystore "github.com/radius-project/radius/pkg/ucp/store/inmemory" "github.com/stretchr/testify/require" "go.uber.org/atomic" "go.uber.org/mock/gomock" @@ -75,7 +75,7 @@ func (c *testAsyncController) Run(ctx context.Context, request *ctrl.Request) (c type testContext struct { ctx context.Context - mockSC *store.MockStorageClient + mockSC *database.MockClient mockSM *manager.MockStatusManager testQueue *inmemory.Client @@ -83,8 +83,8 @@ type testContext struct { } // newTestResourceObject returns new store.Object to prevent datarace when updateResourceState accesses map[string]any{} concurrently. -func newTestResourceObject() *store.Object { - return &store.Object{ +func newTestResourceObject() *database.Object { + return &database.Object{ Data: map[string]any{ "name": "env0", "provisioningState": "Accepted", @@ -114,10 +114,10 @@ func (c *testContext) cancellable(timeout time.Duration) (context.Context, conte func newTestContext(t *testing.T, lockTime time.Duration) (*testContext, *gomock.Controller) { mctrl := gomock.NewController(t) inmemQ := inmemory.NewInMemQueue(lockTime) - storageClient := store.NewMockStorageClient(mctrl) + databaseClient := database.NewMockClient(mctrl) return &testContext{ ctx: context.Background(), - mockSC: storageClient, + mockSC: databaseClient, mockSM: manager.NewMockStatusManager(mctrl), internalQ: inmemQ, testQueue: inmemory.New(inmemQ), @@ -150,7 +150,7 @@ func TestStart_UnknownOperation(t *testing.T) { worker := New(Options{DequeueIntervalDuration: defaultTestDequeueInterval}, nil, tCtx.testQueue, registry) opts := ctrl.Options{ - StorageClient: tCtx.mockSC, + DatabaseClient: tCtx.mockSC, GetDeploymentProcessor: func() deployment.DeploymentProcessor { return deployment.NewMockDeploymentProcessor(mctrl) }, @@ -202,7 +202,7 @@ func TestStart_MaxDequeueCount(t *testing.T) { // set up mocks tCtx.mockSC.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { return newTestResourceObject(), nil }).AnyTimes() tCtx.mockSC.EXPECT().Save(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1) @@ -214,7 +214,7 @@ func TestStart_MaxDequeueCount(t *testing.T) { worker := New(Options{MaxOperationRetryCount: expectedDequeueCount, DequeueIntervalDuration: defaultTestDequeueInterval}, tCtx.mockSM, tCtx.testQueue, registry) opts := ctrl.Options{ - StorageClient: tCtx.mockSC, + DatabaseClient: tCtx.mockSC, } testCtrl := &testAsyncController{ @@ -230,7 +230,7 @@ func TestStart_MaxDequeueCount(t *testing.T) { func(opts ctrl.Options) (ctrl.Controller, error) { return testCtrl, nil }, ctrl.Options{ - StorageClient: tCtx.mockSC, + DatabaseClient: tCtx.mockSC, }) require.NoError(t, err) @@ -262,7 +262,7 @@ func TestStart_MaxConcurrency(t *testing.T) { // set up mocks tCtx.mockSC.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { return newTestResourceObject(), nil }).AnyTimes() tCtx.mockSC.EXPECT().Save(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() @@ -273,7 +273,7 @@ func TestStart_MaxConcurrency(t *testing.T) { worker := New(Options{DequeueIntervalDuration: defaultTestDequeueInterval}, tCtx.mockSM, tCtx.testQueue, registry) opts := ctrl.Options{ - StorageClient: tCtx.mockSC, + DatabaseClient: tCtx.mockSC, GetDeploymentProcessor: func() deployment.DeploymentProcessor { return deployment.NewMockDeploymentProcessor(mctrl) }, @@ -338,7 +338,7 @@ func TestStart_RunOperation(t *testing.T) { // set up mocks tCtx.mockSC.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { return newTestResourceObject(), nil }).AnyTimes() tCtx.mockSC.EXPECT().Save(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() @@ -349,7 +349,7 @@ func TestStart_RunOperation(t *testing.T) { worker := New(Options{}, tCtx.mockSM, tCtx.testQueue, registry) opts := ctrl.Options{ - StorageClient: tCtx.mockSC, + DatabaseClient: tCtx.mockSC, GetDeploymentProcessor: func() deployment.DeploymentProcessor { return deployment.NewMockDeploymentProcessor(mctrl) }, @@ -407,7 +407,7 @@ func TestRunOperation_Successfully(t *testing.T) { // set up mocks tCtx.mockSC.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { return newTestResourceObject(), nil }).AnyTimes() tCtx.mockSC.EXPECT().Save(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() @@ -419,7 +419,7 @@ func TestRunOperation_Successfully(t *testing.T) { worker := New(Options{}, tCtx.mockSM, tCtx.testQueue, nil) opts := ctrl.Options{ - StorageClient: tCtx.mockSC, + DatabaseClient: tCtx.mockSC, GetDeploymentProcessor: func() deployment.DeploymentProcessor { return deployment.NewMockDeploymentProcessor(mctrl) }, @@ -445,7 +445,7 @@ func TestRunOperation_ExtendMessageLock(t *testing.T) { // set up mocks tCtx.mockSC.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { return newTestResourceObject(), nil }).AnyTimes() tCtx.mockSC.EXPECT().Save(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() @@ -458,7 +458,7 @@ func TestRunOperation_ExtendMessageLock(t *testing.T) { worker := New(Options{}, tCtx.mockSM, tCtx.testQueue, nil) opts := ctrl.Options{ - StorageClient: tCtx.mockSC, + DatabaseClient: tCtx.mockSC, GetDeploymentProcessor: func() deployment.DeploymentProcessor { return deployment.NewMockDeploymentProcessor(mctrl) }, @@ -500,7 +500,7 @@ func TestRunOperation_CancelContext(t *testing.T) { // Instead we use the in-memory store. opts := ctrl.Options{ - StorageClient: inmemorystore.NewClient(), + DatabaseClient: inmemorystore.NewClient(), GetDeploymentProcessor: func() deployment.DeploymentProcessor { return nil }, @@ -536,7 +536,7 @@ func TestRunOperation_Timeout(t *testing.T) { // set up mocks tCtx.mockSC.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { return newTestResourceObject(), nil }).AnyTimes() tCtx.mockSC.EXPECT().Save(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() @@ -555,7 +555,7 @@ func TestRunOperation_Timeout(t *testing.T) { worker := New(Options{}, tCtx.mockSM, tCtx.testQueue, nil) opts := ctrl.Options{ - StorageClient: tCtx.mockSC, + DatabaseClient: tCtx.mockSC, GetDeploymentProcessor: func() deployment.DeploymentProcessor { return deployment.NewMockDeploymentProcessor(mctrl) }, @@ -589,7 +589,7 @@ func TestRunOperation_PanicController(t *testing.T) { worker := New(Options{}, nil, tCtx.testQueue, nil) opts := ctrl.Options{ - StorageClient: tCtx.mockSC, + DatabaseClient: tCtx.mockSC, GetDeploymentProcessor: func() deployment.DeploymentProcessor { return nil }, diff --git a/pkg/armrpc/asyncoperation/worker/worker_test.go b/pkg/armrpc/asyncoperation/worker/worker_test.go index f6c8a6f4d6..390e0b7598 100644 --- a/pkg/armrpc/asyncoperation/worker/worker_test.go +++ b/pkg/armrpc/asyncoperation/worker/worker_test.go @@ -23,7 +23,7 @@ import ( "time" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" ) @@ -84,30 +84,30 @@ func TestUpdateResourceState(t *testing.T) { mctrl := gomock.NewController(t) defer mctrl.Finish() - mStorageClient := store.NewMockStorageClient(mctrl) + databaseClient := database.NewMockClient(mctrl) ctx := context.Background() - mStorageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, options ...store.GetOptions) (*store.Object, error) { - return &store.Object{ + DoAndReturn(func(ctx context.Context, id string, options ...database.GetOptions) (*database.Object, error) { + return &database.Object{ Data: tt.in, }, nil }) if tt.callSave { - mStorageClient. + databaseClient. EXPECT(). Save(gomock.Any(), gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, obj *store.Object, options ...store.SaveOptions) error { + DoAndReturn(func(ctx context.Context, obj *database.Object, options ...database.SaveOptions) error { k := obj.Data.(map[string]any) require.Equal(t, k["provisioningState"].(string), string(tt.updateState)) return nil }) } - err := updateResourceState(ctx, mStorageClient, "fakeid", tt.updateState) + err := updateResourceState(ctx, databaseClient, "fakeid", tt.updateState) require.ErrorIs(t, err, tt.outErr) }) } diff --git a/pkg/armrpc/builder/builder_test.go b/pkg/armrpc/builder/builder_test.go index 90695a363c..b25ad4b72b 100644 --- a/pkg/armrpc/builder/builder_test.go +++ b/pkg/armrpc/builder/builder_test.go @@ -28,7 +28,7 @@ import ( "github.com/radius-project/radius/pkg/armrpc/asyncoperation/worker" apictrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rpctest" - "github.com/radius-project/radius/pkg/ucp/store/inmemory" + "github.com/radius-project/radius/pkg/ucp/database/inmemory" "github.com/radius-project/radius/test/testcontext" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -173,10 +173,10 @@ func TestApplyAPIHandlers(t *testing.T) { r := chi.NewRouter() options := apictrl.Options{ - Address: "localhost:8080", - PathBase: "/api.ucp.dev", - StorageClient: inmemory.NewClient(), - StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), + Address: "localhost:8080", + PathBase: "/api.ucp.dev", + DatabaseClient: inmemory.NewClient(), + StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), } return r, b.ApplyAPIHandlers(ctx, r, options) @@ -228,10 +228,10 @@ func TestApplyAPIHandlers_AvailableOperations(t *testing.T) { rpctest.AssertRequests(t, handlerTests, "/api.ucp.dev", "/planes/radius/local", func(ctx context.Context) (chi.Router, error) { r := chi.NewRouter() options := apictrl.Options{ - Address: "localhost:8080", - PathBase: "/api.ucp.dev", - StorageClient: inmemory.NewClient(), - StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), + Address: "localhost:8080", + PathBase: "/api.ucp.dev", + DatabaseClient: inmemory.NewClient(), + StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), } return r, builder.ApplyAPIHandlers(ctx, r, options) }) @@ -244,7 +244,7 @@ func TestApplyAsyncHandler(t *testing.T) { ctx := testcontext.New(t) options := backendctrl.Options{ - StorageClient: inmemory.NewClient(), + DatabaseClient: inmemory.NewClient(), } err := builder.ApplyAsyncHandler(ctx, registry, options) diff --git a/pkg/armrpc/builder/namespace_test.go b/pkg/armrpc/builder/namespace_test.go index b247c72810..60e9890416 100644 --- a/pkg/armrpc/builder/namespace_test.go +++ b/pkg/armrpc/builder/namespace_test.go @@ -26,7 +26,7 @@ import ( apictrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rest" "github.com/radius-project/radius/pkg/armrpc/rpctest" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/stretchr/testify/require" ) @@ -45,7 +45,7 @@ func (c *testAsyncController) Run(ctx context.Context, request *asyncctrl.Reques return asyncctrl.Result{}, nil } -func (c *testAsyncController) StorageClient() store.StorageClient { +func (c *testAsyncController) DatabaseClient() database.Client { return nil } diff --git a/pkg/armrpc/frontend/controller/controller.go b/pkg/armrpc/frontend/controller/controller.go index bbda7f3537..8acd02118a 100644 --- a/pkg/armrpc/frontend/controller/controller.go +++ b/pkg/armrpc/frontend/controller/controller.go @@ -26,7 +26,7 @@ import ( sm "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" "github.com/radius-project/radius/pkg/armrpc/hostoptions" "github.com/radius-project/radius/pkg/armrpc/rest" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/database" runtimeclient "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -52,8 +52,8 @@ type Options struct { // Code that needs to construct a URL path should use the base path prefix when constructing the URL path. PathBase string - // StorageClient is the data storage client. - StorageClient store.StorageClient + // DatabaseClient is the database client. + DatabaseClient database.Client // KubeClient is the Kubernetes controller runtime client. KubeClient runtimeclient.Client @@ -71,8 +71,8 @@ func (o Options) Validate() error { if o.Address == "" { err = errors.Join(err, errors.New(".Address is required")) } - if o.StorageClient == nil { - err = errors.Join(err, errors.New(".StorageClient is required")) + if o.DatabaseClient == nil { + err = errors.Join(err, errors.New(".DatabaseClient is required")) } if o.ResourceType == "" { err = errors.Join(err, errors.New(".ResourceType is required")) @@ -136,9 +136,9 @@ func NewBaseController(options Options) BaseController { } } -// StorageClient gets storage client for this controller. -func (b *BaseController) StorageClient() store.StorageClient { - return b.options.StorageClient +// DatabaseClient gets database client for this controller. +func (b *BaseController) DatabaseClient() database.Client { + return b.options.DatabaseClient } // KubeClient gets Kubernetes client for this controller. @@ -160,8 +160,8 @@ func (b *BaseController) StatusManager() sm.StatusManager { // the ETag of the resource and an error if one occurs. func (c *BaseController) GetResource(ctx context.Context, id string, out any) (etag string, err error) { etag = "" - var res *store.Object - if res, err = c.StorageClient().Get(ctx, id); err == nil { + var res *database.Object + if res, err = c.DatabaseClient().Get(ctx, id); err == nil { if err = res.As(out); err == nil { etag = res.ETag return @@ -171,14 +171,14 @@ func (c *BaseController) GetResource(ctx context.Context, id string, out any) (e } // SaveResource saves a resource to the data store with an ETag and returns a store object or an error if the save fails. -func (c *BaseController) SaveResource(ctx context.Context, id string, in any, etag string) (*store.Object, error) { - nr := &store.Object{ - Metadata: store.Metadata{ +func (c *BaseController) SaveResource(ctx context.Context, id string, in any, etag string) (*database.Object, error) { + nr := &database.Object{ + Metadata: database.Metadata{ ID: id, }, Data: in, } - err := c.StorageClient().Save(ctx, nr, store.WithETag(etag)) + err := c.DatabaseClient().Save(ctx, nr, database.WithETag(etag)) if err != nil { return nil, err } diff --git a/pkg/armrpc/frontend/controller/controller_test.go b/pkg/armrpc/frontend/controller/controller_test.go index af31d927d6..5e28bd77c8 100644 --- a/pkg/armrpc/frontend/controller/controller_test.go +++ b/pkg/armrpc/frontend/controller/controller_test.go @@ -21,7 +21,7 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/stretchr/testify/require" ) @@ -112,39 +112,39 @@ func TestOptionsValidate(t *testing.T) { { name: "valid options", options: Options{ - Address: "localhost:8080", - StorageClient: &store.MockStorageClient{}, - ResourceType: "testResource", - StatusManager: &statusmanager.MockStatusManager{}, + Address: "localhost:8080", + DatabaseClient: &database.MockClient{}, + ResourceType: "testResource", + StatusManager: &statusmanager.MockStatusManager{}, }, wantErr: false, }, { name: "missing address", options: Options{ - StorageClient: &store.MockStorageClient{}, - ResourceType: "testResource", - StatusManager: &statusmanager.MockStatusManager{}, + DatabaseClient: &database.MockClient{}, + ResourceType: "testResource", + StatusManager: &statusmanager.MockStatusManager{}, }, wantErr: true, errMsg: ".Address is required", }, { - name: "missing storage client", + name: "missing database client", options: Options{ Address: "localhost:8080", ResourceType: "testResource", StatusManager: &statusmanager.MockStatusManager{}, }, wantErr: true, - errMsg: ".StorageClient is required", + errMsg: ".DatabaseClient is required", }, { name: "missing resource type", options: Options{ - Address: "localhost:8080", - StorageClient: &store.MockStorageClient{}, - StatusManager: &statusmanager.MockStatusManager{}, + Address: "localhost:8080", + DatabaseClient: &database.MockClient{}, + StatusManager: &statusmanager.MockStatusManager{}, }, wantErr: true, errMsg: ".ResourceType is required", @@ -152,9 +152,9 @@ func TestOptionsValidate(t *testing.T) { { name: "missing status manager", options: Options{ - Address: "localhost:8080", - StorageClient: &store.MockStorageClient{}, - ResourceType: "testResource", + Address: "localhost:8080", + DatabaseClient: &database.MockClient{}, + ResourceType: "testResource", }, wantErr: true, errMsg: ".StatusManager is required", diff --git a/pkg/armrpc/frontend/controller/operation.go b/pkg/armrpc/frontend/controller/operation.go index 2d720c1f47..d8ecbf11bb 100644 --- a/pkg/armrpc/frontend/controller/operation.go +++ b/pkg/armrpc/frontend/controller/operation.go @@ -26,8 +26,8 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" sm "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" "github.com/radius-project/radius/pkg/armrpc/rest" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/store" ) const ( @@ -58,9 +58,9 @@ func (b *Operation[P, T]) Options() *Options { return &b.options } -// StorageClient gets storage client for this controller. -func (b *Operation[P, T]) StorageClient() store.StorageClient { - return b.options.StorageClient +// DatabaseClient gets database client for this controller. +func (b *Operation[P, T]) DatabaseClient() database.Client { + return b.options.DatabaseClient } // ResourceType gets the resource type for this controller. @@ -89,12 +89,12 @@ func (c *Operation[P, T]) GetResourceFromRequest(ctx context.Context, req *http. return dm, nil } -// GetResource is the helper to get the resource via storage client. +// GetResource is the helper to get the resource via database client. func (c *Operation[P, T]) GetResource(ctx context.Context, id resources.ID) (out *T, etag string, err error) { etag = "" out = new(T) - var res *store.Object - if res, err = c.StorageClient().Get(ctx, id.String()); err == nil { + var res *database.Object + if res, err = c.DatabaseClient().Get(ctx, id.String()); err == nil { if err = res.As(out); err == nil { etag = res.ETag return @@ -102,21 +102,21 @@ func (c *Operation[P, T]) GetResource(ctx context.Context, id resources.ID) (out } out = nil - if errors.Is(&store.ErrNotFound{ID: id.String()}, err) { + if errors.Is(&database.ErrNotFound{ID: id.String()}, err) { err = nil } return } -// SaveResource is the helper to save the resource via storage client. +// SaveResource is the helper to save the resource via database client. func (c *Operation[P, T]) SaveResource(ctx context.Context, id string, in *T, etag string) (string, error) { - nr := &store.Object{ - Metadata: store.Metadata{ + nr := &database.Object{ + Metadata: database.Metadata{ ID: id, }, Data: in, } - err := c.StorageClient().Save(ctx, nr, store.WithETag(etag)) + err := c.DatabaseClient().Save(ctx, nr, database.WithETag(etag)) if err != nil { return "", err } diff --git a/pkg/armrpc/frontend/defaultoperation/defaultasyncdelete_test.go b/pkg/armrpc/frontend/defaultoperation/defaultasyncdelete_test.go index 7787f17772..e28a7b6f66 100644 --- a/pkg/armrpc/frontend/defaultoperation/defaultasyncdelete_test.go +++ b/pkg/armrpc/frontend/defaultoperation/defaultasyncdelete_test.go @@ -29,7 +29,7 @@ import ( ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rest" "github.com/radius-project/radius/pkg/armrpc/rpctest" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -46,7 +46,7 @@ func TestDefaultAsyncDelete(t *testing.T) { rejectedByFilter bool code int }{ - {"async-delete-non-existing-resource-no-etag", "", v1.ProvisioningStateNone, &store.ErrNotFound{}, nil, nil, false, http.StatusNoContent}, + {"async-delete-non-existing-resource-no-etag", "", v1.ProvisioningStateNone, &database.ErrNotFound{}, nil, nil, false, http.StatusNoContent}, {"async-delete-existing-resource-blocked-by-filter", "", v1.ProvisioningStateSucceeded, nil, nil, nil, true, http.StatusConflict}, {"async-delete-existing-resource-not-in-terminal-state", "", v1.ProvisioningStateUpdating, nil, nil, nil, false, http.StatusConflict}, {"async-delete-existing-resource-success", "", v1.ProvisioningStateSucceeded, nil, nil, nil, false, http.StatusAccepted}, @@ -75,8 +75,8 @@ func TestDefaultAsyncDelete(t *testing.T) { mds.EXPECT(). Get(gomock.Any(), gomock.Any()). - Return(&store.Object{ - Metadata: store.Metadata{ID: appDataModel.ID}, + Return(&database.Object{ + Metadata: database.Metadata{ID: appDataModel.ID}, Data: appDataModel, }, tt.getErr). Times(1) @@ -96,8 +96,8 @@ func TestDefaultAsyncDelete(t *testing.T) { } opts := ctrl.Options{ - StorageClient: mds, - StatusManager: msm, + DatabaseClient: mds, + StatusManager: msm, } resourceOpts := ctrl.ResourceOptions[TestResourceDataModel]{ diff --git a/pkg/armrpc/frontend/defaultoperation/defaultasyncput_test.go b/pkg/armrpc/frontend/defaultoperation/defaultasyncput_test.go index 005349d86e..a66a120c6f 100644 --- a/pkg/armrpc/frontend/defaultoperation/defaultasyncput_test.go +++ b/pkg/armrpc/frontend/defaultoperation/defaultasyncput_test.go @@ -29,7 +29,7 @@ import ( "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rpctest" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/test/testutil" "github.com/stretchr/testify/require" @@ -48,7 +48,7 @@ func TestDefaultAsyncPut_Create(t *testing.T) { }{ { "async-create-new-resource-success", - &store.ErrNotFound{}, + &database.ErrNotFound{}, nil, nil, nil, @@ -57,16 +57,16 @@ func TestDefaultAsyncPut_Create(t *testing.T) { }, { "async-create-new-resource-concurrency-error", - &store.ErrConcurrency{}, + &database.ErrConcurrency{}, nil, nil, nil, http.StatusCreated, - &store.ErrConcurrency{}, + &database.ErrConcurrency{}, }, { "async-create-new-resource-enqueue-error", - &store.ErrNotFound{}, + &database.ErrNotFound{}, nil, errors.New("enqueuer client is unset"), nil, @@ -95,10 +95,10 @@ func TestDefaultAsyncPut_Create(t *testing.T) { var asyncOperationRetryAfter = 2*time.Second + 2*time.Millisecond mds.EXPECT().Get(gomock.Any(), gomock.Any()). - Return(&store.Object{}, tt.getErr). + Return(&database.Object{}, tt.getErr). Times(1) - if tt.getErr == nil || errors.Is(&store.ErrNotFound{}, tt.getErr) { + if tt.getErr == nil || errors.Is(&database.ErrNotFound{}, tt.getErr) { mds.EXPECT().Save(gomock.Any(), gomock.Any(), gomock.Any()). Return(tt.saveErr). Times(1) @@ -121,8 +121,8 @@ func TestDefaultAsyncPut_Create(t *testing.T) { } opts := ctrl.Options{ - StorageClient: mds, - StatusManager: msm, + DatabaseClient: mds, + StatusManager: msm, } resourceOpts := ctrl.ResourceOptions[TestResourceDataModel]{ @@ -211,11 +211,11 @@ func TestDefaultAsyncPut_Update(t *testing.T) { "resource-datamodel.json", nil, false, - &store.ErrConcurrency{}, + &database.ErrConcurrency{}, nil, nil, http.StatusInternalServerError, - &store.ErrConcurrency{}, + &database.ErrConcurrency{}, }, { "async-update-existing-resource-save-error", @@ -224,11 +224,11 @@ func TestDefaultAsyncPut_Update(t *testing.T) { "resource-datamodel.json", nil, false, - &store.ErrInvalid{Message: "testing initial save err"}, + &database.ErrInvalid{Message: "testing initial save err"}, nil, nil, http.StatusInternalServerError, - &store.ErrInvalid{Message: "testing initial save err"}, + &database.ErrInvalid{Message: "testing initial save err"}, }, { "async-update-existing-resource-enqueue-error", @@ -238,10 +238,10 @@ func TestDefaultAsyncPut_Update(t *testing.T) { nil, false, nil, - &store.ErrInvalid{Message: "testing initial save err"}, + &database.ErrInvalid{Message: "testing initial save err"}, nil, http.StatusInternalServerError, - &store.ErrInvalid{Message: "testing initial save err"}, + &database.ErrInvalid{Message: "testing initial save err"}, }, } @@ -265,8 +265,8 @@ func TestDefaultAsyncPut_Update(t *testing.T) { ctx := rpctest.NewARMRequestContext(req) sCtx := v1.ARMRequestContextFromContext(ctx) - so := &store.Object{ - Metadata: store.Metadata{ID: sCtx.ResourceID.String()}, + so := &database.Object{ + Metadata: database.Metadata{ID: sCtx.ResourceID.String()}, Data: reqDataModel, } @@ -293,8 +293,8 @@ func TestDefaultAsyncPut_Update(t *testing.T) { } opts := ctrl.Options{ - StorageClient: mds, - StatusManager: msm, + DatabaseClient: mds, + StatusManager: msm, } resourceOpts := ctrl.ResourceOptions[TestResourceDataModel]{ diff --git a/pkg/armrpc/frontend/defaultoperation/defaultsyncdelete.go b/pkg/armrpc/frontend/defaultoperation/defaultsyncdelete.go index 6971c3c74a..62ff4e67dd 100644 --- a/pkg/armrpc/frontend/defaultoperation/defaultsyncdelete.go +++ b/pkg/armrpc/frontend/defaultoperation/defaultsyncdelete.go @@ -24,7 +24,7 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rest" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/database" ) // DefaultSyncDelete is the controller implementation to delete resource synchronously. @@ -68,8 +68,8 @@ func (e *DefaultSyncDelete[P, T]) Run(ctx context.Context, w http.ResponseWriter } } - if err := e.StorageClient().Delete(ctx, serviceCtx.ResourceID.String()); err != nil { - if errors.Is(&store.ErrNotFound{ID: serviceCtx.ResourceID.String()}, err) { + if err := e.DatabaseClient().Delete(ctx, serviceCtx.ResourceID.String()); err != nil { + if errors.Is(&database.ErrNotFound{ID: serviceCtx.ResourceID.String()}, err) { return rest.NewNoContentResponse(), nil } return nil, err diff --git a/pkg/armrpc/frontend/defaultoperation/defaultsyncdelete_test.go b/pkg/armrpc/frontend/defaultoperation/defaultsyncdelete_test.go index 6568cb411b..361d1bef18 100644 --- a/pkg/armrpc/frontend/defaultoperation/defaultsyncdelete_test.go +++ b/pkg/armrpc/frontend/defaultoperation/defaultsyncdelete_test.go @@ -26,7 +26,7 @@ import ( ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rest" "github.com/radius-project/radius/pkg/armrpc/rpctest" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -42,9 +42,9 @@ func TestDefaultSyncDelete(t *testing.T) { code int }{ {"sync-delete-existing-resource-success", "", nil, nil, false, http.StatusOK}, - {"sync-delete-non-existing-resource", "", &store.ErrNotFound{}, nil, false, http.StatusNoContent}, + {"sync-delete-non-existing-resource", "", &database.ErrNotFound{}, nil, false, http.StatusNoContent}, {"sync-delete-existing-resource-blocked-by-filter", "", nil, nil, true, http.StatusConflict}, - {"sync-delete-fails-resource-notfound", "", nil, &store.ErrNotFound{}, false, http.StatusNoContent}, + {"sync-delete-fails-resource-notfound", "", nil, &database.ErrNotFound{}, false, http.StatusNoContent}, } for _, tt := range deleteCases { @@ -63,8 +63,8 @@ func TestDefaultSyncDelete(t *testing.T) { mds.EXPECT(). Get(gomock.Any(), gomock.Any()). - Return(&store.Object{ - Metadata: store.Metadata{ID: appDataModel.ID}, + Return(&database.Object{ + Metadata: database.Metadata{ID: appDataModel.ID}, Data: appDataModel, }, tt.getErr). Times(1) @@ -78,8 +78,8 @@ func TestDefaultSyncDelete(t *testing.T) { } opts := ctrl.Options{ - StorageClient: mds, - StatusManager: msm, + DatabaseClient: mds, + StatusManager: msm, } resourceOpts := ctrl.ResourceOptions[TestResourceDataModel]{ diff --git a/pkg/armrpc/frontend/defaultoperation/defaultsyncput_test.go b/pkg/armrpc/frontend/defaultoperation/defaultsyncput_test.go index 025da32807..a5dd737b26 100644 --- a/pkg/armrpc/frontend/defaultoperation/defaultsyncput_test.go +++ b/pkg/armrpc/frontend/defaultoperation/defaultsyncput_test.go @@ -27,7 +27,7 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rpctest" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/test/testutil" "github.com/stretchr/testify/require" @@ -44,17 +44,17 @@ func TestDefaultSyncPut_Create(t *testing.T) { }{ { "sync-create-new-resource-success", - &store.ErrNotFound{}, + &database.ErrNotFound{}, nil, http.StatusOK, nil, }, { "sync-create-new-resource-concurrency-error", - &store.ErrConcurrency{}, + &database.ErrConcurrency{}, nil, http.StatusOK, - &store.ErrConcurrency{}, + &database.ErrConcurrency{}, }, } @@ -72,18 +72,18 @@ func TestDefaultSyncPut_Create(t *testing.T) { ctx := rpctest.NewARMRequestContext(req) mds.EXPECT().Get(gomock.Any(), gomock.Any()). - Return(&store.Object{}, tt.getErr). + Return(&database.Object{}, tt.getErr). Times(1) - if tt.getErr == nil || errors.Is(&store.ErrNotFound{}, tt.getErr) { + if tt.getErr == nil || errors.Is(&database.ErrNotFound{}, tt.getErr) { mds.EXPECT().Save(gomock.Any(), gomock.Any(), gomock.Any()). Return(tt.saveErr). Times(1) } opts := ctrl.Options{ - StorageClient: mds, - StatusManager: msm, + DatabaseClient: mds, + StatusManager: msm, } resourceOpts := ctrl.ResourceOptions[TestResourceDataModel]{ @@ -154,11 +154,11 @@ func TestDefaultSyncPut_Update(t *testing.T) { "resource-datamodel.json", nil, false, - &store.ErrConcurrency{}, + &database.ErrConcurrency{}, nil, nil, http.StatusInternalServerError, - &store.ErrConcurrency{}, + &database.ErrConcurrency{}, }, { "sync-update-existing-resource-save-error", @@ -166,11 +166,11 @@ func TestDefaultSyncPut_Update(t *testing.T) { "resource-datamodel.json", nil, false, - &store.ErrInvalid{Message: "testing initial save err"}, + &database.ErrInvalid{Message: "testing initial save err"}, nil, nil, http.StatusInternalServerError, - &store.ErrInvalid{Message: "testing initial save err"}, + &database.ErrInvalid{Message: "testing initial save err"}, }, } @@ -192,8 +192,8 @@ func TestDefaultSyncPut_Update(t *testing.T) { ctx := rpctest.NewARMRequestContext(req) sCtx := v1.ARMRequestContextFromContext(ctx) - so := &store.Object{ - Metadata: store.Metadata{ID: sCtx.ResourceID.String()}, + so := &database.Object{ + Metadata: database.Metadata{ID: sCtx.ResourceID.String()}, Data: reqDataModel, } @@ -208,8 +208,8 @@ func TestDefaultSyncPut_Update(t *testing.T) { } opts := ctrl.Options{ - StorageClient: mds, - StatusManager: msm, + DatabaseClient: mds, + StatusManager: msm, } resourceOpts := ctrl.ResourceOptions[TestResourceDataModel]{ diff --git a/pkg/armrpc/frontend/defaultoperation/getoperationresult.go b/pkg/armrpc/frontend/defaultoperation/getoperationresult.go index 473884ab24..a9b452892f 100644 --- a/pkg/armrpc/frontend/defaultoperation/getoperationresult.go +++ b/pkg/armrpc/frontend/defaultoperation/getoperationresult.go @@ -27,8 +27,8 @@ import ( manager "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rest" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/store" ) var _ ctrl.Controller = (*GetOperationResult)(nil) @@ -56,8 +56,8 @@ func (e *GetOperationResult) Run(ctx context.Context, w http.ResponseWriter, req return rest.NewBadRequestResponse(err.Error()), nil } - obj, err := e.StorageClient().Get(ctx, id.String()) - if errors.Is(&store.ErrNotFound{ID: id.String()}, err) { + obj, err := e.DatabaseClient().Get(ctx, id.String()) + if errors.Is(&database.ErrNotFound{ID: id.String()}, err) { return rest.NewNotFoundResponse(id), nil } diff --git a/pkg/armrpc/frontend/defaultoperation/getoperationresult_test.go b/pkg/armrpc/frontend/defaultoperation/getoperationresult_test.go index 9434bfed4a..b6fee80ac4 100644 --- a/pkg/armrpc/frontend/defaultoperation/getoperationresult_test.go +++ b/pkg/armrpc/frontend/defaultoperation/getoperationresult_test.go @@ -28,7 +28,7 @@ import ( manager "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rpctest" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/test/testcontext" "github.com/radius-project/radius/test/testutil" @@ -50,22 +50,22 @@ func TestGetOperationResultRun(t *testing.T) { t.Run("get non-existing resource", func(t *testing.T) { mctrl := gomock.NewController(t) - storageClient := store.NewMockStorageClient(mctrl) + databaseClient := database.NewMockClient(mctrl) w := httptest.NewRecorder() req, err := rpctest.NewHTTPRequestFromJSON(testcontext.New(t), http.MethodGet, operationStatusTestHeaderFile, nil) require.NoError(t, err) ctx := rpctest.NewARMRequestContext(req) - storageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { - return nil, &store.ErrNotFound{ID: id} + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { + return nil, &database.ErrNotFound{ID: id} }) ctl, err := NewGetOperationResult(ctrl.Options{ - StorageClient: storageClient, + DatabaseClient: databaseClient, }) require.NoError(t, err) @@ -116,7 +116,7 @@ func TestGetOperationResultRun(t *testing.T) { for _, tt := range opResTestCases { t.Run(tt.desc, func(t *testing.T) { mctrl := gomock.NewController(t) - storageClient := store.NewMockStorageClient(mctrl) + databaseClient := database.NewMockClient(mctrl) w := httptest.NewRecorder() req, err := rpctest.NewHTTPRequestFromJSON(testcontext.New(t), http.MethodGet, operationStatusTestHeaderFile, nil) @@ -126,18 +126,18 @@ func TestGetOperationResultRun(t *testing.T) { osDataModel.Status = tt.provisioningState osDataModel.RetryAfter = time.Second * 5 - storageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { - return &store.Object{ - Metadata: store.Metadata{ID: id}, + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { + return &database.Object{ + Metadata: database.Metadata{ID: id}, Data: osDataModel, }, nil }) ctl, err := NewGetOperationResult(ctrl.Options{ - StorageClient: storageClient, + DatabaseClient: databaseClient, }) require.NoError(t, err) diff --git a/pkg/armrpc/frontend/defaultoperation/getoperationstatus.go b/pkg/armrpc/frontend/defaultoperation/getoperationstatus.go index f83b31d4eb..558822c8aa 100644 --- a/pkg/armrpc/frontend/defaultoperation/getoperationstatus.go +++ b/pkg/armrpc/frontend/defaultoperation/getoperationstatus.go @@ -25,7 +25,7 @@ import ( manager "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rest" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/database" ) var _ ctrl.Controller = (*GetOperationStatus)(nil) @@ -49,7 +49,7 @@ func (e *GetOperationStatus) Run(ctx context.Context, w http.ResponseWriter, req os := &manager.Status{} _, err := e.GetResource(ctx, serviceCtx.ResourceID.String(), os) - if err != nil && errors.Is(&store.ErrNotFound{ID: serviceCtx.ResourceID.String()}, err) { + if err != nil && errors.Is(&database.ErrNotFound{ID: serviceCtx.ResourceID.String()}, err) { return rest.NewNotFoundResponse(serviceCtx.ResourceID), nil } diff --git a/pkg/armrpc/frontend/defaultoperation/getoperationstatus_test.go b/pkg/armrpc/frontend/defaultoperation/getoperationstatus_test.go index 2b4ee7cccb..1ab1f52225 100644 --- a/pkg/armrpc/frontend/defaultoperation/getoperationstatus_test.go +++ b/pkg/armrpc/frontend/defaultoperation/getoperationstatus_test.go @@ -27,7 +27,7 @@ import ( manager "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rpctest" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/test/testutil" "github.com/stretchr/testify/require" @@ -38,7 +38,7 @@ func TestGetOperationStatusRun(t *testing.T) { mctrl := gomock.NewController(t) defer mctrl.Finish() - mStorageClient := store.NewMockStorageClient(mctrl) + databaseClient := database.NewMockClient(mctrl) ctx := context.Background() rawDataModel := testutil.ReadFixture("operationstatus_datamodel.json") @@ -55,15 +55,15 @@ func TestGetOperationStatusRun(t *testing.T) { require.NoError(t, err) ctx := rpctest.NewARMRequestContext(req) - mStorageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { - return nil, &store.ErrNotFound{ID: id} + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { + return nil, &database.ErrNotFound{ID: id} }) ctl, err := NewGetOperationStatus(ctrl.Options{ - StorageClient: mStorageClient, + DatabaseClient: databaseClient, }) require.NoError(t, err) @@ -79,18 +79,18 @@ func TestGetOperationStatusRun(t *testing.T) { require.NoError(t, err) ctx := rpctest.NewARMRequestContext(req) - mStorageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { - return &store.Object{ - Metadata: store.Metadata{ID: id}, + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { + return &database.Object{ + Metadata: database.Metadata{ID: id}, Data: osDataModel, }, nil }) ctl, err := NewGetOperationStatus(ctrl.Options{ - StorageClient: mStorageClient, + DatabaseClient: databaseClient, }) require.NoError(t, err) diff --git a/pkg/armrpc/frontend/defaultoperation/getresource_test.go b/pkg/armrpc/frontend/defaultoperation/getresource_test.go index 9531b9b9e6..3c48a8f977 100644 --- a/pkg/armrpc/frontend/defaultoperation/getresource_test.go +++ b/pkg/armrpc/frontend/defaultoperation/getresource_test.go @@ -26,7 +26,7 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rpctest" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -90,7 +90,7 @@ func TestGetResourceRun(t *testing.T) { mctrl := gomock.NewController(t) defer mctrl.Finish() - mStorageClient := store.NewMockStorageClient(mctrl) + databaseClient := database.NewMockClient(mctrl) ctx := context.Background() testResourceDataModel := &testDataModel{ @@ -106,15 +106,15 @@ func TestGetResourceRun(t *testing.T) { require.NoError(t, err) ctx := rpctest.NewARMRequestContext(req) - mStorageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { - return nil, &store.ErrNotFound{ID: id} + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { + return nil, &database.ErrNotFound{ID: id} }) opts := ctrl.Options{ - StorageClient: mStorageClient, + DatabaseClient: databaseClient, } ctrlOpts := ctrl.ResourceOptions[testDataModel]{ @@ -136,18 +136,18 @@ func TestGetResourceRun(t *testing.T) { require.NoError(t, err) ctx := rpctest.NewARMRequestContext(req) - mStorageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { - return &store.Object{ - Metadata: store.Metadata{ID: id}, + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { + return &database.Object{ + Metadata: database.Metadata{ID: id}, Data: testResourceDataModel, }, nil }) opts := ctrl.Options{ - StorageClient: mStorageClient, + DatabaseClient: databaseClient, } ctrlOpts := ctrl.ResourceOptions[testDataModel]{ diff --git a/pkg/armrpc/frontend/defaultoperation/listresources.go b/pkg/armrpc/frontend/defaultoperation/listresources.go index 0329d6650e..4aee3fe37d 100644 --- a/pkg/armrpc/frontend/defaultoperation/listresources.go +++ b/pkg/armrpc/frontend/defaultoperation/listresources.go @@ -23,7 +23,7 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rest" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/database" ) // ListResources is the controller implementation to get the list of resources in resource group. @@ -48,13 +48,13 @@ func NewListResources[P interface { func (e *ListResources[P, T]) Run(ctx context.Context, w http.ResponseWriter, req *http.Request) (rest.Response, error) { serviceCtx := v1.ARMRequestContextFromContext(ctx) - query := store.Query{ + query := database.Query{ RootScope: serviceCtx.ResourceID.RootScope(), ResourceType: serviceCtx.ResourceID.Type(), ScopeRecursive: e.listRecursiveQuery, } - result, err := e.StorageClient().Query(ctx, query, store.WithPaginationToken(serviceCtx.SkipToken), store.WithMaxQueryItemCount(serviceCtx.Top)) + result, err := e.DatabaseClient().Query(ctx, query, database.WithPaginationToken(serviceCtx.SkipToken), database.WithMaxQueryItemCount(serviceCtx.Top)) if err != nil { return nil, err } @@ -64,7 +64,7 @@ func (e *ListResources[P, T]) Run(ctx context.Context, w http.ResponseWriter, re return rest.NewOKResponse(pagination), err } -func (e *ListResources[P, T]) createPaginationResponse(ctx context.Context, req *http.Request, result *store.ObjectQueryResult) (*v1.PaginatedList, error) { +func (e *ListResources[P, T]) createPaginationResponse(ctx context.Context, req *http.Request, result *database.ObjectQueryResult) (*v1.PaginatedList, error) { serviceCtx := v1.ARMRequestContextFromContext(ctx) items := []any{} diff --git a/pkg/armrpc/frontend/defaultoperation/listresources_test.go b/pkg/armrpc/frontend/defaultoperation/listresources_test.go index 9d11b14188..c000973cb9 100644 --- a/pkg/armrpc/frontend/defaultoperation/listresources_test.go +++ b/pkg/armrpc/frontend/defaultoperation/listresources_test.go @@ -27,7 +27,7 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rpctest" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/google/uuid" "github.com/stretchr/testify/require" @@ -43,7 +43,7 @@ func TestListResourcesRun(t *testing.T) { mctrl := gomock.NewController(t) defer mctrl.Finish() - mStorageClient := store.NewMockStorageClient(mctrl) + databaseClient := database.NewMockClient(mctrl) ctx := context.Background() testResourceDataModel := &testDataModel{ @@ -59,17 +59,17 @@ func TestListResourcesRun(t *testing.T) { require.NoError(t, err) ctx := rpctest.NewARMRequestContext(req) - mStorageClient. + databaseClient. EXPECT(). Query(gomock.Any(), gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, query store.Query, options ...store.QueryOptions) (*store.ObjectQueryResult, error) { - return &store.ObjectQueryResult{ - Items: []store.Object{}, + DoAndReturn(func(ctx context.Context, query database.Query, options ...database.QueryOptions) (*database.ObjectQueryResult, error) { + return &database.ObjectQueryResult{ + Items: []database.Object{}, }, nil }) opts := ctrl.Options{ - StorageClient: mStorageClient, + DatabaseClient: databaseClient, } ctrlOpts := ctrl.ResourceOptions[testDataModel]{ @@ -123,10 +123,10 @@ func TestListResourcesRun(t *testing.T) { paginationToken = "nextLink" } - items := []store.Object{} + items := []database.Object{} for i := 0; i < tt.batchCount; i++ { - item := store.Object{ - Metadata: store.Metadata{ + item := database.Object{ + Metadata: database.Metadata{ ID: uuid.New().String(), }, Data: testResourceDataModel, @@ -134,7 +134,7 @@ func TestListResourcesRun(t *testing.T) { items = append(items, item) } - expectedQuery := store.Query{ + expectedQuery := database.Query{ RootScope: serviceCtx.ResourceID.RootScope(), ResourceType: serviceCtx.ResourceID.Type(), @@ -144,18 +144,18 @@ func TestListResourcesRun(t *testing.T) { ScopeRecursive: tt.planeScope, } - mStorageClient. + databaseClient. EXPECT(). Query(gomock.Any(), expectedQuery, gomock.Any()). - DoAndReturn(func(ctx context.Context, query store.Query, options ...store.QueryOptions) (*store.ObjectQueryResult, error) { - return &store.ObjectQueryResult{ + DoAndReturn(func(ctx context.Context, query database.Query, options ...database.QueryOptions) (*database.ObjectQueryResult, error) { + return &database.ObjectQueryResult{ Items: items, PaginationToken: paginationToken, }, nil }) opts := ctrl.Options{ - StorageClient: mStorageClient, + DatabaseClient: databaseClient, } ctrlOpts := ctrl.ResourceOptions[testDataModel]{ diff --git a/pkg/armrpc/frontend/defaultoperation/resource_test.go b/pkg/armrpc/frontend/defaultoperation/resource_test.go index c66edf0a37..ad17c88548 100644 --- a/pkg/armrpc/frontend/defaultoperation/resource_test.go +++ b/pkg/armrpc/frontend/defaultoperation/resource_test.go @@ -29,7 +29,7 @@ import ( "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rest" "github.com/radius-project/radius/pkg/to" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/test/testutil" "go.uber.org/mock/gomock" @@ -207,9 +207,9 @@ func loadTestResurce() (*TestResource, *TestResourceDataModel, *TestResource) { return reqModel, datamodel, respModel } -func setupTest(tb testing.TB) (func(testing.TB), *store.MockStorageClient, *statusmanager.MockStatusManager) { +func setupTest(tb testing.TB) (func(testing.TB), *database.MockClient, *statusmanager.MockStatusManager) { mctrl := gomock.NewController(tb) - mds := store.NewMockStorageClient(mctrl) + mds := database.NewMockClient(mctrl) msm := statusmanager.NewMockStatusManager(mctrl) return func(tb testing.TB) { diff --git a/pkg/armrpc/frontend/server/handler.go b/pkg/armrpc/frontend/server/handler.go index e5d3c47b3d..fdcbd99453 100644 --- a/pkg/armrpc/frontend/server/handler.go +++ b/pkg/armrpc/frontend/server/handler.go @@ -55,11 +55,6 @@ type ControllerFactoryFunc func(ctrl.Options) (ctrl.Controller, error) // multiple types of resources (e.g. PUT on any type of AWS resource): // - Set ResourceType for operations that are scoped to a resource type. // - Set OperationType for general operations. -// -// In the controller options passed to the controller factory: -// -// - When ResourceType is set, the StorageClient will be configured to use the resource type. -// - When OperationType is set, the StorageClient will be generic and not filtered to a specific resource type. type HandlerOptions struct { // ParentRouter is the router to register the handler with. ParentRouter chi.Router @@ -68,8 +63,6 @@ type HandlerOptions struct { Path string // ResourceType is the resource type of the operation. May be blank if Operation is specified. - // - // If specified the ResourceType will be used to filter the StorageClient. ResourceType string // Method is the method of the operation. May be blank if Operation is specified. diff --git a/pkg/armrpc/frontend/server/handler_test.go b/pkg/armrpc/frontend/server/handler_test.go index 107c8b6877..bd323a06a9 100644 --- a/pkg/armrpc/frontend/server/handler_test.go +++ b/pkg/armrpc/frontend/server/handler_test.go @@ -33,7 +33,7 @@ import ( "github.com/radius-project/radius/pkg/armrpc/rest" "github.com/radius-project/radius/pkg/armrpc/rpctest" "github.com/radius-project/radius/pkg/middleware" - "github.com/radius-project/radius/pkg/ucp/store/inmemory" + "github.com/radius-project/radius/pkg/ucp/database/inmemory" "github.com/radius-project/radius/test/testcontext" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -75,10 +75,10 @@ func Test_NewSubrouter(t *testing.T) { func Test_RegisterHandler_DeplicatedRoutes(t *testing.T) { ctrlOpts := ctrl.Options{ - Address: "localhost:8080", - ResourceType: "Applications.Test/testResources", - StorageClient: inmemory.NewClient(), - StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), + Address: "localhost:8080", + ResourceType: "Applications.Test/testResources", + DatabaseClient: inmemory.NewClient(), + StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), } p := chi.NewRouter() @@ -169,9 +169,9 @@ func Test_RegisterHandler(t *testing.T) { } ctrlOpts := ctrl.Options{ - Address: "localhost:8080", - StorageClient: inmemory.NewClient(), - StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), + Address: "localhost:8080", + DatabaseClient: inmemory.NewClient(), + StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), } for _, tc := range tests { diff --git a/pkg/armrpc/frontend/server/service.go b/pkg/armrpc/frontend/server/service.go index 6d31f1c958..265ec4e17d 100644 --- a/pkg/armrpc/frontend/server/service.go +++ b/pkg/armrpc/frontend/server/service.go @@ -25,8 +25,8 @@ import ( "github.com/radius-project/radius/pkg/armrpc/authentication" "github.com/radius-project/radius/pkg/armrpc/hostoptions" "github.com/radius-project/radius/pkg/kubeutil" - "github.com/radius-project/radius/pkg/ucp/dataprovider" - qprovider "github.com/radius-project/radius/pkg/ucp/queue/provider" + "github.com/radius-project/radius/pkg/ucp/databaseprovider" + "github.com/radius-project/radius/pkg/ucp/queue/queueprovider" "github.com/radius-project/radius/pkg/ucp/ucplog" controller_runtime "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -39,8 +39,8 @@ type Service struct { // Options is the server hosting options. Options hostoptions.HostOptions - // StorageProvider is the provider of storage client. - StorageProvider *dataprovider.DataStorageProvider + // DatabaseProvider is the provider of database client. + DatabaseProvider *databaseprovider.DatabaseProvider // OperationStatusManager is the manager of the operation status. OperationStatusManager manager.StatusManager @@ -52,15 +52,15 @@ type Service struct { KubeClient controller_runtime.Client } -// Init initializes web service - it initializes the StorageProvider, QueueProvider, OperationStatusManager, KubeClient and ARMCertManager +// Init initializes web service - it initializes the DatabaseProvider, QueueProvider, OperationStatusManager, KubeClient and ARMCertManager // with the given context and returns an error if any of the initialization fails. func (s *Service) Init(ctx context.Context) error { logger := ucplog.FromContextOrDiscard(ctx) - s.StorageProvider = dataprovider.DataStorageProviderFromOptions(s.Options.Config.StorageProvider) - qp := qprovider.New(s.Options.Config.QueueProvider) + s.DatabaseProvider = databaseprovider.FromOptions(s.Options.Config.DatabaseProvider) + qp := queueprovider.New(s.Options.Config.QueueProvider) - storageClient, err := s.StorageProvider.GetClient(ctx) + databaseClient, err := s.DatabaseProvider.GetClient(ctx) if err != nil { return err } @@ -69,7 +69,7 @@ func (s *Service) Init(ctx context.Context) error { if err != nil { return err } - s.OperationStatusManager = manager.New(storageClient, reqQueueClient, s.Options.Config.Env.RoleLocation) + s.OperationStatusManager = manager.New(databaseClient, reqQueueClient, s.Options.Config.Env.RoleLocation) s.KubeClient, err = kubeutil.NewRuntimeClient(s.Options.K8sConfig) if err != nil { return err diff --git a/pkg/armrpc/hostoptions/hostoptions.go b/pkg/armrpc/hostoptions/hostoptions.go index f669c93f92..37750cf659 100644 --- a/pkg/armrpc/hostoptions/hostoptions.go +++ b/pkg/armrpc/hostoptions/hostoptions.go @@ -32,7 +32,7 @@ import ( "github.com/radius-project/radius/pkg/sdk" "github.com/radius-project/radius/pkg/ucp/config" sdk_cred "github.com/radius-project/radius/pkg/ucp/credentials" - sprovider "github.com/radius-project/radius/pkg/ucp/secret/provider" + sprovider "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" "gopkg.in/yaml.v3" corev1 "k8s.io/api/core/v1" diff --git a/pkg/armrpc/hostoptions/providerconfig.go b/pkg/armrpc/hostoptions/providerconfig.go index 05f4318012..f2acbf3d28 100644 --- a/pkg/armrpc/hostoptions/providerconfig.go +++ b/pkg/armrpc/hostoptions/providerconfig.go @@ -21,9 +21,9 @@ import ( profilerprovider "github.com/radius-project/radius/pkg/profiler/provider" "github.com/radius-project/radius/pkg/trace" "github.com/radius-project/radius/pkg/ucp/config" - "github.com/radius-project/radius/pkg/ucp/dataprovider" - qprovider "github.com/radius-project/radius/pkg/ucp/queue/provider" - sprovider "github.com/radius-project/radius/pkg/ucp/secret/provider" + "github.com/radius-project/radius/pkg/ucp/databaseprovider" + "github.com/radius-project/radius/pkg/ucp/queue/queueprovider" + "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" "github.com/radius-project/radius/pkg/ucp/ucplog" ) @@ -31,9 +31,9 @@ import ( type ProviderConfig struct { Env EnvironmentOptions `yaml:"environment"` Identity IdentityOptions `yaml:"identity"` - StorageProvider dataprovider.StorageProviderOptions `yaml:"storageProvider"` - SecretProvider sprovider.SecretProviderOptions `yaml:"secretProvider"` - QueueProvider qprovider.QueueProviderOptions `yaml:"queueProvider"` + DatabaseProvider databaseprovider.Options `yaml:"storageProvider"` + SecretProvider secretprovider.SecretProviderOptions `yaml:"secretProvider"` + QueueProvider queueprovider.QueueProviderOptions `yaml:"queueProvider"` Server *ServerOptions `yaml:"server,omitempty"` WorkerServer *WorkerServerOptions `yaml:"workerServer,omitempty"` MetricsProvider metricsprovider.MetricsProviderOptions `yaml:"metricsProvider"` diff --git a/pkg/armrpc/rpctest/controllers.go b/pkg/armrpc/rpctest/controllers.go index 80610ea8f4..819da12b6f 100644 --- a/pkg/armrpc/rpctest/controllers.go +++ b/pkg/armrpc/rpctest/controllers.go @@ -22,7 +22,7 @@ import ( "testing" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/database" "go.uber.org/mock/gomock" ) @@ -30,7 +30,7 @@ import ( type ControllerContext struct { Ctx context.Context MCtrl *gomock.Controller - MockSC *store.MockStorageClient + MockSC *database.MockClient } // NewControllerContext creates a new ControllerContext for testing. @@ -40,12 +40,12 @@ func NewControllerContext(t *testing.T) *ControllerContext { return &ControllerContext{ Ctx: context.Background(), MCtrl: mctrl, - MockSC: store.NewMockStorageClient(mctrl), + MockSC: database.NewMockClient(mctrl), } } // FakeStoreObject creates store.Object for datamodel. -func FakeStoreObject(dm v1.DataModelInterface) *store.Object { +func FakeStoreObject(dm v1.DataModelInterface) *database.Object { b, err := json.Marshal(dm) if err != nil { panic(err) @@ -55,5 +55,5 @@ func FakeStoreObject(dm v1.DataModelInterface) *store.Object { if err != nil { panic(err) } - return &store.Object{Data: r} + return &database.Object{Data: r} } diff --git a/pkg/corerp/backend/controller/createorupdateresource.go b/pkg/corerp/backend/controller/createorupdateresource.go index 8a9a5f0b60..65a86ff108 100644 --- a/pkg/corerp/backend/controller/createorupdateresource.go +++ b/pkg/corerp/backend/controller/createorupdateresource.go @@ -30,8 +30,8 @@ import ( "github.com/radius-project/radius/pkg/corerp/renderers/gateway" "github.com/radius-project/radius/pkg/corerp/renderers/volume" rpv1 "github.com/radius-project/radius/pkg/rp/v1" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/store" ) var _ ctrl.Controller = (*CreateOrUpdateResource)(nil) @@ -63,18 +63,18 @@ func getDataModel(id resources.ID) (v1.DataModelInterface, error) { // Run checks if the resource exists, renders the resource, deploys the resource, applies the // deployment output to the resource, deletes any resources that are no longer needed, and saves the resource. func (c *CreateOrUpdateResource) Run(ctx context.Context, request *ctrl.Request) (ctrl.Result, error) { - obj, err := c.StorageClient().Get(ctx, request.ResourceID) - if err != nil && !errors.Is(&store.ErrNotFound{ID: request.ResourceID}, err) { + obj, err := c.DatabaseClient().Get(ctx, request.ResourceID) + if err != nil && !errors.Is(&database.ErrNotFound{ID: request.ResourceID}, err) { return ctrl.Result{}, err } isNewResource := false - if errors.Is(&store.ErrNotFound{ID: request.ResourceID}, err) { + if errors.Is(&database.ErrNotFound{ID: request.ResourceID}, err) { isNewResource = true } opType, _ := v1.ParseOperationType(request.OperationType) - if opType.Method == http.MethodPatch && errors.Is(&store.ErrNotFound{ID: request.ResourceID}, err) { + if opType.Method == http.MethodPatch && errors.Is(&database.ErrNotFound{ID: request.ResourceID}, err) { return ctrl.Result{}, err } @@ -122,13 +122,13 @@ func (c *CreateOrUpdateResource) Run(ctx context.Context, request *ctrl.Request) } } - nr := &store.Object{ - Metadata: store.Metadata{ + nr := &database.Object{ + Metadata: database.Metadata{ ID: request.ResourceID, }, Data: deploymentDataModel, } - err = c.StorageClient().Save(ctx, nr, store.WithETag(obj.ETag)) + err = c.DatabaseClient().Save(ctx, nr, database.WithETag(obj.ETag)) if err != nil { return ctrl.Result{}, err } diff --git a/pkg/corerp/backend/controller/createorupdateresource_test.go b/pkg/corerp/backend/controller/createorupdateresource_test.go index 3598519f04..8c5a0cc787 100644 --- a/pkg/corerp/backend/controller/createorupdateresource_test.go +++ b/pkg/corerp/backend/controller/createorupdateresource_test.go @@ -34,16 +34,16 @@ import ( "github.com/radius-project/radius/pkg/corerp/renderers/gateway" ds_ctrl "github.com/radius-project/radius/pkg/datastoresrp/frontend/controller" rpv1 "github.com/radius-project/radius/pkg/rp/v1" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/store" ) func TestCreateOrUpdateResourceRun_20231001Preview(t *testing.T) { - setupTest := func() (func(tb testing.TB), *store.MockStorageClient, *deployment.MockDeploymentProcessor) { + setupTest := func() (func(tb testing.TB), *database.MockClient, *deployment.MockDeploymentProcessor) { mctrl := gomock.NewController(t) - msc := store.NewMockStorageClient(mctrl) + msc := database.NewMockClient(mctrl) mdp := deployment.NewMockDeploymentProcessor(mctrl) return func(tb testing.TB) { @@ -80,7 +80,7 @@ func TestCreateOrUpdateResourceRun_20231001Preview(t *testing.T) { container.ResourceType, "APPLICATIONS.CORE/CONTAINERS|PUT", fmt.Sprintf("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/containers/%s", uuid.NewString()), - &store.ErrNotFound{}, + &database.ErrNotFound{}, false, nil, nil, @@ -116,7 +116,7 @@ func TestCreateOrUpdateResourceRun_20231001Preview(t *testing.T) { gateway.ResourceType, "APPLICATIONS.CORE/GATEWAYS|PUT", fmt.Sprintf("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/gateways/%s", uuid.NewString()), - &store.ErrNotFound{}, + &database.ErrNotFound{}, false, nil, nil, @@ -156,7 +156,7 @@ func TestCreateOrUpdateResourceRun_20231001Preview(t *testing.T) { getCall := msc.EXPECT(). Get(gomock.Any(), gomock.Any()). - Return(&store.Object{ + Return(&database.Object{ Data: map[string]any{ "name": "env0", "properties": map[string]any{ @@ -166,7 +166,7 @@ func TestCreateOrUpdateResourceRun_20231001Preview(t *testing.T) { }, tt.getErr). Times(1) - if (tt.getErr == nil || errors.Is(&store.ErrNotFound{ID: tt.rId}, tt.getErr)) && !tt.convErr { + if (tt.getErr == nil || errors.Is(&database.ErrNotFound{ID: tt.rId}, tt.getErr)) && !tt.convErr { renderCall := mdp.EXPECT(). Render(gomock.Any(), gomock.Any(), gomock.Any()). Return(renderers.RendererOutput{}, tt.renderErr). @@ -180,7 +180,7 @@ func TestCreateOrUpdateResourceRun_20231001Preview(t *testing.T) { After(renderCall). Times(1) - if !errors.Is(&store.ErrNotFound{}, tt.getErr) { + if !errors.Is(&database.ErrNotFound{}, tt.getErr) { mdp.EXPECT(). Delete(gomock.Any(), gomock.Any(), gomock.Any()). Return(nil). @@ -199,7 +199,7 @@ func TestCreateOrUpdateResourceRun_20231001Preview(t *testing.T) { } opts := ctrl.Options{ - StorageClient: msc, + DatabaseClient: msc, GetDeploymentProcessor: func() deployment.DeploymentProcessor { return mdp }, @@ -253,12 +253,12 @@ func TestCreateOrUpdateResourceRun_20231001Preview(t *testing.T) { container.ResourceType, "APPLICATIONS.CORE/CONTAINERS|PATCH", fmt.Sprintf("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/containers/%s", uuid.NewString()), - &store.ErrNotFound{}, + &database.ErrNotFound{}, false, nil, nil, nil, - &store.ErrNotFound{}, + &database.ErrNotFound{}, }, { "container-patch-get-err", @@ -289,12 +289,12 @@ func TestCreateOrUpdateResourceRun_20231001Preview(t *testing.T) { gateway.ResourceType, "APPLICATIONS.CORE/GATEWAYS|PATCH", fmt.Sprintf("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/gateways/%s", uuid.NewString()), - &store.ErrNotFound{}, + &database.ErrNotFound{}, false, nil, nil, nil, - &store.ErrNotFound{}, + &database.ErrNotFound{}, }, { "unsupported-type-patch", @@ -329,7 +329,7 @@ func TestCreateOrUpdateResourceRun_20231001Preview(t *testing.T) { getCall := msc.EXPECT(). Get(gomock.Any(), gomock.Any()). - Return(&store.Object{ + Return(&database.Object{ Data: map[string]any{ "name": "env0", "properties": map[string]any{ @@ -371,7 +371,7 @@ func TestCreateOrUpdateResourceRun_20231001Preview(t *testing.T) { } opts := ctrl.Options{ - StorageClient: msc, + DatabaseClient: msc, GetDeploymentProcessor: func() deployment.DeploymentProcessor { return mdp }, diff --git a/pkg/corerp/backend/controller/deleteresource.go b/pkg/corerp/backend/controller/deleteresource.go index de31e5c484..8ddaca6527 100644 --- a/pkg/corerp/backend/controller/deleteresource.go +++ b/pkg/corerp/backend/controller/deleteresource.go @@ -37,11 +37,11 @@ func NewDeleteResource(opts ctrl.Options) (ctrl.Controller, error) { return &DeleteResource{ctrl.NewBaseAsyncController(opts)}, nil } -// Run retrieves a resource from storage, parses its ID, gets its data model, converts it to a deployment -// data model, deletes the resource from the deployment processor, and deletes the resource from storage. +// Run retrieves a resource from the database, parses its ID, gets its data model, converts it to a deployment +// data model, deletes the resource from the deployment processor, and deletes the resource from the database. // It returns an error if any of these steps fail. func (c *DeleteResource) Run(ctx context.Context, request *ctrl.Request) (ctrl.Result, error) { - obj, err := c.StorageClient().Get(ctx, request.ResourceID) + obj, err := c.DatabaseClient().Get(ctx, request.ResourceID) if err != nil { return ctrl.NewFailedResult(v1.ErrorDetails{Message: err.Error()}), err } @@ -71,7 +71,7 @@ func (c *DeleteResource) Run(ctx context.Context, request *ctrl.Request) (ctrl.R return ctrl.Result{}, err } - err = c.StorageClient().Delete(ctx, request.ResourceID) + err = c.DatabaseClient().Delete(ctx, request.ResourceID) if err != nil { return ctrl.Result{}, err } diff --git a/pkg/corerp/backend/controller/deleteresource_test.go b/pkg/corerp/backend/controller/deleteresource_test.go index 639ccc0cdd..b1cff8cf7c 100644 --- a/pkg/corerp/backend/controller/deleteresource_test.go +++ b/pkg/corerp/backend/controller/deleteresource_test.go @@ -25,17 +25,17 @@ import ( "github.com/google/uuid" ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" deployment "github.com/radius-project/radius/pkg/corerp/backend/deployment" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" ) func TestDeleteResourceRun_20231001Preview(t *testing.T) { - setupTest := func() (func(tb testing.TB), *store.MockStorageClient, *deployment.MockDeploymentProcessor, *ctrl.Request) { + setupTest := func() (func(tb testing.TB), *database.MockClient, *deployment.MockDeploymentProcessor, *ctrl.Request) { mctrl := gomock.NewController(t) - msc := store.NewMockStorageClient(mctrl) + msc := database.NewMockClient(mctrl) mdp := deployment.NewMockDeploymentProcessor(mctrl) req := &ctrl.Request{ @@ -61,7 +61,7 @@ func TestDeleteResourceRun_20231001Preview(t *testing.T) { scDelErr error }{ {"delete-existing-resource", nil, nil, nil}, - {"delete-non-existing-resource", &store.ErrNotFound{}, nil, nil}, + {"delete-non-existing-resource", &database.ErrNotFound{}, nil, nil}, {"delete-resource-dp-delete-error", nil, errors.New("deployment processor delete error"), nil}, {"delete-resource-delete-from-db-error", nil, nil, errors.New("delete from db error")}, } @@ -73,7 +73,7 @@ func TestDeleteResourceRun_20231001Preview(t *testing.T) { msc.EXPECT(). Get(gomock.Any(), gomock.Any()). - Return(&store.Object{}, tt.getErr). + Return(&database.Object{}, tt.getErr). Times(1) if tt.getErr == nil { @@ -91,7 +91,7 @@ func TestDeleteResourceRun_20231001Preview(t *testing.T) { } opts := ctrl.Options{ - StorageClient: msc, + DatabaseClient: msc, GetDeploymentProcessor: func() deployment.DeploymentProcessor { return mdp }, diff --git a/pkg/corerp/backend/deployment/deploymentprocessor.go b/pkg/corerp/backend/deployment/deploymentprocessor.go index 80392e48bc..a7803ae8bc 100644 --- a/pkg/corerp/backend/deployment/deploymentprocessor.go +++ b/pkg/corerp/backend/deployment/deploymentprocessor.go @@ -40,8 +40,8 @@ import ( msg_dm "github.com/radius-project/radius/pkg/messagingrp/datamodel" msg_ctrl "github.com/radius-project/radius/pkg/messagingrp/frontend/controller" "github.com/radius-project/radius/pkg/portableresources" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/store" "github.com/radius-project/radius/pkg/ucp/ucplog" "github.com/go-openapi/jsonpointer" @@ -59,15 +59,15 @@ type DeploymentProcessor interface { } // NewDeploymentProcessor creates a new instance of the DeploymentProcessor struct with the given parameters. -func NewDeploymentProcessor(appmodel model.ApplicationModel, storageClient store.StorageClient, k8sClient controller_runtime.Client, k8sClientSet kubernetes.Interface) DeploymentProcessor { - return &deploymentProcessor{appmodel: appmodel, storageClient: storageClient, k8sClient: k8sClient, k8sClientSet: k8sClientSet} +func NewDeploymentProcessor(appmodel model.ApplicationModel, databaseClient database.Client, k8sClient controller_runtime.Client, k8sClientSet kubernetes.Interface) DeploymentProcessor { + return &deploymentProcessor{appmodel: appmodel, databaseClient: databaseClient, k8sClient: k8sClient, k8sClientSet: k8sClientSet} } var _ DeploymentProcessor = (*deploymentProcessor)(nil) type deploymentProcessor struct { - appmodel model.ApplicationModel - storageClient store.StorageClient + appmodel model.ApplicationModel + databaseClient database.Client // k8sClient is the Kubernetes controller runtime client. k8sClient controller_runtime.Client // k8sClientSet is the Kubernetes client. @@ -225,14 +225,14 @@ func (dp *deploymentProcessor) getApplicationAndEnvironmentForResourceID(ctx con // 2. fetch the application properties from the DB app := &corerp_dm.Application{} - err = rp_util.FetchScopeResource(ctx, dp.storageClient, res.AppID.String(), app) + err = rp_util.FetchScopeResource(ctx, dp.databaseClient, res.AppID.String(), app) if err != nil { return nil, nil, err } // 3. fetch the environment resource from the db to get the Namespace env := &corerp_dm.Environment{} - err = rp_util.FetchScopeResource(ctx, dp.storageClient, app.Properties.Environment, env) + err = rp_util.FetchScopeResource(ctx, dp.databaseClient, app.Properties.Environment, env) if err != nil { return nil, nil, err } @@ -480,9 +480,9 @@ func (dp *deploymentProcessor) getAppOptions(appProp *corerp_dm.ApplicationPrope // getResourceDataByID fetches resource for the provided id from the data store func (dp *deploymentProcessor) getResourceDataByID(ctx context.Context, resourceID resources.ID) (ResourceData, error) { errMsg := "failed to fetch the resource %q. Err: %w" - resource, err := dp.storageClient.Get(ctx, resourceID.String()) + resource, err := dp.databaseClient.Get(ctx, resourceID.String()) if err != nil { - if errors.Is(&store.ErrNotFound{ID: resourceID.String()}, err) { + if errors.Is(&database.ErrNotFound{ID: resourceID.String()}, err) { return ResourceData{}, v1.NewClientErrInvalidRequest(fmt.Sprintf("resource %q does not exist", resourceID.String())) } return ResourceData{}, fmt.Errorf(errMsg, resourceID.String(), err) diff --git a/pkg/corerp/backend/deployment/deploymentprocessor_test.go b/pkg/corerp/backend/deployment/deploymentprocessor_test.go index 0f2c4816e8..0cb4aaa29d 100644 --- a/pkg/corerp/backend/deployment/deploymentprocessor_test.go +++ b/pkg/corerp/backend/deployment/deploymentprocessor_test.go @@ -37,10 +37,10 @@ import ( "github.com/radius-project/radius/pkg/resourcemodel" rpv1 "github.com/radius-project/radius/pkg/rp/v1" "github.com/radius-project/radius/pkg/to" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/resources" resources_azure "github.com/radius-project/radius/pkg/ucp/resources/azure" resources_kubernetes "github.com/radius-project/radius/pkg/ucp/resources/kubernetes" - "github.com/radius-project/radius/pkg/ucp/store" "github.com/radius-project/radius/test/testcontext" "github.com/radius-project/radius/test/testutil" @@ -50,7 +50,7 @@ import ( type SharedMocks struct { model model.ApplicationModel - storageClient *store.MockStorageClient + databaseClient *database.MockClient resourceHandler *handlers.MockResourceHandler renderer *renderers.MockRenderer mctrl *gomock.Controller @@ -129,10 +129,10 @@ func setup(t *testing.T) SharedMocks { }, } - storageClient := store.NewMockStorageClient(ctrl) + databaseClient := database.NewMockClient(ctrl) return SharedMocks{ model: model, - storageClient: storageClient, + databaseClient: databaseClient, resourceHandler: resourceHandler, renderer: renderer, mctrl: ctrl, @@ -301,7 +301,7 @@ func Test_Render(t *testing.T) { t.Run("verify render success", func(t *testing.T) { mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.databaseClient, nil, nil} testResource := getTestResource() testRendererOutput := getTestRendererOutput() @@ -313,13 +313,13 @@ func Test_Render(t *testing.T) { mocks.renderer.EXPECT().Render(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(testRendererOutput, nil) mocks.renderer.EXPECT().GetDependencyIDs(gomock.Any(), gomock.Any()).Times(1).Return(requiredResources, nil, nil) - cr := store.Object{ - Metadata: store.Metadata{ + cr := database.Object{ + Metadata: database.Metadata{ ID: testResource.ID, }, Data: testResource, } - mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) + mocks.databaseClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) application := datamodel.Application{ BaseResource: v1.BaseResource{ TrackedResource: v1.TrackedResource{ @@ -332,20 +332,20 @@ func Test_Render(t *testing.T) { }, }, } - ar := store.Object{ - Metadata: store.Metadata{ + ar := database.Object{ + Metadata: database.Metadata{ ID: application.ID, }, Data: application, } - mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&ar, nil) - er := store.Object{ - Metadata: store.Metadata{ + mocks.databaseClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&ar, nil) + er := database.Object{ + Metadata: database.Metadata{ ID: env.ID, }, Data: env, } - mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&er, nil) + mocks.databaseClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&er, nil) mongoResource := dsrp_dm.MongoDatabase{ BaseResource: v1.BaseResource{ @@ -359,14 +359,14 @@ func Test_Render(t *testing.T) { }, }, } - mr := store.Object{ - Metadata: store.Metadata{ + mr := database.Object{ + Metadata: database.Metadata{ ID: mongoResource.ID, }, Data: mongoResource, } - mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&mr, nil) + mocks.databaseClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&mr, nil) rendererOutput, err := dp.Render(ctx, resourceID, &testResource) require.NoError(t, err) @@ -375,7 +375,7 @@ func Test_Render(t *testing.T) { t.Run("verify render success lowercase resourcetype", func(t *testing.T) { mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.databaseClient, nil, nil} testResource := getLowerCaseTestResource() testRendererOutput := getTestRendererOutput() @@ -384,13 +384,13 @@ func Test_Render(t *testing.T) { mocks.renderer.EXPECT().Render(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(testRendererOutput, nil) mocks.renderer.EXPECT().GetDependencyIDs(gomock.Any(), gomock.Any()).Times(1).Return([]resources.ID{}, nil, nil) - cr := store.Object{ - Metadata: store.Metadata{ + cr := database.Object{ + Metadata: database.Metadata{ ID: testResource.ID, }, Data: testResource, } - mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) + mocks.databaseClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) application := datamodel.Application{ BaseResource: v1.BaseResource{ TrackedResource: v1.TrackedResource{ @@ -403,20 +403,20 @@ func Test_Render(t *testing.T) { }, }, } - ar := store.Object{ - Metadata: store.Metadata{ + ar := database.Object{ + Metadata: database.Metadata{ ID: application.ID, }, Data: application, } - mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&ar, nil) - er := store.Object{ - Metadata: store.Metadata{ + mocks.databaseClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&ar, nil) + er := database.Object{ + Metadata: database.Metadata{ ID: env.ID, }, Data: env, } - mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&er, nil) + mocks.databaseClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&er, nil) rendererOutput, err := dp.Render(ctx, resourceID, &testResource) require.NoError(t, err) @@ -425,7 +425,7 @@ func Test_Render(t *testing.T) { t.Run("verify render success uppercase resourcetype", func(t *testing.T) { mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.databaseClient, nil, nil} testResource := getUpperCaseTestResource() testRendererOutput := getTestRendererOutput() @@ -434,13 +434,13 @@ func Test_Render(t *testing.T) { mocks.renderer.EXPECT().Render(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(testRendererOutput, nil) mocks.renderer.EXPECT().GetDependencyIDs(gomock.Any(), gomock.Any()).Times(1).Return([]resources.ID{}, nil, nil) - cr := store.Object{ - Metadata: store.Metadata{ + cr := database.Object{ + Metadata: database.Metadata{ ID: testResource.ID, }, Data: testResource, } - mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) + mocks.databaseClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) application := datamodel.Application{ BaseResource: v1.BaseResource{ TrackedResource: v1.TrackedResource{ @@ -453,20 +453,20 @@ func Test_Render(t *testing.T) { }, }, } - ar := store.Object{ - Metadata: store.Metadata{ + ar := database.Object{ + Metadata: database.Metadata{ ID: application.ID, }, Data: application, } - mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&ar, nil) - er := store.Object{ - Metadata: store.Metadata{ + mocks.databaseClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&ar, nil) + er := database.Object{ + Metadata: database.Metadata{ ID: env.ID, }, Data: env, } - mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&er, nil) + mocks.databaseClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&er, nil) rendererOutput, err := dp.Render(ctx, resourceID, &testResource) require.NoError(t, err) @@ -475,7 +475,7 @@ func Test_Render(t *testing.T) { t.Run("verify render error", func(t *testing.T) { mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.databaseClient, nil, nil} testResource := getTestResource() resourceID := getTestResourceID(testResource.ID) @@ -483,13 +483,13 @@ func Test_Render(t *testing.T) { mocks.renderer.EXPECT().GetDependencyIDs(gomock.Any(), gomock.Any()).Times(1).Return([]resources.ID{}, nil, nil) mocks.renderer.EXPECT().Render(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(renderers.RendererOutput{}, errors.New("failed to render the resource")) - cr := store.Object{ - Metadata: store.Metadata{ + cr := database.Object{ + Metadata: database.Metadata{ ID: testResource.ID, }, Data: testResource, } - mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) + mocks.databaseClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) application := datamodel.Application{ BaseResource: v1.BaseResource{ TrackedResource: v1.TrackedResource{ @@ -502,20 +502,20 @@ func Test_Render(t *testing.T) { }, }, } - ar := store.Object{ - Metadata: store.Metadata{ + ar := database.Object{ + Metadata: database.Metadata{ ID: application.ID, }, Data: application, } - mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&ar, nil) - er := store.Object{ - Metadata: store.Metadata{ + mocks.databaseClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&ar, nil) + er := database.Object{ + Metadata: database.Metadata{ ID: env.ID, }, Data: env, } - mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&er, nil) + mocks.databaseClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&er, nil) _, err := dp.Render(ctx, resourceID, &testResource) require.Error(t, err, "failed to render the resource") @@ -523,12 +523,12 @@ func Test_Render(t *testing.T) { t.Run("Resource not found in data store", func(t *testing.T) { mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.databaseClient, nil, nil} testResource := getTestResource() resourceID := getTestResourceID(testResource.ID) - mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&store.Object{}, &store.ErrNotFound{ID: testResource.ID}) + mocks.databaseClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&database.Object{}, &database.ErrNotFound{ID: testResource.ID}) _, err := dp.Render(ctx, resourceID, &testResource) require.Error(t, err) @@ -538,12 +538,12 @@ func Test_Render(t *testing.T) { t.Run("Data store access error", func(t *testing.T) { mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.databaseClient, nil, nil} testResource := getTestResource() resourceID := getTestResourceID(testResource.ID) - mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&store.Object{}, errors.New("failed to connect to data store")) + mocks.databaseClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&database.Object{}, errors.New("failed to connect to data store")) _, err := dp.Render(ctx, resourceID, &testResource) require.Error(t, err) @@ -552,7 +552,7 @@ func Test_Render(t *testing.T) { t.Run("Invalid resource type", func(t *testing.T) { mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.databaseClient, nil, nil} testInvalidResourceID := "/subscriptions/test-sub/resourceGroups/test-group/providers/Applications.foo/foo/foo" testResource := getTestResource() @@ -564,19 +564,19 @@ func Test_Render(t *testing.T) { t.Run("Invalid application id", func(t *testing.T) { mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.databaseClient, nil, nil} testResource := getTestResource() resourceID := getTestResourceID(testResource.ID) testResource.Properties.Application = "invalid-app-id" - cr := store.Object{ - Metadata: store.Metadata{ + cr := database.Object{ + Metadata: database.Metadata{ ID: testResource.ID, }, Data: testResource, } - mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) + mocks.databaseClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) _, err := dp.Render(ctx, resourceID, &testResource) require.Error(t, err) @@ -586,19 +586,19 @@ func Test_Render(t *testing.T) { t.Run("Missing application id", func(t *testing.T) { mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.databaseClient, nil, nil} testResource := getTestResource() resourceID := getTestResourceID(testResource.ID) testResource.Properties.Application = "" - cr := store.Object{ - Metadata: store.Metadata{ + cr := database.Object{ + Metadata: database.Metadata{ ID: testResource.ID, }, Data: testResource, } - mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) + mocks.databaseClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) _, err := dp.Render(ctx, resourceID, &testResource) require.Error(t, err) @@ -607,19 +607,19 @@ func Test_Render(t *testing.T) { t.Run("Invalid application resource type", func(t *testing.T) { mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.databaseClient, nil, nil} testResource := getTestResource() resourceID := getTestResourceID(testResource.ID) testResource.Properties.Application = "/subscriptions/test-subscription/resourceGroups/test-resource-group/providers/Applications.Core/app/test-application" - cr := store.Object{ - Metadata: store.Metadata{ + cr := database.Object{ + Metadata: database.Metadata{ ID: testResource.ID, }, Data: testResource, } - mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) + mocks.databaseClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) _, err := dp.Render(ctx, resourceID, &testResource) require.Error(t, err) @@ -629,7 +629,7 @@ func Test_Render(t *testing.T) { t.Run("Missing output resource provider", func(t *testing.T) { mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.databaseClient, nil, nil} testResource := getTestResource() testRendererOutput := getTestRendererOutput() @@ -640,13 +640,13 @@ func Test_Render(t *testing.T) { mocks.renderer.EXPECT().Render(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(testRendererOutput, nil) mocks.renderer.EXPECT().GetDependencyIDs(gomock.Any(), gomock.Any()).Times(1).Return([]resources.ID{}, nil, nil) - cr := store.Object{ - Metadata: store.Metadata{ + cr := database.Object{ + Metadata: database.Metadata{ ID: testResource.ID, }, Data: testResource, } - mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) + mocks.databaseClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) application := datamodel.Application{ BaseResource: v1.BaseResource{ TrackedResource: v1.TrackedResource{ @@ -659,20 +659,20 @@ func Test_Render(t *testing.T) { }, }, } - ar := store.Object{ - Metadata: store.Metadata{ + ar := database.Object{ + Metadata: database.Metadata{ ID: application.ID, }, Data: application, } - mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&ar, nil) - er := store.Object{ - Metadata: store.Metadata{ + mocks.databaseClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&ar, nil) + er := database.Object{ + Metadata: database.Metadata{ ID: env.ID, }, Data: env, } - mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&er, nil) + mocks.databaseClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&er, nil) _, err := dp.Render(ctx, resourceID, &testResource) require.Error(t, err, "output resource \"Deployment\" does not have a provider specified") @@ -680,7 +680,7 @@ func Test_Render(t *testing.T) { t.Run("Unsupported output resource provider", func(t *testing.T) { mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.databaseClient, nil, nil} testResource := getTestResource() testRendererOutput := getTestRendererOutput() @@ -690,13 +690,13 @@ func Test_Render(t *testing.T) { mocks.renderer.EXPECT().GetDependencyIDs(gomock.Any(), gomock.Any()).Times(1).Return([]resources.ID{}, nil, nil) - cr := store.Object{ - Metadata: store.Metadata{ + cr := database.Object{ + Metadata: database.Metadata{ ID: testResource.ID, }, Data: testResource, } - mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) + mocks.databaseClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) application := datamodel.Application{ BaseResource: v1.BaseResource{ TrackedResource: v1.TrackedResource{ @@ -709,20 +709,20 @@ func Test_Render(t *testing.T) { }, }, } - ar := store.Object{ - Metadata: store.Metadata{ + ar := database.Object{ + Metadata: database.Metadata{ ID: application.ID, }, Data: application, } - mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&ar, nil) - er := store.Object{ - Metadata: store.Metadata{ + mocks.databaseClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&ar, nil) + er := database.Object{ + Metadata: database.Metadata{ ID: env.ID, }, Data: env, } - mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&er, nil) + mocks.databaseClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&er, nil) mocks.renderer.EXPECT().Render(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(testRendererOutput, nil) @@ -733,13 +733,13 @@ func Test_Render(t *testing.T) { func setupDeployMocks(mocks SharedMocks, simulated bool) { testResource := getTestResource() - cr := store.Object{ - Metadata: store.Metadata{ + cr := database.Object{ + Metadata: database.Metadata{ ID: testResource.ID, }, Data: testResource, } - mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) + mocks.databaseClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&cr, nil) app := datamodel.Application{ BaseResource: v1.BaseResource{ @@ -754,13 +754,13 @@ func setupDeployMocks(mocks SharedMocks, simulated bool) { }, } - ar := store.Object{ - Metadata: store.Metadata{ + ar := database.Object{ + Metadata: database.Metadata{ ID: mocks.testApp.ID, }, Data: app, } - mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&ar, nil) + mocks.databaseClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&ar, nil) env := datamodel.Environment{ BaseResource: v1.BaseResource{ @@ -784,20 +784,20 @@ func setupDeployMocks(mocks SharedMocks, simulated bool) { env.Properties.Simulated = true } - er := store.Object{ - Metadata: store.Metadata{ + er := database.Object{ + Metadata: database.Metadata{ ID: mocks.testEnv.ID, }, Data: env, } - mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&er, nil) + mocks.databaseClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&er, nil) } func Test_Deploy(t *testing.T) { t.Run("Verify deploy success", func(t *testing.T) { ctx := testcontext.New(t) mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.databaseClient, nil, nil} testResource := getTestResource() testRendererOutput := getTestRendererOutput() @@ -837,7 +837,7 @@ func Test_Deploy(t *testing.T) { t.Run("Verify deploy success with simulated env", func(t *testing.T) { ctx := testcontext.New(t) mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.databaseClient, nil, nil} testResource := getTestResource() testRendererOutput := getTestRendererOutput() @@ -855,7 +855,7 @@ func Test_Deploy(t *testing.T) { t.Run("Verify deploy failure", func(t *testing.T) { ctx := testcontext.New(t) mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.databaseClient, nil, nil} testResource := getTestResource() testRendererOutput := getTestRendererOutput() @@ -872,7 +872,7 @@ func Test_Deploy(t *testing.T) { t.Run("Output resource dependency missing local ID", func(t *testing.T) { ctx := testcontext.New(t) mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.databaseClient, nil, nil} testResource := getTestResource() testRendererOutput := getTestRendererOutput() @@ -890,7 +890,7 @@ func Test_Deploy(t *testing.T) { t.Run("Invalid output resource type", func(t *testing.T) { ctx := testcontext.New(t) mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.databaseClient, nil, nil} testResource := getTestResource() testRendererOutput := getTestRendererOutput() @@ -908,7 +908,7 @@ func Test_Deploy(t *testing.T) { t.Run("Missing output resource identity", func(t *testing.T) { ctx := testcontext.New(t) mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.databaseClient, nil, nil} testResource := getTestResource() testRendererOutput := getTestRendererOutput() @@ -934,7 +934,7 @@ func Test_Delete(t *testing.T) { t.Run("Verify delete success", func(t *testing.T) { ctx := testcontext.New(t) mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.databaseClient, nil, nil} testResource := getTestResource() resourceID := getTestResourceID(testResource.ID) @@ -948,7 +948,7 @@ func Test_Delete(t *testing.T) { t.Run("Verify delete failure", func(t *testing.T) { ctx := testcontext.New(t) mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.databaseClient, nil, nil} testResource := getTestResource() resourceID := getTestResourceID(testResource.ID) @@ -962,7 +962,7 @@ func Test_Delete(t *testing.T) { t.Run("Verify delete with no output resources", func(t *testing.T) { ctx := testcontext.New(t) mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.databaseClient, nil, nil} testResource := getTestResource() resourceID := getTestResourceID(testResource.ID) @@ -1037,20 +1037,20 @@ func Test_getEnvOptions_PublicEndpointOverride(t *testing.T) { func Test_getResourceDataByID(t *testing.T) { ctx := testcontext.New(t) mocks := setup(t) - dp := deploymentProcessor{mocks.model, mocks.storageClient, nil, nil} + dp := deploymentProcessor{mocks.model, mocks.databaseClient, nil, nil} t.Run("Get recipe data from connected mongoDB resources", func(t *testing.T) { depId, _ := resources.ParseResource("/subscriptions/test-subscription/resourceGroups/test-resource-group/providers/Applications.Datastores/mongoDatabases/test-mongo") mongoResource := buildMongoDBWithRecipe() mongoResource.PortableResourceMetadata.RecipeData = portableresources.RecipeData{} - mr := store.Object{ - Metadata: store.Metadata{ + mr := database.Object{ + Metadata: database.Metadata{ ID: mongoResource.ID, }, Data: mongoResource, } - mocks.storageClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&mr, nil) + mocks.databaseClient.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&mr, nil) resourceData, err := dp.getResourceDataByID(ctx, depId) require.NoError(t, err) diff --git a/pkg/corerp/frontend/controller/applications/getgraph_test.go b/pkg/corerp/frontend/controller/applications/getgraph_test.go index a7bf5752b4..d859cf5d31 100644 --- a/pkg/corerp/frontend/controller/applications/getgraph_test.go +++ b/pkg/corerp/frontend/controller/applications/getgraph_test.go @@ -25,7 +25,7 @@ import ( ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rpctest" "github.com/radius-project/radius/pkg/sdk" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" ) @@ -34,7 +34,7 @@ func TestGetGraphRun_20231001Preview(t *testing.T) { mctrl := gomock.NewController(t) defer mctrl.Finish() - mStorageClient := store.NewMockStorageClient(mctrl) + databaseClient := database.NewMockClient(mctrl) req, err := rpctest.NewHTTPRequestWithContent( context.Background(), v1.OperationPost.HTTPMethod(), @@ -43,13 +43,13 @@ func TestGetGraphRun_20231001Preview(t *testing.T) { require.NoError(t, err) t.Run("resource not found", func(t *testing.T) { - mStorageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - Return(nil, &store.ErrNotFound{}) + Return(nil, &database.ErrNotFound{}) ctx := rpctest.NewARMRequestContext(req) opts := ctrl.Options{ - StorageClient: mStorageClient, + DatabaseClient: databaseClient, } conn, err := sdk.NewDirectConnection("http://localhost:9000/apis/api.ucp.dev/v1alpha3") diff --git a/pkg/corerp/frontend/controller/applications/updatefilter.go b/pkg/corerp/frontend/controller/applications/updatefilter.go index 2c254f98aa..80efb97f37 100644 --- a/pkg/corerp/frontend/controller/applications/updatefilter.go +++ b/pkg/corerp/frontend/controller/applications/updatefilter.go @@ -63,7 +63,7 @@ func CreateAppScopedNamespace(ctx context.Context, newResource, oldResource *dat kubeNamespace = ext.KubernetesNamespace.Namespace } else { // Construct namespace using the namespace specified by environment resource. - envNamespace, err := rp_kube.FindNamespaceByEnvID(ctx, opt.StorageClient, newResource.Properties.Environment) + envNamespace, err := rp_kube.FindNamespaceByEnvID(ctx, opt.DatabaseClient, newResource.Properties.Environment) if err != nil { return rest.NewBadRequestResponse(fmt.Sprintf("Environment %s could not be constructed: %s", newResource.Properties.Environment, err.Error())), nil @@ -84,7 +84,7 @@ func CreateAppScopedNamespace(ctx context.Context, newResource, oldResource *dat return rest.NewBadRequestResponse(fmt.Sprintf("Environment %s for application %s could not be found", envID.Name(), serviceCtx.ResourceID.Name())), nil } - result, err := util.FindResources(ctx, envID.RootScope(), envID.Type(), envNamespaceQuery, kubeNamespace, opt.StorageClient) + result, err := util.FindResources(ctx, envID.RootScope(), envID.Type(), envNamespaceQuery, kubeNamespace, opt.DatabaseClient) if err != nil { return nil, err } @@ -93,7 +93,7 @@ func CreateAppScopedNamespace(ctx context.Context, newResource, oldResource *dat } // Check if another application resource is using namespace - result, err = util.FindResources(ctx, serviceCtx.ResourceID.RootScope(), serviceCtx.ResourceID.Type(), appNamespaceQuery, kubeNamespace, opt.StorageClient) + result, err = util.FindResources(ctx, serviceCtx.ResourceID.RootScope(), serviceCtx.ResourceID.Type(), appNamespaceQuery, kubeNamespace, opt.DatabaseClient) if err != nil { return nil, err } diff --git a/pkg/corerp/frontend/controller/applications/updatefilter_test.go b/pkg/corerp/frontend/controller/applications/updatefilter_test.go index 0e33bbc401..7145de92f7 100644 --- a/pkg/corerp/frontend/controller/applications/updatefilter_test.go +++ b/pkg/corerp/frontend/controller/applications/updatefilter_test.go @@ -26,8 +26,8 @@ import ( "github.com/radius-project/radius/pkg/armrpc/rpctest" "github.com/radius-project/radius/pkg/corerp/datamodel" rpv1 "github.com/radius-project/radius/pkg/rp/v1" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/store" "github.com/radius-project/radius/test/k8sutil" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" @@ -44,8 +44,8 @@ func TestCreateAppScopedNamespace_valid_namespace(t *testing.T) { tCtx := rpctest.NewControllerContext(t) opts := ctrl.Options{ - StorageClient: tCtx.MockSC, - KubeClient: k8sutil.NewFakeKubeClient(nil), + DatabaseClient: tCtx.MockSC, + KubeClient: k8sutil.NewFakeKubeClient(nil), } t.Run("override namespace", func(t *testing.T) { @@ -68,9 +68,9 @@ func TestCreateAppScopedNamespace_valid_namespace(t *testing.T) { tCtx.MockSC. EXPECT(). Query(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, query store.Query, options ...store.QueryOptions) (*store.ObjectQueryResult, error) { - return &store.ObjectQueryResult{ - Items: []store.Object{}, + DoAndReturn(func(ctx context.Context, query database.Query, options ...database.QueryOptions) (*database.ObjectQueryResult, error) { + return &database.ObjectQueryResult{ + Items: []database.Object{}, }, nil }).Times(2) @@ -104,9 +104,9 @@ func TestCreateAppScopedNamespace_valid_namespace(t *testing.T) { tCtx.MockSC. EXPECT(). Query(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, query store.Query, options ...store.QueryOptions) (*store.ObjectQueryResult, error) { - return &store.ObjectQueryResult{ - Items: []store.Object{}, + DoAndReturn(func(ctx context.Context, query database.Query, options ...database.QueryOptions) (*database.ObjectQueryResult, error) { + return &database.ObjectQueryResult{ + Items: []database.Object{}, }, nil }).Times(2) @@ -151,8 +151,8 @@ func TestCreateAppScopedNamespace_invalid_property(t *testing.T) { tCtx := rpctest.NewControllerContext(t) opts := ctrl.Options{ - StorageClient: tCtx.MockSC, - KubeClient: k8sutil.NewFakeKubeClient(nil), + DatabaseClient: tCtx.MockSC, + KubeClient: k8sutil.NewFakeKubeClient(nil), } t.Run("generated namespace is invalid", func(t *testing.T) { @@ -195,9 +195,9 @@ func TestCreateAppScopedNamespace_invalid_property(t *testing.T) { t.Run("invalid namespace", func(t *testing.T) { tCtx.MockSC.EXPECT(). Query(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, query store.Query, options ...store.QueryOptions) (*store.ObjectQueryResult, error) { - return &store.ObjectQueryResult{ - Items: []store.Object{}, + DoAndReturn(func(ctx context.Context, query database.Query, options ...database.QueryOptions) (*database.ObjectQueryResult, error) { + return &database.ObjectQueryResult{ + Items: []database.Object{}, }, nil }).Times(2) @@ -240,9 +240,9 @@ func TestCreateAppScopedNamespace_invalid_property(t *testing.T) { tCtx.MockSC.EXPECT(). Query(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, query store.Query, options ...store.QueryOptions) (*store.ObjectQueryResult, error) { - return &store.ObjectQueryResult{ - Items: []store.Object{*rpctest.FakeStoreObject(envdm)}, + DoAndReturn(func(ctx context.Context, query database.Query, options ...database.QueryOptions) (*database.ObjectQueryResult, error) { + return &database.ObjectQueryResult{ + Items: []database.Object{*rpctest.FakeStoreObject(envdm)}, }, nil }).Times(1) @@ -293,10 +293,10 @@ func TestCreateAppScopedNamespace_invalid_property(t *testing.T) { tCtx.MockSC.EXPECT(). Query(gomock.Any(), gomock.Any()). - Return(&store.ObjectQueryResult{}, nil).Times(1) + Return(&database.ObjectQueryResult{}, nil).Times(1) tCtx.MockSC.EXPECT(). Query(gomock.Any(), gomock.Any()). - Return(&store.ObjectQueryResult{Items: []store.Object{*rpctest.FakeStoreObject(newResource)}}, nil).Times(1) + Return(&database.ObjectQueryResult{Items: []database.Object{*rpctest.FakeStoreObject(newResource)}}, nil).Times(1) id, err := resources.ParseResource(testAppID) require.NoError(t, err) @@ -347,7 +347,7 @@ func TestCreateAppScopedNamespace_invalid_property(t *testing.T) { tCtx.MockSC.EXPECT(). Query(gomock.Any(), gomock.Any()). - Return(&store.ObjectQueryResult{}, nil).Times(2) + Return(&database.ObjectQueryResult{}, nil).Times(2) id, err := resources.ParseResource(testAppID) require.NoError(t, err) diff --git a/pkg/corerp/frontend/controller/environments/createorupdateenvironment.go b/pkg/corerp/frontend/controller/environments/createorupdateenvironment.go index c011b2675d..4c291ca8ed 100644 --- a/pkg/corerp/frontend/controller/environments/createorupdateenvironment.go +++ b/pkg/corerp/frontend/controller/environments/createorupdateenvironment.go @@ -71,7 +71,7 @@ func (e *CreateOrUpdateEnvironment) Run(ctx context.Context, w http.ResponseWrit // Create Query filter to query kubernetes namespace used by the other environment resources. namespace := newResource.Properties.Compute.KubernetesCompute.Namespace - result, err := util.FindResources(ctx, serviceCtx.ResourceID.RootScope(), serviceCtx.ResourceID.Type(), "properties.compute.kubernetes.namespace", namespace, e.StorageClient()) + result, err := util.FindResources(ctx, serviceCtx.ResourceID.RootScope(), serviceCtx.ResourceID.Type(), "properties.compute.kubernetes.namespace", namespace, e.DatabaseClient()) if err != nil { return nil, err } diff --git a/pkg/corerp/frontend/controller/environments/createorupdateenvironment_test.go b/pkg/corerp/frontend/controller/environments/createorupdateenvironment_test.go index 3854012b2f..13e12d07ce 100644 --- a/pkg/corerp/frontend/controller/environments/createorupdateenvironment_test.go +++ b/pkg/corerp/frontend/controller/environments/createorupdateenvironment_test.go @@ -27,7 +27,7 @@ import ( ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rpctest" "github.com/radius-project/radius/pkg/corerp/api/v20231001preview" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/google/uuid" "github.com/stretchr/testify/require" @@ -38,7 +38,7 @@ func TestCreateOrUpdateEnvironmentRun_20231001Preview(t *testing.T) { mctrl := gomock.NewController(t) defer mctrl.Finish() - mStorageClient := store.NewMockStorageClient(mctrl) + databaseClient := database.NewMockClient(mctrl) ctx := context.Background() createNewResourceCases := []struct { @@ -64,20 +64,20 @@ func TestCreateOrUpdateEnvironmentRun_20231001Preview(t *testing.T) { req.Header.Set(tt.headerKey, tt.headerValue) ctx := rpctest.NewARMRequestContext(req) - mStorageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { - return nil, &store.ErrNotFound{ID: id} + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { + return nil, &database.ErrNotFound{ID: id} }) if !tt.shouldFail { - mStorageClient. + databaseClient. EXPECT(). Query(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, query store.Query, options ...store.QueryOptions) (*store.ObjectQueryResult, error) { - return &store.ObjectQueryResult{ - Items: []store.Object{}, + DoAndReturn(func(ctx context.Context, query database.Query, options ...database.QueryOptions) (*database.ObjectQueryResult, error) { + return &database.ObjectQueryResult{ + Items: []database.Object{}, }, nil }) } @@ -87,10 +87,10 @@ func TestCreateOrUpdateEnvironmentRun_20231001Preview(t *testing.T) { expectedOutput.SystemData.CreatedByType = expectedOutput.SystemData.LastModifiedByType if !tt.shouldFail { - mStorageClient. + databaseClient. EXPECT(). Save(gomock.Any(), gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, obj *store.Object, opts ...store.SaveOptions) error { + DoAndReturn(func(ctx context.Context, obj *database.Object, opts ...database.SaveOptions) error { obj.ETag = "new-resource-etag" obj.Data = envDataModel return nil @@ -98,7 +98,7 @@ func TestCreateOrUpdateEnvironmentRun_20231001Preview(t *testing.T) { } opts := ctrl.Options{ - StorageClient: mStorageClient, + DatabaseClient: databaseClient, } ctl, err := NewCreateOrUpdateEnvironment(opts) @@ -142,32 +142,32 @@ func TestCreateOrUpdateEnvironmentRun_20231001Preview(t *testing.T) { req.Header.Set(tt.headerKey, tt.headerValue) ctx := rpctest.NewARMRequestContext(req) - mStorageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { - return &store.Object{ - Metadata: store.Metadata{ID: id, ETag: tt.resourceETag}, + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { + return &database.Object{ + Metadata: database.Metadata{ID: id, ETag: tt.resourceETag}, Data: envDataModel, }, nil }) if !tt.shouldFail { - mStorageClient. + databaseClient. EXPECT(). Query(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, query store.Query, options ...store.QueryOptions) (*store.ObjectQueryResult, error) { - return &store.ObjectQueryResult{ - Items: []store.Object{}, + DoAndReturn(func(ctx context.Context, query database.Query, options ...database.QueryOptions) (*database.ObjectQueryResult, error) { + return &database.ObjectQueryResult{ + Items: []database.Object{}, }, nil }) } if !tt.shouldFail { - mStorageClient. + databaseClient. EXPECT(). Save(gomock.Any(), gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, obj *store.Object, opts ...store.SaveOptions) error { + DoAndReturn(func(ctx context.Context, obj *database.Object, opts ...database.SaveOptions) error { obj.ETag = "updated-resource-etag" obj.Data = envDataModel return nil @@ -175,7 +175,7 @@ func TestCreateOrUpdateEnvironmentRun_20231001Preview(t *testing.T) { } opts := ctrl.Options{ - StorageClient: mStorageClient, + DatabaseClient: databaseClient, } ctl, err := NewCreateOrUpdateEnvironment(opts) @@ -219,26 +219,26 @@ func TestCreateOrUpdateEnvironmentRun_20231001Preview(t *testing.T) { req.Header.Set(tt.headerKey, tt.headerValue) ctx := rpctest.NewARMRequestContext(req) - mStorageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { - return nil, &store.ErrNotFound{ID: id} + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { + return nil, &database.ErrNotFound{ID: id} }) if !tt.shouldFail { - mStorageClient. + databaseClient. EXPECT(). Query(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, query store.Query, options ...store.QueryOptions) (*store.ObjectQueryResult, error) { - return &store.ObjectQueryResult{ - Items: []store.Object{}, + DoAndReturn(func(ctx context.Context, query database.Query, options ...database.QueryOptions) (*database.ObjectQueryResult, error) { + return &database.ObjectQueryResult{ + Items: []database.Object{}, }, nil }) } opts := ctrl.Options{ - StorageClient: mStorageClient, + DatabaseClient: databaseClient, } ctl, err := NewCreateOrUpdateEnvironment(opts) @@ -273,33 +273,33 @@ func TestCreateOrUpdateEnvironmentRun_20231001Preview(t *testing.T) { req.Header.Set(tt.headerKey, tt.headerValue) ctx := rpctest.NewARMRequestContext(req) - mStorageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { - return &store.Object{ - Metadata: store.Metadata{ID: id, ETag: tt.resourceEtag}, + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { + return &database.Object{ + Metadata: database.Metadata{ID: id, ETag: tt.resourceEtag}, Data: envDataModel, }, nil }) if !tt.shouldFail { - mStorageClient. + databaseClient. EXPECT(). Query(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, query store.Query, options ...store.QueryOptions) (*store.ObjectQueryResult, error) { - return &store.ObjectQueryResult{ - Items: []store.Object{}, + DoAndReturn(func(ctx context.Context, query database.Query, options ...database.QueryOptions) (*database.ObjectQueryResult, error) { + return &database.ObjectQueryResult{ + Items: []database.Object{}, }, nil }) } if !tt.shouldFail { - mStorageClient. + databaseClient. EXPECT(). Save(gomock.Any(), gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, obj *store.Object, opts ...store.SaveOptions) error { - cfg := store.NewSaveConfig(opts...) + DoAndReturn(func(ctx context.Context, obj *database.Object, opts ...database.SaveOptions) error { + cfg := database.NewSaveConfig(opts...) obj.ETag = cfg.ETag obj.Data = envDataModel return nil @@ -307,7 +307,7 @@ func TestCreateOrUpdateEnvironmentRun_20231001Preview(t *testing.T) { } opts := ctrl.Options{ - StorageClient: mStorageClient, + DatabaseClient: databaseClient, } ctl, err := NewCreateOrUpdateEnvironment(opts) @@ -352,43 +352,43 @@ func TestCreateOrUpdateEnvironmentRun_20231001Preview(t *testing.T) { req.Header.Set(tt.headerKey, tt.headerValue) ctx := rpctest.NewARMRequestContext(req) - mStorageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { - return &store.Object{ - Metadata: store.Metadata{ID: id, ETag: tt.resourceEtag}, + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { + return &database.Object{ + Metadata: database.Metadata{ID: id, ETag: tt.resourceEtag}, Data: envDataModel, }, nil }) paginationToken := "nextLink" - items := []store.Object{ + items := []database.Object{ { - Metadata: store.Metadata{ + Metadata: database.Metadata{ ID: uuid.New().String(), }, Data: conflictDataModel, }, } - mStorageClient. + databaseClient. EXPECT(). Query(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, query store.Query, options ...store.QueryOptions) (*store.ObjectQueryResult, error) { - return &store.ObjectQueryResult{ + DoAndReturn(func(ctx context.Context, query database.Query, options ...database.QueryOptions) (*database.ObjectQueryResult, error) { + return &database.ObjectQueryResult{ Items: items, PaginationToken: paginationToken, }, nil }) if !tt.shouldFail { - mStorageClient. + databaseClient. EXPECT(). Save(gomock.Any(), gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, obj *store.Object, opts ...store.SaveOptions) error { - cfg := store.NewSaveConfig(opts...) + DoAndReturn(func(ctx context.Context, obj *database.Object, opts ...database.SaveOptions) error { + cfg := database.NewSaveConfig(opts...) obj.ETag = cfg.ETag obj.Data = envDataModel return nil @@ -396,7 +396,7 @@ func TestCreateOrUpdateEnvironmentRun_20231001Preview(t *testing.T) { } opts := ctrl.Options{ - StorageClient: mStorageClient, + DatabaseClient: databaseClient, } ctl, err := NewCreateOrUpdateEnvironment(opts) diff --git a/pkg/corerp/frontend/controller/environments/getrecipemetadata_test.go b/pkg/corerp/frontend/controller/environments/getrecipemetadata_test.go index ae08874f91..4d33f65af7 100644 --- a/pkg/corerp/frontend/controller/environments/getrecipemetadata_test.go +++ b/pkg/corerp/frontend/controller/environments/getrecipemetadata_test.go @@ -30,7 +30,7 @@ import ( "github.com/radius-project/radius/pkg/corerp/api/v20231001preview" "github.com/radius-project/radius/pkg/recipes" "github.com/radius-project/radius/pkg/recipes/engine" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/test/testutil" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -39,7 +39,7 @@ import ( func TestGetRecipeMetadataRun_20231001Preview(t *testing.T) { mctrl := gomock.NewController(t) defer mctrl.Finish() - mStorageClient := store.NewMockStorageClient(mctrl) + databaseClient := database.NewMockClient(mctrl) mEngine := engine.NewMockEngine(mctrl) ctx := context.Background() t.Parallel() @@ -49,12 +49,12 @@ func TestGetRecipeMetadataRun_20231001Preview(t *testing.T) { req, err := rpctest.NewHTTPRequestFromJSON(ctx, v1.OperationPost.HTTPMethod(), testHeaderfilegetrecipemetadata, envInput) require.NoError(t, err) - mStorageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { - return &store.Object{ - Metadata: store.Metadata{ID: id, ETag: "etag"}, + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { + return &database.Object{ + Metadata: database.Metadata{ID: id, ETag: "etag"}, Data: envDataModel, }, nil }) @@ -84,7 +84,7 @@ func TestGetRecipeMetadataRun_20231001Preview(t *testing.T) { }).Return(recipeData, nil) opts := ctrl.Options{ - StorageClient: mStorageClient, + DatabaseClient: databaseClient, } ctl, err := NewGetRecipeMetadata(opts, mEngine) require.NoError(t, err) @@ -104,12 +104,12 @@ func TestGetRecipeMetadataRun_20231001Preview(t *testing.T) { req, err := rpctest.NewHTTPRequestFromJSON(ctx, v1.OperationPost.HTTPMethod(), testHeaderfilegetrecipemetadata, envInput) require.NoError(t, err) - mStorageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { - return &store.Object{ - Metadata: store.Metadata{ID: id, ETag: "etag"}, + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { + return &database.Object{ + Metadata: database.Metadata{ID: id, ETag: "etag"}, Data: envDataModel, }, nil }) @@ -139,7 +139,7 @@ func TestGetRecipeMetadataRun_20231001Preview(t *testing.T) { }).Return(recipeData, nil) opts := ctrl.Options{ - StorageClient: mStorageClient, + DatabaseClient: databaseClient, } ctl, err := NewGetRecipeMetadata(opts, mEngine) require.NoError(t, err) @@ -159,14 +159,14 @@ func TestGetRecipeMetadataRun_20231001Preview(t *testing.T) { require.NoError(t, err) ctx := rpctest.NewARMRequestContext(req) - mStorageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { - return nil, &store.ErrNotFound{ID: id} + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { + return nil, &database.ErrNotFound{ID: id} }) opts := ctrl.Options{ - StorageClient: mStorageClient, + DatabaseClient: databaseClient, } ctl, err := NewGetRecipeMetadata(opts, mEngine) require.NoError(t, err) @@ -196,18 +196,18 @@ func TestGetRecipeMetadataRun_20231001Preview(t *testing.T) { require.NoError(t, err) ctx := rpctest.NewARMRequestContext(req) - mStorageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { - return &store.Object{ - Metadata: store.Metadata{ID: id, ETag: "etag"}, + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { + return &database.Object{ + Metadata: database.Metadata{ID: id, ETag: "etag"}, Data: envDataModel, }, nil }) opts := ctrl.Options{ - StorageClient: mStorageClient, + DatabaseClient: databaseClient, } ctl, err := NewGetRecipeMetadata(opts, mEngine) require.NoError(t, err) @@ -235,12 +235,12 @@ func TestGetRecipeMetadataRun_20231001Preview(t *testing.T) { req, err := rpctest.NewHTTPRequestFromJSON(ctx, v1.OperationPost.HTTPMethod(), testHeaderfilegetrecipemetadata, envInput) require.NoError(t, err) - mStorageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { - return &store.Object{ - Metadata: store.Metadata{ID: id, ETag: "etag"}, + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { + return &database.Object{ + Metadata: database.Metadata{ID: id, ETag: "etag"}, Data: envDataModel, }, nil }) @@ -264,7 +264,7 @@ func TestGetRecipeMetadataRun_20231001Preview(t *testing.T) { }).Return(nil, engineErr) opts := ctrl.Options{ - StorageClient: mStorageClient, + DatabaseClient: databaseClient, } ctl, err := NewGetRecipeMetadata(opts, mEngine) require.NoError(t, err) diff --git a/pkg/corerp/frontend/controller/extenders/listsecretsextender_test.go b/pkg/corerp/frontend/controller/extenders/listsecretsextender_test.go index bb8ccdfcb5..8dfd721f9a 100644 --- a/pkg/corerp/frontend/controller/extenders/listsecretsextender_test.go +++ b/pkg/corerp/frontend/controller/extenders/listsecretsextender_test.go @@ -27,16 +27,16 @@ import ( "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rpctest" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" ) func TestListSecrets_20231001Preview(t *testing.T) { - setupTest := func() (func(tb testing.TB), *store.MockStorageClient, *statusmanager.MockStatusManager) { + setupTest := func() (func(tb testing.TB), *database.MockClient, *statusmanager.MockStatusManager) { mctrl := gomock.NewController(t) - mds := store.NewMockStorageClient(mctrl) + mds := database.NewMockClient(mctrl) msm := statusmanager.NewMockStatusManager(mctrl) return func(tb testing.TB) { @@ -62,13 +62,13 @@ func TestListSecrets_20231001Preview(t *testing.T) { mds. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { - return nil, &store.ErrNotFound{ID: id} + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { + return nil, &database.ErrNotFound{ID: id} }) opts := ctrl.Options{ - StorageClient: mds, - StatusManager: msm, + DatabaseClient: mds, + StatusManager: msm, } ctl, err := NewListSecretsExtender(opts) @@ -91,16 +91,16 @@ func TestListSecrets_20231001Preview(t *testing.T) { mds. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { - return &store.Object{ - Metadata: store.Metadata{ID: id}, + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { + return &database.Object{ + Metadata: database.Metadata{ID: id}, Data: extenderDataModel, }, nil }) opts := ctrl.Options{ - StorageClient: mds, - StatusManager: msm, + DatabaseClient: mds, + StatusManager: msm, } ctl, err := NewListSecretsExtender(opts) @@ -129,13 +129,13 @@ func TestListSecrets_20231001Preview(t *testing.T) { mds. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { return nil, errors.New("failed to get the resource from data store") }) opts := ctrl.Options{ - StorageClient: mds, - StatusManager: msm, + DatabaseClient: mds, + StatusManager: msm, } ctl, err := NewListSecretsExtender(opts) diff --git a/pkg/corerp/frontend/controller/secretstores/kubernetes.go b/pkg/corerp/frontend/controller/secretstores/kubernetes.go index 62296b5ec6..a5b8448e46 100644 --- a/pkg/corerp/frontend/controller/secretstores/kubernetes.go +++ b/pkg/corerp/frontend/controller/secretstores/kubernetes.go @@ -30,9 +30,9 @@ import ( "github.com/radius-project/radius/pkg/kubeutil" rpv1 "github.com/radius-project/radius/pkg/rp/v1" "github.com/radius-project/radius/pkg/to" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/resources" resources_kubernetes "github.com/radius-project/radius/pkg/ucp/resources/kubernetes" - "github.com/radius-project/radius/pkg/ucp/store" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -141,7 +141,7 @@ func ValidateAndMutateRequest(ctx context.Context, newResource *datamodel.Secret func getNamespace(ctx context.Context, res *datamodel.SecretStore, options *controller.Options) (string, error) { prop := res.Properties if prop.Application != "" { - app, err := store.GetResource[datamodel.Application](ctx, options.StorageClient, prop.Application) + app, err := database.GetResource[datamodel.Application](ctx, options.DatabaseClient, prop.Application) if err != nil { return "", err } @@ -152,7 +152,7 @@ func getNamespace(ctx context.Context, res *datamodel.SecretStore, options *cont } if prop.Environment != "" { - env, err := store.GetResource[datamodel.Environment](ctx, options.StorageClient, prop.Environment) + env, err := database.GetResource[datamodel.Environment](ctx, options.DatabaseClient, prop.Environment) if err != nil { return "", err } diff --git a/pkg/corerp/frontend/controller/secretstores/kubernetes_test.go b/pkg/corerp/frontend/controller/secretstores/kubernetes_test.go index 676107a91b..6ead89f9fc 100644 --- a/pkg/corerp/frontend/controller/secretstores/kubernetes_test.go +++ b/pkg/corerp/frontend/controller/secretstores/kubernetes_test.go @@ -27,8 +27,8 @@ import ( "github.com/radius-project/radius/pkg/corerp/datamodel" "github.com/radius-project/radius/pkg/kubernetes" rpv1 "github.com/radius-project/radius/pkg/rp/v1" + "github.com/radius-project/radius/pkg/ucp/database" resources_kubernetes "github.com/radius-project/radius/pkg/ucp/resources/kubernetes" - "github.com/radius-project/radius/pkg/ucp/store" "github.com/radius-project/radius/test/k8sutil" "github.com/radius-project/radius/test/testutil" "github.com/stretchr/testify/require" @@ -60,10 +60,10 @@ const ( func TestGetNamespace(t *testing.T) { ctrl := gomock.NewController(t) - sc := store.NewMockStorageClient(ctrl) + sc := database.NewMockClient(ctrl) opt := &controller.Options{ - StorageClient: sc, + DatabaseClient: sc, } t.Run("application-scoped", func(t *testing.T) { @@ -71,7 +71,7 @@ func TestGetNamespace(t *testing.T) { secret.Properties.Application = testAppID appData := testutil.MustGetTestData[any]("app_datamodel.json") - sc.EXPECT().Get(gomock.Any(), testAppID, gomock.Any()).Return(&store.Object{ + sc.EXPECT().Get(gomock.Any(), testAppID, gomock.Any()).Return(&database.Object{ Data: *appData, }, nil) @@ -88,7 +88,7 @@ func TestGetNamespace(t *testing.T) { envData := testutil.MustGetTestData[any]("env_datamodel.json") - sc.EXPECT().Get(gomock.Any(), testEnvID, gomock.Any()).Return(&store.Object{ + sc.EXPECT().Get(gomock.Any(), testEnvID, gomock.Any()).Return(&database.Object{ Data: *envData, }, nil) @@ -103,7 +103,7 @@ func TestGetNamespace(t *testing.T) { secret.Properties.Environment = testEnvID envData := testutil.MustGetTestData[any]("env_nonk8s_datamodel.json") - sc.EXPECT().Get(gomock.Any(), testEnvID, gomock.Any()).Return(&store.Object{ + sc.EXPECT().Get(gomock.Any(), testEnvID, gomock.Any()).Return(&database.Object{ Data: *envData, }, nil) @@ -429,11 +429,11 @@ func TestUpsertSecret(t *testing.T) { t.Run("create new generic resource", func(t *testing.T) { ctrl := gomock.NewController(t) - sc := store.NewMockStorageClient(ctrl) + sc := database.NewMockClient(ctrl) appData := testutil.MustGetTestData[any]("app_datamodel.json") - sc.EXPECT().Get(gomock.Any(), testAppID, gomock.Any()).Return(&store.Object{ + sc.EXPECT().Get(gomock.Any(), testAppID, gomock.Any()).Return(&database.Object{ Data: *appData, }, nil) @@ -441,8 +441,8 @@ func TestUpsertSecret(t *testing.T) { newResource.Properties.Resource = "" opt := &controller.Options{ - StorageClient: sc, - KubeClient: k8sutil.NewFakeKubeClient(nil), + DatabaseClient: sc, + KubeClient: k8sutil.NewFakeKubeClient(nil), } _, err := ValidateAndMutateRequest(context.TODO(), newResource, nil, opt) @@ -473,11 +473,11 @@ func TestUpsertSecret(t *testing.T) { t.Run("create new resource when namespace is missing", func(t *testing.T) { ctrl := gomock.NewController(t) - sc := store.NewMockStorageClient(ctrl) + sc := database.NewMockClient(ctrl) appData := testutil.MustGetTestData[any]("app_datamodel.json") - sc.EXPECT().Get(gomock.Any(), testAppID, gomock.Any()).Return(&store.Object{ + sc.EXPECT().Get(gomock.Any(), testAppID, gomock.Any()).Return(&database.Object{ Data: *appData, }, nil) @@ -487,8 +487,8 @@ func TestUpsertSecret(t *testing.T) { newResource.Properties.Resource = "secret0" opt := &controller.Options{ - StorageClient: sc, - KubeClient: k8sutil.NewFakeKubeClient(nil), + DatabaseClient: sc, + KubeClient: k8sutil.NewFakeKubeClient(nil), } _, err := ValidateAndMutateRequest(context.TODO(), newResource, nil, opt) @@ -502,11 +502,11 @@ func TestUpsertSecret(t *testing.T) { t.Run("unmatched resource when namespace is missing in new resource", func(t *testing.T) { ctrl := gomock.NewController(t) - sc := store.NewMockStorageClient(ctrl) + sc := database.NewMockClient(ctrl) appData := testutil.MustGetTestData[any]("app_datamodel.json") - sc.EXPECT().Get(gomock.Any(), testAppID, gomock.Any()).Return(&store.Object{ + sc.EXPECT().Get(gomock.Any(), testAppID, gomock.Any()).Return(&database.Object{ Data: *appData, }, nil) @@ -516,8 +516,8 @@ func TestUpsertSecret(t *testing.T) { newResource.Properties.Resource = "secret1" opt := &controller.Options{ - StorageClient: sc, - KubeClient: k8sutil.NewFakeKubeClient(nil), + DatabaseClient: sc, + KubeClient: k8sutil.NewFakeKubeClient(nil), } _, err := ValidateAndMutateRequest(context.TODO(), newResource, nil, opt) @@ -532,13 +532,13 @@ func TestUpsertSecret(t *testing.T) { t.Run("create a new secret resource with global scope", func(t *testing.T) { ctrl := gomock.NewController(t) - sc := store.NewMockStorageClient(ctrl) + sc := database.NewMockClient(ctrl) newResource := testutil.MustGetTestData[datamodel.SecretStore](testFileGenericValueGlobalScope) opt := &controller.Options{ - StorageClient: sc, - KubeClient: k8sutil.NewFakeKubeClient(nil), + DatabaseClient: sc, + KubeClient: k8sutil.NewFakeKubeClient(nil), } _, err := ValidateAndMutateRequest(context.TODO(), newResource, nil, opt) @@ -569,13 +569,13 @@ func TestUpsertSecret(t *testing.T) { t.Run("create a new secret resource with invalid resource", func(t *testing.T) { ctrl := gomock.NewController(t) - sc := store.NewMockStorageClient(ctrl) + sc := database.NewMockClient(ctrl) newResource := testutil.MustGetTestData[datamodel.SecretStore](testFileGenericValueInvalidResource) opt := &controller.Options{ - StorageClient: sc, - KubeClient: k8sutil.NewFakeKubeClient(nil), + DatabaseClient: sc, + KubeClient: k8sutil.NewFakeKubeClient(nil), } _, err := ValidateAndMutateRequest(context.TODO(), newResource, nil, opt) @@ -587,13 +587,13 @@ func TestUpsertSecret(t *testing.T) { t.Run("create a new secret resource with empty resource", func(t *testing.T) { ctrl := gomock.NewController(t) - sc := store.NewMockStorageClient(ctrl) + sc := database.NewMockClient(ctrl) newResource := testutil.MustGetTestData[datamodel.SecretStore](testFileGenericValueEmptyResource) opt := &controller.Options{ - StorageClient: sc, - KubeClient: k8sutil.NewFakeKubeClient(nil), + DatabaseClient: sc, + KubeClient: k8sutil.NewFakeKubeClient(nil), } _, err := ValidateAndMutateRequest(context.TODO(), newResource, nil, opt) diff --git a/pkg/corerp/frontend/controller/secretstores/listsecrets_test.go b/pkg/corerp/frontend/controller/secretstores/listsecrets_test.go index 99b7770afa..df1551d4d4 100644 --- a/pkg/corerp/frontend/controller/secretstores/listsecrets_test.go +++ b/pkg/corerp/frontend/controller/secretstores/listsecrets_test.go @@ -28,7 +28,7 @@ import ( "github.com/radius-project/radius/pkg/armrpc/rpctest" "github.com/radius-project/radius/pkg/corerp/api/v20231001preview" "github.com/radius-project/radius/pkg/corerp/datamodel" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/test/k8sutil" "github.com/radius-project/radius/test/testutil" "github.com/stretchr/testify/require" @@ -41,7 +41,7 @@ func TestListSecrets_20231001Preview(t *testing.T) { mctrl := gomock.NewController(t) defer mctrl.Finish() - mStorageClient := store.NewMockStorageClient(mctrl) + databaseClient := database.NewMockClient(mctrl) req, err := rpctest.NewHTTPRequestWithContent( context.Background(), v1.OperationPost.HTTPMethod(), @@ -49,13 +49,13 @@ func TestListSecrets_20231001Preview(t *testing.T) { require.NoError(t, err) t.Run("not found the resource", func(t *testing.T) { - mStorageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - Return(nil, &store.ErrNotFound{}) + Return(nil, &database.ErrNotFound{}) ctx := rpctest.NewARMRequestContext(req) opts := ctrl.Options{ - StorageClient: mStorageClient, + DatabaseClient: databaseClient, } ctl, err := NewListSecrets(opts) @@ -71,12 +71,12 @@ func TestListSecrets_20231001Preview(t *testing.T) { t.Run("return secrets successfully", func(t *testing.T) { secretdm := testutil.MustGetTestData[datamodel.SecretStore](testFileCertValueFrom) - mStorageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { - return &store.Object{ - Metadata: store.Metadata{ID: id, ETag: "etag"}, + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { + return &database.Object{ + Metadata: database.Metadata{ID: id, ETag: "etag"}, Data: secretdm, }, nil }) @@ -92,8 +92,8 @@ func TestListSecrets_20231001Preview(t *testing.T) { }, } opts := ctrl.Options{ - StorageClient: mStorageClient, - KubeClient: k8sutil.NewFakeKubeClient(nil, ksecret), + DatabaseClient: databaseClient, + KubeClient: k8sutil.NewFakeKubeClient(nil, ksecret), } ctl, err := NewListSecrets(opts) @@ -115,7 +115,7 @@ func TestListSecrets_InvalidKubernetesSecret(t *testing.T) { mctrl := gomock.NewController(t) defer mctrl.Finish() - mStorageClient := store.NewMockStorageClient(mctrl) + databaseClient := database.NewMockClient(mctrl) req, err := rpctest.NewHTTPRequestWithContent( context.Background(), v1.OperationPost.HTTPMethod(), @@ -172,19 +172,19 @@ func TestListSecrets_InvalidKubernetesSecret(t *testing.T) { for _, tc := range kubeSecretTests { t.Run(tc.name, func(t *testing.T) { - mStorageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { - return &store.Object{ - Metadata: store.Metadata{ID: id, ETag: "etag"}, + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { + return &database.Object{ + Metadata: database.Metadata{ID: id, ETag: "etag"}, Data: secretdm, }, nil }) ctx := rpctest.NewARMRequestContext(req) opts := ctrl.Options{ - StorageClient: mStorageClient, - KubeClient: k8sutil.NewFakeKubeClient(nil, tc.in), + DatabaseClient: databaseClient, + KubeClient: k8sutil.NewFakeKubeClient(nil, tc.in), } ctl, err := NewListSecrets(opts) diff --git a/pkg/corerp/frontend/controller/util/query.go b/pkg/corerp/frontend/controller/util/query.go index a6a8c76f94..1ed1209bab 100644 --- a/pkg/corerp/frontend/controller/util/query.go +++ b/pkg/corerp/frontend/controller/util/query.go @@ -19,20 +19,20 @@ package util import ( "context" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/database" ) // FindResources searches for resources of a given type with a given filter key and value, and returns the query result. -func FindResources(ctx context.Context, rootScope, resourceType, filterKey, filterValue string, storageClient store.StorageClient) (*store.ObjectQueryResult, error) { - query := store.Query{ +func FindResources(ctx context.Context, rootScope, resourceType, filterKey, filterValue string, databaseClient database.Client) (*database.ObjectQueryResult, error) { + query := database.Query{ RootScope: rootScope, ResourceType: resourceType, - Filters: []store.QueryFilter{ + Filters: []database.QueryFilter{ { Field: filterKey, Value: filterValue, }, }, } - return storageClient.Query(ctx, query) + return databaseClient.Query(ctx, query) } diff --git a/pkg/corerp/setup/setup_test.go b/pkg/corerp/setup/setup_test.go index 203ef7681a..fa970df43f 100644 --- a/pkg/corerp/setup/setup_test.go +++ b/pkg/corerp/setup/setup_test.go @@ -32,7 +32,7 @@ import ( "github.com/radius-project/radius/pkg/armrpc/rpctest" "github.com/radius-project/radius/pkg/recipes/controllerconfig" "github.com/radius-project/radius/pkg/sdk" - "github.com/radius-project/radius/pkg/ucp/store/inmemory" + "github.com/radius-project/radius/pkg/ucp/database/inmemory" app_ctrl "github.com/radius-project/radius/pkg/corerp/frontend/controller/applications" ctr_ctrl "github.com/radius-project/radius/pkg/corerp/frontend/controller/containers" @@ -225,10 +225,10 @@ func TestRouter(t *testing.T) { require.NoError(t, err) options := apictrl.Options{ - Address: "localhost:9000", - PathBase: "/api.ucp.dev", - StorageClient: inmemory.NewClient(), - StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), + Address: "localhost:9000", + PathBase: "/api.ucp.dev", + DatabaseClient: inmemory.NewClient(), + StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), } return r, nsBuilder.ApplyAPIHandlers(ctx, r, options, validator) diff --git a/pkg/daprrp/setup/setup_test.go b/pkg/daprrp/setup/setup_test.go index b55cd010d6..d12e79dee4 100644 --- a/pkg/daprrp/setup/setup_test.go +++ b/pkg/daprrp/setup/setup_test.go @@ -32,7 +32,7 @@ import ( "github.com/radius-project/radius/pkg/armrpc/rpctest" dapr_ctrl "github.com/radius-project/radius/pkg/daprrp/frontend/controller" "github.com/radius-project/radius/pkg/recipes/controllerconfig" - "github.com/radius-project/radius/pkg/ucp/store/inmemory" + "github.com/radius-project/radius/pkg/ucp/database/inmemory" ) var handlerTests = []rpctest.HandlerTestSpec{ @@ -147,10 +147,10 @@ func TestRouter(t *testing.T) { require.NoError(t, err) options := apictrl.Options{ - Address: "localhost:9000", - PathBase: "/api.ucp.dev", - StorageClient: inmemory.NewClient(), - StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), + Address: "localhost:9000", + PathBase: "/api.ucp.dev", + DatabaseClient: inmemory.NewClient(), + StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), } return r, nsBuilder.ApplyAPIHandlers(ctx, r, options, validator) diff --git a/pkg/datastoresrp/frontend/controller/mongodatabases/listsecretsmongodatabase_test.go b/pkg/datastoresrp/frontend/controller/mongodatabases/listsecretsmongodatabase_test.go index c339139442..938b08d255 100644 --- a/pkg/datastoresrp/frontend/controller/mongodatabases/listsecretsmongodatabase_test.go +++ b/pkg/datastoresrp/frontend/controller/mongodatabases/listsecretsmongodatabase_test.go @@ -29,7 +29,7 @@ import ( "github.com/radius-project/radius/pkg/armrpc/rpctest" "github.com/radius-project/radius/pkg/datastoresrp/api/v20231001preview" "github.com/radius-project/radius/pkg/portableresources/renderers" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -39,7 +39,7 @@ func TestListSecrets_20231001Preview(t *testing.T) { mctrl := gomock.NewController(t) defer mctrl.Finish() - mStorageClient := store.NewMockStorageClient(mctrl) + databaseClient := database.NewMockClient(mctrl) ctx := context.Background() _, mongoDataModel, _ := getTestModels20231001preview() @@ -50,15 +50,15 @@ func TestListSecrets_20231001Preview(t *testing.T) { require.NoError(t, err) ctx := rpctest.NewARMRequestContext(req) - mStorageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { - return nil, &store.ErrNotFound{ID: id} + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { + return nil, &database.ErrNotFound{ID: id} }) opts := ctrl.Options{ - StorageClient: mStorageClient, + DatabaseClient: databaseClient, } ctl, err := NewListSecretsMongoDatabase(opts) @@ -82,18 +82,18 @@ func TestListSecrets_20231001Preview(t *testing.T) { renderers.ConnectionStringValue: "mongodb://testUser:testPassword@testAccount1.mongo.cosmos.azure.com:10255", } - mStorageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { - return &store.Object{ - Metadata: store.Metadata{ID: id}, + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { + return &database.Object{ + Metadata: database.Metadata{ID: id}, Data: mongoDataModel, }, nil }) opts := ctrl.Options{ - StorageClient: mStorageClient, + DatabaseClient: databaseClient, } ctl, err := NewListSecretsMongoDatabase(opts) @@ -122,18 +122,18 @@ func TestListSecrets_20231001Preview(t *testing.T) { renderers.ConnectionStringValue: "mongodb://testUser:testPassword@testAccount1.mongo.cosmos.azure.com:10255", } - mStorageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { - return &store.Object{ - Metadata: store.Metadata{ID: id}, + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { + return &database.Object{ + Metadata: database.Metadata{ID: id}, Data: mongoDataModel, }, nil }) opts := ctrl.Options{ - StorageClient: mStorageClient, + DatabaseClient: databaseClient, } ctl, err := NewListSecretsMongoDatabase(opts) @@ -157,15 +157,15 @@ func TestListSecrets_20231001Preview(t *testing.T) { ctx := rpctest.NewARMRequestContext(req) w := httptest.NewRecorder() - mStorageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { return nil, errors.New("failed to get the resource from data store") }) opts := ctrl.Options{ - StorageClient: mStorageClient, + DatabaseClient: databaseClient, } ctl, err := NewListSecretsMongoDatabase(opts) @@ -183,18 +183,18 @@ func TestListSecrets_20231001Preview(t *testing.T) { sCtx := v1.ARMRequestContextFromContext(ctx) sCtx.APIVersion = "invalid-api-version" - mStorageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { - return &store.Object{ - Metadata: store.Metadata{ID: id}, + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { + return &database.Object{ + Metadata: database.Metadata{ID: id}, Data: mongoDataModel, }, nil }) opts := ctrl.Options{ - StorageClient: mStorageClient, + DatabaseClient: databaseClient, } ctl, err := NewListSecretsMongoDatabase(opts) diff --git a/pkg/datastoresrp/frontend/controller/rediscaches/listsecretsrediscache.go b/pkg/datastoresrp/frontend/controller/rediscaches/listsecretsrediscache.go index 69e17a838d..89cd7a91ec 100644 --- a/pkg/datastoresrp/frontend/controller/rediscaches/listsecretsrediscache.go +++ b/pkg/datastoresrp/frontend/controller/rediscaches/listsecretsrediscache.go @@ -27,7 +27,7 @@ import ( "github.com/radius-project/radius/pkg/datastoresrp/datamodel" "github.com/radius-project/radius/pkg/datastoresrp/datamodel/converter" "github.com/radius-project/radius/pkg/portableresources/renderers" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/database" ) var _ ctrl.Controller = (*ListSecretsRedisCache)(nil) @@ -57,7 +57,7 @@ func (ctrl *ListSecretsRedisCache) Run(ctx context.Context, w http.ResponseWrite parsedResourceID := sCtx.ResourceID.Truncate() resource, _, err := ctrl.GetResource(ctx, parsedResourceID) if err != nil { - if errors.Is(&store.ErrNotFound{ID: parsedResourceID.String()}, err) { + if errors.Is(&database.ErrNotFound{ID: parsedResourceID.String()}, err) { return rest.NewNotFoundResponse(sCtx.ResourceID), nil } return nil, err diff --git a/pkg/datastoresrp/frontend/controller/rediscaches/listsecretsrediscache_test.go b/pkg/datastoresrp/frontend/controller/rediscaches/listsecretsrediscache_test.go index ffe90e4421..903baac905 100644 --- a/pkg/datastoresrp/frontend/controller/rediscaches/listsecretsrediscache_test.go +++ b/pkg/datastoresrp/frontend/controller/rediscaches/listsecretsrediscache_test.go @@ -29,7 +29,7 @@ import ( "github.com/radius-project/radius/pkg/armrpc/rpctest" "github.com/radius-project/radius/pkg/datastoresrp/api/v20231001preview" "github.com/radius-project/radius/pkg/portableresources/renderers" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -39,7 +39,7 @@ func TestListSecrets_20231001Preview(t *testing.T) { mctrl := gomock.NewController(t) defer mctrl.Finish() - mStorageClient := store.NewMockStorageClient(mctrl) + databaseClient := database.NewMockClient(mctrl) ctx := context.Background() _, redisDataModel, _ := getTestModels20231001preview() @@ -50,15 +50,15 @@ func TestListSecrets_20231001Preview(t *testing.T) { require.NoError(t, err) ctx := rpctest.NewARMRequestContext(req) - mStorageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { - return nil, &store.ErrNotFound{ID: id} + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { + return nil, &database.ErrNotFound{ID: id} }) opts := ctrl.Options{ - StorageClient: mStorageClient, + DatabaseClient: databaseClient, } ctl, err := NewListSecretsRedisCache(opts) require.NoError(t, err) @@ -81,18 +81,18 @@ func TestListSecrets_20231001Preview(t *testing.T) { renderers.ConnectionStringValue: "test-connection-string", } - mStorageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { - return &store.Object{ - Metadata: store.Metadata{ID: id}, + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { + return &database.Object{ + Metadata: database.Metadata{ID: id}, Data: redisDataModel, }, nil }) opts := ctrl.Options{ - StorageClient: mStorageClient, + DatabaseClient: databaseClient, } ctl, err := NewListSecretsRedisCache(opts) @@ -120,18 +120,18 @@ func TestListSecrets_20231001Preview(t *testing.T) { renderers.PasswordStringHolder: "testPassword", } - mStorageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { - return &store.Object{ - Metadata: store.Metadata{ID: id}, + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { + return &database.Object{ + Metadata: database.Metadata{ID: id}, Data: redisDataModel, }, nil }) opts := ctrl.Options{ - StorageClient: mStorageClient, + DatabaseClient: databaseClient, } ctl, err := NewListSecretsRedisCache(opts) @@ -155,15 +155,15 @@ func TestListSecrets_20231001Preview(t *testing.T) { ctx := rpctest.NewARMRequestContext(req) w := httptest.NewRecorder() - mStorageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { return nil, errors.New("failed to get the resource from data store") }) opts := ctrl.Options{ - StorageClient: mStorageClient, + DatabaseClient: databaseClient, } ctl, err := NewListSecretsRedisCache(opts) @@ -181,18 +181,18 @@ func TestListSecrets_20231001Preview(t *testing.T) { sCtx := v1.ARMRequestContextFromContext(ctx) sCtx.APIVersion = "invalid-api-version" - mStorageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { - return &store.Object{ - Metadata: store.Metadata{ID: id}, + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { + return &database.Object{ + Metadata: database.Metadata{ID: id}, Data: redisDataModel, }, nil }) opts := ctrl.Options{ - StorageClient: mStorageClient, + DatabaseClient: databaseClient, } ctl, err := NewListSecretsRedisCache(opts) diff --git a/pkg/datastoresrp/frontend/controller/sqldatabases/listsecretssqldatabase_test.go b/pkg/datastoresrp/frontend/controller/sqldatabases/listsecretssqldatabase_test.go index d43e6fb9eb..cdd3e8bfeb 100644 --- a/pkg/datastoresrp/frontend/controller/sqldatabases/listsecretssqldatabase_test.go +++ b/pkg/datastoresrp/frontend/controller/sqldatabases/listsecretssqldatabase_test.go @@ -28,7 +28,7 @@ import ( ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rpctest" "github.com/radius-project/radius/pkg/datastoresrp/api/v20231001preview" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -43,7 +43,7 @@ func TestListSecrets_20231001Preview(t *testing.T) { mctrl := gomock.NewController(t) defer mctrl.Finish() - mStorageClient := store.NewMockStorageClient(mctrl) + databaseClient := database.NewMockClient(mctrl) ctx := context.Background() _, sqlDataModel, _ := getTestModels20231001preview() @@ -54,15 +54,15 @@ func TestListSecrets_20231001Preview(t *testing.T) { require.NoError(t, err) ctx := rpctest.NewARMRequestContext(req) - mStorageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { - return nil, &store.ErrNotFound{ID: id} + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { + return nil, &database.ErrNotFound{ID: id} }) opts := ctrl.Options{ - StorageClient: mStorageClient, + DatabaseClient: databaseClient, } ctl, err := NewListSecretsSqlDatabase(opts) @@ -84,18 +84,18 @@ func TestListSecrets_20231001Preview(t *testing.T) { connectionStringValue: "Data Source=tcp:testAccount1.sql.cosmos.azure.com,1433;Initial Catalog=testDatabase;User Id=testUser;Password=testPassword;Encrypt=True;TrustServerCertificate=True", } - mStorageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { - return &store.Object{ - Metadata: store.Metadata{ID: id}, + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { + return &database.Object{ + Metadata: database.Metadata{ID: id}, Data: sqlDataModel, }, nil }) opts := ctrl.Options{ - StorageClient: mStorageClient, + DatabaseClient: databaseClient, } ctl, err := NewListSecretsSqlDatabase(opts) @@ -122,18 +122,18 @@ func TestListSecrets_20231001Preview(t *testing.T) { connectionStringValue: "Data Source=tcp:testAccount1.sql.cosmos.azure.com,1433;Initial Catalog=testDatabase;User Id=testUser;Password=testPassword;Encrypt=True;TrustServerCertificate=True", } - mStorageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { - return &store.Object{ - Metadata: store.Metadata{ID: id}, + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { + return &database.Object{ + Metadata: database.Metadata{ID: id}, Data: sqlDataModel, }, nil }) opts := ctrl.Options{ - StorageClient: mStorageClient, + DatabaseClient: databaseClient, } ctl, err := NewListSecretsSqlDatabase(opts) @@ -156,15 +156,15 @@ func TestListSecrets_20231001Preview(t *testing.T) { ctx := rpctest.NewARMRequestContext(req) w := httptest.NewRecorder() - mStorageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { return nil, errors.New("failed to get the resource from data store") }) opts := ctrl.Options{ - StorageClient: mStorageClient, + DatabaseClient: databaseClient, } ctl, err := NewListSecretsSqlDatabase(opts) @@ -182,18 +182,18 @@ func TestListSecrets_20231001Preview(t *testing.T) { sCtx := v1.ARMRequestContextFromContext(ctx) sCtx.APIVersion = "invalid-api-version" - mStorageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { - return &store.Object{ - Metadata: store.Metadata{ID: id}, + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { + return &database.Object{ + Metadata: database.Metadata{ID: id}, Data: sqlDataModel, }, nil }) opts := ctrl.Options{ - StorageClient: mStorageClient, + DatabaseClient: databaseClient, } ctl, err := NewListSecretsSqlDatabase(opts) diff --git a/pkg/datastoresrp/setup/setup_test.go b/pkg/datastoresrp/setup/setup_test.go index d834cc6fbe..918e98e79e 100644 --- a/pkg/datastoresrp/setup/setup_test.go +++ b/pkg/datastoresrp/setup/setup_test.go @@ -32,7 +32,7 @@ import ( "github.com/radius-project/radius/pkg/armrpc/rpctest" ds_ctrl "github.com/radius-project/radius/pkg/datastoresrp/frontend/controller" "github.com/radius-project/radius/pkg/recipes/controllerconfig" - "github.com/radius-project/radius/pkg/ucp/store/inmemory" + "github.com/radius-project/radius/pkg/ucp/database/inmemory" ) var handlerTests = []rpctest.HandlerTestSpec{ @@ -134,10 +134,10 @@ func TestRouter(t *testing.T) { require.NoError(t, err) options := apictrl.Options{ - Address: "localhost:9000", - PathBase: "/api.ucp.dev", - StorageClient: inmemory.NewClient(), - StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), + Address: "localhost:9000", + PathBase: "/api.ucp.dev", + DatabaseClient: inmemory.NewClient(), + StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), } return r, nsBuilder.ApplyAPIHandlers(ctx, r, options, validator) diff --git a/pkg/dynamicrp/backend/service.go b/pkg/dynamicrp/backend/service.go index 370563c80b..bbbd33955f 100644 --- a/pkg/dynamicrp/backend/service.go +++ b/pkg/dynamicrp/backend/service.go @@ -39,10 +39,10 @@ func NewService(options *dynamicrp.Options) *Service { ProviderName: "dynamic-rp", Options: hostoptions.HostOptions{ Config: &hostoptions.ProviderConfig{ - Env: options.Config.Environment, - StorageProvider: options.Config.Storage, - SecretProvider: options.Config.Secrets, - QueueProvider: options.Config.Queue, + Env: options.Config.Environment, + DatabaseProvider: options.Config.Database, + SecretProvider: options.Config.Secrets, + QueueProvider: options.Config.Queue, }, }, }, diff --git a/pkg/dynamicrp/config.go b/pkg/dynamicrp/config.go index 084c469c21..41ffacdbe8 100644 --- a/pkg/dynamicrp/config.go +++ b/pkg/dynamicrp/config.go @@ -24,9 +24,9 @@ import ( profilerprovider "github.com/radius-project/radius/pkg/profiler/provider" "github.com/radius-project/radius/pkg/trace" ucpconfig "github.com/radius-project/radius/pkg/ucp/config" - "github.com/radius-project/radius/pkg/ucp/dataprovider" - queueprovider "github.com/radius-project/radius/pkg/ucp/queue/provider" - secretprovider "github.com/radius-project/radius/pkg/ucp/secret/provider" + "github.com/radius-project/radius/pkg/ucp/databaseprovider" + "github.com/radius-project/radius/pkg/ucp/queue/queueprovider" + "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" "github.com/radius-project/radius/pkg/ucp/ucplog" "gopkg.in/yaml.v3" ) @@ -36,6 +36,9 @@ type Config struct { // Bicep configures properties for the Bicep recipe driver. Bicep hostoptions.BicepOptions `yaml:"bicep"` + // Database is the configuration for the database. + Database databaseprovider.Options `yaml:"storageProvider"` + // Environment is the configuration for the hosting environment. Environment hostoptions.EnvironmentOptions `yaml:"environment"` @@ -57,9 +60,6 @@ type Config struct { // Server is the configuration for the HTTP server. Server hostoptions.ServerOptions `yaml:"server"` - // Storage is the configuration for the database used for storage. - Storage dataprovider.StorageProviderOptions `yaml:"storageProvider"` - // Terraform configures properties for the Terraform recipe driver. Terraform hostoptions.TerraformOptions `yaml:"terraform"` diff --git a/pkg/dynamicrp/frontend/service.go b/pkg/dynamicrp/frontend/service.go index f5bee9dbd9..73518751b5 100644 --- a/pkg/dynamicrp/frontend/service.go +++ b/pkg/dynamicrp/frontend/service.go @@ -48,7 +48,7 @@ func (s *Service) Name() string { return "dynamic-rp api" } -// Initialize sets up the router, storage provider, secret provider, status manager, AWS config, AWS clients, +// Initialize sets up the router, database provider, secret provider, status manager, AWS config, AWS clients, // registers the routes, configures the default planes, and sets up the http server with the appropriate middleware. It // returns an http server and an error if one occurs. func (s *Service) initialize(ctx context.Context) (*http.Server, error) { diff --git a/pkg/dynamicrp/options.go b/pkg/dynamicrp/options.go index 109c1a00e8..8d046c9132 100644 --- a/pkg/dynamicrp/options.go +++ b/pkg/dynamicrp/options.go @@ -26,9 +26,9 @@ import ( "github.com/radius-project/radius/pkg/recipes/controllerconfig" "github.com/radius-project/radius/pkg/sdk" ucpconfig "github.com/radius-project/radius/pkg/ucp/config" - "github.com/radius-project/radius/pkg/ucp/dataprovider" - queueprovider "github.com/radius-project/radius/pkg/ucp/queue/provider" - secretprovider "github.com/radius-project/radius/pkg/ucp/secret/provider" + "github.com/radius-project/radius/pkg/ucp/databaseprovider" + "github.com/radius-project/radius/pkg/ucp/queue/queueprovider" + "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" kube_rest "k8s.io/client-go/rest" ) @@ -37,6 +37,9 @@ type Options struct { // Config is the configuration for the server. Config *Config + // DatabaseProvider provides access to the database. + DatabaseProvider *databaseprovider.DatabaseProvider + // QueueProvider provides access to the message queue client. QueueProvider *queueprovider.QueueProvider @@ -49,9 +52,6 @@ type Options struct { // StatusManager implements operations on async operation statuses. StatusManager statusmanager.StatusManager - // StorageProvider provides access to the data storage system. - StorageProvider *dataprovider.DataStorageProvider - // UCP is the connection to UCP UCP sdk.Connection } @@ -65,9 +65,9 @@ func NewOptions(ctx context.Context, config *Config) (*Options, error) { options.QueueProvider = queueprovider.New(config.Queue) options.SecretProvider = secretprovider.NewSecretProvider(config.Secrets) - options.StorageProvider = dataprovider.DataStorageProviderFromOptions(config.Storage) + options.DatabaseProvider = databaseprovider.FromOptions(config.Database) - storageClient, err := options.StorageProvider.GetClient(ctx) + databaseClient, err := options.DatabaseProvider.GetClient(ctx) if err != nil { return nil, err } @@ -77,7 +77,7 @@ func NewOptions(ctx context.Context, config *Config) (*Options, error) { return nil, err } - options.StatusManager = statusmanager.New(storageClient, queueClient, config.Environment.RoleLocation) + options.StatusManager = statusmanager.New(databaseClient, queueClient, config.Environment.RoleLocation) var cfg *kube_rest.Config cfg, err = kubeutil.NewClientConfig(&kubeutil.ConfigOptions{ diff --git a/pkg/dynamicrp/server/server.go b/pkg/dynamicrp/server/server.go index bb7e97963e..288737c780 100644 --- a/pkg/dynamicrp/server/server.go +++ b/pkg/dynamicrp/server/server.go @@ -26,7 +26,7 @@ import ( profilerservice "github.com/radius-project/radius/pkg/profiler/service" "github.com/radius-project/radius/pkg/trace" "github.com/radius-project/radius/pkg/ucp/data" - "github.com/radius-project/radius/pkg/ucp/dataprovider" + "github.com/radius-project/radius/pkg/ucp/databaseprovider" "github.com/radius-project/radius/pkg/ucp/hosting" ) @@ -43,9 +43,9 @@ func NewServer(options *dynamicrp.Options) (*hosting.Host, error) { services := []hosting.Service{} // In-memory ETCD requires a service running in the process. - if options.Config.Storage.Provider == dataprovider.TypeETCD && - options.Config.Storage.ETCD.InMemory { - services = append(services, data.NewEmbeddedETCDService(data.EmbeddedETCDServiceOptions{ClientConfigSink: options.Config.Storage.ETCD.Client})) + if options.Config.Database.Provider == databaseprovider.TypeETCD && + options.Config.Database.ETCD.InMemory { + services = append(services, data.NewEmbeddedETCDService(data.EmbeddedETCDServiceOptions{ClientConfigSink: options.Config.Database.ETCD.Client})) } // Metrics is provided via a service. diff --git a/pkg/messagingrp/frontend/controller/rabbitmqqueues/listsecretsrabbitmq_test.go b/pkg/messagingrp/frontend/controller/rabbitmqqueues/listsecretsrabbitmq_test.go index 73746ca847..de0bf25c2d 100644 --- a/pkg/messagingrp/frontend/controller/rabbitmqqueues/listsecretsrabbitmq_test.go +++ b/pkg/messagingrp/frontend/controller/rabbitmqqueues/listsecretsrabbitmq_test.go @@ -28,7 +28,7 @@ import ( "github.com/radius-project/radius/pkg/armrpc/rpctest" "github.com/radius-project/radius/pkg/messagingrp/api/v20231001preview" "github.com/radius-project/radius/pkg/portableresources/renderers" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" ) @@ -37,7 +37,7 @@ func TestListSecrets_20231001Preview(t *testing.T) { mctrl := gomock.NewController(t) defer mctrl.Finish() - mStorageClient := store.NewMockStorageClient(mctrl) + databaseClient := database.NewMockClient(mctrl) ctx := context.Background() _, rabbitMQDataModel, _ := getTest_Model20231001preview() @@ -48,15 +48,15 @@ func TestListSecrets_20231001Preview(t *testing.T) { require.NoError(t, err) ctx := rpctest.NewARMRequestContext(req) - mStorageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { - return nil, &store.ErrNotFound{} + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { + return nil, &database.ErrNotFound{} }) opts := ctrl.Options{ - StorageClient: mStorageClient, + DatabaseClient: databaseClient, } ctl, err := NewListSecretsRabbitMQQueue(opts) @@ -78,18 +78,18 @@ func TestListSecrets_20231001Preview(t *testing.T) { renderers.URI: "connection://string", } - mStorageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { - return &store.Object{ - Metadata: store.Metadata{ID: id}, + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { + return &database.Object{ + Metadata: database.Metadata{ID: id}, Data: rabbitMQDataModel, }, nil }) opts := ctrl.Options{ - StorageClient: mStorageClient, + DatabaseClient: databaseClient, } ctl, err := NewListSecretsRabbitMQQueue(opts) @@ -113,15 +113,15 @@ func TestListSecrets_20231001Preview(t *testing.T) { ctx := rpctest.NewARMRequestContext(req) w := httptest.NewRecorder() - mStorageClient. + databaseClient. EXPECT(). Get(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { + DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { return nil, errors.New("failed to get the resource from data store") }) opts := ctrl.Options{ - StorageClient: mStorageClient, + DatabaseClient: databaseClient, } ctl, err := NewListSecretsRabbitMQQueue(opts) diff --git a/pkg/messagingrp/setup/setup_test.go b/pkg/messagingrp/setup/setup_test.go index 0aa5af07bb..48573e2e62 100644 --- a/pkg/messagingrp/setup/setup_test.go +++ b/pkg/messagingrp/setup/setup_test.go @@ -29,7 +29,7 @@ import ( "github.com/radius-project/radius/pkg/armrpc/rpctest" msg_ctrl "github.com/radius-project/radius/pkg/messagingrp/frontend/controller" "github.com/radius-project/radius/pkg/recipes/controllerconfig" - "github.com/radius-project/radius/pkg/ucp/store/inmemory" + "github.com/radius-project/radius/pkg/ucp/database/inmemory" ) var handlerTests = []rpctest.HandlerTestSpec{ @@ -76,10 +76,10 @@ func TestRouter(t *testing.T) { require.NoError(t, err) options := apictrl.Options{ - Address: "localhost:9000", - PathBase: "/api.ucp.dev", - StorageClient: inmemory.NewClient(), - StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), + Address: "localhost:9000", + PathBase: "/api.ucp.dev", + DatabaseClient: inmemory.NewClient(), + StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), } return r, nsBuilder.ApplyAPIHandlers(ctx, r, options, validator) diff --git a/pkg/portableresources/backend/controller/createorupdateresource.go b/pkg/portableresources/backend/controller/createorupdateresource.go index 6e338356a8..028933421a 100644 --- a/pkg/portableresources/backend/controller/createorupdateresource.go +++ b/pkg/portableresources/backend/controller/createorupdateresource.go @@ -29,7 +29,7 @@ import ( "github.com/radius-project/radius/pkg/recipes/engine" "github.com/radius-project/radius/pkg/recipes/util" rpv1 "github.com/radius-project/radius/pkg/rp/v1" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/ucplog" ) @@ -64,8 +64,8 @@ func NewCreateOrUpdateResource[P interface { // processes the resource, cleans up any obsolete output resources, and saves the updated resource. func (c *CreateOrUpdateResource[P, T]) Run(ctx context.Context, req *ctrl.Request) (ctrl.Result, error) { logger := ucplog.FromContextOrDiscard(ctx) - obj, err := c.StorageClient().Get(ctx, req.ResourceID) - if errors.Is(&store.ErrNotFound{ID: req.ResourceID}, err) { + obj, err := c.DatabaseClient().Get(ctx, req.ResourceID) + if errors.Is(&database.ErrNotFound{ID: req.ResourceID}, err) { return ctrl.Result{}, err } else if err != nil { return ctrl.Result{}, err @@ -100,14 +100,14 @@ func (c *CreateOrUpdateResource[P, T]) Run(ctx context.Context, req *ctrl.Reques logger.Error(err, fmt.Sprintf("failed to execute recipe. Encountered error while processing %s ", recipeError.ErrorDetails.Target)) // Set the deployment status to the recipe error code. recipeDataModel.Recipe().DeploymentStatus = util.RecipeDeploymentStatus(recipeError.DeploymentStatus) - update := &store.Object{ - Metadata: store.Metadata{ + update := &database.Object{ + Metadata: database.Metadata{ ID: req.ResourceID, }, Data: recipeDataModel.(rpv1.RadiusResourceModel), } // Save portable resource with updated deployment status to track errors during deletion. - err = c.StorageClient().Save(ctx, update, store.WithETag(obj.ETag)) + err = c.DatabaseClient().Save(ctx, update, database.WithETag(obj.ETag)) if err != nil { return ctrl.Result{}, err } @@ -129,13 +129,13 @@ func (c *CreateOrUpdateResource[P, T]) Run(ctx context.Context, req *ctrl.Reques recipeDataModel.Recipe().DeploymentStatus = util.Success } - update := &store.Object{ - Metadata: store.Metadata{ + update := &database.Object{ + Metadata: database.Metadata{ ID: req.ResourceID, }, Data: recipeDataModel.(rpv1.RadiusResourceModel), } - err = c.StorageClient().Save(ctx, update, store.WithETag(obj.ETag)) + err = c.DatabaseClient().Save(ctx, update, database.WithETag(obj.ETag)) if err != nil { return ctrl.Result{}, err } diff --git a/pkg/portableresources/backend/controller/createorupdateresource_test.go b/pkg/portableresources/backend/controller/createorupdateresource_test.go index 0adf153b75..584ddd3632 100644 --- a/pkg/portableresources/backend/controller/createorupdateresource_test.go +++ b/pkg/portableresources/backend/controller/createorupdateresource_test.go @@ -37,8 +37,8 @@ import ( "github.com/radius-project/radius/pkg/recipes/controllerconfig" "github.com/radius-project/radius/pkg/recipes/engine" rpv1 "github.com/radius-project/radius/pkg/rp/v1" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/store" ) const ( @@ -129,10 +129,10 @@ var newOutputResourceResourceID = "/subscriptions/test-sub/resourceGroups/test-r var newOutputResource = rpv1.OutputResource{ID: resources.MustParse(newOutputResourceResourceID)} func TestCreateOrUpdateResource_Run(t *testing.T) { - setupTest := func() (*store.MockStorageClient, *engine.MockEngine, *processors.MockResourceClient, *configloader.MockConfigurationLoader) { + setupTest := func() (*database.MockClient, *engine.MockEngine, *processors.MockResourceClient, *configloader.MockConfigurationLoader) { mctrl := gomock.NewController(t) - msc := store.NewMockStorageClient(mctrl) + msc := database.NewMockClient(mctrl) eng := engine.NewMockEngine(mctrl) cfg := configloader.NewMockConfigurationLoader(mctrl) client := processors.NewMockResourceClient(mctrl) @@ -156,28 +156,28 @@ func TestCreateOrUpdateResource_Run(t *testing.T) { func(recipeCfg *controllerconfig.RecipeControllerConfig, options ctrl.Options) (ctrl.Controller, error) { return NewCreateOrUpdateResource(options, errorProcessorReference, recipeCfg.Engine, recipeCfg.ResourceClient, recipeCfg.ConfigLoader) }, - &store.ErrNotFound{ID: TestResourceID}, + &database.ErrNotFound{ID: TestResourceID}, false, nil, nil, nil, nil, nil, - &store.ErrNotFound{ID: TestResourceID}, + &database.ErrNotFound{ID: TestResourceID}, }, { "get-error", func(recipeCfg *controllerconfig.RecipeControllerConfig, options ctrl.Options) (ctrl.Controller, error) { return NewCreateOrUpdateResource(options, errorProcessorReference, recipeCfg.Engine, recipeCfg.ResourceClient, recipeCfg.ConfigLoader) }, - &store.ErrInvalid{}, + &database.ErrInvalid{}, false, nil, nil, nil, nil, nil, - &store.ErrInvalid{}, + &database.ErrInvalid{}, }, { "conversion-failure", @@ -314,12 +314,12 @@ func TestCreateOrUpdateResource_Run(t *testing.T) { stillPassing = false msc.EXPECT(). Get(gomock.Any(), TestResourceID). - Return(&store.Object{Data: nil}, tt.getErr). + Return(&database.Object{Data: nil}, tt.getErr). Times(1) } else if stillPassing { msc.EXPECT(). Get(gomock.Any(), TestResourceID). - Return(&store.Object{Data: data}, nil). + Return(&database.Object{Data: data}, nil). Times(1) } @@ -412,7 +412,7 @@ func TestCreateOrUpdateResource_Run(t *testing.T) { } opts := ctrl.Options{ - StorageClient: msc, + DatabaseClient: msc, } recipeCfg := &controllerconfig.RecipeControllerConfig{ diff --git a/pkg/portableresources/backend/controller/deleteresource.go b/pkg/portableresources/backend/controller/deleteresource.go index 4cdc54a04d..a509bff720 100644 --- a/pkg/portableresources/backend/controller/deleteresource.go +++ b/pkg/portableresources/backend/controller/deleteresource.go @@ -55,10 +55,10 @@ func NewDeleteResource[P interface { }, nil } -// Run retrieves a resource from storage, parses the resource ID, gets the data model, deletes the output -// resources, and deletes the resource from storage. It returns an error if any of these steps fail. +// Run retrieves a resource from the database, parses the resource ID, gets the data model, deletes the output +// resources, and deletes the resource from the database. It returns an error if any of these steps fail. func (c *DeleteResource[P, T]) Run(ctx context.Context, request *ctrl.Request) (ctrl.Result, error) { - obj, err := c.StorageClient().Get(ctx, request.ResourceID) + obj, err := c.DatabaseClient().Get(ctx, request.ResourceID) if err != nil { return ctrl.NewFailedResult(v1.ErrorDetails{Message: err.Error()}), err } @@ -114,7 +114,7 @@ func (c *DeleteResource[P, T]) Run(ctx context.Context, request *ctrl.Request) ( return ctrl.Result{}, err } - err = c.StorageClient().Delete(ctx, request.ResourceID) + err = c.DatabaseClient().Delete(ctx, request.ResourceID) if err != nil { return ctrl.Result{}, err } diff --git a/pkg/portableresources/backend/controller/deleteresource_test.go b/pkg/portableresources/backend/controller/deleteresource_test.go index d2da3b3359..ec1c021918 100644 --- a/pkg/portableresources/backend/controller/deleteresource_test.go +++ b/pkg/portableresources/backend/controller/deleteresource_test.go @@ -30,8 +30,8 @@ import ( "github.com/radius-project/radius/pkg/recipes/engine" rpv1 "github.com/radius-project/radius/pkg/rp/v1" "github.com/radius-project/radius/pkg/to" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/store" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" ) @@ -44,9 +44,9 @@ var outputResource = rpv1.OutputResource{ func TestDeleteResourceRun_20231001Preview(t *testing.T) { resourceID := "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Datastores/mongoDatabases/mongo0" - setupTest := func() (func(tb testing.TB), *store.MockStorageClient, *ctrl.Request, *engine.MockEngine, *configloader.MockConfigurationLoader) { + setupTest := func() (func(tb testing.TB), *database.MockClient, *ctrl.Request, *engine.MockEngine, *configloader.MockConfigurationLoader) { mctrl := gomock.NewController(t) - msc := store.NewMockStorageClient(mctrl) + msc := database.NewMockClient(mctrl) eng := engine.NewMockEngine(mctrl) cfg := configloader.NewMockConfigurationLoader(mctrl) @@ -72,7 +72,7 @@ func TestDeleteResourceRun_20231001Preview(t *testing.T) { scDelErr error }{ {"delete-existing-resource", nil, nil, nil}, - {"delete-non-existing-resource", &store.ErrNotFound{ID: resourceID}, nil, nil}, + {"delete-non-existing-resource", &database.ErrNotFound{ID: resourceID}, nil, nil}, {"delete-resource-engine-delete-error", nil, errors.New("engine delete error"), nil}, {"delete-resource-delete-from-db-error", nil, nil, errors.New("delete from db error")}, } @@ -117,7 +117,7 @@ func TestDeleteResourceRun_20231001Preview(t *testing.T) { msc.EXPECT(). Get(gomock.Any(), gomock.Any()). - Return(&store.Object{Data: data}, tt.getErr). + Return(&database.Object{Data: data}, tt.getErr). Times(1) if tt.getErr == nil { @@ -153,7 +153,7 @@ func TestDeleteResourceRun_20231001Preview(t *testing.T) { } } opts := ctrl.Options{ - StorageClient: msc, + DatabaseClient: msc, } ctrl, err := NewDeleteResource(opts, successProcessorReference, eng, configLoader) diff --git a/pkg/recipes/controllerconfig/config.go b/pkg/recipes/controllerconfig/config.go index 6ddeb1ef19..a6ca537506 100644 --- a/pkg/recipes/controllerconfig/config.go +++ b/pkg/recipes/controllerconfig/config.go @@ -29,7 +29,7 @@ import ( "github.com/radius-project/radius/pkg/recipes/engine" "github.com/radius-project/radius/pkg/sdk" "github.com/radius-project/radius/pkg/sdk/clients" - "github.com/radius-project/radius/pkg/ucp/secret/provider" + "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" ) // RecipeControllerConfig is the configuration for the controllers which uses recipe. @@ -100,7 +100,7 @@ func New(options hostoptions.HostOptions) (*RecipeControllerConfig, error) { DeleteRetryDelaySeconds: bicepDeleteRetryDeleteSeconds, }, ), - recipes.TemplateKindTerraform: driver.NewTerraformDriver(options.UCPConnection, provider.NewSecretProvider(options.Config.SecretProvider), + recipes.TemplateKindTerraform: driver.NewTerraformDriver(options.UCPConnection, secretprovider.NewSecretProvider(options.Config.SecretProvider), driver.TerraformOptions{ Path: options.Config.Terraform.Path, }, cfg.K8sClients.ClientSet), diff --git a/pkg/recipes/driver/terraform.go b/pkg/recipes/driver/terraform.go index 645dafb3d1..109af10211 100644 --- a/pkg/recipes/driver/terraform.go +++ b/pkg/recipes/driver/terraform.go @@ -38,7 +38,7 @@ import ( resources "github.com/radius-project/radius/pkg/ucp/resources" awsresources "github.com/radius-project/radius/pkg/ucp/resources/aws" kubernetesresources "github.com/radius-project/radius/pkg/ucp/resources/kubernetes" - ucp_provider "github.com/radius-project/radius/pkg/ucp/secret/provider" + ucp_provider "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" "github.com/radius-project/radius/pkg/ucp/ucplog" "github.com/radius-project/radius/pkg/ucp/util" diff --git a/pkg/recipes/terraform/config/providers/aws.go b/pkg/recipes/terraform/config/providers/aws.go index 98fe67bcf0..228ee7263c 100644 --- a/pkg/recipes/terraform/config/providers/aws.go +++ b/pkg/recipes/terraform/config/providers/aws.go @@ -32,7 +32,7 @@ import ( "github.com/radius-project/radius/pkg/ucp/resources" resources_aws "github.com/radius-project/radius/pkg/ucp/resources/aws" "github.com/radius-project/radius/pkg/ucp/secret" - ucp_provider "github.com/radius-project/radius/pkg/ucp/secret/provider" + ucp_provider "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" "github.com/radius-project/radius/pkg/ucp/ucplog" ) diff --git a/pkg/recipes/terraform/config/providers/azure.go b/pkg/recipes/terraform/config/providers/azure.go index e039ac03d0..5ac32cef3d 100644 --- a/pkg/recipes/terraform/config/providers/azure.go +++ b/pkg/recipes/terraform/config/providers/azure.go @@ -30,7 +30,7 @@ import ( "github.com/radius-project/radius/pkg/ucp/resources" resources_azure "github.com/radius-project/radius/pkg/ucp/resources/azure" "github.com/radius-project/radius/pkg/ucp/secret" - ucp_provider "github.com/radius-project/radius/pkg/ucp/secret/provider" + ucp_provider "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" "github.com/radius-project/radius/pkg/ucp/ucplog" ) diff --git a/pkg/recipes/terraform/config/providers/types.go b/pkg/recipes/terraform/config/providers/types.go index d870f57936..349d93839c 100644 --- a/pkg/recipes/terraform/config/providers/types.go +++ b/pkg/recipes/terraform/config/providers/types.go @@ -23,7 +23,7 @@ import ( "github.com/radius-project/radius/pkg/corerp/datamodel" "github.com/radius-project/radius/pkg/recipes" "github.com/radius-project/radius/pkg/sdk" - ucp_provider "github.com/radius-project/radius/pkg/ucp/secret/provider" + ucp_provider "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" ) //go:generate mockgen -typed -destination=./mock_provider.go -package=providers -self_package github.com/radius-project/radius/pkg/recipes/terraform/config/providers github.com/radius-project/radius/pkg/recipes/terraform/config/providers Provider diff --git a/pkg/recipes/terraform/execute.go b/pkg/recipes/terraform/execute.go index 4fd6709e2f..9e0c88d7e2 100644 --- a/pkg/recipes/terraform/execute.go +++ b/pkg/recipes/terraform/execute.go @@ -33,7 +33,7 @@ import ( "github.com/radius-project/radius/pkg/recipes/terraform/config/backends" "github.com/radius-project/radius/pkg/recipes/terraform/config/providers" "github.com/radius-project/radius/pkg/sdk" - ucp_provider "github.com/radius-project/radius/pkg/ucp/secret/provider" + ucp_provider "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" "github.com/radius-project/radius/pkg/ucp/ucplog" "go.opentelemetry.io/otel/attribute" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" diff --git a/pkg/rp/kube/resources.go b/pkg/rp/kube/resources.go index e55cf914ed..85c7e94ce7 100644 --- a/pkg/rp/kube/resources.go +++ b/pkg/rp/kube/resources.go @@ -25,13 +25,13 @@ import ( "github.com/radius-project/radius/pkg/corerp/api/v20231001preview" cdm "github.com/radius-project/radius/pkg/corerp/datamodel" rpv1 "github.com/radius-project/radius/pkg/rp/v1" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/store" ) // FindNamespaceByEnvID finds the environment-scope Kubernetes namespace. If the environment ID is invalid or the environment is not a Kubernetes // environment, an error is returned. -func FindNamespaceByEnvID(ctx context.Context, storageClient store.StorageClient, envID string) (namespace string, err error) { +func FindNamespaceByEnvID(ctx context.Context, databaseClient database.Client, envID string) (namespace string, err error) { id, err := resources.ParseResource(envID) if err != nil { return @@ -43,7 +43,7 @@ func FindNamespaceByEnvID(ctx context.Context, storageClient store.StorageClient } env := &cdm.Environment{} - res, err := storageClient.Get(ctx, id.String()) + res, err := databaseClient.Get(ctx, id.String()) if err != nil { return } diff --git a/pkg/rp/kube/resources_test.go b/pkg/rp/kube/resources_test.go index 738cd318e3..2c69d6d9a1 100644 --- a/pkg/rp/kube/resources_test.go +++ b/pkg/rp/kube/resources_test.go @@ -26,7 +26,7 @@ import ( "github.com/radius-project/radius/pkg/corerp/datamodel" rpv1 "github.com/radius-project/radius/pkg/rp/v1" "github.com/radius-project/radius/pkg/to" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" ) @@ -37,7 +37,7 @@ const ( appNamespace = "app-default" ) -func fakeStoreObject(dm v1.DataModelInterface) *store.Object { +func fakeStoreObject(dm v1.DataModelInterface) *database.Object { b, err := json.Marshal(dm) if err != nil { return nil @@ -47,7 +47,7 @@ func fakeStoreObject(dm v1.DataModelInterface) *store.Object { if err != nil { return nil } - return &store.Object{Data: r} + return &database.Object{Data: r} } func TestFindNamespaceByEnvID(t *testing.T) { @@ -86,7 +86,7 @@ func TestFindNamespaceByEnvID(t *testing.T) { }, } - mockSC := store.NewMockStorageClient(mctrl) + mockSC := database.NewMockClient(mctrl) mockSC.EXPECT().Get(gomock.Any(), tc.id, gomock.Any()).Return(fakeStoreObject(envdm), nil).Times(1) ns, err := FindNamespaceByEnvID(context.Background(), mockSC, testEnvID) diff --git a/pkg/rp/util/datastore.go b/pkg/rp/util/datastore.go index e0ca7f317b..82ce79afd2 100644 --- a/pkg/rp/util/datastore.go +++ b/pkg/rp/util/datastore.go @@ -23,13 +23,13 @@ import ( "strings" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/ucp/database" resources "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/store" ) // FetchScopeResource checks if the given scopeID is a valid resource ID for the given resource type, fetches the resource -// from the storage client and returns an error if the resource does not exist. -func FetchScopeResource(ctx context.Context, storageClient store.StorageClient, scopeID string, resource v1.DataModelInterface) error { +// from the database client and returns an error if the resource does not exist. +func FetchScopeResource(ctx context.Context, databaseClient database.Client, scopeID string, resource v1.DataModelInterface) error { id, err := resources.ParseResource(scopeID) if err != nil { return v1.NewClientErrInvalidRequest(fmt.Sprintf("%s is not a valid resource id for %s.", scopeID, resource.ResourceTypeName())) @@ -39,8 +39,8 @@ func FetchScopeResource(ctx context.Context, storageClient store.StorageClient, return v1.NewClientErrInvalidRequest(fmt.Sprintf("linked %q has invalid %s resource type.", scopeID, resource.ResourceTypeName())) } - res, err := storageClient.Get(ctx, id.String()) - if errors.Is(&store.ErrNotFound{ID: id.String()}, err) { + res, err := databaseClient.Get(ctx, id.String()) + if errors.Is(&database.ErrNotFound{ID: id.String()}, err) { return v1.NewClientErrInvalidRequest(fmt.Sprintf("linked resource %s does not exist", scopeID)) } if err != nil { diff --git a/pkg/server/apiservice.go b/pkg/server/apiservice.go index 698f935ba8..12730c8ac4 100644 --- a/pkg/server/apiservice.go +++ b/pkg/server/apiservice.go @@ -57,7 +57,7 @@ func (s *APIService) Run(ctx context.Context) error { return err } - storageClient, err := s.StorageProvider.GetClient(ctx) + databaseClient, err := s.DatabaseProvider.GetClient(ctx) if err != nil { return err } @@ -70,11 +70,11 @@ func (s *APIService) Run(ctx context.Context) error { Configure: func(r chi.Router) error { for _, b := range s.handlerBuilder { opts := apictrl.Options{ - Address: address, - PathBase: s.Options.Config.Server.PathBase, - StorageClient: storageClient, - KubeClient: s.KubeClient, - StatusManager: s.OperationStatusManager, + Address: address, + PathBase: s.Options.Config.Server.PathBase, + DatabaseClient: databaseClient, + KubeClient: s.KubeClient, + StatusManager: s.OperationStatusManager, } validator, err := builder.NewOpenAPIValidator(ctx, opts.PathBase, b.Namespace()) diff --git a/pkg/server/asyncworker.go b/pkg/server/asyncworker.go index fd8a162c3e..2531c6b0a9 100644 --- a/pkg/server/asyncworker.go +++ b/pkg/server/asyncworker.go @@ -68,17 +68,17 @@ func (w *AsyncWorker) Run(ctx context.Context) error { return fmt.Errorf("failed to initialize application model: %w", err) } - storageClient, err := w.StorageProvider.GetClient(ctx) + databaseClient, err := w.DatabaseProvider.GetClient(ctx) if err != nil { return err } for _, b := range w.handlerBuilder { opts := ctrl.Options{ - StorageClient: storageClient, - KubeClient: k8s.RuntimeClient, + DatabaseClient: databaseClient, + KubeClient: k8s.RuntimeClient, GetDeploymentProcessor: func() deployment.DeploymentProcessor { - return deployment.NewDeploymentProcessor(appModel, storageClient, k8s.RuntimeClient, k8s.ClientSet) + return deployment.NewDeploymentProcessor(appModel, databaseClient, k8s.RuntimeClient, k8s.ClientSet) }, } diff --git a/pkg/ucp/backend/controller/resourcegroups/trackedresourceprocess.go b/pkg/ucp/backend/controller/resourcegroups/trackedresourceprocess.go index bb2fa2b3d0..3f7ba1c87b 100644 --- a/pkg/ucp/backend/controller/resourcegroups/trackedresourceprocess.go +++ b/pkg/ucp/backend/controller/resourcegroups/trackedresourceprocess.go @@ -25,10 +25,10 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/frontend/controller/resourcegroups" "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/store" "github.com/radius-project/radius/pkg/ucp/trackedresource" "github.com/radius-project/radius/pkg/ucp/ucplog" ) @@ -54,15 +54,15 @@ type TrackedResourceProcessController struct { func NewTrackedResourceProcessController(opts ctrl.Options, transport http.RoundTripper, defaultDownstream *url.URL) (ctrl.Controller, error) { return &TrackedResourceProcessController{ BaseController: ctrl.NewBaseAsyncController(opts), - updater: trackedresource.NewUpdater(opts.StorageClient, &http.Client{Transport: transport}), + updater: trackedresource.NewUpdater(opts.DatabaseClient, &http.Client{Transport: transport}), defaultDownstream: defaultDownstream, }, nil } -// Run retrieves a resource from storage, parses the resource ID, and updates our tracked resource entry in the background. +// Run retrieves a resource from the database, parses the resource ID, and updates our tracked resource entry in the background. func (c *TrackedResourceProcessController) Run(ctx context.Context, request *ctrl.Request) (ctrl.Result, error) { - resource, err := store.GetResource[datamodel.GenericResource](ctx, c.StorageClient(), request.ResourceID) - if errors.Is(err, &store.ErrNotFound{}) { + resource, err := database.GetResource[datamodel.GenericResource](ctx, c.DatabaseClient(), request.ResourceID) + if errors.Is(err, &database.ErrNotFound{}) { return ctrl.NewFailedResult(v1.ErrorDetails{Code: v1.CodeNotFound, Message: fmt.Sprintf("resource %q not found", request.ResourceID), Target: request.ResourceID}), nil } else if err != nil { return ctrl.Result{}, err @@ -73,7 +73,7 @@ func (c *TrackedResourceProcessController) Run(ctx context.Context, request *ctr return ctrl.Result{}, err } - downstreamURL, err := resourcegroups.ValidateDownstream(ctx, c.StorageClient(), originalID, v1.LocationGlobal, resource.Properties.APIVersion) + downstreamURL, err := resourcegroups.ValidateDownstream(ctx, c.DatabaseClient(), originalID, v1.LocationGlobal, resource.Properties.APIVersion) if errors.Is(err, &resourcegroups.NotFoundError{}) { return ctrl.NewFailedResult(v1.ErrorDetails{Code: v1.CodeNotFound, Message: err.Error(), Target: request.ResourceID}), nil } else if errors.Is(err, &resourcegroups.InvalidError{}) { diff --git a/pkg/ucp/backend/controller/resourcegroups/trackedresourceprocess_test.go b/pkg/ucp/backend/controller/resourcegroups/trackedresourceprocess_test.go index c4db345daf..aaee56c4ca 100644 --- a/pkg/ucp/backend/controller/resourcegroups/trackedresourceprocess_test.go +++ b/pkg/ucp/backend/controller/resourcegroups/trackedresourceprocess_test.go @@ -23,9 +23,9 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/store" "github.com/radius-project/radius/pkg/ucp/trackedresource" "github.com/radius-project/radius/test/testcontext" "github.com/stretchr/testify/require" @@ -33,16 +33,16 @@ import ( ) func Test_Run(t *testing.T) { - setup := func(t *testing.T) (*TrackedResourceProcessController, *mockUpdater, *store.MockStorageClient) { + setup := func(t *testing.T) (*TrackedResourceProcessController, *mockUpdater, *database.MockClient) { ctrl := gomock.NewController(t) - storageClient := store.NewMockStorageClient(ctrl) + databaseClient := database.NewMockClient(ctrl) - pc, err := NewTrackedResourceProcessController(controller.Options{StorageClient: storageClient}, nil, nil) + pc, err := NewTrackedResourceProcessController(controller.Options{DatabaseClient: databaseClient}, nil, nil) require.NoError(t, err) updater := mockUpdater{} pc.(*TrackedResourceProcessController).updater = &updater - return pc.(*TrackedResourceProcessController), &updater, storageClient + return pc.(*TrackedResourceProcessController), &updater, databaseClient } id := resources.MustParse("/planes/test/local/resourceGroups/test-rg/providers/Applications.Test/testResources/my-resource") @@ -63,23 +63,23 @@ func Test_Run(t *testing.T) { // Most of the heavy lifting is done by the updater. We just need to test that we're calling it correctly. t.Run("Success", func(t *testing.T) { - pc, _, storageClient := setup(t) + pc, _, databaseClient := setup(t) - storageClient.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), trackingID.String(), gomock.Any()). - Return(&store.Object{Data: data}, nil).Times(1) + Return(&database.Object{Data: data}, nil).Times(1) - storageClient.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), "/planes/"+trackingID.PlaneNamespace(), gomock.Any()). - Return(&store.Object{Data: plane}, nil).Times(1) + Return(&database.Object{Data: plane}, nil).Times(1) - storageClient.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), resourceTypeID.String(), gomock.Any()). - Return(nil, &store.ErrNotFound{}).Times(1) + Return(nil, &database.ErrNotFound{}).Times(1) - storageClient.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), trackingID.RootScope(), gomock.Any()). - Return(&store.Object{Data: resourceGroup}, nil).Times(1) + Return(&database.Object{Data: resourceGroup}, nil).Times(1) result, err := pc.Run(testcontext.New(t), &controller.Request{ResourceID: trackingID.String()}) require.Equal(t, controller.Result{}, result) @@ -87,23 +87,23 @@ func Test_Run(t *testing.T) { }) t.Run("retry", func(t *testing.T) { - pc, updater, storageClient := setup(t) + pc, updater, databaseClient := setup(t) - storageClient.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), trackingID.String(), gomock.Any()). - Return(&store.Object{Data: data}, nil).Times(1) + Return(&database.Object{Data: data}, nil).Times(1) - storageClient.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), "/planes/"+trackingID.PlaneNamespace(), gomock.Any()). - Return(&store.Object{Data: plane}, nil).Times(1) + Return(&database.Object{Data: plane}, nil).Times(1) - storageClient.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), resourceTypeID.String(), gomock.Any()). - Return(nil, &store.ErrNotFound{}).Times(1) + Return(nil, &database.ErrNotFound{}).Times(1) - storageClient.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), trackingID.RootScope(), gomock.Any()). - Return(&store.Object{Data: resourceGroup}, nil).Times(1) + Return(&database.Object{Data: resourceGroup}, nil).Times(1) // Force a retry. updater.Result = &trackedresource.InProgressErr{} @@ -117,11 +117,11 @@ func Test_Run(t *testing.T) { }) t.Run("Failure (resource not found)", func(t *testing.T) { - pc, _, storageClient := setup(t) + pc, _, databaseClient := setup(t) - storageClient.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), trackingID.String(), gomock.Any()). - Return(nil, &store.ErrNotFound{}).Times(1) + Return(nil, &database.ErrNotFound{}).Times(1) expected := controller.NewFailedResult(v1.ErrorDetails{ Code: v1.CodeNotFound, @@ -135,15 +135,15 @@ func Test_Run(t *testing.T) { }) t.Run("Failure (validate downstream: not found)", func(t *testing.T) { - pc, _, storageClient := setup(t) + pc, _, databaseClient := setup(t) - storageClient.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), trackingID.String(), gomock.Any()). - Return(&store.Object{Data: data}, nil).Times(1) + Return(&database.Object{Data: data}, nil).Times(1) - storageClient.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), "/planes/"+trackingID.PlaneNamespace(), gomock.Any()). - Return(nil, &store.ErrNotFound{}).Times(1) + Return(nil, &database.ErrNotFound{}).Times(1) expected := controller.NewFailedResult(v1.ErrorDetails{ Code: v1.CodeNotFound, diff --git a/pkg/ucp/backend/controller/resourceproviders/apiversion_delete.go b/pkg/ucp/backend/controller/resourceproviders/apiversion_delete.go index 4cf969d673..e2efad47a5 100644 --- a/pkg/ucp/backend/controller/resourceproviders/apiversion_delete.go +++ b/pkg/ucp/backend/controller/resourceproviders/apiversion_delete.go @@ -38,12 +38,12 @@ func (c *APIVersionDeleteController) Run(ctx context.Context, request *ctrl.Requ return ctrl.Result{}, err } - err = updateResourceProviderSummaryWithETag(ctx, c.StorageClient(), summaryID, summaryNotFoundIgnore, c.updateSummary(id)) + err = updateResourceProviderSummaryWithETag(ctx, c.DatabaseClient(), summaryID, summaryNotFoundIgnore, c.updateSummary(id)) if err != nil { return ctrl.Result{}, err } - err = c.StorageClient().Delete(ctx, request.ResourceID) + err = c.DatabaseClient().Delete(ctx, request.ResourceID) if err != nil { return ctrl.Result{}, err } diff --git a/pkg/ucp/backend/controller/resourceproviders/apiversion_put.go b/pkg/ucp/backend/controller/resourceproviders/apiversion_put.go index ca97bfe308..7891ccb5d2 100644 --- a/pkg/ucp/backend/controller/resourceproviders/apiversion_put.go +++ b/pkg/ucp/backend/controller/resourceproviders/apiversion_put.go @@ -39,7 +39,7 @@ func (c *APIVersionPutController) Run(ctx context.Context, request *ctrl.Request return ctrl.Result{}, err } - err = updateResourceProviderSummaryWithETag(ctx, c.StorageClient(), summaryID, summaryNotFoundFail, c.updateSummary(id)) + err = updateResourceProviderSummaryWithETag(ctx, c.DatabaseClient(), summaryID, summaryNotFoundFail, c.updateSummary(id)) if err != nil { return ctrl.Result{}, err } diff --git a/pkg/ucp/backend/controller/resourceproviders/location_delete.go b/pkg/ucp/backend/controller/resourceproviders/location_delete.go index 987ab21439..434396e9f1 100644 --- a/pkg/ucp/backend/controller/resourceproviders/location_delete.go +++ b/pkg/ucp/backend/controller/resourceproviders/location_delete.go @@ -38,12 +38,12 @@ func (c *LocationDeleteController) Run(ctx context.Context, request *ctrl.Reques return ctrl.Result{}, err } - err = updateResourceProviderSummaryWithETag(ctx, c.StorageClient(), summaryID, summaryNotFoundIgnore, c.updateSummary(id)) + err = updateResourceProviderSummaryWithETag(ctx, c.DatabaseClient(), summaryID, summaryNotFoundIgnore, c.updateSummary(id)) if err != nil { return ctrl.Result{}, err } - err = c.StorageClient().Delete(ctx, request.ResourceID) + err = c.DatabaseClient().Delete(ctx, request.ResourceID) if err != nil { return ctrl.Result{}, err } diff --git a/pkg/ucp/backend/controller/resourceproviders/location_put.go b/pkg/ucp/backend/controller/resourceproviders/location_put.go index 7ff0e3a130..7c118a2b4c 100644 --- a/pkg/ucp/backend/controller/resourceproviders/location_put.go +++ b/pkg/ucp/backend/controller/resourceproviders/location_put.go @@ -38,7 +38,7 @@ func (c *LocationPutController) Run(ctx context.Context, request *ctrl.Request) return ctrl.Result{}, err } - err = updateResourceProviderSummaryWithETag(ctx, c.StorageClient(), summaryID, summaryNotFoundFail, c.updateSummary(id)) + err = updateResourceProviderSummaryWithETag(ctx, c.DatabaseClient(), summaryID, summaryNotFoundFail, c.updateSummary(id)) if err != nil { return ctrl.Result{}, err } diff --git a/pkg/ucp/backend/controller/resourceproviders/resourceprovider_delete.go b/pkg/ucp/backend/controller/resourceproviders/resourceprovider_delete.go index 0984c4d2f7..450aa29261 100644 --- a/pkg/ucp/backend/controller/resourceproviders/resourceprovider_delete.go +++ b/pkg/ucp/backend/controller/resourceproviders/resourceprovider_delete.go @@ -25,9 +25,9 @@ import ( aztoken "github.com/radius-project/radius/pkg/azure/tokencredentials" "github.com/radius-project/radius/pkg/sdk" "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/resources" resources_radius "github.com/radius-project/radius/pkg/ucp/resources/radius" - "github.com/radius-project/radius/pkg/ucp/store" "github.com/radius-project/radius/pkg/ucp/ucplog" ) @@ -53,7 +53,7 @@ func (c *ResourceProviderDeleteController) Run(ctx context.Context, request *ctr return ctrl.Result{}, fmt.Errorf("failed to delete resource provider summary: %w", err) } - err = c.StorageClient().Delete(ctx, request.ResourceID) + err = c.DatabaseClient().Delete(ctx, request.ResourceID) if err != nil { return ctrl.Result{}, err } @@ -222,8 +222,8 @@ func (c *ResourceProviderDeleteController) deleteSummary(ctx context.Context, re return err } - err = c.StorageClient().Delete(ctx, summaryID.String()) - if errors.Is(err, &store.ErrNotFound{}) { + err = c.DatabaseClient().Delete(ctx, summaryID.String()) + if errors.Is(err, &database.ErrNotFound{}) { // It's OK if the summary was already deleted. return nil } else if err != nil { diff --git a/pkg/ucp/backend/controller/resourceproviders/resourceprovider_put.go b/pkg/ucp/backend/controller/resourceproviders/resourceprovider_put.go index de416896fa..16dc046fef 100644 --- a/pkg/ucp/backend/controller/resourceproviders/resourceprovider_put.go +++ b/pkg/ucp/backend/controller/resourceproviders/resourceprovider_put.go @@ -37,7 +37,7 @@ func (c *ResourceProviderPutController) Run(ctx context.Context, request *ctrl.R return ctrl.Result{}, err } - err = updateResourceProviderSummaryWithETag(ctx, c.StorageClient(), summaryID, summaryNotFoundCreate, c.updateSummary()) + err = updateResourceProviderSummaryWithETag(ctx, c.DatabaseClient(), summaryID, summaryNotFoundCreate, c.updateSummary()) if err != nil { return ctrl.Result{}, err } diff --git a/pkg/ucp/backend/controller/resourceproviders/resourcetype_delete.go b/pkg/ucp/backend/controller/resourceproviders/resourcetype_delete.go index 76758b0b6f..72575b7e80 100644 --- a/pkg/ucp/backend/controller/resourceproviders/resourcetype_delete.go +++ b/pkg/ucp/backend/controller/resourceproviders/resourcetype_delete.go @@ -53,12 +53,12 @@ func (c *ResourceTypeDeleteController) Run(ctx context.Context, request *ctrl.Re return ctrl.Result{}, err } - err = updateResourceProviderSummaryWithETag(ctx, c.StorageClient(), summaryID, summaryNotFoundIgnore, c.updateSummary(id)) + err = updateResourceProviderSummaryWithETag(ctx, c.DatabaseClient(), summaryID, summaryNotFoundIgnore, c.updateSummary(id)) if err != nil { return ctrl.Result{}, err } - err = c.StorageClient().Delete(ctx, request.ResourceID) + err = c.DatabaseClient().Delete(ctx, request.ResourceID) if err != nil { return ctrl.Result{}, err } diff --git a/pkg/ucp/backend/controller/resourceproviders/resourcetype_put.go b/pkg/ucp/backend/controller/resourceproviders/resourcetype_put.go index 890be9748e..ee1c02eae1 100644 --- a/pkg/ucp/backend/controller/resourceproviders/resourcetype_put.go +++ b/pkg/ucp/backend/controller/resourceproviders/resourcetype_put.go @@ -43,7 +43,7 @@ func (c *ResourceTypePutController) Run(ctx context.Context, request *ctrl.Reque return ctrl.Result{}, err } - err = updateResourceProviderSummaryWithETag(ctx, c.StorageClient(), summaryID, summaryNotFoundFail, c.updateSummary(id, resourceType)) + err = updateResourceProviderSummaryWithETag(ctx, c.DatabaseClient(), summaryID, summaryNotFoundFail, c.updateSummary(id, resourceType)) if err != nil { return ctrl.Result{}, err } @@ -52,7 +52,7 @@ func (c *ResourceTypePutController) Run(ctx context.Context, request *ctrl.Reque } func (c *ResourceTypePutController) fetchResourceType(ctx context.Context, id resources.ID) (*datamodel.ResourceType, error) { - obj, err := c.StorageClient().Get(ctx, id.String()) + obj, err := c.DatabaseClient().Get(ctx, id.String()) if err != nil { return nil, err } diff --git a/pkg/ucp/backend/controller/resourceproviders/util.go b/pkg/ucp/backend/controller/resourceproviders/util.go index e6c7ccafb5..4e1ea46ccd 100644 --- a/pkg/ucp/backend/controller/resourceproviders/util.go +++ b/pkg/ucp/backend/controller/resourceproviders/util.go @@ -23,9 +23,9 @@ import ( "strings" ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/store" "github.com/radius-project/radius/pkg/ucp/ucplog" ) @@ -63,8 +63,8 @@ func resourceProviderSummaryIDFromRequest(request *ctrl.Request) (resources.ID, return id, summaryID, nil } -// updateResourceProviderSummaryWithETag updates the summary with the provided function and saves it to the storage client. -func updateResourceProviderSummaryWithETag(ctx context.Context, client store.StorageClient, summaryID resources.ID, policy summaryNotFoundPolicy, update func(summary *datamodel.ResourceProviderSummary) error) error { +// updateResourceProviderSummaryWithETag updates the summary with the provided function and saves it to the database client. +func updateResourceProviderSummaryWithETag(ctx context.Context, client database.Client, summaryID resources.ID, policy summaryNotFoundPolicy, update func(summary *datamodel.ResourceProviderSummary) error) error { // There are a few cases here: // 1. The summary does not exist and we are allowed to create it (in the resource provider). // 2. The summary does not exist and we are not allowed to create it (in the child-types of resource provider). @@ -72,20 +72,20 @@ func updateResourceProviderSummaryWithETag(ctx context.Context, client store.Sto summary := &datamodel.ResourceProviderSummary{} obj, err := client.Get(ctx, summaryID.String()) - if errors.Is(err, &store.ErrNotFound{}) && policy == summaryNotFoundCreate { + if errors.Is(err, &database.ErrNotFound{}) && policy == summaryNotFoundCreate { // This is fine. We will create a new summary. summary.ID = summaryID.String() summary.Name = summaryID.Name() summary.Type = summaryID.Type() - obj = &store.Object{ - Metadata: store.Metadata{ + obj = &database.Object{ + Metadata: database.Metadata{ ID: summary.ID, }, } - } else if errors.Is(err, &store.ErrNotFound{}) && policy == summaryNotFoundIgnore { + } else if errors.Is(err, &database.ErrNotFound{}) && policy == summaryNotFoundIgnore { return nil - } else if errors.Is(err, &store.ErrNotFound{}) { + } else if errors.Is(err, &database.ErrNotFound{}) { return err } else if err != nil { return err @@ -104,9 +104,9 @@ func updateResourceProviderSummaryWithETag(ctx context.Context, client store.Sto } // Now we can save. Use the ETag if the resource already existed. - options := []store.SaveOptions{} + options := []database.SaveOptions{} if obj.ETag != "" { - options = append(options, store.WithETag(obj.ETag)) + options = append(options, database.WithETag(obj.ETag)) } obj.Data = summary diff --git a/pkg/ucp/backend/controller/resourceproviders/util_test.go b/pkg/ucp/backend/controller/resourceproviders/util_test.go index 874076ca3e..656be012f4 100644 --- a/pkg/ucp/backend/controller/resourceproviders/util_test.go +++ b/pkg/ucp/backend/controller/resourceproviders/util_test.go @@ -21,9 +21,9 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/store" "github.com/radius-project/radius/pkg/ucp/util/etag" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -151,11 +151,11 @@ func Test_UpdateResourceProviderSummaryWithETag(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) - client := store.NewMockStorageClient(ctrl) + client := database.NewMockClient(ctrl) expectedETag := "" if tt.existing == nil { - client.EXPECT().Get(gomock.Any(), tt.summaryID.String()).Return(nil, &store.ErrNotFound{}) + client.EXPECT().Get(gomock.Any(), tt.summaryID.String()).Return(nil, &database.ErrNotFound{}) } else { bs, err := json.Marshal(tt.existing) require.NoError(t, err) @@ -165,8 +165,8 @@ func Test_UpdateResourceProviderSummaryWithETag(t *testing.T) { require.NoError(t, err) expectedETag = etag.New(bs) - obj := store.Object{ - Metadata: store.Metadata{ + obj := database.Object{ + Metadata: database.Metadata{ ID: tt.summaryID.String(), ETag: expectedETag, }, @@ -176,9 +176,9 @@ func Test_UpdateResourceProviderSummaryWithETag(t *testing.T) { } if tt.expectedSave { - client.EXPECT().Save(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, o *store.Object, so ...store.SaveOptions) error { + client.EXPECT().Save(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, o *database.Object, so ...database.SaveOptions) error { - config := store.NewSaveConfig(so...) + config := database.NewSaveConfig(so...) require.Equal(t, expectedETag, config.ETag) expectedResourceTypes := map[string]datamodel.ResourceProviderSummaryPropertiesResourceType{ diff --git a/pkg/ucp/backend/service.go b/pkg/ucp/backend/service.go index 708dca726b..c61ed873b6 100644 --- a/pkg/ucp/backend/service.go +++ b/pkg/ucp/backend/service.go @@ -80,13 +80,13 @@ func (w *Service) Run(ctx context.Context) error { } } - storageClient, err := w.StorageProvider.GetClient(ctx) + databaseClient, err := w.DatabaseProvider.GetClient(ctx) if err != nil { return err } opts := ctrl.Options{ - StorageClient: storageClient, + DatabaseClient: databaseClient, } defaultDownstream, err := url.Parse(w.config.Routing.DefaultDownstreamEndpoint) diff --git a/pkg/ucp/credentials/aws.go b/pkg/ucp/credentials/aws.go index 3750062608..1fa8e52e95 100644 --- a/pkg/ucp/credentials/aws.go +++ b/pkg/ucp/credentials/aws.go @@ -26,20 +26,20 @@ import ( "github.com/radius-project/radius/pkg/to" ucpapi "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" "github.com/radius-project/radius/pkg/ucp/secret" - "github.com/radius-project/radius/pkg/ucp/secret/provider" + "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" ) var _ CredentialProvider[AWSCredential] = (*AWSCredentialProvider)(nil) // AWSCredentialProvider is UCP credential provider for Azure. type AWSCredentialProvider struct { - secretProvider *provider.SecretProvider + secretProvider *secretprovider.SecretProvider client *ucpapi.AwsCredentialsClient } // NewAWSCredentialProvider creates a new AWSCredentialProvider struct using the given SecretProvider, UCP connection and // TokenCredential, and returns it or an error if one occurs. -func NewAWSCredentialProvider(provider *provider.SecretProvider, ucpConn sdk.Connection, credential azcore.TokenCredential) (*AWSCredentialProvider, error) { +func NewAWSCredentialProvider(provider *secretprovider.SecretProvider, ucpConn sdk.Connection, credential azcore.TokenCredential) (*AWSCredentialProvider, error) { cli, err := ucpapi.NewAwsCredentialsClient(credential, sdk.NewClientOptions(ucpConn)) if err != nil { return nil, err diff --git a/pkg/ucp/credentials/azure.go b/pkg/ucp/credentials/azure.go index 2712c00fa6..4d9e773564 100644 --- a/pkg/ucp/credentials/azure.go +++ b/pkg/ucp/credentials/azure.go @@ -26,20 +26,20 @@ import ( "github.com/radius-project/radius/pkg/to" ucpapi "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" "github.com/radius-project/radius/pkg/ucp/secret" - "github.com/radius-project/radius/pkg/ucp/secret/provider" + "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" ) var _ CredentialProvider[AzureCredential] = (*AzureCredentialProvider)(nil) // AzureCredentialProvider is UCP credential provider for Azure. type AzureCredentialProvider struct { - secretProvider *provider.SecretProvider + secretProvider *secretprovider.SecretProvider client *ucpapi.AzureCredentialsClient } // NewAzureCredentialProvider creates a new AzureCredentialProvider by creating a new AzureCredentialClient with the given // credential and connection, and returns an error if one occurs. -func NewAzureCredentialProvider(provider *provider.SecretProvider, ucpConn sdk.Connection, credential azcore.TokenCredential) (*AzureCredentialProvider, error) { +func NewAzureCredentialProvider(provider *secretprovider.SecretProvider, ucpConn sdk.Connection, credential azcore.TokenCredential) (*AzureCredentialProvider, error) { cli, err := ucpapi.NewAzureCredentialsClient(credential, sdk.NewClientOptions(ucpConn)) if err != nil { return nil, err diff --git a/pkg/ucp/store/apiserverstore/api/ucp.dev/v1alpha1/groupversion_info.go b/pkg/ucp/database/apiserverstore/api/ucp.dev/v1alpha1/groupversion_info.go similarity index 100% rename from pkg/ucp/store/apiserverstore/api/ucp.dev/v1alpha1/groupversion_info.go rename to pkg/ucp/database/apiserverstore/api/ucp.dev/v1alpha1/groupversion_info.go diff --git a/pkg/ucp/store/apiserverstore/api/ucp.dev/v1alpha1/queuemessage_types.go b/pkg/ucp/database/apiserverstore/api/ucp.dev/v1alpha1/queuemessage_types.go similarity index 100% rename from pkg/ucp/store/apiserverstore/api/ucp.dev/v1alpha1/queuemessage_types.go rename to pkg/ucp/database/apiserverstore/api/ucp.dev/v1alpha1/queuemessage_types.go diff --git a/pkg/ucp/store/apiserverstore/api/ucp.dev/v1alpha1/resource_types.go b/pkg/ucp/database/apiserverstore/api/ucp.dev/v1alpha1/resource_types.go similarity index 100% rename from pkg/ucp/store/apiserverstore/api/ucp.dev/v1alpha1/resource_types.go rename to pkg/ucp/database/apiserverstore/api/ucp.dev/v1alpha1/resource_types.go diff --git a/pkg/ucp/store/apiserverstore/api/ucp.dev/v1alpha1/zz_generated.deepcopy.go b/pkg/ucp/database/apiserverstore/api/ucp.dev/v1alpha1/zz_generated.deepcopy.go similarity index 100% rename from pkg/ucp/store/apiserverstore/api/ucp.dev/v1alpha1/zz_generated.deepcopy.go rename to pkg/ucp/database/apiserverstore/api/ucp.dev/v1alpha1/zz_generated.deepcopy.go diff --git a/pkg/ucp/store/apiserverstore/apiserverclient.go b/pkg/ucp/database/apiserverstore/apiserverclient.go similarity index 83% rename from pkg/ucp/store/apiserverstore/apiserverclient.go rename to pkg/ucp/database/apiserverstore/apiserverclient.go index d684a2b2b5..6976bcfb71 100644 --- a/pkg/ucp/store/apiserverstore/apiserverclient.go +++ b/pkg/ucp/database/apiserverstore/apiserverclient.go @@ -48,10 +48,10 @@ import ( "strings" "unicode" + "github.com/radius-project/radius/pkg/ucp/database" + ucpv1alpha1 "github.com/radius-project/radius/pkg/ucp/database/apiserverstore/api/ucp.dev/v1alpha1" + "github.com/radius-project/radius/pkg/ucp/database/databaseutil" "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/store" - ucpv1alpha1 "github.com/radius-project/radius/pkg/ucp/store/apiserverstore/api/ucp.dev/v1alpha1" - "github.com/radius-project/radius/pkg/ucp/store/storeutil" "github.com/radius-project/radius/pkg/ucp/ucplog" "github.com/radius-project/radius/pkg/ucp/util/etag" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -87,7 +87,7 @@ func NewAPIServerClient(client runtimeclient.Client, namespace string) *APIServe return &APIServerClient{client: client, namespace: namespace} } -var _ store.StorageClient = (*APIServerClient)(nil) +var _ database.Client = (*APIServerClient)(nil) type APIServerClient struct { client runtimeclient.Client @@ -101,13 +101,13 @@ type APIServerClient struct { } // Query searches for objects in the store that match the given query and returns them. -func (c *APIServerClient) Query(ctx context.Context, query store.Query, options ...store.QueryOptions) (*store.ObjectQueryResult, error) { +func (c *APIServerClient) Query(ctx context.Context, query database.Query, options ...database.QueryOptions) (*database.ObjectQueryResult, error) { if ctx == nil { - return nil, &store.ErrInvalid{Message: "invalid argument. 'ctx' is required"} + return nil, &database.ErrInvalid{Message: "invalid argument. 'ctx' is required"} } err := query.Validate() if err != nil { - return nil, &store.ErrInvalid{Message: fmt.Sprintf("invalid argument. Query is invalid: %s", err.Error())} + return nil, &database.ErrInvalid{Message: fmt.Sprintf("invalid argument. Query is invalid: %s", err.Error())} } selector, err := createLabelSelector(query) @@ -121,7 +121,7 @@ func (c *APIServerClient) Query(ctx context.Context, query store.Query, options return nil, err } - results := store.ObjectQueryResult{} + results := database.ObjectQueryResult{} for _, resource := range rs.Items { for _, entry := range resource.Entries { id, err := resources.Parse(entry.ID) @@ -133,7 +133,7 @@ func (c *APIServerClient) Query(ctx context.Context, query store.Query, options continue } - if storeutil.IDMatchesQuery(id, query) { + if databaseutil.IDMatchesQuery(id, query) { converted, err := readEntry(&entry) if err != nil { return nil, err @@ -155,19 +155,19 @@ func (c *APIServerClient) Query(ctx context.Context, query store.Query, options } // Get retrieves an object from the store given its ID, or returns an error if the object does not exist or if an error occurs. -func (c *APIServerClient) Get(ctx context.Context, id string, options ...store.GetOptions) (*store.Object, error) { +func (c *APIServerClient) Get(ctx context.Context, id string, options ...database.GetOptions) (*database.Object, error) { if ctx == nil { - return nil, &store.ErrInvalid{Message: "invalid argument. 'ctx' is required"} + return nil, &database.ErrInvalid{Message: "invalid argument. 'ctx' is required"} } parsed, err := resources.Parse(id) if err != nil { - return nil, &store.ErrInvalid{Message: "invalid argument. 'id' must be a valid resource id"} + return nil, &database.ErrInvalid{Message: "invalid argument. 'id' must be a valid resource id"} } if parsed.IsEmpty() { - return nil, &store.ErrInvalid{Message: "invalid argument. 'id' must not be empty"} + return nil, &database.ErrInvalid{Message: "invalid argument. 'id' must not be empty"} } if parsed.IsResourceCollection() || parsed.IsScopeCollection() { - return nil, &store.ErrInvalid{Message: "invalid argument. 'id' must refer to a named resource, not a collection"} + return nil, &database.ErrInvalid{Message: "invalid argument. 'id' must refer to a named resource, not a collection"} } resourceName := resourceName(parsed) @@ -175,7 +175,7 @@ func (c *APIServerClient) Get(ctx context.Context, id string, options ...store.G resource := ucpv1alpha1.Resource{} err = c.client.Get(ctx, runtimeclient.ObjectKey{Namespace: c.namespace, Name: resourceName}, &resource) if err != nil && apierrors.IsNotFound(err) { - return nil, &store.ErrNotFound{ID: id} + return nil, &database.ErrNotFound{ID: id} } else if err != nil { return nil, err } @@ -184,50 +184,50 @@ func (c *APIServerClient) Get(ctx context.Context, id string, options ...store.G if err != nil { return nil, err } else if obj == nil { - return nil, &store.ErrNotFound{ID: id} + return nil, &database.ErrNotFound{ID: id} } return obj, nil } // Delete deletes a resource from the store, returning an error if the resource does not exist or if there is a concurrency conflict. -func (c *APIServerClient) Delete(ctx context.Context, id string, options ...store.DeleteOptions) error { +func (c *APIServerClient) Delete(ctx context.Context, id string, options ...database.DeleteOptions) error { if ctx == nil { - return &store.ErrInvalid{Message: "invalid argument. 'ctx' is required"} + return &database.ErrInvalid{Message: "invalid argument. 'ctx' is required"} } parsed, err := resources.Parse(id) if err != nil { - return &store.ErrInvalid{Message: "invalid argument. 'id' must be a valid resource id"} + return &database.ErrInvalid{Message: "invalid argument. 'id' must be a valid resource id"} } if parsed.IsEmpty() { - return &store.ErrInvalid{Message: "invalid argument. 'id' must not be empty"} + return &database.ErrInvalid{Message: "invalid argument. 'id' must not be empty"} } if parsed.IsResourceCollection() || parsed.IsScopeCollection() { - return &store.ErrInvalid{Message: "invalid argument. 'id' must refer to a named resource, not a collection"} + return &database.ErrInvalid{Message: "invalid argument. 'id' must refer to a named resource, not a collection"} } resourceName := resourceName(parsed) - config := store.NewDeleteConfig(options...) + config := database.NewDeleteConfig(options...) err = c.doWithRetry(func() (bool, error) { resource := ucpv1alpha1.Resource{} err := c.client.Get(ctx, runtimeclient.ObjectKey{Namespace: c.namespace, Name: resourceName}, &resource) if err != nil && apierrors.IsNotFound(err) && config.ETag != "" { - return false, &store.ErrConcurrency{} + return false, &database.ErrConcurrency{} } else if err != nil && apierrors.IsNotFound(err) { - return false, &store.ErrNotFound{ID: id} + return false, &database.ErrNotFound{ID: id} } else if err != nil { return false, err } index := findIndex(&resource, parsed) if index == nil { - return false, &store.ErrNotFound{ID: id} + return false, &database.ErrNotFound{ID: id} } if config.ETag != "" && config.ETag != resource.Entries[*index].ETag { - return false, &store.ErrConcurrency{} + return false, &database.ErrConcurrency{} } c.synchronize() @@ -242,7 +242,7 @@ func (c *APIServerClient) Delete(ctx context.Context, id string, options ...stor } err := c.client.Delete(ctx, &resource, &options) if err != nil && apierrors.IsNotFound(err) { - return false, &store.ErrNotFound{ID: id} + return false, &database.ErrNotFound{ID: id} } else if apierrors.IsConflict(err) { return true, err // RETRY this! } else if err != nil { @@ -256,7 +256,7 @@ func (c *APIServerClient) Delete(ctx context.Context, id string, options ...stor err := c.client.Update(ctx, &resource) if err != nil && apierrors.IsNotFound(err) { - return false, &store.ErrNotFound{ID: id} + return false, &database.ErrNotFound{ID: id} } else if apierrors.IsConflict(err) { return true, err // RETRY this! } else if err != nil { @@ -271,12 +271,12 @@ func (c *APIServerClient) Delete(ctx context.Context, id string, options ...stor } // Save saves an object to the store, or updates an existing object if it already exists, and returns an error if the operation fails. -func (c *APIServerClient) Save(ctx context.Context, obj *store.Object, options ...store.SaveOptions) error { +func (c *APIServerClient) Save(ctx context.Context, obj *database.Object, options ...database.SaveOptions) error { if ctx == nil { - return &store.ErrInvalid{Message: "invalid argument. 'ctx' is required"} + return &database.ErrInvalid{Message: "invalid argument. 'ctx' is required"} } if obj == nil { - return &store.ErrInvalid{Message: "invalid argument. 'obj' is required"} + return &database.ErrInvalid{Message: "invalid argument. 'obj' is required"} } id, err := resources.Parse(obj.ID) @@ -286,7 +286,7 @@ func (c *APIServerClient) Save(ctx context.Context, obj *store.Object, options . resourceName := resourceName(id) - config := store.NewSaveConfig(options...) + config := database.NewSaveConfig(options...) err = c.doWithRetry(func() (bool, error) { found := true @@ -314,12 +314,12 @@ func (c *APIServerClient) Save(ctx context.Context, obj *store.Object, options . if index == nil && config.ETag != "" { // The ETag is only meaning for a replace/update operation not a create. We treat // the absence of the resource as a match failure. - return false, &store.ErrConcurrency{} + return false, &database.ErrConcurrency{} } else if index == nil { resource.Entries = append(resource.Entries, *converted) } else { if config.ETag != "" && config.ETag != resource.Entries[*index].ETag { - return false, &store.ErrConcurrency{} + return false, &database.ErrConcurrency{} } resource.Entries[*index] = *converted @@ -368,7 +368,7 @@ func (c *APIServerClient) doWithRetry(action func() (bool, error)) error { } // If we get here then we ran out of retries. - return &store.ErrConcurrency{} + return &database.ErrConcurrency{} } // synchronize is used for testing concurrency behavior. The client can be configured by tests to pause between reading and writing @@ -406,9 +406,9 @@ func resourceName(id resources.ID) string { _, _ = hasher.Write([]byte(strings.ToLower(id.String()))) hash := hasher.Sum(nil) - prefix := store.UCPResourcePrefix + prefix := database.UCPResourcePrefix if id.IsScope() { - prefix = store.UCPScopePrefix + prefix = database.UCPScopePrefix } noramlizedName := normalizeName(id.Name()) @@ -432,7 +432,7 @@ func assignLabels(resource *ucpv1alpha1.Resource) labels.Set { continue } - prefix, rootScope, _, resourceType := storeutil.ExtractStorageParts(id) + prefix, rootScope, _, resourceType := databaseutil.ExtractStorageParts(id) set[LabelKind] = prefix // We need to take apart the root scope so we can turn it into key-value-pairs. @@ -465,7 +465,7 @@ func assignLabels(resource *ucpv1alpha1.Resource) labels.Set { return set } -func createLabelSelector(query store.Query) (labels.Selector, error) { +func createLabelSelector(query database.Query) (labels.Selector, error) { id, err := resources.Parse(query.RootScope) if err != nil { return nil, err @@ -473,14 +473,14 @@ func createLabelSelector(query store.Query) (labels.Selector, error) { selector := labels.NewSelector() if query.IsScopeQuery { - requirement, err := labels.NewRequirement(LabelKind, selection.Equals, []string{storeutil.ScopePrefix}) + requirement, err := labels.NewRequirement(LabelKind, selection.Equals, []string{databaseutil.ScopePrefix}) if err != nil { return nil, err } selector = selector.Add(*requirement) } else { - requirement, err := labels.NewRequirement(LabelKind, selection.Equals, []string{storeutil.ResourcePrefix}) + requirement, err := labels.NewRequirement(LabelKind, selection.Equals, []string{databaseutil.ResourcePrefix}) if err != nil { return nil, err } @@ -524,15 +524,15 @@ func findIndex(resource *ucpv1alpha1.Resource, id resources.ID) *int { return nil } -func readEntry(entry *ucpv1alpha1.ResourceEntry) (*store.Object, error) { +func readEntry(entry *ucpv1alpha1.ResourceEntry) (*database.Object, error) { var data any err := json.Unmarshal(entry.Data.Raw, &data) if err != nil { return nil, err } - obj := store.Object{ - Metadata: store.Metadata{ + obj := database.Object{ + Metadata: database.Metadata{ ID: entry.ID, ETag: entry.ETag, }, @@ -542,7 +542,7 @@ func readEntry(entry *ucpv1alpha1.ResourceEntry) (*store.Object, error) { return &obj, nil } -func read(resource *ucpv1alpha1.Resource, id resources.ID) (*store.Object, error) { +func read(resource *ucpv1alpha1.Resource, id resources.ID) (*database.Object, error) { for _, entry := range resource.Entries { if strings.EqualFold(entry.ID, id.String()) { return readEntry(&entry) @@ -552,7 +552,7 @@ func read(resource *ucpv1alpha1.Resource, id resources.ID) (*store.Object, error return nil, nil } -func convert(obj *store.Object) (*ucpv1alpha1.ResourceEntry, error) { +func convert(obj *database.Object) (*ucpv1alpha1.ResourceEntry, error) { raw, err := json.Marshal(obj.Data) if err != nil { return nil, err diff --git a/pkg/ucp/store/apiserverstore/apiserverclient_test.go b/pkg/ucp/database/apiserverstore/apiserverclient_test.go similarity index 97% rename from pkg/ucp/store/apiserverstore/apiserverclient_test.go rename to pkg/ucp/database/apiserverstore/apiserverclient_test.go index ffcec75416..cdc28fbda1 100644 --- a/pkg/ucp/store/apiserverstore/apiserverclient_test.go +++ b/pkg/ucp/database/apiserverstore/apiserverclient_test.go @@ -28,9 +28,9 @@ import ( "k8s.io/apimachinery/pkg/runtime" runtimeclient "sigs.k8s.io/controller-runtime/pkg/client" + "github.com/radius-project/radius/pkg/ucp/database" + ucpv1alpha1 "github.com/radius-project/radius/pkg/ucp/database/apiserverstore/api/ucp.dev/v1alpha1" "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/store" - ucpv1alpha1 "github.com/radius-project/radius/pkg/ucp/store/apiserverstore/api/ucp.dev/v1alpha1" "github.com/radius-project/radius/pkg/ucp/util/etag" "github.com/radius-project/radius/test/testcontext" "github.com/radius-project/radius/test/ucp/kubeenv" @@ -137,8 +137,8 @@ func Test_APIServer_Client(t *testing.T) { t.Run("save_resource_and_validate_kubernetes_object", func(t *testing.T) { clear(t) - obj1 := store.Object{ - Metadata: store.Metadata{ + obj1 := database.Object{ + Metadata: database.Metadata{ ID: shared.Resource1ID.String(), }, Data: shared.Data1, @@ -166,8 +166,8 @@ func Test_APIServer_Client(t *testing.T) { t.Run("save_resource_and_validate_kubernetes_object_uppercase_name", func(t *testing.T) { clear(t) - obj1 := store.Object{ - Metadata: store.Metadata{ + obj1 := database.Object{ + Metadata: database.Metadata{ ID: shared.Resource3ID.String(), }, Data: shared.Data1, @@ -195,8 +195,8 @@ func Test_APIServer_Client(t *testing.T) { t.Run("save_scope_and_validate_kubernetes_object", func(t *testing.T) { clear(t) - obj1 := store.Object{ - Metadata: store.Metadata{ + obj1 := database.Object{ + Metadata: database.Metadata{ ID: shared.ResourceGroup1ID.String(), }, Data: shared.ResourceGroup1Data, @@ -243,8 +243,8 @@ func Test_APIServer_Client(t *testing.T) { err := rc.Create(ctx, &resource) require.NoError(t, err) - obj2 := store.Object{ - Metadata: store.Metadata{ + obj2 := database.Object{ + Metadata: database.Metadata{ ID: shared.Resource2ID.String(), }, Data: shared.Data2, @@ -288,12 +288,12 @@ func Test_APIServer_Client(t *testing.T) { require.Equal(t, shared.Data2, obj.Data) // We can query it though... - objs, err := client.Query(ctx, store.Query{RootScope: shared.RadiusScope, ScopeRecursive: true, ResourceType: shared.ResourceType2}) + objs, err := client.Query(ctx, database.Query{RootScope: shared.RadiusScope, ScopeRecursive: true, ResourceType: shared.ResourceType2}) require.NoError(t, err) - expected := []store.Object{ + expected := []database.Object{ *obj, { - Metadata: store.Metadata{ + Metadata: database.Metadata{ ID: shared.Resource3ID.String(), ETag: etag.New(shared.MarshalOrPanic(shared.Data3)), }, @@ -322,8 +322,8 @@ func Test_APIServer_Client(t *testing.T) { // Start an operation to "save" resource 1 go func() { - obj1 := store.Object{ - Metadata: store.Metadata{ + obj1 := database.Object{ + Metadata: database.Metadata{ ID: shared.Resource1ID.String(), }, Data: shared.Data1, @@ -427,8 +427,8 @@ func Test_APIServer_Client(t *testing.T) { // Start an operation to "save" resource 1 go func() { - obj1 := store.Object{ - Metadata: store.Metadata{ + obj1 := database.Object{ + Metadata: database.Metadata{ ID: shared.Resource1ID.String(), }, Data: shared.Data1, @@ -729,7 +729,7 @@ func Test_AssignLabels_AllConflict(t *testing.T) { } func Test_CreateLabelSelector_UCPID(t *testing.T) { - query := store.Query{ + query := database.Query{ RootScope: "/planes/radius/local/resourceGroups/cool-group", ResourceType: "Applications.Core/containers", } @@ -772,7 +772,7 @@ func Test_CreateLabelSelector_UCPID(t *testing.T) { } func Test_CreateLabelSelector_ResourceQuery(t *testing.T) { - query := store.Query{ + query := database.Query{ RootScope: "/planes/radius/local/resourceGroups/cool-group", ResourceType: "Applications.Core/containers", } @@ -815,7 +815,7 @@ func Test_CreateLabelSelector_ResourceQuery(t *testing.T) { } func Test_CreateLabelSelector_ScopeQuery(t *testing.T) { - query := store.Query{ + query := database.Query{ RootScope: "/planes/radius/local", ResourceType: "resourceGroups", IsScopeQuery: true, diff --git a/pkg/ucp/store/client.go b/pkg/ucp/database/client.go similarity index 91% rename from pkg/ucp/store/client.go rename to pkg/ucp/database/client.go index 471e45a577..32c1a7b76b 100644 --- a/pkg/ucp/store/client.go +++ b/pkg/ucp/database/client.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package store +package database import ( "context" @@ -31,18 +31,18 @@ var jsonPropertyPattern = "[a-zA-Z$_][a-zA-Z0-9$_]*" // - Multople properties separated by a '.' var fieldRegex = regexp.MustCompile(fmt.Sprintf(`^(%s)(\.%s)*$`, jsonPropertyPattern, jsonPropertyPattern)) -//go:generate mockgen -typed -destination=./mock_storageClient.go -package=store -self_package github.com/radius-project/radius/pkg/ucp/store github.com/radius-project/radius/pkg/ucp/store StorageClient +//go:generate mockgen -typed -destination=./mock_client.go -package=database -self_package github.com/radius-project/radius/pkg/ucp/database github.com/radius-project/radius/pkg/ucp/database Client -// StorageClient is the interface for persisting and querying resource data. +// Client is the interface for persisting and querying resource data. // -// The StorageClient is purpose-built to work with resource data and understands concepts like +// The Client is purpose-built to work with resource data and understands concepts like // scopes, resource types, and resource ids. This is a higher level abstraction than a generic // key-value store, but low-level enough to support multiple implementation strategies. // -// The StorageClient provides a optimistic concurrency control using ETags. Callers that want +// The Client provides a optimistic concurrency control using ETags. Callers that want // to enforce OCC should provide the ETag value in the options when calling Save or Delete. // -// The StorageClient may return the errors ErrNotFound, ErrInvalid, and ErrConcurrency. +// The Client may return the errors ErrNotFound, ErrInvalid, and ErrConcurrency. // // - Callers should handle ErrNotFound on Get, Save, and Delete operations. // - Callers should handle ErrConcurrency when using ETags. @@ -50,7 +50,7 @@ var fieldRegex = regexp.MustCompile(fmt.Sprintf(`^(%s)(\.%s)*$`, jsonPropertyPat // // When using ETags, the Save or Delete operation will fail with ErrConcurrency (rather than ErrNotFound) // if the underlying resource has been deleted. -type StorageClient interface { +type Client interface { // Query executes a query against the data store and returns the results. // // Queries must provide a root scope and a resource type. Other fields are optional. diff --git a/pkg/ucp/store/client_test.go b/pkg/ucp/database/client_test.go similarity index 99% rename from pkg/ucp/store/client_test.go rename to pkg/ucp/database/client_test.go index 580fe2b9b7..10e4352582 100644 --- a/pkg/ucp/store/client_test.go +++ b/pkg/ucp/database/client_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package store +package database import ( "testing" diff --git a/pkg/ucp/store/storeutil/doc.go b/pkg/ucp/database/databaseutil/doc.go similarity index 84% rename from pkg/ucp/store/storeutil/doc.go rename to pkg/ucp/database/databaseutil/doc.go index 2733db3279..e465360de3 100644 --- a/pkg/ucp/store/storeutil/doc.go +++ b/pkg/ucp/database/databaseutil/doc.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -// storeutil contains utility functions for implementing storage backends. The store package only +// storeutil contains utility functions for implementing database backends. The store package only // has dependencies on the std library (intentionally) so that clients of the store are easy to // reference. -package storeutil +package databaseutil diff --git a/pkg/ucp/store/storeutil/id.go b/pkg/ucp/database/databaseutil/id.go similarity index 97% rename from pkg/ucp/store/storeutil/id.go rename to pkg/ucp/database/databaseutil/id.go index 4b72a54624..a2f953abcb 100644 --- a/pkg/ucp/store/storeutil/id.go +++ b/pkg/ucp/database/databaseutil/id.go @@ -14,14 +14,14 @@ See the License for the specific language governing permissions and limitations under the License. */ -package storeutil +package databaseutil import ( "fmt" "strings" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/store" ) const ( @@ -59,7 +59,7 @@ func ExtractStorageParts(id resources.ID) (string, string, string, string) { } // IDMatchesQuery checks if the given ID matches the given query. -func IDMatchesQuery(id resources.ID, query store.Query) bool { +func IDMatchesQuery(id resources.ID, query database.Query) bool { prefix, rootScope, routingScope, resourceType := ExtractStorageParts(id) if query.IsScopeQuery && !strings.EqualFold(prefix, ScopePrefix) { return false diff --git a/pkg/ucp/store/storeutil/id_test.go b/pkg/ucp/database/databaseutil/id_test.go similarity index 95% rename from pkg/ucp/store/storeutil/id_test.go rename to pkg/ucp/database/databaseutil/id_test.go index 7fe40de4cb..4b2a13d3b1 100644 --- a/pkg/ucp/store/storeutil/id_test.go +++ b/pkg/ucp/database/databaseutil/id_test.go @@ -14,13 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -package storeutil +package databaseutil import ( "testing" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/store" "github.com/stretchr/testify/require" ) @@ -109,7 +109,7 @@ func Test_ExtractStorageParts(t *testing.T) { func Test_IDMatchesQuery(t *testing.T) { type testcase struct { ID string - Query store.Query + Query database.Query IsMatch bool } @@ -117,26 +117,26 @@ func Test_IDMatchesQuery(t *testing.T) { // Tests in this section target block-coverage of all of our negative cases. { ID: "/planes/radius/local", - Query: store.Query{ + Query: database.Query{ IsScopeQuery: false, // mismatched query type }, }, { ID: "/planes/radius/local/resourceGroups/cool-group/providers/Applications.Core/applications/cool-app", - Query: store.Query{ + Query: database.Query{ IsScopeQuery: true, // mismatched query type }, }, { ID: "/planes/radius/local/resourceGroups/cool-group", - Query: store.Query{ + Query: database.Query{ RootScope: "/planes/radius/local/resourceGroups/cool-group", // mismatched root scope IsScopeQuery: true, }, }, { ID: "/planes/radius/local/resourceGroups/cool-group", - Query: store.Query{ + Query: database.Query{ RootScope: "/planes/radius/another-plane", // mismatched root scope ScopeRecursive: true, IsScopeQuery: true, @@ -144,7 +144,7 @@ func Test_IDMatchesQuery(t *testing.T) { }, { ID: "/subscriptions/cool-sub/resourceGroups/cool-group/providers/Applications.Core/applications/cool-app/nested/cool-nested", - Query: store.Query{ + Query: database.Query{ RootScope: "/subscriptions/cool-sub/resourceGroups/cool-group", RoutingScopePrefix: "Applications.Core/applications/different-app", // mismatched routing scope prefix IsScopeQuery: false, @@ -152,7 +152,7 @@ func Test_IDMatchesQuery(t *testing.T) { }, { ID: "/planes/radius/local/resourceGroups/cool-group/providers/Applications.Core/applications/cool-app", - Query: store.Query{ + Query: database.Query{ RootScope: "/planes/radius/local/resourceGroups", ResourceType: "Applications.Core/containers", // mismatched resource type IsScopeQuery: false, @@ -162,7 +162,7 @@ func Test_IDMatchesQuery(t *testing.T) { // Tests in this section target our main use-cases for the query logic. { ID: "/planes/radius/local", - Query: store.Query{ + Query: database.Query{ RootScope: "/planes", // list all planes (regardless of type) IsScopeQuery: true, }, @@ -170,7 +170,7 @@ func Test_IDMatchesQuery(t *testing.T) { }, { ID: "/planes/radius/local", - Query: store.Query{ + Query: database.Query{ RootScope: "/planes", // list all planes (specific type) ResourceType: "radius", IsScopeQuery: true, @@ -179,7 +179,7 @@ func Test_IDMatchesQuery(t *testing.T) { }, { ID: "/planes/radius/local/resourceGroups/cool-group", - Query: store.Query{ + Query: database.Query{ RootScope: "/planes/radius/local/", // list all resource groups ResourceType: "resourceGroups", IsScopeQuery: true, @@ -188,7 +188,7 @@ func Test_IDMatchesQuery(t *testing.T) { }, { ID: "/planes/radius/local/resourceGroups/cool-group", - Query: store.Query{ + Query: database.Query{ RootScope: "/planes", // list all resource groups across planes ResourceType: "resourceGroups", ScopeRecursive: true, @@ -198,7 +198,7 @@ func Test_IDMatchesQuery(t *testing.T) { }, { ID: "/planes/radius/local/resourceGroups/cool-group/providers/Applications.Core/applications/cool-app", - Query: store.Query{ + Query: database.Query{ RootScope: "/planes/radius/local", // list all resources in plane ScopeRecursive: true, IsScopeQuery: false, @@ -207,7 +207,7 @@ func Test_IDMatchesQuery(t *testing.T) { }, { ID: "/planes/radius/local/resourceGroups/cool-group/providers/Applications.Core/applications/cool-app", - Query: store.Query{ + Query: database.Query{ RootScope: "/planes/radius/local/resourceGroups/cool-group", // list all resources in resource group ScopeRecursive: false, IsScopeQuery: false, @@ -216,7 +216,7 @@ func Test_IDMatchesQuery(t *testing.T) { }, { ID: "/planes/radius/local/resourceGroups/cool-group/providers/Applications.Core/applications/cool-app", - Query: store.Query{ + Query: database.Query{ RootScope: "/planes/radius/local/resourceGroups/cool-group", // list all applications in resource group ResourceType: "Applications.Core/applications", ScopeRecursive: false, @@ -226,7 +226,7 @@ func Test_IDMatchesQuery(t *testing.T) { }, { ID: "/planes/radius/local/resourceGroups/cool-group/providers/Applications.Core/applications/cool-app", - Query: store.Query{ + Query: database.Query{ RootScope: "/planes/radius/local/", // list all applications in plane ResourceType: "Applications.Core/applications", ScopeRecursive: true, @@ -236,7 +236,7 @@ func Test_IDMatchesQuery(t *testing.T) { }, { ID: "/planes/radius/local/resourceGroups/cool-group/providers/Applications.Core/applications/cool-app/nested/cool-nested", - Query: store.Query{ + Query: database.Query{ RootScope: "/planes/radius/local/resourceGroups/cool-group", // list nested resources RoutingScopePrefix: "/Applications.Core/applications/cool-app", ResourceType: "Applications.Core/applications/nested", diff --git a/pkg/ucp/store/err.go b/pkg/ucp/database/err.go similarity index 99% rename from pkg/ucp/store/err.go rename to pkg/ucp/database/err.go index c0c0613a55..a38ce56562 100644 --- a/pkg/ucp/store/err.go +++ b/pkg/ucp/database/err.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package store +package database import "fmt" diff --git a/pkg/ucp/store/etcdstore/etcdclient.go b/pkg/ucp/database/etcdstore/etcdclient.go similarity index 76% rename from pkg/ucp/store/etcdstore/etcdclient.go rename to pkg/ucp/database/etcdstore/etcdclient.go index 4f5d1e8857..ed3c7a1a76 100644 --- a/pkg/ucp/store/etcdstore/etcdclient.go +++ b/pkg/ucp/database/etcdstore/etcdclient.go @@ -52,9 +52,9 @@ import ( "fmt" "strings" + "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/ucp/database/databaseutil" "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/store" - "github.com/radius-project/radius/pkg/ucp/store/storeutil" "github.com/radius-project/radius/pkg/ucp/util/etag" etcdclient "go.etcd.io/etcd/client/v3" ) @@ -68,21 +68,21 @@ func NewETCDClient(c *etcdclient.Client) *ETCDClient { return &ETCDClient{client: c} } -var _ store.StorageClient = (*ETCDClient)(nil) +var _ database.Client = (*ETCDClient)(nil) type ETCDClient struct { client *etcdclient.Client } // Query retrieves objects from the store that match the given query and filters, and returns them in a store.ObjectQueryResult. -func (c *ETCDClient) Query(ctx context.Context, query store.Query, options ...store.QueryOptions) (*store.ObjectQueryResult, error) { +func (c *ETCDClient) Query(ctx context.Context, query database.Query, options ...database.QueryOptions) (*database.ObjectQueryResult, error) { if ctx == nil { - return nil, &store.ErrInvalid{Message: "invalid argument. 'ctx' is required"} + return nil, &database.ErrInvalid{Message: "invalid argument. 'ctx' is required"} } - + err := query.Validate() if err != nil { - return nil, &store.ErrInvalid{Message: fmt.Sprintf("invalid argument. Query is invalid: %s", err.Error())} + return nil, &database.ErrInvalid{Message: fmt.Sprintf("invalid argument. Query is invalid: %s", err.Error())} } key := keyFromQuery(query) @@ -96,10 +96,10 @@ func (c *ETCDClient) Query(ctx context.Context, query store.Query, options ...st return nil, err } - results := store.ObjectQueryResult{} + results := database.ObjectQueryResult{} for _, kv := range response.Kvs { if keyMatchesQuery(kv.Key, query) { - value := store.Object{} + value := database.Object{} err = json.Unmarshal(kv.Value, &value) if err != nil { return nil, err @@ -122,19 +122,19 @@ func (c *ETCDClient) Query(ctx context.Context, query store.Query, options ...st // Get checks if the provided context, id and options are valid, then retrieves the corresponding object from // the store and returns it, or an error if the object is not found or an error occurs. -func (c *ETCDClient) Get(ctx context.Context, id string, options ...store.GetOptions) (*store.Object, error) { +func (c *ETCDClient) Get(ctx context.Context, id string, options ...database.GetOptions) (*database.Object, error) { if ctx == nil { - return nil, &store.ErrInvalid{Message: "invalid argument. 'ctx' is required"} + return nil, &database.ErrInvalid{Message: "invalid argument. 'ctx' is required"} } parsed, err := resources.Parse(id) if err != nil { - return nil, &store.ErrInvalid{Message: "invalid argument. 'id' must be a valid resource id"} + return nil, &database.ErrInvalid{Message: "invalid argument. 'id' must be a valid resource id"} } if parsed.IsEmpty() { - return nil, &store.ErrInvalid{Message: "invalid argument. 'id' must not be empty"} + return nil, &database.ErrInvalid{Message: "invalid argument. 'id' must not be empty"} } if parsed.IsResourceCollection() || parsed.IsScopeCollection() { - return nil, &store.ErrInvalid{Message: "invalid argument. 'id' must refer to a named resource, not a collection"} + return nil, &database.ErrInvalid{Message: "invalid argument. 'id' must refer to a named resource, not a collection"} } key := keyFromID(parsed) @@ -144,10 +144,10 @@ func (c *ETCDClient) Get(ctx context.Context, id string, options ...store.GetOpt } if response.Count == 0 { - return nil, &store.ErrNotFound{ID: id} + return nil, &database.ErrNotFound{ID: id} } - value := store.Object{} + value := database.Object{} err = json.Unmarshal(response.Kvs[0].Value, &value) if err != nil { return nil, err @@ -160,30 +160,30 @@ func (c *ETCDClient) Get(ctx context.Context, id string, options ...store.GetOpt // Delete checks if the given resource ID is valid, and if so, deletes it from the store, returning an error if the // resource does not exist or if an ETag is provided and does not match. -func (c *ETCDClient) Delete(ctx context.Context, id string, options ...store.DeleteOptions) error { +func (c *ETCDClient) Delete(ctx context.Context, id string, options ...database.DeleteOptions) error { if ctx == nil { - return &store.ErrInvalid{Message: "invalid argument. 'ctx' is required"} + return &database.ErrInvalid{Message: "invalid argument. 'ctx' is required"} } parsed, err := resources.Parse(id) if err != nil { - return &store.ErrInvalid{Message: "invalid argument. 'id' must be a valid resource id"} + return &database.ErrInvalid{Message: "invalid argument. 'id' must be a valid resource id"} } if parsed.IsEmpty() { - return &store.ErrInvalid{Message: "invalid argument. 'id' must not be empty"} + return &database.ErrInvalid{Message: "invalid argument. 'id' must not be empty"} } if parsed.IsResourceCollection() || parsed.IsScopeCollection() { - return &store.ErrInvalid{Message: "invalid argument. 'id' must refer to a named resource, not a collection"} + return &database.ErrInvalid{Message: "invalid argument. 'id' must refer to a named resource, not a collection"} } key := keyFromID(parsed) - config := store.NewDeleteConfig(options...) + config := database.NewDeleteConfig(options...) // If we have an ETag then we do to execute a transaction. if config.ETag != "" { revision, err := etag.ParseRevision(config.ETag) if err != nil { // Treat an invalid ETag as a concurrency failure, since it will never match. - return &store.ErrConcurrency{} + return &database.ErrConcurrency{} } txn, err := c.client.Txn(ctx). @@ -195,12 +195,12 @@ func (c *ETCDClient) Delete(ctx context.Context, id string, options ...store.Del } if !txn.Succeeded { - return &store.ErrConcurrency{} + return &database.ErrConcurrency{} } response := txn.Responses[0].GetResponseDeleteRange() if response.Deleted == 0 { - return &store.ErrNotFound{ID: id} + return &database.ErrNotFound{ID: id} } else { return nil } @@ -213,7 +213,7 @@ func (c *ETCDClient) Delete(ctx context.Context, id string, options ...store.Del } if response.Deleted == 0 { - return &store.ErrNotFound{ID: id} + return &database.ErrNotFound{ID: id} } return nil @@ -221,12 +221,12 @@ func (c *ETCDClient) Delete(ctx context.Context, id string, options ...store.Del // Save checks the context and object parameters, parses the object ID, marshals the object into JSON, saves the object to // the store, and sets the object's ETag. If an ETag is provided, a transaction is executed to ensure concurrency. -func (c *ETCDClient) Save(ctx context.Context, obj *store.Object, options ...store.SaveOptions) error { +func (c *ETCDClient) Save(ctx context.Context, obj *database.Object, options ...database.SaveOptions) error { if ctx == nil { - return &store.ErrInvalid{Message: "invalid argument. 'ctx' is required"} + return &database.ErrInvalid{Message: "invalid argument. 'ctx' is required"} } if obj == nil { - return &store.ErrInvalid{Message: "invalid argument. 'obj' is required"} + return &database.ErrInvalid{Message: "invalid argument. 'obj' is required"} } id := obj.Metadata.ID @@ -241,14 +241,14 @@ func (c *ETCDClient) Save(ctx context.Context, obj *store.Object, options ...sto } key := keyFromID(parsed) - config := store.NewSaveConfig(options...) + config := database.NewSaveConfig(options...) // If we have an ETag then we do to execute a transaction. if config.ETag != "" { revision, err := etag.ParseRevision(config.ETag) if err != nil { // Treat an invalid ETag as a concurrency failure, since it will never match. - return &store.ErrConcurrency{} + return &database.ErrConcurrency{} } txn, err := c.client.Txn(ctx). @@ -260,7 +260,7 @@ func (c *ETCDClient) Save(ctx context.Context, obj *store.Object, options ...sto } if !txn.Succeeded { - return &store.ErrConcurrency{} + return &database.ErrConcurrency{} } response := txn.Responses[0].GetResponsePut() @@ -293,12 +293,12 @@ func idFromKey(key []byte) (resources.ID, error) { } switch parts[0] { - case storeutil.ScopePrefix: + case databaseutil.ScopePrefix: // The key might look like: // scope|/planes/radius/local/|/resourceGroups/cool-group/ return resources.Parse(parts[1] + strings.TrimPrefix(parts[2], resources.SegmentSeparator)) - case storeutil.ResourcePrefix: + case databaseutil.ResourcePrefix: // The key might look like: // resource|/subscriptions/{guid}/resourceGroups/cool-group/|/Applications.Core/applications/cool-app/ return resources.Parse(parts[1] + resources.ProvidersSegment + parts[2]) @@ -310,34 +310,34 @@ func idFromKey(key []byte) (resources.ID, error) { // keyFromID returns the key to use for an ID. They key should be used as an exact match. func keyFromID(id resources.ID) string { - prefix, rootScope, routingScope, _ := storeutil.ExtractStorageParts(id) + prefix, rootScope, routingScope, _ := databaseutil.ExtractStorageParts(id) return prefix + SectionSeparator + rootScope + SectionSeparator + routingScope } // keyFromQuery returns the key to use for an for executing a query. The key should be used as a prefix. -func keyFromQuery(query store.Query) string { +func keyFromQuery(query database.Query) string { // These patterns require a prefix match for us in ETCd. // // A recursive query will not be able to consider anything in the routing scope, so it // always requires client-side filtering. - prefix := storeutil.ResourcePrefix + prefix := databaseutil.ResourcePrefix if query.IsScopeQuery { - prefix = storeutil.ScopePrefix + prefix = databaseutil.ScopePrefix } if query.ScopeRecursive { - return prefix + SectionSeparator + storeutil.NormalizePart(query.RootScope) + return prefix + SectionSeparator + databaseutil.NormalizePart(query.RootScope) } else { - return prefix + SectionSeparator + storeutil.NormalizePart(query.RootScope) + SectionSeparator + storeutil.NormalizePart(query.RoutingScopePrefix) + return prefix + SectionSeparator + databaseutil.NormalizePart(query.RootScope) + SectionSeparator + databaseutil.NormalizePart(query.RoutingScopePrefix) } } -func keyMatchesQuery(key []byte, query store.Query) bool { +func keyMatchesQuery(key []byte, query database.Query) bool { // Ignore invalid keys, we don't expect to find them. id, err := idFromKey(key) if err != nil { return false } - return storeutil.IDMatchesQuery(id, query) + return databaseutil.IDMatchesQuery(id, query) } diff --git a/pkg/ucp/store/etcdstore/etcdclient_test.go b/pkg/ucp/database/etcdstore/etcdclient_test.go similarity index 100% rename from pkg/ucp/store/etcdstore/etcdclient_test.go rename to pkg/ucp/database/etcdstore/etcdclient_test.go diff --git a/pkg/ucp/store/filter.go b/pkg/ucp/database/filter.go similarity index 99% rename from pkg/ucp/store/filter.go rename to pkg/ucp/database/filter.go index 7288aba2c9..7a99ada2e6 100644 --- a/pkg/ucp/store/filter.go +++ b/pkg/ucp/database/filter.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package store +package database import ( "reflect" diff --git a/pkg/ucp/store/filter_test.go b/pkg/ucp/database/filter_test.go similarity index 99% rename from pkg/ucp/store/filter_test.go rename to pkg/ucp/database/filter_test.go index c31aa7fcac..0c80924193 100644 --- a/pkg/ucp/store/filter_test.go +++ b/pkg/ucp/database/filter_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package store +package database import ( "testing" diff --git a/pkg/ucp/store/inmemory/client.go b/pkg/ucp/database/inmemory/client.go similarity index 63% rename from pkg/ucp/store/inmemory/client.go rename to pkg/ucp/database/inmemory/client.go index 97adc07337..9b9ac141e9 100644 --- a/pkg/ucp/store/inmemory/client.go +++ b/pkg/ucp/database/inmemory/client.go @@ -23,16 +23,16 @@ import ( "strings" "sync" + "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/ucp/database/databaseutil" "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/store" - "github.com/radius-project/radius/pkg/ucp/store/storeutil" "github.com/radius-project/radius/pkg/ucp/util/etag" "golang.org/x/exp/maps" ) -var _ store.StorageClient = (*Client)(nil) +var _ database.Client = (*Client)(nil) -// Client is an in-memory implementation of store.StorageClient. +// Client is an in-memory implementation of database.Client. type Client struct { // mutex is used to synchronize access to the resources map. mutex sync.Mutex @@ -67,7 +67,7 @@ type Client struct { // All fields are compared case-insensitively. type entry struct { // obj stores the object data. - obj store.Object + obj database.Object // rootScope is the root scope of the resource ID. rootScope string @@ -87,23 +87,23 @@ func NewClient() *Client { } } -// Get implements store.StorageClient. -func (c *Client) Get(ctx context.Context, id string, options ...store.GetOptions) (*store.Object, error) { +// Get implements database.Client. +func (c *Client) Get(ctx context.Context, id string, options ...database.GetOptions) (*database.Object, error) { if ctx == nil { - return nil, &store.ErrInvalid{Message: "invalid argument. 'ctx' is required"} + return nil, &database.ErrInvalid{Message: "invalid argument. 'ctx' is required"} } parsed, err := resources.Parse(id) if err != nil { - return nil, &store.ErrInvalid{Message: "invalid argument. 'id' must be a valid resource id"} + return nil, &database.ErrInvalid{Message: "invalid argument. 'id' must be a valid resource id"} } if parsed.IsEmpty() { - return nil, &store.ErrInvalid{Message: "invalid argument. 'id' must not be empty"} + return nil, &database.ErrInvalid{Message: "invalid argument. 'id' must not be empty"} } if parsed.IsResourceCollection() || parsed.IsScopeCollection() { - return nil, &store.ErrInvalid{Message: "invalid argument. 'id' must refer to a named resource, not a collection"} + return nil, &database.ErrInvalid{Message: "invalid argument. 'id' must refer to a named resource, not a collection"} } - converted, err := storeutil.ConvertScopeIDToResourceID(parsed) + converted, err := databaseutil.ConvertScopeIDToResourceID(parsed) if err != nil { return nil, err } @@ -113,7 +113,7 @@ func (c *Client) Get(ctx context.Context, id string, options ...store.GetOptions entry, ok := c.resources[strings.ToLower(converted.String())] if !ok { - return nil, &store.ErrNotFound{ID: id} + return nil, &database.ErrNotFound{ID: id} } // Make a defensive copy so users can't modify the data in the store. @@ -125,23 +125,23 @@ func (c *Client) Get(ctx context.Context, id string, options ...store.GetOptions return copy, nil } -// Delete implements store.StorageClient. -func (c *Client) Delete(ctx context.Context, id string, options ...store.DeleteOptions) error { +// Delete implements database.Client. +func (c *Client) Delete(ctx context.Context, id string, options ...database.DeleteOptions) error { if ctx == nil { - return &store.ErrInvalid{Message: "invalid argument. 'ctx' is required"} + return &database.ErrInvalid{Message: "invalid argument. 'ctx' is required"} } parsed, err := resources.Parse(id) if err != nil { - return &store.ErrInvalid{Message: "invalid argument. 'id' must be a valid resource id"} + return &database.ErrInvalid{Message: "invalid argument. 'id' must be a valid resource id"} } if parsed.IsEmpty() { - return &store.ErrInvalid{Message: "invalid argument. 'id' must not be empty"} + return &database.ErrInvalid{Message: "invalid argument. 'id' must not be empty"} } if parsed.IsResourceCollection() || parsed.IsScopeCollection() { - return &store.ErrInvalid{Message: "invalid argument. 'id' must refer to a named resource, not a collection"} + return &database.ErrInvalid{Message: "invalid argument. 'id' must refer to a named resource, not a collection"} } - converted, err := storeutil.ConvertScopeIDToResourceID(parsed) + converted, err := databaseutil.ConvertScopeIDToResourceID(parsed) if err != nil { return err } @@ -149,15 +149,15 @@ func (c *Client) Delete(ctx context.Context, id string, options ...store.DeleteO c.mutex.Lock() defer c.mutex.Unlock() - config := store.NewDeleteConfig(options...) + config := database.NewDeleteConfig(options...) entry, ok := c.resources[strings.ToLower(converted.String())] if !ok && config.ETag != "" { - return &store.ErrConcurrency{} + return &database.ErrConcurrency{} } else if !ok { - return &store.ErrNotFound{ID: id} + return &database.ErrNotFound{ID: id} } else if config.ETag != "" && config.ETag != entry.obj.ETag { - return &store.ErrConcurrency{} + return &database.ErrConcurrency{} } delete(c.resources, strings.ToLower(converted.String())) @@ -165,40 +165,40 @@ func (c *Client) Delete(ctx context.Context, id string, options ...store.DeleteO return nil } -// Query implements store.StorageClient. -func (c *Client) Query(ctx context.Context, query store.Query, options ...store.QueryOptions) (*store.ObjectQueryResult, error) { +// Query implements database.Client. +func (c *Client) Query(ctx context.Context, query database.Query, options ...database.QueryOptions) (*database.ObjectQueryResult, error) { if ctx == nil { - return nil, &store.ErrInvalid{Message: "invalid argument. 'ctx' is required"} + return nil, &database.ErrInvalid{Message: "invalid argument. 'ctx' is required"} } err := query.Validate() if err != nil { - return nil, &store.ErrInvalid{Message: fmt.Sprintf("invalid argument. Query is invalid: %s", err.Error())} + return nil, &database.ErrInvalid{Message: fmt.Sprintf("invalid argument. Query is invalid: %s", err.Error())} } c.mutex.Lock() defer c.mutex.Unlock() - result := &store.ObjectQueryResult{} + result := &database.ObjectQueryResult{} for _, entry := range c.resources { // Check root scope. - if query.ScopeRecursive && !strings.HasPrefix(entry.rootScope, storeutil.NormalizePart(query.RootScope)) { + if query.ScopeRecursive && !strings.HasPrefix(entry.rootScope, databaseutil.NormalizePart(query.RootScope)) { continue - } else if !query.ScopeRecursive && entry.rootScope != storeutil.NormalizePart(query.RootScope) { + } else if !query.ScopeRecursive && entry.rootScope != databaseutil.NormalizePart(query.RootScope) { continue } // Check resource type. - resourceType, err := storeutil.ConvertScopeTypeToResourceType(query.ResourceType) + resourceType, err := databaseutil.ConvertScopeTypeToResourceType(query.ResourceType) if err != nil { return nil, err } - if entry.resourceType != storeutil.NormalizePart(resourceType) { + if entry.resourceType != databaseutil.NormalizePart(resourceType) { continue } // Check routing scope prefix (optional). - if query.RoutingScopePrefix != "" && !strings.HasPrefix(entry.routingScope, storeutil.NormalizePart(query.RoutingScopePrefix)) { + if query.RoutingScopePrefix != "" && !strings.HasPrefix(entry.routingScope, databaseutil.NormalizePart(query.RoutingScopePrefix)) { continue } @@ -223,21 +223,21 @@ func (c *Client) Query(ctx context.Context, query store.Query, options ...store. return result, nil } -// Save implements store.StorageClient. -func (c *Client) Save(ctx context.Context, obj *store.Object, options ...store.SaveOptions) error { +// Save implements database.Client. +func (c *Client) Save(ctx context.Context, obj *database.Object, options ...database.SaveOptions) error { if ctx == nil { - return &store.ErrInvalid{Message: "invalid argument. 'ctx' is required"} + return &database.ErrInvalid{Message: "invalid argument. 'ctx' is required"} } if obj == nil { - return &store.ErrInvalid{Message: "invalid argument. 'obj' is required"} + return &database.ErrInvalid{Message: "invalid argument. 'obj' is required"} } parsed, err := resources.Parse(obj.ID) if err != nil { - return &store.ErrInvalid{Message: "invalid argument. 'obj.ID' must be a valid resource id"} + return &database.ErrInvalid{Message: "invalid argument. 'obj.ID' must be a valid resource id"} } - converted, err := storeutil.ConvertScopeIDToResourceID(parsed) + converted, err := databaseutil.ConvertScopeIDToResourceID(parsed) if err != nil { return err } @@ -245,18 +245,18 @@ func (c *Client) Save(ctx context.Context, obj *store.Object, options ...store.S c.mutex.Lock() defer c.mutex.Unlock() - config := store.NewSaveConfig(options...) + config := database.NewSaveConfig(options...) entry, ok := c.resources[strings.ToLower(converted.String())] if !ok && config.ETag != "" { - return &store.ErrConcurrency{} + return &database.ErrConcurrency{} } else if ok && config.ETag != "" && config.ETag != entry.obj.ETag { - return &store.ErrConcurrency{} + return &database.ErrConcurrency{} } else if !ok { // New entry, initialize it. - entry.rootScope = storeutil.NormalizePart(converted.RootScope()) - entry.resourceType = storeutil.NormalizePart(converted.Type()) - entry.routingScope = storeutil.NormalizePart(converted.RoutingScope()) + entry.rootScope = databaseutil.NormalizePart(converted.RootScope()) + entry.resourceType = databaseutil.NormalizePart(converted.Type()) + entry.routingScope = databaseutil.NormalizePart(converted.RoutingScope()) } raw, err := json.Marshal(obj.Data) diff --git a/pkg/ucp/store/inmemory/client_test.go b/pkg/ucp/database/inmemory/client_test.go similarity index 100% rename from pkg/ucp/store/inmemory/client_test.go rename to pkg/ucp/database/inmemory/client_test.go diff --git a/pkg/ucp/store/inmemory/doc.go b/pkg/ucp/database/inmemory/doc.go similarity index 100% rename from pkg/ucp/store/inmemory/doc.go rename to pkg/ucp/database/inmemory/doc.go diff --git a/pkg/ucp/store/map.go b/pkg/ucp/database/map.go similarity index 98% rename from pkg/ucp/store/map.go rename to pkg/ucp/database/map.go index ab236e8f3f..b0578ed6e8 100644 --- a/pkg/ucp/store/map.go +++ b/pkg/ucp/database/map.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package store +package database import ( "reflect" diff --git a/pkg/ucp/store/map_test.go b/pkg/ucp/database/map_test.go similarity index 99% rename from pkg/ucp/store/map_test.go rename to pkg/ucp/database/map_test.go index fd99198bcd..b1c1918786 100644 --- a/pkg/ucp/store/map_test.go +++ b/pkg/ucp/database/map_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package store +package database import ( "encoding/json" diff --git a/pkg/ucp/database/mock_client.go b/pkg/ucp/database/mock_client.go new file mode 100644 index 0000000000..5a3a7b3939 --- /dev/null +++ b/pkg/ucp/database/mock_client.go @@ -0,0 +1,214 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/radius-project/radius/pkg/ucp/database (interfaces: Client) +// +// Generated by this command: +// +// mockgen -typed -destination=./mock_client.go -package=database -self_package github.com/radius-project/radius/pkg/ucp/database github.com/radius-project/radius/pkg/ucp/database Client +// + +// Package database is a generated GoMock package. +package database + +import ( + context "context" + reflect "reflect" + + gomock "go.uber.org/mock/gomock" +) + +// MockClient is a mock of Client interface. +type MockClient struct { + ctrl *gomock.Controller + recorder *MockClientMockRecorder +} + +// MockClientMockRecorder is the mock recorder for MockClient. +type MockClientMockRecorder struct { + mock *MockClient +} + +// NewMockClient creates a new mock instance. +func NewMockClient(ctrl *gomock.Controller) *MockClient { + mock := &MockClient{ctrl: ctrl} + mock.recorder = &MockClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockClient) EXPECT() *MockClientMockRecorder { + return m.recorder +} + +// Delete mocks base method. +func (m *MockClient) Delete(arg0 context.Context, arg1 string, arg2 ...DeleteOptions) error { + m.ctrl.T.Helper() + varargs := []any{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Delete", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// Delete indicates an expected call of Delete. +func (mr *MockClientMockRecorder) Delete(arg0, arg1 any, arg2 ...any) *MockClientDeleteCall { + mr.mock.ctrl.T.Helper() + varargs := append([]any{arg0, arg1}, arg2...) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockClient)(nil).Delete), varargs...) + return &MockClientDeleteCall{Call: call} +} + +// MockClientDeleteCall wrap *gomock.Call +type MockClientDeleteCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockClientDeleteCall) Return(arg0 error) *MockClientDeleteCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockClientDeleteCall) Do(f func(context.Context, string, ...DeleteOptions) error) *MockClientDeleteCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockClientDeleteCall) DoAndReturn(f func(context.Context, string, ...DeleteOptions) error) *MockClientDeleteCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// Get mocks base method. +func (m *MockClient) Get(arg0 context.Context, arg1 string, arg2 ...GetOptions) (*Object, error) { + m.ctrl.T.Helper() + varargs := []any{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Get", varargs...) + ret0, _ := ret[0].(*Object) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockClientMockRecorder) Get(arg0, arg1 any, arg2 ...any) *MockClientGetCall { + mr.mock.ctrl.T.Helper() + varargs := append([]any{arg0, arg1}, arg2...) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockClient)(nil).Get), varargs...) + return &MockClientGetCall{Call: call} +} + +// MockClientGetCall wrap *gomock.Call +type MockClientGetCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockClientGetCall) Return(arg0 *Object, arg1 error) *MockClientGetCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockClientGetCall) Do(f func(context.Context, string, ...GetOptions) (*Object, error)) *MockClientGetCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockClientGetCall) DoAndReturn(f func(context.Context, string, ...GetOptions) (*Object, error)) *MockClientGetCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// Query mocks base method. +func (m *MockClient) Query(arg0 context.Context, arg1 Query, arg2 ...QueryOptions) (*ObjectQueryResult, error) { + m.ctrl.T.Helper() + varargs := []any{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Query", varargs...) + ret0, _ := ret[0].(*ObjectQueryResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Query indicates an expected call of Query. +func (mr *MockClientMockRecorder) Query(arg0, arg1 any, arg2 ...any) *MockClientQueryCall { + mr.mock.ctrl.T.Helper() + varargs := append([]any{arg0, arg1}, arg2...) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Query", reflect.TypeOf((*MockClient)(nil).Query), varargs...) + return &MockClientQueryCall{Call: call} +} + +// MockClientQueryCall wrap *gomock.Call +type MockClientQueryCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockClientQueryCall) Return(arg0 *ObjectQueryResult, arg1 error) *MockClientQueryCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockClientQueryCall) Do(f func(context.Context, Query, ...QueryOptions) (*ObjectQueryResult, error)) *MockClientQueryCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockClientQueryCall) DoAndReturn(f func(context.Context, Query, ...QueryOptions) (*ObjectQueryResult, error)) *MockClientQueryCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// Save mocks base method. +func (m *MockClient) Save(arg0 context.Context, arg1 *Object, arg2 ...SaveOptions) error { + m.ctrl.T.Helper() + varargs := []any{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Save", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// Save indicates an expected call of Save. +func (mr *MockClientMockRecorder) Save(arg0, arg1 any, arg2 ...any) *MockClientSaveCall { + mr.mock.ctrl.T.Helper() + varargs := append([]any{arg0, arg1}, arg2...) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Save", reflect.TypeOf((*MockClient)(nil).Save), varargs...) + return &MockClientSaveCall{Call: call} +} + +// MockClientSaveCall wrap *gomock.Call +type MockClientSaveCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockClientSaveCall) Return(arg0 error) *MockClientSaveCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockClientSaveCall) Do(f func(context.Context, *Object, ...SaveOptions) error) *MockClientSaveCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockClientSaveCall) DoAndReturn(f func(context.Context, *Object, ...SaveOptions) error) *MockClientSaveCall { + c.Call = c.Call.DoAndReturn(f) + return c +} diff --git a/pkg/ucp/store/object.go b/pkg/ucp/database/object.go similarity index 87% rename from pkg/ucp/store/object.go rename to pkg/ucp/database/object.go index e006ecfe56..2b9ea08dd5 100644 --- a/pkg/ucp/store/object.go +++ b/pkg/ucp/database/object.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package store +package database import ( "context" @@ -70,11 +70,11 @@ func (o *Object) As(out any) error { return DecodeMap(o.Data, out) } -// GetResource gets the resource data from StorageClient for id. -func GetResource[T any](ctx context.Context, client StorageClient, id string) (*T, error) { +// GetResource gets the resource data for the provided resource id using the provided client. +func GetResource[T any](ctx context.Context, databaseClient Client, id string) (*T, error) { var out T - obj, err := client.Get(ctx, id) + obj, err := databaseClient.Get(ctx, id) if err != nil { return nil, err } diff --git a/pkg/ucp/store/options.go b/pkg/ucp/database/options.go similarity index 77% rename from pkg/ucp/store/options.go rename to pkg/ucp/database/options.go index 4d80eba2a0..35ac3072ae 100644 --- a/pkg/ucp/store/options.go +++ b/pkg/ucp/database/options.go @@ -14,12 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -package store +package database type ( // QueryOptions applies an option to Query(). QueryOptions interface { - ApplyQueryOption(StoreConfig) StoreConfig + ApplyQueryOption(DatabaseOptions) DatabaseOptions // A private method to prevent users implementing the // interface and so future additions to it will not @@ -37,7 +37,7 @@ type ( // DeleteOptions applies an option to Delete(). DeleteOptions interface { - ApplyDeleteOption(StoreConfig) StoreConfig + ApplyDeleteOption(DatabaseOptions) DatabaseOptions // A private method to prevent users implementing the // interface and so future additions to it will not @@ -47,7 +47,7 @@ type ( // SaveOptions applies an option to Save(). SaveOptions interface { - ApplySaveOption(StoreConfig) StoreConfig + ApplySaveOption(DatabaseOptions) DatabaseOptions // A private method to prevent users implementing the // interface and so future additions to it will not @@ -62,8 +62,8 @@ type ( } ) -// Store Config represents the configurations of storageclient APIs. -type StoreConfig struct { +// DatabaseOptions represents the configurations of the underlying database APIs. +type DatabaseOptions struct { // PaginationToken represents pagination token such as continuation token. PaginationToken string @@ -76,11 +76,11 @@ type StoreConfig struct { // Query Options type queryOptions struct { - fn func(StoreConfig) StoreConfig + fn func(DatabaseOptions) DatabaseOptions } // ApplyQueryOption applies a function to the StoreConfig to modify it. -func (q *queryOptions) ApplyQueryOption(cfg StoreConfig) StoreConfig { +func (q *queryOptions) ApplyQueryOption(cfg DatabaseOptions) DatabaseOptions { return q.fn(cfg) } @@ -89,7 +89,7 @@ func (q queryOptions) private() {} // WithPaginationToken sets pagination token for Query(). func WithPaginationToken(token string) QueryOptions { return &queryOptions{ - fn: func(cfg StoreConfig) StoreConfig { + fn: func(cfg DatabaseOptions) DatabaseOptions { cfg.PaginationToken = token return cfg }, @@ -99,7 +99,7 @@ func WithPaginationToken(token string) QueryOptions { // WithMaxQueryItemCount creates a QueryOptions instance that sets the maximum number of items in query result. func WithMaxQueryItemCount(maxcnt int) QueryOptions { return &queryOptions{ - fn: func(cfg StoreConfig) StoreConfig { + fn: func(cfg DatabaseOptions) DatabaseOptions { cfg.MaxQueryItemCount = maxcnt return cfg }, @@ -108,19 +108,19 @@ func WithMaxQueryItemCount(maxcnt int) QueryOptions { // MutatingOptions type mutatingOptions struct { - fn func(StoreConfig) StoreConfig + fn func(DatabaseOptions) DatabaseOptions } var _ DeleteOptions = (*mutatingOptions)(nil) var _ SaveOptions = (*mutatingOptions)(nil) // ApplyDeleteOption applies the delete option to the given StoreConfig and returns the modified StoreConfig. -func (s *mutatingOptions) ApplyDeleteOption(cfg StoreConfig) StoreConfig { +func (s *mutatingOptions) ApplyDeleteOption(cfg DatabaseOptions) DatabaseOptions { return s.fn(cfg) } // ApplySaveOption applies the save option to the given StoreConfig and returns the modified StoreConfig. -func (s *mutatingOptions) ApplySaveOption(cfg StoreConfig) StoreConfig { +func (s *mutatingOptions) ApplySaveOption(cfg DatabaseOptions) DatabaseOptions { return s.fn(cfg) } @@ -128,13 +128,13 @@ func (s mutatingOptions) private() {} // SaveOptions type saveOptions struct { - fn func(StoreConfig) StoreConfig + fn func(DatabaseOptions) DatabaseOptions } var _ SaveOptions = (*saveOptions)(nil) // ApplySaveOption applies a save option to a StoreConfig. -func (s *saveOptions) ApplySaveOption(cfg StoreConfig) StoreConfig { +func (s *saveOptions) ApplySaveOption(cfg DatabaseOptions) DatabaseOptions { return s.fn(cfg) } @@ -143,7 +143,7 @@ func (s saveOptions) private() {} // WithETag sets the ETag field in the StoreConfig struct. func WithETag(etag ETag) MutatingOptions { return &mutatingOptions{ - fn: func(cfg StoreConfig) StoreConfig { + fn: func(cfg DatabaseOptions) DatabaseOptions { cfg.ETag = etag return cfg }, @@ -151,8 +151,8 @@ func WithETag(etag ETag) MutatingOptions { } // NewQueryConfig applies a set of QueryOptions to a StoreConfig and returns the modified StoreConfig for Query(). -func NewQueryConfig(opts ...QueryOptions) StoreConfig { - cfg := StoreConfig{} +func NewQueryConfig(opts ...QueryOptions) DatabaseOptions { + cfg := DatabaseOptions{} for _, opt := range opts { cfg = opt.ApplyQueryOption(cfg) } @@ -160,8 +160,8 @@ func NewQueryConfig(opts ...QueryOptions) StoreConfig { } // NewDeleteConfig applies the given DeleteOptions to a StoreConfig and returns the resulting StoreConfig for Delete(). -func NewDeleteConfig(opts ...DeleteOptions) StoreConfig { - cfg := StoreConfig{} +func NewDeleteConfig(opts ...DeleteOptions) DatabaseOptions { + cfg := DatabaseOptions{} for _, opt := range opts { cfg = opt.ApplyDeleteOption(cfg) } @@ -169,8 +169,8 @@ func NewDeleteConfig(opts ...DeleteOptions) StoreConfig { } // NewSaveConfig applies a set of SaveOptions to a StoreConfig and returns the modified StoreConfig for Save(). -func NewSaveConfig(opts ...SaveOptions) StoreConfig { - cfg := StoreConfig{} +func NewSaveConfig(opts ...SaveOptions) DatabaseOptions { + cfg := DatabaseOptions{} for _, opt := range opts { cfg = opt.ApplySaveOption(cfg) } diff --git a/pkg/ucp/store/postgres/postgresclient.go b/pkg/ucp/database/postgres/postgresclient.go similarity index 70% rename from pkg/ucp/store/postgres/postgresclient.go rename to pkg/ucp/database/postgres/postgresclient.go index bdab94f031..d5dbb681db 100644 --- a/pkg/ucp/store/postgres/postgresclient.go +++ b/pkg/ucp/database/postgres/postgresclient.go @@ -27,9 +27,9 @@ import ( "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgconn" "github.com/radius-project/radius/pkg/to" + "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/ucp/database/databaseutil" "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/store" - "github.com/radius-project/radius/pkg/ucp/store/storeutil" "github.com/radius-project/radius/pkg/ucp/util/etag" ) @@ -50,36 +50,36 @@ func NewPostgresClient(api PostgresAPI) *PostgresClient { return &PostgresClient{api: api} } -var _ store.StorageClient = (*PostgresClient)(nil) +var _ database.Client = (*PostgresClient)(nil) -// PostgresClient is a storage client that uses Postgres as the backend. +// PostgresClient is a database client that uses Postgres as the backend. type PostgresClient struct { api PostgresAPI } -// Delete implements store.StorageClient. -func (p *PostgresClient) Delete(ctx context.Context, id string, options ...store.DeleteOptions) error { +// Delete implements database.Client. +func (p *PostgresClient) Delete(ctx context.Context, id string, options ...database.DeleteOptions) error { if ctx == nil { - return &store.ErrInvalid{Message: "invalid argument. 'ctx' is required"} + return &database.ErrInvalid{Message: "invalid argument. 'ctx' is required"} } parsed, err := resources.Parse(id) if err != nil { - return &store.ErrInvalid{Message: "invalid argument. 'id' must be a valid resource id"} + return &database.ErrInvalid{Message: "invalid argument. 'id' must be a valid resource id"} } if parsed.IsEmpty() { - return &store.ErrInvalid{Message: "invalid argument. 'id' must not be empty"} + return &database.ErrInvalid{Message: "invalid argument. 'id' must not be empty"} } if parsed.IsResourceCollection() || parsed.IsScopeCollection() { - return &store.ErrInvalid{Message: "invalid argument. 'id' must refer to a named resource, not a collection"} + return &database.ErrInvalid{Message: "invalid argument. 'id' must refer to a named resource, not a collection"} } - converted, err := storeutil.ConvertScopeIDToResourceID(parsed) + converted, err := databaseutil.ConvertScopeIDToResourceID(parsed) if err != nil { return err } - config := store.NewDeleteConfig(options...) + config := database.NewDeleteConfig(options...) var etag *string if config.ETag != "" { etag = &config.ETag @@ -101,7 +101,7 @@ CASE ELSE 'ErrNotFound' END AS result;` - args := []any{storeutil.NormalizePart(converted.String())} + args := []any{databaseutil.NormalizePart(converted.String())} if config.ETag != "" { // NOTE: we want to report ErrConcurrency for all failure cases here. This is what the tests do. @@ -118,7 +118,7 @@ CASE ELSE 'ErrConcurrency' END AS result;` - args = []any{storeutil.NormalizePart(converted.String()), etag} + args = []any{databaseutil.NormalizePart(converted.String()), etag} } result := "" @@ -126,43 +126,43 @@ END AS result;` if err != nil { return err } else if result == "ErrNotFound" { - return &store.ErrNotFound{ID: id} + return &database.ErrNotFound{ID: id} } else if result == "ErrConcurrency" { - return &store.ErrConcurrency{} + return &database.ErrConcurrency{} } return nil } -// Get implements store.StorageClient. -func (p *PostgresClient) Get(ctx context.Context, id string, options ...store.GetOptions) (*store.Object, error) { +// Get implements database.Client. +func (p *PostgresClient) Get(ctx context.Context, id string, options ...database.GetOptions) (*database.Object, error) { if ctx == nil { - return nil, &store.ErrInvalid{Message: "invalid argument. 'ctx' is required"} + return nil, &database.ErrInvalid{Message: "invalid argument. 'ctx' is required"} } parsed, err := resources.Parse(id) if err != nil { - return nil, &store.ErrInvalid{Message: "invalid argument. 'id' must be a valid resource id"} + return nil, &database.ErrInvalid{Message: "invalid argument. 'id' must be a valid resource id"} } if parsed.IsEmpty() { - return nil, &store.ErrInvalid{Message: "invalid argument. 'id' must not be empty"} + return nil, &database.ErrInvalid{Message: "invalid argument. 'id' must not be empty"} } if parsed.IsResourceCollection() || parsed.IsScopeCollection() { - return nil, &store.ErrInvalid{Message: "invalid argument. 'id' must refer to a named resource, not a collection"} + return nil, &database.ErrInvalid{Message: "invalid argument. 'id' must refer to a named resource, not a collection"} } - converted, err := storeutil.ConvertScopeIDToResourceID(parsed) + converted, err := databaseutil.ConvertScopeIDToResourceID(parsed) if err != nil { return nil, err } - obj := store.Object{} + obj := database.Object{} err = p.api.QueryRow( ctx, "SELECT original_id, etag, resource_data FROM resources WHERE id = $1", - storeutil.NormalizePart(converted.String())).Scan(&obj.ID, &obj.ETag, &obj.Data) + databaseutil.NormalizePart(converted.String())).Scan(&obj.ID, &obj.ETag, &obj.Data) if errors.Is(err, pgx.ErrNoRows) { - return nil, &store.ErrNotFound{ID: id} + return nil, &database.ErrNotFound{ID: id} } else if err != nil { return nil, err } @@ -170,41 +170,41 @@ func (p *PostgresClient) Get(ctx context.Context, id string, options ...store.Ge return &obj, nil } -// Query implements store.StorageClient. -func (p *PostgresClient) Query(ctx context.Context, query store.Query, options ...store.QueryOptions) (*store.ObjectQueryResult, error) { +// Query implements database.Client. +func (p *PostgresClient) Query(ctx context.Context, query database.Query, options ...database.QueryOptions) (*database.ObjectQueryResult, error) { if ctx == nil { - return nil, &store.ErrInvalid{Message: "invalid argument. 'ctx' is required"} + return nil, &database.ErrInvalid{Message: "invalid argument. 'ctx' is required"} } err := query.Validate() if err != nil { - return nil, &store.ErrInvalid{Message: fmt.Sprintf("invalid argument. Query is invalid: %s", err.Error())} + return nil, &database.ErrInvalid{Message: fmt.Sprintf("invalid argument. Query is invalid: %s", err.Error())} } - config := store.NewQueryConfig(options...) + config := database.NewQueryConfig(options...) // For a scope query, we need to perform the same normalization as we do for other operations on scopes. - resourceType := storeutil.NormalizePart(query.ResourceType) + resourceType := databaseutil.NormalizePart(query.ResourceType) if query.IsScopeQuery { var err error - resourceType, err = storeutil.ConvertScopeTypeToResourceType(query.ResourceType) + resourceType, err = databaseutil.ConvertScopeTypeToResourceType(query.ResourceType) if err != nil { return nil, err } - resourceType = storeutil.NormalizePart(resourceType) + resourceType = databaseutil.NormalizePart(resourceType) } var routingScopePrefixFilter *string if query.RoutingScopePrefix != "" { - routingScopePrefixFilter = to.Ptr(storeutil.NormalizePart(query.RoutingScopePrefix)) + routingScopePrefixFilter = to.Ptr(databaseutil.NormalizePart(query.RoutingScopePrefix)) } var timestampFilter *string if config.PaginationToken != "" { ts, err := p.parsePaginationToken(config.PaginationToken) if err != nil { - return nil, &store.ErrInvalid{Message: "invalid argument. 'query.PaginationToken' is invalid."} + return nil, &database.ErrInvalid{Message: "invalid argument. 'query.PaginationToken' is invalid."} } timestampFilter = &ts } @@ -217,7 +217,7 @@ func (p *PostgresClient) Query(ctx context.Context, query store.Query, options . // For a scope query, we need to perform the same normalization as we do for other operations on scopes. if query.IsScopeQuery { var err error - query.ResourceType, err = storeutil.ConvertScopeTypeToResourceType(query.ResourceType) + query.ResourceType, err = databaseutil.ConvertScopeTypeToResourceType(query.ResourceType) if err != nil { return nil, err } @@ -239,7 +239,7 @@ LIMIT $6` args := []any{ // If ScopeRecursive is false, the RootScope must match exactly. // If ScopeRecursive is true, the RootScope must be a prefix of the stored RootScope. - storeutil.NormalizePart(query.RootScope), + databaseutil.NormalizePart(query.RootScope), query.ScopeRecursive, resourceType, routingScopePrefixFilter, // RoutingScopePrefix is optional and always treated as as prefix. @@ -256,9 +256,9 @@ LIMIT $6` // Capture the last timestamp so we can use it for pagination. var timestamp *time.Time - result := store.ObjectQueryResult{} + result := database.ObjectQueryResult{} for rows.Next() { - obj := store.Object{} + obj := database.Object{} err := rows.Scan(&obj.ID, &obj.ETag, &obj.Data, ×tamp) if err != nil { return nil, err @@ -300,32 +300,32 @@ LIMIT $6` return &result, nil } -// Save implements store.StorageClient. -func (p *PostgresClient) Save(ctx context.Context, obj *store.Object, options ...store.SaveOptions) error { +// Save implements database.Client. +func (p *PostgresClient) Save(ctx context.Context, obj *database.Object, options ...database.SaveOptions) error { if ctx == nil { - return &store.ErrInvalid{Message: "invalid argument. 'ctx' is required"} + return &database.ErrInvalid{Message: "invalid argument. 'ctx' is required"} } if obj == nil { - return &store.ErrInvalid{Message: "invalid argument. 'obj' is required"} + return &database.ErrInvalid{Message: "invalid argument. 'obj' is required"} } parsed, err := resources.Parse(obj.ID) if err != nil { - return &store.ErrInvalid{Message: "invalid argument. 'obj.ID' must be a valid resource id"} + return &database.ErrInvalid{Message: "invalid argument. 'obj.ID' must be a valid resource id"} } if parsed.IsEmpty() { - return &store.ErrInvalid{Message: "invalid argument. 'obj.ID' must not be empty"} + return &database.ErrInvalid{Message: "invalid argument. 'obj.ID' must not be empty"} } if parsed.IsResourceCollection() || parsed.IsScopeCollection() { - return &store.ErrInvalid{Message: "invalid argument. 'obj.ID' must refer to a named resource, not a collection"} + return &database.ErrInvalid{Message: "invalid argument. 'obj.ID' must refer to a named resource, not a collection"} } - converted, err := storeutil.ConvertScopeIDToResourceID(parsed) + converted, err := databaseutil.ConvertScopeIDToResourceID(parsed) if err != nil { return err } - config := store.NewSaveConfig(options...) + config := database.NewSaveConfig(options...) // Compute ETag for the current state of the object. raw, err := json.Marshal(obj.Data) @@ -356,11 +356,11 @@ CASE END AS result;` args := []any{ - storeutil.NormalizePart(converted.String()), + databaseutil.NormalizePart(converted.String()), obj.ID, // MUST NOT BE NORMALIZED. Preserve the original casing and format. - storeutil.NormalizePart(converted.Type()), - storeutil.NormalizePart(converted.RootScope()), - storeutil.NormalizePart(converted.RoutingScope()), + databaseutil.NormalizePart(converted.Type()), + databaseutil.NormalizePart(converted.RootScope()), + databaseutil.NormalizePart(converted.RoutingScope()), obj.ETag, obj.Data, } @@ -381,7 +381,7 @@ CASE ELSE 'ErrConcurrency' END AS result;` - args = []any{storeutil.NormalizePart(converted.String()), obj.Data, config.ETag} + args = []any{databaseutil.NormalizePart(converted.String()), obj.Data, config.ETag} } result := "" @@ -389,9 +389,9 @@ END AS result;` if err != nil { return err } else if result == "ErrNotFound" { - return &store.ErrNotFound{ID: obj.ID} + return &database.ErrNotFound{ID: obj.ID} } else if result == "ErrConcurrency" { - return &store.ErrConcurrency{} + return &database.ErrConcurrency{} } return nil diff --git a/pkg/ucp/store/postgres/postgresclient_test.go b/pkg/ucp/database/postgres/postgresclient_test.go similarity index 100% rename from pkg/ucp/store/postgres/postgresclient_test.go rename to pkg/ucp/database/postgres/postgresclient_test.go diff --git a/pkg/ucp/store/resources.go b/pkg/ucp/database/resources.go similarity index 97% rename from pkg/ucp/store/resources.go rename to pkg/ucp/database/resources.go index ecefd07a68..f1dbc3be89 100644 --- a/pkg/ucp/store/resources.go +++ b/pkg/ucp/database/resources.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package store +package database const ( UCPScopePrefix = "scope" diff --git a/pkg/ucp/dataprovider/factory.go b/pkg/ucp/databaseprovider/factory.go similarity index 74% rename from pkg/ucp/dataprovider/factory.go rename to pkg/ucp/databaseprovider/factory.go index e879339d2b..4f02132430 100644 --- a/pkg/ucp/dataprovider/factory.go +++ b/pkg/ucp/databaseprovider/factory.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package dataprovider +package databaseprovider import ( context "context" @@ -24,28 +24,28 @@ import ( "github.com/jackc/pgx/v5/pgxpool" "github.com/radius-project/radius/pkg/kubeutil" - store "github.com/radius-project/radius/pkg/ucp/store" - "github.com/radius-project/radius/pkg/ucp/store/apiserverstore" - ucpv1alpha1 "github.com/radius-project/radius/pkg/ucp/store/apiserverstore/api/ucp.dev/v1alpha1" - "github.com/radius-project/radius/pkg/ucp/store/etcdstore" - "github.com/radius-project/radius/pkg/ucp/store/inmemory" - "github.com/radius-project/radius/pkg/ucp/store/postgres" + store "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/ucp/database/apiserverstore" + ucpv1alpha1 "github.com/radius-project/radius/pkg/ucp/database/apiserverstore/api/ucp.dev/v1alpha1" + "github.com/radius-project/radius/pkg/ucp/database/etcdstore" + "github.com/radius-project/radius/pkg/ucp/database/inmemory" + "github.com/radius-project/radius/pkg/ucp/database/postgres" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/rest" runtimeclient "sigs.k8s.io/controller-runtime/pkg/client" ) -type storageFactoryFunc func(ctx context.Context, options StorageProviderOptions) (store.StorageClient, error) +type databaseClientFactoryFunc func(ctx context.Context, options Options) (store.Client, error) -var storageClientFactory = map[StorageProviderType]storageFactoryFunc{ +var databaseClientFactory = map[DatabaseProviderType]databaseClientFactoryFunc{ TypeAPIServer: initAPIServerClient, TypeETCD: InitETCDClient, TypeInMemory: initInMemoryClient, TypePostgreSQL: initPostgreSQLClient, } -func initAPIServerClient(ctx context.Context, opt StorageProviderOptions) (store.StorageClient, error) { +func initAPIServerClient(ctx context.Context, opt Options) (store.Client, error) { if opt.APIServer.Namespace == "" { return nil, errors.New("failed to initialize APIServer client: namespace is required") } @@ -81,9 +81,9 @@ func initAPIServerClient(ctx context.Context, opt StorageProviderOptions) (store return client, nil } -// InitETCDClient checks if the ETCD client is in memory and if the client is not nil, then it initializes the storage +// InitETCDClient checks if the ETCD client is in memory and if the client is not nil, then it initializes the database // client and returns an ETCDClient. If either of these conditions are not met, an error is returned. -func InitETCDClient(ctx context.Context, opt StorageProviderOptions) (store.StorageClient, error) { +func InitETCDClient(ctx context.Context, opt Options) (store.Client, error) { if !opt.ETCD.InMemory { return nil, errors.New("failed to initialize etcd client: inmemory is the only supported mode for now") } @@ -91,7 +91,7 @@ func InitETCDClient(ctx context.Context, opt StorageProviderOptions) (store.Stor return nil, errors.New("failed to initialize etcd client: ETCDOptions.Client is nil, this is a bug") } - // Initialize the storage client once the storage service has started + // Initialize the database client once the etcd service has started client, err := opt.ETCD.Client.Get(ctx) if err != nil { return nil, fmt.Errorf("failed to initialize etcd client: %w", err) @@ -102,12 +102,12 @@ func InitETCDClient(ctx context.Context, opt StorageProviderOptions) (store.Stor } // initInMemoryClient creates a new in-memory store client. -func initInMemoryClient(ctx context.Context, opt StorageProviderOptions) (store.StorageClient, error) { +func initInMemoryClient(ctx context.Context, opt Options) (store.Client, error) { return inmemory.NewClient(), nil } // initPostgreSQLClient creates a new PostgreSQL store client. -func initPostgreSQLClient(ctx context.Context, opt StorageProviderOptions) (store.StorageClient, error) { +func initPostgreSQLClient(ctx context.Context, opt Options) (store.Client, error) { if opt.PostgreSQL.URL == "" { return nil, errors.New("failed to initialize PostgreSQL client: URL is required") } diff --git a/pkg/ucp/dataprovider/options.go b/pkg/ucp/databaseprovider/options.go similarity index 81% rename from pkg/ucp/dataprovider/options.go rename to pkg/ucp/databaseprovider/options.go index 95f99d323b..249e1b6447 100644 --- a/pkg/ucp/dataprovider/options.go +++ b/pkg/ucp/databaseprovider/options.go @@ -14,24 +14,21 @@ See the License for the specific language governing permissions and limitations under the License. */ -package dataprovider +package databaseprovider import ( "github.com/radius-project/radius/pkg/ucp/hosting" etcdclient "go.etcd.io/etcd/client/v3" ) -// StorageProviderOptions represents the data storage provider options. -type StorageProviderOptions struct { - // Provider configures the storage provider. - Provider StorageProviderType `yaml:"provider"` +// Options represents the database provider options. +type Options struct { + // Provider configures the database provider. + Provider DatabaseProviderType `yaml:"provider"` // APIServer configures options for the Kubernetes APIServer store. Will be ignored if another store is configured. APIServer APIServerOptions `yaml:"apiserver,omitempty"` - // CosmosDB configures options for the CosmosDB store. Will be ignored if another store is configured. - CosmosDB CosmosDBOptions `yaml:"cosmosdb,omitempty"` - // ETCD configures options for the etcd store. Will be ignored if another store is configured. ETCD ETCDOptions `yaml:"etcd,omitempty"` @@ -52,14 +49,6 @@ type APIServerOptions struct { Namespace string `yaml:"namespace"` } -// CosmosDBOptions represents cosmosdb options for data storage provider. -type CosmosDBOptions struct { - Url string `yaml:"url"` - Database string `yaml:"database"` - MasterKey string `yaml:"masterKey"` - CollectionThroughput int `yaml:"collectionThroughput,omitempty"` -} - // ETCDOptions represents options for the configuring the etcd store. type ETCDOptions struct { // InMemory configures the etcd store to run in-memory with the resource provider. This is not suitable for production use. diff --git a/pkg/ucp/dataprovider/storageprovider.go b/pkg/ucp/databaseprovider/storageprovider.go similarity index 51% rename from pkg/ucp/dataprovider/storageprovider.go rename to pkg/ucp/databaseprovider/storageprovider.go index 0fb89234b7..2891515aaa 100644 --- a/pkg/ucp/dataprovider/storageprovider.go +++ b/pkg/ucp/databaseprovider/storageprovider.go @@ -14,30 +14,30 @@ See the License for the specific language governing permissions and limitations under the License. */ -package dataprovider +package databaseprovider import ( "context" "fmt" "sync" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/database" ) -// DataStorageProvider acts as a factory for storage clients. +// DatabaseProvider acts as a factory for database clients. // // Do not use construct this directly: // -// - Use DataStorageProviderFromOptions instead for production use. -// - Use DataStorageProviderFromMemory or DataStorageProviderFromClient for testing. -type DataStorageProvider struct { - // options configures the settings of the storage provider. - options StorageProviderOptions +// - Use FromOptions instead for production use. +// - Use FromMemory or FromClient for testing. +type DatabaseProvider struct { + // options configures the settings of the database provider. + options Options - // factory is factory function to create a new storage client. Can be overridden for testing. - factory storageFactoryFunc + // factory is factory function to create a new database client. Can be overridden for testing. + factory databaseClientFactoryFunc - // init is used to guarantee single-initialization of the storage provider. + // init is used to guarantee single-initialization of the database provider. init sync.RWMutex // result is result of invoking the factory (cached). @@ -45,34 +45,34 @@ type DataStorageProvider struct { } type result struct { - client store.StorageClient + client database.Client err error } -// DataStorageProviderFromOptions creates a new instance of the DataStorageProvider struct with the given options. +// FromOptions creates a new instance of the DatabaseProvider struct with the given options. // -// This will used the known factory functions to instantiate the storage client. -func DataStorageProviderFromOptions(options StorageProviderOptions) *DataStorageProvider { - return &DataStorageProvider{options: options} +// This will used the known factory functions to instantiate the database client. +func FromOptions(options Options) *DatabaseProvider { + return &DatabaseProvider{options: options} } -// DataStorageProviderFromMemory creates a new instance of the DataStorageProvider struct using the in-memory client. +// FromMemory creates a new instance of the DatabaseProvider struct using the in-memory client. // -// This will use the ephemeral in-memory storage client. -func DataStorageProviderFromMemory() *DataStorageProvider { - return &DataStorageProvider{options: StorageProviderOptions{Provider: TypeInMemory}} +// This will use the ephemeral in-memory database client. +func FromMemory() *DatabaseProvider { + return &DatabaseProvider{options: Options{Provider: TypeInMemory}} } -// DataStorageProviderFromClient creates a new instance of the DataStorageProvider struct with the given client. +// FromClient creates a new instance of the DatabaseProvider struct with the given client. // // This will always return the given client and will not attempt to create a new one. This can be used for testing // with mocks. -func DataStorageProviderFromClient(client store.StorageClient) *DataStorageProvider { - return &DataStorageProvider{result: result{client: client}} +func FromClient(client database.Client) *DatabaseProvider { + return &DatabaseProvider{result: result{client: client}} } -// GetStorageClient returns a storage client for the given resource type. -func (p *DataStorageProvider) GetClient(ctx context.Context) (store.StorageClient, error) { +// GetClient returns a database client for the given resource type. +func (p *DatabaseProvider) GetClient(ctx context.Context) (database.Client, error) { // Guarantee single initialization. p.init.RLock() result := p.result @@ -94,7 +94,7 @@ func (p *DataStorageProvider) GetClient(ctx context.Context) (store.StorageClien return result.client, nil } -func (p *DataStorageProvider) initialize(ctx context.Context) result { +func (p *DatabaseProvider) initialize(ctx context.Context) result { p.init.Lock() defer p.init.Unlock() @@ -109,13 +109,13 @@ func (p *DataStorageProvider) initialize(ctx context.Context) result { return p.result } - // If we get here we have the exclusive lock and need to initialize the storage client. + // If we get here we have the exclusive lock and need to initialize the database client. factory := p.factory if factory == nil { - fn, ok := storageClientFactory[p.options.Provider] + fn, ok := databaseClientFactory[p.options.Provider] if !ok { - p.result = result{nil, fmt.Errorf("unsupported storage provider: %s", p.options.Provider)} + p.result = result{nil, fmt.Errorf("unsupported database provider: %s", p.options.Provider)} return p.result } @@ -124,10 +124,10 @@ func (p *DataStorageProvider) initialize(ctx context.Context) result { client, err := factory(ctx, p.options) if err != nil { - p.result = result{nil, fmt.Errorf("failed to initialize storage client: %w", err)} + p.result = result{nil, fmt.Errorf("failed to initialize database client: %w", err)} return p.result } else if client == nil { - p.result = result{nil, fmt.Errorf("failed to initialize storage client: provider returned nil")} + p.result = result{nil, fmt.Errorf("failed to initialize database client: provider returned nil")} return p.result } diff --git a/pkg/ucp/dataprovider/storageprovider_test.go b/pkg/ucp/databaseprovider/storageprovider_test.go similarity index 63% rename from pkg/ucp/dataprovider/storageprovider_test.go rename to pkg/ucp/databaseprovider/storageprovider_test.go index 984c73597f..f6a0eb9285 100644 --- a/pkg/ucp/dataprovider/storageprovider_test.go +++ b/pkg/ucp/databaseprovider/storageprovider_test.go @@ -14,20 +14,20 @@ See the License for the specific language governing permissions and limitations under the License. */ -package dataprovider +package databaseprovider import ( "context" "errors" "testing" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/stretchr/testify/require" ) -func Test_DataStorageProviderFromOptions(t *testing.T) { - options := StorageProviderOptions{Provider: TypeInMemory} - provider := DataStorageProviderFromOptions(options) +func Test_FromOptions(t *testing.T) { + options := Options{Provider: TypeInMemory} + provider := FromOptions(options) require.NotNil(t, provider) require.Equal(t, options, provider.options) @@ -37,8 +37,8 @@ func Test_DataStorageProviderFromOptions(t *testing.T) { require.NotNil(t, client) } -func Test_DataStorageProviderFromMemory(t *testing.T) { - provider := DataStorageProviderFromMemory() +func Test_FromMemory(t *testing.T) { + provider := FromMemory() require.NotNil(t, provider) require.Equal(t, TypeInMemory, provider.options.Provider) @@ -48,9 +48,9 @@ func Test_DataStorageProviderFromMemory(t *testing.T) { require.NotNil(t, client) } -func Test_DataStorageProviderFromClient(t *testing.T) { - mockClient := &store.MockStorageClient{} - provider := DataStorageProviderFromClient(mockClient) +func Test_FromClient(t *testing.T) { + mockClient := &database.MockClient{} + provider := FromClient(mockClient) require.NotNil(t, provider) require.Same(t, mockClient, provider.result.client) @@ -61,11 +61,11 @@ func Test_DataStorageProviderFromClient(t *testing.T) { } func Test_GetClient_CachedClient(t *testing.T) { - mockClient := &store.MockStorageClient{} - provider := DataStorageProviderFromOptions(StorageProviderOptions{Provider: "Test"}) + mockClient := &database.MockClient{} + provider := FromOptions(Options{Provider: "Test"}) callCount := 0 - provider.factory = storageFactoryFunc(func(ctx context.Context, options StorageProviderOptions) (store.StorageClient, error) { + provider.factory = databaseClientFactoryFunc(func(ctx context.Context, options Options) (database.Client, error) { callCount++ return mockClient, nil }) @@ -83,44 +83,44 @@ func Test_GetClient_CachedClient(t *testing.T) { } func Test_GetClient_CachedError(t *testing.T) { - provider := DataStorageProviderFromOptions(StorageProviderOptions{Provider: "Test"}) + provider := FromOptions(Options{Provider: "Test"}) expectedErr := errors.New("oh noes!") callCount := 0 - provider.factory = storageFactoryFunc(func(ctx context.Context, options StorageProviderOptions) (store.StorageClient, error) { + provider.factory = databaseClientFactoryFunc(func(ctx context.Context, options Options) (database.Client, error) { callCount++ return nil, expectedErr }) client, err := provider.GetClient(context.Background()) require.Error(t, err) - require.Equal(t, "failed to initialize storage client: oh noes!", err.Error()) + require.Equal(t, "failed to initialize database client: oh noes!", err.Error()) require.Nil(t, client) // Do it twice to ensure the client is cached. client, err = provider.GetClient(context.Background()) require.Error(t, err) - require.Equal(t, "failed to initialize storage client: oh noes!", err.Error()) + require.Equal(t, "failed to initialize database client: oh noes!", err.Error()) require.Nil(t, client) require.Equal(t, 1, callCount) } func TestGetClient_UnsupportedProvider(t *testing.T) { - options := StorageProviderOptions{Provider: "unsupported"} - provider := DataStorageProviderFromOptions(options) + options := Options{Provider: "unsupported"} + provider := FromOptions(options) client, err := provider.GetClient(context.Background()) require.Error(t, err) require.Nil(t, client) - require.Equal(t, "unsupported storage provider: unsupported", err.Error()) + require.Equal(t, "unsupported database provider: unsupported", err.Error()) } func TestInitialize(t *testing.T) { - options := StorageProviderOptions{Provider: TypeInMemory} - provider := DataStorageProviderFromOptions(options) + options := Options{Provider: TypeInMemory} + provider := FromOptions(options) result := provider.initialize(context.Background()) diff --git a/pkg/ucp/dataprovider/types.go b/pkg/ucp/databaseprovider/types.go similarity index 71% rename from pkg/ucp/dataprovider/types.go rename to pkg/ucp/databaseprovider/types.go index cc577b0d63..3a17486c72 100644 --- a/pkg/ucp/dataprovider/types.go +++ b/pkg/ucp/databaseprovider/types.go @@ -14,21 +14,21 @@ See the License for the specific language governing permissions and limitations under the License. */ -package dataprovider +package databaseprovider -// StorageProviderType represents types of storage provider. -type StorageProviderType string +// DatabaseProviderType represents types of database provider. +type DatabaseProviderType string const ( // TypeAPIServer represents the Kubernetes APIServer provider. - TypeAPIServer StorageProviderType = "apiserver" + TypeAPIServer DatabaseProviderType = "apiserver" // TypeETCD represents the etcd provider. - TypeETCD StorageProviderType = "etcd" + TypeETCD DatabaseProviderType = "etcd" // TypeInMemory represents the in-memory provider. - TypeInMemory StorageProviderType = "inmemory" + TypeInMemory DatabaseProviderType = "inmemory" // TypePostgreSQL represents the PostgreSQL provider. - TypePostgreSQL StorageProviderType = "postgresql" + TypePostgreSQL DatabaseProviderType = "postgresql" ) diff --git a/pkg/ucp/frontend/api/routes.go b/pkg/ucp/frontend/api/routes.go index fd84d51aa5..b8e732cb79 100644 --- a/pkg/ucp/frontend/api/routes.go +++ b/pkg/ucp/frontend/api/routes.go @@ -142,16 +142,16 @@ func Register(ctx context.Context, router chi.Router, planeModules []modules.Ini }, }...) - storageClient, err := options.DataProvider.GetClient(ctx) + databaseClient, err := options.DatabaseProvider.GetClient(ctx) if err != nil { return err } ctrlOptions := controller.Options{ - Address: options.Address, - PathBase: options.PathBase, - StorageClient: storageClient, - StatusManager: options.StatusManager, + Address: options.Address, + PathBase: options.PathBase, + DatabaseClient: databaseClient, + StatusManager: options.StatusManager, } for _, h := range handlerOptions { diff --git a/pkg/ucp/frontend/api/routes_test.go b/pkg/ucp/frontend/api/routes_test.go index 32fc581a22..9833f0733e 100644 --- a/pkg/ucp/frontend/api/routes_test.go +++ b/pkg/ucp/frontend/api/routes_test.go @@ -25,7 +25,7 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" "github.com/radius-project/radius/pkg/armrpc/rpctest" - "github.com/radius-project/radius/pkg/ucp/dataprovider" + "github.com/radius-project/radius/pkg/ucp/databaseprovider" "github.com/radius-project/radius/pkg/ucp/frontend/modules" "github.com/radius-project/radius/test/testcontext" "github.com/stretchr/testify/require" @@ -80,10 +80,10 @@ func Test_Routes(t *testing.T) { } options := modules.Options{ - Address: "localhost", - PathBase: pathBase, - DataProvider: dataprovider.DataStorageProviderFromMemory(), - StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), + Address: "localhost", + PathBase: pathBase, + DatabaseProvider: databaseprovider.FromMemory(), + StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), } rpctest.AssertRouters(t, tests, pathBase, "", func(ctx context.Context) (chi.Router, error) { @@ -96,10 +96,10 @@ func Test_Route_ToModule(t *testing.T) { pathBase := "/some-path-base" options := modules.Options{ - Address: "localhost", - PathBase: pathBase, - DataProvider: dataprovider.DataStorageProviderFromMemory(), - StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), + Address: "localhost", + PathBase: pathBase, + DatabaseProvider: databaseprovider.FromMemory(), + StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), } r := chi.NewRouter() diff --git a/pkg/ucp/frontend/api/server.go b/pkg/ucp/frontend/api/server.go index 4f121582c7..99e31911ed 100644 --- a/pkg/ucp/frontend/api/server.go +++ b/pkg/ucp/frontend/api/server.go @@ -32,9 +32,9 @@ import ( "github.com/radius-project/radius/pkg/armrpc/servicecontext" "github.com/radius-project/radius/pkg/middleware" "github.com/radius-project/radius/pkg/sdk" + "github.com/radius-project/radius/pkg/ucp/databaseprovider" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/datamodel/converter" - "github.com/radius-project/radius/pkg/ucp/dataprovider" aws_frontend "github.com/radius-project/radius/pkg/ucp/frontend/aws" azure_frontend "github.com/radius-project/radius/pkg/ucp/frontend/azure" "github.com/radius-project/radius/pkg/ucp/frontend/modules" @@ -42,10 +42,10 @@ import ( "github.com/radius-project/radius/pkg/ucp/frontend/versions" "github.com/radius-project/radius/pkg/ucp/hosting" "github.com/radius-project/radius/pkg/ucp/hostoptions" - queueprovider "github.com/radius-project/radius/pkg/ucp/queue/provider" + "github.com/radius-project/radius/pkg/ucp/queue/queueprovider" "github.com/radius-project/radius/pkg/ucp/resources" "github.com/radius-project/radius/pkg/ucp/rest" - secretprovider "github.com/radius-project/radius/pkg/ucp/secret/provider" + "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" "github.com/radius-project/radius/pkg/ucp/ucplog" "github.com/radius-project/radius/pkg/validator" "github.com/radius-project/radius/swagger" @@ -69,7 +69,7 @@ type ServiceOptions struct { Configure func(chi.Router) TLSCertDir string DefaultPlanesConfigFile string - StorageProviderOptions dataprovider.StorageProviderOptions + DatabaseProviderOptions databaseprovider.Options SecretProviderOptions secretprovider.SecretProviderOptions QueueProviderOptions queueprovider.QueueProviderOptions InitialPlanes []rest.Plane @@ -83,10 +83,10 @@ type ServiceOptions struct { // Service implements the hosting.Service interface for the UCP frontend API. type Service struct { - options ServiceOptions - storageProvider *dataprovider.DataStorageProvider - queueProvider *queueprovider.QueueProvider - secretProvider *secretprovider.SecretProvider + options ServiceOptions + databaseProvider *databaseprovider.DatabaseProvider + queueProvider *queueprovider.QueueProvider + secretProvider *secretprovider.SecretProvider } // DefaultModules returns a list of default modules that will be registered with the router. @@ -112,13 +112,13 @@ func (s *Service) Name() string { return "api" } -// Initialize sets up the router, storage provider, secret provider, status manager, AWS config, AWS clients, +// Initialize sets up the router, database provider, secret provider, status manager, AWS config, AWS clients, // registers the routes, configures the default planes, and sets up the http server with the appropriate middleware. It // returns an http server and an error if one occurs. func (s *Service) Initialize(ctx context.Context) (*http.Server, error) { r := chi.NewRouter() - s.storageProvider = dataprovider.DataStorageProviderFromOptions(s.options.StorageProviderOptions) + s.databaseProvider = databaseprovider.FromOptions(s.options.DatabaseProviderOptions) s.queueProvider = queueprovider.New(s.options.QueueProviderOptions) s.secretProvider = secretprovider.NewSecretProvider(s.options.SecretProviderOptions) @@ -127,7 +127,7 @@ func (s *Service) Initialize(ctx context.Context) (*http.Server, error) { return nil, err } - storageClient, err := s.storageProvider.GetClient(ctx) + databaseClient, err := s.databaseProvider.GetClient(ctx) if err != nil { return nil, err } @@ -137,19 +137,19 @@ func (s *Service) Initialize(ctx context.Context) (*http.Server, error) { return nil, err } - statusManager := statusmanager.New(storageClient, queueClient, s.options.Location) + statusManager := statusmanager.New(databaseClient, queueClient, s.options.Location) moduleOptions := modules.Options{ - Address: s.options.Address, - PathBase: s.options.PathBase, - Config: s.options.Config, - Location: s.options.Location, - DataProvider: s.storageProvider, - QueueProvider: s.queueProvider, - SecretProvider: s.secretProvider, - SpecLoader: specLoader, - StatusManager: statusManager, - UCPConnection: s.options.UCPConnection, + Address: s.options.Address, + PathBase: s.options.PathBase, + Config: s.options.Config, + Location: s.options.Location, + DatabaseProvider: s.databaseProvider, + QueueProvider: s.queueProvider, + SecretProvider: s.secretProvider, + SpecLoader: specLoader, + StatusManager: statusManager, + UCPConnection: s.options.UCPConnection, } modules := DefaultModules(moduleOptions) @@ -222,13 +222,13 @@ func (s *Service) createPlane(ctx context.Context, plane rest.Plane) error { return fmt.Errorf("invalid plane ID: %s", plane.ID) } - db, err := s.storageProvider.GetClient(ctx) + db, err := s.databaseProvider.GetClient(ctx) if err != nil { return err } opts := armrpc_controller.Options{ - StorageClient: db, + DatabaseClient: db, } var ctrl armrpc_controller.Controller diff --git a/pkg/ucp/frontend/aws/routes.go b/pkg/ucp/frontend/aws/routes.go index 6713157d50..7923343d9d 100644 --- a/pkg/ucp/frontend/aws/routes.go +++ b/pkg/ucp/frontend/aws/routes.go @@ -292,16 +292,16 @@ func (m *Module) Initialize(ctx context.Context) (http.Handler, error) { }, }...) - storageClient, err := m.options.DataProvider.GetClient(ctx) + databaseClient, err := m.options.DatabaseProvider.GetClient(ctx) if err != nil { return nil, err } ctrlOpts := controller.Options{ - Address: m.options.Address, - PathBase: m.options.PathBase, - StorageClient: storageClient, - StatusManager: m.options.StatusManager, + Address: m.options.Address, + PathBase: m.options.PathBase, + DatabaseClient: databaseClient, + StatusManager: m.options.StatusManager, } for _, h := range handlerOptions { diff --git a/pkg/ucp/frontend/aws/routes_test.go b/pkg/ucp/frontend/aws/routes_test.go index 5311a646d8..634b2fd23f 100644 --- a/pkg/ucp/frontend/aws/routes_test.go +++ b/pkg/ucp/frontend/aws/routes_test.go @@ -28,12 +28,12 @@ import ( "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" "github.com/radius-project/radius/pkg/armrpc/rpctest" "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" + "github.com/radius-project/radius/pkg/ucp/databaseprovider" "github.com/radius-project/radius/pkg/ucp/datamodel" - "github.com/radius-project/radius/pkg/ucp/dataprovider" "github.com/radius-project/radius/pkg/ucp/frontend/modules" "github.com/radius-project/radius/pkg/ucp/hostoptions" "github.com/radius-project/radius/pkg/ucp/secret" - secretprovider "github.com/radius-project/radius/pkg/ucp/secret/provider" + secretprovider "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" ) const pathBase = "/some-path-base" @@ -118,12 +118,12 @@ func Test_Routes(t *testing.T) { secretProvider.SetClient(secretClient) options := modules.Options{ - Address: "localhost", - PathBase: pathBase, - Config: &hostoptions.UCPConfig{}, - DataProvider: dataprovider.DataStorageProviderFromMemory(), - SecretProvider: secretProvider, - StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), + Address: "localhost", + PathBase: pathBase, + Config: &hostoptions.UCPConfig{}, + DatabaseProvider: databaseprovider.FromMemory(), + SecretProvider: secretProvider, + StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), } rpctest.AssertRouters(t, tests, pathBase, "", func(ctx context.Context) (chi.Router, error) { diff --git a/pkg/ucp/frontend/azure/routes.go b/pkg/ucp/frontend/azure/routes.go index c54cd05fb5..b77d70afe8 100644 --- a/pkg/ucp/frontend/azure/routes.go +++ b/pkg/ucp/frontend/azure/routes.go @@ -165,16 +165,16 @@ func (m *Module) Initialize(ctx context.Context) (http.Handler, error) { }, } - storageClient, err := m.options.DataProvider.GetClient(ctx) + databaseClient, err := m.options.DatabaseProvider.GetClient(ctx) if err != nil { return nil, err } ctrlOpts := controller.Options{ - Address: m.options.Address, - PathBase: m.options.PathBase, - StorageClient: storageClient, - StatusManager: m.options.StatusManager, + Address: m.options.Address, + PathBase: m.options.PathBase, + DatabaseClient: databaseClient, + StatusManager: m.options.StatusManager, } for _, h := range handlerOptions { diff --git a/pkg/ucp/frontend/azure/routes_test.go b/pkg/ucp/frontend/azure/routes_test.go index 1e0e3a5f8c..57109e5e08 100644 --- a/pkg/ucp/frontend/azure/routes_test.go +++ b/pkg/ucp/frontend/azure/routes_test.go @@ -28,12 +28,12 @@ import ( "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" "github.com/radius-project/radius/pkg/armrpc/rpctest" "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" + "github.com/radius-project/radius/pkg/ucp/databaseprovider" "github.com/radius-project/radius/pkg/ucp/datamodel" - "github.com/radius-project/radius/pkg/ucp/dataprovider" "github.com/radius-project/radius/pkg/ucp/frontend/modules" "github.com/radius-project/radius/pkg/ucp/hostoptions" "github.com/radius-project/radius/pkg/ucp/secret" - secretprovider "github.com/radius-project/radius/pkg/ucp/secret/provider" + secretprovider "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" ) const pathBase = "/some-path-base" @@ -92,12 +92,12 @@ func Test_Routes(t *testing.T) { secretProvider.SetClient(secretClient) options := modules.Options{ - Address: "localhost", - PathBase: pathBase, - Config: &hostoptions.UCPConfig{}, - DataProvider: dataprovider.DataStorageProviderFromMemory(), - SecretProvider: secretProvider, - StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), + Address: "localhost", + PathBase: pathBase, + Config: &hostoptions.UCPConfig{}, + DatabaseProvider: databaseprovider.FromMemory(), + SecretProvider: secretProvider, + StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), } rpctest.AssertRouters(t, tests, pathBase, "", func(ctx context.Context) (chi.Router, error) { diff --git a/pkg/ucp/frontend/controller/awsproxy/awsproxytest.go b/pkg/ucp/frontend/controller/awsproxy/awsproxytest.go index 0d7f4a13d7..c5f05ead31 100644 --- a/pkg/ucp/frontend/controller/awsproxy/awsproxytest.go +++ b/pkg/ucp/frontend/controller/awsproxy/awsproxytest.go @@ -21,7 +21,7 @@ import ( "fmt" "testing" - "github.com/radius-project/radius/pkg/ucp/store" + "github.com/radius-project/radius/pkg/ucp/database" "go.uber.org/mock/gomock" awsclient "github.com/radius-project/radius/pkg/ucp/aws" @@ -43,19 +43,19 @@ const ( type TestOptions struct { AWSCloudControlClient *awsclient.MockAWSCloudControlClient AWSCloudFormationClient *awsclient.MockAWSCloudFormationClient - StorageClient *store.MockStorageClient + DatabaseClient *database.MockClient } -// setupTest returns a TestOptions struct with mocked AWS and Storage clients +// setupTest returns a TestOptions struct with mocked AWS and database clients func setupTest(t *testing.T) TestOptions { mockCtrl := gomock.NewController(t) mockCloudControlClient := awsclient.NewMockAWSCloudControlClient(mockCtrl) mockCloudFormationClient := awsclient.NewMockAWSCloudFormationClient(mockCtrl) - mockStorageClient := store.NewMockStorageClient(mockCtrl) + mockDatabaseClient := database.NewMockClient(mockCtrl) return TestOptions{ AWSCloudControlClient: mockCloudControlClient, AWSCloudFormationClient: mockCloudFormationClient, - StorageClient: mockStorageClient, + DatabaseClient: mockDatabaseClient, } } diff --git a/pkg/ucp/frontend/controller/awsproxy/createorupdateawsresource_test.go b/pkg/ucp/frontend/controller/awsproxy/createorupdateawsresource_test.go index ba953a9c6e..a52e69c762 100644 --- a/pkg/ucp/frontend/controller/awsproxy/createorupdateawsresource_test.go +++ b/pkg/ucp/frontend/controller/awsproxy/createorupdateawsresource_test.go @@ -66,7 +66,7 @@ func Test_CreateAWSResource(t *testing.T) { CloudControl: testOptions.AWSCloudControlClient, CloudFormation: testOptions.AWSCloudFormationClient, } - awsController, err := NewCreateOrUpdateAWSResource(armrpc_controller.Options{StorageClient: testOptions.StorageClient}, awsClients) + awsController, err := NewCreateOrUpdateAWSResource(armrpc_controller.Options{DatabaseClient: testOptions.DatabaseClient}, awsClients) require.NoError(t, err) request, err := http.NewRequest(http.MethodPut, testResource.SingleResourcePath, bytes.NewBuffer(requestBodyBytes)) @@ -131,7 +131,7 @@ func Test_CreateAWSResourceInvalidRegion(t *testing.T) { CloudControl: testOptions.AWSCloudControlClient, CloudFormation: testOptions.AWSCloudFormationClient, } - awsController, err := NewCreateOrUpdateAWSResource(armrpc_controller.Options{StorageClient: testOptions.StorageClient}, awsClients) + awsController, err := NewCreateOrUpdateAWSResource(armrpc_controller.Options{DatabaseClient: testOptions.DatabaseClient}, awsClients) require.NoError(t, err) request, err := http.NewRequest(http.MethodPut, testResource.SingleResourcePath, bytes.NewBuffer(requestBodyBytes)) @@ -222,7 +222,7 @@ func Test_UpdateAWSResource(t *testing.T) { CloudControl: testOptions.AWSCloudControlClient, CloudFormation: testOptions.AWSCloudFormationClient, } - awsController, err := NewCreateOrUpdateAWSResource(armrpc_controller.Options{StorageClient: testOptions.StorageClient}, awsClients) + awsController, err := NewCreateOrUpdateAWSResource(armrpc_controller.Options{DatabaseClient: testOptions.DatabaseClient}, awsClients) require.NoError(t, err) request, err := http.NewRequest(http.MethodPut, testResource.SingleResourcePath, bytes.NewBuffer(requestBodyBytes)) @@ -304,7 +304,7 @@ func Test_UpdateNoChangesDoesNotCallUpdate(t *testing.T) { CloudControl: testOptions.AWSCloudControlClient, CloudFormation: testOptions.AWSCloudFormationClient, } - awsController, err := NewCreateOrUpdateAWSResource(armrpc_controller.Options{StorageClient: testOptions.StorageClient}, awsClients) + awsController, err := NewCreateOrUpdateAWSResource(armrpc_controller.Options{DatabaseClient: testOptions.DatabaseClient}, awsClients) require.NoError(t, err) request, err := http.NewRequest(http.MethodPut, testResource.SingleResourcePath, bytes.NewBuffer(requestBodyBytes)) diff --git a/pkg/ucp/frontend/controller/awsproxy/createorupdateawsresourcewithpost_test.go b/pkg/ucp/frontend/controller/awsproxy/createorupdateawsresourcewithpost_test.go index faf6e624f5..ea0efa8a34 100644 --- a/pkg/ucp/frontend/controller/awsproxy/createorupdateawsresourcewithpost_test.go +++ b/pkg/ucp/frontend/controller/awsproxy/createorupdateawsresourcewithpost_test.go @@ -74,7 +74,7 @@ func Test_CreateAWSResourceWithPost(t *testing.T) { CloudControl: testOptions.AWSCloudControlClient, CloudFormation: testOptions.AWSCloudFormationClient, } - awsController, err := NewCreateOrUpdateAWSResourceWithPost(armrpc_controller.Options{StorageClient: testOptions.StorageClient}, awsClients) + awsController, err := NewCreateOrUpdateAWSResourceWithPost(armrpc_controller.Options{DatabaseClient: testOptions.DatabaseClient}, awsClients) require.NoError(t, err) request, err := http.NewRequest(http.MethodPost, testResource.CollectionPath, bytes.NewBuffer(requestBodyBytes)) @@ -167,7 +167,7 @@ func Test_UpdateAWSResourceWithPost(t *testing.T) { CloudControl: testOptions.AWSCloudControlClient, CloudFormation: testOptions.AWSCloudFormationClient, } - awsController, err := NewCreateOrUpdateAWSResourceWithPost(armrpc_controller.Options{StorageClient: testOptions.StorageClient}, awsClients) + awsController, err := NewCreateOrUpdateAWSResourceWithPost(armrpc_controller.Options{DatabaseClient: testOptions.DatabaseClient}, awsClients) require.NoError(t, err) request, err := http.NewRequest(http.MethodPost, testResource.CollectionPath, bytes.NewBuffer(requestBodyBytes)) @@ -256,7 +256,7 @@ func Test_UpdateAWSResourceWithPost_NoChangesNoops(t *testing.T) { CloudControl: testOptions.AWSCloudControlClient, CloudFormation: testOptions.AWSCloudFormationClient, } - awsController, err := NewCreateOrUpdateAWSResourceWithPost(armrpc_controller.Options{StorageClient: testOptions.StorageClient}, awsClients) + awsController, err := NewCreateOrUpdateAWSResourceWithPost(armrpc_controller.Options{DatabaseClient: testOptions.DatabaseClient}, awsClients) require.NoError(t, err) request, err := http.NewRequest(http.MethodPost, "/planes/aws/aws/accounts/1234567/regions/us-west-2/providers/AWS.MemoryDB/Cluster", bytes.NewBuffer(requestBodyBytes)) @@ -335,7 +335,7 @@ func Test_CreateAWSResourceWithPost_NoPrimaryIdentifierAvailable(t *testing.T) { CloudControl: testOptions.AWSCloudControlClient, CloudFormation: testOptions.AWSCloudFormationClient, } - awsController, err := NewCreateOrUpdateAWSResourceWithPost(armrpc_controller.Options{StorageClient: testOptions.StorageClient}, awsClients) + awsController, err := NewCreateOrUpdateAWSResourceWithPost(armrpc_controller.Options{DatabaseClient: testOptions.DatabaseClient}, awsClients) require.NoError(t, err) request, err := http.NewRequest(http.MethodPost, testResource.CollectionPath+"/:put", bytes.NewBuffer(requestBodyBytes)) @@ -416,7 +416,7 @@ func Test_CreateAWSResourceWithPost_MultiIdentifier(t *testing.T) { CloudControl: testOptions.AWSCloudControlClient, CloudFormation: testOptions.AWSCloudFormationClient, } - awsController, err := NewCreateOrUpdateAWSResourceWithPost(armrpc_controller.Options{StorageClient: testOptions.StorageClient}, awsClients) + awsController, err := NewCreateOrUpdateAWSResourceWithPost(armrpc_controller.Options{DatabaseClient: testOptions.DatabaseClient}, awsClients) require.NoError(t, err) request, err := http.NewRequest(http.MethodPost, testResource.CollectionPath+"/:put", bytes.NewBuffer(requestBodyBytes)) @@ -508,7 +508,7 @@ func Test_UpdateAWSResourceWithPost_MultiIdentifier(t *testing.T) { CloudControl: testOptions.AWSCloudControlClient, CloudFormation: testOptions.AWSCloudFormationClient, } - awsController, err := NewCreateOrUpdateAWSResourceWithPost(armrpc_controller.Options{StorageClient: testOptions.StorageClient}, awsClients) + awsController, err := NewCreateOrUpdateAWSResourceWithPost(armrpc_controller.Options{DatabaseClient: testOptions.DatabaseClient}, awsClients) require.NoError(t, err) request, err := http.NewRequest(http.MethodPost, testResource.CollectionPath, bytes.NewBuffer(requestBodyBytes)) diff --git a/pkg/ucp/frontend/controller/awsproxy/deleteawsresource_test.go b/pkg/ucp/frontend/controller/awsproxy/deleteawsresource_test.go index 89af90c43d..4ebfe22046 100644 --- a/pkg/ucp/frontend/controller/awsproxy/deleteawsresource_test.go +++ b/pkg/ucp/frontend/controller/awsproxy/deleteawsresource_test.go @@ -47,7 +47,7 @@ func Test_DeleteAWSResource(t *testing.T) { CloudControl: testOptions.AWSCloudControlClient, CloudFormation: testOptions.AWSCloudFormationClient, } - awsController, err := NewDeleteAWSResource(armrpc_controller.Options{StorageClient: testOptions.StorageClient}, awsClients) + awsController, err := NewDeleteAWSResource(armrpc_controller.Options{DatabaseClient: testOptions.DatabaseClient}, awsClients) require.NoError(t, err) request, err := http.NewRequest(http.MethodDelete, testResource.SingleResourcePath, nil) @@ -89,7 +89,7 @@ func Test_DeleteAWSResource_ResourceDoesNotExist(t *testing.T) { CloudControl: testOptions.AWSCloudControlClient, CloudFormation: testOptions.AWSCloudFormationClient, } - awsController, err := NewDeleteAWSResource(armrpc_controller.Options{StorageClient: testOptions.StorageClient}, awsClients) + awsController, err := NewDeleteAWSResource(armrpc_controller.Options{DatabaseClient: testOptions.DatabaseClient}, awsClients) require.NoError(t, err) request, err := http.NewRequest(http.MethodDelete, testResource.SingleResourcePath, nil) diff --git a/pkg/ucp/frontend/controller/awsproxy/deleteawsresourcewithpost_test.go b/pkg/ucp/frontend/controller/awsproxy/deleteawsresourcewithpost_test.go index 929038c1f0..b86d29675a 100644 --- a/pkg/ucp/frontend/controller/awsproxy/deleteawsresourcewithpost_test.go +++ b/pkg/ucp/frontend/controller/awsproxy/deleteawsresourcewithpost_test.go @@ -59,7 +59,7 @@ func Test_DeleteAWSResourceWithPost(t *testing.T) { CloudControl: testOptions.AWSCloudControlClient, CloudFormation: testOptions.AWSCloudFormationClient, } - awsController, err := NewDeleteAWSResourceWithPost(armrpc_controller.Options{StorageClient: testOptions.StorageClient}, awsClients) + awsController, err := NewDeleteAWSResourceWithPost(armrpc_controller.Options{DatabaseClient: testOptions.DatabaseClient}, awsClients) require.NoError(t, err) requestBody := map[string]any{ @@ -110,7 +110,7 @@ func Test_DeleteAWSResourceWithPost_ResourceDoesNotExist(t *testing.T) { CloudControl: testOptions.AWSCloudControlClient, CloudFormation: testOptions.AWSCloudFormationClient, } - awsController, err := NewDeleteAWSResourceWithPost(armrpc_controller.Options{StorageClient: testOptions.StorageClient}, awsClients) + awsController, err := NewDeleteAWSResourceWithPost(armrpc_controller.Options{DatabaseClient: testOptions.DatabaseClient}, awsClients) require.NoError(t, err) requestBody := map[string]any{ @@ -183,7 +183,7 @@ func Test_DeleteAWSResourceWithPost_MultiIdentifier(t *testing.T) { CloudControl: testOptions.AWSCloudControlClient, CloudFormation: testOptions.AWSCloudFormationClient, } - awsController, err := NewDeleteAWSResourceWithPost(armrpc_controller.Options{StorageClient: testOptions.StorageClient}, awsClients) + awsController, err := NewDeleteAWSResourceWithPost(armrpc_controller.Options{DatabaseClient: testOptions.DatabaseClient}, awsClients) require.NoError(t, err) actualResponse, err := awsController.Run(ctx, nil, request) diff --git a/pkg/ucp/frontend/controller/awsproxy/getawsoperationresults_test.go b/pkg/ucp/frontend/controller/awsproxy/getawsoperationresults_test.go index 78e35150b4..ef44c9f8e3 100644 --- a/pkg/ucp/frontend/controller/awsproxy/getawsoperationresults_test.go +++ b/pkg/ucp/frontend/controller/awsproxy/getawsoperationresults_test.go @@ -49,7 +49,7 @@ func Test_GetAWSOperationResults_TerminalStatus(t *testing.T) { CloudControl: testOptions.AWSCloudControlClient, CloudFormation: testOptions.AWSCloudFormationClient, } - awsController, err := NewGetAWSOperationResults(armrpc_controller.Options{StorageClient: testOptions.StorageClient}, awsClients) + awsController, err := NewGetAWSOperationResults(armrpc_controller.Options{DatabaseClient: testOptions.DatabaseClient}, awsClients) require.NoError(t, err) request, err := http.NewRequest(http.MethodGet, testResource.OperationResultsPath, nil) @@ -80,7 +80,7 @@ func Test_GetAWSOperationResults_NonTerminalStatus(t *testing.T) { CloudControl: testOptions.AWSCloudControlClient, CloudFormation: testOptions.AWSCloudFormationClient, } - awsController, err := NewGetAWSOperationResults(armrpc_controller.Options{StorageClient: testOptions.StorageClient}, awsClients) + awsController, err := NewGetAWSOperationResults(armrpc_controller.Options{DatabaseClient: testOptions.DatabaseClient}, awsClients) require.NoError(t, err) request, err := http.NewRequest(http.MethodGet, testResource.OperationResultsPath, nil) diff --git a/pkg/ucp/frontend/controller/awsproxy/getawsoperationstatuses_test.go b/pkg/ucp/frontend/controller/awsproxy/getawsoperationstatuses_test.go index d10a35175f..b07e30014c 100644 --- a/pkg/ucp/frontend/controller/awsproxy/getawsoperationstatuses_test.go +++ b/pkg/ucp/frontend/controller/awsproxy/getawsoperationstatuses_test.go @@ -52,7 +52,7 @@ func Test_GetAWSOperationStatuses(t *testing.T) { CloudControl: testOptions.AWSCloudControlClient, CloudFormation: testOptions.AWSCloudFormationClient, } - awsController, err := NewGetAWSOperationStatuses(armrpc_controller.Options{StorageClient: testOptions.StorageClient}, awsClients) + awsController, err := NewGetAWSOperationStatuses(armrpc_controller.Options{DatabaseClient: testOptions.DatabaseClient}, awsClients) require.NoError(t, err) request, err := http.NewRequest(http.MethodGet, testResource.OperationStatusesPath, nil) @@ -93,7 +93,7 @@ func Test_GetAWSOperationStatuses_Failed(t *testing.T) { CloudControl: testOptions.AWSCloudControlClient, CloudFormation: testOptions.AWSCloudFormationClient, } - awsController, err := NewGetAWSOperationStatuses(armrpc_controller.Options{StorageClient: testOptions.StorageClient}, awsClients) + awsController, err := NewGetAWSOperationStatuses(armrpc_controller.Options{DatabaseClient: testOptions.DatabaseClient}, awsClients) require.NoError(t, err) request, err := http.NewRequest(http.MethodGet, testResource.OperationStatusesPath, nil) @@ -135,7 +135,7 @@ func Test_GetAWSOperationStatuses_Delete_NotFound(t *testing.T) { CloudControl: testOptions.AWSCloudControlClient, CloudFormation: testOptions.AWSCloudFormationClient, } - awsController, err := NewGetAWSOperationStatuses(armrpc_controller.Options{StorageClient: testOptions.StorageClient}, awsClients) + awsController, err := NewGetAWSOperationStatuses(armrpc_controller.Options{DatabaseClient: testOptions.DatabaseClient}, awsClients) require.NoError(t, err) request, err := http.NewRequest(http.MethodGet, testResource.OperationStatusesPath, nil) diff --git a/pkg/ucp/frontend/controller/awsproxy/getawsresource_test.go b/pkg/ucp/frontend/controller/awsproxy/getawsresource_test.go index 80d53375d4..d30dbdb708 100644 --- a/pkg/ucp/frontend/controller/awsproxy/getawsresource_test.go +++ b/pkg/ucp/frontend/controller/awsproxy/getawsresource_test.go @@ -61,7 +61,7 @@ func Test_GetAWSResource(t *testing.T) { CloudControl: testOptions.AWSCloudControlClient, CloudFormation: testOptions.AWSCloudFormationClient, } - awsController, err := NewGetAWSResource(armrpc_controller.Options{StorageClient: testOptions.StorageClient}, awsClients) + awsController, err := NewGetAWSResource(armrpc_controller.Options{DatabaseClient: testOptions.DatabaseClient}, awsClients) require.NoError(t, err) request, err := http.NewRequest(http.MethodGet, testResource.SingleResourcePath, nil) @@ -97,7 +97,7 @@ func Test_GetAWSResource_NotFound(t *testing.T) { CloudControl: testOptions.AWSCloudControlClient, CloudFormation: testOptions.AWSCloudFormationClient, } - awsController, err := NewGetAWSResource(armrpc_controller.Options{StorageClient: testOptions.StorageClient}, awsClients) + awsController, err := NewGetAWSResource(armrpc_controller.Options{DatabaseClient: testOptions.DatabaseClient}, awsClients) require.NoError(t, err) request, err := http.NewRequest(http.MethodGet, testResource.SingleResourcePath, nil) @@ -124,7 +124,7 @@ func Test_GetAWSResource_UnknownError(t *testing.T) { CloudControl: testOptions.AWSCloudControlClient, CloudFormation: testOptions.AWSCloudFormationClient, } - awsController, err := NewGetAWSResource(armrpc_controller.Options{StorageClient: testOptions.StorageClient}, awsClients) + awsController, err := NewGetAWSResource(armrpc_controller.Options{DatabaseClient: testOptions.DatabaseClient}, awsClients) require.NoError(t, err) request, err := http.NewRequest(http.MethodGet, testResource.SingleResourcePath, nil) @@ -155,7 +155,7 @@ func Test_GetAWSResource_SmithyError(t *testing.T) { CloudControl: testOptions.AWSCloudControlClient, CloudFormation: testOptions.AWSCloudFormationClient, } - awsController, err := NewGetAWSResource(armrpc_controller.Options{StorageClient: testOptions.StorageClient}, awsClients) + awsController, err := NewGetAWSResource(armrpc_controller.Options{DatabaseClient: testOptions.DatabaseClient}, awsClients) require.NoError(t, err) request, err := http.NewRequest(http.MethodGet, testResource.SingleResourcePath, nil) diff --git a/pkg/ucp/frontend/controller/awsproxy/getawsresourcewithpost_test.go b/pkg/ucp/frontend/controller/awsproxy/getawsresourcewithpost_test.go index ce61b22cb9..06063cadc8 100644 --- a/pkg/ucp/frontend/controller/awsproxy/getawsresourcewithpost_test.go +++ b/pkg/ucp/frontend/controller/awsproxy/getawsresourcewithpost_test.go @@ -74,7 +74,7 @@ func Test_GetAWSResourceWithPost(t *testing.T) { CloudControl: testOptions.AWSCloudControlClient, CloudFormation: testOptions.AWSCloudFormationClient, } - awsController, err := NewGetAWSResourceWithPost(armrpc_controller.Options{StorageClient: testOptions.StorageClient}, awsClients) + awsController, err := NewGetAWSResourceWithPost(armrpc_controller.Options{DatabaseClient: testOptions.DatabaseClient}, awsClients) require.NoError(t, err) requestBody := map[string]any{ @@ -126,7 +126,7 @@ func Test_GetAWSResourceWithPost_NotFound(t *testing.T) { CloudControl: testOptions.AWSCloudControlClient, CloudFormation: testOptions.AWSCloudFormationClient, } - awsController, err := NewGetAWSResourceWithPost(armrpc_controller.Options{StorageClient: testOptions.StorageClient}, awsClients) + awsController, err := NewGetAWSResourceWithPost(armrpc_controller.Options{DatabaseClient: testOptions.DatabaseClient}, awsClients) require.NoError(t, err) requestBody := map[string]any{ @@ -165,7 +165,7 @@ func Test_GetAWSResourceWithPost_UnknownError(t *testing.T) { CloudControl: testOptions.AWSCloudControlClient, CloudFormation: testOptions.AWSCloudFormationClient, } - awsController, err := NewGetAWSResourceWithPost(armrpc_controller.Options{StorageClient: testOptions.StorageClient}, awsClients) + awsController, err := NewGetAWSResourceWithPost(armrpc_controller.Options{DatabaseClient: testOptions.DatabaseClient}, awsClients) require.NoError(t, err) requestBody := map[string]any{ @@ -211,7 +211,7 @@ func Test_GetAWSResourceWithPost_SmithyError(t *testing.T) { CloudControl: testOptions.AWSCloudControlClient, CloudFormation: testOptions.AWSCloudFormationClient, } - awsController, err := NewGetAWSResourceWithPost(armrpc_controller.Options{StorageClient: testOptions.StorageClient}, awsClients) + awsController, err := NewGetAWSResourceWithPost(armrpc_controller.Options{DatabaseClient: testOptions.DatabaseClient}, awsClients) require.NoError(t, err) requestBody := map[string]any{ @@ -287,7 +287,7 @@ func Test_GetAWSResourceWithPost_MultiIdentifier(t *testing.T) { CloudFormation: testOptions.AWSCloudFormationClient, } - awsController, err := NewGetAWSResourceWithPost(armrpc_controller.Options{StorageClient: testOptions.StorageClient}, awsClients) + awsController, err := NewGetAWSResourceWithPost(armrpc_controller.Options{DatabaseClient: testOptions.DatabaseClient}, awsClients) require.NoError(t, err) actualResponse, err := awsController.Run(ctx, nil, request) diff --git a/pkg/ucp/frontend/controller/awsproxy/listawsresources_test.go b/pkg/ucp/frontend/controller/awsproxy/listawsresources_test.go index 290d8136b6..74327e0e71 100644 --- a/pkg/ucp/frontend/controller/awsproxy/listawsresources_test.go +++ b/pkg/ucp/frontend/controller/awsproxy/listawsresources_test.go @@ -75,7 +75,7 @@ func Test_ListAWSResources(t *testing.T) { CloudControl: testOptions.AWSCloudControlClient, CloudFormation: testOptions.AWSCloudFormationClient, } - awsController, err := NewListAWSResources(armrpc_controller.Options{StorageClient: testOptions.StorageClient}, awsClients) + awsController, err := NewListAWSResources(armrpc_controller.Options{DatabaseClient: testOptions.DatabaseClient}, awsClients) require.NoError(t, err) request, err := http.NewRequest(http.MethodGet, firstTestResource.CollectionPath, nil) @@ -121,7 +121,7 @@ func Test_ListAWSResourcesEmpty(t *testing.T) { CloudControl: testOptions.AWSCloudControlClient, CloudFormation: testOptions.AWSCloudFormationClient, } - awsController, err := NewListAWSResources(armrpc_controller.Options{StorageClient: testOptions.StorageClient}, awsClients) + awsController, err := NewListAWSResources(armrpc_controller.Options{DatabaseClient: testOptions.DatabaseClient}, awsClients) require.NoError(t, err) request, err := http.NewRequest(http.MethodGet, testResource.CollectionPath, nil) @@ -148,7 +148,7 @@ func Test_ListAWSResource_UnknownError(t *testing.T) { CloudControl: testOptions.AWSCloudControlClient, CloudFormation: testOptions.AWSCloudFormationClient, } - awsController, err := NewListAWSResources(armrpc_controller.Options{StorageClient: testOptions.StorageClient}, awsClients) + awsController, err := NewListAWSResources(armrpc_controller.Options{DatabaseClient: testOptions.DatabaseClient}, awsClients) require.NoError(t, err) request, err := http.NewRequest(http.MethodGet, testResource.CollectionPath, nil) @@ -179,7 +179,7 @@ func Test_ListAWSResource_SmithyError(t *testing.T) { CloudControl: testOptions.AWSCloudControlClient, CloudFormation: testOptions.AWSCloudFormationClient, } - awsController, err := NewListAWSResources(armrpc_controller.Options{StorageClient: testOptions.StorageClient}, awsClients) + awsController, err := NewListAWSResources(armrpc_controller.Options{DatabaseClient: testOptions.DatabaseClient}, awsClients) require.NoError(t, err) request, err := http.NewRequest(http.MethodGet, testResource.CollectionPath, nil) diff --git a/pkg/ucp/frontend/controller/credentials/aws/createorupdateawscredential_test.go b/pkg/ucp/frontend/controller/credentials/aws/createorupdateawscredential_test.go index 72ddac212e..0e590c45cf 100644 --- a/pkg/ucp/frontend/controller/credentials/aws/createorupdateawscredential_test.go +++ b/pkg/ucp/frontend/controller/credentials/aws/createorupdateawscredential_test.go @@ -27,8 +27,8 @@ import ( "github.com/radius-project/radius/pkg/armrpc/rpctest" "github.com/radius-project/radius/pkg/to" "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/secret" - "github.com/radius-project/radius/pkg/ucp/store" "github.com/radius-project/radius/test/testutil" armrpc_controller "github.com/radius-project/radius/pkg/armrpc/frontend/controller" @@ -39,10 +39,10 @@ import ( func Test_AWS_Credential(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() - mockStorageClient := store.NewMockStorageClient(mockCtrl) + mockDatabaseClient := database.NewMockClient(mockCtrl) mockSecretClient := secret.NewMockClient(mockCtrl) - credentialCtrl, err := NewCreateOrUpdateAWSCredential(armrpc_controller.Options{StorageClient: mockStorageClient}, mockSecretClient) + credentialCtrl, err := NewCreateOrUpdateAWSCredential(armrpc_controller.Options{DatabaseClient: mockDatabaseClient}, mockSecretClient) require.NoError(t, err) tests := []struct { @@ -51,7 +51,7 @@ func Test_AWS_Credential(t *testing.T) { headerfile string url string expected armrpc_rest.Response - fn func(mockStorageClient store.MockStorageClient, mockSecretClient secret.MockClient) + fn func(mockDatabaseClient database.MockClient, mockSecretClient secret.MockClient) err error }{ { @@ -121,7 +121,7 @@ func Test_AWS_Credential(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - tt.fn(*mockStorageClient, *mockSecretClient) + tt.fn(*mockDatabaseClient, *mockSecretClient) credentialVersionedInput := &v20231001preview.AwsCredentialResource{} credentialInput := testutil.ReadFixture(tt.filename) @@ -165,44 +165,44 @@ func getAwsResponse() armrpc_rest.Response { }, map[string]string{"ETag": ""}) } -func setupCredentialSuccessMocks(mockStorageClient store.MockStorageClient, mockSecretClient secret.MockClient) { - mockStorageClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { - return nil, &store.ErrNotFound{ID: id} +func setupCredentialSuccessMocks(mockDatabaseClient database.MockClient, mockSecretClient secret.MockClient) { + mockDatabaseClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { + return nil, &database.ErrNotFound{ID: id} }) mockSecretClient.EXPECT().Save(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1) - mockStorageClient.EXPECT().Save(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1) + mockDatabaseClient.EXPECT().Save(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1) } -func setupEmptyMocks(mockStorageClient store.MockStorageClient, mockSecretClient secret.MockClient) { +func setupEmptyMocks(mockDatabaseClient database.MockClient, mockSecretClient secret.MockClient) { } -func setupCredentialNotFoundMocks(mockStorageClient store.MockStorageClient, mockSecretClient secret.MockClient) { - mockStorageClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, options ...store.GetOptions) (*store.Object, error) { - return nil, &store.ErrNotFound{ID: id} +func setupCredentialNotFoundMocks(mockDatabaseClient database.MockClient, mockSecretClient secret.MockClient) { + mockDatabaseClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()). + DoAndReturn(func(ctx context.Context, id string, options ...database.GetOptions) (*database.Object, error) { + return nil, &database.ErrNotFound{ID: id} }).Times(1) mockSecretClient.EXPECT().Save(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1) - mockStorageClient.EXPECT().Save(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1) + mockDatabaseClient.EXPECT().Save(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1) } -func setupCredentialNotFoundErrorMocks(mockStorageClient store.MockStorageClient, mockSecretClient secret.MockClient) { - mockStorageClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, options ...store.GetOptions) (*store.Object, error) { +func setupCredentialNotFoundErrorMocks(mockDatabaseClient database.MockClient, mockSecretClient secret.MockClient) { + mockDatabaseClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()). + DoAndReturn(func(ctx context.Context, id string, options ...database.GetOptions) (*database.Object, error) { return nil, errors.New("Error") }).Times(1) } -func setupCredentialGetFailMocks(mockStorageClient store.MockStorageClient, mockSecretClient secret.MockClient) { - mockStorageClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, options ...store.GetOptions) (*store.Object, error) { +func setupCredentialGetFailMocks(mockDatabaseClient database.MockClient, mockSecretClient secret.MockClient) { + mockDatabaseClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()). + DoAndReturn(func(ctx context.Context, id string, options ...database.GetOptions) (*database.Object, error) { return nil, errors.New("Failed Get") }).Times(1) } -func setupCredentialSecretSaveFailMocks(mockStorageClient store.MockStorageClient, mockSecretClient secret.MockClient) { - mockStorageClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Times(1). - DoAndReturn(func(ctx context.Context, id string, options ...store.GetOptions) (*store.Object, error) { - return nil, &store.ErrNotFound{ID: id} +func setupCredentialSecretSaveFailMocks(mockDatabaseClient database.MockClient, mockSecretClient secret.MockClient) { + mockDatabaseClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Times(1). + DoAndReturn(func(ctx context.Context, id string, options ...database.GetOptions) (*database.Object, error) { + return nil, &database.ErrNotFound{ID: id} }).Times(1) mockSecretClient.EXPECT().Save(gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("Secret Save Failure")).Times(1) } diff --git a/pkg/ucp/frontend/controller/credentials/aws/deleteawscredential.go b/pkg/ucp/frontend/controller/credentials/aws/deleteawscredential.go index 0f6b0b96aa..eaa9d9c7f7 100644 --- a/pkg/ucp/frontend/controller/credentials/aws/deleteawscredential.go +++ b/pkg/ucp/frontend/controller/credentials/aws/deleteawscredential.go @@ -24,11 +24,11 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" armrpc_controller "github.com/radius-project/radius/pkg/armrpc/frontend/controller" armrpcrest "github.com/radius-project/radius/pkg/armrpc/rest" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/datamodel/converter" "github.com/radius-project/radius/pkg/ucp/frontend/controller/credentials" "github.com/radius-project/radius/pkg/ucp/secret" - "github.com/radius-project/radius/pkg/ucp/store" "github.com/radius-project/radius/pkg/ucp/ucplog" ) @@ -82,8 +82,8 @@ func (c *DeleteAWSCredential) Run(ctx context.Context, w http.ResponseWriter, re return r, err } - if err := c.StorageClient().Delete(ctx, serviceCtx.ResourceID.String()); err != nil { - if errors.Is(&store.ErrNotFound{ID: serviceCtx.ResourceID.String()}, err) { + if err := c.DatabaseClient().Delete(ctx, serviceCtx.ResourceID.String()); err != nil { + if errors.Is(&database.ErrNotFound{ID: serviceCtx.ResourceID.String()}, err) { return armrpcrest.NewNoContentResponse(), nil } return nil, err diff --git a/pkg/ucp/frontend/controller/credentials/aws/deleteawscredential_test.go b/pkg/ucp/frontend/controller/credentials/aws/deleteawscredential_test.go index d3b562264e..223ff65c2a 100644 --- a/pkg/ucp/frontend/controller/credentials/aws/deleteawscredential_test.go +++ b/pkg/ucp/frontend/controller/credentials/aws/deleteawscredential_test.go @@ -25,9 +25,9 @@ import ( armrpc_controller "github.com/radius-project/radius/pkg/armrpc/frontend/controller" armrpcrest "github.com/radius-project/radius/pkg/armrpc/rest" "github.com/radius-project/radius/pkg/armrpc/rpctest" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/secret" - "github.com/radius-project/radius/pkg/ucp/store" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -36,17 +36,17 @@ import ( func Test_Credential_Delete(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() - mockStorageClient := store.NewMockStorageClient(mockCtrl) + mockDatabaseClient := database.NewMockClient(mockCtrl) mockSecretClient := secret.NewMockClient(mockCtrl) - credentialCtrl, err := NewDeleteAWSCredential(armrpc_controller.Options{StorageClient: mockStorageClient}, mockSecretClient) + credentialCtrl, err := NewDeleteAWSCredential(armrpc_controller.Options{DatabaseClient: mockDatabaseClient}, mockSecretClient) require.NoError(t, err) tests := []struct { name string url string headerfile string - fn func(mockStorageClient store.MockStorageClient, mockSecretClient secret.MockClient) + fn func(mockDatabaseClient database.MockClient, mockSecretClient secret.MockClient) expected armrpcrest.Response err error }{ @@ -110,7 +110,7 @@ func Test_Credential_Delete(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - tt.fn(*mockStorageClient, *mockSecretClient) + tt.fn(*mockDatabaseClient, *mockSecretClient) request, err := rpctest.NewHTTPRequestFromJSON(context.Background(), http.MethodDelete, tt.headerfile, nil) require.NoError(t, err) ctx := rpctest.NewARMRequestContext(request) @@ -125,7 +125,7 @@ func Test_Credential_Delete(t *testing.T) { } } -func setupCredentialMocks(mockStorageClient store.MockStorageClient) { +func setupCredentialMocks(mockDatabaseClient database.MockClient) { datamodelCredential := datamodel.AWSCredential{ BaseResource: v1.BaseResource{}, Properties: &datamodel.AWSCredentialResourceProperties{ @@ -133,10 +133,10 @@ func setupCredentialMocks(mockStorageClient store.MockStorageClient) { }, } - mockStorageClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, options ...store.GetOptions) (*store.Object, error) { - return &store.Object{ - Metadata: store.Metadata{ + mockDatabaseClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()). + DoAndReturn(func(ctx context.Context, id string, options ...database.GetOptions) (*database.Object, error) { + return &database.Object{ + Metadata: database.Metadata{ ID: datamodelCredential.TrackedResource.ID, }, Data: &datamodelCredential, @@ -144,40 +144,40 @@ func setupCredentialMocks(mockStorageClient store.MockStorageClient) { }).Times(1) } -func setupCredentialDeleteSuccessMocks(mockStorageClient store.MockStorageClient, mockSecretClient secret.MockClient) { - setupCredentialMocks(mockStorageClient) +func setupCredentialDeleteSuccessMocks(mockDatabaseClient database.MockClient, mockSecretClient secret.MockClient) { + setupCredentialMocks(mockDatabaseClient) mockSecretClient.EXPECT().Delete(gomock.Any(), gomock.Any()).Return(nil).Times(1) - mockStorageClient.EXPECT().Delete(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1) + mockDatabaseClient.EXPECT().Delete(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1) } -func setupNonExistentCredentialDeleteMocks(mockStorageClient store.MockStorageClient, mockSecretClient secret.MockClient) { - mockStorageClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, &store.ErrNotFound{}).Times(1) +func setupNonExistentCredentialDeleteMocks(mockDatabaseClient database.MockClient, mockSecretClient secret.MockClient) { + mockDatabaseClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, &database.ErrNotFound{}).Times(1) } -func setupCredentialExistenceCheckFailureMocks(mockStorageClient store.MockStorageClient, mockSecretClient secret.MockClient) { - mockStorageClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errors.New("test_failure")).Times(1) +func setupCredentialExistenceCheckFailureMocks(mockDatabaseClient database.MockClient, mockSecretClient secret.MockClient) { + mockDatabaseClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errors.New("test_failure")).Times(1) } -func setupNonExistentSecretDeleteMocks(mockStorageClient store.MockStorageClient, mockSecretClient secret.MockClient) { - setupCredentialMocks(mockStorageClient) +func setupNonExistentSecretDeleteMocks(mockDatabaseClient database.MockClient, mockSecretClient secret.MockClient) { + setupCredentialMocks(mockDatabaseClient) mockSecretClient.EXPECT().Delete(gomock.Any(), gomock.Any()).Return(&secret.ErrNotFound{}).Times(1) } -func setupSecretDeleteFailureMocks(mockStorageClient store.MockStorageClient, mockSecretClient secret.MockClient) { - setupCredentialMocks(mockStorageClient) +func setupSecretDeleteFailureMocks(mockDatabaseClient database.MockClient, mockSecretClient secret.MockClient) { + setupCredentialMocks(mockDatabaseClient) mockSecretClient.EXPECT().Delete(gomock.Any(), gomock.Any()).Return(errors.New("Failed secret deletion")).Times(1) } -func setupNonExistingCredentialDeleteFromStorageMocks(mockStorageClient store.MockStorageClient, mockSecretClient secret.MockClient) { - setupCredentialMocks(mockStorageClient) +func setupNonExistingCredentialDeleteFromStorageMocks(mockDatabaseClient database.MockClient, mockSecretClient secret.MockClient) { + setupCredentialMocks(mockDatabaseClient) mockSecretClient.EXPECT().Delete(gomock.Any(), gomock.Any()).Return(nil).Times(1) - mockStorageClient.EXPECT().Delete(gomock.Any(), gomock.Any(), gomock.Any()).Return(&store.ErrNotFound{}).Times(1) + mockDatabaseClient.EXPECT().Delete(gomock.Any(), gomock.Any(), gomock.Any()).Return(&database.ErrNotFound{}).Times(1) } -func setupFailedCredentialDeleteFromStorageMocks(mockStorageClient store.MockStorageClient, mockSecretClient secret.MockClient) { - setupCredentialMocks(mockStorageClient) +func setupFailedCredentialDeleteFromStorageMocks(mockDatabaseClient database.MockClient, mockSecretClient secret.MockClient) { + setupCredentialMocks(mockDatabaseClient) mockSecretClient.EXPECT().Delete(gomock.Any(), gomock.Any()).Return(nil).Times(1) - mockStorageClient.EXPECT().Delete(gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("Failed Storage Deletion")).Times(1) + mockDatabaseClient.EXPECT().Delete(gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("Failed Storage Deletion")).Times(1) } diff --git a/pkg/ucp/frontend/controller/credentials/azure/createorupdateazurecredential_test.go b/pkg/ucp/frontend/controller/credentials/azure/createorupdateazurecredential_test.go index 03a98b5788..1889ede636 100644 --- a/pkg/ucp/frontend/controller/credentials/azure/createorupdateazurecredential_test.go +++ b/pkg/ucp/frontend/controller/credentials/azure/createorupdateazurecredential_test.go @@ -27,8 +27,8 @@ import ( "github.com/radius-project/radius/pkg/armrpc/rpctest" "github.com/radius-project/radius/pkg/to" "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/secret" - "github.com/radius-project/radius/pkg/ucp/store" "github.com/radius-project/radius/test/testutil" armrpc_controller "github.com/radius-project/radius/pkg/armrpc/frontend/controller" @@ -39,11 +39,11 @@ import ( func Test_Azure_Credential(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() - mockStorageClient := store.NewMockStorageClient(mockCtrl) + mockDatabaseClient := database.NewMockClient(mockCtrl) mockSecretClient := secret.NewMockClient(mockCtrl) credentialCtrl, err := NewCreateOrUpdateAzureCredential(armrpc_controller.Options{ - StorageClient: mockStorageClient, + DatabaseClient: mockDatabaseClient, }, mockSecretClient) require.NoError(t, err) @@ -53,7 +53,7 @@ func Test_Azure_Credential(t *testing.T) { headerfile string url string expected armrpc_rest.Response - fn func(mockStorageClient store.MockStorageClient, mockSecretClient secret.MockClient) + fn func(mockDatabaseClient database.MockClient, mockSecretClient secret.MockClient) err error }{ { @@ -123,7 +123,7 @@ func Test_Azure_Credential(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - tt.fn(*mockStorageClient, *mockSecretClient) + tt.fn(*mockDatabaseClient, *mockSecretClient) credentialVersionedInput := &v20231001preview.AzureCredentialResource{} credentialInput := testutil.ReadFixture(tt.filename) err = json.Unmarshal(credentialInput, credentialVersionedInput) @@ -165,44 +165,44 @@ func getAzureCredentialResponse() armrpc_rest.Response { }, map[string]string{"ETag": ""}) } -func setupCredentialSuccessMocks(mockStorageClient store.MockStorageClient, mockSecretClient secret.MockClient) { - mockStorageClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).DoAndReturn(func(ctx context.Context, id string, _ ...store.GetOptions) (*store.Object, error) { - return nil, &store.ErrNotFound{ID: id} +func setupCredentialSuccessMocks(mockDatabaseClient database.MockClient, mockSecretClient secret.MockClient) { + mockDatabaseClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).DoAndReturn(func(ctx context.Context, id string, _ ...database.GetOptions) (*database.Object, error) { + return nil, &database.ErrNotFound{ID: id} }) mockSecretClient.EXPECT().Save(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1) - mockStorageClient.EXPECT().Save(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1) + mockDatabaseClient.EXPECT().Save(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1) } -func setupCredentialNotFoundMocks(mockStorageClient store.MockStorageClient, mockSecretClient secret.MockClient) { - mockStorageClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, options ...store.GetOptions) (*store.Object, error) { - return nil, &store.ErrNotFound{ID: id} +func setupCredentialNotFoundMocks(mockDatabaseClient database.MockClient, mockSecretClient secret.MockClient) { + mockDatabaseClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()). + DoAndReturn(func(ctx context.Context, id string, options ...database.GetOptions) (*database.Object, error) { + return nil, &database.ErrNotFound{ID: id} }).Times(1) mockSecretClient.EXPECT().Save(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1) - mockStorageClient.EXPECT().Save(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1) + mockDatabaseClient.EXPECT().Save(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1) } -func setupCredentialNotFoundErrorMocks(mockStorageClient store.MockStorageClient, mockSecretClient secret.MockClient) { - mockStorageClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, options ...store.GetOptions) (*store.Object, error) { +func setupCredentialNotFoundErrorMocks(mockDatabaseClient database.MockClient, mockSecretClient secret.MockClient) { + mockDatabaseClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()). + DoAndReturn(func(ctx context.Context, id string, options ...database.GetOptions) (*database.Object, error) { return nil, errors.New("Error") }).Times(1) } -func setupCredentialGetFailMocks(mockStorageClient store.MockStorageClient, mockSecretClient secret.MockClient) { - mockStorageClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, options ...store.GetOptions) (*store.Object, error) { +func setupCredentialGetFailMocks(mockDatabaseClient database.MockClient, mockSecretClient secret.MockClient) { + mockDatabaseClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()). + DoAndReturn(func(ctx context.Context, id string, options ...database.GetOptions) (*database.Object, error) { return nil, errors.New("Failed Get") }).Times(1) } -func setupCredentialSecretSaveFailMocks(mockStorageClient store.MockStorageClient, mockSecretClient secret.MockClient) { - mockStorageClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, options ...store.GetOptions) (*store.Object, error) { - return nil, &store.ErrNotFound{ID: id} +func setupCredentialSecretSaveFailMocks(mockDatabaseClient database.MockClient, mockSecretClient secret.MockClient) { + mockDatabaseClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()). + DoAndReturn(func(ctx context.Context, id string, options ...database.GetOptions) (*database.Object, error) { + return nil, &database.ErrNotFound{ID: id} }).Times(1) mockSecretClient.EXPECT().Save(gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("Secret Save Failure")).Times(1) } -func setupEmptyMocks(mockStorageClient store.MockStorageClient, mockSecretClient secret.MockClient) { +func setupEmptyMocks(mockDatabaseClient database.MockClient, mockSecretClient secret.MockClient) { } diff --git a/pkg/ucp/frontend/controller/credentials/azure/deleteazurecredential.go b/pkg/ucp/frontend/controller/credentials/azure/deleteazurecredential.go index 896d936304..09f1b3025f 100644 --- a/pkg/ucp/frontend/controller/credentials/azure/deleteazurecredential.go +++ b/pkg/ucp/frontend/controller/credentials/azure/deleteazurecredential.go @@ -24,11 +24,11 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" armrpc_controller "github.com/radius-project/radius/pkg/armrpc/frontend/controller" armrpc_rest "github.com/radius-project/radius/pkg/armrpc/rest" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/datamodel/converter" "github.com/radius-project/radius/pkg/ucp/frontend/controller/credentials" "github.com/radius-project/radius/pkg/ucp/secret" - "github.com/radius-project/radius/pkg/ucp/store" "github.com/radius-project/radius/pkg/ucp/ucplog" ) @@ -83,8 +83,8 @@ func (c *DeleteAzureCredential) Run(ctx context.Context, w http.ResponseWriter, return r, err } - if err := c.StorageClient().Delete(ctx, serviceCtx.ResourceID.String()); err != nil { - if errors.Is(&store.ErrNotFound{ID: serviceCtx.ResourceID.String()}, err) { + if err := c.DatabaseClient().Delete(ctx, serviceCtx.ResourceID.String()); err != nil { + if errors.Is(&database.ErrNotFound{ID: serviceCtx.ResourceID.String()}, err) { return armrpc_rest.NewNoContentResponse(), nil } return nil, err diff --git a/pkg/ucp/frontend/controller/credentials/azure/deleteazurecredential_test.go b/pkg/ucp/frontend/controller/credentials/azure/deleteazurecredential_test.go index ff66ff221a..046c54d985 100644 --- a/pkg/ucp/frontend/controller/credentials/azure/deleteazurecredential_test.go +++ b/pkg/ucp/frontend/controller/credentials/azure/deleteazurecredential_test.go @@ -25,9 +25,9 @@ import ( armrpc_controller "github.com/radius-project/radius/pkg/armrpc/frontend/controller" armrpc_rest "github.com/radius-project/radius/pkg/armrpc/rest" "github.com/radius-project/radius/pkg/armrpc/rpctest" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/secret" - "github.com/radius-project/radius/pkg/ucp/store" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" ) @@ -35,11 +35,11 @@ import ( func Test_Credential_Delete(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() - mockStorageClient := store.NewMockStorageClient(mockCtrl) + mockDatabaseClient := database.NewMockClient(mockCtrl) mockSecretClient := secret.NewMockClient(mockCtrl) credentialCtrl, err := NewDeleteAzureCredential(armrpc_controller.Options{ - StorageClient: mockStorageClient, + DatabaseClient: mockDatabaseClient, }, mockSecretClient) require.NoError(t, err) @@ -47,7 +47,7 @@ func Test_Credential_Delete(t *testing.T) { name string url string headerfile string - fn func(mockStorageClient store.MockStorageClient, mockSecretClient secret.MockClient) + fn func(mockDatabaseClient database.MockClient, mockSecretClient secret.MockClient) expected armrpc_rest.Response err error }{ @@ -111,7 +111,7 @@ func Test_Credential_Delete(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - tt.fn(*mockStorageClient, *mockSecretClient) + tt.fn(*mockDatabaseClient, *mockSecretClient) request, err := rpctest.NewHTTPRequestFromJSON(context.Background(), http.MethodDelete, tt.headerfile, nil) require.NoError(t, err) @@ -128,7 +128,7 @@ func Test_Credential_Delete(t *testing.T) { } } -func setupCredentialMocks(mockStorageClient store.MockStorageClient) { +func setupCredentialMocks(mockDatabaseClient database.MockClient) { datamodelCredential := datamodel.AzureCredential{ BaseResource: v1.BaseResource{}, Properties: &datamodel.AzureCredentialResourceProperties{ @@ -136,10 +136,10 @@ func setupCredentialMocks(mockStorageClient store.MockStorageClient) { }, } - mockStorageClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, id string, options ...store.GetOptions) (*store.Object, error) { - return &store.Object{ - Metadata: store.Metadata{ + mockDatabaseClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()). + DoAndReturn(func(ctx context.Context, id string, options ...database.GetOptions) (*database.Object, error) { + return &database.Object{ + Metadata: database.Metadata{ ID: datamodelCredential.TrackedResource.ID, }, Data: &datamodelCredential, @@ -147,42 +147,42 @@ func setupCredentialMocks(mockStorageClient store.MockStorageClient) { }).Times(1) } -func setupCredentialDeleteSuccessMocks(mockStorageClient store.MockStorageClient, mockSecretClient secret.MockClient) { - setupCredentialMocks(mockStorageClient) +func setupCredentialDeleteSuccessMocks(mockDatabaseClient database.MockClient, mockSecretClient secret.MockClient) { + setupCredentialMocks(mockDatabaseClient) mockSecretClient.EXPECT().Delete(gomock.Any(), gomock.Any()).Return(nil).Times(1) - mockStorageClient.EXPECT().Delete(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1) + mockDatabaseClient.EXPECT().Delete(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1) } -func setupNonExistentCredentialDeleteMocks(mockStorageClient store.MockStorageClient, mockSecretClient secret.MockClient) { - mockStorageClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, &store.ErrNotFound{}).Times(1) +func setupNonExistentCredentialDeleteMocks(mockDatabaseClient database.MockClient, mockSecretClient secret.MockClient) { + mockDatabaseClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, &database.ErrNotFound{}).Times(1) } -func setupCredentialExistenceCheckFailureMocks(mockStorageClient store.MockStorageClient, mockSecretClient secret.MockClient) { - mockStorageClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errors.New("test_failure")).Times(1) +func setupCredentialExistenceCheckFailureMocks(mockDatabaseClient database.MockClient, mockSecretClient secret.MockClient) { + mockDatabaseClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errors.New("test_failure")).Times(1) } -func setupNonExistentSecretDeleteMocks(mockStorageClient store.MockStorageClient, mockSecretClient secret.MockClient) { - setupCredentialMocks(mockStorageClient) +func setupNonExistentSecretDeleteMocks(mockDatabaseClient database.MockClient, mockSecretClient secret.MockClient) { + setupCredentialMocks(mockDatabaseClient) mockSecretClient.EXPECT().Delete(gomock.Any(), gomock.Any()).Return(&secret.ErrNotFound{}).Times(1) } -func setupSecretDeleteFailureMocks(mockStorageClient store.MockStorageClient, mockSecretClient secret.MockClient) { - setupCredentialMocks(mockStorageClient) +func setupSecretDeleteFailureMocks(mockDatabaseClient database.MockClient, mockSecretClient secret.MockClient) { + setupCredentialMocks(mockDatabaseClient) mockSecretClient.EXPECT().Delete(gomock.Any(), gomock.Any()).Return(errors.New("Failed secret deletion")).Times(1) } -func setupNonExistingCredentialDeleteFromStorageMocks(mockStorageClient store.MockStorageClient, mockSecretClient secret.MockClient) { - setupCredentialMocks(mockStorageClient) +func setupNonExistingCredentialDeleteFromStorageMocks(mockDatabaseClient database.MockClient, mockSecretClient secret.MockClient) { + setupCredentialMocks(mockDatabaseClient) mockSecretClient.EXPECT().Delete(gomock.Any(), gomock.Any()).Return(nil).Times(1) - mockStorageClient.EXPECT().Delete(gomock.Any(), gomock.Any(), gomock.Any()).Return(&store.ErrNotFound{}).Times(1) + mockDatabaseClient.EXPECT().Delete(gomock.Any(), gomock.Any(), gomock.Any()).Return(&database.ErrNotFound{}).Times(1) } -func setupFailedCredentialDeleteFromStorageMocks(mockStorageClient store.MockStorageClient, mockSecretClient secret.MockClient) { - setupCredentialMocks(mockStorageClient) +func setupFailedCredentialDeleteFromStorageMocks(mockDatabaseClient database.MockClient, mockSecretClient secret.MockClient) { + setupCredentialMocks(mockDatabaseClient) mockSecretClient.EXPECT().Delete(gomock.Any(), gomock.Any()).Return(nil).Times(1) - mockStorageClient.EXPECT().Delete(gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("Failed Storage Deletion")).Times(1) + mockDatabaseClient.EXPECT().Delete(gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("Failed Storage Deletion")).Times(1) } diff --git a/pkg/ucp/frontend/controller/planes/listplanes.go b/pkg/ucp/frontend/controller/planes/listplanes.go index 25592d705d..a90a1c9e5f 100644 --- a/pkg/ucp/frontend/controller/planes/listplanes.go +++ b/pkg/ucp/frontend/controller/planes/listplanes.go @@ -23,9 +23,9 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" armrpc_controller "github.com/radius-project/radius/pkg/armrpc/frontend/controller" armrpc_rest "github.com/radius-project/radius/pkg/armrpc/rest" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/datamodel/converter" - "github.com/radius-project/radius/pkg/ucp/store" "github.com/radius-project/radius/pkg/ucp/ucplog" ) @@ -48,7 +48,7 @@ func NewListPlanes(opts armrpc_controller.Options) (armrpc_controller.Controller }, nil } -// Run() queries the storage client for planes in a given scope, creates a response with the results, and +// Run() queries the database client for planes in a given scope, creates a response with the results, and // returns an OKResponse with the response. If an error occurs, it is returned. func (e *ListPlanes) Run(ctx context.Context, w http.ResponseWriter, req *http.Request) (armrpc_rest.Response, error) { serviceCtx := v1.ARMRequestContextFromContext(ctx) @@ -61,16 +61,16 @@ func (e *ListPlanes) Run(ctx context.Context, w http.ResponseWriter, req *http.R "radius", } - objs := []store.Object{} + objs := []database.Object{} for _, planeType := range planeTypes { - query := store.Query{ + query := database.Query{ RootScope: serviceCtx.ResourceID.String(), ResourceType: planeType, IsScopeQuery: true, } logger.Info(fmt.Sprintf("Listing planes of type %s in scope %s", query.ResourceType, query.RootScope)) - result, err := e.StorageClient().Query(ctx, query) + result, err := e.DatabaseClient().Query(ctx, query) if err != nil { return nil, err } @@ -86,7 +86,7 @@ func (e *ListPlanes) Run(ctx context.Context, w http.ResponseWriter, req *http.R return ok, nil } -func (p *ListPlanes) createResponse(ctx context.Context, objs []store.Object) (*v1.PaginatedList, error) { +func (p *ListPlanes) createResponse(ctx context.Context, objs []database.Object) (*v1.PaginatedList, error) { serviceCtx := v1.ARMRequestContextFromContext(ctx) items := v1.PaginatedList{} diff --git a/pkg/ucp/frontend/controller/planes/listplanes_test.go b/pkg/ucp/frontend/controller/planes/listplanes_test.go index 505a3cd320..aac6c6d086 100644 --- a/pkg/ucp/frontend/controller/planes/listplanes_test.go +++ b/pkg/ucp/frontend/controller/planes/listplanes_test.go @@ -25,8 +25,8 @@ import ( "github.com/radius-project/radius/pkg/armrpc/rpctest" "github.com/radius-project/radius/pkg/to" "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/datamodel" - "github.com/radius-project/radius/pkg/ucp/store" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" ) @@ -34,9 +34,9 @@ import ( func Test_ListPlanes(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() - mockStorageClient := store.NewMockStorageClient(mockCtrl) + mockDatabaseClient := database.NewMockClient(mockCtrl) - planesCtrl, err := NewListPlanes(armrpc_controller.Options{StorageClient: mockStorageClient}) + planesCtrl, err := NewListPlanes(armrpc_controller.Options{DatabaseClient: mockDatabaseClient}) require.NoError(t, err) url := "/planes?api-version=2023-10-01-preview" @@ -57,30 +57,30 @@ func Test_ListPlanes(t *testing.T) { Properties: datamodel.AWSPlaneProperties{}, } - mockStorageClient.EXPECT().Query(gomock.Any(), store.Query{ + mockDatabaseClient.EXPECT().Query(gomock.Any(), database.Query{ RootScope: "/planes", ResourceType: "aws", IsScopeQuery: true, - }).Return(&store.ObjectQueryResult{ - Items: []store.Object{ + }).Return(&database.ObjectQueryResult{ + Items: []database.Object{ { - Metadata: store.Metadata{}, + Metadata: database.Metadata{}, Data: &planeData, }, }, }, nil) - mockStorageClient.EXPECT().Query(gomock.Any(), store.Query{ + mockDatabaseClient.EXPECT().Query(gomock.Any(), database.Query{ RootScope: "/planes", ResourceType: "azure", IsScopeQuery: true, - }).Return(&store.ObjectQueryResult{}, nil) + }).Return(&database.ObjectQueryResult{}, nil) - mockStorageClient.EXPECT().Query(gomock.Any(), store.Query{ + mockDatabaseClient.EXPECT().Query(gomock.Any(), database.Query{ RootScope: "/planes", ResourceType: "radius", IsScopeQuery: true, - }).Return(&store.ObjectQueryResult{}, nil) + }).Return(&database.ObjectQueryResult{}, nil) request, err := http.NewRequest(http.MethodGet, url, nil) require.NoError(t, err) diff --git a/pkg/ucp/frontend/controller/planes/listplanesbytype.go b/pkg/ucp/frontend/controller/planes/listplanesbytype.go index 69ca21f49d..c5dda15396 100644 --- a/pkg/ucp/frontend/controller/planes/listplanesbytype.go +++ b/pkg/ucp/frontend/controller/planes/listplanesbytype.go @@ -26,9 +26,9 @@ import ( armrpc_controller "github.com/radius-project/radius/pkg/armrpc/frontend/controller" armrpc_rest "github.com/radius-project/radius/pkg/armrpc/rest" "github.com/radius-project/radius/pkg/middleware" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/store" "github.com/radius-project/radius/pkg/ucp/ucplog" ) @@ -42,20 +42,20 @@ type ListPlanesByType[P interface { armrpc_controller.Operation[P, T] } -// ListPlanesByType takes in a request object and returns a list of planes of a given type from the storage client. If +// ListPlanesByType takes in a request object and returns a list of planes of a given type from the database client. If // an error occurs, it returns an error. func (e *ListPlanesByType[P, T]) Run(ctx context.Context, w http.ResponseWriter, req *http.Request) (armrpc_rest.Response, error) { path := middleware.GetRelativePath(e.Options().PathBase, req.URL.Path) // The path is /planes/{planeType} planeType := strings.Split(path, resources.SegmentSeparator)[2] - query := store.Query{ + query := database.Query{ RootScope: resources.SegmentSeparator + resources.PlanesSegment, IsScopeQuery: true, ResourceType: planeType, } logger := ucplog.FromContextOrDiscard(ctx) logger.Info(fmt.Sprintf("Listing planes in scope %s/%s", query.RootScope, planeType)) - result, err := e.StorageClient().Query(ctx, query) + result, err := e.DatabaseClient().Query(ctx, query) if err != nil { return nil, err } @@ -67,7 +67,7 @@ func (e *ListPlanesByType[P, T]) Run(ctx context.Context, w http.ResponseWriter, return ok, nil } -func (p *ListPlanesByType[P, T]) createResponse(ctx context.Context, result *store.ObjectQueryResult) (*v1.PaginatedList, error) { +func (p *ListPlanesByType[P, T]) createResponse(ctx context.Context, result *database.ObjectQueryResult) (*v1.PaginatedList, error) { serviceCtx := v1.ARMRequestContextFromContext(ctx) items := v1.PaginatedList{} diff --git a/pkg/ucp/frontend/controller/planes/listplanesbytype_test.go b/pkg/ucp/frontend/controller/planes/listplanesbytype_test.go index 58bf997eed..e426a6255b 100644 --- a/pkg/ucp/frontend/controller/planes/listplanesbytype_test.go +++ b/pkg/ucp/frontend/controller/planes/listplanesbytype_test.go @@ -25,9 +25,9 @@ import ( "github.com/radius-project/radius/pkg/armrpc/rpctest" "github.com/radius-project/radius/pkg/to" "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/datamodel/converter" - "github.com/radius-project/radius/pkg/ucp/store" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" ) @@ -35,11 +35,11 @@ import ( func Test_ListPlanesByType(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() - mockStorageClient := store.NewMockStorageClient(mockCtrl) + mockDatabaseClient := database.NewMockClient(mockCtrl) ctrl := &ListPlanesByType[*datamodel.RadiusPlane, datamodel.RadiusPlane]{ Operation: armrpc_controller.NewOperation[*datamodel.RadiusPlane, datamodel.RadiusPlane]( - armrpc_controller.Options{StorageClient: mockStorageClient}, + armrpc_controller.Options{DatabaseClient: mockDatabaseClient}, armrpc_controller.ResourceOptions[datamodel.RadiusPlane]{ ResponseConverter: converter.RadiusPlaneDataModelToVersioned, }), @@ -47,7 +47,7 @@ func Test_ListPlanesByType(t *testing.T) { url := "/planes/radius?api-version=2023-10-01-preview" - query := store.Query{ + query := database.Query{ RootScope: "/planes", IsScopeQuery: true, ResourceType: "radius", @@ -73,10 +73,10 @@ func Test_ListPlanesByType(t *testing.T) { }, } - mockStorageClient.EXPECT().Query(gomock.Any(), query).Return(&store.ObjectQueryResult{ - Items: []store.Object{ + mockDatabaseClient.EXPECT().Query(gomock.Any(), query).Return(&database.ObjectQueryResult{ + Items: []database.Object{ { - Metadata: store.Metadata{}, + Metadata: database.Metadata{}, Data: &planeData, }, }, diff --git a/pkg/ucp/frontend/controller/radius/proxy.go b/pkg/ucp/frontend/controller/radius/proxy.go index 84cc7360a2..25fff5ea23 100644 --- a/pkg/ucp/frontend/controller/radius/proxy.go +++ b/pkg/ucp/frontend/controller/radius/proxy.go @@ -31,11 +31,11 @@ import ( armrpc_controller "github.com/radius-project/radius/pkg/armrpc/frontend/controller" armrpc_rest "github.com/radius-project/radius/pkg/armrpc/rest" "github.com/radius-project/radius/pkg/middleware" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/frontend/controller/resourcegroups" "github.com/radius-project/radius/pkg/ucp/proxy" "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/store" "github.com/radius-project/radius/pkg/ucp/trackedresource" "github.com/radius-project/radius/pkg/ucp/ucplog" ) @@ -82,7 +82,7 @@ func NewProxyController(opts armrpc_controller.Options, transport http.RoundTrip return nil, fmt.Errorf("failed to parse default downstream URL: %w", err) } - updater := trackedresource.NewUpdater(opts.StorageClient, &http.Client{Transport: transport}) + updater := trackedresource.NewUpdater(opts.DatabaseClient, &http.Client{Transport: transport}) return &ProxyController{ Operation: armrpc_controller.NewOperation(opts, armrpc_controller.ResourceOptions[datamodel.RadiusPlane]{}), transport: transport, @@ -113,7 +113,7 @@ func (p *ProxyController) Run(ctx context.Context, w http.ResponseWriter, req *h return armrpc_rest.NewBadRequestARMResponse(response), nil } - downstreamURL, err := resourcegroups.ValidateDownstream(ctx, p.StorageClient(), id, v1.LocationGlobal, apiVersion) + downstreamURL, err := resourcegroups.ValidateDownstream(ctx, p.DatabaseClient(), id, v1.LocationGlobal, apiVersion) if errors.Is(err, &resourcegroups.NotFoundError{}) { return armrpc_rest.NewNotFoundResponseWithCause(id, err.Error()), nil } else if errors.Is(err, &resourcegroups.InvalidError{}) { @@ -279,8 +279,8 @@ func (p *ProxyController) EnqueueTrackedResourceUpdate(ctx context.Context, id r queueOperation := false retry: for retryCount := 1; retryCount <= EnqueueOperationRetryCount; retryCount++ { - obj, err := p.StorageClient().Get(ctx, trackingID.String()) - if errors.Is(err, &store.ErrNotFound{}) { + obj, err := p.DatabaseClient().Get(ctx, trackingID.String()) + if errors.Is(err, &database.ErrNotFound{}) { // Safe to ignore. This means that the resource has not been tracked yet. } else if err != nil { return err @@ -302,8 +302,8 @@ retry: } logger.V(ucplog.LevelDebug).Info("enqueuing tracked resource update") - err = p.StorageClient().Save(ctx, &store.Object{Metadata: store.Metadata{ID: trackingID.String()}, Data: entry}, store.WithETag(etag)) - if errors.Is(err, &store.ErrConcurrency{}) { + err = p.DatabaseClient().Save(ctx, &database.Object{Metadata: database.Metadata{ID: trackingID.String()}, Data: entry}, database.WithETag(etag)) + if errors.Is(err, &database.ErrConcurrency{}) { // This means we hit a concurrency error saving the tracked resource entry. This means that the resource // was updated in the background. We should retry. logger.V(ucplog.LevelDebug).Info("enqueue tracked resource update failed due to concurrency error", "retryCount", retryCount) diff --git a/pkg/ucp/frontend/controller/radius/proxy_test.go b/pkg/ucp/frontend/controller/radius/proxy_test.go index 8e51472637..aeb5d4d4d5 100644 --- a/pkg/ucp/frontend/controller/radius/proxy_test.go +++ b/pkg/ucp/frontend/controller/radius/proxy_test.go @@ -28,9 +28,9 @@ import ( "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rest" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/store" "github.com/radius-project/radius/pkg/ucp/trackedresource" "github.com/radius-project/radius/test/testcontext" "github.com/stretchr/testify/require" @@ -44,15 +44,15 @@ const ( // The Run function is also tested by integration tests in the pkg/ucp/integrationtests/radius package. -func createController(t *testing.T) (*ProxyController, *store.MockStorageClient, *mockUpdater, *mockRoundTripper, *statusmanager.MockStatusManager) { +func createController(t *testing.T) (*ProxyController, *database.MockClient, *mockUpdater, *mockRoundTripper, *statusmanager.MockStatusManager) { ctrl := gomock.NewController(t) - storageClient := store.NewMockStorageClient(ctrl) + databaseClient := database.NewMockClient(ctrl) statusManager := statusmanager.NewMockStatusManager(ctrl) roundTripper := mockRoundTripper{} p, err := NewProxyController( - controller.Options{StorageClient: storageClient, StatusManager: statusManager}, + controller.Options{DatabaseClient: databaseClient, StatusManager: statusManager}, &roundTripper, "http://localhost:1234") require.NoError(t, err) @@ -62,7 +62,7 @@ func createController(t *testing.T) (*ProxyController, *store.MockStorageClient, pc := p.(*ProxyController) pc.updater = &updater - return pc, storageClient, &updater, &roundTripper, statusManager + return pc, databaseClient, &updater, &roundTripper, statusManager } func Test_Run(t *testing.T) { @@ -83,7 +83,7 @@ func Test_Run(t *testing.T) { resourceGroup := datamodel.ResourceGroup{} t.Run("success (non-tracked)", func(t *testing.T) { - p, storageClient, _, roundTripper, _ := createController(t) + p, databaseClient, _, roundTripper, _ := createController(t) svcContext := &v1.ARMRequestContext{ APIVersion: apiVersion, @@ -97,17 +97,17 @@ func Test_Run(t *testing.T) { // Not a mutating request req := httptest.NewRequest(http.MethodGet, id.String()+"?api-version="+apiVersion, nil) - storageClient.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), resourceTypeID.String(), gomock.Any()). - Return(nil, &store.ErrNotFound{}).Times(1) + Return(nil, &database.ErrNotFound{}).Times(1) - storageClient.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), "/planes/"+id.PlaneNamespace(), gomock.Any()). - Return(&store.Object{Data: plane}, nil).Times(1) + Return(&database.Object{Data: plane}, nil).Times(1) - storageClient.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), id.RootScope(), gomock.Any()). - Return(&store.Object{Data: resourceGroup}, nil).Times(1) + Return(&database.Object{Data: resourceGroup}, nil).Times(1) downstreamResponse := httptest.NewRecorder() downstreamResponse.WriteHeader(http.StatusOK) @@ -119,7 +119,7 @@ func Test_Run(t *testing.T) { }) t.Run("success (tracked terminal response)", func(t *testing.T) { - p, storageClient, updater, roundTripper, _ := createController(t) + p, databaseClient, updater, roundTripper, _ := createController(t) svcContext := &v1.ARMRequestContext{ APIVersion: apiVersion, @@ -133,17 +133,17 @@ func Test_Run(t *testing.T) { // Mutating request that will complete synchronously req := httptest.NewRequest(http.MethodDelete, id.String()+"?api-version="+apiVersion, nil) - storageClient.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), resourceTypeID.String(), gomock.Any()). - Return(nil, &store.ErrNotFound{}).Times(1) + Return(nil, &database.ErrNotFound{}).Times(1) - storageClient.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), "/planes/"+id.PlaneNamespace(), gomock.Any()). - Return(&store.Object{Data: plane}, nil).Times(1) + Return(&database.Object{Data: plane}, nil).Times(1) - storageClient.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), id.RootScope(), gomock.Any()). - Return(&store.Object{Data: resourceGroup}, nil).Times(1) + Return(&database.Object{Data: resourceGroup}, nil).Times(1) downstreamResponse := httptest.NewRecorder() downstreamResponse.WriteHeader(http.StatusOK) @@ -158,7 +158,7 @@ func Test_Run(t *testing.T) { }) t.Run("success (fallback to async)", func(t *testing.T) { - p, storageClient, updater, roundTripper, statusManager := createController(t) + p, databaseClient, updater, roundTripper, statusManager := createController(t) svcContext := &v1.ARMRequestContext{ APIVersion: apiVersion, @@ -172,23 +172,23 @@ func Test_Run(t *testing.T) { // Mutating request that will complete synchronously req := httptest.NewRequest(http.MethodDelete, id.String()+"?api-version="+apiVersion, nil) - storageClient.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), resourceTypeID.String(), gomock.Any()). - Return(nil, &store.ErrNotFound{}).Times(1) + Return(nil, &database.ErrNotFound{}).Times(1) - storageClient.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), "/planes/"+id.PlaneNamespace(), gomock.Any()). - Return(&store.Object{Data: plane}, nil).Times(1) + Return(&database.Object{Data: plane}, nil).Times(1) - storageClient.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), id.RootScope(), gomock.Any()). - Return(&store.Object{Data: resourceGroup}, nil).Times(1) + Return(&database.Object{Data: resourceGroup}, nil).Times(1) // Tracking entry created - storageClient.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), gomock.Any(), gomock.Any()). - Return(nil, &store.ErrNotFound{}).Times(1) - storageClient.EXPECT(). + Return(nil, &database.ErrNotFound{}).Times(1) + databaseClient.EXPECT(). Save(gomock.Any(), gomock.Any(), gomock.Any()). Return(nil).Times(1) @@ -209,7 +209,7 @@ func Test_Run(t *testing.T) { }) t.Run("success (fallback to async without workitem)", func(t *testing.T) { - p, storageClient, updater, roundTripper, _ := createController(t) + p, databaseClient, updater, roundTripper, _ := createController(t) svcContext := &v1.ARMRequestContext{ APIVersion: apiVersion, @@ -223,20 +223,20 @@ func Test_Run(t *testing.T) { // Mutating request that will complete asynchronously req := httptest.NewRequest(http.MethodDelete, id.String()+"?api-version="+apiVersion, nil) - storageClient.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), resourceTypeID.String(), gomock.Any()). - Return(nil, &store.ErrNotFound{}).Times(1) + Return(nil, &database.ErrNotFound{}).Times(1) - storageClient.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), "/planes/"+id.PlaneNamespace(), gomock.Any()). - Return(&store.Object{Data: plane}, nil).Times(1) + Return(&database.Object{Data: plane}, nil).Times(1) - storageClient.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), id.RootScope(), gomock.Any()). - Return(&store.Object{Data: resourceGroup}, nil).Times(1) + Return(&database.Object{Data: resourceGroup}, nil).Times(1) // Tracking entry created - existingEntry := &store.Object{ + existingEntry := &database.Object{ Data: &datamodel.GenericResource{ BaseResource: v1.BaseResource{ InternalMetadata: v1.InternalMetadata{ @@ -245,10 +245,10 @@ func Test_Run(t *testing.T) { }, }, } - storageClient.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), gomock.Any(), gomock.Any()). Return(existingEntry, nil).Times(1) - storageClient.EXPECT(). + databaseClient.EXPECT(). Save(gomock.Any(), gomock.Any(), gomock.Any()). Return(nil).Times(1) @@ -267,7 +267,7 @@ func Test_Run(t *testing.T) { }) t.Run("failure (validate downstream: not found)", func(t *testing.T) { - p, storageClient, _, _, _ := createController(t) + p, databaseClient, _, _, _ := createController(t) svcContext := &v1.ARMRequestContext{ APIVersion: apiVersion, @@ -279,9 +279,9 @@ func Test_Run(t *testing.T) { w := httptest.NewRecorder() req := httptest.NewRequest(http.MethodPut, id.String()+"?api-version="+apiVersion, nil) - storageClient.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), "/planes/"+id.PlaneNamespace(), gomock.Any()). - Return(nil, &store.ErrNotFound{}).Times(1) + Return(nil, &database.ErrNotFound{}).Times(1) expected := rest.NewNotFoundResponseWithCause(id, "plane \"/planes/test/local\" not found") diff --git a/pkg/ucp/frontend/controller/resourcegroups/listresourcegroups.go b/pkg/ucp/frontend/controller/resourcegroups/listresourcegroups.go index c4ad1e988c..58cf0dd321 100644 --- a/pkg/ucp/frontend/controller/resourcegroups/listresourcegroups.go +++ b/pkg/ucp/frontend/controller/resourcegroups/listresourcegroups.go @@ -23,10 +23,10 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" armrpc_controller "github.com/radius-project/radius/pkg/armrpc/frontend/controller" armrpc_rest "github.com/radius-project/radius/pkg/armrpc/rest" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/datamodel/converter" "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/store" "github.com/radius-project/radius/pkg/ucp/ucplog" ) @@ -49,7 +49,7 @@ func NewListResourceGroups(opts armrpc_controller.Options) (armrpc_controller.Co }, nil } -// Run() function extracts the plane type and name from the request URL, queries the storage client for resource groups in the +// Run() function extracts the plane type and name from the request URL, queries the database client for resource groups in the // scope of the plane, creates a response with the list of resource groups and returns an OK response with the list of resource groups. func (r *ListResourceGroups) Run(ctx context.Context, w http.ResponseWriter, req *http.Request) (armrpc_rest.Response, error) { logger := ucplog.FromContextOrDiscard(ctx) @@ -60,13 +60,13 @@ func (r *ListResourceGroups) Run(ctx context.Context, w http.ResponseWriter, req return nil, err } - var query store.Query + var query database.Query query.RootScope = resources.SegmentSeparator + resources.PlanesSegment + resources.SegmentSeparator + planeType + resources.SegmentSeparator + planeName query.IsScopeQuery = true query.ResourceType = "resourcegroups" logger.Info(fmt.Sprintf("Listing resource groups in scope %s", query.RootScope)) - result, err := r.StorageClient().Query(ctx, query) + result, err := r.DatabaseClient().Query(ctx, query) if err != nil { return nil, err } @@ -79,7 +79,7 @@ func (r *ListResourceGroups) Run(ctx context.Context, w http.ResponseWriter, req return ok, nil } -func (e *ListResourceGroups) createResponse(ctx context.Context, result *store.ObjectQueryResult) (*v1.PaginatedList, error) { +func (e *ListResourceGroups) createResponse(ctx context.Context, result *database.ObjectQueryResult) (*v1.PaginatedList, error) { items := v1.PaginatedList{} serviceCtx := v1.ARMRequestContextFromContext(ctx) diff --git a/pkg/ucp/frontend/controller/resourcegroups/listresourcegroups_test.go b/pkg/ucp/frontend/controller/resourcegroups/listresourcegroups_test.go index e91a917c8d..ce7b87ce8b 100644 --- a/pkg/ucp/frontend/controller/resourcegroups/listresourcegroups_test.go +++ b/pkg/ucp/frontend/controller/resourcegroups/listresourcegroups_test.go @@ -29,21 +29,21 @@ import ( "github.com/radius-project/radius/pkg/armrpc/rpctest" "github.com/radius-project/radius/pkg/to" "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/datamodel" - "github.com/radius-project/radius/pkg/ucp/store" ) func Test_ListResourceGroups(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() - mockStorageClient := store.NewMockStorageClient(mockCtrl) + mockDatabaseClient := database.NewMockClient(mockCtrl) - rgCtrl, err := NewListResourceGroups(armrpc_controller.Options{StorageClient: mockStorageClient}) + rgCtrl, err := NewListResourceGroups(armrpc_controller.Options{DatabaseClient: mockDatabaseClient}) require.NoError(t, err) url := "/planes/radius/local/resourceGroups?api-version=2023-10-01-preview" - query := store.Query{ + query := database.Query{ RootScope: "/planes/radius/local", IsScopeQuery: true, ResourceType: "resourcegroups", @@ -63,11 +63,11 @@ func Test_ListResourceGroups(t *testing.T) { }, } - mockStorageClient.EXPECT().Query(gomock.Any(), query).DoAndReturn(func(ctx context.Context, query store.Query, options ...store.QueryOptions) (*store.ObjectQueryResult, error) { - return &store.ObjectQueryResult{ - Items: []store.Object{ + mockDatabaseClient.EXPECT().Query(gomock.Any(), query).DoAndReturn(func(ctx context.Context, query database.Query, options ...database.QueryOptions) (*database.ObjectQueryResult, error) { + return &database.ObjectQueryResult{ + Items: []database.Object{ { - Metadata: store.Metadata{}, + Metadata: database.Metadata{}, Data: &rg, }, }, diff --git a/pkg/ucp/frontend/controller/resourcegroups/listresources.go b/pkg/ucp/frontend/controller/resourcegroups/listresources.go index 4cfc82a53c..66a3cb6b66 100644 --- a/pkg/ucp/frontend/controller/resourcegroups/listresources.go +++ b/pkg/ucp/frontend/controller/resourcegroups/listresources.go @@ -25,10 +25,10 @@ import ( armrpc_rest "github.com/radius-project/radius/pkg/armrpc/rest" "github.com/radius-project/radius/pkg/middleware" "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/datamodel/converter" "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/store" ) var _ armrpc_controller.Controller = (*ListResources)(nil) @@ -62,19 +62,19 @@ func (r *ListResources) Run(ctx context.Context, w http.ResponseWriter, req *htt resourceGroupID := id.Truncate() // First check if the resource group exists. - _, err = r.StorageClient().Get(ctx, resourceGroupID.String()) - if errors.Is(err, &store.ErrNotFound{}) { + _, err = r.DatabaseClient().Get(ctx, resourceGroupID.String()) + if errors.Is(err, &database.ErrNotFound{}) { return armrpc_rest.NewNotFoundResponse(id), nil } else if err != nil { return nil, err } - query := store.Query{ + query := database.Query{ RootScope: resourceGroupID.String(), ResourceType: v20231001preview.ResourceType, } - result, err := r.StorageClient().Query(ctx, query) + result, err := r.DatabaseClient().Query(ctx, query) if err != nil { return nil, err } @@ -87,7 +87,7 @@ func (r *ListResources) Run(ctx context.Context, w http.ResponseWriter, req *htt return armrpc_rest.NewOKResponse(response), nil } -func (r *ListResources) createResponse(ctx context.Context, result *store.ObjectQueryResult) (*v1.PaginatedList, error) { +func (r *ListResources) createResponse(ctx context.Context, result *database.ObjectQueryResult) (*v1.PaginatedList, error) { items := v1.PaginatedList{} serviceCtx := v1.ARMRequestContextFromContext(ctx) diff --git a/pkg/ucp/frontend/controller/resourcegroups/listresources_test.go b/pkg/ucp/frontend/controller/resourcegroups/listresources_test.go index c7f77c9934..35e58eb8d8 100644 --- a/pkg/ucp/frontend/controller/resourcegroups/listresources_test.go +++ b/pkg/ucp/frontend/controller/resourcegroups/listresources_test.go @@ -29,9 +29,9 @@ import ( "github.com/radius-project/radius/pkg/armrpc/rpctest" "github.com/radius-project/radius/pkg/to" "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/store" ) func Test_ListResources(t *testing.T) { @@ -62,17 +62,17 @@ func Test_ListResources(t *testing.T) { id := resourceGroupID + "/resources" t.Run("success", func(t *testing.T) { - storage, ctrl := setupListResources(t) + databaseClient, ctrl := setupListResources(t) - storage.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), resourceGroupID). - Return(&store.Object{Data: resourceGroupDatamodel}, nil). + Return(&database.Object{Data: resourceGroupDatamodel}, nil). Times(1) - expectedQuery := store.Query{RootScope: resourceGroupID, ResourceType: v20231001preview.ResourceType} - storage.EXPECT(). + expectedQuery := database.Query{RootScope: resourceGroupID, ResourceType: v20231001preview.ResourceType} + databaseClient.EXPECT(). Query(gomock.Any(), expectedQuery). - Return(&store.ObjectQueryResult{Items: []store.Object{{Data: entryDatamodel}}}, nil). + Return(&database.ObjectQueryResult{Items: []database.Object{{Data: entryDatamodel}}}, nil). Times(1) expected := armrpc_rest.NewOKResponse(&v1.PaginatedList{ @@ -88,17 +88,17 @@ func Test_ListResources(t *testing.T) { }) t.Run("success - empty", func(t *testing.T) { - storage, ctrl := setupListResources(t) + databaseClient, ctrl := setupListResources(t) - storage.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), resourceGroupID). - Return(&store.Object{Data: resourceGroupDatamodel}, nil). + Return(&database.Object{Data: resourceGroupDatamodel}, nil). Times(1) - expectedQuery := store.Query{RootScope: resourceGroupID, ResourceType: v20231001preview.ResourceType} - storage.EXPECT(). + expectedQuery := database.Query{RootScope: resourceGroupID, ResourceType: v20231001preview.ResourceType} + databaseClient.EXPECT(). Query(gomock.Any(), expectedQuery). - Return(&store.ObjectQueryResult{Items: []store.Object{}}, nil). + Return(&database.ObjectQueryResult{Items: []database.Object{}}, nil). Times(1) expected := armrpc_rest.NewOKResponse(&v1.PaginatedList{}) @@ -112,11 +112,11 @@ func Test_ListResources(t *testing.T) { }) t.Run("resource group not found", func(t *testing.T) { - storage, ctrl := setupListResources(t) + databaseClient, ctrl := setupListResources(t) - storage.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), resourceGroupID). - Return(nil, &store.ErrNotFound{ID: resourceGroupID}). + Return(nil, &database.ErrNotFound{ID: resourceGroupID}). Times(1) parsed, err := resources.Parse(id) @@ -133,12 +133,12 @@ func Test_ListResources(t *testing.T) { }) } -func setupListResources(t *testing.T) (*store.MockStorageClient, *ListResources) { +func setupListResources(t *testing.T) (*database.MockClient, *ListResources) { ctrl := gomock.NewController(t) - storage := store.NewMockStorageClient(ctrl) + databaseClient := database.NewMockClient(ctrl) - c, err := NewListResources(armrpc_controller.Options{StorageClient: storage, PathBase: "/" + uuid.New().String()}) + c, err := NewListResources(armrpc_controller.Options{DatabaseClient: databaseClient, PathBase: "/" + uuid.New().String()}) require.NoError(t, err) - return storage, c.(*ListResources) + return databaseClient, c.(*ListResources) } diff --git a/pkg/ucp/frontend/controller/resourcegroups/util.go b/pkg/ucp/frontend/controller/resourcegroups/util.go index a1d5f8c507..4edbef9f96 100644 --- a/pkg/ucp/frontend/controller/resourcegroups/util.go +++ b/pkg/ucp/frontend/controller/resourcegroups/util.go @@ -23,10 +23,10 @@ import ( "net/url" "strings" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/resources" resources_radius "github.com/radius-project/radius/pkg/ucp/resources/radius" - "github.com/radius-project/radius/pkg/ucp/store" ) // NotFoundError is returned when a resource group or plane is not found. @@ -62,15 +62,15 @@ func (e *InvalidError) Is(err error) bool { } // ValidateRadiusPlane validates that the plane specified in the id exists. Returns NotFoundError if the plane does not exist. -func ValidateRadiusPlane(ctx context.Context, client store.StorageClient, id resources.ID) (*datamodel.RadiusPlane, error) { +func ValidateRadiusPlane(ctx context.Context, client database.Client, id resources.ID) (*datamodel.RadiusPlane, error) { planeID, err := resources.ParseScope(id.PlaneScope()) if err != nil { // Not expected to happen. return nil, err } - plane, err := store.GetResource[datamodel.RadiusPlane](ctx, client, planeID.String()) - if errors.Is(err, &store.ErrNotFound{}) { + plane, err := database.GetResource[datamodel.RadiusPlane](ctx, client, planeID.String()) + if errors.Is(err, &database.ErrNotFound{}) { return nil, &NotFoundError{Message: fmt.Sprintf("plane %q not found", planeID.String())} } else if err != nil { return nil, fmt.Errorf("failed to fetch plane %q: %w", planeID.String(), err) @@ -81,7 +81,7 @@ func ValidateRadiusPlane(ctx context.Context, client store.StorageClient, id res // ValidateResourceGroup validates that the resource group specified in the id exists (if applicable). // Returns NotFoundError if the resource group does not exist. -func ValidateResourceGroup(ctx context.Context, client store.StorageClient, id resources.ID) error { +func ValidateResourceGroup(ctx context.Context, client database.Client, id resources.ID) error { // If the ID contains a resource group, validate it now. if id.FindScope(resources_radius.ScopeResourceGroups) == "" { return nil @@ -93,8 +93,8 @@ func ValidateResourceGroup(ctx context.Context, client store.StorageClient, id r return err } - _, err = store.GetResource[datamodel.ResourceGroup](ctx, client, resourceGroupID.String()) - if errors.Is(err, &store.ErrNotFound{}) { + _, err = database.GetResource[datamodel.ResourceGroup](ctx, client, resourceGroupID.String()) + if errors.Is(err, &database.ErrNotFound{}) { return &NotFoundError{Message: fmt.Sprintf("resource group %q not found", resourceGroupID.String())} } else if err != nil { return fmt.Errorf("failed to fetch resource group %q: %w", resourceGroupID.String(), err) @@ -108,7 +108,7 @@ func ValidateResourceGroup(ctx context.Context, client store.StorageClient, id r // // Returns NotFoundError if the resource type does not exist. // Returns InvalidError if the request cannot be routed due to an invalid configuration. -func ValidateResourceType(ctx context.Context, client store.StorageClient, id resources.ID, locationName string, apiVersion string) (*url.URL, error) { +func ValidateResourceType(ctx context.Context, client database.Client, id resources.ID, locationName string, apiVersion string) (*url.URL, error) { // The strategy is to: // - Look up the resource type and validate that it exists .. then // - Look up the location resource, and validate that it supports the requested resource type and API version. @@ -121,8 +121,8 @@ func ValidateResourceType(ctx context.Context, client store.StorageClient, id re return nil, err } - _, err = store.GetResource[datamodel.ResourceType](ctx, client, resourceTypeID.String()) - if errors.Is(err, &store.ErrNotFound{}) { + _, err = database.GetResource[datamodel.ResourceType](ctx, client, resourceTypeID.String()) + if errors.Is(err, &database.ErrNotFound{}) { // Return the error as-is to fallback to the legacy routing behavior. return nil, err @@ -139,8 +139,8 @@ func ValidateResourceType(ctx context.Context, client store.StorageClient, id re return nil, err } - location, err := store.GetResource[datamodel.Location](ctx, client, locationID.String()) - if errors.Is(err, &store.ErrNotFound{}) { + location, err := database.GetResource[datamodel.Location](ctx, client, locationID.String()) + if errors.Is(err, &database.ErrNotFound{}) { // Return the error as-is to fallback to the legacy routing behavior. return nil, err @@ -228,7 +228,7 @@ func isOperationResourceType(id resources.ID) bool { // ValidateLegacyResourceProvider validates that the resource provider specified in the id exists. Returns InvalidError if the plane // contains invalid data. -func ValidateLegacyResourceProvider(ctx context.Context, client store.StorageClient, id resources.ID, plane *datamodel.RadiusPlane) (*url.URL, error) { +func ValidateLegacyResourceProvider(ctx context.Context, client database.Client, id resources.ID, plane *datamodel.RadiusPlane) (*url.URL, error) { downstream := plane.LookupResourceProvider(id.ProviderNamespace()) if downstream == "" { return nil, &InvalidError{Message: fmt.Sprintf("resource provider %s not configured", id.ProviderNamespace())} @@ -245,7 +245,7 @@ func ValidateLegacyResourceProvider(ctx context.Context, client store.StorageCli // ValidateDownstream can be used to find and validate the downstream URL for a resource. // Returns NotFoundError for the case where the plane or resource group does not exist. // Returns InvalidError for cases where the data is invalid, like when the resource provider is not configured. -func ValidateDownstream(ctx context.Context, client store.StorageClient, id resources.ID, location string, apiVersion string) (*url.URL, error) { +func ValidateDownstream(ctx context.Context, client database.Client, id resources.ID, location string, apiVersion string) (*url.URL, error) { // There are a few steps to validation: // // - The plane exists @@ -269,7 +269,7 @@ func ValidateDownstream(ctx context.Context, client store.StorageClient, id reso // If this returns success, it means the resource type is configured using new/UDT routing. downstreamURL, err := ValidateResourceType(ctx, client, id, location, apiVersion) - if errors.Is(err, &store.ErrNotFound{}) { + if errors.Is(err, &database.ErrNotFound{}) { // If the resource provider is not found, treat it like a legacy provider. return ValidateLegacyResourceProvider(ctx, client, id, plane) } else if err != nil { diff --git a/pkg/ucp/frontend/controller/resourcegroups/util_test.go b/pkg/ucp/frontend/controller/resourcegroups/util_test.go index f266458196..7def6c8b60 100644 --- a/pkg/ucp/frontend/controller/resourcegroups/util_test.go +++ b/pkg/ucp/frontend/controller/resourcegroups/util_test.go @@ -23,9 +23,9 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" "github.com/radius-project/radius/pkg/to" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/store" "github.com/radius-project/radius/test/testcontext" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -93,9 +93,9 @@ func Test_ValidateDownstream(t *testing.T) { }, } - setup := func(t *testing.T) *store.MockStorageClient { + setup := func(t *testing.T) *database.MockClient { ctrl := gomock.NewController(t) - return store.NewMockStorageClient(ctrl) + return database.NewMockClient(ctrl) } t.Run("success (resource group)", func(t *testing.T) { @@ -108,10 +108,10 @@ func Test_ValidateDownstream(t *testing.T) { } mock := setup(t) - mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&store.Object{Data: plane}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&store.Object{Data: resourceGroup}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), resourceTypeResource.ID).Return(&store.Object{Data: resourceTypeResource}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), locationResource.ID).Return(&store.Object{Data: locationResource}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&database.Object{Data: resourceGroup}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), resourceTypeResource.ID).Return(&database.Object{Data: resourceTypeResource}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), locationResource.ID).Return(&database.Object{Data: locationResource}, nil).Times(1) expectedURL, err := url.Parse(downstream) require.NoError(t, err) @@ -123,9 +123,9 @@ func Test_ValidateDownstream(t *testing.T) { t.Run("success (non resource group)", func(t *testing.T) { mock := setup(t) - mock.EXPECT().Get(gomock.Any(), idWithoutResourceGroup.PlaneScope()).Return(&store.Object{Data: plane}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), resourceTypeResource.ID).Return(&store.Object{Data: resourceTypeResource}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), locationResource.ID).Return(&store.Object{Data: locationResource}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), idWithoutResourceGroup.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), resourceTypeResource.ID).Return(&database.Object{Data: resourceTypeResource}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), locationResource.ID).Return(&database.Object{Data: locationResource}, nil).Times(1) expectedURL, err := url.Parse(downstream) require.NoError(t, err) @@ -138,8 +138,8 @@ func Test_ValidateDownstream(t *testing.T) { // The deployment engine models its operation status resources as child resources of the deployment resource. t.Run("success (operationstatuses as child resource)", func(t *testing.T) { mock := setup(t) - mock.EXPECT().Get(gomock.Any(), idWithoutResourceGroup.PlaneScope()).Return(&store.Object{Data: plane}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), locationResource.ID).Return(&store.Object{Data: locationResource}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), idWithoutResourceGroup.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), locationResource.ID).Return(&database.Object{Data: locationResource}, nil).Times(1) operationStatusID := resources.MustParse("/planes/radius/local/providers/System.TestRP/deployments/xzy/operationStatuses/abcd") @@ -154,8 +154,8 @@ func Test_ValidateDownstream(t *testing.T) { // All of the Radius RPs include a location in the operation status child resource. t.Run("success (operationstatuses with location)", func(t *testing.T) { mock := setup(t) - mock.EXPECT().Get(gomock.Any(), idWithoutResourceGroup.PlaneScope()).Return(&store.Object{Data: plane}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), locationResource.ID).Return(&store.Object{Data: locationResource}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), idWithoutResourceGroup.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), locationResource.ID).Return(&database.Object{Data: locationResource}, nil).Times(1) operationStatusID := resources.MustParse("/planes/radius/local/providers/System.TestRP/locations/east/operationStatuses/abcd") @@ -170,8 +170,8 @@ func Test_ValidateDownstream(t *testing.T) { // The deployment engine models its operation result resources as child resources of the deployment resource. t.Run("success (operationresults as child resource)", func(t *testing.T) { mock := setup(t) - mock.EXPECT().Get(gomock.Any(), idWithoutResourceGroup.PlaneScope()).Return(&store.Object{Data: plane}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), locationResource.ID).Return(&store.Object{Data: locationResource}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), idWithoutResourceGroup.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), locationResource.ID).Return(&database.Object{Data: locationResource}, nil).Times(1) operationResultID := resources.MustParse("/planes/radius/local/providers/System.TestRP/deployments/xzy/operationResults/abcd") @@ -186,8 +186,8 @@ func Test_ValidateDownstream(t *testing.T) { // All of the Radius RPs include a location in the operation result child resource. t.Run("success (operationresults with location)", func(t *testing.T) { mock := setup(t) - mock.EXPECT().Get(gomock.Any(), idWithoutResourceGroup.PlaneScope()).Return(&store.Object{Data: plane}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), locationResource.ID).Return(&store.Object{Data: locationResource}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), idWithoutResourceGroup.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), locationResource.ID).Return(&database.Object{Data: locationResource}, nil).Times(1) operationResultID := resources.MustParse("/planes/radius/local/providers/System.TestRP/locations/east/operationResults/abcd") @@ -201,7 +201,7 @@ func Test_ValidateDownstream(t *testing.T) { t.Run("plane not found", func(t *testing.T) { mock := setup(t) - mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(nil, &store.ErrNotFound{}).Times(1) + mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(nil, &database.ErrNotFound{}).Times(1) downstreamURL, err := ValidateDownstream(testcontext.New(t), mock, id, location, apiVersion) require.Error(t, err) @@ -223,8 +223,8 @@ func Test_ValidateDownstream(t *testing.T) { t.Run("resource group not found", func(t *testing.T) { mock := setup(t) - mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&store.Object{Data: plane}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(nil, &store.ErrNotFound{}).Times(1) + mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(nil, &database.ErrNotFound{}).Times(1) downstreamURL, err := ValidateDownstream(testcontext.New(t), mock, id, location, apiVersion) require.Error(t, err) @@ -235,7 +235,7 @@ func Test_ValidateDownstream(t *testing.T) { t.Run("resource group err", func(t *testing.T) { mock := setup(t) - mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&store.Object{Data: plane}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(nil, errors.New("test error")).Times(1) downstreamURL, err := ValidateDownstream(testcontext.New(t), mock, id, location, apiVersion) @@ -256,8 +256,8 @@ func Test_ValidateDownstream(t *testing.T) { expected := fmt.Errorf("failed to fetch resource type %q: %w", "System.TestRP/testResources", errors.New("test error")) mock := setup(t) - mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&store.Object{Data: plane}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&store.Object{Data: resourceGroup}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&database.Object{Data: resourceGroup}, nil).Times(1) mock.EXPECT().Get(gomock.Any(), resourceTypeResource.ID).Return(nil, errors.New("test error")).Times(1) downstreamURL, err := ValidateDownstream(testcontext.New(t), mock, id, location, apiVersion) @@ -278,9 +278,9 @@ func Test_ValidateDownstream(t *testing.T) { expected := fmt.Errorf("failed to fetch location %q: %w", locationResource.ID, errors.New("test error")) mock := setup(t) - mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&store.Object{Data: plane}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&store.Object{Data: resourceGroup}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), resourceTypeResource.ID).Return(&store.Object{Data: resourceTypeID}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&database.Object{Data: resourceGroup}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), resourceTypeResource.ID).Return(&database.Object{Data: resourceTypeID}, nil).Times(1) mock.EXPECT().Get(gomock.Any(), locationResource.ID).Return(nil, errors.New("test error")).Times(1) downstreamURL, err := ValidateDownstream(testcontext.New(t), mock, id, location, apiVersion) @@ -318,10 +318,10 @@ func Test_ValidateDownstream(t *testing.T) { } mock := setup(t) - mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&store.Object{Data: plane}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&store.Object{Data: resourceGroup}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), resourceTypeResource.ID).Return(&store.Object{Data: resourceTypeID}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), locationResource.ID).Return(&store.Object{Data: locationResource}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&database.Object{Data: resourceGroup}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), resourceTypeResource.ID).Return(&database.Object{Data: resourceTypeID}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), locationResource.ID).Return(&database.Object{Data: locationResource}, nil).Times(1) downstreamURL, err := ValidateDownstream(testcontext.New(t), mock, id, location, apiVersion) require.Error(t, err) @@ -358,10 +358,10 @@ func Test_ValidateDownstream(t *testing.T) { } mock := setup(t) - mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&store.Object{Data: plane}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&store.Object{Data: resourceGroup}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), resourceTypeResource.ID).Return(&store.Object{Data: resourceTypeID}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), locationResource.ID).Return(&store.Object{Data: locationResource}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&database.Object{Data: resourceGroup}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), resourceTypeResource.ID).Return(&database.Object{Data: resourceTypeID}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), locationResource.ID).Return(&database.Object{Data: locationResource}, nil).Times(1) downstreamURL, err := ValidateDownstream(testcontext.New(t), mock, id, location, apiVersion) require.Error(t, err) @@ -398,10 +398,10 @@ func Test_ValidateDownstream(t *testing.T) { } mock := setup(t) - mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&store.Object{Data: plane}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&store.Object{Data: resourceGroup}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), resourceTypeResource.ID).Return(&store.Object{Data: resourceTypeID}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), locationResource.ID).Return(&store.Object{Data: locationResource}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&database.Object{Data: resourceGroup}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), resourceTypeResource.ID).Return(&database.Object{Data: resourceTypeID}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), locationResource.ID).Return(&database.Object{Data: locationResource}, nil).Times(1) downstreamURL, err := ValidateDownstream(testcontext.New(t), mock, id, location, apiVersion) require.Error(t, err) @@ -437,9 +437,9 @@ func Test_ValidateDownstream_Legacy(t *testing.T) { }, } - setup := func(t *testing.T) *store.MockStorageClient { + setup := func(t *testing.T) *database.MockClient { ctrl := gomock.NewController(t) - return store.NewMockStorageClient(ctrl) + return database.NewMockClient(ctrl) } t.Run("success (resource group)", func(t *testing.T) { @@ -452,9 +452,9 @@ func Test_ValidateDownstream_Legacy(t *testing.T) { } mock := setup(t) - mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&store.Object{Data: plane}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&store.Object{Data: resourceGroup}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), resourceTypeID.String()).Return(nil, &store.ErrNotFound{}).Times(1) + mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&database.Object{Data: resourceGroup}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), resourceTypeID.String()).Return(nil, &database.ErrNotFound{}).Times(1) expectedURL, err := url.Parse(downstream) require.NoError(t, err) @@ -466,8 +466,8 @@ func Test_ValidateDownstream_Legacy(t *testing.T) { t.Run("success (non resource group)", func(t *testing.T) { mock := setup(t) - mock.EXPECT().Get(gomock.Any(), idWithoutResourceGroup.PlaneScope()).Return(&store.Object{Data: plane}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), resourceTypeID.String()).Return(nil, &store.ErrNotFound{}).Times(1) + mock.EXPECT().Get(gomock.Any(), idWithoutResourceGroup.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), resourceTypeID.String()).Return(nil, &database.ErrNotFound{}).Times(1) expectedURL, err := url.Parse(downstream) require.NoError(t, err) @@ -479,7 +479,7 @@ func Test_ValidateDownstream_Legacy(t *testing.T) { t.Run("plane not found", func(t *testing.T) { mock := setup(t) - mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(nil, &store.ErrNotFound{}).Times(1) + mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(nil, &database.ErrNotFound{}).Times(1) downstreamURL, err := ValidateDownstream(testcontext.New(t), mock, id, location, apiVersion) require.Error(t, err) @@ -501,8 +501,8 @@ func Test_ValidateDownstream_Legacy(t *testing.T) { t.Run("resource group not found", func(t *testing.T) { mock := setup(t) - mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&store.Object{Data: plane}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(nil, &store.ErrNotFound{}).Times(1) + mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(nil, &database.ErrNotFound{}).Times(1) downstreamURL, err := ValidateDownstream(testcontext.New(t), mock, id, location, apiVersion) require.Error(t, err) @@ -513,7 +513,7 @@ func Test_ValidateDownstream_Legacy(t *testing.T) { t.Run("resource group err", func(t *testing.T) { mock := setup(t) - mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&store.Object{Data: plane}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(nil, errors.New("test error")).Times(1) downstreamURL, err := ValidateDownstream(testcontext.New(t), mock, id, location, apiVersion) @@ -543,9 +543,9 @@ func Test_ValidateDownstream_Legacy(t *testing.T) { } mock := setup(t) - mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&store.Object{Data: plane}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&store.Object{Data: resourceGroup}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), resourceTypeID.String()).Return(nil, &store.ErrNotFound{}).Times(1) + mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&database.Object{Data: resourceGroup}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), resourceTypeID.String()).Return(nil, &database.ErrNotFound{}).Times(1) downstreamURL, err := ValidateDownstream(testcontext.New(t), mock, id, location, apiVersion) require.Error(t, err) @@ -574,9 +574,9 @@ func Test_ValidateDownstream_Legacy(t *testing.T) { } mock := setup(t) - mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&store.Object{Data: plane}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&store.Object{Data: resourceGroup}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), resourceTypeID.String()).Return(nil, &store.ErrNotFound{}).Times(1) + mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&database.Object{Data: resourceGroup}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), resourceTypeID.String()).Return(nil, &database.ErrNotFound{}).Times(1) downstreamURL, err := ValidateDownstream(testcontext.New(t), mock, id, location, apiVersion) require.Error(t, err) @@ -607,9 +607,9 @@ func Test_ValidateDownstream_Legacy(t *testing.T) { } mock := setup(t) - mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&store.Object{Data: plane}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&store.Object{Data: resourceGroup}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), resourceTypeID.String()).Return(nil, &store.ErrNotFound{}).Times(1) + mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&database.Object{Data: resourceGroup}, nil).Times(1) + mock.EXPECT().Get(gomock.Any(), resourceTypeID.String()).Return(nil, &database.ErrNotFound{}).Times(1) downstreamURL, err := ValidateDownstream(testcontext.New(t), mock, id, location, apiVersion) require.Error(t, err) diff --git a/pkg/ucp/frontend/controller/resourceproviders/getresourceprovidersummary.go b/pkg/ucp/frontend/controller/resourceproviders/getresourceprovidersummary.go index ac0b92acd4..a1b5b638a4 100644 --- a/pkg/ucp/frontend/controller/resourceproviders/getresourceprovidersummary.go +++ b/pkg/ucp/frontend/controller/resourceproviders/getresourceprovidersummary.go @@ -26,10 +26,10 @@ import ( armrpc_controller "github.com/radius-project/radius/pkg/armrpc/frontend/controller" armrpc_rest "github.com/radius-project/radius/pkg/armrpc/rest" "github.com/radius-project/radius/pkg/middleware" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/datamodel/converter" "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/store" ) var _ armrpc_controller.Controller = (*GetResourceProviderSummary)(nil) @@ -61,8 +61,8 @@ func (r *GetResourceProviderSummary) Run(ctx context.Context, w http.ResponseWri } // First check if the plane exists. - _, err = r.StorageClient().Get(ctx, scope.String()) - if errors.Is(err, &store.ErrNotFound{}) { + _, err = r.DatabaseClient().Get(ctx, scope.String()) + if errors.Is(err, &database.ErrNotFound{}) { return armrpc_rest.NewNotFoundResponse(scope), nil } else if err != nil { return nil, err @@ -74,8 +74,8 @@ func (r *GetResourceProviderSummary) Run(ctx context.Context, w http.ResponseWri return nil, err } - result, err := r.StorageClient().Get(ctx, id.String()) - if errors.Is(err, &store.ErrNotFound{}) { + result, err := r.DatabaseClient().Get(ctx, id.String()) + if errors.Is(err, &database.ErrNotFound{}) { // If we fail to find the summary, then use the relative path as the target. message := fmt.Sprintf("the resource provider with name '%s' was not found", name) return armrpc_rest.NewNotFoundMessageResponse(message), nil @@ -117,7 +117,7 @@ func (r *GetResourceProviderSummary) extractScopeAndName(relativePath string) (r return scope, name, nil } -func (r *GetResourceProviderSummary) createResponse(ctx context.Context, result *store.Object) (any, error) { +func (r *GetResourceProviderSummary) createResponse(ctx context.Context, result *database.Object) (any, error) { serviceCtx := v1.ARMRequestContextFromContext(ctx) summary := datamodel.ResourceProviderSummary{} diff --git a/pkg/ucp/frontend/controller/resourceproviders/listresourceprovidersummaries.go b/pkg/ucp/frontend/controller/resourceproviders/listresourceprovidersummaries.go index edd72c4575..f50570b755 100644 --- a/pkg/ucp/frontend/controller/resourceproviders/listresourceprovidersummaries.go +++ b/pkg/ucp/frontend/controller/resourceproviders/listresourceprovidersummaries.go @@ -25,10 +25,10 @@ import ( armrpc_controller "github.com/radius-project/radius/pkg/armrpc/frontend/controller" armrpc_rest "github.com/radius-project/radius/pkg/armrpc/rest" "github.com/radius-project/radius/pkg/middleware" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/datamodel/converter" "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/store" ) var _ armrpc_controller.Controller = (*ListResourceProviderSummaries)(nil) @@ -68,20 +68,20 @@ func (r *ListResourceProviderSummaries) Run(ctx context.Context, w http.Response } // First check if the plane exists. - _, err = r.StorageClient().Get(ctx, scope.String()) - if errors.Is(err, &store.ErrNotFound{}) { + _, err = r.DatabaseClient().Get(ctx, scope.String()) + if errors.Is(err, &database.ErrNotFound{}) { return armrpc_rest.NewNotFoundResponse(scope), nil } else if err != nil { return nil, err } // Now query for resource provider summaries - query := store.Query{ + query := database.Query{ RootScope: scope.String(), ResourceType: datamodel.ResourceProviderSummaryResourceType, } - result, err := r.StorageClient().Query(ctx, query) + result, err := r.DatabaseClient().Query(ctx, query) if err != nil { return nil, err } @@ -94,7 +94,7 @@ func (r *ListResourceProviderSummaries) Run(ctx context.Context, w http.Response return armrpc_rest.NewOKResponse(response), nil } -func (r *ListResourceProviderSummaries) createResponse(ctx context.Context, result *store.ObjectQueryResult) (*v1.PaginatedList, error) { +func (r *ListResourceProviderSummaries) createResponse(ctx context.Context, result *database.ObjectQueryResult) (*v1.PaginatedList, error) { items := v1.PaginatedList{ Value: []any{}, // Initialize to empty list for testability } diff --git a/pkg/ucp/frontend/modules/types.go b/pkg/ucp/frontend/modules/types.go index 87abadc2c4..2c959df585 100644 --- a/pkg/ucp/frontend/modules/types.go +++ b/pkg/ucp/frontend/modules/types.go @@ -22,10 +22,10 @@ import ( "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" "github.com/radius-project/radius/pkg/sdk" - "github.com/radius-project/radius/pkg/ucp/dataprovider" + "github.com/radius-project/radius/pkg/ucp/databaseprovider" "github.com/radius-project/radius/pkg/ucp/hostoptions" - queueprovider "github.com/radius-project/radius/pkg/ucp/queue/provider" - secretprovider "github.com/radius-project/radius/pkg/ucp/secret/provider" + "github.com/radius-project/radius/pkg/ucp/queue/queueprovider" + "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" "github.com/radius-project/radius/pkg/validator" ) @@ -60,8 +60,8 @@ type Options struct { // Location is the location of the server hosting UCP. Location string - // DataProvider is the data storage provider. - DataProvider *dataprovider.DataStorageProvider + // DatabaseProvider is the database provider. + DatabaseProvider *databaseprovider.DatabaseProvider // QeueueProvider provides access to the queue for async operations. QueueProvider *queueprovider.QueueProvider diff --git a/pkg/ucp/frontend/radius/routes.go b/pkg/ucp/frontend/radius/routes.go index cee401c79b..951a49af3a 100644 --- a/pkg/ucp/frontend/radius/routes.go +++ b/pkg/ucp/frontend/radius/routes.go @@ -64,16 +64,16 @@ func (m *Module) Initialize(ctx context.Context) (http.Handler, error) { return handler } - storageClient, err := m.options.DataProvider.GetClient(ctx) + databaseClient, err := m.options.DatabaseProvider.GetClient(ctx) if err != nil { return nil, err } ctrlOptions := controller.Options{ - Address: m.options.Address, - PathBase: m.options.PathBase, - StorageClient: storageClient, - StatusManager: m.options.StatusManager, + Address: m.options.Address, + PathBase: m.options.PathBase, + DatabaseClient: databaseClient, + StatusManager: m.options.StatusManager, } // NOTE: we're careful where we use the `apiValidator` middleware. It's not used for the proxy routes. @@ -380,6 +380,6 @@ func operationStatusGetHandler(ctx context.Context, ctrlOptions controller.Optio } func operationResultGetHandler(ctx context.Context, ctrlOptions controller.Options) (http.HandlerFunc, error) { - // NOTE: The resource type below is CORRECT. operation status and operation result use the same resource for storage. + // NOTE: The resource type below is CORRECT. operation status and operation result use the same resource type in the database. return server.CreateHandler(ctx, "System.Resources/operationstatuses", v1.OperationGet, ctrlOptions, defaultoperation.NewGetOperationResult) } diff --git a/pkg/ucp/frontend/radius/routes_test.go b/pkg/ucp/frontend/radius/routes_test.go index 6e26e16adf..ac6f6e8be5 100644 --- a/pkg/ucp/frontend/radius/routes_test.go +++ b/pkg/ucp/frontend/radius/routes_test.go @@ -25,12 +25,12 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" "github.com/radius-project/radius/pkg/armrpc/rpctest" "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" + "github.com/radius-project/radius/pkg/ucp/databaseprovider" "github.com/radius-project/radius/pkg/ucp/datamodel" - "github.com/radius-project/radius/pkg/ucp/dataprovider" "github.com/radius-project/radius/pkg/ucp/frontend/modules" "github.com/radius-project/radius/pkg/ucp/hostoptions" "github.com/radius-project/radius/pkg/ucp/secret" - secretprovider "github.com/radius-project/radius/pkg/ucp/secret/provider" + secretprovider "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" "go.uber.org/mock/gomock" ) @@ -185,18 +185,18 @@ func Test_Routes(t *testing.T) { } ctrl := gomock.NewController(t) - dataProvider := dataprovider.DataStorageProviderFromMemory() + databaseProvider := databaseprovider.FromMemory() secretClient := secret.NewMockClient(ctrl) secretProvider := secretprovider.NewSecretProvider(secretprovider.SecretProviderOptions{}) secretProvider.SetClient(secretClient) options := modules.Options{ - Address: "localhost", - PathBase: pathBase, - Config: &hostoptions.UCPConfig{}, - DataProvider: dataProvider, - SecretProvider: secretProvider, + Address: "localhost", + PathBase: pathBase, + Config: &hostoptions.UCPConfig{}, + DatabaseProvider: databaseProvider, + SecretProvider: secretProvider, } rpctest.AssertRouters(t, tests, pathBase, "", func(ctx context.Context) (chi.Router, error) { diff --git a/pkg/ucp/hostoptions/providerconfig.go b/pkg/ucp/hostoptions/providerconfig.go index 4b37e6a8cf..3f615a0c98 100644 --- a/pkg/ucp/hostoptions/providerconfig.go +++ b/pkg/ucp/hostoptions/providerconfig.go @@ -21,21 +21,21 @@ import ( profilerprovider "github.com/radius-project/radius/pkg/profiler/provider" "github.com/radius-project/radius/pkg/trace" "github.com/radius-project/radius/pkg/ucp/config" - "github.com/radius-project/radius/pkg/ucp/dataprovider" - qprovider "github.com/radius-project/radius/pkg/ucp/queue/provider" + "github.com/radius-project/radius/pkg/ucp/databaseprovider" + "github.com/radius-project/radius/pkg/ucp/queue/queueprovider" "github.com/radius-project/radius/pkg/ucp/rest" - "github.com/radius-project/radius/pkg/ucp/secret/provider" + "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" "github.com/radius-project/radius/pkg/ucp/ucplog" ) // UCPConfig includes the resource provider configuration. type UCPConfig struct { - StorageProvider dataprovider.StorageProviderOptions `yaml:"storageProvider"` + DatabaseProvider databaseprovider.Options `yaml:"storageProvider"` Planes []rest.Plane `yaml:"planes"` - SecretProvider provider.SecretProviderOptions `yaml:"secretProvider"` + SecretProvider secretprovider.SecretProviderOptions `yaml:"secretProvider"` MetricsProvider metricsprovider.MetricsProviderOptions `yaml:"metricsProvider"` ProfilerProvider profilerprovider.ProfilerProviderOptions `yaml:"profilerProvider"` - QueueProvider qprovider.QueueProviderOptions `yaml:"queueProvider"` + QueueProvider queueprovider.QueueProviderOptions `yaml:"queueProvider"` TracerProvider trace.Options `yaml:"tracerProvider"` Logging ucplog.LoggingOptions `yaml:"logging"` Identity Identity `yaml:"identity,omitempty"` diff --git a/pkg/ucp/integrationtests/aws/awstest.go b/pkg/ucp/integrationtests/aws/awstest.go index 52aa311d44..6be034b1c5 100644 --- a/pkg/ucp/integrationtests/aws/awstest.go +++ b/pkg/ucp/integrationtests/aws/awstest.go @@ -22,11 +22,11 @@ import ( "testing" ucp_aws "github.com/radius-project/radius/pkg/ucp/aws" + "github.com/radius-project/radius/pkg/ucp/database" ucp_aws_frontend "github.com/radius-project/radius/pkg/ucp/frontend/aws" "github.com/radius-project/radius/pkg/ucp/frontend/modules" "github.com/radius-project/radius/pkg/ucp/integrationtests/testserver" "github.com/radius-project/radius/pkg/ucp/secret" - "github.com/radius-project/radius/pkg/ucp/store" "go.uber.org/mock/gomock" ) @@ -38,7 +38,7 @@ const ( testAWSRequestToken = "79B9F0DA-4882-4DC8-A367-6FD3BC122DED" // Random UUID ) -func initializeAWSTest(t *testing.T) (*testserver.TestServer, *store.MockStorageClient, *secret.MockClient, *ucp_aws.MockAWSCloudControlClient, *ucp_aws.MockAWSCloudFormationClient) { +func initializeAWSTest(t *testing.T) (*testserver.TestServer, *database.MockClient, *secret.MockClient, *ucp_aws.MockAWSCloudControlClient, *ucp_aws.MockAWSCloudFormationClient) { ctrl := gomock.NewController(t) cloudControlClient := ucp_aws.NewMockAWSCloudControlClient(ctrl) cloudFormationClient := ucp_aws.NewMockAWSCloudFormationClient(ctrl) @@ -50,5 +50,5 @@ func initializeAWSTest(t *testing.T) (*testserver.TestServer, *store.MockStorage return []modules.Initializer{module} }) - return ucp, ucp.Mocks.Storage, ucp.Mocks.Secrets, cloudControlClient, cloudFormationClient + return ucp, ucp.Mocks.Database, ucp.Mocks.Secrets, cloudControlClient, cloudFormationClient } diff --git a/pkg/ucp/integrationtests/radius/proxy_test.go b/pkg/ucp/integrationtests/radius/proxy_test.go index 0cd69ea5fc..4b96eb7229 100644 --- a/pkg/ucp/integrationtests/radius/proxy_test.go +++ b/pkg/ucp/integrationtests/radius/proxy_test.go @@ -176,7 +176,7 @@ func Test_RadiusPlane_ResourceAsync(t *testing.T) { return result, nil } - client, err := ucp.Clients.StorageProvider.GetClient(ctx) + client, err := ucp.Clients.DatabaseProvider.GetClient(ctx) require.NoError(t, err) err = client.Delete(ctx, testResourceID) require.NoError(t, err) diff --git a/pkg/ucp/integrationtests/testrp/async.go b/pkg/ucp/integrationtests/testrp/async.go index 61043b1092..1af3838fb5 100644 --- a/pkg/ucp/integrationtests/testrp/async.go +++ b/pkg/ucp/integrationtests/testrp/async.go @@ -33,7 +33,7 @@ import ( "github.com/radius-project/radius/pkg/armrpc/servicecontext" "github.com/radius-project/radius/pkg/middleware" "github.com/radius-project/radius/pkg/ucp/integrationtests/testserver" - queueprovider "github.com/radius-project/radius/pkg/ucp/queue/provider" + "github.com/radius-project/radius/pkg/ucp/queue/queueprovider" "github.com/radius-project/radius/test/testcontext" "github.com/stretchr/testify/require" ) @@ -59,8 +59,8 @@ func AsyncResource(t *testing.T, ts *testserver.TestServer, rootScope string, pu resourceType := "System.Test/testResources" - // We can share the storage provider with the test server. - storageClient, err := ts.Clients.StorageProvider.GetClient(ctx) + // We can share the database provider with the test server. + databaseClient, err := ts.Clients.DatabaseProvider.GetClient(ctx) require.NoError(t, err) // Do not share the queue. @@ -73,10 +73,10 @@ func AsyncResource(t *testing.T, ts *testserver.TestServer, rootScope string, pu queueClient, err := queueProvider.GetClient(ctx) require.NoError(t, err) - statusManager := statusmanager.New(storageClient, queueClient, v1.LocationGlobal) + statusManager := statusmanager.New(databaseClient, queueClient, v1.LocationGlobal) backendOpts := backend_ctrl.Options{ - StorageClient: storageClient, + DatabaseClient: databaseClient, } registry := worker.NewControllerRegistry() @@ -100,9 +100,9 @@ func AsyncResource(t *testing.T, ts *testserver.TestServer, rootScope string, pu }() frontendOpts := frontend_ctrl.Options{ - Address: "localhost:8080", - StorageClient: storageClient, - StatusManager: statusManager, + Address: "localhost:8080", + DatabaseClient: databaseClient, + StatusManager: statusManager, } err = server.ConfigureDefaultHandlers(ctx, r, rootScope, false, "System.Test", nil, frontendOpts) diff --git a/pkg/ucp/integrationtests/testrp/sync.go b/pkg/ucp/integrationtests/testrp/sync.go index f720a319ed..94b4d9af7b 100644 --- a/pkg/ucp/integrationtests/testrp/sync.go +++ b/pkg/ucp/integrationtests/testrp/sync.go @@ -30,7 +30,7 @@ import ( "github.com/radius-project/radius/pkg/armrpc/servicecontext" "github.com/radius-project/radius/pkg/middleware" "github.com/radius-project/radius/pkg/ucp/integrationtests/testserver" - queueprovider "github.com/radius-project/radius/pkg/ucp/queue/provider" + "github.com/radius-project/radius/pkg/ucp/queue/queueprovider" "github.com/radius-project/radius/test/testcontext" "github.com/stretchr/testify/require" ) @@ -43,8 +43,8 @@ func SyncResource(t *testing.T, ts *testserver.TestServer, rootScope string) fun r := chi.NewRouter() r.Use(servicecontext.ARMRequestCtx("", v1.LocationGlobal), middleware.LowercaseURLPath) - // We can share the storage provider with the test server. - storageClient, err := ts.Clients.StorageProvider.GetClient(ctx) + // We can share the database provider with the test server. + databaseClient, err := ts.Clients.DatabaseProvider.GetClient(ctx) require.NoError(t, err) // Do not share the queue. @@ -57,12 +57,12 @@ func SyncResource(t *testing.T, ts *testserver.TestServer, rootScope string) fun queueClient, err := queueProvider.GetClient(ctx) require.NoError(t, err) - statusManager := statusmanager.New(storageClient, queueClient, v1.LocationGlobal) + statusManager := statusmanager.New(databaseClient, queueClient, v1.LocationGlobal) ctrlOpts := frontend_ctrl.Options{ - Address: "localhost:8080", - StatusManager: statusManager, - StorageClient: storageClient, + Address: "localhost:8080", + StatusManager: statusManager, + DatabaseClient: databaseClient, } err = server.ConfigureDefaultHandlers(ctx, r, rootScope, false, "System.Test", nil, ctrlOpts) diff --git a/pkg/ucp/integrationtests/testserver/testserver.go b/pkg/ucp/integrationtests/testserver/testserver.go index 218181b40c..8643c3f839 100644 --- a/pkg/ucp/integrationtests/testserver/testserver.go +++ b/pkg/ucp/integrationtests/testserver/testserver.go @@ -48,17 +48,17 @@ import ( "github.com/radius-project/radius/pkg/sdk" "github.com/radius-project/radius/pkg/ucp/backend" "github.com/radius-project/radius/pkg/ucp/data" - "github.com/radius-project/radius/pkg/ucp/dataprovider" + "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/ucp/databaseprovider" "github.com/radius-project/radius/pkg/ucp/frontend/api" "github.com/radius-project/radius/pkg/ucp/frontend/modules" "github.com/radius-project/radius/pkg/ucp/hosting" "github.com/radius-project/radius/pkg/ucp/hostoptions" queue "github.com/radius-project/radius/pkg/ucp/queue/client" - queueprovider "github.com/radius-project/radius/pkg/ucp/queue/provider" + "github.com/radius-project/radius/pkg/ucp/queue/queueprovider" "github.com/radius-project/radius/pkg/ucp/secret" - secretprovider "github.com/radius-project/radius/pkg/ucp/secret/provider" + "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" "github.com/radius-project/radius/pkg/ucp/server" - "github.com/radius-project/radius/pkg/ucp/store" "github.com/radius-project/radius/pkg/validator" "github.com/radius-project/radius/swagger" "github.com/radius-project/radius/test/testcontext" @@ -104,8 +104,8 @@ type TestServerClients struct { // SecretProvider is the secret client provider. SecretProvider *secretprovider.SecretProvider - // StorageProvider is the storage client provider. - StorageProvider *dataprovider.DataStorageProvider + // DatabaseProvider is the database client provider. + DatabaseProvider *databaseprovider.DatabaseProvider } // TestServerMocks provides access to mock instances created by the TestServer. @@ -113,8 +113,8 @@ type TestServerMocks struct { // Secrets is the mock secret client. Secrets *secret.MockClient - // Storage is the mock storage client. - Storage *store.MockStorageClient + // Database is the mock database client. + Database *database.MockClient } // Client provides access to an http.Client that can be used to send requests. Most tests should use the functionality @@ -149,8 +149,8 @@ func StartWithMocks(t *testing.T, configureModules func(options modules.Options) pathBase := "/" + uuid.New().String() ctrl := gomock.NewController(t) - dataClient := store.NewMockStorageClient(ctrl) - dataProvider := dataprovider.DataStorageProviderFromClient(dataClient) + databaseClient := database.NewMockClient(ctrl) + databaseProvider := databaseprovider.FromClient(databaseClient) queueClient := queue.NewMockClient(ctrl) queueProvider := queueprovider.New(queueprovider.QueueProviderOptions{Name: "System.Resources"}) @@ -176,13 +176,13 @@ func StartWithMocks(t *testing.T, configureModules func(options modules.Options) require.NoError(t, err, "failed to load OpenAPI spec") options := modules.Options{ - Address: "localhost:9999", // Will be dynamically populated when server is started - PathBase: pathBase, - Config: &hostoptions.UCPConfig{}, - DataProvider: dataProvider, - SecretProvider: secretProvider, - SpecLoader: specLoader, - StatusManager: statusManager, + Address: "localhost:9999", // Will be dynamically populated when server is started + PathBase: pathBase, + Config: &hostoptions.UCPConfig{}, + DatabaseProvider: databaseProvider, + SecretProvider: secretProvider, + SpecLoader: specLoader, + StatusManager: statusManager, } if configureModules == nil { @@ -202,13 +202,13 @@ func StartWithMocks(t *testing.T, configureModules func(options modules.Options) ucp := &TestServer{ BaseURL: server.URL + pathBase, Clients: &TestServerClients{ - QueueProvider: queueProvider, - SecretProvider: secretProvider, - StorageProvider: dataProvider, + QueueProvider: queueProvider, + SecretProvider: secretProvider, + DatabaseProvider: databaseProvider, }, Mocks: &TestServerMocks{ - Secrets: secretClient, - Storage: dataClient, + Secrets: secretClient, + Database: databaseClient, }, Server: server, cancel: cancel, @@ -252,16 +252,16 @@ func StartWithETCD(t *testing.T, configureModules func(options modules.Options) } }() - storageOptions := dataprovider.StorageProviderOptions{ - Provider: dataprovider.TypeETCD, - ETCD: dataprovider.ETCDOptions{ + databaseOptions := databaseprovider.Options{ + Provider: databaseprovider.TypeETCD, + ETCD: databaseprovider.ETCDOptions{ InMemory: true, Client: config, }, } secretOptions := secretprovider.SecretProviderOptions{ Provider: secretprovider.TypeETCDSecret, - ETCD: storageOptions.ETCD, + ETCD: databaseOptions.ETCD, } queueOptions := queueprovider.QueueProviderOptions{ Name: server.UCPProviderName, @@ -271,7 +271,7 @@ func StartWithETCD(t *testing.T, configureModules func(options modules.Options) // Generate a random base path to ensure we're handling it correctly. pathBase := "/" + uuid.New().String() - dataProvider := dataprovider.DataStorageProviderFromOptions(storageOptions) + databaseProvider := databaseprovider.FromOptions(databaseOptions) secretProvider := secretprovider.NewSecretProvider(secretOptions) queueProvider := queueprovider.New(queueOptions) @@ -292,23 +292,23 @@ func StartWithETCD(t *testing.T, configureModules func(options modules.Options) connection, err := sdk.NewDirectConnection(address + pathBase) require.NoError(t, err) - storageClient, err := dataProvider.GetClient(ctx) + databaseClient, err := databaseProvider.GetClient(ctx) require.NoError(t, err) - statusManager := statusmanager.New(storageClient, queueClient, v1.LocationGlobal) + statusManager := statusmanager.New(databaseClient, queueClient, v1.LocationGlobal) specLoader, err := validator.LoadSpec(ctx, "ucp", swagger.SpecFilesUCP, []string{pathBase}, "") require.NoError(t, err, "failed to load OpenAPI spec") options := modules.Options{ - Address: address, - PathBase: pathBase, - Config: &hostoptions.UCPConfig{}, - DataProvider: dataProvider, - SecretProvider: secretProvider, - SpecLoader: specLoader, - QueueProvider: queueProvider, - StatusManager: statusManager, + Address: address, + PathBase: pathBase, + Config: &hostoptions.UCPConfig{}, + DatabaseProvider: databaseProvider, + SecretProvider: secretProvider, + SpecLoader: specLoader, + QueueProvider: queueProvider, + StatusManager: statusManager, } if configureModules == nil { @@ -327,7 +327,7 @@ func StartWithETCD(t *testing.T, configureModules func(options modules.Options) require.NoError(t, err) registry := worker.NewControllerRegistry() - err = backend.RegisterControllers(registry, connection, http.DefaultTransport, backend_ctrl.Options{StorageClient: storageClient}, defaultDownstream) + err = backend.RegisterControllers(registry, connection, http.DefaultTransport, backend_ctrl.Options{DatabaseClient: databaseClient}, defaultDownstream) require.NoError(t, err) w := worker.New(worker.Options{}, statusManager, queueClient, registry) @@ -356,9 +356,9 @@ func StartWithETCD(t *testing.T, configureModules func(options modules.Options) ucp := &TestServer{ BaseURL: server.URL + pathBase, Clients: &TestServerClients{ - QueueProvider: queueProvider, - SecretProvider: secretProvider, - StorageProvider: dataProvider, + QueueProvider: queueProvider, + SecretProvider: secretProvider, + DatabaseProvider: databaseProvider, }, Server: server, cancel: cancel, diff --git a/pkg/ucp/queue/apiserver/client.go b/pkg/ucp/queue/apiserver/client.go index 225e9eef93..c133d874e4 100644 --- a/pkg/ucp/queue/apiserver/client.go +++ b/pkg/ucp/queue/apiserver/client.go @@ -62,7 +62,7 @@ import ( "github.com/radius-project/radius/pkg/ucp/queue/client" - v1alpha1 "github.com/radius-project/radius/pkg/ucp/store/apiserverstore/api/ucp.dev/v1alpha1" + v1alpha1 "github.com/radius-project/radius/pkg/ucp/database/apiserverstore/api/ucp.dev/v1alpha1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" diff --git a/pkg/ucp/queue/apiserver/client_test.go b/pkg/ucp/queue/apiserver/client_test.go index 6b786ebb51..77c608199f 100644 --- a/pkg/ucp/queue/apiserver/client_test.go +++ b/pkg/ucp/queue/apiserver/client_test.go @@ -22,8 +22,8 @@ import ( "testing" "time" + v1alpha1 "github.com/radius-project/radius/pkg/ucp/database/apiserverstore/api/ucp.dev/v1alpha1" "github.com/radius-project/radius/pkg/ucp/queue/client" - v1alpha1 "github.com/radius-project/radius/pkg/ucp/store/apiserverstore/api/ucp.dev/v1alpha1" "github.com/radius-project/radius/test/testcontext" "github.com/radius-project/radius/test/ucp/kubeenv" sharedtest "github.com/radius-project/radius/test/ucp/queuetest" diff --git a/pkg/ucp/queue/provider/factory.go b/pkg/ucp/queue/queueprovider/factory.go similarity index 95% rename from pkg/ucp/queue/provider/factory.go rename to pkg/ucp/queue/queueprovider/factory.go index 3cc87958c4..c206cb7942 100644 --- a/pkg/ucp/queue/provider/factory.go +++ b/pkg/ucp/queue/queueprovider/factory.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package provider +package queueprovider import ( context "context" @@ -22,10 +22,10 @@ import ( "fmt" "github.com/radius-project/radius/pkg/kubeutil" + ucpv1alpha1 "github.com/radius-project/radius/pkg/ucp/database/apiserverstore/api/ucp.dev/v1alpha1" "github.com/radius-project/radius/pkg/ucp/queue/apiserver" queue "github.com/radius-project/radius/pkg/ucp/queue/client" qinmem "github.com/radius-project/radius/pkg/ucp/queue/inmemory" - ucpv1alpha1 "github.com/radius-project/radius/pkg/ucp/store/apiserverstore/api/ucp.dev/v1alpha1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/rest" runtimeclient "sigs.k8s.io/controller-runtime/pkg/client" diff --git a/pkg/ucp/queue/provider/options.go b/pkg/ucp/queue/queueprovider/options.go similarity index 92% rename from pkg/ucp/queue/provider/options.go rename to pkg/ucp/queue/queueprovider/options.go index 116313d03a..2b15e56090 100644 --- a/pkg/ucp/queue/provider/options.go +++ b/pkg/ucp/queue/queueprovider/options.go @@ -14,11 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -package provider +package queueprovider -// QueueProviderOptions represents the data storage provider options. +// QueueProviderOptions represents the queueprovider options. type QueueProviderOptions struct { - // Provider configures the storage provider. + // Provider configures the queue provider. Provider QueueProviderType `yaml:"provider"` // Name represents the unique name of queue. diff --git a/pkg/ucp/queue/provider/provider.go b/pkg/ucp/queue/queueprovider/provider.go similarity index 92% rename from pkg/ucp/queue/provider/provider.go rename to pkg/ucp/queue/queueprovider/provider.go index b22418800c..6b25ea1e4f 100644 --- a/pkg/ucp/queue/provider/provider.go +++ b/pkg/ucp/queue/queueprovider/provider.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package provider +package queueprovider import ( "context" @@ -25,7 +25,7 @@ import ( ) var ( - ErrUnsupportedStorageProvider = errors.New("unsupported queue provider") + ErrUnsupportedQueueProvider = errors.New("unsupported queue provider") ) // QueueProvider is the provider to create and manage queue client. @@ -50,7 +50,7 @@ func (p *QueueProvider) GetClient(ctx context.Context) (queue.Client, error) { return p.queueClient, nil } - err := ErrUnsupportedStorageProvider + err := ErrUnsupportedQueueProvider p.once.Do(func() { if fn, ok := clientFactory[p.options.Provider]; ok { p.queueClient, err = fn(ctx, p.options) diff --git a/pkg/ucp/queue/provider/provider_test.go b/pkg/ucp/queue/queueprovider/provider_test.go similarity index 94% rename from pkg/ucp/queue/provider/provider_test.go rename to pkg/ucp/queue/queueprovider/provider_test.go index 1a1bb49320..6b4c4a174e 100644 --- a/pkg/ucp/queue/provider/provider_test.go +++ b/pkg/ucp/queue/queueprovider/provider_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package provider +package queueprovider import ( "context" @@ -45,5 +45,5 @@ func TestGetClient_InvalidQueue(t *testing.T) { }) _, err := p.GetClient(context.TODO()) - require.ErrorIs(t, ErrUnsupportedStorageProvider, err) + require.ErrorIs(t, ErrUnsupportedQueueProvider, err) } diff --git a/pkg/ucp/queue/provider/types.go b/pkg/ucp/queue/queueprovider/types.go similarity index 97% rename from pkg/ucp/queue/provider/types.go rename to pkg/ucp/queue/queueprovider/types.go index ff69fb9b37..e3d18553ff 100644 --- a/pkg/ucp/queue/provider/types.go +++ b/pkg/ucp/queue/queueprovider/types.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package provider +package queueprovider // QueueProviderType represents types of queue provider. type QueueProviderType string diff --git a/pkg/ucp/secret/provider/factory.go b/pkg/ucp/secret/secretprovider/factory.go similarity index 91% rename from pkg/ucp/secret/provider/factory.go rename to pkg/ucp/secret/secretprovider/factory.go index 839fb7b9fa..219b355827 100644 --- a/pkg/ucp/secret/provider/factory.go +++ b/pkg/ucp/secret/secretprovider/factory.go @@ -14,19 +14,19 @@ See the License for the specific language governing permissions and limitations under the License. */ -package provider +package secretprovider import ( "context" "errors" "github.com/radius-project/radius/pkg/kubeutil" - "github.com/radius-project/radius/pkg/ucp/dataprovider" + "github.com/radius-project/radius/pkg/ucp/database/etcdstore" + "github.com/radius-project/radius/pkg/ucp/databaseprovider" "github.com/radius-project/radius/pkg/ucp/secret" "github.com/radius-project/radius/pkg/ucp/secret/etcd" "github.com/radius-project/radius/pkg/ucp/secret/inmemory" kubernetes_client "github.com/radius-project/radius/pkg/ucp/secret/kubernetes" - "github.com/radius-project/radius/pkg/ucp/store/etcdstore" "k8s.io/kubectl/pkg/scheme" controller_runtime "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -42,7 +42,7 @@ var secretClientFactory = map[SecretProviderType]secretFactoryFunc{ func initETCDSecretClient(ctx context.Context, opts SecretProviderOptions) (secret.Client, error) { // etcd is a separate process run for development storage. // data provider already creates an etcd process which can be re-used instead of a new process for secret. - client, err := dataprovider.InitETCDClient(ctx, dataprovider.StorageProviderOptions{ + client, err := databaseprovider.InitETCDClient(ctx, databaseprovider.Options{ ETCD: opts.ETCD, }) if err != nil { diff --git a/pkg/ucp/secret/provider/options.go b/pkg/ucp/secret/secretprovider/options.go similarity index 86% rename from pkg/ucp/secret/provider/options.go rename to pkg/ucp/secret/secretprovider/options.go index 4d414aea25..b10fe006b8 100644 --- a/pkg/ucp/secret/provider/options.go +++ b/pkg/ucp/secret/secretprovider/options.go @@ -14,9 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. */ -package provider +package secretprovider -import "github.com/radius-project/radius/pkg/ucp/dataprovider" +import "github.com/radius-project/radius/pkg/ucp/databaseprovider" // SecretProviderOptions contains provider information of the secret. type SecretProviderOptions struct { @@ -24,7 +24,7 @@ type SecretProviderOptions struct { Provider SecretProviderType `yaml:"provider"` // ETCD configures options for the etcd secret store. - ETCD dataprovider.ETCDOptions `yaml:"etcd,omitempty"` + ETCD databaseprovider.ETCDOptions `yaml:"etcd,omitempty"` // InMemory configures options for the in-memory secret store. InMemory struct{} `yaml:"inmemory,omitempty"` diff --git a/pkg/ucp/secret/provider/provider.go b/pkg/ucp/secret/secretprovider/provider.go similarity index 98% rename from pkg/ucp/secret/provider/provider.go rename to pkg/ucp/secret/secretprovider/provider.go index 8365851aef..feef4f47a3 100644 --- a/pkg/ucp/secret/provider/provider.go +++ b/pkg/ucp/secret/secretprovider/provider.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package provider +package secretprovider import ( "context" diff --git a/pkg/ucp/secret/provider/provider_test.go b/pkg/ucp/secret/secretprovider/provider_test.go similarity index 97% rename from pkg/ucp/secret/provider/provider_test.go rename to pkg/ucp/secret/secretprovider/provider_test.go index 05e3c52d33..2eb8c67597 100644 --- a/pkg/ucp/secret/provider/provider_test.go +++ b/pkg/ucp/secret/secretprovider/provider_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package provider +package secretprovider import ( "context" diff --git a/pkg/ucp/secret/provider/types.go b/pkg/ucp/secret/secretprovider/types.go similarity index 97% rename from pkg/ucp/secret/provider/types.go rename to pkg/ucp/secret/secretprovider/types.go index 72abadf5da..66a0c77ff6 100644 --- a/pkg/ucp/secret/provider/types.go +++ b/pkg/ucp/secret/secretprovider/types.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package provider +package secretprovider // SecretProviderType represents types of secret provider. type SecretProviderType string diff --git a/pkg/ucp/server/server.go b/pkg/ucp/server/server.go index 83a1e9cd8b..5759bc2959 100644 --- a/pkg/ucp/server/server.go +++ b/pkg/ucp/server/server.go @@ -34,13 +34,13 @@ import ( "github.com/radius-project/radius/pkg/ucp/backend" "github.com/radius-project/radius/pkg/ucp/config" "github.com/radius-project/radius/pkg/ucp/data" - "github.com/radius-project/radius/pkg/ucp/dataprovider" + "github.com/radius-project/radius/pkg/ucp/databaseprovider" "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" - qprovider "github.com/radius-project/radius/pkg/ucp/queue/provider" + "github.com/radius-project/radius/pkg/ucp/queue/queueprovider" "github.com/radius-project/radius/pkg/ucp/rest" - "github.com/radius-project/radius/pkg/ucp/secret/provider" + "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" "github.com/radius-project/radius/pkg/ucp/ucplog" kube_rest "k8s.io/client-go/rest" @@ -54,10 +54,10 @@ const ( type Options struct { Config *hostoptions.UCPConfig Port string - StorageProviderOptions dataprovider.StorageProviderOptions + DatabaseProviderOptions databaseprovider.Options LoggingOptions ucplog.LoggingOptions - SecretProviderOptions provider.SecretProviderOptions - QueueProviderOptions qprovider.QueueProviderOptions + SecretProviderOptions secretprovider.SecretProviderOptions + QueueProviderOptions queueprovider.QueueProviderOptions MetricsProviderOptions metricsprovider.MetricsProviderOptions ProfilerProviderOptions profilerprovider.ProfilerProviderOptions TracerProviderOptions trace.Options @@ -89,7 +89,7 @@ func NewServerOptionsFromEnvironment(configFilePath string) (Options, error) { return Options{}, err } - storeOpts := opts.Config.StorageProvider + storeOpts := opts.Config.DatabaseProvider planes := opts.Config.Planes secretOpts := opts.Config.SecretProvider qproviderOpts := opts.Config.QueueProvider @@ -131,7 +131,7 @@ func NewServerOptionsFromEnvironment(configFilePath string) (Options, error) { Port: port, TLSCertDir: tlsCertDir, PathBase: basePath, - StorageProviderOptions: storeOpts, + DatabaseProviderOptions: storeOpts, SecretProviderOptions: secretOpts, QueueProviderOptions: qproviderOpts, MetricsProviderOptions: metricsOpts, @@ -150,24 +150,24 @@ func NewServerOptionsFromEnvironment(configFilePath string) (Options, error) { func NewServer(options *Options) (*hosting.Host, error) { hostingServices := []hosting.Service{ api.NewService(api.ServiceOptions{ - ProviderName: UCPProviderName, - Address: ":" + options.Port, - PathBase: options.PathBase, - Config: options.Config, - Location: options.Location, - TLSCertDir: options.TLSCertDir, - StorageProviderOptions: options.StorageProviderOptions, - SecretProviderOptions: options.SecretProviderOptions, - QueueProviderOptions: options.QueueProviderOptions, - InitialPlanes: options.InitialPlanes, - Identity: options.Identity, - UCPConnection: options.UCPConnection, + ProviderName: UCPProviderName, + Address: ":" + options.Port, + PathBase: options.PathBase, + Config: options.Config, + Location: options.Location, + TLSCertDir: options.TLSCertDir, + DatabaseProviderOptions: options.DatabaseProviderOptions, + SecretProviderOptions: options.SecretProviderOptions, + QueueProviderOptions: options.QueueProviderOptions, + InitialPlanes: options.InitialPlanes, + Identity: options.Identity, + UCPConnection: options.UCPConnection, }), } - if options.StorageProviderOptions.Provider == dataprovider.TypeETCD && - options.StorageProviderOptions.ETCD.InMemory { - hostingServices = append(hostingServices, data.NewEmbeddedETCDService(data.EmbeddedETCDServiceOptions{ClientConfigSink: options.StorageProviderOptions.ETCD.Client})) + if options.DatabaseProviderOptions.Provider == databaseprovider.TypeETCD && + options.DatabaseProviderOptions.ETCD.InMemory { + hostingServices = append(hostingServices, data.NewEmbeddedETCDService(data.EmbeddedETCDServiceOptions{ClientConfigSink: options.DatabaseProviderOptions.ETCD.Client})) } options.MetricsProviderOptions.ServiceName = ServiceName @@ -191,7 +191,7 @@ func NewServer(options *Options) (*hosting.Host, error) { Env: hostopts.EnvironmentOptions{ RoleLocation: options.Config.Location, }, - StorageProvider: options.StorageProviderOptions, + DatabaseProvider: options.DatabaseProviderOptions, SecretProvider: options.SecretProviderOptions, QueueProvider: options.QueueProviderOptions, MetricsProvider: options.MetricsProviderOptions, diff --git a/pkg/ucp/store/mock_storageClient.go b/pkg/ucp/store/mock_storageClient.go deleted file mode 100644 index 8784a9848c..0000000000 --- a/pkg/ucp/store/mock_storageClient.go +++ /dev/null @@ -1,214 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: github.com/radius-project/radius/pkg/ucp/store (interfaces: StorageClient) -// -// Generated by this command: -// -// mockgen -typed -destination=./mock_storageClient.go -package=store -self_package github.com/radius-project/radius/pkg/ucp/store github.com/radius-project/radius/pkg/ucp/store StorageClient -// - -// Package store is a generated GoMock package. -package store - -import ( - context "context" - reflect "reflect" - - gomock "go.uber.org/mock/gomock" -) - -// MockStorageClient is a mock of StorageClient interface. -type MockStorageClient struct { - ctrl *gomock.Controller - recorder *MockStorageClientMockRecorder -} - -// MockStorageClientMockRecorder is the mock recorder for MockStorageClient. -type MockStorageClientMockRecorder struct { - mock *MockStorageClient -} - -// NewMockStorageClient creates a new mock instance. -func NewMockStorageClient(ctrl *gomock.Controller) *MockStorageClient { - mock := &MockStorageClient{ctrl: ctrl} - mock.recorder = &MockStorageClientMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockStorageClient) EXPECT() *MockStorageClientMockRecorder { - return m.recorder -} - -// Delete mocks base method. -func (m *MockStorageClient) Delete(arg0 context.Context, arg1 string, arg2 ...DeleteOptions) error { - m.ctrl.T.Helper() - varargs := []any{arg0, arg1} - for _, a := range arg2 { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "Delete", varargs...) - ret0, _ := ret[0].(error) - return ret0 -} - -// Delete indicates an expected call of Delete. -func (mr *MockStorageClientMockRecorder) Delete(arg0, arg1 any, arg2 ...any) *MockStorageClientDeleteCall { - mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0, arg1}, arg2...) - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockStorageClient)(nil).Delete), varargs...) - return &MockStorageClientDeleteCall{Call: call} -} - -// MockStorageClientDeleteCall wrap *gomock.Call -type MockStorageClientDeleteCall struct { - *gomock.Call -} - -// Return rewrite *gomock.Call.Return -func (c *MockStorageClientDeleteCall) Return(arg0 error) *MockStorageClientDeleteCall { - c.Call = c.Call.Return(arg0) - return c -} - -// Do rewrite *gomock.Call.Do -func (c *MockStorageClientDeleteCall) Do(f func(context.Context, string, ...DeleteOptions) error) *MockStorageClientDeleteCall { - c.Call = c.Call.Do(f) - return c -} - -// DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockStorageClientDeleteCall) DoAndReturn(f func(context.Context, string, ...DeleteOptions) error) *MockStorageClientDeleteCall { - c.Call = c.Call.DoAndReturn(f) - return c -} - -// Get mocks base method. -func (m *MockStorageClient) Get(arg0 context.Context, arg1 string, arg2 ...GetOptions) (*Object, error) { - m.ctrl.T.Helper() - varargs := []any{arg0, arg1} - for _, a := range arg2 { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "Get", varargs...) - ret0, _ := ret[0].(*Object) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Get indicates an expected call of Get. -func (mr *MockStorageClientMockRecorder) Get(arg0, arg1 any, arg2 ...any) *MockStorageClientGetCall { - mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0, arg1}, arg2...) - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockStorageClient)(nil).Get), varargs...) - return &MockStorageClientGetCall{Call: call} -} - -// MockStorageClientGetCall wrap *gomock.Call -type MockStorageClientGetCall struct { - *gomock.Call -} - -// Return rewrite *gomock.Call.Return -func (c *MockStorageClientGetCall) Return(arg0 *Object, arg1 error) *MockStorageClientGetCall { - c.Call = c.Call.Return(arg0, arg1) - return c -} - -// Do rewrite *gomock.Call.Do -func (c *MockStorageClientGetCall) Do(f func(context.Context, string, ...GetOptions) (*Object, error)) *MockStorageClientGetCall { - c.Call = c.Call.Do(f) - return c -} - -// DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockStorageClientGetCall) DoAndReturn(f func(context.Context, string, ...GetOptions) (*Object, error)) *MockStorageClientGetCall { - c.Call = c.Call.DoAndReturn(f) - return c -} - -// Query mocks base method. -func (m *MockStorageClient) Query(arg0 context.Context, arg1 Query, arg2 ...QueryOptions) (*ObjectQueryResult, error) { - m.ctrl.T.Helper() - varargs := []any{arg0, arg1} - for _, a := range arg2 { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "Query", varargs...) - ret0, _ := ret[0].(*ObjectQueryResult) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Query indicates an expected call of Query. -func (mr *MockStorageClientMockRecorder) Query(arg0, arg1 any, arg2 ...any) *MockStorageClientQueryCall { - mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0, arg1}, arg2...) - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Query", reflect.TypeOf((*MockStorageClient)(nil).Query), varargs...) - return &MockStorageClientQueryCall{Call: call} -} - -// MockStorageClientQueryCall wrap *gomock.Call -type MockStorageClientQueryCall struct { - *gomock.Call -} - -// Return rewrite *gomock.Call.Return -func (c *MockStorageClientQueryCall) Return(arg0 *ObjectQueryResult, arg1 error) *MockStorageClientQueryCall { - c.Call = c.Call.Return(arg0, arg1) - return c -} - -// Do rewrite *gomock.Call.Do -func (c *MockStorageClientQueryCall) Do(f func(context.Context, Query, ...QueryOptions) (*ObjectQueryResult, error)) *MockStorageClientQueryCall { - c.Call = c.Call.Do(f) - return c -} - -// DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockStorageClientQueryCall) DoAndReturn(f func(context.Context, Query, ...QueryOptions) (*ObjectQueryResult, error)) *MockStorageClientQueryCall { - c.Call = c.Call.DoAndReturn(f) - return c -} - -// Save mocks base method. -func (m *MockStorageClient) Save(arg0 context.Context, arg1 *Object, arg2 ...SaveOptions) error { - m.ctrl.T.Helper() - varargs := []any{arg0, arg1} - for _, a := range arg2 { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "Save", varargs...) - ret0, _ := ret[0].(error) - return ret0 -} - -// Save indicates an expected call of Save. -func (mr *MockStorageClientMockRecorder) Save(arg0, arg1 any, arg2 ...any) *MockStorageClientSaveCall { - mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0, arg1}, arg2...) - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Save", reflect.TypeOf((*MockStorageClient)(nil).Save), varargs...) - return &MockStorageClientSaveCall{Call: call} -} - -// MockStorageClientSaveCall wrap *gomock.Call -type MockStorageClientSaveCall struct { - *gomock.Call -} - -// Return rewrite *gomock.Call.Return -func (c *MockStorageClientSaveCall) Return(arg0 error) *MockStorageClientSaveCall { - c.Call = c.Call.Return(arg0) - return c -} - -// Do rewrite *gomock.Call.Do -func (c *MockStorageClientSaveCall) Do(f func(context.Context, *Object, ...SaveOptions) error) *MockStorageClientSaveCall { - c.Call = c.Call.Do(f) - return c -} - -// DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockStorageClientSaveCall) DoAndReturn(f func(context.Context, *Object, ...SaveOptions) error) *MockStorageClientSaveCall { - c.Call = c.Call.DoAndReturn(f) - return c -} diff --git a/pkg/ucp/trackedresource/update.go b/pkg/ucp/trackedresource/update.go index 49f9de3c8c..813d4273ed 100644 --- a/pkg/ucp/trackedresource/update.go +++ b/pkg/ucp/trackedresource/update.go @@ -29,9 +29,9 @@ import ( "github.com/go-logr/logr" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/store" "github.com/radius-project/radius/pkg/ucp/ucplog" ) @@ -42,9 +42,9 @@ const ( ) // NewUpdater creates a new Updater. -func NewUpdater(storeClient store.StorageClient, httpClient *http.Client) *Updater { +func NewUpdater(databaseClient database.Client, httpClient *http.Client) *Updater { return &Updater{ - Store: storeClient, + DatabaseClient: databaseClient, Client: httpClient, AttemptCount: retryCount, RetryDelay: retryDelay, @@ -54,8 +54,8 @@ func NewUpdater(storeClient store.StorageClient, httpClient *http.Client) *Updat // Updater is a utility struct that can perform updates on tracked resources. type Updater struct { - // Store is the storage client used to access the database. - Store store.StorageClient + // DatabaseClient is the database client used to access the database. + DatabaseClient database.Client // Client is the HTTP client used to make requests to the downstream API. Client *http.Client @@ -153,8 +153,8 @@ func (u *Updater) Update(ctx context.Context, downstream string, id resources.ID func (u *Updater) run(ctx context.Context, id resources.ID, trackingID resources.ID, destination *url.URL, apiVersion string) error { logger := ucplog.FromContextOrDiscard(ctx) - obj, err := u.Store.Get(ctx, trackingID.String()) - if errors.Is(err, &store.ErrNotFound{}) { + obj, err := u.DatabaseClient.Get(ctx, trackingID.String()) + if errors.Is(err, &database.ErrNotFound{}) { // This is fine. It might be a new resource. } else if err != nil { return err @@ -179,8 +179,8 @@ func (u *Updater) run(ctx context.Context, id resources.ID, trackingID resources if data == nil { // Resource was not found. We can delete the tracked resource entry. logger.V(ucplog.LevelDebug).Info("deleting tracked resource entry") - err = u.Store.Delete(ctx, trackingID.String(), store.WithETag(etag)) - if errors.Is(err, &store.ErrNotFound{}) { + err = u.DatabaseClient.Delete(ctx, trackingID.String(), database.WithETag(etag)) + if errors.Is(err, &database.ErrNotFound{}) { return nil } else if err != nil { return err @@ -201,15 +201,15 @@ func (u *Updater) run(ctx context.Context, id resources.ID, trackingID resources entry.AsyncProvisioningState = *data.Properties.ProvisioningState } - obj = &store.Object{ - Metadata: store.Metadata{ + obj = &database.Object{ + Metadata: database.Metadata{ ID: trackingID.String(), }, Data: entry, } logger.V(ucplog.LevelDebug).Info("updating tracked resource entry") - err = u.Store.Save(ctx, obj, store.WithETag(etag)) - if errors.Is(err, &store.ErrConcurrency{}) { + err = u.DatabaseClient.Save(ctx, obj, database.WithETag(etag)) + if errors.Is(err, &database.ErrConcurrency{}) { logger.V(ucplog.LevelDebug).Info("tracked resource was updated concurrently") return &InProgressErr{} } else if err != nil { diff --git a/pkg/ucp/trackedresource/update_test.go b/pkg/ucp/trackedresource/update_test.go index 98d3ba173b..2a2c475cc9 100644 --- a/pkg/ucp/trackedresource/update_test.go +++ b/pkg/ucp/trackedresource/update_test.go @@ -28,8 +28,8 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" "github.com/radius-project/radius/pkg/to" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/datamodel" - "github.com/radius-project/radius/pkg/ucp/store" "github.com/radius-project/radius/test/testcontext" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -45,12 +45,12 @@ var ( }() ) -func setupUpdater(t *testing.T) (*Updater, *store.MockStorageClient, *mockRoundTripper) { +func setupUpdater(t *testing.T) (*Updater, *database.MockClient, *mockRoundTripper) { ctrl := gomock.NewController(t) - storeClient := store.NewMockStorageClient(ctrl) + databaseClient := database.NewMockClient(ctrl) roundTripper := &mockRoundTripper{} - updater := NewUpdater(storeClient, &http.Client{Transport: roundTripper}) + updater := NewUpdater(databaseClient, &http.Client{Transport: roundTripper}) // Optimize these values for testability. We don't want to wait for retries or timeouts unless // the test is specifically testing that behavior. @@ -58,12 +58,12 @@ func setupUpdater(t *testing.T) (*Updater, *store.MockStorageClient, *mockRoundT updater.AttemptCount = 1 updater.RequestTimeout = time.Microsecond * 100 - return updater, storeClient, roundTripper + return updater, databaseClient, roundTripper } func Test_Update(t *testing.T) { t.Run("successful update", func(t *testing.T) { - updater, storeClient, roundTripper := setupUpdater(t) + updater, databaseClient, roundTripper := setupUpdater(t) apiVersion := "1234" resource := map[string]any{ @@ -73,17 +73,17 @@ func Test_Update(t *testing.T) { "properties": map[string]any{}, } - storeClient.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), IDFor(testID).String()). - Return(nil, &store.ErrNotFound{}). + Return(nil, &database.ErrNotFound{}). Times(1) // Mock a successful (terminal) response from the downstream API. roundTripper.RespondWithJSON(t, http.StatusOK, resource) - storeClient.EXPECT(). + databaseClient.EXPECT(). Save(gomock.Any(), gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, obj *store.Object, options ...store.SaveOptions) error { + DoAndReturn(func(ctx context.Context, obj *database.Object, options ...database.SaveOptions) error { require.Equal(t, IDFor(testID).String(), obj.ID) dm := obj.Data.(*datamodel.GenericResource) @@ -99,7 +99,7 @@ func Test_Update(t *testing.T) { }) t.Run("retry then success", func(t *testing.T) { - updater, storeClient, roundTripper := setupUpdater(t) + updater, databaseClient, roundTripper := setupUpdater(t) updater.AttemptCount = 2 apiVersion := "1234" @@ -111,21 +111,21 @@ func Test_Update(t *testing.T) { } // Fail once, then succeed. - storeClient.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), IDFor(testID).String()). Return(nil, errors.New("this will be retried")). Times(1) - storeClient.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), IDFor(testID).String()). - Return(nil, &store.ErrNotFound{}). + Return(nil, &database.ErrNotFound{}). Times(1) // Mock a successful (terminal) response from the downstream API. roundTripper.RespondWithJSON(t, http.StatusOK, resource) - storeClient.EXPECT(). + databaseClient.EXPECT(). Save(gomock.Any(), gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, obj *store.Object, options ...store.SaveOptions) error { + DoAndReturn(func(ctx context.Context, obj *database.Object, options ...database.SaveOptions) error { require.Equal(t, IDFor(testID).String(), obj.ID) dm := obj.Data.(*datamodel.GenericResource) @@ -141,7 +141,7 @@ func Test_Update(t *testing.T) { }) t.Run("resource still provisioning", func(t *testing.T) { - updater, storeClient, roundTripper := setupUpdater(t) + updater, databaseClient, roundTripper := setupUpdater(t) apiVersion := "1234" resource := map[string]any{ @@ -153,9 +153,9 @@ func Test_Update(t *testing.T) { }, } - storeClient.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), IDFor(testID).String()). - Return(nil, &store.ErrNotFound{}). + Return(nil, &database.ErrNotFound{}). Times(1) // Mock a successful (non-terminal) response from the downstream API. @@ -167,7 +167,7 @@ func Test_Update(t *testing.T) { }) t.Run("tracked resource updated concurrently", func(t *testing.T) { - updater, storeClient, roundTripper := setupUpdater(t) + updater, databaseClient, roundTripper := setupUpdater(t) apiVersion := "1234" resource := map[string]any{ @@ -176,15 +176,15 @@ func Test_Update(t *testing.T) { "type": testID.Type(), } - storeClient.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), IDFor(testID).String()). - Return(nil, &store.ErrNotFound{}). + Return(nil, &database.ErrNotFound{}). Times(1) // Fail the "Save" operation due to a concurrent update. - storeClient.EXPECT(). + databaseClient.EXPECT(). Save(gomock.Any(), gomock.Any(), gomock.Any()). - Return(&store.ErrConcurrency{}). + Return(&database.ErrConcurrency{}). Times(1) // Mock a successful (non-terminal) response from the downstream API. @@ -196,13 +196,13 @@ func Test_Update(t *testing.T) { }) t.Run("retries exhausted", func(t *testing.T) { - updater, storeClient, _ := setupUpdater(t) + updater, databaseClient, _ := setupUpdater(t) updater.AttemptCount = 3 apiVersion := "1234" // Fail enough times to exhaust our retries. - storeClient.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), IDFor(testID).String()). Return(nil, errors.New("this will be retried")). Times(3) @@ -217,7 +217,7 @@ func Test_run(t *testing.T) { apiVersion := "1234" t.Run("successful update (new resource)", func(t *testing.T) { - updater, storeClient, roundTripper := setupUpdater(t) + updater, databaseClient, roundTripper := setupUpdater(t) resource := map[string]any{ "id": testID.String(), @@ -226,17 +226,17 @@ func Test_run(t *testing.T) { "properties": map[string]any{}, } - storeClient.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), IDFor(testID).String()). - Return(nil, &store.ErrNotFound{}). + Return(nil, &database.ErrNotFound{}). Times(1) // Mock a successful (terminal) response from the downstream API. roundTripper.RespondWithJSON(t, http.StatusOK, resource) - storeClient.EXPECT(). + databaseClient.EXPECT(). Save(gomock.Any(), gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, obj *store.Object, options ...store.SaveOptions) error { + DoAndReturn(func(ctx context.Context, obj *database.Object, options ...database.SaveOptions) error { require.Equal(t, IDFor(testID).String(), obj.ID) dm := obj.Data.(*datamodel.GenericResource) @@ -252,7 +252,7 @@ func Test_run(t *testing.T) { }) t.Run("successful update (existing resource)", func(t *testing.T) { - updater, storeClient, roundTripper := setupUpdater(t) + updater, databaseClient, roundTripper := setupUpdater(t) resource := map[string]any{ "id": testID.String(), @@ -265,17 +265,17 @@ func Test_run(t *testing.T) { dm := datamodel.GenericResourceFromID(testID, IDFor(testID)) dm.Properties.APIVersion = apiVersion - storeClient.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), IDFor(testID).String()). - Return(&store.Object{Metadata: store.Metadata{ETag: etag}, Data: dm}, nil). + Return(&database.Object{Metadata: database.Metadata{ETag: etag}, Data: dm}, nil). Times(1) // Mock a successful (terminal) response from the downstream API. roundTripper.RespondWithJSON(t, http.StatusOK, resource) - storeClient.EXPECT(). + databaseClient.EXPECT(). Save(gomock.Any(), gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, obj *store.Object, options ...store.SaveOptions) error { + DoAndReturn(func(ctx context.Context, obj *database.Object, options ...database.SaveOptions) error { require.Equal(t, IDFor(testID).String(), obj.ID) dm := obj.Data.(*datamodel.GenericResource) @@ -291,21 +291,21 @@ func Test_run(t *testing.T) { }) t.Run("successful delete", func(t *testing.T) { - updater, storeClient, roundTripper := setupUpdater(t) + updater, databaseClient, roundTripper := setupUpdater(t) etag := "some-etag" dm := datamodel.GenericResourceFromID(testID, IDFor(testID)) dm.Properties.APIVersion = apiVersion - storeClient.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), IDFor(testID).String()). - Return(&store.Object{Metadata: store.Metadata{ETag: etag}, Data: dm}, nil). + Return(&database.Object{Metadata: database.Metadata{ETag: etag}, Data: dm}, nil). Times(1) // Mock a successful (terminal) response from the downstream API. roundTripper.RespondWithJSON(t, http.StatusNotFound, &v1.ErrorResponse{Error: v1.ErrorDetails{Code: v1.CodeNotFound}}) - storeClient.EXPECT(). + databaseClient.EXPECT(). Delete(gomock.Any(), IDFor(testID).String(), gomock.Any()). Return(nil). Times(1) @@ -315,7 +315,7 @@ func Test_run(t *testing.T) { }) t.Run("resource still provisioning", func(t *testing.T) { - updater, storeClient, roundTripper := setupUpdater(t) + updater, databaseClient, roundTripper := setupUpdater(t) resource := map[string]any{ "id": testID.String(), @@ -326,9 +326,9 @@ func Test_run(t *testing.T) { }, } - storeClient.EXPECT(). + databaseClient.EXPECT(). Get(gomock.Any(), IDFor(testID).String()). - Return(nil, &store.ErrNotFound{}). + Return(nil, &database.ErrNotFound{}). Times(1) // Mock a successful (terminal) response from the downstream API. diff --git a/test/ucp/kubeenv/testenv.go b/test/ucp/kubeenv/testenv.go index a800c309c9..2c78248704 100644 --- a/test/ucp/kubeenv/testenv.go +++ b/test/ucp/kubeenv/testenv.go @@ -23,7 +23,7 @@ import ( "os" "os/exec" - ucpv1alpha1 "github.com/radius-project/radius/pkg/ucp/store/apiserverstore/api/ucp.dev/v1alpha1" + ucpv1alpha1 "github.com/radius-project/radius/pkg/ucp/database/apiserverstore/api/ucp.dev/v1alpha1" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" diff --git a/test/ucp/storetest/shared.go b/test/ucp/storetest/shared.go index 6dcd7da34c..0ab6e761bb 100644 --- a/test/ucp/storetest/shared.go +++ b/test/ucp/storetest/shared.go @@ -21,8 +21,8 @@ import ( "encoding/json" "testing" + "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/store" "github.com/radius-project/radius/pkg/ucp/util/etag" "github.com/radius-project/radius/test/testcontext" "github.com/stretchr/testify/require" @@ -143,16 +143,16 @@ func parseOrPanic(id string) resources.ID { return parsed } -func createObject(id resources.ID, data any) store.Object { - return store.Object{ - Metadata: store.Metadata{ +func createObject(id resources.ID, data any) database.Object { + return database.Object{ + Metadata: database.Metadata{ ID: id.String(), }, Data: data, } } -func compareObjects(t *testing.T, expected *store.Object, actual *store.Object) { +func compareObjects(t *testing.T, expected *database.Object, actual *database.Object) { t.Helper() // Compare everything except ETags @@ -166,13 +166,13 @@ func compareObjects(t *testing.T, expected *store.Object, actual *store.Object) } // CompareObjectLists compares two slices of store.Objects, ignoring their ETags. -func CompareObjectLists(t *testing.T, expected []store.Object, actual []store.Object) { +func CompareObjectLists(t *testing.T, expected []database.Object, actual []database.Object) { t.Helper() - expectedCopy := []store.Object{} + expectedCopy := []database.Object{} expectedCopy = append(expectedCopy, expected...) - actualCopy := []store.Object{} + actualCopy := []database.Object{} actualCopy = append(actualCopy, actual...) // Compare everything except ETags @@ -187,10 +187,10 @@ func CompareObjectLists(t *testing.T, expected []store.Object, actual []store.Ob require.ElementsMatch(t, expectedCopy, actualCopy) } -// This function tests the StorageClient's Get, Save and Delete methods by creating, updating and deleting objects with +// This function tests the database Client's Get, Save and Delete methods by creating, updating and deleting objects with // different IDs and scopes, and checks the results of various query scenarios with different filters and scopes. It also // checks that the expected objects are returned. -func RunTest(t *testing.T, client store.StorageClient, clear func(t *testing.T)) { +func RunTest(t *testing.T, client database.Client, clear func(t *testing.T)) { ctx, cancel := testcontext.NewWithCancel(t) t.Cleanup(cancel) @@ -198,7 +198,7 @@ func RunTest(t *testing.T, client store.StorageClient, clear func(t *testing.T)) clear(t) obj, err := client.Get(ctx, Resource1ID.String()) - require.ErrorIs(t, err, &store.ErrNotFound{ID: Resource1ID.String()}) + require.ErrorIs(t, err, &database.ErrNotFound{ID: Resource1ID.String()}) require.Nil(t, obj) }) @@ -206,7 +206,7 @@ func RunTest(t *testing.T, client store.StorageClient, clear func(t *testing.T)) clear(t) err := client.Delete(ctx, Resource1ID.String()) - require.ErrorIs(t, err, &store.ErrNotFound{ID: Resource1ID.String()}) + require.ErrorIs(t, err, &database.ErrNotFound{ID: Resource1ID.String()}) }) t.Run("save_and_get_arm", func(t *testing.T) { @@ -275,7 +275,7 @@ func RunTest(t *testing.T, client store.StorageClient, clear func(t *testing.T)) require.NotEmpty(t, obj1.ETag) obj1.Data = Data2 - err = client.Save(ctx, &obj1, store.WithETag(obj1.ETag)) + err = client.Save(ctx, &obj1, database.WithETag(obj1.ETag)) require.NoError(t, err) obj1Get, err := client.Get(ctx, Resource1ID.String()) @@ -291,8 +291,8 @@ func RunTest(t *testing.T, client store.StorageClient, clear func(t *testing.T)) require.NoError(t, err) obj1.Data = Data2 - err = client.Save(ctx, &obj1, store.WithETag(etag.New(MarshalOrPanic(Data2)))) - require.ErrorIs(t, err, &store.ErrConcurrency{}) + err = client.Save(ctx, &obj1, database.WithETag(etag.New(MarshalOrPanic(Data2)))) + require.ErrorIs(t, err, &database.ErrConcurrency{}) obj1.Data = Data1 obj1Get, err := client.Get(ctx, Resource1ID.String()) @@ -305,11 +305,11 @@ func RunTest(t *testing.T, client store.StorageClient, clear func(t *testing.T)) obj1 := createObject(Resource1ID, Data1) - err := client.Save(ctx, &obj1, store.WithETag(etag.New(MarshalOrPanic(Data1)))) - require.ErrorIs(t, err, &store.ErrConcurrency{}) + err := client.Save(ctx, &obj1, database.WithETag(etag.New(MarshalOrPanic(Data1)))) + require.ErrorIs(t, err, &database.ErrConcurrency{}) obj1Get, err := client.Get(ctx, Resource1ID.String()) - require.ErrorIs(t, err, &store.ErrNotFound{ID: Resource1ID.String()}) + require.ErrorIs(t, err, &database.ErrNotFound{ID: Resource1ID.String()}) require.Nil(t, obj1Get) }) @@ -336,7 +336,7 @@ func RunTest(t *testing.T, client store.StorageClient, clear func(t *testing.T)) require.NoError(t, err) obj1Get, err := client.Get(ctx, Resource1ID.String()) - require.ErrorIs(t, err, &store.ErrNotFound{ID: Resource1ID.String()}) + require.ErrorIs(t, err, &database.ErrNotFound{ID: Resource1ID.String()}) require.Nil(t, obj1Get) }) @@ -347,11 +347,11 @@ func RunTest(t *testing.T, client store.StorageClient, clear func(t *testing.T)) err := client.Save(ctx, &obj1) require.NoError(t, err) - err = client.Delete(ctx, Resource1ID.String(), store.WithETag(obj1.ETag)) + err = client.Delete(ctx, Resource1ID.String(), database.WithETag(obj1.ETag)) require.NoError(t, err) obj1Get, err := client.Get(ctx, Resource1ID.String()) - require.ErrorIs(t, err, &store.ErrNotFound{ID: Resource1ID.String()}) + require.ErrorIs(t, err, &database.ErrNotFound{ID: Resource1ID.String()}) require.Nil(t, obj1Get) }) @@ -362,8 +362,8 @@ func RunTest(t *testing.T, client store.StorageClient, clear func(t *testing.T)) err := client.Save(ctx, &obj1) require.NoError(t, err) - err = client.Delete(ctx, Resource1ID.String(), store.WithETag(etag.New(MarshalOrPanic(Data2)))) - require.ErrorIs(t, err, &store.ErrConcurrency{}) + err = client.Delete(ctx, Resource1ID.String(), database.WithETag(etag.New(MarshalOrPanic(Data2)))) + require.ErrorIs(t, err, &database.ErrConcurrency{}) obj1Get, err := client.Get(ctx, Resource1ID.String()) require.NoError(t, err) @@ -373,14 +373,14 @@ func RunTest(t *testing.T, client store.StorageClient, clear func(t *testing.T)) t.Run("delete_cannot_delete_missing_resource_with_not_matching_etag", func(t *testing.T) { clear(t) - err := client.Delete(ctx, Resource1ID.String(), store.WithETag(etag.New(MarshalOrPanic(Data1)))) - require.ErrorIs(t, err, &store.ErrConcurrency{}) + err := client.Delete(ctx, Resource1ID.String(), database.WithETag(etag.New(MarshalOrPanic(Data1)))) + require.ErrorIs(t, err, &database.ErrConcurrency{}) }) t.Run("list_can_be_empty", func(t *testing.T) { clear(t) - objs, err := client.Query(ctx, store.Query{RootScope: RadiusScope, ResourceType: "asdf"}) + objs, err := client.Query(ctx, database.Query{RootScope: RadiusScope, ResourceType: "asdf"}) require.NoError(t, err) require.Empty(t, objs) }) @@ -425,9 +425,9 @@ func RunTest(t *testing.T, client store.StorageClient, clear func(t *testing.T)) require.NoError(t, err) t.Run("query_resources_at_resource_group_scope", func(t *testing.T) { - objs, err := client.Query(ctx, store.Query{RootScope: ResourceGroup1Scope, ResourceType: NestedResourceType1}) + objs, err := client.Query(ctx, database.Query{RootScope: ResourceGroup1Scope, ResourceType: NestedResourceType1}) require.NoError(t, err) - expected := []store.Object{ + expected := []database.Object{ nested1, nested2, nested3, @@ -437,19 +437,19 @@ func RunTest(t *testing.T, client store.StorageClient, clear func(t *testing.T)) }) t.Run("query_resources_at_resource_group_scope_with_field_filter", func(t *testing.T) { - filters := []store.QueryFilter{{Field: "value", Value: "n1"}} - objs, err := client.Query(ctx, store.Query{RootScope: ResourceGroup1Scope, ResourceType: NestedResourceType1, Filters: filters}) + filters := []database.QueryFilter{{Field: "value", Value: "n1"}} + objs, err := client.Query(ctx, database.Query{RootScope: ResourceGroup1Scope, ResourceType: NestedResourceType1, Filters: filters}) require.NoError(t, err) - expected := []store.Object{ + expected := []database.Object{ nested1, } CompareObjectLists(t, expected, objs.Items) }) t.Run("query_resources_at_resource_group_scope_with_prefix", func(t *testing.T) { - objs, err := client.Query(ctx, store.Query{RootScope: ResourceGroup1Scope, ResourceType: NestedResourceType1, RoutingScopePrefix: ResourcePath1}) + objs, err := client.Query(ctx, database.Query{RootScope: ResourceGroup1Scope, ResourceType: NestedResourceType1, RoutingScopePrefix: ResourcePath1}) require.NoError(t, err) - expected := []store.Object{ + expected := []database.Object{ nested1, nested2, } @@ -457,22 +457,22 @@ func RunTest(t *testing.T, client store.StorageClient, clear func(t *testing.T)) }) t.Run("query_scopes_at_resource_group_scope", func(t *testing.T) { - objs, err := client.Query(ctx, store.Query{RootScope: ResourceGroup1Scope, IsScopeQuery: true, ResourceType: "resourceGroups"}) + objs, err := client.Query(ctx, database.Query{RootScope: ResourceGroup1Scope, IsScopeQuery: true, ResourceType: "resourceGroups"}) require.NoError(t, err) - expected := []store.Object{} + expected := []database.Object{} CompareObjectLists(t, expected, objs.Items) }) t.Run("query_resources_at_plane_scope", func(t *testing.T) { - objs, err := client.Query(ctx, store.Query{RootScope: RadiusScope, ResourceType: ResourceType1}) + objs, err := client.Query(ctx, database.Query{RootScope: RadiusScope, ResourceType: ResourceType1}) require.NoError(t, err) require.Empty(t, objs) }) t.Run("query_resources_at_plane_scope_recursive", func(t *testing.T) { - objs, err := client.Query(ctx, store.Query{RootScope: RadiusScope, ScopeRecursive: true, ResourceType: NestedResourceType1}) + objs, err := client.Query(ctx, database.Query{RootScope: RadiusScope, ScopeRecursive: true, ResourceType: NestedResourceType1}) require.NoError(t, err) - expected := []store.Object{ + expected := []database.Object{ nested1, nested2, nested3, @@ -482,19 +482,19 @@ func RunTest(t *testing.T, client store.StorageClient, clear func(t *testing.T)) }) t.Run("query_resources_at_plane_scope_recursive_with_field_filter", func(t *testing.T) { - filters := []store.QueryFilter{{Field: "value", Value: "1"}} - objs, err := client.Query(ctx, store.Query{RootScope: RadiusScope, ScopeRecursive: true, ResourceType: ResourceType1, Filters: filters}) + filters := []database.QueryFilter{{Field: "value", Value: "1"}} + objs, err := client.Query(ctx, database.Query{RootScope: RadiusScope, ScopeRecursive: true, ResourceType: ResourceType1, Filters: filters}) require.NoError(t, err) - expected := []store.Object{ + expected := []database.Object{ obj1, } CompareObjectLists(t, expected, objs.Items) }) t.Run("query_resources_at_plane_scope_recursive_with_prefix", func(t *testing.T) { - objs, err := client.Query(ctx, store.Query{RootScope: RadiusScope, ScopeRecursive: true, ResourceType: NestedResourceType1, RoutingScopePrefix: ResourcePath1}) + objs, err := client.Query(ctx, database.Query{RootScope: RadiusScope, ScopeRecursive: true, ResourceType: NestedResourceType1, RoutingScopePrefix: ResourcePath1}) require.NoError(t, err) - expected := []store.Object{ + expected := []database.Object{ nested1, nested2, } @@ -502,18 +502,18 @@ func RunTest(t *testing.T, client store.StorageClient, clear func(t *testing.T)) }) t.Run("query_scopes_at_plane_scope", func(t *testing.T) { - objs, err := client.Query(ctx, store.Query{RootScope: PlaneScope, IsScopeQuery: true, ResourceType: "radius"}) + objs, err := client.Query(ctx, database.Query{RootScope: PlaneScope, IsScopeQuery: true, ResourceType: "radius"}) require.NoError(t, err) - expected := []store.Object{ + expected := []database.Object{ plane1, } CompareObjectLists(t, expected, objs.Items) }) t.Run("query_scopes_at_plane_scope_recursive", func(t *testing.T) { - objs, err := client.Query(ctx, store.Query{RootScope: RadiusScope, ScopeRecursive: true, IsScopeQuery: true, ResourceType: "resourcegroups"}) + objs, err := client.Query(ctx, database.Query{RootScope: RadiusScope, ScopeRecursive: true, IsScopeQuery: true, ResourceType: "resourcegroups"}) require.NoError(t, err) - expected := []store.Object{ + expected := []database.Object{ group1, group2, } @@ -521,18 +521,18 @@ func RunTest(t *testing.T, client store.StorageClient, clear func(t *testing.T)) }) t.Run("query_scopes_with_filter_non_matching", func(t *testing.T) { - objs, err := client.Query(ctx, store.Query{RootScope: RadiusScope, IsScopeQuery: true, ResourceType: "resourcegroups", Filters: []store.QueryFilter{ + objs, err := client.Query(ctx, database.Query{RootScope: RadiusScope, IsScopeQuery: true, ResourceType: "resourcegroups", Filters: []database.QueryFilter{ {Field: "value", Value: "asdf"}, }}) require.NoError(t, err) - expected := []store.Object{} + expected := []database.Object{} CompareObjectLists(t, expected, objs.Items) }) t.Run("query_scopes_at_plane_scope_recursive", func(t *testing.T) { - objs, err := client.Query(ctx, store.Query{RootScope: RadiusScope, ScopeRecursive: true, IsScopeQuery: true, ResourceType: "resourcegroups"}) + objs, err := client.Query(ctx, database.Query{RootScope: RadiusScope, ScopeRecursive: true, IsScopeQuery: true, ResourceType: "resourcegroups"}) require.NoError(t, err) - expected := []store.Object{ + expected := []database.Object{ group1, group2, } @@ -540,10 +540,10 @@ func RunTest(t *testing.T, client store.StorageClient, clear func(t *testing.T)) }) t.Run("query_scopes_at_plane_scope_recursive_with_field_filter", func(t *testing.T) { - filters := []store.QueryFilter{{Field: "value", Value: "1"}} - objs, err := client.Query(ctx, store.Query{RootScope: RadiusScope, ScopeRecursive: true, IsScopeQuery: true, Filters: filters, ResourceType: "resourcegroups"}) + filters := []database.QueryFilter{{Field: "value", Value: "1"}} + objs, err := client.Query(ctx, database.Query{RootScope: RadiusScope, ScopeRecursive: true, IsScopeQuery: true, Filters: filters, ResourceType: "resourcegroups"}) require.NoError(t, err) - expected := []store.Object{ + expected := []database.Object{ group1, } CompareObjectLists(t, expected, objs.Items) From d87e8548d4b6415643f0c034b30feffc5eef41d1 Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Wed, 18 Dec 2024 11:04:50 -0800 Subject: [PATCH 11/37] Move database and similar packages to components (#8148) # Description This change moves and rearranges some our packages so they are easy to reference. The new `components` folder is for shared components that are used in all of our code. Previously these components were part of UCP which doesn't make sense, they are used by all of our control-plane services. ## Type of change - This pull request is a minor refactor, code cleanup, test improvement, or other maintenance task and doesn't change the functionality of Radius (issue link optional). ## Contributor checklist Please verify that the PR meets the following requirements, where applicable: - [ ] An overview of proposed schema changes is included in a linked GitHub issue. - [ ] A design document PR is created in the [design-notes repository](https://github.com/radius-project/design-notes/), if new APIs are being introduced. - [ ] If applicable, design document has been reviewed and approved by Radius maintainers/approvers. - [ ] A PR for the [samples repository](https://github.com/radius-project/samples) is created, if existing samples are affected by the changes in this PR. - [ ] A PR for the [documentation repository](https://github.com/radius-project/docs) is created, if the changes in this PR affect the documentation or any user facing updates are made. - [ ] A PR for the [recipes repository](https://github.com/radius-project/recipes) is created, if existing recipes are affected by the changes in this PR. Signed-off-by: Ryan Nowak --- build/generate.mk | 4 +- cmd/applications-rp/cmd/root.go | 2 +- cmd/ucpd/cmd/root.go | 2 +- .../asyncoperation/controller/controller.go | 2 +- .../statusmanager/statusmanager.go | 4 +- .../statusmanager/statusmanager_test.go | 4 +- .../asyncoperation/worker/registry_test.go | 2 +- pkg/armrpc/asyncoperation/worker/service.go | 6 +- pkg/armrpc/asyncoperation/worker/worker.go | 4 +- .../worker/worker_runoperation_test.go | 8 +-- .../asyncoperation/worker/worker_test.go | 2 +- pkg/armrpc/builder/builder_test.go | 2 +- pkg/armrpc/builder/namespace_test.go | 2 +- pkg/armrpc/frontend/controller/controller.go | 2 +- .../frontend/controller/controller_test.go | 2 +- pkg/armrpc/frontend/controller/operation.go | 2 +- .../defaultasyncdelete_test.go | 2 +- .../defaultoperation/defaultasyncput_test.go | 2 +- .../defaultoperation/defaultsyncdelete.go | 2 +- .../defaultsyncdelete_test.go | 2 +- .../defaultoperation/defaultsyncput_test.go | 2 +- .../defaultoperation/getoperationresult.go | 2 +- .../getoperationresult_test.go | 2 +- .../defaultoperation/getoperationstatus.go | 2 +- .../getoperationstatus_test.go | 2 +- .../defaultoperation/getresource_test.go | 2 +- .../defaultoperation/listresources.go | 2 +- .../defaultoperation/listresources_test.go | 2 +- .../defaultoperation/resource_test.go | 2 +- pkg/armrpc/frontend/server/handler_test.go | 2 +- pkg/armrpc/frontend/server/service.go | 4 +- pkg/armrpc/hostoptions/hostoptions.go | 2 +- pkg/armrpc/hostoptions/providerconfig.go | 6 +- pkg/armrpc/rpctest/controllers.go | 2 +- .../api/ucp.dev/v1alpha1/groupversion_info.go | 0 .../ucp.dev/v1alpha1/queuemessage_types.go | 0 .../api/ucp.dev/v1alpha1/resource_types.go | 0 .../ucp.dev/v1alpha1/zz_generated.deepcopy.go | 0 .../apiserverstore/apiserverclient.go | 6 +- .../apiserverstore/apiserverclient_test.go | 4 +- pkg/{ucp => components}/database/client.go | 2 +- .../database/client_test.go | 0 .../database}/databaseprovider/factory.go | 12 ++-- .../database}/databaseprovider/options.go | 0 .../databaseprovider/storageprovider.go | 2 +- .../databaseprovider/storageprovider_test.go | 2 +- .../database}/databaseprovider/types.go | 0 .../database/databaseutil/doc.go | 0 .../database/databaseutil/id.go | 2 +- .../database/databaseutil/id_test.go | 2 +- pkg/{ucp => components}/database/err.go | 0 .../database/etcdstore/etcdclient.go | 4 +- .../database/etcdstore/etcdclient_test.go | 0 pkg/{ucp => components}/database/filter.go | 0 .../database/filter_test.go | 0 .../database/inmemory/client.go | 4 +- .../database/inmemory/client_test.go | 0 .../database/inmemory/doc.go | 0 pkg/{ucp => components}/database/map.go | 0 pkg/{ucp => components}/database/map_test.go | 0 .../database/mock_client.go | 4 +- pkg/{ucp => components}/database/object.go | 0 pkg/{ucp => components}/database/options.go | 0 .../database/postgres/postgresclient.go | 4 +- .../database/postgres/postgresclient_test.go | 0 pkg/{ucp => components}/database/resources.go | 0 pkg/components/doc.go | 18 ++++++ .../queue/apiserver/client.go | 43 +++++++------ .../queue/apiserver/client_test.go | 18 +++--- .../client => components/queue}/client.go | 4 +- .../queue}/client_test.go | 2 +- .../queue/inmemory/client.go | 20 +++--- .../queue/inmemory/client_test.go | 6 +- .../queue/inmemory/queue.go | 18 +++--- .../queue/inmemory/queue_test.go | 12 ++-- .../client => components/queue}/message.go | 2 +- .../queue}/mock_client.go | 8 +-- .../client => components/queue}/options.go | 2 +- .../queue/queueprovider/factory.go | 8 +-- .../queue/queueprovider/options.go | 0 .../queue/queueprovider/provider.go | 2 +- .../queue/queueprovider/provider_test.go | 0 .../queue/queueprovider/types.go | 0 pkg/{ucp => components}/secret/client.go | 2 +- pkg/{ucp => components}/secret/client_test.go | 0 pkg/{ucp => components}/secret/etcd/client.go | 2 +- .../secret/etcd/client_test.go | 2 +- .../secret/inmemory/client.go | 2 +- .../secret/inmemory/client_test.go | 2 +- .../secret/kubernetes/client.go | 2 +- .../secret/kubernetes/client_test.go | 2 +- pkg/{ucp => components}/secret/mock_client.go | 4 +- .../secret/secretprovider/factory.go | 12 ++-- .../secret/secretprovider/options.go | 2 +- .../secret/secretprovider/provider.go | 2 +- .../secret/secretprovider/provider_test.go | 0 .../secret/secretprovider/types.go | 0 .../controller/createorupdateresource.go | 2 +- .../controller/createorupdateresource_test.go | 2 +- .../backend/controller/deleteresource_test.go | 2 +- .../backend/deployment/deploymentprocessor.go | 2 +- .../deployment/deploymentprocessor_test.go | 2 +- .../controller/applications/getgraph_test.go | 2 +- .../applications/updatefilter_test.go | 2 +- .../createorupdateenvironment_test.go | 2 +- .../environments/getrecipemetadata_test.go | 2 +- .../extenders/listsecretsextender_test.go | 2 +- .../controller/secretstores/kubernetes.go | 2 +- .../secretstores/kubernetes_test.go | 2 +- .../secretstores/listsecrets_test.go | 2 +- pkg/corerp/frontend/controller/util/query.go | 2 +- pkg/corerp/setup/setup_test.go | 2 +- pkg/daprrp/setup/setup_test.go | 2 +- .../listsecretsmongodatabase_test.go | 2 +- .../rediscaches/listsecretsrediscache.go | 2 +- .../rediscaches/listsecretsrediscache_test.go | 2 +- .../listsecretssqldatabase_test.go | 2 +- pkg/datastoresrp/setup/setup_test.go | 2 +- pkg/dynamicrp/config.go | 6 +- pkg/dynamicrp/options.go | 6 +- pkg/dynamicrp/server/server.go | 2 +- .../listsecretsrabbitmq_test.go | 2 +- pkg/messagingrp/setup/setup_test.go | 2 +- .../controller/createorupdateresource.go | 2 +- .../controller/createorupdateresource_test.go | 2 +- .../backend/controller/deleteresource_test.go | 2 +- pkg/recipes/controllerconfig/config.go | 2 +- pkg/recipes/driver/terraform.go | 4 +- pkg/recipes/terraform/config/providers/aws.go | 8 +-- .../terraform/config/providers/aws_test.go | 2 +- .../terraform/config/providers/azure.go | 8 +-- .../terraform/config/providers/azure_test.go | 2 +- .../terraform/config/providers/types.go | 4 +- pkg/recipes/terraform/execute.go | 6 +- pkg/rp/kube/resources.go | 2 +- pkg/rp/kube/resources_test.go | 2 +- pkg/rp/util/datastore.go | 2 +- .../resourcegroups/trackedresourceprocess.go | 2 +- .../trackedresourceprocess_test.go | 2 +- .../resourceprovider_delete.go | 2 +- .../controller/resourceproviders/util.go | 2 +- .../controller/resourceproviders/util_test.go | 2 +- pkg/ucp/credentials/aws.go | 4 +- pkg/ucp/credentials/azure.go | 4 +- pkg/ucp/frontend/api/routes_test.go | 2 +- pkg/ucp/frontend/api/server.go | 6 +- pkg/ucp/frontend/aws/routes_test.go | 6 +- pkg/ucp/frontend/azure/routes_test.go | 6 +- .../controller/awsproxy/awsproxytest.go | 2 +- .../aws/createorupdateawscredential.go | 2 +- .../aws/createorupdateawscredential_test.go | 4 +- .../credentials/aws/deleteawscredential.go | 4 +- .../aws/deleteawscredential_test.go | 4 +- .../azure/createorupdateazurecredential.go | 2 +- .../createorupdateazurecredential_test.go | 4 +- .../azure/deleteazurecredential.go | 4 +- .../azure/deleteazurecredential_test.go | 4 +- .../frontend/controller/planes/listplanes.go | 2 +- .../controller/planes/listplanes_test.go | 2 +- .../controller/planes/listplanesbytype.go | 2 +- .../planes/listplanesbytype_test.go | 2 +- pkg/ucp/frontend/controller/radius/proxy.go | 2 +- .../frontend/controller/radius/proxy_test.go | 2 +- .../resourcegroups/listresourcegroups.go | 2 +- .../resourcegroups/listresourcegroups_test.go | 2 +- .../resourcegroups/listresources.go | 2 +- .../resourcegroups/listresources_test.go | 2 +- .../controller/resourcegroups/util.go | 2 +- .../controller/resourcegroups/util_test.go | 2 +- .../getresourceprovidersummary.go | 2 +- .../listresourceprovidersummaries.go | 2 +- pkg/ucp/frontend/modules/types.go | 6 +- pkg/ucp/frontend/radius/routes_test.go | 6 +- pkg/ucp/hostoptions/providerconfig.go | 6 +- pkg/ucp/integrationtests/aws/awstest.go | 4 +- pkg/ucp/integrationtests/testrp/async.go | 2 +- pkg/ucp/integrationtests/testrp/sync.go | 2 +- .../integrationtests/testserver/testserver.go | 12 ++-- pkg/ucp/server/server.go | 6 +- pkg/ucp/trackedresource/update.go | 2 +- pkg/ucp/trackedresource/update_test.go | 2 +- test/ucp/kubeenv/testenv.go | 2 +- test/ucp/queuetest/shared.go | 62 +++++++++---------- test/ucp/storetest/shared.go | 2 +- 184 files changed, 336 insertions(+), 319 deletions(-) rename pkg/{ucp => components}/database/apiserverstore/api/ucp.dev/v1alpha1/groupversion_info.go (100%) rename pkg/{ucp => components}/database/apiserverstore/api/ucp.dev/v1alpha1/queuemessage_types.go (100%) rename pkg/{ucp => components}/database/apiserverstore/api/ucp.dev/v1alpha1/resource_types.go (100%) rename pkg/{ucp => components}/database/apiserverstore/api/ucp.dev/v1alpha1/zz_generated.deepcopy.go (100%) rename pkg/{ucp => components}/database/apiserverstore/apiserverclient.go (98%) rename pkg/{ucp => components}/database/apiserverstore/apiserverclient_test.go (99%) rename pkg/{ucp => components}/database/client.go (98%) rename pkg/{ucp => components}/database/client_test.go (100%) rename pkg/{ucp => components/database}/databaseprovider/factory.go (89%) rename pkg/{ucp => components/database}/databaseprovider/options.go (100%) rename pkg/{ucp => components/database}/databaseprovider/storageprovider.go (98%) rename pkg/{ucp => components/database}/databaseprovider/storageprovider_test.go (98%) rename pkg/{ucp => components/database}/databaseprovider/types.go (100%) rename pkg/{ucp => components}/database/databaseutil/doc.go (100%) rename pkg/{ucp => components}/database/databaseutil/id.go (99%) rename pkg/{ucp => components}/database/databaseutil/id_test.go (99%) rename pkg/{ucp => components}/database/err.go (100%) rename pkg/{ucp => components}/database/etcdstore/etcdclient.go (98%) rename pkg/{ucp => components}/database/etcdstore/etcdclient_test.go (100%) rename pkg/{ucp => components}/database/filter.go (100%) rename pkg/{ucp => components}/database/filter_test.go (100%) rename pkg/{ucp => components}/database/inmemory/client.go (98%) rename pkg/{ucp => components}/database/inmemory/client_test.go (100%) rename pkg/{ucp => components}/database/inmemory/doc.go (100%) rename pkg/{ucp => components}/database/map.go (100%) rename pkg/{ucp => components}/database/map_test.go (100%) rename pkg/{ucp => components}/database/mock_client.go (96%) rename pkg/{ucp => components}/database/object.go (100%) rename pkg/{ucp => components}/database/options.go (100%) rename pkg/{ucp => components}/database/postgres/postgresclient.go (98%) rename pkg/{ucp => components}/database/postgres/postgresclient_test.go (100%) rename pkg/{ucp => components}/database/resources.go (100%) create mode 100644 pkg/components/doc.go rename pkg/{ucp => components}/queue/apiserver/client.go (90%) rename pkg/{ucp => components}/queue/apiserver/client_test.go (88%) rename pkg/{ucp/queue/client => components/queue}/client.go (94%) rename pkg/{ucp/queue/client => components/queue}/client_test.go (99%) rename pkg/{ucp => components}/queue/inmemory/client.go (74%) rename pkg/{ucp => components}/queue/inmemory/client_test.go (85%) rename pkg/{ucp => components}/queue/inmemory/queue.go (88%) rename pkg/{ucp => components}/queue/inmemory/queue_test.go (90%) rename pkg/{ucp/queue/client => components/queue}/message.go (99%) rename pkg/{ucp/queue/client => components/queue}/mock_client.go (94%) rename pkg/{ucp/queue/client => components/queue}/options.go (99%) rename pkg/{ucp => components}/queue/queueprovider/factory.go (87%) rename pkg/{ucp => components}/queue/queueprovider/options.go (100%) rename pkg/{ucp => components}/queue/queueprovider/provider.go (96%) rename pkg/{ucp => components}/queue/queueprovider/provider_test.go (100%) rename pkg/{ucp => components}/queue/queueprovider/types.go (100%) rename pkg/{ucp => components}/secret/client.go (95%) rename pkg/{ucp => components}/secret/client_test.go (100%) rename pkg/{ucp => components}/secret/etcd/client.go (97%) rename pkg/{ucp => components}/secret/etcd/client_test.go (98%) rename pkg/{ucp => components}/secret/inmemory/client.go (97%) rename pkg/{ucp => components}/secret/inmemory/client_test.go (98%) rename pkg/{ucp => components}/secret/kubernetes/client.go (98%) rename pkg/{ucp => components}/secret/kubernetes/client_test.go (98%) rename pkg/{ucp => components}/secret/mock_client.go (95%) rename pkg/{ucp => components}/secret/secretprovider/factory.go (85%) rename pkg/{ucp => components}/secret/secretprovider/options.go (92%) rename pkg/{ucp => components}/secret/secretprovider/provider.go (96%) rename pkg/{ucp => components}/secret/secretprovider/provider_test.go (100%) rename pkg/{ucp => components}/secret/secretprovider/types.go (100%) diff --git a/build/generate.mk b/build/generate.mk index 13c4fc0a53..0685a0ed59 100644 --- a/build/generate.mk +++ b/build/generate.mk @@ -62,8 +62,8 @@ generate-controller-gen-installed: .PHONY: generate-ucp-crd generate-ucp-crd: generate-controller-gen-installed ## Generates the CRDs for UCP APIServer store. @echo "$(ARROW) Generating CRDs for ucp.dev..." - controller-gen object:headerFile=./boilerplate.go.txt paths=./pkg/ucp/database/apiserverstore/api/ucp.dev/v1alpha1/... - controller-gen crd paths=./pkg/ucp/database/apiserverstore/api/ucp.dev/v1alpha1/... output:crd:dir=./deploy/Chart/crds/ucpd + controller-gen object:headerFile=./boilerplate.go.txt paths=./pkg/components/database/apiserverstore/api/ucp.dev/v1alpha1/... + controller-gen crd paths=./pkg/components/database/apiserverstore/api/ucp.dev/v1alpha1/... output:crd:dir=./deploy/Chart/crds/ucpd .PHONY: generate-controller generate-controller: generate-controller-gen-installed ## Generates the CRDs for the Radius controller. diff --git a/cmd/applications-rp/cmd/root.go b/cmd/applications-rp/cmd/root.go index 75241ea968..8656dae6bc 100644 --- a/cmd/applications-rp/cmd/root.go +++ b/cmd/applications-rp/cmd/root.go @@ -33,8 +33,8 @@ import ( "github.com/radius-project/radius/pkg/server" "github.com/radius-project/radius/pkg/trace" + "github.com/radius-project/radius/pkg/components/database/databaseprovider" "github.com/radius-project/radius/pkg/ucp/data" - "github.com/radius-project/radius/pkg/ucp/databaseprovider" "github.com/radius-project/radius/pkg/ucp/hosting" "github.com/radius-project/radius/pkg/ucp/ucplog" diff --git a/cmd/ucpd/cmd/root.go b/cmd/ucpd/cmd/root.go index c11ec85178..4c012bd07e 100644 --- a/cmd/ucpd/cmd/root.go +++ b/cmd/ucpd/cmd/root.go @@ -26,7 +26,7 @@ import ( runtimelog "sigs.k8s.io/controller-runtime/pkg/log" "github.com/radius-project/radius/pkg/armrpc/hostoptions" - "github.com/radius-project/radius/pkg/ucp/databaseprovider" + "github.com/radius-project/radius/pkg/components/database/databaseprovider" "github.com/radius-project/radius/pkg/ucp/hosting" "github.com/radius-project/radius/pkg/ucp/server" "github.com/radius-project/radius/pkg/ucp/ucplog" diff --git a/pkg/armrpc/asyncoperation/controller/controller.go b/pkg/armrpc/asyncoperation/controller/controller.go index a0a89b0720..0c76737b42 100644 --- a/pkg/armrpc/asyncoperation/controller/controller.go +++ b/pkg/armrpc/asyncoperation/controller/controller.go @@ -20,8 +20,8 @@ import ( "context" "errors" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/corerp/backend/deployment" - "github.com/radius-project/radius/pkg/ucp/database" runtimeclient "sigs.k8s.io/controller-runtime/pkg/client" ) diff --git a/pkg/armrpc/asyncoperation/statusmanager/statusmanager.go b/pkg/armrpc/asyncoperation/statusmanager/statusmanager.go index bc9b2acc1a..091b80e1a2 100644 --- a/pkg/armrpc/asyncoperation/statusmanager/statusmanager.go +++ b/pkg/armrpc/asyncoperation/statusmanager/statusmanager.go @@ -25,10 +25,10 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" + "github.com/radius-project/radius/pkg/components/database" + "github.com/radius-project/radius/pkg/components/queue" "github.com/radius-project/radius/pkg/metrics" "github.com/radius-project/radius/pkg/trace" - "github.com/radius-project/radius/pkg/ucp/database" - queue "github.com/radius-project/radius/pkg/ucp/queue/client" "github.com/radius-project/radius/pkg/ucp/resources" "github.com/google/uuid" diff --git a/pkg/armrpc/asyncoperation/statusmanager/statusmanager_test.go b/pkg/armrpc/asyncoperation/statusmanager/statusmanager_test.go index c1de8a5bd6..6cce71bc6d 100644 --- a/pkg/armrpc/asyncoperation/statusmanager/statusmanager_test.go +++ b/pkg/armrpc/asyncoperation/statusmanager/statusmanager_test.go @@ -26,8 +26,8 @@ import ( "github.com/google/uuid" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" "github.com/radius-project/radius/pkg/armrpc/rpctest" - "github.com/radius-project/radius/pkg/ucp/database" - queue "github.com/radius-project/radius/pkg/ucp/queue/client" + "github.com/radius-project/radius/pkg/components/database" + "github.com/radius-project/radius/pkg/components/queue" "github.com/radius-project/radius/pkg/ucp/resources" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" diff --git a/pkg/armrpc/asyncoperation/worker/registry_test.go b/pkg/armrpc/asyncoperation/worker/registry_test.go index 3df0c2ac5e..dc765925d3 100644 --- a/pkg/armrpc/asyncoperation/worker/registry_test.go +++ b/pkg/armrpc/asyncoperation/worker/registry_test.go @@ -22,8 +22,8 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" + "github.com/radius-project/radius/pkg/components/database/inmemory" "github.com/radius-project/radius/pkg/corerp/backend/deployment" - "github.com/radius-project/radius/pkg/ucp/database/inmemory" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" ) diff --git a/pkg/armrpc/asyncoperation/worker/service.go b/pkg/armrpc/asyncoperation/worker/service.go index 7a2440266c..305a362060 100644 --- a/pkg/armrpc/asyncoperation/worker/service.go +++ b/pkg/armrpc/asyncoperation/worker/service.go @@ -21,9 +21,9 @@ import ( manager "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" "github.com/radius-project/radius/pkg/armrpc/hostoptions" - "github.com/radius-project/radius/pkg/ucp/databaseprovider" - queue "github.com/radius-project/radius/pkg/ucp/queue/client" - "github.com/radius-project/radius/pkg/ucp/queue/queueprovider" + "github.com/radius-project/radius/pkg/components/database/databaseprovider" + "github.com/radius-project/radius/pkg/components/queue" + "github.com/radius-project/radius/pkg/components/queue/queueprovider" "github.com/radius-project/radius/pkg/ucp/ucplog" ) diff --git a/pkg/armrpc/asyncoperation/worker/worker.go b/pkg/armrpc/asyncoperation/worker/worker.go index d10d862a8a..587068a3bc 100644 --- a/pkg/armrpc/asyncoperation/worker/worker.go +++ b/pkg/armrpc/asyncoperation/worker/worker.go @@ -28,11 +28,11 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" manager "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" + "github.com/radius-project/radius/pkg/components/database" + "github.com/radius-project/radius/pkg/components/queue" "github.com/radius-project/radius/pkg/logging" "github.com/radius-project/radius/pkg/metrics" "github.com/radius-project/radius/pkg/trace" - "github.com/radius-project/radius/pkg/ucp/database" - queue "github.com/radius-project/radius/pkg/ucp/queue/client" "github.com/radius-project/radius/pkg/ucp/resources" "github.com/radius-project/radius/pkg/ucp/ucplog" diff --git a/pkg/armrpc/asyncoperation/worker/worker_runoperation_test.go b/pkg/armrpc/asyncoperation/worker/worker_runoperation_test.go index 9359186c6d..8cce24600b 100644 --- a/pkg/armrpc/asyncoperation/worker/worker_runoperation_test.go +++ b/pkg/armrpc/asyncoperation/worker/worker_runoperation_test.go @@ -28,11 +28,11 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" manager "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" + "github.com/radius-project/radius/pkg/components/database" + inmemorystore "github.com/radius-project/radius/pkg/components/database/inmemory" + "github.com/radius-project/radius/pkg/components/queue" + "github.com/radius-project/radius/pkg/components/queue/inmemory" "github.com/radius-project/radius/pkg/corerp/backend/deployment" - "github.com/radius-project/radius/pkg/ucp/database" - inmemorystore "github.com/radius-project/radius/pkg/ucp/database/inmemory" - queue "github.com/radius-project/radius/pkg/ucp/queue/client" - "github.com/radius-project/radius/pkg/ucp/queue/inmemory" "github.com/radius-project/radius/pkg/ucp/resources" "github.com/stretchr/testify/require" "go.uber.org/atomic" diff --git a/pkg/armrpc/asyncoperation/worker/worker_test.go b/pkg/armrpc/asyncoperation/worker/worker_test.go index 390e0b7598..1f5a792137 100644 --- a/pkg/armrpc/asyncoperation/worker/worker_test.go +++ b/pkg/armrpc/asyncoperation/worker/worker_test.go @@ -23,7 +23,7 @@ import ( "time" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" ) diff --git a/pkg/armrpc/builder/builder_test.go b/pkg/armrpc/builder/builder_test.go index b25ad4b72b..ac076810c7 100644 --- a/pkg/armrpc/builder/builder_test.go +++ b/pkg/armrpc/builder/builder_test.go @@ -28,7 +28,7 @@ import ( "github.com/radius-project/radius/pkg/armrpc/asyncoperation/worker" apictrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rpctest" - "github.com/radius-project/radius/pkg/ucp/database/inmemory" + "github.com/radius-project/radius/pkg/components/database/inmemory" "github.com/radius-project/radius/test/testcontext" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" diff --git a/pkg/armrpc/builder/namespace_test.go b/pkg/armrpc/builder/namespace_test.go index 60e9890416..bdcc368151 100644 --- a/pkg/armrpc/builder/namespace_test.go +++ b/pkg/armrpc/builder/namespace_test.go @@ -26,7 +26,7 @@ import ( apictrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rest" "github.com/radius-project/radius/pkg/armrpc/rpctest" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" "github.com/stretchr/testify/require" ) diff --git a/pkg/armrpc/frontend/controller/controller.go b/pkg/armrpc/frontend/controller/controller.go index 8acd02118a..d06764be65 100644 --- a/pkg/armrpc/frontend/controller/controller.go +++ b/pkg/armrpc/frontend/controller/controller.go @@ -26,7 +26,7 @@ import ( sm "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" "github.com/radius-project/radius/pkg/armrpc/hostoptions" "github.com/radius-project/radius/pkg/armrpc/rest" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" runtimeclient "sigs.k8s.io/controller-runtime/pkg/client" ) diff --git a/pkg/armrpc/frontend/controller/controller_test.go b/pkg/armrpc/frontend/controller/controller_test.go index 5e28bd77c8..3952d3da6a 100644 --- a/pkg/armrpc/frontend/controller/controller_test.go +++ b/pkg/armrpc/frontend/controller/controller_test.go @@ -21,7 +21,7 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" "github.com/stretchr/testify/require" ) diff --git a/pkg/armrpc/frontend/controller/operation.go b/pkg/armrpc/frontend/controller/operation.go index d8ecbf11bb..2877b507ae 100644 --- a/pkg/armrpc/frontend/controller/operation.go +++ b/pkg/armrpc/frontend/controller/operation.go @@ -26,7 +26,7 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" sm "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" "github.com/radius-project/radius/pkg/armrpc/rest" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/ucp/resources" ) diff --git a/pkg/armrpc/frontend/defaultoperation/defaultasyncdelete_test.go b/pkg/armrpc/frontend/defaultoperation/defaultasyncdelete_test.go index e28a7b6f66..64db0a26b3 100644 --- a/pkg/armrpc/frontend/defaultoperation/defaultasyncdelete_test.go +++ b/pkg/armrpc/frontend/defaultoperation/defaultasyncdelete_test.go @@ -29,7 +29,7 @@ import ( ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rest" "github.com/radius-project/radius/pkg/armrpc/rpctest" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" diff --git a/pkg/armrpc/frontend/defaultoperation/defaultasyncput_test.go b/pkg/armrpc/frontend/defaultoperation/defaultasyncput_test.go index a66a120c6f..3c76945cca 100644 --- a/pkg/armrpc/frontend/defaultoperation/defaultasyncput_test.go +++ b/pkg/armrpc/frontend/defaultoperation/defaultasyncput_test.go @@ -29,7 +29,7 @@ import ( "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rpctest" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/test/testutil" "github.com/stretchr/testify/require" diff --git a/pkg/armrpc/frontend/defaultoperation/defaultsyncdelete.go b/pkg/armrpc/frontend/defaultoperation/defaultsyncdelete.go index 62ff4e67dd..3b070afbe3 100644 --- a/pkg/armrpc/frontend/defaultoperation/defaultsyncdelete.go +++ b/pkg/armrpc/frontend/defaultoperation/defaultsyncdelete.go @@ -24,7 +24,7 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rest" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" ) // DefaultSyncDelete is the controller implementation to delete resource synchronously. diff --git a/pkg/armrpc/frontend/defaultoperation/defaultsyncdelete_test.go b/pkg/armrpc/frontend/defaultoperation/defaultsyncdelete_test.go index 361d1bef18..27ec3b887c 100644 --- a/pkg/armrpc/frontend/defaultoperation/defaultsyncdelete_test.go +++ b/pkg/armrpc/frontend/defaultoperation/defaultsyncdelete_test.go @@ -26,7 +26,7 @@ import ( ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rest" "github.com/radius-project/radius/pkg/armrpc/rpctest" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" diff --git a/pkg/armrpc/frontend/defaultoperation/defaultsyncput_test.go b/pkg/armrpc/frontend/defaultoperation/defaultsyncput_test.go index a5dd737b26..570564ef35 100644 --- a/pkg/armrpc/frontend/defaultoperation/defaultsyncput_test.go +++ b/pkg/armrpc/frontend/defaultoperation/defaultsyncput_test.go @@ -27,7 +27,7 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rpctest" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/test/testutil" "github.com/stretchr/testify/require" diff --git a/pkg/armrpc/frontend/defaultoperation/getoperationresult.go b/pkg/armrpc/frontend/defaultoperation/getoperationresult.go index a9b452892f..fe78aa33a5 100644 --- a/pkg/armrpc/frontend/defaultoperation/getoperationresult.go +++ b/pkg/armrpc/frontend/defaultoperation/getoperationresult.go @@ -27,7 +27,7 @@ import ( manager "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rest" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/ucp/resources" ) diff --git a/pkg/armrpc/frontend/defaultoperation/getoperationresult_test.go b/pkg/armrpc/frontend/defaultoperation/getoperationresult_test.go index b6fee80ac4..933a88c0a4 100644 --- a/pkg/armrpc/frontend/defaultoperation/getoperationresult_test.go +++ b/pkg/armrpc/frontend/defaultoperation/getoperationresult_test.go @@ -28,7 +28,7 @@ import ( manager "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rpctest" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/test/testcontext" "github.com/radius-project/radius/test/testutil" diff --git a/pkg/armrpc/frontend/defaultoperation/getoperationstatus.go b/pkg/armrpc/frontend/defaultoperation/getoperationstatus.go index 558822c8aa..e8635bb01a 100644 --- a/pkg/armrpc/frontend/defaultoperation/getoperationstatus.go +++ b/pkg/armrpc/frontend/defaultoperation/getoperationstatus.go @@ -25,7 +25,7 @@ import ( manager "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rest" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" ) var _ ctrl.Controller = (*GetOperationStatus)(nil) diff --git a/pkg/armrpc/frontend/defaultoperation/getoperationstatus_test.go b/pkg/armrpc/frontend/defaultoperation/getoperationstatus_test.go index 1ab1f52225..7367d9acaa 100644 --- a/pkg/armrpc/frontend/defaultoperation/getoperationstatus_test.go +++ b/pkg/armrpc/frontend/defaultoperation/getoperationstatus_test.go @@ -27,7 +27,7 @@ import ( manager "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rpctest" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/test/testutil" "github.com/stretchr/testify/require" diff --git a/pkg/armrpc/frontend/defaultoperation/getresource_test.go b/pkg/armrpc/frontend/defaultoperation/getresource_test.go index 3c48a8f977..2ce5fa6a98 100644 --- a/pkg/armrpc/frontend/defaultoperation/getresource_test.go +++ b/pkg/armrpc/frontend/defaultoperation/getresource_test.go @@ -26,7 +26,7 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rpctest" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" diff --git a/pkg/armrpc/frontend/defaultoperation/listresources.go b/pkg/armrpc/frontend/defaultoperation/listresources.go index 4aee3fe37d..f59fecfa94 100644 --- a/pkg/armrpc/frontend/defaultoperation/listresources.go +++ b/pkg/armrpc/frontend/defaultoperation/listresources.go @@ -23,7 +23,7 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rest" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" ) // ListResources is the controller implementation to get the list of resources in resource group. diff --git a/pkg/armrpc/frontend/defaultoperation/listresources_test.go b/pkg/armrpc/frontend/defaultoperation/listresources_test.go index c000973cb9..d00b29bc53 100644 --- a/pkg/armrpc/frontend/defaultoperation/listresources_test.go +++ b/pkg/armrpc/frontend/defaultoperation/listresources_test.go @@ -27,7 +27,7 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rpctest" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" "github.com/google/uuid" "github.com/stretchr/testify/require" diff --git a/pkg/armrpc/frontend/defaultoperation/resource_test.go b/pkg/armrpc/frontend/defaultoperation/resource_test.go index ad17c88548..64e9e6e1aa 100644 --- a/pkg/armrpc/frontend/defaultoperation/resource_test.go +++ b/pkg/armrpc/frontend/defaultoperation/resource_test.go @@ -28,8 +28,8 @@ import ( "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rest" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/to" - "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/test/testutil" "go.uber.org/mock/gomock" diff --git a/pkg/armrpc/frontend/server/handler_test.go b/pkg/armrpc/frontend/server/handler_test.go index bd323a06a9..7c3791e7fd 100644 --- a/pkg/armrpc/frontend/server/handler_test.go +++ b/pkg/armrpc/frontend/server/handler_test.go @@ -32,8 +32,8 @@ import ( ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rest" "github.com/radius-project/radius/pkg/armrpc/rpctest" + "github.com/radius-project/radius/pkg/components/database/inmemory" "github.com/radius-project/radius/pkg/middleware" - "github.com/radius-project/radius/pkg/ucp/database/inmemory" "github.com/radius-project/radius/test/testcontext" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" diff --git a/pkg/armrpc/frontend/server/service.go b/pkg/armrpc/frontend/server/service.go index 265ec4e17d..5854fcf506 100644 --- a/pkg/armrpc/frontend/server/service.go +++ b/pkg/armrpc/frontend/server/service.go @@ -24,9 +24,9 @@ import ( manager "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" "github.com/radius-project/radius/pkg/armrpc/authentication" "github.com/radius-project/radius/pkg/armrpc/hostoptions" + "github.com/radius-project/radius/pkg/components/database/databaseprovider" + "github.com/radius-project/radius/pkg/components/queue/queueprovider" "github.com/radius-project/radius/pkg/kubeutil" - "github.com/radius-project/radius/pkg/ucp/databaseprovider" - "github.com/radius-project/radius/pkg/ucp/queue/queueprovider" "github.com/radius-project/radius/pkg/ucp/ucplog" controller_runtime "sigs.k8s.io/controller-runtime/pkg/client" ) diff --git a/pkg/armrpc/hostoptions/hostoptions.go b/pkg/armrpc/hostoptions/hostoptions.go index 37750cf659..8f5eb4c310 100644 --- a/pkg/armrpc/hostoptions/hostoptions.go +++ b/pkg/armrpc/hostoptions/hostoptions.go @@ -28,11 +28,11 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" "github.com/radius-project/radius/pkg/azure/armauth" aztoken "github.com/radius-project/radius/pkg/azure/tokencredentials" + sprovider "github.com/radius-project/radius/pkg/components/secret/secretprovider" "github.com/radius-project/radius/pkg/kubeutil" "github.com/radius-project/radius/pkg/sdk" "github.com/radius-project/radius/pkg/ucp/config" sdk_cred "github.com/radius-project/radius/pkg/ucp/credentials" - sprovider "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" "gopkg.in/yaml.v3" corev1 "k8s.io/api/core/v1" diff --git a/pkg/armrpc/hostoptions/providerconfig.go b/pkg/armrpc/hostoptions/providerconfig.go index f2acbf3d28..59fd7a7589 100644 --- a/pkg/armrpc/hostoptions/providerconfig.go +++ b/pkg/armrpc/hostoptions/providerconfig.go @@ -17,13 +17,13 @@ limitations under the License. package hostoptions import ( + "github.com/radius-project/radius/pkg/components/database/databaseprovider" + "github.com/radius-project/radius/pkg/components/queue/queueprovider" + "github.com/radius-project/radius/pkg/components/secret/secretprovider" metricsprovider "github.com/radius-project/radius/pkg/metrics/provider" profilerprovider "github.com/radius-project/radius/pkg/profiler/provider" "github.com/radius-project/radius/pkg/trace" "github.com/radius-project/radius/pkg/ucp/config" - "github.com/radius-project/radius/pkg/ucp/databaseprovider" - "github.com/radius-project/radius/pkg/ucp/queue/queueprovider" - "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" "github.com/radius-project/radius/pkg/ucp/ucplog" ) diff --git a/pkg/armrpc/rpctest/controllers.go b/pkg/armrpc/rpctest/controllers.go index 819da12b6f..08b53e83ba 100644 --- a/pkg/armrpc/rpctest/controllers.go +++ b/pkg/armrpc/rpctest/controllers.go @@ -22,7 +22,7 @@ import ( "testing" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" "go.uber.org/mock/gomock" ) diff --git a/pkg/ucp/database/apiserverstore/api/ucp.dev/v1alpha1/groupversion_info.go b/pkg/components/database/apiserverstore/api/ucp.dev/v1alpha1/groupversion_info.go similarity index 100% rename from pkg/ucp/database/apiserverstore/api/ucp.dev/v1alpha1/groupversion_info.go rename to pkg/components/database/apiserverstore/api/ucp.dev/v1alpha1/groupversion_info.go diff --git a/pkg/ucp/database/apiserverstore/api/ucp.dev/v1alpha1/queuemessage_types.go b/pkg/components/database/apiserverstore/api/ucp.dev/v1alpha1/queuemessage_types.go similarity index 100% rename from pkg/ucp/database/apiserverstore/api/ucp.dev/v1alpha1/queuemessage_types.go rename to pkg/components/database/apiserverstore/api/ucp.dev/v1alpha1/queuemessage_types.go diff --git a/pkg/ucp/database/apiserverstore/api/ucp.dev/v1alpha1/resource_types.go b/pkg/components/database/apiserverstore/api/ucp.dev/v1alpha1/resource_types.go similarity index 100% rename from pkg/ucp/database/apiserverstore/api/ucp.dev/v1alpha1/resource_types.go rename to pkg/components/database/apiserverstore/api/ucp.dev/v1alpha1/resource_types.go diff --git a/pkg/ucp/database/apiserverstore/api/ucp.dev/v1alpha1/zz_generated.deepcopy.go b/pkg/components/database/apiserverstore/api/ucp.dev/v1alpha1/zz_generated.deepcopy.go similarity index 100% rename from pkg/ucp/database/apiserverstore/api/ucp.dev/v1alpha1/zz_generated.deepcopy.go rename to pkg/components/database/apiserverstore/api/ucp.dev/v1alpha1/zz_generated.deepcopy.go diff --git a/pkg/ucp/database/apiserverstore/apiserverclient.go b/pkg/components/database/apiserverstore/apiserverclient.go similarity index 98% rename from pkg/ucp/database/apiserverstore/apiserverclient.go rename to pkg/components/database/apiserverstore/apiserverclient.go index 6976bcfb71..271f848078 100644 --- a/pkg/ucp/database/apiserverstore/apiserverclient.go +++ b/pkg/components/database/apiserverstore/apiserverclient.go @@ -48,9 +48,9 @@ import ( "strings" "unicode" - "github.com/radius-project/radius/pkg/ucp/database" - ucpv1alpha1 "github.com/radius-project/radius/pkg/ucp/database/apiserverstore/api/ucp.dev/v1alpha1" - "github.com/radius-project/radius/pkg/ucp/database/databaseutil" + "github.com/radius-project/radius/pkg/components/database" + ucpv1alpha1 "github.com/radius-project/radius/pkg/components/database/apiserverstore/api/ucp.dev/v1alpha1" + "github.com/radius-project/radius/pkg/components/database/databaseutil" "github.com/radius-project/radius/pkg/ucp/resources" "github.com/radius-project/radius/pkg/ucp/ucplog" "github.com/radius-project/radius/pkg/ucp/util/etag" diff --git a/pkg/ucp/database/apiserverstore/apiserverclient_test.go b/pkg/components/database/apiserverstore/apiserverclient_test.go similarity index 99% rename from pkg/ucp/database/apiserverstore/apiserverclient_test.go rename to pkg/components/database/apiserverstore/apiserverclient_test.go index cdc28fbda1..4a964e6281 100644 --- a/pkg/ucp/database/apiserverstore/apiserverclient_test.go +++ b/pkg/components/database/apiserverstore/apiserverclient_test.go @@ -28,8 +28,8 @@ import ( "k8s.io/apimachinery/pkg/runtime" runtimeclient "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/radius-project/radius/pkg/ucp/database" - ucpv1alpha1 "github.com/radius-project/radius/pkg/ucp/database/apiserverstore/api/ucp.dev/v1alpha1" + "github.com/radius-project/radius/pkg/components/database" + ucpv1alpha1 "github.com/radius-project/radius/pkg/components/database/apiserverstore/api/ucp.dev/v1alpha1" "github.com/radius-project/radius/pkg/ucp/resources" "github.com/radius-project/radius/pkg/ucp/util/etag" "github.com/radius-project/radius/test/testcontext" diff --git a/pkg/ucp/database/client.go b/pkg/components/database/client.go similarity index 98% rename from pkg/ucp/database/client.go rename to pkg/components/database/client.go index 32c1a7b76b..c17d419a32 100644 --- a/pkg/ucp/database/client.go +++ b/pkg/components/database/client.go @@ -31,7 +31,7 @@ var jsonPropertyPattern = "[a-zA-Z$_][a-zA-Z0-9$_]*" // - Multople properties separated by a '.' var fieldRegex = regexp.MustCompile(fmt.Sprintf(`^(%s)(\.%s)*$`, jsonPropertyPattern, jsonPropertyPattern)) -//go:generate mockgen -typed -destination=./mock_client.go -package=database -self_package github.com/radius-project/radius/pkg/ucp/database github.com/radius-project/radius/pkg/ucp/database Client +//go:generate mockgen -typed -destination=./mock_client.go -package=database -self_package github.com/radius-project/radius/pkg/components/database github.com/radius-project/radius/pkg/components/database Client // Client is the interface for persisting and querying resource data. // diff --git a/pkg/ucp/database/client_test.go b/pkg/components/database/client_test.go similarity index 100% rename from pkg/ucp/database/client_test.go rename to pkg/components/database/client_test.go diff --git a/pkg/ucp/databaseprovider/factory.go b/pkg/components/database/databaseprovider/factory.go similarity index 89% rename from pkg/ucp/databaseprovider/factory.go rename to pkg/components/database/databaseprovider/factory.go index 4f02132430..50c9bae764 100644 --- a/pkg/ucp/databaseprovider/factory.go +++ b/pkg/components/database/databaseprovider/factory.go @@ -23,13 +23,13 @@ import ( "regexp" "github.com/jackc/pgx/v5/pgxpool" + store "github.com/radius-project/radius/pkg/components/database" + "github.com/radius-project/radius/pkg/components/database/apiserverstore" + ucpv1alpha1 "github.com/radius-project/radius/pkg/components/database/apiserverstore/api/ucp.dev/v1alpha1" + "github.com/radius-project/radius/pkg/components/database/etcdstore" + "github.com/radius-project/radius/pkg/components/database/inmemory" + "github.com/radius-project/radius/pkg/components/database/postgres" "github.com/radius-project/radius/pkg/kubeutil" - store "github.com/radius-project/radius/pkg/ucp/database" - "github.com/radius-project/radius/pkg/ucp/database/apiserverstore" - ucpv1alpha1 "github.com/radius-project/radius/pkg/ucp/database/apiserverstore/api/ucp.dev/v1alpha1" - "github.com/radius-project/radius/pkg/ucp/database/etcdstore" - "github.com/radius-project/radius/pkg/ucp/database/inmemory" - "github.com/radius-project/radius/pkg/ucp/database/postgres" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/rest" diff --git a/pkg/ucp/databaseprovider/options.go b/pkg/components/database/databaseprovider/options.go similarity index 100% rename from pkg/ucp/databaseprovider/options.go rename to pkg/components/database/databaseprovider/options.go diff --git a/pkg/ucp/databaseprovider/storageprovider.go b/pkg/components/database/databaseprovider/storageprovider.go similarity index 98% rename from pkg/ucp/databaseprovider/storageprovider.go rename to pkg/components/database/databaseprovider/storageprovider.go index 2891515aaa..02a331211e 100644 --- a/pkg/ucp/databaseprovider/storageprovider.go +++ b/pkg/components/database/databaseprovider/storageprovider.go @@ -21,7 +21,7 @@ import ( "fmt" "sync" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" ) // DatabaseProvider acts as a factory for database clients. diff --git a/pkg/ucp/databaseprovider/storageprovider_test.go b/pkg/components/database/databaseprovider/storageprovider_test.go similarity index 98% rename from pkg/ucp/databaseprovider/storageprovider_test.go rename to pkg/components/database/databaseprovider/storageprovider_test.go index f6a0eb9285..ef038e57dd 100644 --- a/pkg/ucp/databaseprovider/storageprovider_test.go +++ b/pkg/components/database/databaseprovider/storageprovider_test.go @@ -21,7 +21,7 @@ import ( "errors" "testing" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" "github.com/stretchr/testify/require" ) diff --git a/pkg/ucp/databaseprovider/types.go b/pkg/components/database/databaseprovider/types.go similarity index 100% rename from pkg/ucp/databaseprovider/types.go rename to pkg/components/database/databaseprovider/types.go diff --git a/pkg/ucp/database/databaseutil/doc.go b/pkg/components/database/databaseutil/doc.go similarity index 100% rename from pkg/ucp/database/databaseutil/doc.go rename to pkg/components/database/databaseutil/doc.go diff --git a/pkg/ucp/database/databaseutil/id.go b/pkg/components/database/databaseutil/id.go similarity index 99% rename from pkg/ucp/database/databaseutil/id.go rename to pkg/components/database/databaseutil/id.go index a2f953abcb..7eb5ecd476 100644 --- a/pkg/ucp/database/databaseutil/id.go +++ b/pkg/components/database/databaseutil/id.go @@ -20,7 +20,7 @@ import ( "fmt" "strings" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/ucp/resources" ) diff --git a/pkg/ucp/database/databaseutil/id_test.go b/pkg/components/database/databaseutil/id_test.go similarity index 99% rename from pkg/ucp/database/databaseutil/id_test.go rename to pkg/components/database/databaseutil/id_test.go index 4b2a13d3b1..ed1361f2c6 100644 --- a/pkg/ucp/database/databaseutil/id_test.go +++ b/pkg/components/database/databaseutil/id_test.go @@ -19,7 +19,7 @@ package databaseutil import ( "testing" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/ucp/resources" "github.com/stretchr/testify/require" ) diff --git a/pkg/ucp/database/err.go b/pkg/components/database/err.go similarity index 100% rename from pkg/ucp/database/err.go rename to pkg/components/database/err.go diff --git a/pkg/ucp/database/etcdstore/etcdclient.go b/pkg/components/database/etcdstore/etcdclient.go similarity index 98% rename from pkg/ucp/database/etcdstore/etcdclient.go rename to pkg/components/database/etcdstore/etcdclient.go index ed3c7a1a76..96a7e1d281 100644 --- a/pkg/ucp/database/etcdstore/etcdclient.go +++ b/pkg/components/database/etcdstore/etcdclient.go @@ -52,8 +52,8 @@ import ( "fmt" "strings" - "github.com/radius-project/radius/pkg/ucp/database" - "github.com/radius-project/radius/pkg/ucp/database/databaseutil" + "github.com/radius-project/radius/pkg/components/database" + "github.com/radius-project/radius/pkg/components/database/databaseutil" "github.com/radius-project/radius/pkg/ucp/resources" "github.com/radius-project/radius/pkg/ucp/util/etag" etcdclient "go.etcd.io/etcd/client/v3" diff --git a/pkg/ucp/database/etcdstore/etcdclient_test.go b/pkg/components/database/etcdstore/etcdclient_test.go similarity index 100% rename from pkg/ucp/database/etcdstore/etcdclient_test.go rename to pkg/components/database/etcdstore/etcdclient_test.go diff --git a/pkg/ucp/database/filter.go b/pkg/components/database/filter.go similarity index 100% rename from pkg/ucp/database/filter.go rename to pkg/components/database/filter.go diff --git a/pkg/ucp/database/filter_test.go b/pkg/components/database/filter_test.go similarity index 100% rename from pkg/ucp/database/filter_test.go rename to pkg/components/database/filter_test.go diff --git a/pkg/ucp/database/inmemory/client.go b/pkg/components/database/inmemory/client.go similarity index 98% rename from pkg/ucp/database/inmemory/client.go rename to pkg/components/database/inmemory/client.go index 9b9ac141e9..1d74d4aa6a 100644 --- a/pkg/ucp/database/inmemory/client.go +++ b/pkg/components/database/inmemory/client.go @@ -23,8 +23,8 @@ import ( "strings" "sync" - "github.com/radius-project/radius/pkg/ucp/database" - "github.com/radius-project/radius/pkg/ucp/database/databaseutil" + "github.com/radius-project/radius/pkg/components/database" + "github.com/radius-project/radius/pkg/components/database/databaseutil" "github.com/radius-project/radius/pkg/ucp/resources" "github.com/radius-project/radius/pkg/ucp/util/etag" "golang.org/x/exp/maps" diff --git a/pkg/ucp/database/inmemory/client_test.go b/pkg/components/database/inmemory/client_test.go similarity index 100% rename from pkg/ucp/database/inmemory/client_test.go rename to pkg/components/database/inmemory/client_test.go diff --git a/pkg/ucp/database/inmemory/doc.go b/pkg/components/database/inmemory/doc.go similarity index 100% rename from pkg/ucp/database/inmemory/doc.go rename to pkg/components/database/inmemory/doc.go diff --git a/pkg/ucp/database/map.go b/pkg/components/database/map.go similarity index 100% rename from pkg/ucp/database/map.go rename to pkg/components/database/map.go diff --git a/pkg/ucp/database/map_test.go b/pkg/components/database/map_test.go similarity index 100% rename from pkg/ucp/database/map_test.go rename to pkg/components/database/map_test.go diff --git a/pkg/ucp/database/mock_client.go b/pkg/components/database/mock_client.go similarity index 96% rename from pkg/ucp/database/mock_client.go rename to pkg/components/database/mock_client.go index 5a3a7b3939..1e758da1fc 100644 --- a/pkg/ucp/database/mock_client.go +++ b/pkg/components/database/mock_client.go @@ -1,9 +1,9 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/radius-project/radius/pkg/ucp/database (interfaces: Client) +// Source: github.com/radius-project/radius/pkg/components/database (interfaces: Client) // // Generated by this command: // -// mockgen -typed -destination=./mock_client.go -package=database -self_package github.com/radius-project/radius/pkg/ucp/database github.com/radius-project/radius/pkg/ucp/database Client +// mockgen -typed -destination=./mock_client.go -package=database -self_package github.com/radius-project/radius/pkg/components/database github.com/radius-project/radius/pkg/components/database Client // // Package database is a generated GoMock package. diff --git a/pkg/ucp/database/object.go b/pkg/components/database/object.go similarity index 100% rename from pkg/ucp/database/object.go rename to pkg/components/database/object.go diff --git a/pkg/ucp/database/options.go b/pkg/components/database/options.go similarity index 100% rename from pkg/ucp/database/options.go rename to pkg/components/database/options.go diff --git a/pkg/ucp/database/postgres/postgresclient.go b/pkg/components/database/postgres/postgresclient.go similarity index 98% rename from pkg/ucp/database/postgres/postgresclient.go rename to pkg/components/database/postgres/postgresclient.go index d5dbb681db..3c38628c09 100644 --- a/pkg/ucp/database/postgres/postgresclient.go +++ b/pkg/components/database/postgres/postgresclient.go @@ -27,8 +27,8 @@ import ( "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgconn" "github.com/radius-project/radius/pkg/to" - "github.com/radius-project/radius/pkg/ucp/database" - "github.com/radius-project/radius/pkg/ucp/database/databaseutil" + "github.com/radius-project/radius/pkg/components/database" + "github.com/radius-project/radius/pkg/components/database/databaseutil" "github.com/radius-project/radius/pkg/ucp/resources" "github.com/radius-project/radius/pkg/ucp/util/etag" ) diff --git a/pkg/ucp/database/postgres/postgresclient_test.go b/pkg/components/database/postgres/postgresclient_test.go similarity index 100% rename from pkg/ucp/database/postgres/postgresclient_test.go rename to pkg/components/database/postgres/postgresclient_test.go diff --git a/pkg/ucp/database/resources.go b/pkg/components/database/resources.go similarity index 100% rename from pkg/ucp/database/resources.go rename to pkg/components/database/resources.go diff --git a/pkg/components/doc.go b/pkg/components/doc.go new file mode 100644 index 0000000000..49d7adc4ba --- /dev/null +++ b/pkg/components/doc.go @@ -0,0 +1,18 @@ +/* +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. +*/ + +// components and its subpackages hold the implementations of shared components used by the Radius control-plane services. +package components diff --git a/pkg/ucp/queue/apiserver/client.go b/pkg/components/queue/apiserver/client.go similarity index 90% rename from pkg/ucp/queue/apiserver/client.go rename to pkg/components/queue/apiserver/client.go index c133d874e4..a8a1dd6e95 100644 --- a/pkg/ucp/queue/apiserver/client.go +++ b/pkg/components/queue/apiserver/client.go @@ -60,9 +60,8 @@ import ( "strconv" "time" - "github.com/radius-project/radius/pkg/ucp/queue/client" - - v1alpha1 "github.com/radius-project/radius/pkg/ucp/database/apiserverstore/api/ucp.dev/v1alpha1" + v1alpha1 "github.com/radius-project/radius/pkg/components/database/apiserverstore/api/ucp.dev/v1alpha1" + "github.com/radius-project/radius/pkg/components/queue" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" @@ -81,7 +80,7 @@ const ( defaultExpiryDuration = time.Duration(10) * time.Hour ) -var _ client.Client = (*Client)(nil) +var _ queue.Client = (*Client)(nil) // Client is the queue client used for dev and test purpose. type Client struct { @@ -117,15 +116,15 @@ func getTimeFromString(s string) time.Time { return time.Unix(0, nsec) } -func copyMessage(msg *client.Message, queueMessage *v1alpha1.QueueMessage) { - msg.Metadata = client.Metadata{ +func copyMessage(msg *queue.Message, queueMessage *v1alpha1.QueueMessage) { + msg.Metadata = queue.Metadata{ ID: queueMessage.Name, DequeueCount: queueMessage.Spec.DequeueCount, EnqueueAt: queueMessage.Spec.EnqueueAt.Time, ExpireAt: queueMessage.Spec.ExpireAt.Time, NextVisibleAt: getTimeFromString(queueMessage.Labels[LabelNextVisibleAt]), } - msg.ContentType = client.JSONContentType + msg.ContentType = queue.JSONContentType msg.Data = make([]byte, len(queueMessage.Spec.Data.Raw)) copy(msg.Data, queueMessage.Spec.Data.Raw) } @@ -155,13 +154,13 @@ func (c *Client) generateID() (string, error) { return fmt.Sprintf("%s.%10d.%32x", c.opts.Name, time.Now().Unix(), b), nil } -func (c *Client) Enqueue(ctx context.Context, msg *client.Message, options ...client.EnqueueOptions) error { +func (c *Client) Enqueue(ctx context.Context, msg *queue.Message, options ...queue.EnqueueOptions) error { if msg == nil || msg.Data == nil || len(msg.Data) == 0 { - return client.ErrEmptyMessage + return queue.ErrEmptyMessage } - if msg.ContentType != client.JSONContentType { - return client.ErrUnsupportedContentType + if msg.ContentType != queue.JSONContentType { + return queue.ErrUnsupportedContentType } now := time.Now() @@ -183,7 +182,7 @@ func (c *Client) Enqueue(ctx context.Context, msg *client.Message, options ...cl DequeueCount: 0, EnqueueAt: metav1.Time{Time: now.UTC()}, ExpireAt: metav1.Time{Time: now.Add(c.opts.ExpiryDuration).UTC()}, - ContentType: client.JSONContentType, // RawExtension supports only JSON seralized data + ContentType: queue.JSONContentType, // RawExtension supports only JSON seralized data Data: &runtime.RawExtension{Raw: msg.Data}, }, } @@ -236,7 +235,7 @@ func (c *Client) getQueueMessage(ctx context.Context, now time.Time) (*v1alpha1. return &ql.Items[0], nil } - return nil, client.ErrMessageNotFound + return nil, queue.ErrMessageNotFound } // extendItem udpates LabelNextVisibleAt to extend the lease time of message. Dequeue and ExtendMessage @@ -257,14 +256,14 @@ func (c *Client) extendItem(ctx context.Context, id string, expectedDequeueCount // mismatched if another client leased this message. This can happen by clock skew Because // Dequeue() operation relies on system clock. if result.Spec.DequeueCount != expectedDequeueCount { - return client.ErrDequeuedMessage + return queue.ErrDequeuedMessage } nsec := mustParseInt64(result.Labels[LabelNextVisibleAt]) // Check if the message is already requeued. This condition is required for ExtendMessage because we cannot extend the message which was requeued. if !isDequeue && nsec < afterTime.UnixNano() { - return client.ErrInvalidMessage + return queue.ErrInvalidMessage } result.Labels[LabelNextVisibleAt] = int64toa(nextVisibleAt) @@ -284,11 +283,11 @@ func (c *Client) extendItem(ctx context.Context, id string, expectedDequeueCount return result, nil } -func (c *Client) Dequeue(ctx context.Context, opts client.QueueClientConfig) (*client.Message, error) { +func (c *Client) Dequeue(ctx context.Context, opts queue.QueueClientConfig) (*queue.Message, error) { var result *v1alpha1.QueueMessage DequeuedMessageError := func(err error) bool { - return errors.Is(err, client.ErrDequeuedMessage) + return errors.Is(err, queue.ErrDequeuedMessage) } now := time.Now() @@ -312,15 +311,15 @@ func (c *Client) Dequeue(ctx context.Context, opts client.QueueClientConfig) (*c return nil, retryErr } - msg := &client.Message{} + msg := &queue.Message{} copyMessage(msg, result) return msg, nil } -func (c *Client) FinishMessage(ctx context.Context, msg *client.Message) error { +func (c *Client) FinishMessage(ctx context.Context, msg *queue.Message) error { if msg == nil { - return client.ErrEmptyMessage + return queue.ErrEmptyMessage } result := &v1alpha1.QueueMessage{} @@ -342,9 +341,9 @@ func (c *Client) FinishMessage(ctx context.Context, msg *client.Message) error { return retryErr } -func (c *Client) ExtendMessage(ctx context.Context, msg *client.Message) error { +func (c *Client) ExtendMessage(ctx context.Context, msg *queue.Message) error { if msg == nil { - return client.ErrEmptyMessage + return queue.ErrEmptyMessage } now := time.Now() diff --git a/pkg/ucp/queue/apiserver/client_test.go b/pkg/components/queue/apiserver/client_test.go similarity index 88% rename from pkg/ucp/queue/apiserver/client_test.go rename to pkg/components/queue/apiserver/client_test.go index 77c608199f..7cc808b51a 100644 --- a/pkg/ucp/queue/apiserver/client_test.go +++ b/pkg/components/queue/apiserver/client_test.go @@ -22,8 +22,8 @@ import ( "testing" "time" - v1alpha1 "github.com/radius-project/radius/pkg/ucp/database/apiserverstore/api/ucp.dev/v1alpha1" - "github.com/radius-project/radius/pkg/ucp/queue/client" + v1alpha1 "github.com/radius-project/radius/pkg/components/database/apiserverstore/api/ucp.dev/v1alpha1" + "github.com/radius-project/radius/pkg/components/queue" "github.com/radius-project/radius/test/testcontext" "github.com/radius-project/radius/test/ucp/kubeenv" sharedtest "github.com/radius-project/radius/test/ucp/queuetest" @@ -54,8 +54,8 @@ func TestGetTimeFromString(t *testing.T) { } func TestCopyMessage(t *testing.T) { - msg := &client.Message{ - Metadata: client.Metadata{ID: "testid"}, + msg := &queue.Message{ + Metadata: queue.Metadata{ID: "testid"}, } now := time.Now() queueM := &v1alpha1.QueueMessage{ @@ -71,7 +71,7 @@ func TestCopyMessage(t *testing.T) { DequeueCount: 2, EnqueueAt: metav1.Time{Time: now.UTC()}, ExpireAt: metav1.Time{Time: now.Add(10 * time.Second).UTC()}, - ContentType: client.JSONContentType, // RawExtension supports only JSON seralized data + ContentType: queue.JSONContentType, // RawExtension supports only JSON seralized data Data: &runtime.RawExtension{Raw: []byte("hello world")}, }, } @@ -79,7 +79,7 @@ func TestCopyMessage(t *testing.T) { copyMessage(msg, queueM) require.Equal(t, queueM.ObjectMeta.Name, msg.ID) - require.Equal(t, client.JSONContentType, msg.ContentType) + require.Equal(t, queue.JSONContentType, msg.ContentType) require.Equal(t, queueM.Spec.DequeueCount, msg.DequeueCount) require.Equal(t, queueM.Spec.Data.Raw, msg.Data) require.Equal(t, queueM.Spec.ExpireAt.Time, msg.ExpireAt) @@ -131,9 +131,9 @@ func TestClient(t *testing.T) { client2, err := New(rc, Options{Name: "applications.core", Namespace: ns, MessageLockDuration: time.Duration(1) * time.Minute}) require.NoError(t, err) - err = client1.Enqueue(ctx, client.NewMessage("{}")) + err = client1.Enqueue(ctx, queue.NewMessage("{}")) require.NoError(t, err) - msg, err := client2.Dequeue(ctx, client.QueueClientConfig{}) + msg, err := client2.Dequeue(ctx, queue.QueueClientConfig{}) require.NoError(t, err) // Increase DequeueCount to mimic the situation when client1 updates message by the clock skew. @@ -141,6 +141,6 @@ func TestClient(t *testing.T) { require.NoError(t, err) err = client2.ExtendMessage(ctx, msg) - require.ErrorIs(t, err, client.ErrDequeuedMessage) + require.ErrorIs(t, err, queue.ErrDequeuedMessage) }) } diff --git a/pkg/ucp/queue/client/client.go b/pkg/components/queue/client.go similarity index 94% rename from pkg/ucp/queue/client/client.go rename to pkg/components/queue/client.go index 7a4471bd6c..c998c5aa19 100644 --- a/pkg/ucp/queue/client/client.go +++ b/pkg/components/queue/client.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package client +package queue import ( "context" @@ -41,7 +41,7 @@ var ( ErrEmptyMessage = errors.New("message must not be nil or message is empty") ) -//go:generate mockgen -typed -destination=./mock_client.go -package=client -self_package github.com/radius-project/radius/pkg/ucp/queue/client github.com/radius-project/radius/pkg/ucp/queue/client Client +//go:generate mockgen -typed -destination=./mock_client.go -package=queue -self_package github.com/radius-project/radius/pkg/components/queue github.com/radius-project/radius/pkg/components/queue Client // Client is an interface to implement queue operations. type Client interface { diff --git a/pkg/ucp/queue/client/client_test.go b/pkg/components/queue/client_test.go similarity index 99% rename from pkg/ucp/queue/client/client_test.go rename to pkg/components/queue/client_test.go index f6c4a6ad1b..d9fe6b91da 100644 --- a/pkg/ucp/queue/client/client_test.go +++ b/pkg/components/queue/client_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package client +package queue import ( "context" diff --git a/pkg/ucp/queue/inmemory/client.go b/pkg/components/queue/inmemory/client.go similarity index 74% rename from pkg/ucp/queue/inmemory/client.go rename to pkg/components/queue/inmemory/client.go index 70347ca322..86978bb204 100644 --- a/pkg/ucp/queue/inmemory/client.go +++ b/pkg/components/queue/inmemory/client.go @@ -20,11 +20,11 @@ import ( "context" "sync" - "github.com/radius-project/radius/pkg/ucp/queue/client" + "github.com/radius-project/radius/pkg/components/queue" ) var namedQueue = &sync.Map{} -var _ client.Client = (*Client)(nil) +var _ queue.Client = (*Client)(nil) // Client is the queue client used for dev and test purpose. type Client struct { @@ -51,36 +51,36 @@ func NewNamedQueue(name string) *Client { } // Enqueue enqueues message to the in-memory queue. -func (c *Client) Enqueue(ctx context.Context, msg *client.Message, options ...client.EnqueueOptions) error { +func (c *Client) Enqueue(ctx context.Context, msg *queue.Message, options ...queue.EnqueueOptions) error { if msg == nil || msg.Data == nil || len(msg.Data) == 0 { - return client.ErrEmptyMessage + return queue.ErrEmptyMessage } c.queue.Enqueue(msg) return nil } // Dequeue dequeues message from the in-memory queue. -func (c *Client) Dequeue(ctx context.Context, opts client.QueueClientConfig) (*client.Message, error) { +func (c *Client) Dequeue(ctx context.Context, opts queue.QueueClientConfig) (*queue.Message, error) { msg := c.queue.Dequeue() if msg == nil { - return nil, client.ErrMessageNotFound + return nil, queue.ErrMessageNotFound } return msg, nil } // FinishMessage finishes or deletes the message in the queue. -func (c *Client) FinishMessage(ctx context.Context, msg *client.Message) error { +func (c *Client) FinishMessage(ctx context.Context, msg *queue.Message) error { if msg == nil { - return client.ErrEmptyMessage + return queue.ErrEmptyMessage } return c.queue.Complete(msg) } // ExtendMessage extends the message lock. -func (c *Client) ExtendMessage(ctx context.Context, msg *client.Message) error { +func (c *Client) ExtendMessage(ctx context.Context, msg *queue.Message) error { if msg == nil { - return client.ErrEmptyMessage + return queue.ErrEmptyMessage } err := c.queue.Extend(msg) diff --git a/pkg/ucp/queue/inmemory/client_test.go b/pkg/components/queue/inmemory/client_test.go similarity index 85% rename from pkg/ucp/queue/inmemory/client_test.go rename to pkg/components/queue/inmemory/client_test.go index e3b35b9281..d175adfc2f 100644 --- a/pkg/ucp/queue/inmemory/client_test.go +++ b/pkg/components/queue/inmemory/client_test.go @@ -20,9 +20,9 @@ import ( "context" "testing" - "github.com/radius-project/radius/pkg/ucp/queue/client" "github.com/stretchr/testify/require" + "github.com/radius-project/radius/pkg/components/queue" sharedtest "github.com/radius-project/radius/test/ucp/queuetest" ) @@ -30,9 +30,9 @@ func TestNamedQueue(t *testing.T) { cli1 := NewNamedQueue("queue1") cli2 := NewNamedQueue("queue2") - err := cli1.Enqueue(context.Background(), &client.Message{Data: []byte("test1")}) + err := cli1.Enqueue(context.Background(), &queue.Message{Data: []byte("test1")}) require.NoError(t, err) - err = cli2.Enqueue(context.Background(), &client.Message{Data: []byte("test2")}) + err = cli2.Enqueue(context.Background(), &queue.Message{Data: []byte("test2")}) require.NoError(t, err) require.Equal(t, 1, cli1.queue.Len()) diff --git a/pkg/ucp/queue/inmemory/queue.go b/pkg/components/queue/inmemory/queue.go similarity index 88% rename from pkg/ucp/queue/inmemory/queue.go rename to pkg/components/queue/inmemory/queue.go index 5a428db340..5aa8df15a8 100644 --- a/pkg/ucp/queue/inmemory/queue.go +++ b/pkg/components/queue/inmemory/queue.go @@ -22,7 +22,7 @@ import ( "time" "github.com/google/uuid" - "github.com/radius-project/radius/pkg/ucp/queue/client" + "github.com/radius-project/radius/pkg/components/queue" ) var ( @@ -33,7 +33,7 @@ var ( ) type element struct { - val *client.Message + val *queue.Message visible bool } @@ -65,7 +65,7 @@ func (q *InmemQueue) DeleteAll() { _ = q.v.Init() } -func (q *InmemQueue) Enqueue(msg *client.Message) { +func (q *InmemQueue) Enqueue(msg *queue.Message) { q.updateQueue() q.vMu.Lock() @@ -79,10 +79,10 @@ func (q *InmemQueue) Enqueue(msg *client.Message) { q.v.PushBack(&element{val: msg, visible: true}) } -func (q *InmemQueue) Dequeue() *client.Message { +func (q *InmemQueue) Dequeue() *queue.Message { q.updateQueue() - var found *client.Message + var found *queue.Message q.elementRange(func(e *list.Element, elem *element) bool { if elem.visible { @@ -98,7 +98,7 @@ func (q *InmemQueue) Dequeue() *client.Message { return found } -func (q *InmemQueue) Complete(msg *client.Message) error { +func (q *InmemQueue) Complete(msg *queue.Message) error { found := false q.elementRange(func(e *list.Element, elem *element) bool { if elem.val.ID == msg.ID { @@ -110,13 +110,13 @@ func (q *InmemQueue) Complete(msg *client.Message) error { }) if !found { - return client.ErrInvalidMessage + return queue.ErrInvalidMessage } return nil } -func (q *InmemQueue) Extend(msg *client.Message) error { +func (q *InmemQueue) Extend(msg *queue.Message) error { found := false now := time.Now() q.elementRange(func(e *list.Element, elem *element) bool { @@ -135,7 +135,7 @@ func (q *InmemQueue) Extend(msg *client.Message) error { }) if !found { - return client.ErrInvalidMessage + return queue.ErrInvalidMessage } return nil diff --git a/pkg/ucp/queue/inmemory/queue_test.go b/pkg/components/queue/inmemory/queue_test.go similarity index 90% rename from pkg/ucp/queue/inmemory/queue_test.go rename to pkg/components/queue/inmemory/queue_test.go index 1a13e8d769..ec9b34810a 100644 --- a/pkg/ucp/queue/inmemory/queue_test.go +++ b/pkg/components/queue/inmemory/queue_test.go @@ -22,14 +22,14 @@ import ( "time" - "github.com/radius-project/radius/pkg/ucp/queue/client" + "github.com/radius-project/radius/pkg/components/queue" "github.com/stretchr/testify/require" ) func TestEnqueueDequeueMulti(t *testing.T) { q := NewInMemQueue(messageLockDuration) for i := 0; i < 10; i++ { - q.Enqueue(&client.Message{ + q.Enqueue(&queue.Message{ Data: []byte(fmt.Sprintf("test%d", i)), }) } @@ -48,7 +48,7 @@ func TestEnqueueDequeueMulti(t *testing.T) { func TestMessageLock(t *testing.T) { q := NewInMemQueue(2 * time.Millisecond) - q.Enqueue(&client.Message{ + q.Enqueue(&queue.Message{ Data: []byte("test"), }) @@ -67,7 +67,7 @@ func TestMessageLock(t *testing.T) { func TestExpiry(t *testing.T) { q := NewInMemQueue(messageLockDuration) - q.Enqueue(&client.Message{ + q.Enqueue(&queue.Message{ Data: []byte("test"), }) @@ -86,7 +86,7 @@ func TestExpiry(t *testing.T) { func TestComplete(t *testing.T) { q := NewInMemQueue(messageLockDuration) - q.Enqueue(&client.Message{ + q.Enqueue(&queue.Message{ Data: []byte("test"), }) @@ -96,7 +96,7 @@ func TestComplete(t *testing.T) { // Try to complete the message again. err = q.Complete(msg) - require.ErrorIs(t, client.ErrInvalidMessage, err) + require.ErrorIs(t, queue.ErrInvalidMessage, err) msg2 := q.Dequeue() require.Nil(t, msg2) diff --git a/pkg/ucp/queue/client/message.go b/pkg/components/queue/message.go similarity index 99% rename from pkg/ucp/queue/client/message.go rename to pkg/components/queue/message.go index 16237ef727..a76beb0294 100644 --- a/pkg/ucp/queue/client/message.go +++ b/pkg/components/queue/message.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package client +package queue import ( "encoding/json" diff --git a/pkg/ucp/queue/client/mock_client.go b/pkg/components/queue/mock_client.go similarity index 94% rename from pkg/ucp/queue/client/mock_client.go rename to pkg/components/queue/mock_client.go index fe0165e80e..aaf644513d 100644 --- a/pkg/ucp/queue/client/mock_client.go +++ b/pkg/components/queue/mock_client.go @@ -1,13 +1,13 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/radius-project/radius/pkg/ucp/queue/client (interfaces: Client) +// Source: github.com/radius-project/radius/pkg/components/queue (interfaces: Client) // // Generated by this command: // -// mockgen -typed -destination=./mock_client.go -package=client -self_package github.com/radius-project/radius/pkg/ucp/queue/client github.com/radius-project/radius/pkg/ucp/queue/client Client +// mockgen -typed -destination=./mock_client.go -package=queue -self_package github.com/radius-project/radius/pkg/components/queue github.com/radius-project/radius/pkg/components/queue Client // -// Package client is a generated GoMock package. -package client +// Package queue is a generated GoMock package. +package queue import ( context "context" diff --git a/pkg/ucp/queue/client/options.go b/pkg/components/queue/options.go similarity index 99% rename from pkg/ucp/queue/client/options.go rename to pkg/components/queue/options.go index d3d49a9c71..7ae1f50c87 100644 --- a/pkg/ucp/queue/client/options.go +++ b/pkg/components/queue/options.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package client +package queue import "time" diff --git a/pkg/ucp/queue/queueprovider/factory.go b/pkg/components/queue/queueprovider/factory.go similarity index 87% rename from pkg/ucp/queue/queueprovider/factory.go rename to pkg/components/queue/queueprovider/factory.go index c206cb7942..af43746283 100644 --- a/pkg/ucp/queue/queueprovider/factory.go +++ b/pkg/components/queue/queueprovider/factory.go @@ -21,11 +21,11 @@ import ( "errors" "fmt" + ucpv1alpha1 "github.com/radius-project/radius/pkg/components/database/apiserverstore/api/ucp.dev/v1alpha1" + "github.com/radius-project/radius/pkg/components/queue" + "github.com/radius-project/radius/pkg/components/queue/apiserver" + qinmem "github.com/radius-project/radius/pkg/components/queue/inmemory" "github.com/radius-project/radius/pkg/kubeutil" - ucpv1alpha1 "github.com/radius-project/radius/pkg/ucp/database/apiserverstore/api/ucp.dev/v1alpha1" - "github.com/radius-project/radius/pkg/ucp/queue/apiserver" - queue "github.com/radius-project/radius/pkg/ucp/queue/client" - qinmem "github.com/radius-project/radius/pkg/ucp/queue/inmemory" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/rest" runtimeclient "sigs.k8s.io/controller-runtime/pkg/client" diff --git a/pkg/ucp/queue/queueprovider/options.go b/pkg/components/queue/queueprovider/options.go similarity index 100% rename from pkg/ucp/queue/queueprovider/options.go rename to pkg/components/queue/queueprovider/options.go diff --git a/pkg/ucp/queue/queueprovider/provider.go b/pkg/components/queue/queueprovider/provider.go similarity index 96% rename from pkg/ucp/queue/queueprovider/provider.go rename to pkg/components/queue/queueprovider/provider.go index 6b25ea1e4f..2350089fc0 100644 --- a/pkg/ucp/queue/queueprovider/provider.go +++ b/pkg/components/queue/queueprovider/provider.go @@ -21,7 +21,7 @@ import ( "errors" "sync" - queue "github.com/radius-project/radius/pkg/ucp/queue/client" + "github.com/radius-project/radius/pkg/components/queue" ) var ( diff --git a/pkg/ucp/queue/queueprovider/provider_test.go b/pkg/components/queue/queueprovider/provider_test.go similarity index 100% rename from pkg/ucp/queue/queueprovider/provider_test.go rename to pkg/components/queue/queueprovider/provider_test.go diff --git a/pkg/ucp/queue/queueprovider/types.go b/pkg/components/queue/queueprovider/types.go similarity index 100% rename from pkg/ucp/queue/queueprovider/types.go rename to pkg/components/queue/queueprovider/types.go diff --git a/pkg/ucp/secret/client.go b/pkg/components/secret/client.go similarity index 95% rename from pkg/ucp/secret/client.go rename to pkg/components/secret/client.go index a4ae6a3d0f..737bb18d39 100644 --- a/pkg/ucp/secret/client.go +++ b/pkg/components/secret/client.go @@ -21,7 +21,7 @@ import ( "encoding/json" ) -//go:generate mockgen -typed -destination=./mock_client.go -package=secret -self_package github.com/radius-project/radius/pkg/ucp/secret github.com/radius-project/radius/pkg/ucp/secret Client +//go:generate mockgen -typed -destination=./mock_client.go -package=secret -self_package github.com/radius-project/radius/pkg/components/secret github.com/radius-project/radius/pkg/components/secret Client // Client is an interface to implement secret operations. type Client interface { diff --git a/pkg/ucp/secret/client_test.go b/pkg/components/secret/client_test.go similarity index 100% rename from pkg/ucp/secret/client_test.go rename to pkg/components/secret/client_test.go diff --git a/pkg/ucp/secret/etcd/client.go b/pkg/components/secret/etcd/client.go similarity index 97% rename from pkg/ucp/secret/etcd/client.go rename to pkg/components/secret/etcd/client.go index 05a26276d6..344cb0a4c4 100644 --- a/pkg/ucp/secret/etcd/client.go +++ b/pkg/components/secret/etcd/client.go @@ -19,7 +19,7 @@ package etcd import ( "context" - "github.com/radius-project/radius/pkg/ucp/secret" + "github.com/radius-project/radius/pkg/components/secret" "github.com/radius-project/radius/pkg/ucp/util" etcdclient "go.etcd.io/etcd/client/v3" ) diff --git a/pkg/ucp/secret/etcd/client_test.go b/pkg/components/secret/etcd/client_test.go similarity index 98% rename from pkg/ucp/secret/etcd/client_test.go rename to pkg/components/secret/etcd/client_test.go index f7f033a11d..29219e2f16 100644 --- a/pkg/ucp/secret/etcd/client_test.go +++ b/pkg/components/secret/etcd/client_test.go @@ -22,9 +22,9 @@ import ( "strconv" "testing" + "github.com/radius-project/radius/pkg/components/secret" "github.com/radius-project/radius/pkg/ucp/data" "github.com/radius-project/radius/pkg/ucp/hosting" - "github.com/radius-project/radius/pkg/ucp/secret" "github.com/radius-project/radius/test/testcontext" "github.com/stretchr/testify/require" etcdclient "go.etcd.io/etcd/client/v3" diff --git a/pkg/ucp/secret/inmemory/client.go b/pkg/components/secret/inmemory/client.go similarity index 97% rename from pkg/ucp/secret/inmemory/client.go rename to pkg/components/secret/inmemory/client.go index 8c9c7d3c35..c22ea350fd 100644 --- a/pkg/ucp/secret/inmemory/client.go +++ b/pkg/components/secret/inmemory/client.go @@ -20,8 +20,8 @@ import ( "context" "sync" + "github.com/radius-project/radius/pkg/components/secret" "github.com/radius-project/radius/pkg/kubernetes" - "github.com/radius-project/radius/pkg/ucp/secret" ) var _ secret.Client = (*Client)(nil) diff --git a/pkg/ucp/secret/inmemory/client_test.go b/pkg/components/secret/inmemory/client_test.go similarity index 98% rename from pkg/ucp/secret/inmemory/client_test.go rename to pkg/components/secret/inmemory/client_test.go index b24e5ffd43..eaeed5ddf9 100644 --- a/pkg/ucp/secret/inmemory/client_test.go +++ b/pkg/components/secret/inmemory/client_test.go @@ -21,7 +21,7 @@ import ( "encoding/json" "testing" - "github.com/radius-project/radius/pkg/ucp/secret" + "github.com/radius-project/radius/pkg/components/secret" "github.com/stretchr/testify/require" ) diff --git a/pkg/ucp/secret/kubernetes/client.go b/pkg/components/secret/kubernetes/client.go similarity index 98% rename from pkg/ucp/secret/kubernetes/client.go rename to pkg/components/secret/kubernetes/client.go index 6a1c7dfc89..5018dcee3e 100644 --- a/pkg/ucp/secret/kubernetes/client.go +++ b/pkg/components/secret/kubernetes/client.go @@ -19,8 +19,8 @@ package kubernetes import ( "context" + "github.com/radius-project/radius/pkg/components/secret" "github.com/radius-project/radius/pkg/kubernetes" - "github.com/radius-project/radius/pkg/ucp/secret" corev1 "k8s.io/api/core/v1" k8s_error "k8s.io/apimachinery/pkg/api/errors" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" diff --git a/pkg/ucp/secret/kubernetes/client_test.go b/pkg/components/secret/kubernetes/client_test.go similarity index 98% rename from pkg/ucp/secret/kubernetes/client_test.go rename to pkg/components/secret/kubernetes/client_test.go index b6b1905e65..f4acd9c972 100644 --- a/pkg/ucp/secret/kubernetes/client_test.go +++ b/pkg/components/secret/kubernetes/client_test.go @@ -21,7 +21,7 @@ import ( "encoding/json" "testing" - "github.com/radius-project/radius/pkg/ucp/secret" + "github.com/radius-project/radius/pkg/components/secret" "github.com/radius-project/radius/test/k8sutil" "github.com/stretchr/testify/require" diff --git a/pkg/ucp/secret/mock_client.go b/pkg/components/secret/mock_client.go similarity index 95% rename from pkg/ucp/secret/mock_client.go rename to pkg/components/secret/mock_client.go index 492431c027..0999d8f8e8 100644 --- a/pkg/ucp/secret/mock_client.go +++ b/pkg/components/secret/mock_client.go @@ -1,9 +1,9 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/radius-project/radius/pkg/ucp/secret (interfaces: Client) +// Source: github.com/radius-project/radius/pkg/components/secret (interfaces: Client) // // Generated by this command: // -// mockgen -typed -destination=./mock_client.go -package=secret -self_package github.com/radius-project/radius/pkg/ucp/secret github.com/radius-project/radius/pkg/ucp/secret Client +// mockgen -typed -destination=./mock_client.go -package=secret -self_package github.com/radius-project/radius/pkg/components/secret github.com/radius-project/radius/pkg/components/secret Client // // Package secret is a generated GoMock package. diff --git a/pkg/ucp/secret/secretprovider/factory.go b/pkg/components/secret/secretprovider/factory.go similarity index 85% rename from pkg/ucp/secret/secretprovider/factory.go rename to pkg/components/secret/secretprovider/factory.go index 219b355827..f9ca30572e 100644 --- a/pkg/ucp/secret/secretprovider/factory.go +++ b/pkg/components/secret/secretprovider/factory.go @@ -20,13 +20,13 @@ import ( "context" "errors" + "github.com/radius-project/radius/pkg/components/database/databaseprovider" + "github.com/radius-project/radius/pkg/components/database/etcdstore" + "github.com/radius-project/radius/pkg/components/secret" + "github.com/radius-project/radius/pkg/components/secret/etcd" + "github.com/radius-project/radius/pkg/components/secret/inmemory" + kubernetes_client "github.com/radius-project/radius/pkg/components/secret/kubernetes" "github.com/radius-project/radius/pkg/kubeutil" - "github.com/radius-project/radius/pkg/ucp/database/etcdstore" - "github.com/radius-project/radius/pkg/ucp/databaseprovider" - "github.com/radius-project/radius/pkg/ucp/secret" - "github.com/radius-project/radius/pkg/ucp/secret/etcd" - "github.com/radius-project/radius/pkg/ucp/secret/inmemory" - kubernetes_client "github.com/radius-project/radius/pkg/ucp/secret/kubernetes" "k8s.io/kubectl/pkg/scheme" controller_runtime "sigs.k8s.io/controller-runtime/pkg/client" ) diff --git a/pkg/ucp/secret/secretprovider/options.go b/pkg/components/secret/secretprovider/options.go similarity index 92% rename from pkg/ucp/secret/secretprovider/options.go rename to pkg/components/secret/secretprovider/options.go index b10fe006b8..9820a91f8a 100644 --- a/pkg/ucp/secret/secretprovider/options.go +++ b/pkg/components/secret/secretprovider/options.go @@ -16,7 +16,7 @@ limitations under the License. package secretprovider -import "github.com/radius-project/radius/pkg/ucp/databaseprovider" +import "github.com/radius-project/radius/pkg/components/database/databaseprovider" // SecretProviderOptions contains provider information of the secret. type SecretProviderOptions struct { diff --git a/pkg/ucp/secret/secretprovider/provider.go b/pkg/components/secret/secretprovider/provider.go similarity index 96% rename from pkg/ucp/secret/secretprovider/provider.go rename to pkg/components/secret/secretprovider/provider.go index feef4f47a3..46ebd57bcd 100644 --- a/pkg/ucp/secret/secretprovider/provider.go +++ b/pkg/components/secret/secretprovider/provider.go @@ -21,7 +21,7 @@ import ( "errors" "sync" - "github.com/radius-project/radius/pkg/ucp/secret" + "github.com/radius-project/radius/pkg/components/secret" ) var ( diff --git a/pkg/ucp/secret/secretprovider/provider_test.go b/pkg/components/secret/secretprovider/provider_test.go similarity index 100% rename from pkg/ucp/secret/secretprovider/provider_test.go rename to pkg/components/secret/secretprovider/provider_test.go diff --git a/pkg/ucp/secret/secretprovider/types.go b/pkg/components/secret/secretprovider/types.go similarity index 100% rename from pkg/ucp/secret/secretprovider/types.go rename to pkg/components/secret/secretprovider/types.go diff --git a/pkg/corerp/backend/controller/createorupdateresource.go b/pkg/corerp/backend/controller/createorupdateresource.go index 65a86ff108..69fe39329a 100644 --- a/pkg/corerp/backend/controller/createorupdateresource.go +++ b/pkg/corerp/backend/controller/createorupdateresource.go @@ -25,12 +25,12 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/corerp/datamodel" "github.com/radius-project/radius/pkg/corerp/renderers/container" "github.com/radius-project/radius/pkg/corerp/renderers/gateway" "github.com/radius-project/radius/pkg/corerp/renderers/volume" rpv1 "github.com/radius-project/radius/pkg/rp/v1" - "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/resources" ) diff --git a/pkg/corerp/backend/controller/createorupdateresource_test.go b/pkg/corerp/backend/controller/createorupdateresource_test.go index 8c5a0cc787..21f6d9c0e6 100644 --- a/pkg/corerp/backend/controller/createorupdateresource_test.go +++ b/pkg/corerp/backend/controller/createorupdateresource_test.go @@ -28,13 +28,13 @@ import ( "go.uber.org/mock/gomock" ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" + "github.com/radius-project/radius/pkg/components/database" deployment "github.com/radius-project/radius/pkg/corerp/backend/deployment" "github.com/radius-project/radius/pkg/corerp/renderers" "github.com/radius-project/radius/pkg/corerp/renderers/container" "github.com/radius-project/radius/pkg/corerp/renderers/gateway" ds_ctrl "github.com/radius-project/radius/pkg/datastoresrp/frontend/controller" rpv1 "github.com/radius-project/radius/pkg/rp/v1" - "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/resources" ) diff --git a/pkg/corerp/backend/controller/deleteresource_test.go b/pkg/corerp/backend/controller/deleteresource_test.go index b1cff8cf7c..e5d9ffe2f2 100644 --- a/pkg/corerp/backend/controller/deleteresource_test.go +++ b/pkg/corerp/backend/controller/deleteresource_test.go @@ -24,8 +24,8 @@ import ( "github.com/google/uuid" ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" + "github.com/radius-project/radius/pkg/components/database" deployment "github.com/radius-project/radius/pkg/corerp/backend/deployment" - "github.com/radius-project/radius/pkg/ucp/database" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" ) diff --git a/pkg/corerp/backend/deployment/deploymentprocessor.go b/pkg/corerp/backend/deployment/deploymentprocessor.go index a7803ae8bc..1e4de863c2 100644 --- a/pkg/corerp/backend/deployment/deploymentprocessor.go +++ b/pkg/corerp/backend/deployment/deploymentprocessor.go @@ -29,6 +29,7 @@ import ( rp_util "github.com/radius-project/radius/pkg/rp/util" rpv1 "github.com/radius-project/radius/pkg/rp/v1" + "github.com/radius-project/radius/pkg/components/database" corerp_dm "github.com/radius-project/radius/pkg/corerp/datamodel" "github.com/radius-project/radius/pkg/corerp/handlers" "github.com/radius-project/radius/pkg/corerp/model" @@ -40,7 +41,6 @@ import ( msg_dm "github.com/radius-project/radius/pkg/messagingrp/datamodel" msg_ctrl "github.com/radius-project/radius/pkg/messagingrp/frontend/controller" "github.com/radius-project/radius/pkg/portableresources" - "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/resources" "github.com/radius-project/radius/pkg/ucp/ucplog" diff --git a/pkg/corerp/backend/deployment/deploymentprocessor_test.go b/pkg/corerp/backend/deployment/deploymentprocessor_test.go index 0cb4aaa29d..a0d64f52bf 100644 --- a/pkg/corerp/backend/deployment/deploymentprocessor_test.go +++ b/pkg/corerp/backend/deployment/deploymentprocessor_test.go @@ -25,6 +25,7 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" "github.com/radius-project/radius/pkg/azure/clientv2" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/corerp/datamodel" "github.com/radius-project/radius/pkg/corerp/handlers" "github.com/radius-project/radius/pkg/corerp/model" @@ -37,7 +38,6 @@ import ( "github.com/radius-project/radius/pkg/resourcemodel" rpv1 "github.com/radius-project/radius/pkg/rp/v1" "github.com/radius-project/radius/pkg/to" - "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/resources" resources_azure "github.com/radius-project/radius/pkg/ucp/resources/azure" resources_kubernetes "github.com/radius-project/radius/pkg/ucp/resources/kubernetes" diff --git a/pkg/corerp/frontend/controller/applications/getgraph_test.go b/pkg/corerp/frontend/controller/applications/getgraph_test.go index d859cf5d31..4191d1dfe5 100644 --- a/pkg/corerp/frontend/controller/applications/getgraph_test.go +++ b/pkg/corerp/frontend/controller/applications/getgraph_test.go @@ -24,8 +24,8 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rpctest" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/sdk" - "github.com/radius-project/radius/pkg/ucp/database" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" ) diff --git a/pkg/corerp/frontend/controller/applications/updatefilter_test.go b/pkg/corerp/frontend/controller/applications/updatefilter_test.go index 7145de92f7..96e94304e0 100644 --- a/pkg/corerp/frontend/controller/applications/updatefilter_test.go +++ b/pkg/corerp/frontend/controller/applications/updatefilter_test.go @@ -24,9 +24,9 @@ import ( ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rest" "github.com/radius-project/radius/pkg/armrpc/rpctest" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/corerp/datamodel" rpv1 "github.com/radius-project/radius/pkg/rp/v1" - "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/resources" "github.com/radius-project/radius/test/k8sutil" diff --git a/pkg/corerp/frontend/controller/environments/createorupdateenvironment_test.go b/pkg/corerp/frontend/controller/environments/createorupdateenvironment_test.go index 13e12d07ce..aa3420cf7e 100644 --- a/pkg/corerp/frontend/controller/environments/createorupdateenvironment_test.go +++ b/pkg/corerp/frontend/controller/environments/createorupdateenvironment_test.go @@ -26,8 +26,8 @@ import ( ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rpctest" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/corerp/api/v20231001preview" - "github.com/radius-project/radius/pkg/ucp/database" "github.com/google/uuid" "github.com/stretchr/testify/require" diff --git a/pkg/corerp/frontend/controller/environments/getrecipemetadata_test.go b/pkg/corerp/frontend/controller/environments/getrecipemetadata_test.go index 4d33f65af7..1d44ec10ba 100644 --- a/pkg/corerp/frontend/controller/environments/getrecipemetadata_test.go +++ b/pkg/corerp/frontend/controller/environments/getrecipemetadata_test.go @@ -27,10 +27,10 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rpctest" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/corerp/api/v20231001preview" "github.com/radius-project/radius/pkg/recipes" "github.com/radius-project/radius/pkg/recipes/engine" - "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/test/testutil" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" diff --git a/pkg/corerp/frontend/controller/extenders/listsecretsextender_test.go b/pkg/corerp/frontend/controller/extenders/listsecretsextender_test.go index 8dfd721f9a..8b98752c82 100644 --- a/pkg/corerp/frontend/controller/extenders/listsecretsextender_test.go +++ b/pkg/corerp/frontend/controller/extenders/listsecretsextender_test.go @@ -27,7 +27,7 @@ import ( "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rpctest" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" diff --git a/pkg/corerp/frontend/controller/secretstores/kubernetes.go b/pkg/corerp/frontend/controller/secretstores/kubernetes.go index a5b8448e46..0468b8246a 100644 --- a/pkg/corerp/frontend/controller/secretstores/kubernetes.go +++ b/pkg/corerp/frontend/controller/secretstores/kubernetes.go @@ -25,12 +25,12 @@ import ( "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rest" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/corerp/datamodel" "github.com/radius-project/radius/pkg/kubernetes" "github.com/radius-project/radius/pkg/kubeutil" rpv1 "github.com/radius-project/radius/pkg/rp/v1" "github.com/radius-project/radius/pkg/to" - "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/resources" resources_kubernetes "github.com/radius-project/radius/pkg/ucp/resources/kubernetes" corev1 "k8s.io/api/core/v1" diff --git a/pkg/corerp/frontend/controller/secretstores/kubernetes_test.go b/pkg/corerp/frontend/controller/secretstores/kubernetes_test.go index 6ead89f9fc..8305f2c06d 100644 --- a/pkg/corerp/frontend/controller/secretstores/kubernetes_test.go +++ b/pkg/corerp/frontend/controller/secretstores/kubernetes_test.go @@ -24,10 +24,10 @@ import ( "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rest" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/corerp/datamodel" "github.com/radius-project/radius/pkg/kubernetes" rpv1 "github.com/radius-project/radius/pkg/rp/v1" - "github.com/radius-project/radius/pkg/ucp/database" resources_kubernetes "github.com/radius-project/radius/pkg/ucp/resources/kubernetes" "github.com/radius-project/radius/test/k8sutil" "github.com/radius-project/radius/test/testutil" diff --git a/pkg/corerp/frontend/controller/secretstores/listsecrets_test.go b/pkg/corerp/frontend/controller/secretstores/listsecrets_test.go index df1551d4d4..e868cc11e4 100644 --- a/pkg/corerp/frontend/controller/secretstores/listsecrets_test.go +++ b/pkg/corerp/frontend/controller/secretstores/listsecrets_test.go @@ -26,9 +26,9 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rpctest" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/corerp/api/v20231001preview" "github.com/radius-project/radius/pkg/corerp/datamodel" - "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/test/k8sutil" "github.com/radius-project/radius/test/testutil" "github.com/stretchr/testify/require" diff --git a/pkg/corerp/frontend/controller/util/query.go b/pkg/corerp/frontend/controller/util/query.go index 1ed1209bab..07df01005c 100644 --- a/pkg/corerp/frontend/controller/util/query.go +++ b/pkg/corerp/frontend/controller/util/query.go @@ -19,7 +19,7 @@ package util import ( "context" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" ) // FindResources searches for resources of a given type with a given filter key and value, and returns the query result. diff --git a/pkg/corerp/setup/setup_test.go b/pkg/corerp/setup/setup_test.go index fa970df43f..1e719c127e 100644 --- a/pkg/corerp/setup/setup_test.go +++ b/pkg/corerp/setup/setup_test.go @@ -30,9 +30,9 @@ import ( "github.com/radius-project/radius/pkg/armrpc/builder" apictrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rpctest" + "github.com/radius-project/radius/pkg/components/database/inmemory" "github.com/radius-project/radius/pkg/recipes/controllerconfig" "github.com/radius-project/radius/pkg/sdk" - "github.com/radius-project/radius/pkg/ucp/database/inmemory" app_ctrl "github.com/radius-project/radius/pkg/corerp/frontend/controller/applications" ctr_ctrl "github.com/radius-project/radius/pkg/corerp/frontend/controller/containers" diff --git a/pkg/daprrp/setup/setup_test.go b/pkg/daprrp/setup/setup_test.go index d12e79dee4..8e3d0097e7 100644 --- a/pkg/daprrp/setup/setup_test.go +++ b/pkg/daprrp/setup/setup_test.go @@ -30,9 +30,9 @@ import ( "github.com/radius-project/radius/pkg/armrpc/builder" apictrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rpctest" + "github.com/radius-project/radius/pkg/components/database/inmemory" dapr_ctrl "github.com/radius-project/radius/pkg/daprrp/frontend/controller" "github.com/radius-project/radius/pkg/recipes/controllerconfig" - "github.com/radius-project/radius/pkg/ucp/database/inmemory" ) var handlerTests = []rpctest.HandlerTestSpec{ diff --git a/pkg/datastoresrp/frontend/controller/mongodatabases/listsecretsmongodatabase_test.go b/pkg/datastoresrp/frontend/controller/mongodatabases/listsecretsmongodatabase_test.go index 938b08d255..f1e0240728 100644 --- a/pkg/datastoresrp/frontend/controller/mongodatabases/listsecretsmongodatabase_test.go +++ b/pkg/datastoresrp/frontend/controller/mongodatabases/listsecretsmongodatabase_test.go @@ -27,9 +27,9 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rpctest" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/datastoresrp/api/v20231001preview" "github.com/radius-project/radius/pkg/portableresources/renderers" - "github.com/radius-project/radius/pkg/ucp/database" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" diff --git a/pkg/datastoresrp/frontend/controller/rediscaches/listsecretsrediscache.go b/pkg/datastoresrp/frontend/controller/rediscaches/listsecretsrediscache.go index 89cd7a91ec..fe83298a49 100644 --- a/pkg/datastoresrp/frontend/controller/rediscaches/listsecretsrediscache.go +++ b/pkg/datastoresrp/frontend/controller/rediscaches/listsecretsrediscache.go @@ -24,10 +24,10 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rest" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/datastoresrp/datamodel" "github.com/radius-project/radius/pkg/datastoresrp/datamodel/converter" "github.com/radius-project/radius/pkg/portableresources/renderers" - "github.com/radius-project/radius/pkg/ucp/database" ) var _ ctrl.Controller = (*ListSecretsRedisCache)(nil) diff --git a/pkg/datastoresrp/frontend/controller/rediscaches/listsecretsrediscache_test.go b/pkg/datastoresrp/frontend/controller/rediscaches/listsecretsrediscache_test.go index 903baac905..e11e4d141e 100644 --- a/pkg/datastoresrp/frontend/controller/rediscaches/listsecretsrediscache_test.go +++ b/pkg/datastoresrp/frontend/controller/rediscaches/listsecretsrediscache_test.go @@ -27,9 +27,9 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rpctest" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/datastoresrp/api/v20231001preview" "github.com/radius-project/radius/pkg/portableresources/renderers" - "github.com/radius-project/radius/pkg/ucp/database" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" diff --git a/pkg/datastoresrp/frontend/controller/sqldatabases/listsecretssqldatabase_test.go b/pkg/datastoresrp/frontend/controller/sqldatabases/listsecretssqldatabase_test.go index cdd3e8bfeb..925f5b24b4 100644 --- a/pkg/datastoresrp/frontend/controller/sqldatabases/listsecretssqldatabase_test.go +++ b/pkg/datastoresrp/frontend/controller/sqldatabases/listsecretssqldatabase_test.go @@ -27,8 +27,8 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rpctest" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/datastoresrp/api/v20231001preview" - "github.com/radius-project/radius/pkg/ucp/database" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" diff --git a/pkg/datastoresrp/setup/setup_test.go b/pkg/datastoresrp/setup/setup_test.go index 918e98e79e..97c31480cb 100644 --- a/pkg/datastoresrp/setup/setup_test.go +++ b/pkg/datastoresrp/setup/setup_test.go @@ -30,9 +30,9 @@ import ( "github.com/radius-project/radius/pkg/armrpc/builder" apictrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rpctest" + "github.com/radius-project/radius/pkg/components/database/inmemory" ds_ctrl "github.com/radius-project/radius/pkg/datastoresrp/frontend/controller" "github.com/radius-project/radius/pkg/recipes/controllerconfig" - "github.com/radius-project/radius/pkg/ucp/database/inmemory" ) var handlerTests = []rpctest.HandlerTestSpec{ diff --git a/pkg/dynamicrp/config.go b/pkg/dynamicrp/config.go index 41ffacdbe8..7cda77cf6e 100644 --- a/pkg/dynamicrp/config.go +++ b/pkg/dynamicrp/config.go @@ -20,13 +20,13 @@ import ( "bytes" "github.com/radius-project/radius/pkg/armrpc/hostoptions" + "github.com/radius-project/radius/pkg/components/database/databaseprovider" + "github.com/radius-project/radius/pkg/components/queue/queueprovider" + "github.com/radius-project/radius/pkg/components/secret/secretprovider" metricsprovider "github.com/radius-project/radius/pkg/metrics/provider" profilerprovider "github.com/radius-project/radius/pkg/profiler/provider" "github.com/radius-project/radius/pkg/trace" ucpconfig "github.com/radius-project/radius/pkg/ucp/config" - "github.com/radius-project/radius/pkg/ucp/databaseprovider" - "github.com/radius-project/radius/pkg/ucp/queue/queueprovider" - "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" "github.com/radius-project/radius/pkg/ucp/ucplog" "gopkg.in/yaml.v3" ) diff --git a/pkg/dynamicrp/options.go b/pkg/dynamicrp/options.go index 8d046c9132..4ebaf80a1a 100644 --- a/pkg/dynamicrp/options.go +++ b/pkg/dynamicrp/options.go @@ -22,13 +22,13 @@ import ( "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" "github.com/radius-project/radius/pkg/armrpc/hostoptions" + "github.com/radius-project/radius/pkg/components/database/databaseprovider" + "github.com/radius-project/radius/pkg/components/queue/queueprovider" + "github.com/radius-project/radius/pkg/components/secret/secretprovider" "github.com/radius-project/radius/pkg/kubeutil" "github.com/radius-project/radius/pkg/recipes/controllerconfig" "github.com/radius-project/radius/pkg/sdk" ucpconfig "github.com/radius-project/radius/pkg/ucp/config" - "github.com/radius-project/radius/pkg/ucp/databaseprovider" - "github.com/radius-project/radius/pkg/ucp/queue/queueprovider" - "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" kube_rest "k8s.io/client-go/rest" ) diff --git a/pkg/dynamicrp/server/server.go b/pkg/dynamicrp/server/server.go index 288737c780..ff3fc804b5 100644 --- a/pkg/dynamicrp/server/server.go +++ b/pkg/dynamicrp/server/server.go @@ -19,6 +19,7 @@ package server import ( "time" + "github.com/radius-project/radius/pkg/components/database/databaseprovider" "github.com/radius-project/radius/pkg/dynamicrp" "github.com/radius-project/radius/pkg/dynamicrp/backend" "github.com/radius-project/radius/pkg/dynamicrp/frontend" @@ -26,7 +27,6 @@ import ( profilerservice "github.com/radius-project/radius/pkg/profiler/service" "github.com/radius-project/radius/pkg/trace" "github.com/radius-project/radius/pkg/ucp/data" - "github.com/radius-project/radius/pkg/ucp/databaseprovider" "github.com/radius-project/radius/pkg/ucp/hosting" ) diff --git a/pkg/messagingrp/frontend/controller/rabbitmqqueues/listsecretsrabbitmq_test.go b/pkg/messagingrp/frontend/controller/rabbitmqqueues/listsecretsrabbitmq_test.go index de0bf25c2d..a0aa3d890e 100644 --- a/pkg/messagingrp/frontend/controller/rabbitmqqueues/listsecretsrabbitmq_test.go +++ b/pkg/messagingrp/frontend/controller/rabbitmqqueues/listsecretsrabbitmq_test.go @@ -26,9 +26,9 @@ import ( ctrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rpctest" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/messagingrp/api/v20231001preview" "github.com/radius-project/radius/pkg/portableresources/renderers" - "github.com/radius-project/radius/pkg/ucp/database" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" ) diff --git a/pkg/messagingrp/setup/setup_test.go b/pkg/messagingrp/setup/setup_test.go index 48573e2e62..93f5ad0f2b 100644 --- a/pkg/messagingrp/setup/setup_test.go +++ b/pkg/messagingrp/setup/setup_test.go @@ -27,9 +27,9 @@ import ( "github.com/radius-project/radius/pkg/armrpc/builder" apictrl "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rpctest" + "github.com/radius-project/radius/pkg/components/database/inmemory" msg_ctrl "github.com/radius-project/radius/pkg/messagingrp/frontend/controller" "github.com/radius-project/radius/pkg/recipes/controllerconfig" - "github.com/radius-project/radius/pkg/ucp/database/inmemory" ) var handlerTests = []rpctest.HandlerTestSpec{ diff --git a/pkg/portableresources/backend/controller/createorupdateresource.go b/pkg/portableresources/backend/controller/createorupdateresource.go index 028933421a..4cb1c162c6 100644 --- a/pkg/portableresources/backend/controller/createorupdateresource.go +++ b/pkg/portableresources/backend/controller/createorupdateresource.go @@ -22,6 +22,7 @@ import ( "fmt" ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/portableresources/datamodel" "github.com/radius-project/radius/pkg/portableresources/processors" "github.com/radius-project/radius/pkg/recipes" @@ -29,7 +30,6 @@ import ( "github.com/radius-project/radius/pkg/recipes/engine" "github.com/radius-project/radius/pkg/recipes/util" rpv1 "github.com/radius-project/radius/pkg/rp/v1" - "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/ucplog" ) diff --git a/pkg/portableresources/backend/controller/createorupdateresource_test.go b/pkg/portableresources/backend/controller/createorupdateresource_test.go index 584ddd3632..cc1ab85aba 100644 --- a/pkg/portableresources/backend/controller/createorupdateresource_test.go +++ b/pkg/portableresources/backend/controller/createorupdateresource_test.go @@ -29,6 +29,7 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/portableresources" "github.com/radius-project/radius/pkg/portableresources/datamodel" "github.com/radius-project/radius/pkg/portableresources/processors" @@ -37,7 +38,6 @@ import ( "github.com/radius-project/radius/pkg/recipes/controllerconfig" "github.com/radius-project/radius/pkg/recipes/engine" rpv1 "github.com/radius-project/radius/pkg/rp/v1" - "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/resources" ) diff --git a/pkg/portableresources/backend/controller/deleteresource_test.go b/pkg/portableresources/backend/controller/deleteresource_test.go index ec1c021918..89fd05c376 100644 --- a/pkg/portableresources/backend/controller/deleteresource_test.go +++ b/pkg/portableresources/backend/controller/deleteresource_test.go @@ -25,12 +25,12 @@ import ( "github.com/google/uuid" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/recipes" "github.com/radius-project/radius/pkg/recipes/configloader" "github.com/radius-project/radius/pkg/recipes/engine" rpv1 "github.com/radius-project/radius/pkg/rp/v1" "github.com/radius-project/radius/pkg/to" - "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/resources" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" diff --git a/pkg/recipes/controllerconfig/config.go b/pkg/recipes/controllerconfig/config.go index a6ca537506..e2f60fc312 100644 --- a/pkg/recipes/controllerconfig/config.go +++ b/pkg/recipes/controllerconfig/config.go @@ -21,6 +21,7 @@ import ( "github.com/radius-project/radius/pkg/armrpc/hostoptions" aztoken "github.com/radius-project/radius/pkg/azure/tokencredentials" + "github.com/radius-project/radius/pkg/components/secret/secretprovider" "github.com/radius-project/radius/pkg/kubeutil" "github.com/radius-project/radius/pkg/portableresources/processors" "github.com/radius-project/radius/pkg/recipes" @@ -29,7 +30,6 @@ import ( "github.com/radius-project/radius/pkg/recipes/engine" "github.com/radius-project/radius/pkg/sdk" "github.com/radius-project/radius/pkg/sdk/clients" - "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" ) // RecipeControllerConfig is the configuration for the controllers which uses recipe. diff --git a/pkg/recipes/driver/terraform.go b/pkg/recipes/driver/terraform.go index 109af10211..72387a4b8f 100644 --- a/pkg/recipes/driver/terraform.go +++ b/pkg/recipes/driver/terraform.go @@ -27,6 +27,7 @@ import ( "github.com/google/uuid" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/components/secret/secretprovider" rpv1 "github.com/radius-project/radius/pkg/rp/v1" "golang.org/x/exp/slices" "k8s.io/client-go/kubernetes" @@ -38,7 +39,6 @@ import ( resources "github.com/radius-project/radius/pkg/ucp/resources" awsresources "github.com/radius-project/radius/pkg/ucp/resources/aws" kubernetesresources "github.com/radius-project/radius/pkg/ucp/resources/kubernetes" - ucp_provider "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" "github.com/radius-project/radius/pkg/ucp/ucplog" "github.com/radius-project/radius/pkg/ucp/util" @@ -48,7 +48,7 @@ import ( var _ Driver = (*terraformDriver)(nil) // NewTerraformDriver creates a new instance of driver to execute a Terraform recipe. -func NewTerraformDriver(ucpConn sdk.Connection, secretProvider *ucp_provider.SecretProvider, options TerraformOptions, k8sClientSet kubernetes.Interface) Driver { +func NewTerraformDriver(ucpConn sdk.Connection, secretProvider *secretprovider.SecretProvider, options TerraformOptions, k8sClientSet kubernetes.Interface) Driver { return &terraformDriver{ terraformExecutor: terraform.NewExecutor(ucpConn, secretProvider, k8sClientSet), options: options, diff --git a/pkg/recipes/terraform/config/providers/aws.go b/pkg/recipes/terraform/config/providers/aws.go index 228ee7263c..c9500b2d44 100644 --- a/pkg/recipes/terraform/config/providers/aws.go +++ b/pkg/recipes/terraform/config/providers/aws.go @@ -25,14 +25,14 @@ import ( ucp_datamodel "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/azure/tokencredentials" + "github.com/radius-project/radius/pkg/components/secret" + "github.com/radius-project/radius/pkg/components/secret/secretprovider" "github.com/radius-project/radius/pkg/corerp/datamodel" "github.com/radius-project/radius/pkg/recipes" "github.com/radius-project/radius/pkg/sdk" "github.com/radius-project/radius/pkg/ucp/credentials" "github.com/radius-project/radius/pkg/ucp/resources" resources_aws "github.com/radius-project/radius/pkg/ucp/resources/aws" - "github.com/radius-project/radius/pkg/ucp/secret" - ucp_provider "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" "github.com/radius-project/radius/pkg/ucp/ucplog" ) @@ -61,11 +61,11 @@ var _ Provider = (*awsProvider)(nil) type awsProvider struct { ucpConn sdk.Connection - secretProvider *ucp_provider.SecretProvider + secretProvider *secretprovider.SecretProvider } // NewAWSProvider creates a new AWSProvider instance. -func NewAWSProvider(ucpConn sdk.Connection, secretProvider *ucp_provider.SecretProvider) Provider { +func NewAWSProvider(ucpConn sdk.Connection, secretProvider *secretprovider.SecretProvider) Provider { return &awsProvider{ucpConn: ucpConn, secretProvider: secretProvider} } diff --git a/pkg/recipes/terraform/config/providers/aws_test.go b/pkg/recipes/terraform/config/providers/aws_test.go index 79ecfd44c6..1d11865a8a 100644 --- a/pkg/recipes/terraform/config/providers/aws_test.go +++ b/pkg/recipes/terraform/config/providers/aws_test.go @@ -25,9 +25,9 @@ import ( "github.com/radius-project/radius/pkg/recipes" "github.com/radius-project/radius/pkg/sdk" + "github.com/radius-project/radius/pkg/components/secret" ucp_credentials "github.com/radius-project/radius/pkg/ucp/credentials" ucp_datamodel "github.com/radius-project/radius/pkg/ucp/datamodel" - "github.com/radius-project/radius/pkg/ucp/secret" "github.com/radius-project/radius/test/testcontext" "github.com/stretchr/testify/require" ) diff --git a/pkg/recipes/terraform/config/providers/azure.go b/pkg/recipes/terraform/config/providers/azure.go index 5ac32cef3d..2a97b8f0e3 100644 --- a/pkg/recipes/terraform/config/providers/azure.go +++ b/pkg/recipes/terraform/config/providers/azure.go @@ -22,6 +22,8 @@ import ( "fmt" "github.com/radius-project/radius/pkg/azure/tokencredentials" + "github.com/radius-project/radius/pkg/components/secret" + "github.com/radius-project/radius/pkg/components/secret/secretprovider" "github.com/radius-project/radius/pkg/corerp/datamodel" "github.com/radius-project/radius/pkg/recipes" "github.com/radius-project/radius/pkg/sdk" @@ -29,8 +31,6 @@ import ( ucp_datamodel "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/resources" resources_azure "github.com/radius-project/radius/pkg/ucp/resources/azure" - "github.com/radius-project/radius/pkg/ucp/secret" - ucp_provider "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" "github.com/radius-project/radius/pkg/ucp/ucplog" ) @@ -59,11 +59,11 @@ var _ Provider = (*azureProvider)(nil) type azureProvider struct { ucpConn sdk.Connection - secretProvider *ucp_provider.SecretProvider + secretProvider *secretprovider.SecretProvider } // NewAzureProvider creates a new AzureProvider instance. -func NewAzureProvider(ucpConn sdk.Connection, secretProvider *ucp_provider.SecretProvider) Provider { +func NewAzureProvider(ucpConn sdk.Connection, secretProvider *secretprovider.SecretProvider) Provider { return &azureProvider{ucpConn: ucpConn, secretProvider: secretProvider} } diff --git a/pkg/recipes/terraform/config/providers/azure_test.go b/pkg/recipes/terraform/config/providers/azure_test.go index f930866a7e..ebb9e01fc6 100644 --- a/pkg/recipes/terraform/config/providers/azure_test.go +++ b/pkg/recipes/terraform/config/providers/azure_test.go @@ -21,11 +21,11 @@ import ( "errors" "testing" + "github.com/radius-project/radius/pkg/components/secret" "github.com/radius-project/radius/pkg/corerp/datamodel" "github.com/radius-project/radius/pkg/recipes" "github.com/radius-project/radius/pkg/sdk" ucp_credentials "github.com/radius-project/radius/pkg/ucp/credentials" - "github.com/radius-project/radius/pkg/ucp/secret" "github.com/radius-project/radius/test/testcontext" "github.com/stretchr/testify/require" ) diff --git a/pkg/recipes/terraform/config/providers/types.go b/pkg/recipes/terraform/config/providers/types.go index 349d93839c..f48950cb17 100644 --- a/pkg/recipes/terraform/config/providers/types.go +++ b/pkg/recipes/terraform/config/providers/types.go @@ -20,10 +20,10 @@ import ( "context" "fmt" + "github.com/radius-project/radius/pkg/components/secret/secretprovider" "github.com/radius-project/radius/pkg/corerp/datamodel" "github.com/radius-project/radius/pkg/recipes" "github.com/radius-project/radius/pkg/sdk" - ucp_provider "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" ) //go:generate mockgen -typed -destination=./mock_provider.go -package=providers -self_package github.com/radius-project/radius/pkg/recipes/terraform/config/providers github.com/radius-project/radius/pkg/recipes/terraform/config/providers Provider @@ -39,7 +39,7 @@ type Provider interface { // GetUCPConfiguredTerraformProviders returns a map of Terraform provider names to provider config builder. // These providers represent Terraform providers for which Radius generates custom provider configurations based on credentials stored with UCP // and providers configured on the Radius environment. For example, the Azure subscription id is added to Azure provider config using Radius Environment's Azure provider scope. -func GetUCPConfiguredTerraformProviders(ucpConn sdk.Connection, secretProvider *ucp_provider.SecretProvider) map[string]Provider { +func GetUCPConfiguredTerraformProviders(ucpConn sdk.Connection, secretProvider *secretprovider.SecretProvider) map[string]Provider { return map[string]Provider{ AWSProviderName: NewAWSProvider(ucpConn, secretProvider), AzureProviderName: NewAzureProvider(ucpConn, secretProvider), diff --git a/pkg/recipes/terraform/execute.go b/pkg/recipes/terraform/execute.go index 9e0c88d7e2..464e9b512d 100644 --- a/pkg/recipes/terraform/execute.go +++ b/pkg/recipes/terraform/execute.go @@ -27,13 +27,13 @@ import ( install "github.com/hashicorp/hc-install" "github.com/hashicorp/terraform-exec/tfexec" tfjson "github.com/hashicorp/terraform-json" + "github.com/radius-project/radius/pkg/components/secret/secretprovider" "github.com/radius-project/radius/pkg/metrics" "github.com/radius-project/radius/pkg/recipes/recipecontext" "github.com/radius-project/radius/pkg/recipes/terraform/config" "github.com/radius-project/radius/pkg/recipes/terraform/config/backends" "github.com/radius-project/radius/pkg/recipes/terraform/config/providers" "github.com/radius-project/radius/pkg/sdk" - ucp_provider "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" "github.com/radius-project/radius/pkg/ucp/ucplog" "go.opentelemetry.io/otel/attribute" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -48,7 +48,7 @@ var ( var _ TerraformExecutor = (*executor)(nil) // NewExecutor creates a new Executor with the given UCP connection and secret provider, to execute a Terraform recipe. -func NewExecutor(ucpConn sdk.Connection, secretProvider *ucp_provider.SecretProvider, k8sClientSet kubernetes.Interface) *executor { +func NewExecutor(ucpConn sdk.Connection, secretProvider *secretprovider.SecretProvider, k8sClientSet kubernetes.Interface) *executor { return &executor{ucpConn: ucpConn, secretProvider: secretProvider, k8sClientSet: k8sClientSet} } @@ -57,7 +57,7 @@ type executor struct { ucpConn sdk.Connection // secretProvider is the secret store provider used for managing credentials in UCP. - secretProvider *ucp_provider.SecretProvider + secretProvider *secretprovider.SecretProvider // k8sClientSet is the Kubernetes client. k8sClientSet kubernetes.Interface diff --git a/pkg/rp/kube/resources.go b/pkg/rp/kube/resources.go index 85c7e94ce7..d88574fbd0 100644 --- a/pkg/rp/kube/resources.go +++ b/pkg/rp/kube/resources.go @@ -22,10 +22,10 @@ import ( "strings" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/corerp/api/v20231001preview" cdm "github.com/radius-project/radius/pkg/corerp/datamodel" rpv1 "github.com/radius-project/radius/pkg/rp/v1" - "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/resources" ) diff --git a/pkg/rp/kube/resources_test.go b/pkg/rp/kube/resources_test.go index 2c69d6d9a1..d2ba20aca9 100644 --- a/pkg/rp/kube/resources_test.go +++ b/pkg/rp/kube/resources_test.go @@ -22,11 +22,11 @@ import ( "testing" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/components/database" model "github.com/radius-project/radius/pkg/corerp/api/v20231001preview" "github.com/radius-project/radius/pkg/corerp/datamodel" rpv1 "github.com/radius-project/radius/pkg/rp/v1" "github.com/radius-project/radius/pkg/to" - "github.com/radius-project/radius/pkg/ucp/database" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" ) diff --git a/pkg/rp/util/datastore.go b/pkg/rp/util/datastore.go index 82ce79afd2..fa1ccbc47d 100644 --- a/pkg/rp/util/datastore.go +++ b/pkg/rp/util/datastore.go @@ -23,7 +23,7 @@ import ( "strings" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" resources "github.com/radius-project/radius/pkg/ucp/resources" ) diff --git a/pkg/ucp/backend/controller/resourcegroups/trackedresourceprocess.go b/pkg/ucp/backend/controller/resourcegroups/trackedresourceprocess.go index 3f7ba1c87b..060e28e663 100644 --- a/pkg/ucp/backend/controller/resourcegroups/trackedresourceprocess.go +++ b/pkg/ucp/backend/controller/resourcegroups/trackedresourceprocess.go @@ -25,7 +25,7 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/frontend/controller/resourcegroups" "github.com/radius-project/radius/pkg/ucp/resources" diff --git a/pkg/ucp/backend/controller/resourcegroups/trackedresourceprocess_test.go b/pkg/ucp/backend/controller/resourcegroups/trackedresourceprocess_test.go index aaee56c4ca..9ee2929d5f 100644 --- a/pkg/ucp/backend/controller/resourcegroups/trackedresourceprocess_test.go +++ b/pkg/ucp/backend/controller/resourcegroups/trackedresourceprocess_test.go @@ -23,7 +23,7 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/resources" "github.com/radius-project/radius/pkg/ucp/trackedresource" diff --git a/pkg/ucp/backend/controller/resourceproviders/resourceprovider_delete.go b/pkg/ucp/backend/controller/resourceproviders/resourceprovider_delete.go index 450aa29261..66a030540d 100644 --- a/pkg/ucp/backend/controller/resourceproviders/resourceprovider_delete.go +++ b/pkg/ucp/backend/controller/resourceproviders/resourceprovider_delete.go @@ -23,9 +23,9 @@ import ( ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" aztoken "github.com/radius-project/radius/pkg/azure/tokencredentials" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/sdk" "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" - "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/resources" resources_radius "github.com/radius-project/radius/pkg/ucp/resources/radius" "github.com/radius-project/radius/pkg/ucp/ucplog" diff --git a/pkg/ucp/backend/controller/resourceproviders/util.go b/pkg/ucp/backend/controller/resourceproviders/util.go index 4e1ea46ccd..25149ef19f 100644 --- a/pkg/ucp/backend/controller/resourceproviders/util.go +++ b/pkg/ucp/backend/controller/resourceproviders/util.go @@ -23,7 +23,7 @@ import ( "strings" ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/resources" "github.com/radius-project/radius/pkg/ucp/ucplog" diff --git a/pkg/ucp/backend/controller/resourceproviders/util_test.go b/pkg/ucp/backend/controller/resourceproviders/util_test.go index 656be012f4..b5c508c0b7 100644 --- a/pkg/ucp/backend/controller/resourceproviders/util_test.go +++ b/pkg/ucp/backend/controller/resourceproviders/util_test.go @@ -21,7 +21,7 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/resources" "github.com/radius-project/radius/pkg/ucp/util/etag" diff --git a/pkg/ucp/credentials/aws.go b/pkg/ucp/credentials/aws.go index 1fa8e52e95..629fb00b0b 100644 --- a/pkg/ucp/credentials/aws.go +++ b/pkg/ucp/credentials/aws.go @@ -22,11 +22,11 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/radius-project/radius/pkg/components/secret" + "github.com/radius-project/radius/pkg/components/secret/secretprovider" "github.com/radius-project/radius/pkg/sdk" "github.com/radius-project/radius/pkg/to" ucpapi "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" - "github.com/radius-project/radius/pkg/ucp/secret" - "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" ) var _ CredentialProvider[AWSCredential] = (*AWSCredentialProvider)(nil) diff --git a/pkg/ucp/credentials/azure.go b/pkg/ucp/credentials/azure.go index 4d9e773564..8358e1fbba 100644 --- a/pkg/ucp/credentials/azure.go +++ b/pkg/ucp/credentials/azure.go @@ -22,11 +22,11 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/radius-project/radius/pkg/components/secret" + "github.com/radius-project/radius/pkg/components/secret/secretprovider" "github.com/radius-project/radius/pkg/sdk" "github.com/radius-project/radius/pkg/to" ucpapi "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" - "github.com/radius-project/radius/pkg/ucp/secret" - "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" ) var _ CredentialProvider[AzureCredential] = (*AzureCredentialProvider)(nil) diff --git a/pkg/ucp/frontend/api/routes_test.go b/pkg/ucp/frontend/api/routes_test.go index 9833f0733e..46ee16b30c 100644 --- a/pkg/ucp/frontend/api/routes_test.go +++ b/pkg/ucp/frontend/api/routes_test.go @@ -25,7 +25,7 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" "github.com/radius-project/radius/pkg/armrpc/rpctest" - "github.com/radius-project/radius/pkg/ucp/databaseprovider" + "github.com/radius-project/radius/pkg/components/database/databaseprovider" "github.com/radius-project/radius/pkg/ucp/frontend/modules" "github.com/radius-project/radius/test/testcontext" "github.com/stretchr/testify/require" diff --git a/pkg/ucp/frontend/api/server.go b/pkg/ucp/frontend/api/server.go index 99e31911ed..bd851d967a 100644 --- a/pkg/ucp/frontend/api/server.go +++ b/pkg/ucp/frontend/api/server.go @@ -30,9 +30,11 @@ import ( armrpc_controller "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/frontend/defaultoperation" "github.com/radius-project/radius/pkg/armrpc/servicecontext" + "github.com/radius-project/radius/pkg/components/database/databaseprovider" + "github.com/radius-project/radius/pkg/components/queue/queueprovider" + "github.com/radius-project/radius/pkg/components/secret/secretprovider" "github.com/radius-project/radius/pkg/middleware" "github.com/radius-project/radius/pkg/sdk" - "github.com/radius-project/radius/pkg/ucp/databaseprovider" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/datamodel/converter" aws_frontend "github.com/radius-project/radius/pkg/ucp/frontend/aws" @@ -42,10 +44,8 @@ import ( "github.com/radius-project/radius/pkg/ucp/frontend/versions" "github.com/radius-project/radius/pkg/ucp/hosting" "github.com/radius-project/radius/pkg/ucp/hostoptions" - "github.com/radius-project/radius/pkg/ucp/queue/queueprovider" "github.com/radius-project/radius/pkg/ucp/resources" "github.com/radius-project/radius/pkg/ucp/rest" - "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" "github.com/radius-project/radius/pkg/ucp/ucplog" "github.com/radius-project/radius/pkg/validator" "github.com/radius-project/radius/swagger" diff --git a/pkg/ucp/frontend/aws/routes_test.go b/pkg/ucp/frontend/aws/routes_test.go index 634b2fd23f..8d65dfa39a 100644 --- a/pkg/ucp/frontend/aws/routes_test.go +++ b/pkg/ucp/frontend/aws/routes_test.go @@ -27,13 +27,13 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" "github.com/radius-project/radius/pkg/armrpc/rpctest" + "github.com/radius-project/radius/pkg/components/database/databaseprovider" + "github.com/radius-project/radius/pkg/components/secret" + "github.com/radius-project/radius/pkg/components/secret/secretprovider" "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" - "github.com/radius-project/radius/pkg/ucp/databaseprovider" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/frontend/modules" "github.com/radius-project/radius/pkg/ucp/hostoptions" - "github.com/radius-project/radius/pkg/ucp/secret" - secretprovider "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" ) const pathBase = "/some-path-base" diff --git a/pkg/ucp/frontend/azure/routes_test.go b/pkg/ucp/frontend/azure/routes_test.go index 57109e5e08..818031e22c 100644 --- a/pkg/ucp/frontend/azure/routes_test.go +++ b/pkg/ucp/frontend/azure/routes_test.go @@ -27,13 +27,13 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" "github.com/radius-project/radius/pkg/armrpc/rpctest" + "github.com/radius-project/radius/pkg/components/database/databaseprovider" + "github.com/radius-project/radius/pkg/components/secret" + "github.com/radius-project/radius/pkg/components/secret/secretprovider" "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" - "github.com/radius-project/radius/pkg/ucp/databaseprovider" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/frontend/modules" "github.com/radius-project/radius/pkg/ucp/hostoptions" - "github.com/radius-project/radius/pkg/ucp/secret" - secretprovider "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" ) const pathBase = "/some-path-base" diff --git a/pkg/ucp/frontend/controller/awsproxy/awsproxytest.go b/pkg/ucp/frontend/controller/awsproxy/awsproxytest.go index c5f05ead31..51761904a4 100644 --- a/pkg/ucp/frontend/controller/awsproxy/awsproxytest.go +++ b/pkg/ucp/frontend/controller/awsproxy/awsproxytest.go @@ -21,7 +21,7 @@ import ( "fmt" "testing" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" "go.uber.org/mock/gomock" awsclient "github.com/radius-project/radius/pkg/ucp/aws" diff --git a/pkg/ucp/frontend/controller/credentials/aws/createorupdateawscredential.go b/pkg/ucp/frontend/controller/credentials/aws/createorupdateawscredential.go index 9fd2ea5433..ecf12bf725 100644 --- a/pkg/ucp/frontend/controller/credentials/aws/createorupdateawscredential.go +++ b/pkg/ucp/frontend/controller/credentials/aws/createorupdateawscredential.go @@ -22,10 +22,10 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" armrpc_controller "github.com/radius-project/radius/pkg/armrpc/frontend/controller" armrpc_rest "github.com/radius-project/radius/pkg/armrpc/rest" + "github.com/radius-project/radius/pkg/components/secret" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/datamodel/converter" "github.com/radius-project/radius/pkg/ucp/frontend/controller/credentials" - "github.com/radius-project/radius/pkg/ucp/secret" ) var _ armrpc_controller.Controller = (*CreateOrUpdateAWSCredential)(nil) diff --git a/pkg/ucp/frontend/controller/credentials/aws/createorupdateawscredential_test.go b/pkg/ucp/frontend/controller/credentials/aws/createorupdateawscredential_test.go index 0e590c45cf..cab95b7ebb 100644 --- a/pkg/ucp/frontend/controller/credentials/aws/createorupdateawscredential_test.go +++ b/pkg/ucp/frontend/controller/credentials/aws/createorupdateawscredential_test.go @@ -25,10 +25,10 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" armrpc_rest "github.com/radius-project/radius/pkg/armrpc/rest" "github.com/radius-project/radius/pkg/armrpc/rpctest" + "github.com/radius-project/radius/pkg/components/database" + "github.com/radius-project/radius/pkg/components/secret" "github.com/radius-project/radius/pkg/to" "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" - "github.com/radius-project/radius/pkg/ucp/database" - "github.com/radius-project/radius/pkg/ucp/secret" "github.com/radius-project/radius/test/testutil" armrpc_controller "github.com/radius-project/radius/pkg/armrpc/frontend/controller" diff --git a/pkg/ucp/frontend/controller/credentials/aws/deleteawscredential.go b/pkg/ucp/frontend/controller/credentials/aws/deleteawscredential.go index eaa9d9c7f7..ea5608c5c8 100644 --- a/pkg/ucp/frontend/controller/credentials/aws/deleteawscredential.go +++ b/pkg/ucp/frontend/controller/credentials/aws/deleteawscredential.go @@ -24,11 +24,11 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" armrpc_controller "github.com/radius-project/radius/pkg/armrpc/frontend/controller" armrpcrest "github.com/radius-project/radius/pkg/armrpc/rest" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" + "github.com/radius-project/radius/pkg/components/secret" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/datamodel/converter" "github.com/radius-project/radius/pkg/ucp/frontend/controller/credentials" - "github.com/radius-project/radius/pkg/ucp/secret" "github.com/radius-project/radius/pkg/ucp/ucplog" ) diff --git a/pkg/ucp/frontend/controller/credentials/aws/deleteawscredential_test.go b/pkg/ucp/frontend/controller/credentials/aws/deleteawscredential_test.go index 223ff65c2a..760c6094b0 100644 --- a/pkg/ucp/frontend/controller/credentials/aws/deleteawscredential_test.go +++ b/pkg/ucp/frontend/controller/credentials/aws/deleteawscredential_test.go @@ -25,9 +25,9 @@ import ( armrpc_controller "github.com/radius-project/radius/pkg/armrpc/frontend/controller" armrpcrest "github.com/radius-project/radius/pkg/armrpc/rest" "github.com/radius-project/radius/pkg/armrpc/rpctest" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" + "github.com/radius-project/radius/pkg/components/secret" "github.com/radius-project/radius/pkg/ucp/datamodel" - "github.com/radius-project/radius/pkg/ucp/secret" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" diff --git a/pkg/ucp/frontend/controller/credentials/azure/createorupdateazurecredential.go b/pkg/ucp/frontend/controller/credentials/azure/createorupdateazurecredential.go index 40dd59fe36..32ebdc4e47 100644 --- a/pkg/ucp/frontend/controller/credentials/azure/createorupdateazurecredential.go +++ b/pkg/ucp/frontend/controller/credentials/azure/createorupdateazurecredential.go @@ -22,10 +22,10 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" armrpc_controller "github.com/radius-project/radius/pkg/armrpc/frontend/controller" armrpc_rest "github.com/radius-project/radius/pkg/armrpc/rest" + "github.com/radius-project/radius/pkg/components/secret" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/datamodel/converter" "github.com/radius-project/radius/pkg/ucp/frontend/controller/credentials" - "github.com/radius-project/radius/pkg/ucp/secret" ) var _ armrpc_controller.Controller = (*CreateOrUpdateAzureCredential)(nil) diff --git a/pkg/ucp/frontend/controller/credentials/azure/createorupdateazurecredential_test.go b/pkg/ucp/frontend/controller/credentials/azure/createorupdateazurecredential_test.go index 1889ede636..4cbeb86292 100644 --- a/pkg/ucp/frontend/controller/credentials/azure/createorupdateazurecredential_test.go +++ b/pkg/ucp/frontend/controller/credentials/azure/createorupdateazurecredential_test.go @@ -25,10 +25,10 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" armrpc_rest "github.com/radius-project/radius/pkg/armrpc/rest" "github.com/radius-project/radius/pkg/armrpc/rpctest" + "github.com/radius-project/radius/pkg/components/database" + "github.com/radius-project/radius/pkg/components/secret" "github.com/radius-project/radius/pkg/to" "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" - "github.com/radius-project/radius/pkg/ucp/database" - "github.com/radius-project/radius/pkg/ucp/secret" "github.com/radius-project/radius/test/testutil" armrpc_controller "github.com/radius-project/radius/pkg/armrpc/frontend/controller" diff --git a/pkg/ucp/frontend/controller/credentials/azure/deleteazurecredential.go b/pkg/ucp/frontend/controller/credentials/azure/deleteazurecredential.go index 09f1b3025f..89c49fad9a 100644 --- a/pkg/ucp/frontend/controller/credentials/azure/deleteazurecredential.go +++ b/pkg/ucp/frontend/controller/credentials/azure/deleteazurecredential.go @@ -24,11 +24,11 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" armrpc_controller "github.com/radius-project/radius/pkg/armrpc/frontend/controller" armrpc_rest "github.com/radius-project/radius/pkg/armrpc/rest" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" + "github.com/radius-project/radius/pkg/components/secret" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/datamodel/converter" "github.com/radius-project/radius/pkg/ucp/frontend/controller/credentials" - "github.com/radius-project/radius/pkg/ucp/secret" "github.com/radius-project/radius/pkg/ucp/ucplog" ) diff --git a/pkg/ucp/frontend/controller/credentials/azure/deleteazurecredential_test.go b/pkg/ucp/frontend/controller/credentials/azure/deleteazurecredential_test.go index 046c54d985..9bcc09634d 100644 --- a/pkg/ucp/frontend/controller/credentials/azure/deleteazurecredential_test.go +++ b/pkg/ucp/frontend/controller/credentials/azure/deleteazurecredential_test.go @@ -25,9 +25,9 @@ import ( armrpc_controller "github.com/radius-project/radius/pkg/armrpc/frontend/controller" armrpc_rest "github.com/radius-project/radius/pkg/armrpc/rest" "github.com/radius-project/radius/pkg/armrpc/rpctest" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" + "github.com/radius-project/radius/pkg/components/secret" "github.com/radius-project/radius/pkg/ucp/datamodel" - "github.com/radius-project/radius/pkg/ucp/secret" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" ) diff --git a/pkg/ucp/frontend/controller/planes/listplanes.go b/pkg/ucp/frontend/controller/planes/listplanes.go index a90a1c9e5f..b09dcd7590 100644 --- a/pkg/ucp/frontend/controller/planes/listplanes.go +++ b/pkg/ucp/frontend/controller/planes/listplanes.go @@ -23,7 +23,7 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" armrpc_controller "github.com/radius-project/radius/pkg/armrpc/frontend/controller" armrpc_rest "github.com/radius-project/radius/pkg/armrpc/rest" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/datamodel/converter" "github.com/radius-project/radius/pkg/ucp/ucplog" diff --git a/pkg/ucp/frontend/controller/planes/listplanes_test.go b/pkg/ucp/frontend/controller/planes/listplanes_test.go index aac6c6d086..a6590359e4 100644 --- a/pkg/ucp/frontend/controller/planes/listplanes_test.go +++ b/pkg/ucp/frontend/controller/planes/listplanes_test.go @@ -23,9 +23,9 @@ import ( armrpc_controller "github.com/radius-project/radius/pkg/armrpc/frontend/controller" armrpc_rest "github.com/radius-project/radius/pkg/armrpc/rest" "github.com/radius-project/radius/pkg/armrpc/rpctest" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/to" "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" - "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" diff --git a/pkg/ucp/frontend/controller/planes/listplanesbytype.go b/pkg/ucp/frontend/controller/planes/listplanesbytype.go index c5dda15396..ff4c764a20 100644 --- a/pkg/ucp/frontend/controller/planes/listplanesbytype.go +++ b/pkg/ucp/frontend/controller/planes/listplanesbytype.go @@ -25,8 +25,8 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" armrpc_controller "github.com/radius-project/radius/pkg/armrpc/frontend/controller" armrpc_rest "github.com/radius-project/radius/pkg/armrpc/rest" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/middleware" - "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/resources" "github.com/radius-project/radius/pkg/ucp/ucplog" diff --git a/pkg/ucp/frontend/controller/planes/listplanesbytype_test.go b/pkg/ucp/frontend/controller/planes/listplanesbytype_test.go index e426a6255b..0b4a5c3acd 100644 --- a/pkg/ucp/frontend/controller/planes/listplanesbytype_test.go +++ b/pkg/ucp/frontend/controller/planes/listplanesbytype_test.go @@ -23,9 +23,9 @@ import ( armrpc_controller "github.com/radius-project/radius/pkg/armrpc/frontend/controller" armrpc_rest "github.com/radius-project/radius/pkg/armrpc/rest" "github.com/radius-project/radius/pkg/armrpc/rpctest" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/to" "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" - "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/datamodel/converter" "github.com/stretchr/testify/require" diff --git a/pkg/ucp/frontend/controller/radius/proxy.go b/pkg/ucp/frontend/controller/radius/proxy.go index 25fff5ea23..43c157a21b 100644 --- a/pkg/ucp/frontend/controller/radius/proxy.go +++ b/pkg/ucp/frontend/controller/radius/proxy.go @@ -30,8 +30,8 @@ import ( "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" armrpc_controller "github.com/radius-project/radius/pkg/armrpc/frontend/controller" armrpc_rest "github.com/radius-project/radius/pkg/armrpc/rest" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/middleware" - "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/frontend/controller/resourcegroups" "github.com/radius-project/radius/pkg/ucp/proxy" diff --git a/pkg/ucp/frontend/controller/radius/proxy_test.go b/pkg/ucp/frontend/controller/radius/proxy_test.go index aeb5d4d4d5..063105e599 100644 --- a/pkg/ucp/frontend/controller/radius/proxy_test.go +++ b/pkg/ucp/frontend/controller/radius/proxy_test.go @@ -28,7 +28,7 @@ import ( "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/rest" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/resources" "github.com/radius-project/radius/pkg/ucp/trackedresource" diff --git a/pkg/ucp/frontend/controller/resourcegroups/listresourcegroups.go b/pkg/ucp/frontend/controller/resourcegroups/listresourcegroups.go index 58cf0dd321..0e9efd33ba 100644 --- a/pkg/ucp/frontend/controller/resourcegroups/listresourcegroups.go +++ b/pkg/ucp/frontend/controller/resourcegroups/listresourcegroups.go @@ -23,7 +23,7 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" armrpc_controller "github.com/radius-project/radius/pkg/armrpc/frontend/controller" armrpc_rest "github.com/radius-project/radius/pkg/armrpc/rest" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/datamodel/converter" "github.com/radius-project/radius/pkg/ucp/resources" diff --git a/pkg/ucp/frontend/controller/resourcegroups/listresourcegroups_test.go b/pkg/ucp/frontend/controller/resourcegroups/listresourcegroups_test.go index ce7b87ce8b..716317ba8e 100644 --- a/pkg/ucp/frontend/controller/resourcegroups/listresourcegroups_test.go +++ b/pkg/ucp/frontend/controller/resourcegroups/listresourcegroups_test.go @@ -27,9 +27,9 @@ import ( armrpc_controller "github.com/radius-project/radius/pkg/armrpc/frontend/controller" armrpc_rest "github.com/radius-project/radius/pkg/armrpc/rest" "github.com/radius-project/radius/pkg/armrpc/rpctest" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/to" "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" - "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/datamodel" ) diff --git a/pkg/ucp/frontend/controller/resourcegroups/listresources.go b/pkg/ucp/frontend/controller/resourcegroups/listresources.go index 66a3cb6b66..be04b63073 100644 --- a/pkg/ucp/frontend/controller/resourcegroups/listresources.go +++ b/pkg/ucp/frontend/controller/resourcegroups/listresources.go @@ -23,9 +23,9 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" armrpc_controller "github.com/radius-project/radius/pkg/armrpc/frontend/controller" armrpc_rest "github.com/radius-project/radius/pkg/armrpc/rest" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/middleware" "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" - "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/datamodel/converter" "github.com/radius-project/radius/pkg/ucp/resources" diff --git a/pkg/ucp/frontend/controller/resourcegroups/listresources_test.go b/pkg/ucp/frontend/controller/resourcegroups/listresources_test.go index 35e58eb8d8..9b2a5178aa 100644 --- a/pkg/ucp/frontend/controller/resourcegroups/listresources_test.go +++ b/pkg/ucp/frontend/controller/resourcegroups/listresources_test.go @@ -27,9 +27,9 @@ import ( armrpc_controller "github.com/radius-project/radius/pkg/armrpc/frontend/controller" armrpc_rest "github.com/radius-project/radius/pkg/armrpc/rest" "github.com/radius-project/radius/pkg/armrpc/rpctest" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/to" "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" - "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/resources" ) diff --git a/pkg/ucp/frontend/controller/resourcegroups/util.go b/pkg/ucp/frontend/controller/resourcegroups/util.go index 4edbef9f96..a1e012208f 100644 --- a/pkg/ucp/frontend/controller/resourcegroups/util.go +++ b/pkg/ucp/frontend/controller/resourcegroups/util.go @@ -23,7 +23,7 @@ import ( "net/url" "strings" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/resources" resources_radius "github.com/radius-project/radius/pkg/ucp/resources/radius" diff --git a/pkg/ucp/frontend/controller/resourcegroups/util_test.go b/pkg/ucp/frontend/controller/resourcegroups/util_test.go index 7def6c8b60..3512294633 100644 --- a/pkg/ucp/frontend/controller/resourcegroups/util_test.go +++ b/pkg/ucp/frontend/controller/resourcegroups/util_test.go @@ -22,8 +22,8 @@ import ( "testing" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/to" - "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/resources" "github.com/radius-project/radius/test/testcontext" diff --git a/pkg/ucp/frontend/controller/resourceproviders/getresourceprovidersummary.go b/pkg/ucp/frontend/controller/resourceproviders/getresourceprovidersummary.go index a1b5b638a4..21ec860164 100644 --- a/pkg/ucp/frontend/controller/resourceproviders/getresourceprovidersummary.go +++ b/pkg/ucp/frontend/controller/resourceproviders/getresourceprovidersummary.go @@ -25,8 +25,8 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" armrpc_controller "github.com/radius-project/radius/pkg/armrpc/frontend/controller" armrpc_rest "github.com/radius-project/radius/pkg/armrpc/rest" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/middleware" - "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/datamodel/converter" "github.com/radius-project/radius/pkg/ucp/resources" diff --git a/pkg/ucp/frontend/controller/resourceproviders/listresourceprovidersummaries.go b/pkg/ucp/frontend/controller/resourceproviders/listresourceprovidersummaries.go index f50570b755..0be6d7a366 100644 --- a/pkg/ucp/frontend/controller/resourceproviders/listresourceprovidersummaries.go +++ b/pkg/ucp/frontend/controller/resourceproviders/listresourceprovidersummaries.go @@ -24,8 +24,8 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" armrpc_controller "github.com/radius-project/radius/pkg/armrpc/frontend/controller" armrpc_rest "github.com/radius-project/radius/pkg/armrpc/rest" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/middleware" - "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/datamodel/converter" "github.com/radius-project/radius/pkg/ucp/resources" diff --git a/pkg/ucp/frontend/modules/types.go b/pkg/ucp/frontend/modules/types.go index 2c959df585..3e1638302f 100644 --- a/pkg/ucp/frontend/modules/types.go +++ b/pkg/ucp/frontend/modules/types.go @@ -21,11 +21,11 @@ import ( "net/http" "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" + "github.com/radius-project/radius/pkg/components/database/databaseprovider" + "github.com/radius-project/radius/pkg/components/queue/queueprovider" + "github.com/radius-project/radius/pkg/components/secret/secretprovider" "github.com/radius-project/radius/pkg/sdk" - "github.com/radius-project/radius/pkg/ucp/databaseprovider" "github.com/radius-project/radius/pkg/ucp/hostoptions" - "github.com/radius-project/radius/pkg/ucp/queue/queueprovider" - "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" "github.com/radius-project/radius/pkg/validator" ) diff --git a/pkg/ucp/frontend/radius/routes_test.go b/pkg/ucp/frontend/radius/routes_test.go index ac6f6e8be5..8bca6bf56f 100644 --- a/pkg/ucp/frontend/radius/routes_test.go +++ b/pkg/ucp/frontend/radius/routes_test.go @@ -24,13 +24,13 @@ import ( "github.com/go-chi/chi/v5" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" "github.com/radius-project/radius/pkg/armrpc/rpctest" + "github.com/radius-project/radius/pkg/components/database/databaseprovider" + "github.com/radius-project/radius/pkg/components/secret" + "github.com/radius-project/radius/pkg/components/secret/secretprovider" "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" - "github.com/radius-project/radius/pkg/ucp/databaseprovider" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/frontend/modules" "github.com/radius-project/radius/pkg/ucp/hostoptions" - "github.com/radius-project/radius/pkg/ucp/secret" - secretprovider "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" "go.uber.org/mock/gomock" ) diff --git a/pkg/ucp/hostoptions/providerconfig.go b/pkg/ucp/hostoptions/providerconfig.go index 3f615a0c98..30729336f0 100644 --- a/pkg/ucp/hostoptions/providerconfig.go +++ b/pkg/ucp/hostoptions/providerconfig.go @@ -17,14 +17,14 @@ limitations under the License. package hostoptions import ( + "github.com/radius-project/radius/pkg/components/database/databaseprovider" + "github.com/radius-project/radius/pkg/components/queue/queueprovider" + "github.com/radius-project/radius/pkg/components/secret/secretprovider" metricsprovider "github.com/radius-project/radius/pkg/metrics/provider" profilerprovider "github.com/radius-project/radius/pkg/profiler/provider" "github.com/radius-project/radius/pkg/trace" "github.com/radius-project/radius/pkg/ucp/config" - "github.com/radius-project/radius/pkg/ucp/databaseprovider" - "github.com/radius-project/radius/pkg/ucp/queue/queueprovider" "github.com/radius-project/radius/pkg/ucp/rest" - "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" "github.com/radius-project/radius/pkg/ucp/ucplog" ) diff --git a/pkg/ucp/integrationtests/aws/awstest.go b/pkg/ucp/integrationtests/aws/awstest.go index 6be034b1c5..bca5b13119 100644 --- a/pkg/ucp/integrationtests/aws/awstest.go +++ b/pkg/ucp/integrationtests/aws/awstest.go @@ -21,12 +21,12 @@ package aws import ( "testing" + "github.com/radius-project/radius/pkg/components/database" + "github.com/radius-project/radius/pkg/components/secret" ucp_aws "github.com/radius-project/radius/pkg/ucp/aws" - "github.com/radius-project/radius/pkg/ucp/database" ucp_aws_frontend "github.com/radius-project/radius/pkg/ucp/frontend/aws" "github.com/radius-project/radius/pkg/ucp/frontend/modules" "github.com/radius-project/radius/pkg/ucp/integrationtests/testserver" - "github.com/radius-project/radius/pkg/ucp/secret" "go.uber.org/mock/gomock" ) diff --git a/pkg/ucp/integrationtests/testrp/async.go b/pkg/ucp/integrationtests/testrp/async.go index 1af3838fb5..1fbcac1098 100644 --- a/pkg/ucp/integrationtests/testrp/async.go +++ b/pkg/ucp/integrationtests/testrp/async.go @@ -31,9 +31,9 @@ import ( "github.com/radius-project/radius/pkg/armrpc/frontend/defaultoperation" "github.com/radius-project/radius/pkg/armrpc/frontend/server" "github.com/radius-project/radius/pkg/armrpc/servicecontext" + "github.com/radius-project/radius/pkg/components/queue/queueprovider" "github.com/radius-project/radius/pkg/middleware" "github.com/radius-project/radius/pkg/ucp/integrationtests/testserver" - "github.com/radius-project/radius/pkg/ucp/queue/queueprovider" "github.com/radius-project/radius/test/testcontext" "github.com/stretchr/testify/require" ) diff --git a/pkg/ucp/integrationtests/testrp/sync.go b/pkg/ucp/integrationtests/testrp/sync.go index 94b4d9af7b..8adcf7e1cd 100644 --- a/pkg/ucp/integrationtests/testrp/sync.go +++ b/pkg/ucp/integrationtests/testrp/sync.go @@ -28,9 +28,9 @@ import ( "github.com/radius-project/radius/pkg/armrpc/frontend/defaultoperation" "github.com/radius-project/radius/pkg/armrpc/frontend/server" "github.com/radius-project/radius/pkg/armrpc/servicecontext" + "github.com/radius-project/radius/pkg/components/queue/queueprovider" "github.com/radius-project/radius/pkg/middleware" "github.com/radius-project/radius/pkg/ucp/integrationtests/testserver" - "github.com/radius-project/radius/pkg/ucp/queue/queueprovider" "github.com/radius-project/radius/test/testcontext" "github.com/stretchr/testify/require" ) diff --git a/pkg/ucp/integrationtests/testserver/testserver.go b/pkg/ucp/integrationtests/testserver/testserver.go index 8643c3f839..9c59873171 100644 --- a/pkg/ucp/integrationtests/testserver/testserver.go +++ b/pkg/ucp/integrationtests/testserver/testserver.go @@ -44,20 +44,20 @@ import ( "github.com/radius-project/radius/pkg/armrpc/asyncoperation/worker" "github.com/radius-project/radius/pkg/armrpc/rpctest" "github.com/radius-project/radius/pkg/armrpc/servicecontext" + "github.com/radius-project/radius/pkg/components/database" + "github.com/radius-project/radius/pkg/components/database/databaseprovider" + "github.com/radius-project/radius/pkg/components/queue" + "github.com/radius-project/radius/pkg/components/queue/queueprovider" + "github.com/radius-project/radius/pkg/components/secret" + "github.com/radius-project/radius/pkg/components/secret/secretprovider" "github.com/radius-project/radius/pkg/middleware" "github.com/radius-project/radius/pkg/sdk" "github.com/radius-project/radius/pkg/ucp/backend" "github.com/radius-project/radius/pkg/ucp/data" - "github.com/radius-project/radius/pkg/ucp/database" - "github.com/radius-project/radius/pkg/ucp/databaseprovider" "github.com/radius-project/radius/pkg/ucp/frontend/api" "github.com/radius-project/radius/pkg/ucp/frontend/modules" "github.com/radius-project/radius/pkg/ucp/hosting" "github.com/radius-project/radius/pkg/ucp/hostoptions" - queue "github.com/radius-project/radius/pkg/ucp/queue/client" - "github.com/radius-project/radius/pkg/ucp/queue/queueprovider" - "github.com/radius-project/radius/pkg/ucp/secret" - "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" "github.com/radius-project/radius/pkg/ucp/server" "github.com/radius-project/radius/pkg/validator" "github.com/radius-project/radius/swagger" diff --git a/pkg/ucp/server/server.go b/pkg/ucp/server/server.go index 5759bc2959..375497a1cb 100644 --- a/pkg/ucp/server/server.go +++ b/pkg/ucp/server/server.go @@ -24,6 +24,9 @@ import ( "time" hostopts "github.com/radius-project/radius/pkg/armrpc/hostoptions" + "github.com/radius-project/radius/pkg/components/database/databaseprovider" + "github.com/radius-project/radius/pkg/components/queue/queueprovider" + "github.com/radius-project/radius/pkg/components/secret/secretprovider" "github.com/radius-project/radius/pkg/kubeutil" metricsprovider "github.com/radius-project/radius/pkg/metrics/provider" metricsservice "github.com/radius-project/radius/pkg/metrics/service" @@ -34,13 +37,10 @@ import ( "github.com/radius-project/radius/pkg/ucp/backend" "github.com/radius-project/radius/pkg/ucp/config" "github.com/radius-project/radius/pkg/ucp/data" - "github.com/radius-project/radius/pkg/ucp/databaseprovider" "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/queue/queueprovider" "github.com/radius-project/radius/pkg/ucp/rest" - "github.com/radius-project/radius/pkg/ucp/secret/secretprovider" "github.com/radius-project/radius/pkg/ucp/ucplog" kube_rest "k8s.io/client-go/rest" diff --git a/pkg/ucp/trackedresource/update.go b/pkg/ucp/trackedresource/update.go index 813d4273ed..cfa567888d 100644 --- a/pkg/ucp/trackedresource/update.go +++ b/pkg/ucp/trackedresource/update.go @@ -29,7 +29,7 @@ import ( "github.com/go-logr/logr" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/resources" "github.com/radius-project/radius/pkg/ucp/ucplog" diff --git a/pkg/ucp/trackedresource/update_test.go b/pkg/ucp/trackedresource/update_test.go index 2a2c475cc9..5e3ebe9213 100644 --- a/pkg/ucp/trackedresource/update_test.go +++ b/pkg/ucp/trackedresource/update_test.go @@ -27,8 +27,8 @@ import ( "time" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/to" - "github.com/radius-project/radius/pkg/ucp/database" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/test/testcontext" "github.com/stretchr/testify/require" diff --git a/test/ucp/kubeenv/testenv.go b/test/ucp/kubeenv/testenv.go index 2c78248704..04ad83c807 100644 --- a/test/ucp/kubeenv/testenv.go +++ b/test/ucp/kubeenv/testenv.go @@ -23,7 +23,7 @@ import ( "os" "os/exec" - ucpv1alpha1 "github.com/radius-project/radius/pkg/ucp/database/apiserverstore/api/ucp.dev/v1alpha1" + ucpv1alpha1 "github.com/radius-project/radius/pkg/components/database/apiserverstore/api/ucp.dev/v1alpha1" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" diff --git a/test/ucp/queuetest/shared.go b/test/ucp/queuetest/shared.go index dbe640e06f..2bd0214871 100644 --- a/test/ucp/queuetest/shared.go +++ b/test/ucp/queuetest/shared.go @@ -24,7 +24,7 @@ import ( "testing" "time" - "github.com/radius-project/radius/pkg/ucp/queue/client" + "github.com/radius-project/radius/pkg/components/queue" "github.com/radius-project/radius/test/testcontext" "github.com/stretchr/testify/require" ) @@ -42,12 +42,12 @@ type testQueueMessage struct { Message string `json:"msg"` } -func queueTestMessage(cli client.Client, num int) error { +func queueTestMessage(cli queue.Client, num int) error { // Enqueue multiple message and dequeue them for i := 0; i < num; i++ { msg := &testQueueMessage{ID: fmt.Sprintf("%d", i), Message: fmt.Sprintf("hello world %d", i)} - err := cli.Enqueue(context.Background(), client.NewMessage(msg)) + err := cli.Enqueue(context.Background(), queue.NewMessage(msg)) if err != nil { return err } @@ -60,21 +60,21 @@ func queueTestMessage(cli client.Client, num int) error { // and checking for errors when nil messages are passed. It also tests the StartDequeuer method by dequeuing messages via a // // channel. -func RunTest(t *testing.T, cli client.Client, clear func(t *testing.T)) { +func RunTest(t *testing.T, cli queue.Client, clear func(t *testing.T)) { ctx, cancel := testcontext.NewWithCancel(t) t.Cleanup(cancel) t.Run("nil message", func(t *testing.T) { - err := cli.Enqueue(ctx, &client.Message{Data: []byte("")}) - require.ErrorIs(t, err, client.ErrEmptyMessage) - err = cli.Enqueue(ctx, &client.Message{Data: nil}) - require.ErrorIs(t, err, client.ErrEmptyMessage) + err := cli.Enqueue(ctx, &queue.Message{Data: []byte("")}) + require.ErrorIs(t, err, queue.ErrEmptyMessage) + err = cli.Enqueue(ctx, &queue.Message{Data: nil}) + require.ErrorIs(t, err, queue.ErrEmptyMessage) err = cli.Enqueue(ctx, nil) - require.ErrorIs(t, err, client.ErrEmptyMessage) + require.ErrorIs(t, err, queue.ErrEmptyMessage) err = cli.FinishMessage(ctx, nil) - require.ErrorIs(t, err, client.ErrEmptyMessage) + require.ErrorIs(t, err, queue.ErrEmptyMessage) err = cli.ExtendMessage(ctx, nil) - require.ErrorIs(t, err, client.ErrEmptyMessage) + require.ErrorIs(t, err, queue.ErrEmptyMessage) }) t.Run("enqueue and dequeue messages", func(t *testing.T) { @@ -85,9 +85,9 @@ func RunTest(t *testing.T, cli client.Client, clear func(t *testing.T)) { err := queueTestMessage(cli, num) require.NoError(t, err) - checked := map[string]*client.Message{} + checked := map[string]*queue.Message{} for i := 0; i < num; i++ { - msg, err := cli.Dequeue(ctx, client.QueueClientConfig{}) + msg, err := cli.Dequeue(ctx, queue.QueueClientConfig{}) require.NoError(t, err) result := &testQueueMessage{} err = json.Unmarshal(msg.Data, result) @@ -110,24 +110,24 @@ func RunTest(t *testing.T, cli client.Client, clear func(t *testing.T)) { err := queueTestMessage(cli, 2) require.NoError(t, err) - msg1, err := cli.Dequeue(ctx, client.QueueClientConfig{}) + msg1, err := cli.Dequeue(ctx, queue.QueueClientConfig{}) require.NoError(t, err) require.NotNil(t, msg1) time.Sleep(10 * time.Millisecond) - msg2, err := cli.Dequeue(ctx, client.QueueClientConfig{}) + msg2, err := cli.Dequeue(ctx, queue.QueueClientConfig{}) require.NoError(t, err) require.NotNil(t, msg2) // Ensure that queue doesn't have any valid messages - _, err = cli.Dequeue(ctx, client.QueueClientConfig{}) - require.ErrorIs(t, err, client.ErrMessageNotFound) + _, err = cli.Dequeue(ctx, queue.QueueClientConfig{}) + require.ErrorIs(t, err, queue.ErrMessageNotFound) // Dequeue until message is requeued. - var msg3 *client.Message + var msg3 *queue.Message for { - msg3, err = cli.Dequeue(ctx, client.QueueClientConfig{}) + msg3, err = cli.Dequeue(ctx, queue.QueueClientConfig{}) if err == nil { break } @@ -143,17 +143,17 @@ func RunTest(t *testing.T, cli client.Client, clear func(t *testing.T)) { err := queueTestMessage(cli, 2) require.NoError(t, err) - msg1, err := cli.Dequeue(ctx, client.QueueClientConfig{}) + msg1, err := cli.Dequeue(ctx, queue.QueueClientConfig{}) require.NoError(t, err) t.Logf("%s %v", msg1.ID, msg1.NextVisibleAt) - msg2, err := cli.Dequeue(ctx, client.QueueClientConfig{}) + msg2, err := cli.Dequeue(ctx, queue.QueueClientConfig{}) require.NoError(t, err) t.Logf("%s %v", msg2.ID, msg2.NextVisibleAt) // Ensure that queue doesn't have any valid messages - _, err = cli.Dequeue(ctx, client.QueueClientConfig{}) - require.ErrorIs(t, err, client.ErrMessageNotFound) + _, err = cli.Dequeue(ctx, queue.QueueClientConfig{}) + require.ErrorIs(t, err, queue.ErrMessageNotFound) // Extend msg1 after sometime time.Sleep(TestMessageLockTime / 2) err = cli.ExtendMessage(ctx, msg1) @@ -163,7 +163,7 @@ func RunTest(t *testing.T, cli client.Client, clear func(t *testing.T)) { for { // msg2 is requeued. msg3 must be msg2 - msg3, err := cli.Dequeue(ctx, client.QueueClientConfig{}) + msg3, err := cli.Dequeue(ctx, queue.QueueClientConfig{}) if err == nil { t.Logf("%s %v", msg3.ID, msg3.NextVisibleAt) require.Equal(t, msg2.ID, msg3.ID) @@ -179,18 +179,18 @@ func RunTest(t *testing.T, cli client.Client, clear func(t *testing.T)) { err := queueTestMessage(cli, 2) require.NoError(t, err) - msg1, err := cli.Dequeue(ctx, client.QueueClientConfig{}) + msg1, err := cli.Dequeue(ctx, queue.QueueClientConfig{}) require.NoError(t, err) t.Logf("%s %v", msg1.ID, msg1.NextVisibleAt) time.Sleep(TestMessageLockTime / 2) - msg2, err := cli.Dequeue(ctx, client.QueueClientConfig{}) + msg2, err := cli.Dequeue(ctx, queue.QueueClientConfig{}) require.NoError(t, err) t.Logf("%s %v", msg2.ID, msg2.NextVisibleAt) for { - msg3, err := cli.Dequeue(ctx, client.QueueClientConfig{}) + msg3, err := cli.Dequeue(ctx, queue.QueueClientConfig{}) if err == nil { t.Logf("%s %v", msg3.ID, msg3.NextVisibleAt) break @@ -201,12 +201,12 @@ func RunTest(t *testing.T, cli client.Client, clear func(t *testing.T)) { // Wait until message lock is released. time.Sleep(TestMessageLockTime * 2) err = cli.ExtendMessage(ctx, msg2) - require.ErrorIs(t, err, client.ErrInvalidMessage) + require.ErrorIs(t, err, queue.ErrInvalidMessage) }) t.Run("StartDequeuer dequeues message via channel", func(t *testing.T) { clear(t) - msgCh, err := client.StartDequeuer(ctx, cli, client.WithDequeueInterval(defaultTestDequeueInterval)) + msgCh, err := queue.StartDequeuer(ctx, cli, queue.WithDequeueInterval(defaultTestDequeueInterval)) require.NoError(t, err) recvCnt := 0 @@ -215,7 +215,7 @@ func RunTest(t *testing.T, cli client.Client, clear func(t *testing.T)) { msgCount := 10 // Consumer - go func(msgCh <-chan *client.Message) { + go func(msgCh <-chan *queue.Message) { for msg := range msgCh { require.Equal(t, 1, msg.DequeueCount) t.Logf("Dequeued Message ID: %s", msg.ID) @@ -230,7 +230,7 @@ func RunTest(t *testing.T, cli client.Client, clear func(t *testing.T)) { // Producer for i := 0; i < msgCount; i++ { msg := &testQueueMessage{ID: fmt.Sprintf("%d", i), Message: fmt.Sprintf("hello world %d", i)} - err = cli.Enqueue(ctx, client.NewMessage(msg)) + err = cli.Enqueue(ctx, queue.NewMessage(msg)) require.NoError(t, err) } diff --git a/test/ucp/storetest/shared.go b/test/ucp/storetest/shared.go index 0ab6e761bb..8b7f11be35 100644 --- a/test/ucp/storetest/shared.go +++ b/test/ucp/storetest/shared.go @@ -21,7 +21,7 @@ import ( "encoding/json" "testing" - "github.com/radius-project/radius/pkg/ucp/database" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/ucp/resources" "github.com/radius-project/radius/pkg/ucp/util/etag" "github.com/radius-project/radius/test/testcontext" From 8632b607655342e951d9853a7b1d64784a8bb594 Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Thu, 19 Dec 2024 09:49:10 -0800 Subject: [PATCH 12/37] Refactor and implement shared integration test host (#8112) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description This change updates implements a shared (reusable) integration test host for the Radius control-plane services. The new integration test host enables us to do in-memory testing of UCP and dynamic-rp using a "full stack" approach. This change is a significant refactor because the "glue" code in UCP had many points of divergence with the rest of our codebase. The following major changes are the bulk of the work: - Defining new types for configuration + options in UCP - Updating the UCP configuration file to match the format of other components ## Type of change - This pull request is a minor refactor, code cleanup, test improvement, or other maintenance task and doesn't change the functionality of Radius (issue link optional). Note: this is a pretty significant refactor, not a minor one 😆 Part of: #6688 ## Contributor checklist Please verify that the PR meets the following requirements, where applicable: - [ ] An overview of proposed schema changes is included in a linked GitHub issue. - [ ] A design document PR is created in the [design-notes repository](https://github.com/radius-project/design-notes/), if new APIs are being introduced. - [ ] If applicable, design document has been reviewed and approved by Radius maintainers/approvers. - [ ] A PR for the [samples repository](https://github.com/radius-project/samples) is created, if existing samples are affected by the changes in this PR. - [ ] A PR for the [documentation repository](https://github.com/radius-project/docs) is created, if the changes in this PR affect the documentation or any user facing updates are made. - [ ] A PR for the [recipes repository](https://github.com/radius-project/recipes) is created, if existing recipes are affected by the changes in this PR. Signed-off-by: Ryan Nowak --- .vscode/launch.json | 7 +- cmd/applications-rp/cmd/root.go | 5 + cmd/applications-rp/radius-dev.yaml | 2 +- cmd/applications-rp/radius-self-hosted.yaml | 4 +- cmd/dynamic-rp/dynamicrp-dev.yaml | 2 +- cmd/ucpd/cmd/root.go | 29 +- cmd/ucpd/ucp-dev.yaml | 39 +- .../templates/controller/configmaps.yaml | 2 +- .../templates/dynamic-rp/configmaps.yaml | 2 +- deploy/Chart/templates/rp/configmaps.yaml | 2 +- deploy/Chart/templates/ucp/configmaps.yaml | 38 +- deploy/Chart/templates/ucp/deployment.yaml | 2 - .../configSettings.md | 16 +- pkg/armrpc/asyncoperation/worker/service.go | 60 +- pkg/armrpc/hostoptions/providerconfig.go | 17 +- pkg/components/testhost/clients.go | 274 ++++++++ pkg/components/testhost/doc.go | 23 + pkg/components/testhost/host.go | 148 +++++ pkg/dynamicrp/backend/service.go | 49 +- pkg/dynamicrp/config.go | 4 +- .../integrationtest/dynamic/providers_test.go | 174 +++++ pkg/dynamicrp/options.go | 60 +- pkg/dynamicrp/testhost/doc.go | 18 + pkg/dynamicrp/testhost/host.go | 138 ++++ pkg/recipes/controllerconfig/config.go | 8 + pkg/server/asyncworker.go | 76 ++- pkg/ucp/backend/service.go | 58 +- pkg/ucp/config.go | 153 +++++ pkg/ucp/config/ucpoptions.go | 5 +- pkg/ucp/datamodel/resourcegroup.go | 7 +- pkg/ucp/doc.go | 19 + pkg/ucp/frontend/api/routes.go | 20 +- pkg/ucp/frontend/api/routes_test.go | 27 +- pkg/ucp/frontend/api/server.go | 107 +--- pkg/ucp/frontend/aws/module.go | 5 +- pkg/ucp/frontend/aws/routes.go | 23 +- pkg/ucp/frontend/aws/routes_test.go | 16 +- pkg/ucp/frontend/azure/module.go | 14 +- pkg/ucp/frontend/azure/routes.go | 17 +- pkg/ucp/frontend/azure/routes_test.go | 16 +- .../controller/resourcegroups/util.go | 10 +- .../controller/resourcegroups/util_test.go | 216 +++---- pkg/ucp/frontend/modules/types.go | 41 -- pkg/ucp/frontend/radius/module.go | 5 +- pkg/ucp/frontend/radius/routes.go | 9 +- pkg/ucp/frontend/radius/routes_test.go | 16 +- pkg/ucp/hostoptions/hostoptions.go | 64 -- pkg/ucp/hostoptions/providerconfig.go | 65 -- pkg/ucp/integrationtests/aws/awstest.go | 15 +- .../aws/createresource_test.go | 4 +- .../aws/createresourcewithpost_test.go | 4 +- .../aws/deleteresource_test.go | 4 +- .../aws/deleteresourcewithpost_test.go | 4 +- .../integrationtests/aws/getresource_test.go | 4 +- .../aws/getresourcewithpost_test.go | 4 +- .../aws/listresources_test.go | 4 +- .../aws/operationresults_test.go | 4 +- .../aws/operationstatuses_test.go | 4 +- .../aws/updateresource_test.go | 4 +- .../aws/updateresourcewithpost_test.go | 4 +- pkg/ucp/integrationtests/azure/proxy_test.go | 7 +- pkg/ucp/integrationtests/handler_test.go | 18 +- pkg/ucp/integrationtests/planes/aws_test.go | 18 +- pkg/ucp/integrationtests/planes/azure_test.go | 17 +- .../integrationtests/planes/planes_test.go | 5 +- .../integrationtests/planes/radius_test.go | 17 +- .../planes/validation_test.go | 7 +- pkg/ucp/integrationtests/radius/proxy_test.go | 19 +- .../resourcegroups/resourcegroups_test.go | 17 +- .../resourceproviders/apiversions_test.go | 5 +- .../resourceproviders/locations_test.go | 5 +- .../resourceproviders_test.go | 7 +- .../resourceproviders/resourcetypes_test.go | 7 +- .../resourceproviders/summary_test.go | 5 +- .../resourceproviders/util_test.go | 20 +- pkg/ucp/integrationtests/testrp/async.go | 6 +- pkg/ucp/integrationtests/testrp/sync.go | 6 +- .../integrationtests/testserver/testserver.go | 604 ------------------ pkg/ucp/options.go | 116 ++++ pkg/ucp/rest/objects.go | 69 -- pkg/ucp/server/server.go | 171 +---- pkg/ucp/testhost/doc.go | 18 + pkg/ucp/testhost/host.go | 218 +++++++ 83 files changed, 1963 insertions(+), 1590 deletions(-) create mode 100644 pkg/components/testhost/clients.go create mode 100644 pkg/components/testhost/doc.go create mode 100644 pkg/components/testhost/host.go create mode 100644 pkg/dynamicrp/integrationtest/dynamic/providers_test.go create mode 100644 pkg/dynamicrp/testhost/doc.go create mode 100644 pkg/dynamicrp/testhost/host.go create mode 100644 pkg/ucp/config.go create mode 100644 pkg/ucp/doc.go delete mode 100644 pkg/ucp/hostoptions/hostoptions.go delete mode 100644 pkg/ucp/hostoptions/providerconfig.go delete mode 100644 pkg/ucp/integrationtests/testserver/testserver.go create mode 100644 pkg/ucp/options.go delete mode 100644 pkg/ucp/rest/objects.go create mode 100644 pkg/ucp/testhost/doc.go create mode 100644 pkg/ucp/testhost/host.go diff --git a/.vscode/launch.json b/.vscode/launch.json index d98ac8286c..899ef7aa1e 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -58,12 +58,7 @@ "args": [ "--config-file", "${workspaceFolder}/cmd/ucpd/ucp-dev.yaml" - ], - "env": { - "BASE_PATH": "/apis/api.ucp.dev/v1alpha3", - "PORT": "9000", - "UCP_CONFIG": "${workspaceFolder}/cmd/ucpd/ucp-self-hosted-dev.yaml" - } + ] }, { "name": "Launch Controller", diff --git a/cmd/applications-rp/cmd/root.go b/cmd/applications-rp/cmd/root.go index 8656dae6bc..7df3495907 100644 --- a/cmd/applications-rp/cmd/root.go +++ b/cmd/applications-rp/cmd/root.go @@ -111,7 +111,12 @@ var rootCmd = &cobra.Command{ Services: hostingSvc, } + // Make the logger available to the services. ctx := logr.NewContext(context.Background(), logger) + + // Make the hosting configuration available to the services. + ctx = hostoptions.WithContext(ctx, options.Config) + return hosting.RunWithInterrupts(ctx, host) }, } diff --git a/cmd/applications-rp/radius-dev.yaml b/cmd/applications-rp/radius-dev.yaml index c13ad6ada4..c69667a7dc 100644 --- a/cmd/applications-rp/radius-dev.yaml +++ b/cmd/applications-rp/radius-dev.yaml @@ -2,7 +2,7 @@ environment: name: Dev roleLocation: "global" -storageProvider: +databaseProvider: provider: "etcd" etcd: inmemory: true diff --git a/cmd/applications-rp/radius-self-hosted.yaml b/cmd/applications-rp/radius-self-hosted.yaml index 2b9dd9368d..121ee728d0 100644 --- a/cmd/applications-rp/radius-self-hosted.yaml +++ b/cmd/applications-rp/radius-self-hosted.yaml @@ -8,9 +8,9 @@ # - Disables metrics and profiler # environment: - name: Dev + name: self-hosted roleLocation: "global" -storageProvider: +databaseProvider: provider: "apiserver" apiserver: context: '' diff --git a/cmd/dynamic-rp/dynamicrp-dev.yaml b/cmd/dynamic-rp/dynamicrp-dev.yaml index 4794a02ce0..22f60a4fe7 100644 --- a/cmd/dynamic-rp/dynamicrp-dev.yaml +++ b/cmd/dynamic-rp/dynamicrp-dev.yaml @@ -2,7 +2,7 @@ environment: name: Dev roleLocation: "global" -storageProvider: +databaseProvider: provider: "apiserver" apiserver: context: '' diff --git a/cmd/ucpd/cmd/root.go b/cmd/ucpd/cmd/root.go index 4c012bd07e..d99e885800 100644 --- a/cmd/ucpd/cmd/root.go +++ b/cmd/ucpd/cmd/root.go @@ -19,6 +19,7 @@ package cmd import ( "context" "fmt" + "os" "github.com/go-logr/logr" "github.com/spf13/cobra" @@ -27,6 +28,7 @@ import ( "github.com/radius-project/radius/pkg/armrpc/hostoptions" "github.com/radius-project/radius/pkg/components/database/databaseprovider" + "github.com/radius-project/radius/pkg/ucp" "github.com/radius-project/radius/pkg/ucp/hosting" "github.com/radius-project/radius/pkg/ucp/server" "github.com/radius-project/radius/pkg/ucp/ucplog" @@ -38,12 +40,23 @@ var rootCmd = &cobra.Command{ Long: `Server process for the Universal Control Plane (UCP).`, RunE: func(cmd *cobra.Command, args []string) error { configFilePath := cmd.Flag("config-file").Value.String() - options, err := server.NewServerOptionsFromEnvironment(configFilePath) + + bs, err := os.ReadFile(configFilePath) if err != nil { - return err + return fmt.Errorf("failed to read configuration file: %w", err) + } + + config, err := ucp.LoadConfig(bs) + if err != nil { + return fmt.Errorf("failed to parse configuration file: %w", err) + } + + options, err := ucp.NewOptions(cmd.Context(), config) + if err != nil { + return fmt.Errorf("failed to create server options: %w", err) } - logger, flush, err := ucplog.NewLogger(ucplog.LoggerName, &options.LoggingOptions) + logger, flush, err := ucplog.NewLogger(ucplog.LoggerName, &options.Config.Logging) if err != nil { return err } @@ -52,17 +65,17 @@ var rootCmd = &cobra.Command{ // Must set the logger before using controller-runtime. runtimelog.SetLogger(logger) - if options.DatabaseProviderOptions.Provider == databaseprovider.TypeETCD && - options.DatabaseProviderOptions.ETCD.InMemory { + if options.Config.Database.Provider == databaseprovider.TypeETCD && + options.Config.Database.ETCD.InMemory { // For in-memory etcd we need to register another service to manage its lifecycle. // // The client will be initialized asynchronously. clientconfigSource := hosting.NewAsyncValue[etcdclient.Client]() - options.DatabaseProviderOptions.ETCD.Client = clientconfigSource - options.SecretProviderOptions.ETCD.Client = clientconfigSource + options.Config.Database.ETCD.Client = clientconfigSource + options.Config.Secrets.ETCD.Client = clientconfigSource } - host, err := server.NewServer(&options) + host, err := server.NewServer(options) if err != nil { return err } diff --git a/cmd/ucpd/ucp-dev.yaml b/cmd/ucpd/ucp-dev.yaml index 32f1a465cf..a7561eeb7a 100644 --- a/cmd/ucpd/ucp-dev.yaml +++ b/cmd/ucpd/ucp-dev.yaml @@ -9,8 +9,14 @@ # - Talk to Portable Resources' Providers on port 8081 # - Disables metrics and profiler # -location: 'global' -storageProvider: +environment: + name: Dev + roleLocation: "global" +server: + port: 9000 + pathBase: /apis/api.ucp.dev/v1alpha3 + +databaseProvider: provider: "apiserver" apiserver: context: '' @@ -32,19 +38,20 @@ profilerProvider: #Default planes configuration with which ucp starts # TODO: Remove azure and aws planes once rad provider commands are supported -planes: - - id: "/planes/aws/aws" - properties: - kind: "AWS" - - id: "/planes/radius/local" - properties: - resourceProviders: - Applications.Core: "http://localhost:8080" - Applications.Messaging: "http://localhost:8080" - Applications.Dapr: "http://localhost:8080" - Applications.Datastores: "http://localhost:8080" - Microsoft.Resources: "http://localhost:5017" - kind: "UCPNative" +initialization: + planes: + - id: "/planes/aws/aws" + properties: + kind: "AWS" + - id: "/planes/radius/local" + properties: + resourceProviders: + Applications.Core: "http://localhost:8080" + Applications.Messaging: "http://localhost:8080" + Applications.Dapr: "http://localhost:8080" + Applications.Datastores: "http://localhost:8080" + Microsoft.Resources: "http://localhost:5017" + kind: "UCPNative" identity: authMethod: default @@ -76,4 +83,4 @@ logging: tracerProvider: serviceName: "ucp" zipkin: - url: "http://localhost:9411/api/v2/spans" + url: "http://localhost:9411/api/v2/spans" \ No newline at end of file diff --git a/deploy/Chart/templates/controller/configmaps.yaml b/deploy/Chart/templates/controller/configmaps.yaml index 314213647b..17fabbc717 100644 --- a/deploy/Chart/templates/controller/configmaps.yaml +++ b/deploy/Chart/templates/controller/configmaps.yaml @@ -12,7 +12,7 @@ data: host: "0.0.0.0" port: 9443 - storageProvider: + databaseProvider: provider: "apiserver" apiserver: context: "" diff --git a/deploy/Chart/templates/dynamic-rp/configmaps.yaml b/deploy/Chart/templates/dynamic-rp/configmaps.yaml index 31368b5db0..9dc3c2ddf2 100644 --- a/deploy/Chart/templates/dynamic-rp/configmaps.yaml +++ b/deploy/Chart/templates/dynamic-rp/configmaps.yaml @@ -13,7 +13,7 @@ data: environment: name: self-hosted roleLocation: "global" - storageProvider: + databaseProvider: provider: "apiserver" apiserver: context: "" diff --git a/deploy/Chart/templates/rp/configmaps.yaml b/deploy/Chart/templates/rp/configmaps.yaml index e444f31999..6429960c08 100644 --- a/deploy/Chart/templates/rp/configmaps.yaml +++ b/deploy/Chart/templates/rp/configmaps.yaml @@ -13,7 +13,7 @@ data: environment: name: self-hosted roleLocation: "global" - storageProvider: + databaseProvider: provider: "apiserver" apiserver: context: "" diff --git a/deploy/Chart/templates/ucp/configmaps.yaml b/deploy/Chart/templates/ucp/configmaps.yaml index d56d0f4f30..cc9968228c 100644 --- a/deploy/Chart/templates/ucp/configmaps.yaml +++ b/deploy/Chart/templates/ucp/configmaps.yaml @@ -10,8 +10,14 @@ data: ucp-config.yaml: |- # Radius configuration file. # See https://github.com/radius-project/radius/blob/main/docs/contributing/contributing-code/contributing-code-control-plane/configSettings.md for more information. - location: 'global' - storageProvider: + environment: + name: Dev + roleLocation: "global" + server: + port: 9443 + pathBase: /apis/api.ucp.dev/v1alpha3 + tlsCertificateDirectory: /var/tls/cert + databaseProvider: provider: "apiserver" apiserver: context: "" @@ -30,20 +36,20 @@ data: profilerProvider: enabled: true port: 6060 - - planes: - - id: "/planes/radius/local" - properties: - resourceProviders: - Applications.Core: "http://applications-rp.radius-system:5443" - Applications.Dapr: "http://applications-rp.radius-system:5443" - Applications.Datastores: "http://applications-rp.radius-system:5443" - Applications.Messaging: "http://applications-rp.radius-system:5443" - Microsoft.Resources: "http://bicep-de.radius-system:6443" - kind: "UCPNative" - - id: "/planes/aws/aws" - properties: - kind: "AWS" + initialization: + planes: + - id: "/planes/radius/local" + properties: + resourceProviders: + Applications.Core: "http://applications-rp.radius-system:5443" + Applications.Dapr: "http://applications-rp.radius-system:5443" + Applications.Datastores: "http://applications-rp.radius-system:5443" + Applications.Messaging: "http://applications-rp.radius-system:5443" + Microsoft.Resources: "http://bicep-de.radius-system:6443" + kind: "UCPNative" + - id: "/planes/aws/aws" + properties: + kind: "AWS" identity: authMethod: UCPCredential diff --git a/deploy/Chart/templates/ucp/deployment.yaml b/deploy/Chart/templates/ucp/deployment.yaml index 776b01ac7b..9c79557899 100644 --- a/deploy/Chart/templates/ucp/deployment.yaml +++ b/deploy/Chart/templates/ucp/deployment.yaml @@ -36,8 +36,6 @@ spec: args: - --config-file=/etc/config/ucp-config.yaml env: - - name: BASE_PATH - value: '/apis/api.ucp.dev/v1alpha3' # listen for APIService URLs - name: TLS_CERT_DIR value: '/var/tls/cert' - name: PORT diff --git a/docs/contributing/contributing-code/contributing-code-control-plane/configSettings.md b/docs/contributing/contributing-code/contributing-code-control-plane/configSettings.md index e54edf127a..356140ac77 100644 --- a/docs/contributing/contributing-code/contributing-code-control-plane/configSettings.md +++ b/docs/contributing/contributing-code/contributing-code-control-plane/configSettings.md @@ -20,7 +20,7 @@ The following properties can be specified in configuration for all services: |-----|-------------|---------| | environment | Environment name and its role location | [**See below**](#environment) | | identity | AAD APP authentication for the resource provider | [**See below**](#identity) | -| storageProvider | Configuration options for the data storage provider | [**See below**](#storageprovider) | +| databaseProvider | Configuration options for the database provider | [**See below**](#databaseprovider) | | queueProvider | Configuration options for the provider to create and manage the queue client | [**See below**](#queueprovider) | | secretProvider | Configuration options for the provider to manage credential | [**See below**](#secretprovider) | | server | Configuration options for the HTTP server bootstrap | [**See below**](#server) | @@ -61,18 +61,18 @@ The following are properties that can be specified for UCP: | audience | The recipient of the certificate | `https://management.core.windows.net` | | pemCertPath | Path to certificate file | `/var/certs/rp-aad-app.pem` | -### storageProvider +### databaseProvider | Key | Description | Example | |-----|-------------|---------| -| provider | The type of storage provider | `apiServer` | -| apiServer | Object containing properties for Kubernetes APIServer store | [**See below**](#apiserver) | -| etcd | Object containing properties for ETCD store | [**See below**](#etcd)| +| provider | The type of database provider | `apiServer` | +| apiServer | Object containing properties for Kubernetes APIServer database | [**See below**](#apiserver) | +| etcd | Object containing properties for ETCD database | [**See below**](#etcd)| ### queueProvider | Key | Description | Example | |-----|-------------|---------| | provider | The type of queue provider | `apiServer` | -| apiServer | Object containing properties for Kubernetes APIServer store | [**See below**](#apiserver) | +| apiServer | Object containing properties for Kubernetes APIServer queue | [**See below**](#apiserver) | | inMemoryQueue | Object containing properties for InMemory Queue client | | ### secretProvider @@ -181,7 +181,7 @@ Below are completed examples of possible configurations: environment: name: self-hosted roleLocation: "global" -storageProvider: +databaseProvider: provider: "apiserver" apiserver: context: "" @@ -210,7 +210,7 @@ ucp: ### UCP ```yaml location: 'global' -storageProvider: +databaseProvider: provider: "apiserver" apiserver: context: "" diff --git a/pkg/armrpc/asyncoperation/worker/service.go b/pkg/armrpc/asyncoperation/worker/service.go index 305a362060..f4eaefa79f 100644 --- a/pkg/armrpc/asyncoperation/worker/service.go +++ b/pkg/armrpc/asyncoperation/worker/service.go @@ -18,64 +18,56 @@ package worker import ( "context" + "sync" manager "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" - "github.com/radius-project/radius/pkg/armrpc/hostoptions" - "github.com/radius-project/radius/pkg/components/database/databaseprovider" + "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/components/queue" - "github.com/radius-project/radius/pkg/components/queue/queueprovider" "github.com/radius-project/radius/pkg/ucp/ucplog" ) // Service is the base worker service implementation to initialize and start worker. +// All exported fields should be initialized by the caller. type Service struct { - // ProviderName is the name of provider namespace. - ProviderName string - // Options is the server hosting options. - Options hostoptions.HostOptions - // DatabaseProvider is the provider of the database client. - DatabaseProvider *databaseprovider.DatabaseProvider + // DatabaseClient is database client. + DatabaseClient database.Client + // OperationStatusManager is the manager of the operation status. OperationStatusManager manager.StatusManager - // Controllers is the registry of the async operation controllers. - Controllers *ControllerRegistry - // RequestQueue is the queue client for async operation request message. - RequestQueue queue.Client -} -// Init initializes worker service - it initializes the StorageProvider, RequestQueue, OperationStatusManager, Controllers, KubeClient and -// returns an error if any of these operations fail. -func (s *Service) Init(ctx context.Context) error { - s.DatabaseProvider = databaseprovider.FromOptions(s.Options.Config.DatabaseProvider) - qp := queueprovider.New(s.Options.Config.QueueProvider) + // Options configures options for the async worker. + Options Options - var err error - storageClient, err := s.DatabaseProvider.GetClient(ctx) - if err != nil { - return err - } + // QueueProvider is the queue client. + QueueClient queue.Client - s.RequestQueue, err = qp.GetClient(ctx) - if err != nil { - return err - } + // controllers is the registry of the async operation controllers. + controllers *ControllerRegistry - s.OperationStatusManager = manager.New(storageClient, s.RequestQueue, s.Options.Config.Env.RoleLocation) - s.Controllers = NewControllerRegistry() - return nil + // controllersInit is used to ensure single initialization of controllers. + controllersInit sync.Once +} + +// Controllers returns the controller registry for the worker service. +func (s *Service) Controllers() *ControllerRegistry { + s.controllersInit.Do(func() { + s.controllers = NewControllerRegistry() + }) + + return s.controllers } // Start creates and starts a worker, and logs any errors that occur while starting the worker. -func (s *Service) Start(ctx context.Context, opt Options) error { +func (s *Service) Start(ctx context.Context) error { logger := ucplog.FromContextOrDiscard(ctx) - ctx = hostoptions.WithContext(ctx, s.Options.Config) // Create and start worker. - worker := New(opt, s.OperationStatusManager, s.RequestQueue, s.Controllers) + worker := New(s.Options, s.OperationStatusManager, s.QueueClient, s.Controllers()) logger.Info("Start Worker...") if err := worker.Start(ctx); err != nil { logger.Error(err, "failed to start worker...") + return err } logger.Info("Worker stopped...") diff --git a/pkg/armrpc/hostoptions/providerconfig.go b/pkg/armrpc/hostoptions/providerconfig.go index 59fd7a7589..e8f19f1fb0 100644 --- a/pkg/armrpc/hostoptions/providerconfig.go +++ b/pkg/armrpc/hostoptions/providerconfig.go @@ -17,9 +17,12 @@ limitations under the License. package hostoptions import ( + "fmt" + "github.com/radius-project/radius/pkg/components/database/databaseprovider" "github.com/radius-project/radius/pkg/components/queue/queueprovider" "github.com/radius-project/radius/pkg/components/secret/secretprovider" + metricsprovider "github.com/radius-project/radius/pkg/metrics/provider" profilerprovider "github.com/radius-project/radius/pkg/profiler/provider" "github.com/radius-project/radius/pkg/trace" @@ -31,7 +34,7 @@ import ( type ProviderConfig struct { Env EnvironmentOptions `yaml:"environment"` Identity IdentityOptions `yaml:"identity"` - DatabaseProvider databaseprovider.Options `yaml:"storageProvider"` + DatabaseProvider databaseprovider.Options `yaml:"databaseProvider"` SecretProvider secretprovider.SecretProviderOptions `yaml:"secretProvider"` QueueProvider queueprovider.QueueProviderOptions `yaml:"queueProvider"` Server *ServerOptions `yaml:"server,omitempty"` @@ -59,6 +62,18 @@ type ServerOptions struct { ArmMetadataEndpoint string `yaml:"armMetadataEndpoint,omitempty"` // EnableAuth when set the arm client authetication will be performed EnableArmAuth bool `yaml:"enableArmAuth,omitempty"` + + // TLSCertificateDirectory is the directory where the TLS certificates are stored. + // + // The server code will expect to find the following files in this directory: + // - tls.crt: The server's certificate. + // - tls.key: The server's private key. + TLSCertificateDirectory string `yaml:"tlsCertificateDirectory,omitempty"` +} + +// Address returns the address of the server in host:port format. +func (s ServerOptions) Address() string { + return s.Host + ":" + fmt.Sprint(s.Port) } // WorkerServerOptions includes the worker server options. diff --git a/pkg/components/testhost/clients.go b/pkg/components/testhost/clients.go new file mode 100644 index 0000000000..3f534c6d96 --- /dev/null +++ b/pkg/components/testhost/clients.go @@ -0,0 +1,274 @@ +/* +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 testhost + +import ( + "bytes" + "context" + "encoding/json" + "io" + "net/http" + "net/url" + "os" + "testing" + "time" + + v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/armrpc/rpctest" + "github.com/stretchr/testify/require" +) + +// TestResponse is returned from requests made against a TestHost. Tests should use the functions defined +// on TestResponse for validation. +type TestResponse struct { + // Raw is the raw HTTP response. + Raw *http.Response + + // Body is the response body. + Body *bytes.Buffer + + // Error is the ARM error response if the response status code is >= 400. + Error *v1.ErrorResponse + + // t is the test object. + t *testing.T + + // host is the TestHost that served this response. + host *TestHost +} + +// MakeFixtureRequest sends a request to the server using a file on disk as the payload (body). Use the fixture +// parameter to specify the path to a file. +func (th *TestHost) MakeFixtureRequest(method string, pathAndQuery string, fixture string) *TestResponse { + body, err := os.ReadFile(fixture) + require.NoError(th.t, err, "reading fixture failed") + return th.MakeRequest(method, pathAndQuery, body) +} + +// MakeTypedRequest sends a request to the server by marshalling the provided object to JSON. +func (th *TestHost) MakeTypedRequest(method string, pathAndQuery string, body any) *TestResponse { + if body == nil { + return th.MakeRequest(method, pathAndQuery, nil) + } + + b, err := json.Marshal(body) + require.NoError(th.t, err, "marshalling body failed") + return th.MakeRequest(method, pathAndQuery, b) +} + +// MakeRequest sends a request to the server. +func (th *TestHost) MakeRequest(method string, pathAndQuery string, body []byte) *TestResponse { + // Prepend the base path if this is a relative URL. + requestUrl := pathAndQuery + parsed, err := url.Parse(pathAndQuery) + require.NoError(th.t, err, "parsing URL failed") + if !parsed.IsAbs() { + requestUrl = th.BaseURL() + pathAndQuery + } + + client := th.Client() + request, err := rpctest.NewHTTPRequestWithContent(context.Background(), method, requestUrl, body) + require.NoError(th.t, err, "creating request failed") + + ctx := rpctest.NewARMRequestContext(request) + request = request.WithContext(ctx) + + response, err := client.Do(request) + require.NoError(th.t, err, "sending request failed") + + // Buffer the response so we can read multiple times. + responseBuffer := &bytes.Buffer{} + _, err = io.Copy(responseBuffer, response.Body) + response.Body.Close() + require.NoError(th.t, err, "copying response failed") + + response.Body = io.NopCloser(responseBuffer) + + // Pretty-print response for logs. + if len(responseBuffer.Bytes()) > 0 { + var data any + err = json.Unmarshal(responseBuffer.Bytes(), &data) + require.NoError(th.t, err, "unmarshalling response failed") + + text, err := json.MarshalIndent(&data, "", " ") + require.NoError(th.t, err, "marshalling response failed") + th.t.Log("Response Body: \n" + string(text)) + } + + var errorResponse *v1.ErrorResponse + if response.StatusCode >= 400 { + // The response MUST be an arm error for a non-success status code. + errorResponse = &v1.ErrorResponse{} + err := json.Unmarshal(responseBuffer.Bytes(), &errorResponse) + require.NoError(th.t, err, "unmarshalling error response failed - THIS IS A SERIOUS BUG. ALL ERROR RESPONSES MUST USE THE STANDARD FORMAT") + } + + return &TestResponse{Raw: response, Body: responseBuffer, Error: errorResponse, host: th, t: th.t} +} + +// EqualsErrorCode compares a TestResponse against an expected status code and error code. EqualsErrorCode assumes the response +// uses the ARM error format (required for our APIs). +func (tr *TestResponse) EqualsErrorCode(statusCode int, code string) { + require.Equal(tr.t, statusCode, tr.Raw.StatusCode, "status code did not match expected") + require.NotNil(tr.t, tr.Error, "expected an error but actual response did not contain one") + require.Equal(tr.t, code, tr.Error.Error.Code, "actual error code was different from expected") +} + +// EqualsFixture compares a TestResponse against an expected status code and body payload. Use the fixture parameter to specify +// the path to a file. +func (tr *TestResponse) EqualsFixture(statusCode int, fixture string) { + body, err := os.ReadFile(fixture) + require.NoError(tr.t, err, "reading fixture failed") + tr.EqualsResponse(statusCode, body) +} + +// EqualsStatusCode compares a TestResponse against an expected status code (ingnores the body payload). +func (tr *TestResponse) EqualsStatusCode(statusCode int) { + require.Equal(tr.t, statusCode, tr.Raw.StatusCode, "status code did not match expected") +} + +// EqualsFixture compares a TestResponse against an expected status code and body payload. +func (tr *TestResponse) EqualsResponse(statusCode int, body []byte) { + if len(body) == 0 { + require.Equal(tr.t, statusCode, tr.Raw.StatusCode, "status code did not match expected") + require.Empty(tr.t, tr.Body.Bytes(), "expected an empty response but actual response had a body") + return + } + + var expected map[string]any + err := json.Unmarshal(body, &expected) + require.NoError(tr.t, err, "unmarshalling expected response failed") + + var actual map[string]any + err = json.Unmarshal(tr.Body.Bytes(), &actual) + + tr.removeSystemData(actual) + + require.NoError(tr.t, err, "unmarshalling actual response failed. Got '%v'", tr.Body.String()) + require.EqualValues(tr.t, expected, actual, "response body did not match expected") + require.Equal(tr.t, statusCode, tr.Raw.StatusCode, "status code did not match expected") +} + +// EqualsValue compares a TestResponse against an expected status code and an response body. +// +// If the systemData propert is present in the response, it will be removed. +func (tr *TestResponse) EqualsValue(statusCode int, expected any) { + var actual map[string]any + err := json.Unmarshal(tr.Body.Bytes(), &actual) + require.NoError(tr.t, err, "unmarshalling actual response failed") + + // Convert expected input to map[string]any to compare with actual response. + expectedBytes, err := json.Marshal(expected) + require.NoError(tr.t, err, "marshalling expected response failed") + + var expectedMap map[string]any + err = json.Unmarshal(expectedBytes, &expectedMap) + require.NoError(tr.t, err, "unmarshalling expected response failed") + + tr.removeSystemData(expectedMap) + tr.removeSystemData(actual) + + require.EqualValues(tr.t, expectedMap, actual, "response body did not match expected") + require.Equal(tr.t, statusCode, tr.Raw.StatusCode, "status code did not match expected") +} + +// EqualsEmptyList compares a TestResponse against an expected status code and an empty resource list. +func (tr *TestResponse) EqualsEmptyList() { + expected := map[string]any{ + "value": []any{}, + } + + var actual map[string]any + err := json.Unmarshal(tr.Body.Bytes(), &actual) + + tr.removeSystemData(actual) + + require.NoError(tr.t, err, "unmarshalling actual response failed") + require.EqualValues(tr.t, expected, actual, "response body did not match expected") + require.Equal(tr.t, http.StatusOK, tr.Raw.StatusCode, "status code did not match expected") +} + +func (tr *TestResponse) ReadAs(obj any) { + tr.t.Helper() + + decoder := json.NewDecoder(tr.Body) + decoder.DisallowUnknownFields() + + err := decoder.Decode(obj) + require.NoError(tr.t, err, "unmarshalling expected response failed") +} + +func (tr *TestResponse) WaitForOperationComplete(timeout *time.Duration) *TestResponse { + if tr.Raw.StatusCode != http.StatusCreated && tr.Raw.StatusCode != http.StatusAccepted { + // Response is already terminal. + return tr + } + + if timeout == nil { + x := 30 * time.Second + timeout = &x + } + + timer := time.After(*timeout) + poller := time.NewTicker(1 * time.Second) + defer poller.Stop() + for { + select { + case <-timer: + tr.t.Fatalf("timed out waiting for operation to complete") + return nil // unreachable + case <-poller.C: + // The Location header should give us the operation status URL. + response := tr.host.MakeRequest(http.MethodGet, tr.Raw.Header.Get("Azure-AsyncOperation"), nil) + + // To determine if the response is terminal we need to read the provisioning state field. + operationStatus := v1.AsyncOperationStatus{} + response.ReadAs(&operationStatus) + if operationStatus.Status.IsTerminal() { + // Response is terminal. + return response + } + + continue + } + } +} + +func (tr *TestResponse) removeSystemData(responseBody map[string]any) { + // Delete systemData property if found, it's not stable so we don't include it in baselines. + _, ok := responseBody["systemData"] + if ok { + delete(responseBody, "systemData") + return + } + + value, ok := responseBody["value"] + if !ok { + return + } + + valueSlice, ok := value.([]any) + if !ok { + return + } + + for _, v := range valueSlice { + if vMap, ok := v.(map[string]any); ok { + tr.removeSystemData(vMap) + } + } +} diff --git a/pkg/components/testhost/doc.go b/pkg/components/testhost/doc.go new file mode 100644 index 0000000000..0d187588b9 --- /dev/null +++ b/pkg/components/testhost/doc.go @@ -0,0 +1,23 @@ +/* +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 testhost provides a host for running any Radius control-plane component +// as an in-memory server for testing purposes. +// +// This package should be wrapped in a test package specific to the component under test. +// The wrapping design allows for component-specific depenendendencies to be defined without +// polluting the shared code. +package testhost diff --git a/pkg/components/testhost/host.go b/pkg/components/testhost/host.go new file mode 100644 index 0000000000..83f5f19cc9 --- /dev/null +++ b/pkg/components/testhost/host.go @@ -0,0 +1,148 @@ +/* +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 testhost provides a host for running any Radius control-plane component +// as an in-memory server for testing purposes. +// +// This package should be wrapped in a test package specific to the component under test. +// The wrapping design allows for component-specific depenendendencies to be defined without +// polluting the shared code. +package testhost + +import ( + "context" + "net" + "net/http" + "net/url" + "sync" + "testing" + "time" + + "github.com/radius-project/radius/pkg/ucp/hosting" + "github.com/radius-project/radius/test/testcontext" + "github.com/stretchr/testify/require" +) + +// StartHost starts a new test host for the given hosting.Host and returns a TestHost instance. +// The TestHost will have its lifecycle managed by the test context, and will be shut down when the test +// completes. +func StartHost(t *testing.T, host *hosting.Host, baseURL string) *TestHost { + ctx, cancel := context.WithCancel(testcontext.New(t)) + errs, messages := host.RunAsync(ctx) + + go func() { + for msg := range messages { + t.Logf("Message: %s", msg) + } + }() + + th := &TestHost{ + baseURL: baseURL, + host: host, + messages: messages, + cancel: cancel, + stoppedChan: errs, + t: t, + } + t.Cleanup(th.Close) + + // Wait for the server to start listening on the port. + require.Eventuallyf(t, func() bool { + u, err := url.Parse(baseURL) + if err != nil { + panic("Invalid URL: " + baseURL) + } + + conn, err := net.Dial("tcp", net.JoinHostPort(u.Hostname(), u.Port())) + if err != nil { + t.Logf("Waiting for server to start listening on port: %v", err) + return false + } + defer conn.Close() + + return true + }, time.Second*5, time.Millisecond*20, "server did not start listening on port") + + return th +} + +// TestHost is a test server for any Radius control-plane component. Do not construct this type directly, use the Start function. +type TestHost struct { + // baseURL is the base URL of the server, including the path base. + baseURL string + + // host is the hosting process running the component. + host *hosting.Host + + // messages is the channel that will receive lifecycle messages from the host. + messages <-chan hosting.LifecycleMessage + + // cancel is the function to call to stop the server. + cancel context.CancelFunc + + // stoppedChan is the channel that will be closed when the server has stopped. + stoppedChan <-chan error + + // shutdown is used to ensure that Close is only called once. + shutdown sync.Once + + // t is the testing.T instance to use for assertions. + t *testing.T +} + +// Close shuts down the server and will block until shutdown completes. +func (th *TestHost) Close() { + // We're being picking about resource cleanup here, because unless we are picky we hit scalability + // problems in tests pretty quickly. + th.shutdown.Do(func() { + // Shut down the host. + th.cancel() + + if th.stoppedChan != nil { + <-th.stoppedChan // host stopped + } + }) +} + +// BaseURL returns the base URL of the server, including the path base. +// +// This should be used as a URL prefix for all requests to the server. +func (th *TestHost) BaseURL() string { + return th.baseURL +} + +// Client returns the HTTP client to use to make requests to the server. +func (th *TestHost) Client() *http.Client { + return http.DefaultClient +} + +// T returns the testing.T instance associated with the test host. +func (th *TestHost) T() *testing.T { + return th.t +} + +// AllocateFreePort chooses a random port for use in tests. +func AllocateFreePort(t *testing.T) int { + listener, err := net.Listen("tcp", ":0") + require.NoError(t, err, "failed to allocate port") + + port := listener.Addr().(*net.TCPAddr).Port + + err = listener.Close() + require.NoError(t, err, "failed to close listener") + + return port +} diff --git a/pkg/dynamicrp/backend/service.go b/pkg/dynamicrp/backend/service.go index bbbd33955f..710a0fccf0 100644 --- a/pkg/dynamicrp/backend/service.go +++ b/pkg/dynamicrp/backend/service.go @@ -18,17 +18,17 @@ package backend import ( "context" - "fmt" "github.com/radius-project/radius/pkg/armrpc/asyncoperation/worker" - "github.com/radius-project/radius/pkg/armrpc/hostoptions" "github.com/radius-project/radius/pkg/dynamicrp" + "github.com/radius-project/radius/pkg/recipes/controllerconfig" ) // Service runs the backend for the dynamic-rp. type Service struct { worker.Service options *dynamicrp.Options + recipes *controllerconfig.RecipeControllerConfig } // NewService creates a new service to run the dynamic-rp backend. @@ -36,38 +36,49 @@ func NewService(options *dynamicrp.Options) *Service { return &Service{ options: options, Service: worker.Service{ - ProviderName: "dynamic-rp", - Options: hostoptions.HostOptions{ - Config: &hostoptions.ProviderConfig{ - Env: options.Config.Environment, - DatabaseProvider: options.Config.Database, - SecretProvider: options.Config.Secrets, - QueueProvider: options.Config.Queue, - }, - }, + // Will be initialized later }, + recipes: options.Recipes, } } // Name returns the name of the service used for logging. func (w *Service) Name() string { - return fmt.Sprintf("%s async worker", w.Service.ProviderName) + return "dynamic-rp async worker" } // Run runs the service. func (w *Service) Run(ctx context.Context) error { - err := w.Init(ctx) + if w.options.Config.Worker.MaxOperationConcurrency != nil { + w.Service.Options.MaxOperationConcurrency = *w.options.Config.Worker.MaxOperationConcurrency + } + if w.options.Config.Worker.MaxOperationRetryCount != nil { + w.Service.Options.MaxOperationRetryCount = *w.options.Config.Worker.MaxOperationRetryCount + } + + databaseClient, err := w.options.DatabaseProvider.GetClient(ctx) if err != nil { return err } - workerOptions := worker.Options{} - if w.options.Config.Worker.MaxOperationConcurrency != nil { - workerOptions.MaxOperationConcurrency = *w.options.Config.Worker.MaxOperationConcurrency + queueClient, err := w.options.QueueProvider.GetClient(ctx) + if err != nil { + return err } - if w.options.Config.Worker.MaxOperationRetryCount != nil { - workerOptions.MaxOperationRetryCount = *w.options.Config.Worker.MaxOperationRetryCount + + w.Service.DatabaseClient = databaseClient + w.Service.QueueClient = queueClient + w.Service.OperationStatusManager = w.options.StatusManager + + err = w.registerControllers(ctx) + if err != nil { + return err } - return w.Start(ctx, workerOptions) + return w.Start(ctx) +} + +func (w *Service) registerControllers(ctx context.Context) error { + // No controllers yet. + return nil } diff --git a/pkg/dynamicrp/config.go b/pkg/dynamicrp/config.go index 7cda77cf6e..1ffa71bd47 100644 --- a/pkg/dynamicrp/config.go +++ b/pkg/dynamicrp/config.go @@ -32,12 +32,14 @@ import ( ) // Config defines the configuration for the DynamicRP server. +// +// For testability, all fields on this struct MUST be parsable from YAML without any further initialization required. type Config struct { // Bicep configures properties for the Bicep recipe driver. Bicep hostoptions.BicepOptions `yaml:"bicep"` // Database is the configuration for the database. - Database databaseprovider.Options `yaml:"storageProvider"` + Database databaseprovider.Options `yaml:"databaseProvider"` // Environment is the configuration for the hosting environment. Environment hostoptions.EnvironmentOptions `yaml:"environment"` diff --git a/pkg/dynamicrp/integrationtest/dynamic/providers_test.go b/pkg/dynamicrp/integrationtest/dynamic/providers_test.go new file mode 100644 index 0000000000..bcfda112ff --- /dev/null +++ b/pkg/dynamicrp/integrationtest/dynamic/providers_test.go @@ -0,0 +1,174 @@ +/* +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 dynamic + +import ( + "context" + "net/http" + "testing" + + v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/dynamicrp/testhost" + "github.com/radius-project/radius/pkg/to" + "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" + ucptesthost "github.com/radius-project/radius/pkg/ucp/testhost" + "github.com/stretchr/testify/require" +) + +const ( + radiusPlaneName = "testing" + resourceProviderNamespace = "Applications.Test" + resourceTypeName = "exampleResources" + locationName = v1.LocationGlobal + apiVersion = "2024-01-01" + + resourceGroupName = "test-group" + exampleResourceName = "my-example" + + exampleResourcePlaneID = "/planes/radius/" + radiusPlaneName + exampleResourceGroupID = exampleResourcePlaneID + "/resourceGroups/test-group" + + exampleResourceID = exampleResourceGroupID + "/providers/Applications.Test/exampleResources/" + exampleResourceName + exampleResourceURL = exampleResourceID + "?api-version=" + apiVersion +) + +// This test covers the lifecycle of a dynamic resource. +func Test_Dynamic_Resource_Lifecycle(t *testing.T) { + _, ucp := testhost.Start(t) + + // Setup a resource provider (Applications.Test/exampleResources) + createRadiusPlane(ucp) + createResourceProvider(ucp) + createResourceType(ucp) + createAPIVersion(ucp) + createLocation(ucp) + + // Setup a resource group where we can interact with the new resource type. + createResourceGroup(ucp) + + // We have not yet implemented any functionality for dynamic RP. + // + // This is the hello-worldiest of tests. We're just making sure that all + // of the infrastructure works. + response := ucp.MakeRequest(http.MethodGet, exampleResourceURL, nil) + response.EqualsErrorCode(404, "NotFound") +} + +func createRadiusPlane(server *ucptesthost.TestHost) v20231001preview.RadiusPlanesClientCreateOrUpdateResponse { + ctx := context.Background() + + plane := v20231001preview.RadiusPlaneResource{ + Location: to.Ptr(v1.LocationGlobal), + Properties: &v20231001preview.RadiusPlaneResourceProperties{ + // Note: this is a workaround. Properties is marked as a required field in + // the API. Without passing *something* here the body will be rejected. + ProvisioningState: to.Ptr(v20231001preview.ProvisioningStateSucceeded), + ResourceProviders: map[string]*string{}, + }, + } + + client := server.UCP().NewRadiusPlanesClient() + poller, err := client.BeginCreateOrUpdate(ctx, radiusPlaneName, plane, nil) + require.NoError(server.T(), err) + + response, err := poller.PollUntilDone(ctx, nil) + require.NoError(server.T(), err) + + return response +} + +func createResourceProvider(server *ucptesthost.TestHost) { + ctx := context.Background() + + resourceProvider := v20231001preview.ResourceProviderResource{ + Location: to.Ptr(v1.LocationGlobal), + Properties: &v20231001preview.ResourceProviderProperties{}, + } + + client := server.UCP().NewResourceProvidersClient() + poller, err := client.BeginCreateOrUpdate(ctx, radiusPlaneName, resourceProviderNamespace, resourceProvider, nil) + require.NoError(server.T(), err) + + _, err = poller.PollUntilDone(ctx, nil) + require.NoError(server.T(), err) +} + +func createResourceType(server *ucptesthost.TestHost) { + ctx := context.Background() + + resourceType := v20231001preview.ResourceTypeResource{ + Properties: &v20231001preview.ResourceTypeProperties{}, + } + + client := server.UCP().NewResourceTypesClient() + poller, err := client.BeginCreateOrUpdate(ctx, radiusPlaneName, resourceProviderNamespace, resourceTypeName, resourceType, nil) + require.NoError(server.T(), err) + + _, err = poller.PollUntilDone(ctx, nil) + require.NoError(server.T(), err) +} + +func createAPIVersion(server *ucptesthost.TestHost) { + ctx := context.Background() + + apiVersionResource := v20231001preview.APIVersionResource{ + Properties: &v20231001preview.APIVersionProperties{}, + } + + client := server.UCP().NewAPIVersionsClient() + poller, err := client.BeginCreateOrUpdate(ctx, radiusPlaneName, resourceProviderNamespace, resourceTypeName, apiVersion, apiVersionResource, nil) + require.NoError(server.T(), err) + + _, err = poller.PollUntilDone(ctx, nil) + require.NoError(server.T(), err) +} + +func createLocation(server *ucptesthost.TestHost) { + ctx := context.Background() + + location := v20231001preview.LocationResource{ + Properties: &v20231001preview.LocationProperties{ + ResourceTypes: map[string]*v20231001preview.LocationResourceType{ + resourceTypeName: { + APIVersions: map[string]map[string]any{ + apiVersion: {}, + }, + }, + }, + }, + } + + client := server.UCP().NewLocationsClient() + poller, err := client.BeginCreateOrUpdate(ctx, radiusPlaneName, resourceProviderNamespace, locationName, location, nil) + require.NoError(server.T(), err) + + _, err = poller.PollUntilDone(ctx, nil) + require.NoError(server.T(), err) +} + +func createResourceGroup(server *ucptesthost.TestHost) { + ctx := context.Background() + + resourceGroup := v20231001preview.ResourceGroupResource{ + Location: to.Ptr(v1.LocationGlobal), + Properties: &v20231001preview.ResourceGroupProperties{}, + } + + client := server.UCP().NewResourceGroupsClient() + _, err := client.CreateOrUpdate(ctx, radiusPlaneName, resourceGroupName, resourceGroup, nil) + require.NoError(server.T(), err) +} diff --git a/pkg/dynamicrp/options.go b/pkg/dynamicrp/options.go index 4ebaf80a1a..ac32ce8f62 100644 --- a/pkg/dynamicrp/options.go +++ b/pkg/dynamicrp/options.go @@ -21,7 +21,6 @@ import ( "fmt" "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" - "github.com/radius-project/radius/pkg/armrpc/hostoptions" "github.com/radius-project/radius/pkg/components/database/databaseprovider" "github.com/radius-project/radius/pkg/components/queue/queueprovider" "github.com/radius-project/radius/pkg/components/secret/secretprovider" @@ -32,7 +31,10 @@ import ( kube_rest "k8s.io/client-go/rest" ) -// Options holds the configuration options and shared services for the server. +// Options holds the configuration options and shared services for the DyanmicRP server. +// +// For testability, all fields on this struct MUST be constructed from the NewOptions function without any +// additional initialization required. type Options struct { // Config is the configuration for the server. Config *Config @@ -80,14 +82,16 @@ func NewOptions(ctx context.Context, config *Config) (*Options, error) { options.StatusManager = statusmanager.New(databaseClient, queueClient, config.Environment.RoleLocation) var cfg *kube_rest.Config - cfg, err = kubeutil.NewClientConfig(&kubeutil.ConfigOptions{ - // TODO: Allow to use custom context via configuration. - https://github.com/radius-project/radius/issues/5433 - ContextName: "", - QPS: kubeutil.DefaultServerQPS, - Burst: kubeutil.DefaultServerBurst, - }) - if err != nil { - return nil, fmt.Errorf("failed to get kubernetes config: %w", err) + if config.UCP.Kind == ucpconfig.UCPConnectionKindKubernetes { + cfg, err = kubeutil.NewClientConfig(&kubeutil.ConfigOptions{ + // TODO: Allow to use custom context via configuration. - https://github.com/radius-project/radius/issues/5433 + ContextName: "", + QPS: kubeutil.DefaultServerQPS, + Burst: kubeutil.DefaultServerBurst, + }) + if err != nil { + return nil, fmt.Errorf("failed to get kubernetes config: %w", err) + } } options.UCP, err = ucpconfig.NewConnectionFromUCPConfig(&config.UCP, cfg) @@ -95,22 +99,28 @@ func NewOptions(ctx context.Context, config *Config) (*Options, error) { return nil, err } + // TODO: This is the right place to initialize the recipe infrastructure. Unfortunately this + // has a dependency on Kubernetes right now, which isn't available for integration tests. + // + // We have a future work item to untangle this dependency and then this code can be uncommented. + // For now this is a placeholder/reminder of the code we need, and where to put it. + // // The recipe infrastructure is tied to corerp's dependencies, so we need to create it here. - recipes, err := controllerconfig.New(hostoptions.HostOptions{ - Config: &hostoptions.ProviderConfig{ - Bicep: config.Bicep, - Env: config.Environment, - Terraform: config.Terraform, - UCP: config.UCP, - }, - K8sConfig: cfg, - UCPConnection: options.UCP, - }) - if err != nil { - return nil, err - } - - options.Recipes = recipes + // recipes, err := controllerconfig.New(hostoptions.HostOptions{ + // Config: &hostoptions.ProviderConfig{ + // Bicep: config.Bicep, + // Env: config.Environment, + // Terraform: config.Terraform, + // UCP: config.UCP, + // }, + // K8sConfig: cfg, + // UCPConnection: options.UCP, + // }) + // if err != nil { + // return nil, err + // } + // + // options.Recipes = recipes return &options, nil } diff --git a/pkg/dynamicrp/testhost/doc.go b/pkg/dynamicrp/testhost/doc.go new file mode 100644 index 0000000000..bdd65e049e --- /dev/null +++ b/pkg/dynamicrp/testhost/doc.go @@ -0,0 +1,18 @@ +/* +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. +*/ + +// testhost provides an implementation of a test server for the dynamic RP. +package testhost diff --git a/pkg/dynamicrp/testhost/host.go b/pkg/dynamicrp/testhost/host.go new file mode 100644 index 0000000000..d6b8045608 --- /dev/null +++ b/pkg/dynamicrp/testhost/host.go @@ -0,0 +1,138 @@ +/* +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 testhost + +import ( + "context" + "fmt" + "strings" + "testing" + + "github.com/google/uuid" + v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/armrpc/hostoptions" + "github.com/radius-project/radius/pkg/components/database/databaseprovider" + "github.com/radius-project/radius/pkg/components/queue/queueprovider" + "github.com/radius-project/radius/pkg/components/secret/secretprovider" + "github.com/radius-project/radius/pkg/components/testhost" + "github.com/radius-project/radius/pkg/dynamicrp" + "github.com/radius-project/radius/pkg/dynamicrp/server" + "github.com/radius-project/radius/pkg/sdk" + "github.com/radius-project/radius/pkg/ucp" + "github.com/radius-project/radius/pkg/ucp/config" + ucptesthost "github.com/radius-project/radius/pkg/ucp/testhost" + "github.com/stretchr/testify/require" +) + +// TestHostOptions supports configuring the dynamic-rp test host. +type TestHostOption interface { + // Apply applies the option to the dynamic-rp options. + Apply(options *dynamicrp.Options) +} + +// TestHostOptionFunc is a function that implements the TestHostOption interface. +type TestHostOptionFunc func(options *dynamicrp.Options) + +// Apply applies the function to the dynamic-rp options. +func (f TestHostOptionFunc) Apply(options *dynamicrp.Options) { + f(options) +} + +// TestHost provides a test host for the dynamic-rp server. +type TestHost struct { + *testhost.TestHost +} + +func Start(t *testing.T, opts ...TestHostOption) (*TestHost, *ucptesthost.TestHost) { + config := &dynamicrp.Config{ + Database: databaseprovider.Options{ + Provider: databaseprovider.TypeInMemory, + }, + Environment: hostoptions.EnvironmentOptions{ + Name: "test", + RoleLocation: v1.LocationGlobal, + }, + Queue: queueprovider.QueueProviderOptions{ + Provider: queueprovider.TypeInmemory, + Name: "dynamic-rp", + }, + Secrets: secretprovider.SecretProviderOptions{ + Provider: secretprovider.TypeInMemorySecret, + }, + Server: hostoptions.ServerOptions{ + // Initialized dynamically when the server is started. + }, + UCP: config.UCPOptions{ + Kind: config.UCPConnectionKindDirect, + Direct: &config.UCPDirectConnectionOptions{ + Endpoint: "http://localhost:65000", // Initialized dynamically when the server is started. + }, + }, + } + + options, err := dynamicrp.NewOptions(context.Background(), config) + require.NoError(t, err) + + for _, opt := range opts { + opt.Apply(options) + } + + return StartWithOptions(t, options) +} + +// StartWithOptions uses the provided options to start the dynamic-rp test host and an instance of UCP +// configured to route traffic to the dynamic-rp test host. +// +// Manually configuring the server information other than the port is not supported. +func StartWithOptions(t *testing.T, options *dynamicrp.Options) (*TestHost, *ucptesthost.TestHost) { + options.Config.Server.Host = "localhost" + options.Config.Server.PathBase = "/" + uuid.New().String() + if options.Config.Server.Port == 0 { + options.Config.Server.Port = testhost.AllocateFreePort(t) + } + + // Allocate a port for UCP. + ucpPort := testhost.AllocateFreePort(t) + options.Config.UCP.Kind = config.UCPConnectionKindDirect + options.Config.UCP.Direct = &config.UCPDirectConnectionOptions{Endpoint: fmt.Sprintf("http://localhost:%d", ucpPort)} + + var err error + options.UCP, err = sdk.NewDirectConnection(options.Config.UCP.Direct.Endpoint) + require.NoError(t, err) + + baseURL := fmt.Sprintf( + "http://%s%s", + options.Config.Server.Address(), + options.Config.Server.PathBase) + baseURL = strings.TrimSuffix(baseURL, "/") + + host, err := server.NewServer(options) + require.NoError(t, err, "failed to create server") + + th := testhost.StartHost(t, host, baseURL) + return &TestHost{th}, startUCP(t, baseURL, ucpPort) +} + +func startUCP(t *testing.T, dynamicRPURL string, ucpPort int) *ucptesthost.TestHost { + return ucptesthost.Start(t, ucptesthost.TestHostOptionFunc(func(options *ucp.Options) { + // Initialize UCP with its listening port + options.Config.Server.Port = ucpPort + + // Intitialize UCP with the dynamic-rp URL + options.Config.Routing.DefaultDownstreamEndpoint = dynamicRPURL + })) +} diff --git a/pkg/recipes/controllerconfig/config.go b/pkg/recipes/controllerconfig/config.go index e2f60fc312..40fbd98a2d 100644 --- a/pkg/recipes/controllerconfig/config.go +++ b/pkg/recipes/controllerconfig/config.go @@ -76,6 +76,14 @@ func New(options hostoptions.HostOptions) (*RecipeControllerConfig, error) { return nil, err } + if options.Config.Bicep.DeleteRetryCount == "" { + options.Config.Bicep.DeleteRetryCount = "3" + } + + if options.Config.Bicep.DeleteRetryDelaySeconds == "" { + options.Config.Bicep.DeleteRetryDelaySeconds = "10" + } + bicepDeleteRetryCount, err := strconv.Atoi(options.Config.Bicep.DeleteRetryCount) if err != nil { return nil, err diff --git a/pkg/server/asyncworker.go b/pkg/server/asyncworker.go index 2531c6b0a9..cc7ab8da1e 100644 --- a/pkg/server/asyncworker.go +++ b/pkg/server/asyncworker.go @@ -21,9 +21,12 @@ import ( "fmt" ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" + "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" "github.com/radius-project/radius/pkg/armrpc/asyncoperation/worker" "github.com/radius-project/radius/pkg/armrpc/builder" "github.com/radius-project/radius/pkg/armrpc/hostoptions" + "github.com/radius-project/radius/pkg/components/database/databaseprovider" + "github.com/radius-project/radius/pkg/components/queue/queueprovider" "github.com/radius-project/radius/pkg/corerp/backend/deployment" "github.com/radius-project/radius/pkg/corerp/model" "github.com/radius-project/radius/pkg/kubeutil" @@ -33,17 +36,18 @@ import ( type AsyncWorker struct { worker.Service + options hostoptions.HostOptions handlerBuilder []builder.Builder } // NewAsyncWorker creates new service instance to run AsyncRequestProcessWorker. func NewAsyncWorker(options hostoptions.HostOptions, builder []builder.Builder) *AsyncWorker { return &AsyncWorker{ - Service: worker.Service{ - ProviderName: "radius", - Options: options, - }, + options: options, handlerBuilder: builder, + Service: worker.Service{ + // Will be initialized later + }, } } @@ -52,51 +56,73 @@ func (w *AsyncWorker) Name() string { return "radiusasyncworker" } -// Run starts the service and worker. -func (w *AsyncWorker) Run(ctx context.Context) error { - if err := w.Init(ctx); err != nil { +func (w *AsyncWorker) init(ctx context.Context) error { + workerOptions := worker.Options{} + if w.options.Config.WorkerServer != nil { + if w.options.Config.WorkerServer.MaxOperationConcurrency != nil { + workerOptions.MaxOperationConcurrency = *w.options.Config.WorkerServer.MaxOperationConcurrency + } + if w.options.Config.WorkerServer.MaxOperationRetryCount != nil { + workerOptions.MaxOperationRetryCount = *w.options.Config.WorkerServer.MaxOperationRetryCount + } + } + + queueProvider := queueprovider.New(w.options.Config.QueueProvider) + databaseProvider := databaseprovider.FromOptions(w.options.Config.DatabaseProvider) + + databaseClient, err := databaseProvider.GetClient(ctx) + if err != nil { + return err + } + + queueClient, err := queueProvider.GetClient(ctx) + if err != nil { return err } - k8s, err := kubeutil.NewClients(w.Options.K8sConfig) + statusManager := statusmanager.New(databaseClient, queueClient, w.options.Config.Env.RoleLocation) + + w.Service = worker.Service{ + DatabaseClient: databaseClient, + OperationStatusManager: statusManager, + Options: workerOptions, + QueueClient: queueClient, + } + + return nil +} + +// Run starts the service and worker. +func (w *AsyncWorker) Run(ctx context.Context) error { + k8s, err := kubeutil.NewClients(w.options.K8sConfig) if err != nil { return fmt.Errorf("failed to initialize kubernetes clients: %w", err) } - appModel, err := model.NewApplicationModel(w.Options.Arm, k8s.RuntimeClient, k8s.ClientSet, k8s.DiscoveryClient, k8s.DynamicClient) + appModel, err := model.NewApplicationModel(w.options.Arm, k8s.RuntimeClient, k8s.ClientSet, k8s.DiscoveryClient, k8s.DynamicClient) if err != nil { return fmt.Errorf("failed to initialize application model: %w", err) } - databaseClient, err := w.DatabaseProvider.GetClient(ctx) + err = w.init(ctx) if err != nil { - return err + return fmt.Errorf("failed to initialize async worker: %w", err) } for _, b := range w.handlerBuilder { opts := ctrl.Options{ - DatabaseClient: databaseClient, + DatabaseClient: w.DatabaseClient, KubeClient: k8s.RuntimeClient, GetDeploymentProcessor: func() deployment.DeploymentProcessor { - return deployment.NewDeploymentProcessor(appModel, databaseClient, k8s.RuntimeClient, k8s.ClientSet) + return deployment.NewDeploymentProcessor(appModel, w.DatabaseClient, k8s.RuntimeClient, k8s.ClientSet) }, } - err := b.ApplyAsyncHandler(ctx, w.Controllers, opts) + err := b.ApplyAsyncHandler(ctx, w.Controllers(), opts) if err != nil { panic(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 - } - } - - return w.Start(ctx, workerOpts) + return w.Start(ctx) } diff --git a/pkg/ucp/backend/service.go b/pkg/ucp/backend/service.go index c61ed873b6..acb284191f 100644 --- a/pkg/ucp/backend/service.go +++ b/pkg/ucp/backend/service.go @@ -19,88 +19,82 @@ package backend import ( "context" "errors" - "fmt" "net/http" "net/url" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" "github.com/radius-project/radius/pkg/armrpc/asyncoperation/worker" - "github.com/radius-project/radius/pkg/armrpc/hostoptions" "github.com/radius-project/radius/pkg/sdk" + "github.com/radius-project/radius/pkg/ucp" "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" "github.com/radius-project/radius/pkg/ucp/backend/controller/resourcegroups" "github.com/radius-project/radius/pkg/ucp/backend/controller/resourceproviders" "github.com/radius-project/radius/pkg/ucp/datamodel" - ucpoptions "github.com/radius-project/radius/pkg/ucp/hostoptions" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" ) -const ( - UCPProviderName = "System.Resources" -) - // Service is a service to run AsyncReqeustProcessWorker. type Service struct { worker.Service - - config ucpoptions.UCPConfig + options *ucp.Options } -// NewService creates new service instance to run AsyncRequestProcessWorker. -func NewService(options hostoptions.HostOptions, config ucpoptions.UCPConfig) *Service { +// NewService creates new backend service instance to run the async worker. +func NewService(options *ucp.Options) *Service { return &Service{ + options: options, Service: worker.Service{ - ProviderName: UCPProviderName, - Options: options, + // Will be initialized later. + }, - config: config, } } -// Name returns a string containing the UCPProviderName and the text "async worker". +// Name returns the service name. func (w *Service) Name() string { - return fmt.Sprintf("%s async worker", UCPProviderName) + return "ucp async worker" } -// 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. +// Run starts the background worker. func (w *Service) Run(ctx context.Context) error { - if err := w.Init(ctx); err != nil { - return err + if w.options.Config.Worker.MaxOperationConcurrency != nil { + w.Service.Options.MaxOperationConcurrency = *w.options.Config.Worker.MaxOperationConcurrency + } + if w.options.Config.Worker.MaxOperationRetryCount != nil { + w.Service.Options.MaxOperationRetryCount = *w.options.Config.Worker.MaxOperationRetryCount } - 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 - } + databaseClient, err := w.options.DatabaseProvider.GetClient(ctx) + if err != nil { + return err } - databaseClient, err := w.DatabaseProvider.GetClient(ctx) + queueClient, err := w.options.QueueProvider.GetClient(ctx) if err != nil { return err } + w.Service.DatabaseClient = databaseClient + w.Service.QueueClient = queueClient + w.Service.OperationStatusManager = w.options.StatusManager + opts := ctrl.Options{ DatabaseClient: databaseClient, } - defaultDownstream, err := url.Parse(w.config.Routing.DefaultDownstreamEndpoint) + defaultDownstream, err := url.Parse(w.options.Config.Routing.DefaultDownstreamEndpoint) if err != nil { return err } transport := otelhttp.NewTransport(http.DefaultTransport) - err = RegisterControllers(w.Controllers, w.Options.UCPConnection, transport, opts, defaultDownstream) + err = RegisterControllers(w.Controllers(), w.options.UCP, transport, opts, defaultDownstream) if err != nil { return err } - return w.Start(ctx, workerOpts) + return w.Start(ctx) } // RegisterControllers registers the controllers for the UCP backend. diff --git a/pkg/ucp/config.go b/pkg/ucp/config.go new file mode 100644 index 0000000000..7ef4a8ff56 --- /dev/null +++ b/pkg/ucp/config.go @@ -0,0 +1,153 @@ +/* +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 ucp + +import ( + "bytes" + + "github.com/radius-project/radius/pkg/armrpc/hostoptions" + "github.com/radius-project/radius/pkg/components/database/databaseprovider" + "github.com/radius-project/radius/pkg/components/queue/queueprovider" + "github.com/radius-project/radius/pkg/components/secret/secretprovider" + metricsprovider "github.com/radius-project/radius/pkg/metrics/provider" + profilerprovider "github.com/radius-project/radius/pkg/profiler/provider" + "github.com/radius-project/radius/pkg/trace" + ucpconfig "github.com/radius-project/radius/pkg/ucp/config" + "github.com/radius-project/radius/pkg/ucp/ucplog" + "gopkg.in/yaml.v3" +) + +// Config defines the configuration for the UCP server. +// +// For testability, all fields on this struct MUST be parsable from YAML without any further initialization required. +type Config struct { + // Database is the configuration for the database used for resource data. + Database databaseprovider.Options `yaml:"databaseProvider"` + + // Environment is the configuration for the hosting environment. + Environment hostoptions.EnvironmentOptions `yaml:"environment"` + + // Identity is the configuration for authenticating with external systems like Azure and AWS. + Identity IdentityConfig `yaml:"identity"` + + // Initialization is the configuration for initializing the UCP server. + Initialization InitializationConfig `yaml:"initialization"` + + // Logging is the configuration for the logging system. + Logging ucplog.LoggingOptions `yaml:"logging"` + + // Metrics is the configuration for the metrics endpoint. + Metrics metricsprovider.MetricsProviderOptions `yaml:"metricsProvider"` + + // Profiler is the configuration for the profiler endpoint. + Profiler profilerprovider.ProfilerProviderOptions `yaml:"profilerProvider"` + + // Routing is the configuration for UCP routing. + Routing RoutingConfig `yaml:"routing"` + + // Queue is the configuration for the message queue. + Queue queueprovider.QueueProviderOptions `yaml:"queueProvider"` + + // Secrets is the configuration for the secret storage system. + Secrets secretprovider.SecretProviderOptions `yaml:"secretProvider"` + + // Server is the configuration for the HTTP server. + Server hostoptions.ServerOptions `yaml:"server"` + + // Tracing is the configuration for the tracing system. + Tracing trace.Options `yaml:"tracerProvider"` + + // UCPConfig is the configuration for the connection to UCP. + UCP ucpconfig.UCPOptions `yaml:"ucp"` + + // Worker is the configuration for the backend worker server. + Worker hostoptions.WorkerServerOptions `yaml:"workerServer"` +} + +const ( + // AuthUCPCredential is the authentication method via UCP Credential API. + AuthUCPCredential = "UCPCredential" + + // AuthDefault is the default authentication method, such as environment variables. + AuthDefault = "default" +) + +// Identity represents configuration options for authenticating with external systems like Azure and AWS. +type IdentityConfig struct { + // AuthMethod represents the method of authentication for authenticating with external systems like Azure and AWS. + AuthMethod string `yaml:"authMethod"` +} + +// RoutingConfig provides configuration for UCP routing. +type RoutingConfig struct { + // DefaultDownstreamEndpoint is the default destination when a resource provider does not provide a downstream endpoint. + // In practice, this points to the URL of dynamic-rp. + DefaultDownstreamEndpoint string `yaml:"defaultDownstreamEndpoint"` +} + +// InitializeConfig defines the configuration for initializing the UCP server. +// +// This includes resources that are added to UCP's data on startup. +// +// TODO: this will be generalized as part of the UDT work. Right now it only +// handles planes, and we need to support other kinds of resources. +type InitializationConfig struct { + // Planes is a list of planes to create at startup. + Planes []Plane `yaml:"planes,omitempty"` +} + +// Plane is a configuration entry for a plane resource. This is used to create a plane resource at startup. +type Plane struct { + // ID is the resource ID of the plane. + ID string `json:"id" yaml:"id"` + + // Type is the resource type of the plane. + Type string `json:"type" yaml:"type"` + + // Name is the resource name of the plane. + Name string `json:"name" yaml:"name"` + + // Properties is the properties of the plane. + Properties PlaneProperties `json:"properties" yaml:"properties"` +} + +type PlaneProperties struct { + // ResourceProviders is a map of resource provider namespaces to their respective addresses. + // + // This is part of legacy (non-UDT) support for planes and will be removed. + ResourceProviders map[string]string `json:"resourceProviders" yaml:"resourceProviders"` + + // Kind is the legacy UCP plane type. + Kind string `json:"kind" yaml:"kind"` + + // URL is the downsteam URL for the plane. + URL string `json:"url" yaml:"url"` +} + +// LoadConfig loads a Config from bytes. +func LoadConfig(bs []byte) (*Config, error) { + decoder := yaml.NewDecoder(bytes.NewBuffer(bs)) + decoder.KnownFields(true) + + config := Config{} + err := decoder.Decode(&config) + if err != nil { + return nil, err + } + + return &config, nil +} diff --git a/pkg/ucp/config/ucpoptions.go b/pkg/ucp/config/ucpoptions.go index 57583033ae..785a2efcc7 100644 --- a/pkg/ucp/config/ucpoptions.go +++ b/pkg/ucp/config/ucpoptions.go @@ -59,6 +59,9 @@ func NewConnectionFromUCPConfig(option *UCPOptions, k8sConfig *rest.Config) (sdk return nil, errors.New("the property .ucp.direct.endpoint is required when using a direct connection") } return sdk.NewDirectConnection(option.Direct.Endpoint) + } else if option.Kind == UCPConnectionKindKubernetes { + return sdk.NewKubernetesConnectionFromConfig(k8sConfig) } - return sdk.NewKubernetesConnectionFromConfig(k8sConfig) + + return nil, errors.New("invalid connection kind: " + option.Kind) } diff --git a/pkg/ucp/datamodel/resourcegroup.go b/pkg/ucp/datamodel/resourcegroup.go index 0272b954a6..353e2b4a3e 100644 --- a/pkg/ucp/datamodel/resourcegroup.go +++ b/pkg/ucp/datamodel/resourcegroup.go @@ -18,6 +18,11 @@ package datamodel import v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" +const ( + // ResourceGroupResourceType is the type of a resource group. + ResourceGroupResourceType = "System.Resources/resourceGroups" +) + // ResourceGroup represents UCP ResourceGroup. type ResourceGroup struct { v1.BaseResource @@ -25,5 +30,5 @@ type ResourceGroup struct { // ResourceTypeName returns a string representing the resource type name of the ResourceGroup object. func (p ResourceGroup) ResourceTypeName() string { - return "System.Resources/resourceGroups" + return ResourceGroupResourceType } diff --git a/pkg/ucp/doc.go b/pkg/ucp/doc.go new file mode 100644 index 0000000000..44a63b58c4 --- /dev/null +++ b/pkg/ucp/doc.go @@ -0,0 +1,19 @@ +/* +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. +*/ + +// ucp holds the configuration and options types for UCP. See the packages nested inside this +// one for the UCP implementation. +package ucp diff --git a/pkg/ucp/frontend/api/routes.go b/pkg/ucp/frontend/api/routes.go index b8e732cb79..a1c050f44d 100644 --- a/pkg/ucp/frontend/api/routes.go +++ b/pkg/ucp/frontend/api/routes.go @@ -26,6 +26,7 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/frontend/server" + "github.com/radius-project/radius/pkg/ucp" kubernetes_ctrl "github.com/radius-project/radius/pkg/ucp/frontend/controller/kubernetes" planes_ctrl "github.com/radius-project/radius/pkg/ucp/frontend/controller/planes" "github.com/radius-project/radius/pkg/ucp/frontend/modules" @@ -74,9 +75,9 @@ func initModules(ctx context.Context, mods []modules.Initializer) (map[string]ht } // Register registers the routes for UCP including modules. -func Register(ctx context.Context, router chi.Router, planeModules []modules.Initializer, options modules.Options) error { +func Register(ctx context.Context, router chi.Router, planeModules []modules.Initializer, options *ucp.Options) error { logger := ucplog.FromContextOrDiscard(ctx) - logger.Info(fmt.Sprintf("Registering routes with path base: %s", options.PathBase)) + logger.Info(fmt.Sprintf("Registering routes with path base: %s", options.Config.Server.PathBase)) router.NotFound(validator.APINotFoundHandler()) router.MethodNotAllowed(validator.APIMethodNotAllowedHandler()) @@ -89,7 +90,7 @@ func Register(ctx context.Context, router chi.Router, planeModules []modules.Ini handlerOptions := []server.HandlerOptions{} // If we're in Kubernetes we have some required routes to implement. - if options.PathBase != "" { + if options.Config.Server.PathBase != "" { // NOTE: the Kubernetes API Server does not include the gvr (base path) in // the URL for swagger routes. handlerOptions = append(handlerOptions, []server.HandlerOptions{ @@ -111,7 +112,7 @@ func Register(ctx context.Context, router chi.Router, planeModules []modules.Ini }, { ParentRouter: router, - Path: options.PathBase, + Path: options.Config.Server.PathBase, OperationType: &v1.OperationType{Type: OperationTypeKubernetesDiscoveryDoc, Method: v1.OperationGet}, ResourceType: OperationTypeKubernetesDiscoveryDoc, Method: v1.OperationGet, @@ -127,7 +128,7 @@ func Register(ctx context.Context, router chi.Router, planeModules []modules.Ini }) // Configures planes collection and resource routes. - planeCollectionRouter := server.NewSubrouter(router, options.PathBase+planeCollectionPath, apiValidator) + planeCollectionRouter := server.NewSubrouter(router, options.Config.Server.PathBase+planeCollectionPath, apiValidator) // The "list all planes by type" handler is registered here. handlerOptions = append(handlerOptions, []server.HandlerOptions{ @@ -148,10 +149,13 @@ func Register(ctx context.Context, router chi.Router, planeModules []modules.Ini } ctrlOptions := controller.Options{ - Address: options.Address, - PathBase: options.PathBase, + Address: options.Config.Server.Address(), DatabaseClient: databaseClient, + PathBase: options.Config.Server.PathBase, StatusManager: options.StatusManager, + + KubeClient: nil, // Unused by UCP + ResourceType: "", // Set dynamically } for _, h := range handlerOptions { @@ -161,7 +165,7 @@ func Register(ctx context.Context, router chi.Router, planeModules []modules.Ini } // Register a catch-all route to handle requests that get dispatched to a specific plane. - unknownPlaneRouter := server.NewSubrouter(router, options.PathBase+planeTypeCollectionPath) + unknownPlaneRouter := server.NewSubrouter(router, options.Config.Server.PathBase+planeTypeCollectionPath) unknownPlaneRouter.HandleFunc(server.CatchAllPath, func(w http.ResponseWriter, r *http.Request) { planeType := chi.URLParam(r, "planeType") handler, ok := moduleHandlers[planeType] diff --git a/pkg/ucp/frontend/api/routes_test.go b/pkg/ucp/frontend/api/routes_test.go index 46ee16b30c..fe91a73422 100644 --- a/pkg/ucp/frontend/api/routes_test.go +++ b/pkg/ucp/frontend/api/routes_test.go @@ -24,8 +24,11 @@ import ( "github.com/go-chi/chi/v5" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" + "github.com/radius-project/radius/pkg/armrpc/hostoptions" "github.com/radius-project/radius/pkg/armrpc/rpctest" "github.com/radius-project/radius/pkg/components/database/databaseprovider" + "github.com/radius-project/radius/pkg/components/secret/secretprovider" + "github.com/radius-project/radius/pkg/ucp" "github.com/radius-project/radius/pkg/ucp/frontend/modules" "github.com/radius-project/radius/test/testcontext" "github.com/stretchr/testify/require" @@ -79,10 +82,16 @@ func Test_Routes(t *testing.T) { }, } - options := modules.Options{ - Address: "localhost", - PathBase: pathBase, + options := &ucp.Options{ + Config: &ucp.Config{ + Server: hostoptions.ServerOptions{ + Host: "localhost", + Port: 8080, + PathBase: pathBase, + }, + }, DatabaseProvider: databaseprovider.FromMemory(), + SecretProvider: secretprovider.NewSecretProvider(secretprovider.SecretProviderOptions{Provider: secretprovider.TypeInMemorySecret}), StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), } @@ -95,10 +104,16 @@ func Test_Routes(t *testing.T) { func Test_Route_ToModule(t *testing.T) { pathBase := "/some-path-base" - options := modules.Options{ - Address: "localhost", - PathBase: pathBase, + options := &ucp.Options{ + Config: &ucp.Config{ + Server: hostoptions.ServerOptions{ + Host: "localhost", + Port: 8080, + PathBase: pathBase, + }, + }, DatabaseProvider: databaseprovider.FromMemory(), + SecretProvider: secretprovider.NewSecretProvider(secretprovider.SecretProviderOptions{Provider: secretprovider.TypeInMemorySecret}), StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), } diff --git a/pkg/ucp/frontend/api/server.go b/pkg/ucp/frontend/api/server.go index bd851d967a..3dc47ad9b3 100644 --- a/pkg/ucp/frontend/api/server.go +++ b/pkg/ucp/frontend/api/server.go @@ -26,15 +26,11 @@ import ( "strings" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" - "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" armrpc_controller "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/frontend/defaultoperation" "github.com/radius-project/radius/pkg/armrpc/servicecontext" - "github.com/radius-project/radius/pkg/components/database/databaseprovider" - "github.com/radius-project/radius/pkg/components/queue/queueprovider" - "github.com/radius-project/radius/pkg/components/secret/secretprovider" "github.com/radius-project/radius/pkg/middleware" - "github.com/radius-project/radius/pkg/sdk" + "github.com/radius-project/radius/pkg/ucp" "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/datamodel/converter" aws_frontend "github.com/radius-project/radius/pkg/ucp/frontend/aws" @@ -43,54 +39,21 @@ import ( radius_frontend "github.com/radius-project/radius/pkg/ucp/frontend/radius" "github.com/radius-project/radius/pkg/ucp/frontend/versions" "github.com/radius-project/radius/pkg/ucp/hosting" - "github.com/radius-project/radius/pkg/ucp/hostoptions" "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/rest" "github.com/radius-project/radius/pkg/ucp/ucplog" - "github.com/radius-project/radius/pkg/validator" - "github.com/radius-project/radius/swagger" "github.com/go-chi/chi/v5" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" "go.opentelemetry.io/otel" ) -const ( - DefaultPlanesConfig = "DEFAULT_PLANES_CONFIG" -) - -type ServiceOptions struct { - // Config is the bootstrap configuration loaded from config file. - Config *hostoptions.UCPConfig - - ProviderName string - Address string - PathBase string - Configure func(chi.Router) - TLSCertDir string - DefaultPlanesConfigFile string - DatabaseProviderOptions databaseprovider.Options - SecretProviderOptions secretprovider.SecretProviderOptions - QueueProviderOptions queueprovider.QueueProviderOptions - InitialPlanes []rest.Plane - Identity hostoptions.Identity - UCPConnection sdk.Connection - Location string - - // Modules is a list of modules that will be registered with the router. - Modules []modules.Initializer -} - // Service implements the hosting.Service interface for the UCP frontend API. type Service struct { - options ServiceOptions - databaseProvider *databaseprovider.DatabaseProvider - queueProvider *queueprovider.QueueProvider - secretProvider *secretprovider.SecretProvider + options *ucp.Options } // DefaultModules returns a list of default modules that will be registered with the router. -func DefaultModules(options modules.Options) []modules.Initializer { +func DefaultModules(options *ucp.Options) []modules.Initializer { return []modules.Initializer{ aws_frontend.NewModule(options), azure_frontend.NewModule(options), @@ -101,7 +64,7 @@ func DefaultModules(options modules.Options) []modules.Initializer { var _ hosting.Service = (*Service)(nil) // NewService creates a server to serve UCP API requests. -func NewService(options ServiceOptions) *Service { +func NewService(options *ucp.Options) *Service { return &Service{ options: options, } @@ -118,57 +81,25 @@ func (s *Service) Name() string { func (s *Service) Initialize(ctx context.Context) (*http.Server, error) { r := chi.NewRouter() - s.databaseProvider = databaseprovider.FromOptions(s.options.DatabaseProviderOptions) - s.queueProvider = queueprovider.New(s.options.QueueProviderOptions) - s.secretProvider = secretprovider.NewSecretProvider(s.options.SecretProviderOptions) - - specLoader, err := validator.LoadSpec(ctx, "ucp", swagger.SpecFilesUCP, []string{s.options.PathBase}, "") - if err != nil { - return nil, err + // Allow tests to override the default modules. + modules := s.options.Modules + if modules == nil { + // If unset, use the default modules. + modules = DefaultModules(s.options) } - databaseClient, err := s.databaseProvider.GetClient(ctx) + err := Register(ctx, r, modules, s.options) if err != nil { return nil, err } - queueClient, err := s.queueProvider.GetClient(ctx) - if err != nil { - return nil, err - } - - statusManager := statusmanager.New(databaseClient, queueClient, s.options.Location) - - moduleOptions := modules.Options{ - Address: s.options.Address, - PathBase: s.options.PathBase, - Config: s.options.Config, - Location: s.options.Location, - DatabaseProvider: s.databaseProvider, - QueueProvider: s.queueProvider, - SecretProvider: s.secretProvider, - SpecLoader: specLoader, - StatusManager: statusManager, - UCPConnection: s.options.UCPConnection, - } - - modules := DefaultModules(moduleOptions) - err = Register(ctx, r, modules, moduleOptions) - if err != nil { - return nil, err - } - - if s.options.Configure != nil { - s.options.Configure(r) - } - err = s.configureDefaultPlanes(ctx) if err != nil { return nil, err } app := http.Handler(r) - app = servicecontext.ARMRequestCtx(s.options.PathBase, "global")(app) + app = servicecontext.ARMRequestCtx(s.options.Config.Server.PathBase, s.options.Config.Environment.RoleLocation)(app) app = middleware.WithLogger(app) app = otelhttp.NewHandler( @@ -182,7 +113,7 @@ func (s *Service) Initialize(ctx context.Context) (*http.Server, error) { app = middleware.RemoveRemoteAddr(app) server := &http.Server{ - Addr: s.options.Address, + Addr: s.options.Config.Server.Address(), // Need to be able to respond to requests with planes and resourcegroups segments with any casing e.g.: /Planes, /resourceGroups // AWS SDK is case sensitive. Therefore, cannot use lowercase middleware. Therefore, introducing a new middleware that translates // the path for only these segments and preserves the case for the other parts of the path. @@ -197,7 +128,7 @@ func (s *Service) Initialize(ctx context.Context) (*http.Server, error) { // configureDefaultPlanes reads the configuration file specified by the env var to configure default planes into UCP func (s *Service) configureDefaultPlanes(ctx context.Context) error { - for _, plane := range s.options.InitialPlanes { + for _, plane := range s.options.Config.Initialization.Planes { err := s.createPlane(ctx, plane) if err != nil { return err @@ -207,7 +138,7 @@ func (s *Service) configureDefaultPlanes(ctx context.Context) error { return nil } -func (s *Service) createPlane(ctx context.Context, plane rest.Plane) error { +func (s *Service) createPlane(ctx context.Context, plane ucp.Plane) error { body, err := json.Marshal(plane) if err != nil { return err @@ -222,7 +153,7 @@ func (s *Service) createPlane(ctx context.Context, plane rest.Plane) error { return fmt.Errorf("invalid plane ID: %s", plane.ID) } - db, err := s.databaseProvider.GetClient(ctx) + db, err := s.options.DatabaseProvider.GetClient(ctx) if err != nil { return err } @@ -271,7 +202,7 @@ func (s *Service) createPlane(ctx context.Context, plane rest.Plane) error { // Wrap the request in an ARM RPC context because this call will bypass the middleware // that normally does this for us. - rpcContext, err := v1.FromARMRequest(request, s.options.PathBase, s.options.Location) + rpcContext, err := v1.FromARMRequest(request, s.options.Config.Server.PathBase, s.options.Config.Environment.RoleLocation) if err != nil { return err } @@ -301,11 +232,11 @@ func (s *Service) Run(ctx context.Context) error { _ = service.Shutdown(ctx) }() - logger.Info(fmt.Sprintf("listening on: '%s'...", s.options.Address)) - if s.options.TLSCertDir == "" { + logger.Info(fmt.Sprintf("listening on: '%s'...", s.options.Config.Server.Address())) + if s.options.Config.Server.TLSCertificateDirectory == "" { err = service.ListenAndServe() } else { - err = service.ListenAndServeTLS(s.options.TLSCertDir+"/tls.crt", s.options.TLSCertDir+"/tls.key") + err = service.ListenAndServeTLS(s.options.Config.Server.TLSCertificateDirectory+"/tls.crt", s.options.Config.Server.TLSCertificateDirectory+"/tls.key") } if err == http.ErrServerClosed { diff --git a/pkg/ucp/frontend/aws/module.go b/pkg/ucp/frontend/aws/module.go index 676b4d0966..776c1df0cd 100644 --- a/pkg/ucp/frontend/aws/module.go +++ b/pkg/ucp/frontend/aws/module.go @@ -18,6 +18,7 @@ package aws import ( "github.com/go-chi/chi/v5" + "github.com/radius-project/radius/pkg/ucp" ucp_aws "github.com/radius-project/radius/pkg/ucp/aws" "github.com/radius-project/radius/pkg/ucp/frontend/modules" "github.com/radius-project/radius/pkg/validator" @@ -32,7 +33,7 @@ const ( ) // NewModule creates a new AWS module. -func NewModule(options modules.Options) *Module { +func NewModule(options *ucp.Options) *Module { m := Module{options: options} m.router = chi.NewRouter() m.router.NotFound(validator.APINotFoundHandler()) @@ -45,7 +46,7 @@ var _ modules.Initializer = &Module{} // Module defines the module for AWS functionality. type Module struct { - options modules.Options + options *ucp.Options router chi.Router // AWSClients provides access to AWS services. This field can be overridden by tests. diff --git a/pkg/ucp/frontend/aws/routes.go b/pkg/ucp/frontend/aws/routes.go index 7923343d9d..8bf2ba9327 100644 --- a/pkg/ucp/frontend/aws/routes.go +++ b/pkg/ucp/frontend/aws/routes.go @@ -29,6 +29,7 @@ import ( "github.com/radius-project/radius/pkg/armrpc/frontend/defaultoperation" "github.com/radius-project/radius/pkg/armrpc/frontend/server" aztoken "github.com/radius-project/radius/pkg/azure/tokencredentials" + "github.com/radius-project/radius/pkg/ucp" "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" ucp_aws "github.com/radius-project/radius/pkg/ucp/aws" sdk_cred "github.com/radius-project/radius/pkg/ucp/credentials" @@ -37,7 +38,6 @@ import ( awsproxy_ctrl "github.com/radius-project/radius/pkg/ucp/frontend/controller/awsproxy" aws_credential_ctrl "github.com/radius-project/radius/pkg/ucp/frontend/controller/credentials/aws" planes_ctrl "github.com/radius-project/radius/pkg/ucp/frontend/controller/planes" - "github.com/radius-project/radius/pkg/ucp/hostoptions" "github.com/radius-project/radius/pkg/ucp/ucplog" "github.com/radius-project/radius/pkg/validator" ) @@ -80,7 +80,7 @@ func (m *Module) Initialize(ctx context.Context) (http.Handler, error) { } } - baseRouter := server.NewSubrouter(m.router, m.options.PathBase) + baseRouter := server.NewSubrouter(m.router, m.options.Config.Server.PathBase+"/") apiValidator := validator.APIValidator(validator.Options{ SpecLoader: m.options.SpecLoader, @@ -101,8 +101,8 @@ func (m *Module) Initialize(ctx context.Context) (http.Handler, error) { // This is a scope query so we can't use the default operation. ParentRouter: planeCollectionRouter, Method: v1.OperationList, - OperationType: &v1.OperationType{Type: datamodel.AWSPlaneResourceType, Method: v1.OperationList}, ResourceType: datamodel.AWSPlaneResourceType, + OperationType: &v1.OperationType{Type: datamodel.AWSPlaneResourceType, Method: v1.OperationList}, ControllerFactory: func(opts controller.Options) (controller.Controller, error) { return &planes_ctrl.ListPlanesByType[*datamodel.AWSPlane, datamodel.AWSPlane]{ Operation: controller.NewOperation(opts, planeResourceOptions), @@ -112,8 +112,8 @@ func (m *Module) Initialize(ctx context.Context) (http.Handler, error) { { ParentRouter: planeResourceRouter, Method: v1.OperationGet, - OperationType: &v1.OperationType{Type: datamodel.AWSPlaneResourceType, Method: v1.OperationGet}, ResourceType: datamodel.AWSPlaneResourceType, + OperationType: &v1.OperationType{Type: datamodel.AWSPlaneResourceType, Method: v1.OperationGet}, ControllerFactory: func(opts controller.Options) (controller.Controller, error) { return defaultoperation.NewGetResource(opts, planeResourceOptions) }, @@ -121,8 +121,8 @@ func (m *Module) Initialize(ctx context.Context) (http.Handler, error) { { ParentRouter: planeResourceRouter, Method: v1.OperationPut, - OperationType: &v1.OperationType{Type: datamodel.AWSPlaneResourceType, Method: v1.OperationPut}, ResourceType: datamodel.AWSPlaneResourceType, + OperationType: &v1.OperationType{Type: datamodel.AWSPlaneResourceType, Method: v1.OperationPut}, ControllerFactory: func(opts controller.Options) (controller.Controller, error) { return defaultoperation.NewDefaultSyncPut(opts, planeResourceOptions) }, @@ -130,8 +130,8 @@ func (m *Module) Initialize(ctx context.Context) (http.Handler, error) { { ParentRouter: planeResourceRouter, Method: v1.OperationDelete, - OperationType: &v1.OperationType{Type: datamodel.AWSPlaneResourceType, Method: v1.OperationDelete}, ResourceType: datamodel.AWSPlaneResourceType, + OperationType: &v1.OperationType{Type: datamodel.AWSPlaneResourceType, Method: v1.OperationDelete}, ControllerFactory: func(opts controller.Options) (controller.Controller, error) { return defaultoperation.NewDefaultSyncDelete(opts, planeResourceOptions) }, @@ -298,10 +298,13 @@ func (m *Module) Initialize(ctx context.Context) (http.Handler, error) { } ctrlOpts := controller.Options{ - Address: m.options.Address, - PathBase: m.options.PathBase, + Address: m.options.Config.Server.Address(), DatabaseClient: databaseClient, + PathBase: m.options.Config.Server.PathBase, StatusManager: m.options.StatusManager, + + KubeClient: nil, // Unused by AWS module + ResourceType: "", // Set dynamically } for _, h := range handlerOptions { @@ -318,8 +321,8 @@ func (m *Module) newAWSConfig(ctx context.Context) (aws.Config, error) { credProviders := []func(*config.LoadOptions) error{} switch m.options.Config.Identity.AuthMethod { - case hostoptions.AuthUCPCredential: - provider, err := sdk_cred.NewAWSCredentialProvider(m.options.SecretProvider, m.options.UCPConnection, &aztoken.AnonymousCredential{}) + case ucp.AuthUCPCredential: + provider, err := sdk_cred.NewAWSCredentialProvider(m.options.SecretProvider, m.options.UCP, &aztoken.AnonymousCredential{}) if err != nil { return aws.Config{}, err } diff --git a/pkg/ucp/frontend/aws/routes_test.go b/pkg/ucp/frontend/aws/routes_test.go index 8d65dfa39a..321e7e049d 100644 --- a/pkg/ucp/frontend/aws/routes_test.go +++ b/pkg/ucp/frontend/aws/routes_test.go @@ -26,14 +26,14 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" + "github.com/radius-project/radius/pkg/armrpc/hostoptions" "github.com/radius-project/radius/pkg/armrpc/rpctest" "github.com/radius-project/radius/pkg/components/database/databaseprovider" "github.com/radius-project/radius/pkg/components/secret" "github.com/radius-project/radius/pkg/components/secret/secretprovider" + "github.com/radius-project/radius/pkg/ucp" "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" "github.com/radius-project/radius/pkg/ucp/datamodel" - "github.com/radius-project/radius/pkg/ucp/frontend/modules" - "github.com/radius-project/radius/pkg/ucp/hostoptions" ) const pathBase = "/some-path-base" @@ -117,10 +117,14 @@ func Test_Routes(t *testing.T) { secretProvider := secretprovider.NewSecretProvider(secretprovider.SecretProviderOptions{}) secretProvider.SetClient(secretClient) - options := modules.Options{ - Address: "localhost", - PathBase: pathBase, - Config: &hostoptions.UCPConfig{}, + options := &ucp.Options{ + Config: &ucp.Config{ + Server: hostoptions.ServerOptions{ + Host: "localhost", + Port: 8080, + PathBase: pathBase, + }, + }, DatabaseProvider: databaseprovider.FromMemory(), SecretProvider: secretProvider, StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), diff --git a/pkg/ucp/frontend/azure/module.go b/pkg/ucp/frontend/azure/module.go index d956d6ce30..24404996f0 100644 --- a/pkg/ucp/frontend/azure/module.go +++ b/pkg/ucp/frontend/azure/module.go @@ -18,25 +18,25 @@ package azure import ( "github.com/go-chi/chi/v5" + "github.com/radius-project/radius/pkg/ucp" "github.com/radius-project/radius/pkg/ucp/frontend/modules" "github.com/radius-project/radius/pkg/validator" ) // NewModule creates a new Azure module. -func NewModule(options modules.Options) *Module { - m := Module{options: options} - m.router = chi.NewRouter() - m.router.NotFound(validator.APINotFoundHandler()) - m.router.MethodNotAllowed(validator.APIMethodNotAllowedHandler()) +func NewModule(options *ucp.Options) *Module { + router := chi.NewRouter() + router.NotFound(validator.APINotFoundHandler()) + router.MethodNotAllowed(validator.APIMethodNotAllowedHandler()) - return &Module{options: options, router: m.router} + return &Module{options: options, router: router} } var _ modules.Initializer = &Module{} // Module defines the module for Azure functionality. type Module struct { - options modules.Options + options *ucp.Options router chi.Router } diff --git a/pkg/ucp/frontend/azure/routes.go b/pkg/ucp/frontend/azure/routes.go index b77d70afe8..285d5a101b 100644 --- a/pkg/ucp/frontend/azure/routes.go +++ b/pkg/ucp/frontend/azure/routes.go @@ -49,7 +49,7 @@ func (m *Module) Initialize(ctx context.Context) (http.Handler, error) { return nil, err } - baseRouter := server.NewSubrouter(m.router, m.options.PathBase) + baseRouter := server.NewSubrouter(m.router, m.options.Config.Server.PathBase+"/") apiValidator := validator.APIValidator(validator.Options{ SpecLoader: m.options.SpecLoader, @@ -73,8 +73,8 @@ func (m *Module) Initialize(ctx context.Context) (http.Handler, error) { // This is a scope query so we can't use the default operation. ParentRouter: planeCollectionRouter, Method: v1.OperationList, - OperationType: &v1.OperationType{Type: datamodel.AzurePlaneResourceType, Method: v1.OperationList}, ResourceType: datamodel.AzurePlaneResourceType, + OperationType: &v1.OperationType{Type: datamodel.AzurePlaneResourceType, Method: v1.OperationList}, ControllerFactory: func(opts controller.Options) (controller.Controller, error) { return &planes_ctrl.ListPlanesByType[*datamodel.AzurePlane, datamodel.AzurePlane]{ Operation: controller.NewOperation(opts, planeResourceOptions), @@ -84,8 +84,8 @@ func (m *Module) Initialize(ctx context.Context) (http.Handler, error) { { ParentRouter: planeResourceRouter, Method: v1.OperationGet, - OperationType: &v1.OperationType{Type: datamodel.AzurePlaneResourceType, Method: v1.OperationGet}, ResourceType: datamodel.AzurePlaneResourceType, + OperationType: &v1.OperationType{Type: datamodel.AzurePlaneResourceType, Method: v1.OperationGet}, ControllerFactory: func(opts controller.Options) (controller.Controller, error) { return defaultoperation.NewGetResource(opts, planeResourceOptions) }, @@ -93,8 +93,8 @@ func (m *Module) Initialize(ctx context.Context) (http.Handler, error) { { ParentRouter: planeResourceRouter, Method: v1.OperationPut, - OperationType: &v1.OperationType{Type: datamodel.AzurePlaneResourceType, Method: v1.OperationPut}, ResourceType: datamodel.AzurePlaneResourceType, + OperationType: &v1.OperationType{Type: datamodel.AzurePlaneResourceType, Method: v1.OperationPut}, ControllerFactory: func(opts controller.Options) (controller.Controller, error) { return defaultoperation.NewDefaultSyncPut(opts, planeResourceOptions) }, @@ -102,8 +102,8 @@ func (m *Module) Initialize(ctx context.Context) (http.Handler, error) { { ParentRouter: planeResourceRouter, Method: v1.OperationDelete, - OperationType: &v1.OperationType{Type: datamodel.AzurePlaneResourceType, Method: v1.OperationDelete}, ResourceType: datamodel.AzurePlaneResourceType, + OperationType: &v1.OperationType{Type: datamodel.AzurePlaneResourceType, Method: v1.OperationDelete}, ControllerFactory: func(opts controller.Options) (controller.Controller, error) { return defaultoperation.NewDefaultSyncDelete(opts, planeResourceOptions) }, @@ -171,10 +171,13 @@ func (m *Module) Initialize(ctx context.Context) (http.Handler, error) { } ctrlOpts := controller.Options{ - Address: m.options.Address, - PathBase: m.options.PathBase, + Address: m.options.Config.Server.Address(), DatabaseClient: databaseClient, + PathBase: m.options.Config.Server.PathBase, StatusManager: m.options.StatusManager, + + KubeClient: nil, // Unused by Azure module + ResourceType: "", // Set dynamically } for _, h := range handlerOptions { diff --git a/pkg/ucp/frontend/azure/routes_test.go b/pkg/ucp/frontend/azure/routes_test.go index 818031e22c..50ecb61f1f 100644 --- a/pkg/ucp/frontend/azure/routes_test.go +++ b/pkg/ucp/frontend/azure/routes_test.go @@ -26,14 +26,14 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" + "github.com/radius-project/radius/pkg/armrpc/hostoptions" "github.com/radius-project/radius/pkg/armrpc/rpctest" "github.com/radius-project/radius/pkg/components/database/databaseprovider" "github.com/radius-project/radius/pkg/components/secret" "github.com/radius-project/radius/pkg/components/secret/secretprovider" + "github.com/radius-project/radius/pkg/ucp" "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" "github.com/radius-project/radius/pkg/ucp/datamodel" - "github.com/radius-project/radius/pkg/ucp/frontend/modules" - "github.com/radius-project/radius/pkg/ucp/hostoptions" ) const pathBase = "/some-path-base" @@ -91,10 +91,14 @@ func Test_Routes(t *testing.T) { secretProvider := secretprovider.NewSecretProvider(secretprovider.SecretProviderOptions{}) secretProvider.SetClient(secretClient) - options := modules.Options{ - Address: "localhost", - PathBase: pathBase, - Config: &hostoptions.UCPConfig{}, + options := &ucp.Options{ + Config: &ucp.Config{ + Server: hostoptions.ServerOptions{ + Host: "localhost", + Port: 8080, + PathBase: pathBase, + }, + }, DatabaseProvider: databaseprovider.FromMemory(), SecretProvider: secretProvider, StatusManager: statusmanager.NewMockStatusManager(gomock.NewController(t)), diff --git a/pkg/ucp/frontend/controller/resourcegroups/util.go b/pkg/ucp/frontend/controller/resourcegroups/util.go index a1e012208f..6c9ab112a3 100644 --- a/pkg/ucp/frontend/controller/resourcegroups/util.go +++ b/pkg/ucp/frontend/controller/resourcegroups/util.go @@ -155,7 +155,10 @@ func ValidateResourceType(ctx context.Context, client database.Client, id resour // Resource types are case-insensitive so we have to iterate. var locationResourceType *datamodel.LocationResourceTypeConfiguration - // We special-case two pseudo-resource types: "locations/operationstatuses" and "locations/operationresults". + // We special-case two pseudo-resource types: "operationstatuses" and "operationresults". + // + // These are implemented by all resource providers, and don't require the resource provider to register them. + // // If the resource type is one of these, we can return the downstream URL directly. if isOperationResourceType(id) { locationResourceType = &datamodel.LocationResourceTypeConfiguration{ @@ -215,10 +218,11 @@ func isOperationResourceType(id resources.ID) bool { return true } - // An older pattern is to use a child resource + // An older pattern is to use a child resource, it might also use the name "operations" typeSegments := id.TypeSegments() if len(typeSegments) >= 2 && (strings.EqualFold(typeSegments[len(typeSegments)-1].Type, "operationstatuses") || - strings.EqualFold(typeSegments[len(typeSegments)-1].Type, "operationresults")) { + strings.EqualFold(typeSegments[len(typeSegments)-1].Type, "operationresults") || + strings.EqualFold(typeSegments[len(typeSegments)-1].Type, "operations")) { return true } diff --git a/pkg/ucp/frontend/controller/resourcegroups/util_test.go b/pkg/ucp/frontend/controller/resourcegroups/util_test.go index 3512294633..3b80d21bc3 100644 --- a/pkg/ucp/frontend/controller/resourcegroups/util_test.go +++ b/pkg/ucp/frontend/controller/resourcegroups/util_test.go @@ -107,138 +107,138 @@ func Test_ValidateDownstream(t *testing.T) { }, } - mock := setup(t) - mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&database.Object{Data: resourceGroup}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), resourceTypeResource.ID).Return(&database.Object{Data: resourceTypeResource}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), locationResource.ID).Return(&database.Object{Data: locationResource}, nil).Times(1) + databaseClient := setup(t) + databaseClient.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&database.Object{Data: resourceGroup}, nil).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), resourceTypeResource.ID).Return(&database.Object{Data: resourceTypeResource}, nil).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), locationResource.ID).Return(&database.Object{Data: locationResource}, nil).Times(1) expectedURL, err := url.Parse(downstream) require.NoError(t, err) - downstreamURL, err := ValidateDownstream(testcontext.New(t), mock, id, location, apiVersion) + downstreamURL, err := ValidateDownstream(testcontext.New(t), databaseClient, id, location, apiVersion) require.NoError(t, err) require.Equal(t, expectedURL, downstreamURL) }) t.Run("success (non resource group)", func(t *testing.T) { - mock := setup(t) - mock.EXPECT().Get(gomock.Any(), idWithoutResourceGroup.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), resourceTypeResource.ID).Return(&database.Object{Data: resourceTypeResource}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), locationResource.ID).Return(&database.Object{Data: locationResource}, nil).Times(1) + databaseClient := setup(t) + databaseClient.EXPECT().Get(gomock.Any(), idWithoutResourceGroup.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), resourceTypeResource.ID).Return(&database.Object{Data: resourceTypeResource}, nil).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), locationResource.ID).Return(&database.Object{Data: locationResource}, nil).Times(1) expectedURL, err := url.Parse(downstream) require.NoError(t, err) - downstreamURL, err := ValidateDownstream(testcontext.New(t), mock, idWithoutResourceGroup, location, apiVersion) + downstreamURL, err := ValidateDownstream(testcontext.New(t), databaseClient, idWithoutResourceGroup, location, apiVersion) require.NoError(t, err) require.Equal(t, expectedURL, downstreamURL) }) // The deployment engine models its operation status resources as child resources of the deployment resource. t.Run("success (operationstatuses as child resource)", func(t *testing.T) { - mock := setup(t) - mock.EXPECT().Get(gomock.Any(), idWithoutResourceGroup.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), locationResource.ID).Return(&database.Object{Data: locationResource}, nil).Times(1) + databaseClient := setup(t) + databaseClient.EXPECT().Get(gomock.Any(), idWithoutResourceGroup.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), locationResource.ID).Return(&database.Object{Data: locationResource}, nil).Times(1) operationStatusID := resources.MustParse("/planes/radius/local/providers/System.TestRP/deployments/xzy/operationStatuses/abcd") expectedURL, err := url.Parse(downstream) require.NoError(t, err) - downstreamURL, err := ValidateDownstream(testcontext.New(t), mock, operationStatusID, location, apiVersion) + downstreamURL, err := ValidateDownstream(testcontext.New(t), databaseClient, operationStatusID, location, apiVersion) require.NoError(t, err) require.Equal(t, expectedURL, downstreamURL) }) // All of the Radius RPs include a location in the operation status child resource. t.Run("success (operationstatuses with location)", func(t *testing.T) { - mock := setup(t) - mock.EXPECT().Get(gomock.Any(), idWithoutResourceGroup.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), locationResource.ID).Return(&database.Object{Data: locationResource}, nil).Times(1) + databaseClient := setup(t) + databaseClient.EXPECT().Get(gomock.Any(), idWithoutResourceGroup.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), locationResource.ID).Return(&database.Object{Data: locationResource}, nil).Times(1) operationStatusID := resources.MustParse("/planes/radius/local/providers/System.TestRP/locations/east/operationStatuses/abcd") expectedURL, err := url.Parse(downstream) require.NoError(t, err) - downstreamURL, err := ValidateDownstream(testcontext.New(t), mock, operationStatusID, location, apiVersion) + downstreamURL, err := ValidateDownstream(testcontext.New(t), databaseClient, operationStatusID, location, apiVersion) require.NoError(t, err) require.Equal(t, expectedURL, downstreamURL) }) // The deployment engine models its operation result resources as child resources of the deployment resource. t.Run("success (operationresults as child resource)", func(t *testing.T) { - mock := setup(t) - mock.EXPECT().Get(gomock.Any(), idWithoutResourceGroup.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), locationResource.ID).Return(&database.Object{Data: locationResource}, nil).Times(1) + databaseClient := setup(t) + databaseClient.EXPECT().Get(gomock.Any(), idWithoutResourceGroup.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), locationResource.ID).Return(&database.Object{Data: locationResource}, nil).Times(1) operationResultID := resources.MustParse("/planes/radius/local/providers/System.TestRP/deployments/xzy/operationResults/abcd") expectedURL, err := url.Parse(downstream) require.NoError(t, err) - downstreamURL, err := ValidateDownstream(testcontext.New(t), mock, operationResultID, location, apiVersion) + downstreamURL, err := ValidateDownstream(testcontext.New(t), databaseClient, operationResultID, location, apiVersion) require.NoError(t, err) require.Equal(t, expectedURL, downstreamURL) }) // All of the Radius RPs include a location in the operation result child resource. t.Run("success (operationresults with location)", func(t *testing.T) { - mock := setup(t) - mock.EXPECT().Get(gomock.Any(), idWithoutResourceGroup.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), locationResource.ID).Return(&database.Object{Data: locationResource}, nil).Times(1) + databaseClient := setup(t) + databaseClient.EXPECT().Get(gomock.Any(), idWithoutResourceGroup.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), locationResource.ID).Return(&database.Object{Data: locationResource}, nil).Times(1) operationResultID := resources.MustParse("/planes/radius/local/providers/System.TestRP/locations/east/operationResults/abcd") expectedURL, err := url.Parse(downstream) require.NoError(t, err) - downstreamURL, err := ValidateDownstream(testcontext.New(t), mock, operationResultID, location, apiVersion) + downstreamURL, err := ValidateDownstream(testcontext.New(t), databaseClient, operationResultID, location, apiVersion) require.NoError(t, err) require.Equal(t, expectedURL, downstreamURL) }) t.Run("plane not found", func(t *testing.T) { - mock := setup(t) - mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(nil, &database.ErrNotFound{}).Times(1) + databaseClient := setup(t) + databaseClient.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(nil, &database.ErrNotFound{}).Times(1) - downstreamURL, err := ValidateDownstream(testcontext.New(t), mock, id, location, apiVersion) + downstreamURL, err := ValidateDownstream(testcontext.New(t), databaseClient, id, location, apiVersion) require.Error(t, err) require.Equal(t, &NotFoundError{Message: "plane \"/planes/radius/local\" not found"}, err) require.Nil(t, downstreamURL) }) t.Run("plane retreival failure", func(t *testing.T) { - mock := setup(t) + databaseClient := setup(t) expected := fmt.Errorf("failed to fetch plane \"/planes/radius/local\": %w", errors.New("test error")) - mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(nil, errors.New("test error")).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(nil, errors.New("test error")).Times(1) - downstreamURL, err := ValidateDownstream(testcontext.New(t), mock, id, location, apiVersion) + downstreamURL, err := ValidateDownstream(testcontext.New(t), databaseClient, id, location, apiVersion) require.Error(t, err) require.Equal(t, expected, err) require.Nil(t, downstreamURL) }) t.Run("resource group not found", func(t *testing.T) { - mock := setup(t) - mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(nil, &database.ErrNotFound{}).Times(1) + databaseClient := setup(t) + databaseClient.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), id.RootScope()).Return(nil, &database.ErrNotFound{}).Times(1) - downstreamURL, err := ValidateDownstream(testcontext.New(t), mock, id, location, apiVersion) + downstreamURL, err := ValidateDownstream(testcontext.New(t), databaseClient, id, location, apiVersion) require.Error(t, err) require.Equal(t, &NotFoundError{Message: "resource group \"/planes/radius/local/resourceGroups/test-group\" not found"}, err) require.Nil(t, downstreamURL) }) t.Run("resource group err", func(t *testing.T) { - mock := setup(t) + databaseClient := setup(t) - mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(nil, errors.New("test error")).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), id.RootScope()).Return(nil, errors.New("test error")).Times(1) - downstreamURL, err := ValidateDownstream(testcontext.New(t), mock, id, location, apiVersion) + downstreamURL, err := ValidateDownstream(testcontext.New(t), databaseClient, id, location, apiVersion) require.Error(t, err) require.Equal(t, "failed to fetch resource group \"/planes/radius/local/resourceGroups/test-group\": test error", err.Error()) require.Nil(t, downstreamURL) @@ -255,12 +255,12 @@ func Test_ValidateDownstream(t *testing.T) { expected := fmt.Errorf("failed to fetch resource type %q: %w", "System.TestRP/testResources", errors.New("test error")) - mock := setup(t) - mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&database.Object{Data: resourceGroup}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), resourceTypeResource.ID).Return(nil, errors.New("test error")).Times(1) + databaseClient := setup(t) + databaseClient.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&database.Object{Data: resourceGroup}, nil).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), resourceTypeResource.ID).Return(nil, errors.New("test error")).Times(1) - downstreamURL, err := ValidateDownstream(testcontext.New(t), mock, id, location, apiVersion) + downstreamURL, err := ValidateDownstream(testcontext.New(t), databaseClient, id, location, apiVersion) require.Error(t, err) require.Equal(t, expected, err) require.Nil(t, downstreamURL) @@ -277,13 +277,13 @@ func Test_ValidateDownstream(t *testing.T) { expected := fmt.Errorf("failed to fetch location %q: %w", locationResource.ID, errors.New("test error")) - mock := setup(t) - mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&database.Object{Data: resourceGroup}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), resourceTypeResource.ID).Return(&database.Object{Data: resourceTypeID}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), locationResource.ID).Return(nil, errors.New("test error")).Times(1) + databaseClient := setup(t) + databaseClient.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&database.Object{Data: resourceGroup}, nil).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), resourceTypeResource.ID).Return(&database.Object{Data: resourceTypeID}, nil).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), locationResource.ID).Return(nil, errors.New("test error")).Times(1) - downstreamURL, err := ValidateDownstream(testcontext.New(t), mock, id, location, apiVersion) + downstreamURL, err := ValidateDownstream(testcontext.New(t), databaseClient, id, location, apiVersion) require.Error(t, err) require.Equal(t, expected, err) require.Nil(t, downstreamURL) @@ -317,13 +317,13 @@ func Test_ValidateDownstream(t *testing.T) { }, } - mock := setup(t) - mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&database.Object{Data: resourceGroup}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), resourceTypeResource.ID).Return(&database.Object{Data: resourceTypeID}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), locationResource.ID).Return(&database.Object{Data: locationResource}, nil).Times(1) + databaseClient := setup(t) + databaseClient.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&database.Object{Data: resourceGroup}, nil).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), resourceTypeResource.ID).Return(&database.Object{Data: resourceTypeID}, nil).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), locationResource.ID).Return(&database.Object{Data: locationResource}, nil).Times(1) - downstreamURL, err := ValidateDownstream(testcontext.New(t), mock, id, location, apiVersion) + downstreamURL, err := ValidateDownstream(testcontext.New(t), databaseClient, id, location, apiVersion) require.Error(t, err) require.Equal(t, &InvalidError{Message: "resource type \"System.TestRP/testResources\" not supported by location \"east\""}, err) require.Nil(t, downstreamURL) @@ -357,13 +357,13 @@ func Test_ValidateDownstream(t *testing.T) { }, } - mock := setup(t) - mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&database.Object{Data: resourceGroup}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), resourceTypeResource.ID).Return(&database.Object{Data: resourceTypeID}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), locationResource.ID).Return(&database.Object{Data: locationResource}, nil).Times(1) + databaseClient := setup(t) + databaseClient.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&database.Object{Data: resourceGroup}, nil).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), resourceTypeResource.ID).Return(&database.Object{Data: resourceTypeID}, nil).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), locationResource.ID).Return(&database.Object{Data: locationResource}, nil).Times(1) - downstreamURL, err := ValidateDownstream(testcontext.New(t), mock, id, location, apiVersion) + downstreamURL, err := ValidateDownstream(testcontext.New(t), databaseClient, id, location, apiVersion) require.Error(t, err) require.Equal(t, &InvalidError{Message: "api version \"2025-01-01\" is not supported for resource type \"System.TestRP/testResources\" by location \"east\""}, err) require.Nil(t, downstreamURL) @@ -397,13 +397,13 @@ func Test_ValidateDownstream(t *testing.T) { }, } - mock := setup(t) - mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&database.Object{Data: resourceGroup}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), resourceTypeResource.ID).Return(&database.Object{Data: resourceTypeID}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), locationResource.ID).Return(&database.Object{Data: locationResource}, nil).Times(1) + databaseClient := setup(t) + databaseClient.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&database.Object{Data: resourceGroup}, nil).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), resourceTypeResource.ID).Return(&database.Object{Data: resourceTypeID}, nil).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), locationResource.ID).Return(&database.Object{Data: locationResource}, nil).Times(1) - downstreamURL, err := ValidateDownstream(testcontext.New(t), mock, id, location, apiVersion) + downstreamURL, err := ValidateDownstream(testcontext.New(t), databaseClient, id, location, apiVersion) require.Error(t, err) require.Equal(t, &InvalidError{Message: "failed to parse location address: parse \"\\ninvalid\": net/url: invalid control character in URL"}, err) require.Nil(t, downstreamURL) @@ -451,72 +451,72 @@ func Test_ValidateDownstream_Legacy(t *testing.T) { }, } - mock := setup(t) - mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&database.Object{Data: resourceGroup}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), resourceTypeID.String()).Return(nil, &database.ErrNotFound{}).Times(1) + databaseClient := setup(t) + databaseClient.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&database.Object{Data: resourceGroup}, nil).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), resourceTypeID.String()).Return(nil, &database.ErrNotFound{}).Times(1) expectedURL, err := url.Parse(downstream) require.NoError(t, err) - downstreamURL, err := ValidateDownstream(testcontext.New(t), mock, id, location, apiVersion) + downstreamURL, err := ValidateDownstream(testcontext.New(t), databaseClient, id, location, apiVersion) require.NoError(t, err) require.Equal(t, expectedURL, downstreamURL) }) t.Run("success (non resource group)", func(t *testing.T) { - mock := setup(t) - mock.EXPECT().Get(gomock.Any(), idWithoutResourceGroup.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), resourceTypeID.String()).Return(nil, &database.ErrNotFound{}).Times(1) + databaseClient := setup(t) + databaseClient.EXPECT().Get(gomock.Any(), idWithoutResourceGroup.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), resourceTypeID.String()).Return(nil, &database.ErrNotFound{}).Times(1) expectedURL, err := url.Parse(downstream) require.NoError(t, err) - downstreamURL, err := ValidateDownstream(testcontext.New(t), mock, idWithoutResourceGroup, location, apiVersion) + downstreamURL, err := ValidateDownstream(testcontext.New(t), databaseClient, idWithoutResourceGroup, location, apiVersion) require.NoError(t, err) require.Equal(t, expectedURL, downstreamURL) }) t.Run("plane not found", func(t *testing.T) { - mock := setup(t) - mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(nil, &database.ErrNotFound{}).Times(1) + databaseClient := setup(t) + databaseClient.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(nil, &database.ErrNotFound{}).Times(1) - downstreamURL, err := ValidateDownstream(testcontext.New(t), mock, id, location, apiVersion) + downstreamURL, err := ValidateDownstream(testcontext.New(t), databaseClient, id, location, apiVersion) require.Error(t, err) require.Equal(t, &NotFoundError{Message: "plane \"/planes/radius/local\" not found"}, err) require.Nil(t, downstreamURL) }) t.Run("plane retrieval failure", func(t *testing.T) { - mock := setup(t) + databaseClient := setup(t) expected := fmt.Errorf("failed to fetch plane \"/planes/radius/local\": %w", errors.New("test error")) - mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(nil, errors.New("test error")).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(nil, errors.New("test error")).Times(1) - downstreamURL, err := ValidateDownstream(testcontext.New(t), mock, id, location, apiVersion) + downstreamURL, err := ValidateDownstream(testcontext.New(t), databaseClient, id, location, apiVersion) require.Error(t, err) require.Equal(t, expected, err) require.Nil(t, downstreamURL) }) t.Run("resource group not found", func(t *testing.T) { - mock := setup(t) - mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(nil, &database.ErrNotFound{}).Times(1) + databaseClient := setup(t) + databaseClient.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), id.RootScope()).Return(nil, &database.ErrNotFound{}).Times(1) - downstreamURL, err := ValidateDownstream(testcontext.New(t), mock, id, location, apiVersion) + downstreamURL, err := ValidateDownstream(testcontext.New(t), databaseClient, id, location, apiVersion) require.Error(t, err) require.Equal(t, &NotFoundError{Message: "resource group \"/planes/radius/local/resourceGroups/test-group\" not found"}, err) require.Nil(t, downstreamURL) }) t.Run("resource group err", func(t *testing.T) { - mock := setup(t) + databaseClient := setup(t) - mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(nil, errors.New("test error")).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), id.RootScope()).Return(nil, errors.New("test error")).Times(1) - downstreamURL, err := ValidateDownstream(testcontext.New(t), mock, id, location, apiVersion) + downstreamURL, err := ValidateDownstream(testcontext.New(t), databaseClient, id, location, apiVersion) require.Error(t, err) require.Equal(t, "failed to fetch resource group \"/planes/radius/local/resourceGroups/test-group\": test error", err.Error()) require.Nil(t, downstreamURL) @@ -542,12 +542,12 @@ func Test_ValidateDownstream_Legacy(t *testing.T) { }, } - mock := setup(t) - mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&database.Object{Data: resourceGroup}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), resourceTypeID.String()).Return(nil, &database.ErrNotFound{}).Times(1) + databaseClient := setup(t) + databaseClient.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&database.Object{Data: resourceGroup}, nil).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), resourceTypeID.String()).Return(nil, &database.ErrNotFound{}).Times(1) - downstreamURL, err := ValidateDownstream(testcontext.New(t), mock, id, location, apiVersion) + downstreamURL, err := ValidateDownstream(testcontext.New(t), databaseClient, id, location, apiVersion) require.Error(t, err) require.Equal(t, &InvalidError{Message: "resource provider System.TestRP not configured"}, err) require.Nil(t, downstreamURL) @@ -573,12 +573,12 @@ func Test_ValidateDownstream_Legacy(t *testing.T) { }, } - mock := setup(t) - mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&database.Object{Data: resourceGroup}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), resourceTypeID.String()).Return(nil, &database.ErrNotFound{}).Times(1) + databaseClient := setup(t) + databaseClient.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&database.Object{Data: resourceGroup}, nil).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), resourceTypeID.String()).Return(nil, &database.ErrNotFound{}).Times(1) - downstreamURL, err := ValidateDownstream(testcontext.New(t), mock, id, location, apiVersion) + downstreamURL, err := ValidateDownstream(testcontext.New(t), databaseClient, id, location, apiVersion) require.Error(t, err) require.Equal(t, &InvalidError{Message: "resource provider System.TestRP not configured"}, err) require.Nil(t, downstreamURL) @@ -606,12 +606,12 @@ func Test_ValidateDownstream_Legacy(t *testing.T) { }, } - mock := setup(t) - mock.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&database.Object{Data: resourceGroup}, nil).Times(1) - mock.EXPECT().Get(gomock.Any(), resourceTypeID.String()).Return(nil, &database.ErrNotFound{}).Times(1) + databaseClient := setup(t) + databaseClient.EXPECT().Get(gomock.Any(), id.PlaneScope()).Return(&database.Object{Data: plane}, nil).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), id.RootScope()).Return(&database.Object{Data: resourceGroup}, nil).Times(1) + databaseClient.EXPECT().Get(gomock.Any(), resourceTypeID.String()).Return(nil, &database.ErrNotFound{}).Times(1) - downstreamURL, err := ValidateDownstream(testcontext.New(t), mock, id, location, apiVersion) + downstreamURL, err := ValidateDownstream(testcontext.New(t), databaseClient, id, location, apiVersion) require.Error(t, err) require.Equal(t, &InvalidError{Message: "failed to parse downstream URL: parse \"\\ninvalid\": net/url: invalid control character in URL"}, err) require.Nil(t, downstreamURL) diff --git a/pkg/ucp/frontend/modules/types.go b/pkg/ucp/frontend/modules/types.go index 3e1638302f..6b93a359d6 100644 --- a/pkg/ucp/frontend/modules/types.go +++ b/pkg/ucp/frontend/modules/types.go @@ -19,14 +19,6 @@ package modules import ( "context" "net/http" - - "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" - "github.com/radius-project/radius/pkg/components/database/databaseprovider" - "github.com/radius-project/radius/pkg/components/queue/queueprovider" - "github.com/radius-project/radius/pkg/components/secret/secretprovider" - "github.com/radius-project/radius/pkg/sdk" - "github.com/radius-project/radius/pkg/ucp/hostoptions" - "github.com/radius-project/radius/pkg/validator" ) // Initializer is an interface that can be implemented by modules that want to provide functionality for a plane. @@ -45,36 +37,3 @@ type Initializer interface { // - radius PlaneType() string } - -// Options defines the options for a module. -type Options struct { - // Config is the bootstrap configuration loaded from config file. - Config *hostoptions.UCPConfig - - // Address is the hostname + port of the server hosting UCP. - Address string - - // PathBase is the base path of the server as it appears in the URL. eg: '/apis/api.ucp.dev/v1alpha3'. - PathBase string - - // Location is the location of the server hosting UCP. - Location string - - // DatabaseProvider is the database provider. - DatabaseProvider *databaseprovider.DatabaseProvider - - // QeueueProvider provides access to the queue for async operations. - QueueProvider *queueprovider.QueueProvider - - // SecretProvider is the secret store provider used for managing credentials. - SecretProvider *secretprovider.SecretProvider - - // SpecLoader is the OpenAPI spec loader containing specs for the UCP APIs. - SpecLoader *validator.Loader - - // StatusManager is the async operation status manager. - StatusManager statusmanager.StatusManager - - // UCPConnection is the connection used to communicate with UCP APIs. - UCPConnection sdk.Connection -} diff --git a/pkg/ucp/frontend/radius/module.go b/pkg/ucp/frontend/radius/module.go index 811e5692d4..6b2a867429 100644 --- a/pkg/ucp/frontend/radius/module.go +++ b/pkg/ucp/frontend/radius/module.go @@ -18,12 +18,13 @@ package radius import ( "github.com/go-chi/chi/v5" + "github.com/radius-project/radius/pkg/ucp" "github.com/radius-project/radius/pkg/ucp/frontend/modules" "github.com/radius-project/radius/pkg/validator" ) // NewModule creates a new Radius module. -func NewModule(options modules.Options) *Module { +func NewModule(options *ucp.Options) *Module { router := chi.NewRouter() router.NotFound(validator.APINotFoundHandler()) router.MethodNotAllowed(validator.APIMethodNotAllowedHandler()) @@ -35,7 +36,7 @@ var _ modules.Initializer = &Module{} // Module defines the module for Radius functionality. type Module struct { - options modules.Options + options *ucp.Options router chi.Router defaultDownstream string } diff --git a/pkg/ucp/frontend/radius/routes.go b/pkg/ucp/frontend/radius/routes.go index 951a49af3a..84576da55b 100644 --- a/pkg/ucp/frontend/radius/routes.go +++ b/pkg/ucp/frontend/radius/routes.go @@ -70,14 +70,17 @@ func (m *Module) Initialize(ctx context.Context) (http.Handler, error) { } ctrlOptions := controller.Options{ - Address: m.options.Address, - PathBase: m.options.PathBase, + Address: m.options.Config.Server.Address(), DatabaseClient: databaseClient, + PathBase: m.options.Config.Server.PathBase, StatusManager: m.options.StatusManager, + + KubeClient: nil, // Unused by Radius module + ResourceType: "", // Set dynamically } // NOTE: we're careful where we use the `apiValidator` middleware. It's not used for the proxy routes. - m.router.Route(m.options.PathBase+"/planes/radius", func(r chi.Router) { + m.router.Route(m.options.Config.Server.PathBase+"/planes/radius", func(r chi.Router) { r.With(apiValidator).Get("/", capture(radiusPlaneListHandler(ctx, ctrlOptions))) r.Route("/{planeName}", func(r chi.Router) { r.With(apiValidator).Get("/", capture(radiusPlaneGetHandler(ctx, ctrlOptions))) diff --git a/pkg/ucp/frontend/radius/routes_test.go b/pkg/ucp/frontend/radius/routes_test.go index 8bca6bf56f..813d33ab6d 100644 --- a/pkg/ucp/frontend/radius/routes_test.go +++ b/pkg/ucp/frontend/radius/routes_test.go @@ -23,14 +23,14 @@ import ( "github.com/go-chi/chi/v5" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/armrpc/hostoptions" "github.com/radius-project/radius/pkg/armrpc/rpctest" "github.com/radius-project/radius/pkg/components/database/databaseprovider" "github.com/radius-project/radius/pkg/components/secret" "github.com/radius-project/radius/pkg/components/secret/secretprovider" + "github.com/radius-project/radius/pkg/ucp" "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" "github.com/radius-project/radius/pkg/ucp/datamodel" - "github.com/radius-project/radius/pkg/ucp/frontend/modules" - "github.com/radius-project/radius/pkg/ucp/hostoptions" "go.uber.org/mock/gomock" ) @@ -191,10 +191,14 @@ func Test_Routes(t *testing.T) { secretProvider := secretprovider.NewSecretProvider(secretprovider.SecretProviderOptions{}) secretProvider.SetClient(secretClient) - options := modules.Options{ - Address: "localhost", - PathBase: pathBase, - Config: &hostoptions.UCPConfig{}, + options := &ucp.Options{ + Config: &ucp.Config{ + Server: hostoptions.ServerOptions{ + Host: "localhost", + Port: 8080, + PathBase: pathBase, + }, + }, DatabaseProvider: databaseProvider, SecretProvider: secretProvider, } diff --git a/pkg/ucp/hostoptions/hostoptions.go b/pkg/ucp/hostoptions/hostoptions.go deleted file mode 100644 index db708f73b5..0000000000 --- a/pkg/ucp/hostoptions/hostoptions.go +++ /dev/null @@ -1,64 +0,0 @@ -/* -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. -*/ - -// hostoptions defines and reads options for the RP's execution environment. - -package hostoptions - -import ( - "bytes" - "fmt" - "os" - - "gopkg.in/yaml.v3" -) - -// HostOptions defines all of the settings that our RP's execution environment provides. -type HostOptions struct { - // Config is the bootstrap configuration loaded from config file. - Config *UCPConfig -} - -// NewHostOptionsFromEnvironment reads the configuration from the given path and returns a HostOptions object, or an -// error if the configuration could not be loaded. -func NewHostOptionsFromEnvironment(configPath string) (HostOptions, error) { - conf, err := loadConfig(configPath) - if err != nil { - return HostOptions{}, err - } - - return HostOptions{ - Config: conf, - }, nil -} - -func loadConfig(configPath string) (*UCPConfig, error) { - buf, err := os.ReadFile(configPath) - if err != nil { - return nil, err - } - - conf := &UCPConfig{} - decoder := yaml.NewDecoder(bytes.NewBuffer(buf)) - decoder.KnownFields(true) - - err = decoder.Decode(conf) - if err != nil { - return nil, fmt.Errorf("failed to load yaml: %w", err) - } - - return conf, nil -} diff --git a/pkg/ucp/hostoptions/providerconfig.go b/pkg/ucp/hostoptions/providerconfig.go deleted file mode 100644 index 30729336f0..0000000000 --- a/pkg/ucp/hostoptions/providerconfig.go +++ /dev/null @@ -1,65 +0,0 @@ -/* -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 hostoptions - -import ( - "github.com/radius-project/radius/pkg/components/database/databaseprovider" - "github.com/radius-project/radius/pkg/components/queue/queueprovider" - "github.com/radius-project/radius/pkg/components/secret/secretprovider" - metricsprovider "github.com/radius-project/radius/pkg/metrics/provider" - profilerprovider "github.com/radius-project/radius/pkg/profiler/provider" - "github.com/radius-project/radius/pkg/trace" - "github.com/radius-project/radius/pkg/ucp/config" - "github.com/radius-project/radius/pkg/ucp/rest" - "github.com/radius-project/radius/pkg/ucp/ucplog" -) - -// UCPConfig includes the resource provider configuration. -type UCPConfig struct { - DatabaseProvider databaseprovider.Options `yaml:"storageProvider"` - Planes []rest.Plane `yaml:"planes"` - SecretProvider secretprovider.SecretProviderOptions `yaml:"secretProvider"` - MetricsProvider metricsprovider.MetricsProviderOptions `yaml:"metricsProvider"` - ProfilerProvider profilerprovider.ProfilerProviderOptions `yaml:"profilerProvider"` - QueueProvider queueprovider.QueueProviderOptions `yaml:"queueProvider"` - TracerProvider trace.Options `yaml:"tracerProvider"` - Logging ucplog.LoggingOptions `yaml:"logging"` - Identity Identity `yaml:"identity,omitempty"` - UCP config.UCPOptions `yaml:"ucp"` - Location string `yaml:"location"` - Routing RoutingConfig `yaml:"routing"` -} - -const ( - // AuthUCPCredential is the authentication method via UCP Credential API. - AuthUCPCredential = "UCPCredential" - // AuthDefault is the default authentication method, such as environment variables. - AuthDefault = "default" -) - -// Identity represents configuration options for authenticating with external systems like Azure and AWS. -type Identity struct { - // AuthMethod represents the method of authentication for authenticating with external systems like Azure and AWS. - AuthMethod string `yaml:"authMethod"` -} - -// RoutingConfig provides configuration for UCP routing. -type RoutingConfig struct { - // DefaultDownstreamEndpoint is the default destination when a resource provider does not provide a downstream endpoint. - // In practice, this points to the URL of dynamic-rp. - DefaultDownstreamEndpoint string `yaml:"defaultDownstreamEndpoint"` -} diff --git a/pkg/ucp/integrationtests/aws/awstest.go b/pkg/ucp/integrationtests/aws/awstest.go index bca5b13119..59e77df5cf 100644 --- a/pkg/ucp/integrationtests/aws/awstest.go +++ b/pkg/ucp/integrationtests/aws/awstest.go @@ -21,12 +21,11 @@ package aws import ( "testing" - "github.com/radius-project/radius/pkg/components/database" - "github.com/radius-project/radius/pkg/components/secret" + "github.com/radius-project/radius/pkg/ucp" ucp_aws "github.com/radius-project/radius/pkg/ucp/aws" ucp_aws_frontend "github.com/radius-project/radius/pkg/ucp/frontend/aws" "github.com/radius-project/radius/pkg/ucp/frontend/modules" - "github.com/radius-project/radius/pkg/ucp/integrationtests/testserver" + "github.com/radius-project/radius/pkg/ucp/testhost" "go.uber.org/mock/gomock" ) @@ -38,17 +37,17 @@ const ( testAWSRequestToken = "79B9F0DA-4882-4DC8-A367-6FD3BC122DED" // Random UUID ) -func initializeAWSTest(t *testing.T) (*testserver.TestServer, *database.MockClient, *secret.MockClient, *ucp_aws.MockAWSCloudControlClient, *ucp_aws.MockAWSCloudFormationClient) { +func initializeAWSTest(t *testing.T) (*testhost.TestHost, *ucp_aws.MockAWSCloudControlClient, *ucp_aws.MockAWSCloudFormationClient) { ctrl := gomock.NewController(t) cloudControlClient := ucp_aws.NewMockAWSCloudControlClient(ctrl) cloudFormationClient := ucp_aws.NewMockAWSCloudFormationClient(ctrl) - ucp := testserver.StartWithMocks(t, func(options modules.Options) []modules.Initializer { + ucp := testhost.Start(t, testhost.TestHostOptionFunc(func(options *ucp.Options) { module := ucp_aws_frontend.NewModule(options) module.AWSClients.CloudControl = cloudControlClient module.AWSClients.CloudFormation = cloudFormationClient - return []modules.Initializer{module} - }) + options.Modules = []modules.Initializer{module} + })) - return ucp, ucp.Mocks.Database, ucp.Mocks.Secrets, cloudControlClient, cloudFormationClient + return ucp, cloudControlClient, cloudFormationClient } diff --git a/pkg/ucp/integrationtests/aws/createresource_test.go b/pkg/ucp/integrationtests/aws/createresource_test.go index 78475c40a8..055f753b52 100644 --- a/pkg/ucp/integrationtests/aws/createresource_test.go +++ b/pkg/ucp/integrationtests/aws/createresource_test.go @@ -35,7 +35,7 @@ import ( ) func Test_CreateAWSResource(t *testing.T) { - ucp, _, _, cloudcontrolClient, _ := initializeAWSTest(t) + ucp, cloudcontrolClient, _ := initializeAWSTest(t) cloudcontrolClient.EXPECT().GetResource(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, params *cloudcontrol.GetResourceInput, optFns ...func(*cloudcontrol.Options)) (*cloudcontrol.GetResourceOutput, error) { notfound := types.ResourceNotFoundException{ @@ -63,7 +63,7 @@ func Test_CreateAWSResource(t *testing.T) { body, err := json.Marshal(requestBody) require.NoError(t, err) - createRequest, err := rpctest.NewHTTPRequestWithContent(context.Background(), http.MethodPut, ucp.BaseURL+testProxyRequestAWSPath, body) + createRequest, err := rpctest.NewHTTPRequestWithContent(context.Background(), http.MethodPut, ucp.BaseURL()+testProxyRequestAWSPath, body) require.NoError(t, err, "creating request failed") ctx := rpctest.NewARMRequestContext(createRequest) diff --git a/pkg/ucp/integrationtests/aws/createresourcewithpost_test.go b/pkg/ucp/integrationtests/aws/createresourcewithpost_test.go index 4909caa092..19d96f852a 100644 --- a/pkg/ucp/integrationtests/aws/createresourcewithpost_test.go +++ b/pkg/ucp/integrationtests/aws/createresourcewithpost_test.go @@ -37,7 +37,7 @@ import ( ) func Test_CreateAWSResourceWithPost(t *testing.T) { - ucp, _, _, cloudcontrolClient, cloudformationClient := initializeAWSTest(t) + ucp, cloudcontrolClient, cloudformationClient := initializeAWSTest(t) primaryIdentifiers := map[string]any{ "primaryIdentifier": []any{ @@ -81,7 +81,7 @@ func Test_CreateAWSResourceWithPost(t *testing.T) { body, err := json.Marshal(requestBody) require.NoError(t, err) - createRequest, err := rpctest.NewHTTPRequestWithContent(context.Background(), http.MethodPost, ucp.BaseURL+testProxyRequestAWSCollectionPath+"/:put", body) + createRequest, err := rpctest.NewHTTPRequestWithContent(context.Background(), http.MethodPost, ucp.BaseURL()+testProxyRequestAWSCollectionPath+"/:put", body) require.NoError(t, err, "creating request failed") ctx := rpctest.NewARMRequestContext(createRequest) diff --git a/pkg/ucp/integrationtests/aws/deleteresource_test.go b/pkg/ucp/integrationtests/aws/deleteresource_test.go index f497772f76..5edca23c39 100644 --- a/pkg/ucp/integrationtests/aws/deleteresource_test.go +++ b/pkg/ucp/integrationtests/aws/deleteresource_test.go @@ -34,7 +34,7 @@ import ( ) func Test_DeleteAWSResource(t *testing.T) { - ucp, _, _, cloudcontrolClient, _ := initializeAWSTest(t) + ucp, cloudcontrolClient, _ := initializeAWSTest(t) cloudcontrolClient.EXPECT().DeleteResource(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, params *cloudcontrol.DeleteResourceInput, optFns ...func(*cloudcontrol.Options)) (*cloudcontrol.DeleteResourceOutput, error) { output := cloudcontrol.DeleteResourceOutput{ @@ -46,7 +46,7 @@ func Test_DeleteAWSResource(t *testing.T) { return &output, nil }) - deleteRequest, err := rpctest.NewHTTPRequestWithContent(context.Background(), http.MethodDelete, ucp.BaseURL+testProxyRequestAWSPath, nil) + deleteRequest, err := rpctest.NewHTTPRequestWithContent(context.Background(), http.MethodDelete, ucp.BaseURL()+testProxyRequestAWSPath, nil) require.NoError(t, err, "creating request failed") ctx := rpctest.NewARMRequestContext(deleteRequest) diff --git a/pkg/ucp/integrationtests/aws/deleteresourcewithpost_test.go b/pkg/ucp/integrationtests/aws/deleteresourcewithpost_test.go index fc78fb54a4..f9f5078d05 100644 --- a/pkg/ucp/integrationtests/aws/deleteresourcewithpost_test.go +++ b/pkg/ucp/integrationtests/aws/deleteresourcewithpost_test.go @@ -37,7 +37,7 @@ import ( ) func Test_DeleteAWSResourceWithPost(t *testing.T) { - ucp, _, _, cloudcontrolClient, cloudformationClient := initializeAWSTest(t) + ucp, cloudcontrolClient, cloudformationClient := initializeAWSTest(t) primaryIdentifiers := map[string]any{ "primaryIdentifier": []any{ @@ -73,7 +73,7 @@ func Test_DeleteAWSResourceWithPost(t *testing.T) { body, err := json.Marshal(requestBody) require.NoError(t, err) - deleteRequest, err := rpctest.NewHTTPRequestWithContent(context.Background(), http.MethodPost, ucp.BaseURL+testProxyRequestAWSCollectionPath+"/:delete", body) + deleteRequest, err := rpctest.NewHTTPRequestWithContent(context.Background(), http.MethodPost, ucp.BaseURL()+testProxyRequestAWSCollectionPath+"/:delete", body) require.NoError(t, err, "creating request failed") ctx := rpctest.NewARMRequestContext(deleteRequest) diff --git a/pkg/ucp/integrationtests/aws/getresource_test.go b/pkg/ucp/integrationtests/aws/getresource_test.go index c9e17c38d9..501be7565f 100644 --- a/pkg/ucp/integrationtests/aws/getresource_test.go +++ b/pkg/ucp/integrationtests/aws/getresource_test.go @@ -35,7 +35,7 @@ import ( ) func Test_GetAWSResource(t *testing.T) { - ucp, _, _, cloudcontrolClient, _ := initializeAWSTest(t) + ucp, cloudcontrolClient, _ := initializeAWSTest(t) getResponseBody := map[string]any{ "RetentionPeriodHours": 178, @@ -54,7 +54,7 @@ func Test_GetAWSResource(t *testing.T) { return &output, nil }) - getRequest, err := rpctest.NewHTTPRequestWithContent(context.Background(), http.MethodGet, ucp.BaseURL+testProxyRequestAWSPath, nil) + getRequest, err := rpctest.NewHTTPRequestWithContent(context.Background(), http.MethodGet, ucp.BaseURL()+testProxyRequestAWSPath, nil) require.NoError(t, err, "creating request failed") ctx := rpctest.NewARMRequestContext(getRequest) diff --git a/pkg/ucp/integrationtests/aws/getresourcewithpost_test.go b/pkg/ucp/integrationtests/aws/getresourcewithpost_test.go index 95d72ee5c6..e33a972e15 100644 --- a/pkg/ucp/integrationtests/aws/getresourcewithpost_test.go +++ b/pkg/ucp/integrationtests/aws/getresourcewithpost_test.go @@ -37,7 +37,7 @@ import ( ) func Test_GetAWSResourceWithPost(t *testing.T) { - ucp, _, _, cloudcontrolClient, cloudformationClient := initializeAWSTest(t) + ucp, cloudcontrolClient, cloudformationClient := initializeAWSTest(t) primaryIdentifiers := map[string]any{ "primaryIdentifier": []any{ @@ -80,7 +80,7 @@ func Test_GetAWSResourceWithPost(t *testing.T) { body, err := json.Marshal(requestBody) require.NoError(t, err) - getRequest, err := rpctest.NewHTTPRequestWithContent(context.Background(), http.MethodPost, ucp.BaseURL+testProxyRequestAWSCollectionPath+"/:get", body) + getRequest, err := rpctest.NewHTTPRequestWithContent(context.Background(), http.MethodPost, ucp.BaseURL()+testProxyRequestAWSCollectionPath+"/:get", body) require.NoError(t, err, "creating request failed") ctx := rpctest.NewARMRequestContext(getRequest) diff --git a/pkg/ucp/integrationtests/aws/listresources_test.go b/pkg/ucp/integrationtests/aws/listresources_test.go index ca97d8ba9a..c068073a4f 100644 --- a/pkg/ucp/integrationtests/aws/listresources_test.go +++ b/pkg/ucp/integrationtests/aws/listresources_test.go @@ -37,7 +37,7 @@ import ( const testProxyRequestAWSListPath = "/planes/aws/aws/accounts/1234567/regions/us-east-1/providers/AWS.Kinesis/Stream" func Test_ListAWSResources(t *testing.T) { - ucp, _, _, cloudcontrolClient, _ := initializeAWSTest(t) + ucp, cloudcontrolClient, _ := initializeAWSTest(t) getResponseBody := map[string]any{ "RetentionPeriodHours": 178, @@ -58,7 +58,7 @@ func Test_ListAWSResources(t *testing.T) { return &output, nil }) - listRequest, err := rpctest.NewHTTPRequestWithContent(context.Background(), http.MethodGet, ucp.BaseURL+testProxyRequestAWSListPath, nil) + listRequest, err := rpctest.NewHTTPRequestWithContent(context.Background(), http.MethodGet, ucp.BaseURL()+testProxyRequestAWSListPath, nil) require.NoError(t, err, "creating request failed") ctx := rpctest.NewARMRequestContext(listRequest) diff --git a/pkg/ucp/integrationtests/aws/operationresults_test.go b/pkg/ucp/integrationtests/aws/operationresults_test.go index 354c79c44b..730480bf71 100644 --- a/pkg/ucp/integrationtests/aws/operationresults_test.go +++ b/pkg/ucp/integrationtests/aws/operationresults_test.go @@ -35,7 +35,7 @@ import ( ) func Test_GetOperationResults(t *testing.T) { - ucp, _, _, cloudcontrolClient, _ := initializeAWSTest(t) + ucp, cloudcontrolClient, _ := initializeAWSTest(t) cloudcontrolClient.EXPECT().GetResourceRequestStatus(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, params *cloudcontrol.GetResourceRequestStatusInput, optFns ...func(*cloudcontrol.Options)) (*cloudcontrol.GetResourceRequestStatusOutput, error) { output := cloudcontrol.GetResourceRequestStatusOutput{ @@ -46,7 +46,7 @@ func Test_GetOperationResults(t *testing.T) { return &output, nil }) - operationResultsRequest, err := rpctest.NewHTTPRequestWithContent(context.Background(), http.MethodGet, ucp.BaseURL+testProxyRequestAWSAsyncPath+"/operationResults/"+strings.ToLower(testAWSRequestToken), nil) + operationResultsRequest, err := rpctest.NewHTTPRequestWithContent(context.Background(), http.MethodGet, ucp.BaseURL()+testProxyRequestAWSAsyncPath+"/operationResults/"+strings.ToLower(testAWSRequestToken), nil) require.NoError(t, err, "creating request failed") ctx := rpctest.NewARMRequestContext(operationResultsRequest) diff --git a/pkg/ucp/integrationtests/aws/operationstatuses_test.go b/pkg/ucp/integrationtests/aws/operationstatuses_test.go index 1de5766732..ae78037c6b 100644 --- a/pkg/ucp/integrationtests/aws/operationstatuses_test.go +++ b/pkg/ucp/integrationtests/aws/operationstatuses_test.go @@ -36,7 +36,7 @@ import ( ) func Test_GetOperationStatuses(t *testing.T) { - ucp, _, _, cloudcontrolClient, _ := initializeAWSTest(t) + ucp, cloudcontrolClient, _ := initializeAWSTest(t) cloudcontrolClient.EXPECT().GetResourceRequestStatus(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, params *cloudcontrol.GetResourceRequestStatusInput, optFns ...func(*cloudcontrol.Options)) (*cloudcontrol.GetResourceRequestStatusOutput, error) { output := cloudcontrol.GetResourceRequestStatusOutput{ @@ -48,7 +48,7 @@ func Test_GetOperationStatuses(t *testing.T) { return &output, nil }) - operationResultsRequest, err := rpctest.NewHTTPRequestWithContent(context.Background(), http.MethodGet, ucp.BaseURL+testProxyRequestAWSAsyncPath+"/operationStatuses/"+strings.ToLower(testAWSRequestToken), nil) + operationResultsRequest, err := rpctest.NewHTTPRequestWithContent(context.Background(), http.MethodGet, ucp.BaseURL()+testProxyRequestAWSAsyncPath+"/operationStatuses/"+strings.ToLower(testAWSRequestToken), nil) require.NoError(t, err, "creating request failed") ctx := rpctest.NewARMRequestContext(operationResultsRequest) diff --git a/pkg/ucp/integrationtests/aws/updateresource_test.go b/pkg/ucp/integrationtests/aws/updateresource_test.go index 6afdcc7138..3f5b9d1a48 100644 --- a/pkg/ucp/integrationtests/aws/updateresource_test.go +++ b/pkg/ucp/integrationtests/aws/updateresource_test.go @@ -39,7 +39,7 @@ import ( const ZeroAWSRequestToken = "00000000-0000-0000-0000-000000000000" func Test_UpdateAWSResource(t *testing.T) { - ucp, _, _, cloudcontrolClient, cloudFormationClient := initializeAWSTest(t) + ucp, cloudcontrolClient, cloudFormationClient := initializeAWSTest(t) getResponseBody := map[string]any{ "RetentionPeriodHours": 178, @@ -94,7 +94,7 @@ func Test_UpdateAWSResource(t *testing.T) { body, err := json.Marshal(requestBody) require.NoError(t, err) - updateRequest, err := rpctest.NewHTTPRequestWithContent(context.Background(), http.MethodPut, ucp.BaseURL+testProxyRequestAWSPath, body) + updateRequest, err := rpctest.NewHTTPRequestWithContent(context.Background(), http.MethodPut, ucp.BaseURL()+testProxyRequestAWSPath, body) require.NoError(t, err, "creating request failed") ctx := rpctest.NewARMRequestContext(updateRequest) diff --git a/pkg/ucp/integrationtests/aws/updateresourcewithpost_test.go b/pkg/ucp/integrationtests/aws/updateresourcewithpost_test.go index 015074783d..8c23b7d1a0 100644 --- a/pkg/ucp/integrationtests/aws/updateresourcewithpost_test.go +++ b/pkg/ucp/integrationtests/aws/updateresourcewithpost_test.go @@ -37,7 +37,7 @@ import ( ) func Test_UpdateAWSResourceWithPost(t *testing.T) { - ucp, _, _, cloudcontrolClient, cloudformationClient := initializeAWSTest(t) + ucp, cloudcontrolClient, cloudformationClient := initializeAWSTest(t) primaryIdentifiers := map[string]any{ "primaryIdentifier": []any{ @@ -90,7 +90,7 @@ func Test_UpdateAWSResourceWithPost(t *testing.T) { body, err := json.Marshal(requestBody) require.NoError(t, err) - updateRequest, err := rpctest.NewHTTPRequestWithContent(context.Background(), http.MethodPost, ucp.BaseURL+testProxyRequestAWSCollectionPath+"/:put", body) + updateRequest, err := rpctest.NewHTTPRequestWithContent(context.Background(), http.MethodPost, ucp.BaseURL()+testProxyRequestAWSCollectionPath+"/:put", body) require.NoError(t, err, "update request failed") ctx := rpctest.NewARMRequestContext(updateRequest) diff --git a/pkg/ucp/integrationtests/azure/proxy_test.go b/pkg/ucp/integrationtests/azure/proxy_test.go index 74261d45f4..d672c83168 100644 --- a/pkg/ucp/integrationtests/azure/proxy_test.go +++ b/pkg/ucp/integrationtests/azure/proxy_test.go @@ -25,9 +25,8 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" "github.com/radius-project/radius/pkg/to" "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" - "github.com/radius-project/radius/pkg/ucp/frontend/api" "github.com/radius-project/radius/pkg/ucp/integrationtests/testrp" - "github.com/radius-project/radius/pkg/ucp/integrationtests/testserver" + "github.com/radius-project/radius/pkg/ucp/testhost" "github.com/stretchr/testify/require" ) @@ -38,7 +37,7 @@ const ( ) func Test_AzurePlane_ProxyRequest(t *testing.T) { - ucp := testserver.StartWithETCD(t, api.DefaultModules) + ucp := testhost.Start(t) data := map[string]string{ "message": "here is some test data", @@ -63,7 +62,7 @@ func Test_AzurePlane_ProxyRequest(t *testing.T) { require.Equal(t, "SomeValue", response.Raw.Header.Get("SomeHeader")) } -func createAzurePlane(ucp *testserver.TestServer, rp *testrp.Server) { +func createAzurePlane(ucp *testhost.TestHost, rp *testrp.Server) { body := v20231001preview.AzurePlaneResource{ Location: to.Ptr(v1.LocationGlobal), Properties: &v20231001preview.AzurePlaneResourceProperties{ diff --git a/pkg/ucp/integrationtests/handler_test.go b/pkg/ucp/integrationtests/handler_test.go index c131536c73..b8c0ee2a40 100644 --- a/pkg/ucp/integrationtests/handler_test.go +++ b/pkg/ucp/integrationtests/handler_test.go @@ -20,20 +20,32 @@ import ( "net/http" "testing" + "github.com/google/uuid" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" - "github.com/radius-project/radius/pkg/ucp/integrationtests/testserver" + "github.com/radius-project/radius/pkg/ucp" + "github.com/radius-project/radius/pkg/ucp/testhost" "github.com/stretchr/testify/require" ) func Test_Handler_MethodNotAllowed(t *testing.T) { - ucp := testserver.StartWithMocks(t, testserver.NoModules) + ucp := testhost.Start(t, testhost.NoModules()) response := ucp.MakeRequest(http.MethodDelete, "/planes?api-version=2023-10-01-preview", nil) require.Equal(t, "failed to parse route: undefined route path", response.Error.Error.Details[0].Message) } func Test_Handler_NotFound(t *testing.T) { - ucp := testserver.StartWithMocks(t, testserver.NoModules) + ucp := testhost.Start(t, testhost.NoModules()) + + response := ucp.MakeRequest(http.MethodGet, "/abc", nil) + response.EqualsErrorCode(http.StatusNotFound, v1.CodeNotFound) + require.Regexp(t, "The request 'GET /abc' is invalid.", response.Error.Error.Message) +} + +func Test_Handler_NotFound_PathBase(t *testing.T) { + ucp := testhost.Start(t, testhost.NoModules(), testhost.TestHostOptionFunc(func(options *ucp.Options) { + options.Config.Server.PathBase = "/" + uuid.New().String() + })) response := ucp.MakeRequest(http.MethodGet, "/abc", nil) response.EqualsErrorCode(http.StatusNotFound, v1.CodeNotFound) diff --git a/pkg/ucp/integrationtests/planes/aws_test.go b/pkg/ucp/integrationtests/planes/aws_test.go index fa707bf3c8..b810b648cf 100644 --- a/pkg/ucp/integrationtests/planes/aws_test.go +++ b/pkg/ucp/integrationtests/planes/aws_test.go @@ -20,8 +20,7 @@ import ( "testing" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" - "github.com/radius-project/radius/pkg/ucp/frontend/api" - "github.com/radius-project/radius/pkg/ucp/integrationtests/testserver" + "github.com/radius-project/radius/pkg/ucp/testhost" ) const ( @@ -35,7 +34,7 @@ const ( ) func Test_AWSPlane_PUT_Create(t *testing.T) { - server := testserver.StartWithETCD(t, api.DefaultModules) + server := testhost.Start(t) defer server.Close() response := server.MakeFixtureRequest("PUT", awsPlaneResourceURL, awsPlaneRequestFixture) @@ -43,7 +42,7 @@ func Test_AWSPlane_PUT_Create(t *testing.T) { } func Test_AWSPlane_PUT_Update(t *testing.T) { - server := testserver.StartWithETCD(t, api.DefaultModules) + server := testhost.Start(t) defer server.Close() response := server.MakeFixtureRequest("PUT", awsPlaneResourceURL, awsPlaneRequestFixture) @@ -54,7 +53,7 @@ func Test_AWSPlane_PUT_Update(t *testing.T) { } func Test_AWSPlane_GET_Empty(t *testing.T) { - server := testserver.StartWithETCD(t, api.DefaultModules) + server := testhost.Start(t) defer server.Close() response := server.MakeRequest("GET", awsPlaneResourceURL, nil) @@ -62,7 +61,7 @@ func Test_AWSPlane_GET_Empty(t *testing.T) { } func Test_AWSPlane_GET_Found(t *testing.T) { - server := testserver.StartWithETCD(t, api.DefaultModules) + server := testhost.Start(t) defer server.Close() response := server.MakeFixtureRequest("PUT", awsPlaneResourceURL, awsPlaneRequestFixture) @@ -73,8 +72,7 @@ func Test_AWSPlane_GET_Found(t *testing.T) { } func Test_AWSPlane_LIST(t *testing.T) { - - server := testserver.StartWithETCD(t, api.DefaultModules) + server := testhost.Start(t) defer server.Close() response := server.MakeFixtureRequest("PUT", awsPlaneResourceURL, awsPlaneRequestFixture) @@ -85,7 +83,7 @@ func Test_AWSPlane_LIST(t *testing.T) { } func Test_AWSPlane_DELETE_DoesNotExist(t *testing.T) { - server := testserver.StartWithETCD(t, api.DefaultModules) + server := testhost.Start(t) defer server.Close() response := server.MakeRequest("DELETE", awsPlaneResourceURL, nil) @@ -93,7 +91,7 @@ func Test_AWSPlane_DELETE_DoesNotExist(t *testing.T) { } func Test_AWSPlane_DELETE_Found(t *testing.T) { - server := testserver.StartWithETCD(t, api.DefaultModules) + server := testhost.Start(t) defer server.Close() response := server.MakeFixtureRequest("PUT", awsPlaneResourceURL, awsPlaneRequestFixture) diff --git a/pkg/ucp/integrationtests/planes/azure_test.go b/pkg/ucp/integrationtests/planes/azure_test.go index 5ce199c9f0..49e9cf4b1f 100644 --- a/pkg/ucp/integrationtests/planes/azure_test.go +++ b/pkg/ucp/integrationtests/planes/azure_test.go @@ -20,8 +20,7 @@ import ( "testing" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" - "github.com/radius-project/radius/pkg/ucp/frontend/api" - "github.com/radius-project/radius/pkg/ucp/integrationtests/testserver" + "github.com/radius-project/radius/pkg/ucp/testhost" ) const ( @@ -35,7 +34,7 @@ const ( ) func Test_AzurePlane_PUT_Create(t *testing.T) { - server := testserver.StartWithETCD(t, api.DefaultModules) + server := testhost.Start(t) defer server.Close() response := server.MakeFixtureRequest("PUT", azurePlaneResourceURL, azurePlaneRequestFixture) @@ -43,7 +42,7 @@ func Test_AzurePlane_PUT_Create(t *testing.T) { } func Test_AzurePlane_PUT_Update(t *testing.T) { - server := testserver.StartWithETCD(t, api.DefaultModules) + server := testhost.Start(t) defer server.Close() response := server.MakeFixtureRequest("PUT", azurePlaneResourceURL, azurePlaneRequestFixture) @@ -54,7 +53,7 @@ func Test_AzurePlane_PUT_Update(t *testing.T) { } func Test_AzurePlane_GET_Empty(t *testing.T) { - server := testserver.StartWithETCD(t, api.DefaultModules) + server := testhost.Start(t) defer server.Close() response := server.MakeRequest("GET", azurePlaneResourceURL, nil) @@ -62,7 +61,7 @@ func Test_AzurePlane_GET_Empty(t *testing.T) { } func Test_AzurePlane_GET_Found(t *testing.T) { - server := testserver.StartWithETCD(t, api.DefaultModules) + server := testhost.Start(t) defer server.Close() response := server.MakeFixtureRequest("PUT", azurePlaneResourceURL, azurePlaneRequestFixture) @@ -73,7 +72,7 @@ func Test_AzurePlane_GET_Found(t *testing.T) { } func Test_AzurePlane_LIST(t *testing.T) { - server := testserver.StartWithETCD(t, api.DefaultModules) + server := testhost.Start(t) defer server.Close() // Add a azure plane @@ -86,7 +85,7 @@ func Test_AzurePlane_LIST(t *testing.T) { } func Test_AzurePlane_DELETE_DoesNotExist(t *testing.T) { - server := testserver.StartWithETCD(t, api.DefaultModules) + server := testhost.Start(t) defer server.Close() response := server.MakeRequest("DELETE", azurePlaneResourceURL, nil) @@ -94,7 +93,7 @@ func Test_AzurePlane_DELETE_DoesNotExist(t *testing.T) { } func Test_AzurePlane_DELETE_Found(t *testing.T) { - server := testserver.StartWithETCD(t, api.DefaultModules) + server := testhost.Start(t) defer server.Close() response := server.MakeFixtureRequest("PUT", azurePlaneResourceURL, azurePlaneRequestFixture) diff --git a/pkg/ucp/integrationtests/planes/planes_test.go b/pkg/ucp/integrationtests/planes/planes_test.go index 2a6eb9c35e..03dbd1baad 100644 --- a/pkg/ucp/integrationtests/planes/planes_test.go +++ b/pkg/ucp/integrationtests/planes/planes_test.go @@ -19,8 +19,7 @@ package planes import ( "testing" - "github.com/radius-project/radius/pkg/ucp/frontend/api" - "github.com/radius-project/radius/pkg/ucp/integrationtests/testserver" + "github.com/radius-project/radius/pkg/ucp/testhost" ) const ( @@ -30,7 +29,7 @@ const ( ) func Test_AllPlanes_LIST(t *testing.T) { - server := testserver.StartWithETCD(t, api.DefaultModules) + server := testhost.Start(t) defer server.Close() response := server.MakeFixtureRequest("PUT", radiusPlaneResourceURL, radiusPlaneRequestFixture) diff --git a/pkg/ucp/integrationtests/planes/radius_test.go b/pkg/ucp/integrationtests/planes/radius_test.go index d2e8fbfa69..667e4a621c 100644 --- a/pkg/ucp/integrationtests/planes/radius_test.go +++ b/pkg/ucp/integrationtests/planes/radius_test.go @@ -20,8 +20,7 @@ import ( "testing" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" - "github.com/radius-project/radius/pkg/ucp/frontend/api" - "github.com/radius-project/radius/pkg/ucp/integrationtests/testserver" + "github.com/radius-project/radius/pkg/ucp/testhost" ) const ( @@ -35,7 +34,7 @@ const ( ) func Test_RadiusPlane_PUT_Create(t *testing.T) { - server := testserver.StartWithETCD(t, api.DefaultModules) + server := testhost.Start(t) defer server.Close() response := server.MakeFixtureRequest("PUT", radiusPlaneResourceURL, radiusPlaneRequestFixture) @@ -43,7 +42,7 @@ func Test_RadiusPlane_PUT_Create(t *testing.T) { } func Test_RadiusPlane_PUT_Update(t *testing.T) { - server := testserver.StartWithETCD(t, api.DefaultModules) + server := testhost.Start(t) defer server.Close() response := server.MakeFixtureRequest("PUT", radiusPlaneResourceURL, radiusPlaneRequestFixture) @@ -54,7 +53,7 @@ func Test_RadiusPlane_PUT_Update(t *testing.T) { } func Test_RadiusPlane_GET_Empty(t *testing.T) { - server := testserver.StartWithETCD(t, api.DefaultModules) + server := testhost.Start(t) defer server.Close() response := server.MakeRequest("GET", radiusPlaneResourceURL, nil) @@ -62,7 +61,7 @@ func Test_RadiusPlane_GET_Empty(t *testing.T) { } func Test_RadiusPlane_GET_Found(t *testing.T) { - server := testserver.StartWithETCD(t, api.DefaultModules) + server := testhost.Start(t) defer server.Close() response := server.MakeFixtureRequest("PUT", radiusPlaneResourceURL, radiusPlaneRequestFixture) @@ -73,7 +72,7 @@ func Test_RadiusPlane_GET_Found(t *testing.T) { } func Test_RadiusPlane_LIST(t *testing.T) { - server := testserver.StartWithETCD(t, api.DefaultModules) + server := testhost.Start(t) defer server.Close() // Add a radius plane @@ -86,7 +85,7 @@ func Test_RadiusPlane_LIST(t *testing.T) { } func Test_RadiusPlane_DELETE_DoesNotExist(t *testing.T) { - server := testserver.StartWithETCD(t, api.DefaultModules) + server := testhost.Start(t) defer server.Close() response := server.MakeRequest("DELETE", radiusPlaneResourceURL, nil) @@ -94,7 +93,7 @@ func Test_RadiusPlane_DELETE_DoesNotExist(t *testing.T) { } func Test_RadiusPlane_DELETE_Found(t *testing.T) { - server := testserver.StartWithETCD(t, api.DefaultModules) + server := testhost.Start(t) defer server.Close() response := server.MakeFixtureRequest("PUT", radiusPlaneResourceURL, radiusPlaneRequestFixture) diff --git a/pkg/ucp/integrationtests/planes/validation_test.go b/pkg/ucp/integrationtests/planes/validation_test.go index 015f49b12f..e6607c45c0 100644 --- a/pkg/ucp/integrationtests/planes/validation_test.go +++ b/pkg/ucp/integrationtests/planes/validation_test.go @@ -23,8 +23,7 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" "github.com/radius-project/radius/pkg/to" "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" - "github.com/radius-project/radius/pkg/ucp/frontend/api" - "github.com/radius-project/radius/pkg/ucp/integrationtests/testserver" + "github.com/radius-project/radius/pkg/ucp/testhost" "github.com/stretchr/testify/require" ) @@ -33,7 +32,7 @@ const ( ) func Test_Planes_GET_BadAPIVersion(t *testing.T) { - ucp := testserver.StartWithMocks(t, api.DefaultModules) + ucp := testhost.Start(t) response := ucp.MakeRequest(http.MethodGet, "/planes?api-version=unsupported-version", nil) response.EqualsErrorCode(http.StatusBadRequest, v1.CodeInvalidApiVersionParameter) @@ -41,7 +40,7 @@ func Test_Planes_GET_BadAPIVersion(t *testing.T) { } func Test_Planes_PUT_BadAPIVersion(t *testing.T) { - ucp := testserver.StartWithMocks(t, api.DefaultModules) + ucp := testhost.Start(t) requestBody := v20231001preview.RadiusPlaneResource{ Location: to.Ptr(v1.LocationGlobal), diff --git a/pkg/ucp/integrationtests/radius/proxy_test.go b/pkg/ucp/integrationtests/radius/proxy_test.go index 4b96eb7229..5275034f31 100644 --- a/pkg/ucp/integrationtests/radius/proxy_test.go +++ b/pkg/ucp/integrationtests/radius/proxy_test.go @@ -28,9 +28,8 @@ import ( backend_ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" "github.com/radius-project/radius/pkg/to" "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" - "github.com/radius-project/radius/pkg/ucp/frontend/api" "github.com/radius-project/radius/pkg/ucp/integrationtests/testrp" - "github.com/radius-project/radius/pkg/ucp/integrationtests/testserver" + "github.com/radius-project/radius/pkg/ucp/testhost" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -48,7 +47,7 @@ const ( ) func Test_RadiusPlane_Proxy_ResourceGroupDoesNotExist(t *testing.T) { - ucp := testserver.StartWithETCD(t, api.DefaultModules) + ucp := testhost.Start(t) rp := testrp.Start(t) rps := map[string]*string{ @@ -62,7 +61,7 @@ func Test_RadiusPlane_Proxy_ResourceGroupDoesNotExist(t *testing.T) { } func Test_RadiusPlane_ResourceSync(t *testing.T) { - ucp := testserver.StartWithETCD(t, api.DefaultModules) + ucp := testhost.Start(t) rp := testrp.Start(t) rp.Handler = testrp.SyncResource(t, ucp, testResourceGroupID) @@ -158,7 +157,7 @@ func Test_RadiusPlane_ResourceSync(t *testing.T) { } func Test_RadiusPlane_ResourceAsync(t *testing.T) { - ucp := testserver.StartWithETCD(t, api.DefaultModules) + ucp := testhost.Start(t) rp := testrp.Start(t) // Block background work item completion until we're ready. @@ -176,7 +175,7 @@ func Test_RadiusPlane_ResourceAsync(t *testing.T) { return result, nil } - client, err := ucp.Clients.DatabaseProvider.GetClient(ctx) + client, err := ucp.Options().DatabaseProvider.GetClient(ctx) require.NoError(t, err) err = client.Delete(ctx, testResourceID) require.NoError(t, err) @@ -222,8 +221,8 @@ func Test_RadiusPlane_ResourceAsync(t *testing.T) { location := response.Raw.Header.Get("Location") azureAsyncOperation := response.Raw.Header.Get("Azure-AsyncOperation") - require.True(t, strings.HasPrefix(location, ucp.BaseURL), "Location starts with UCP URL") - require.True(t, strings.HasPrefix(azureAsyncOperation, ucp.BaseURL), "Azure-AsyncOperation starts with UCP URL") + require.True(t, strings.HasPrefix(location, ucp.BaseURL()), "Location starts with UCP URL") + require.True(t, strings.HasPrefix(azureAsyncOperation, ucp.BaseURL()), "Azure-AsyncOperation starts with UCP URL") }) t.Run("LIST (during PUT)", func(t *testing.T) { @@ -382,7 +381,7 @@ func Test_RadiusPlane_ResourceAsync(t *testing.T) { }) } -func createRadiusPlane(ucp *testserver.TestServer, resourceProviders map[string]*string) { +func createRadiusPlane(ucp *testhost.TestHost, resourceProviders map[string]*string) { body := v20231001preview.RadiusPlaneResource{ Location: to.Ptr(v1.LocationGlobal), Properties: &v20231001preview.RadiusPlaneResourceProperties{ @@ -393,7 +392,7 @@ func createRadiusPlane(ucp *testserver.TestServer, resourceProviders map[string] response.EqualsStatusCode(http.StatusOK) } -func createResourceGroup(ucp *testserver.TestServer, id string) { +func createResourceGroup(ucp *testhost.TestHost, id string) { body := v20231001preview.ResourceGroupResource{ Location: to.Ptr(v1.LocationGlobal), Properties: &v20231001preview.ResourceGroupProperties{}, diff --git a/pkg/ucp/integrationtests/resourcegroups/resourcegroups_test.go b/pkg/ucp/integrationtests/resourcegroups/resourcegroups_test.go index 5106fcf314..a047c3bde2 100644 --- a/pkg/ucp/integrationtests/resourcegroups/resourcegroups_test.go +++ b/pkg/ucp/integrationtests/resourcegroups/resourcegroups_test.go @@ -20,8 +20,7 @@ import ( "testing" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" - "github.com/radius-project/radius/pkg/ucp/frontend/api" - "github.com/radius-project/radius/pkg/ucp/integrationtests/testserver" + "github.com/radius-project/radius/pkg/ucp/testhost" ) const ( @@ -40,13 +39,13 @@ const ( resourceGroupInvalidResponseFixture = "testdata/resourcegroup_invalid_v20231001preview_responsebody.json" ) -func createRadiusPlane(server *testserver.TestServer) { +func createRadiusPlane(server *testhost.TestHost) { response := server.MakeFixtureRequest("PUT", radiusPlaneResourceURL, radiusPlaneRequestFixture) response.EqualsFixture(200, radiusPlaneResponseFixture) } func Test_ResourceGroup_PUT_Create(t *testing.T) { - server := testserver.StartWithETCD(t, api.DefaultModules) + server := testhost.Start(t) defer server.Close() createRadiusPlane(server) @@ -56,7 +55,7 @@ func Test_ResourceGroup_PUT_Create(t *testing.T) { } func Test_ResourceGroup_PUT_Update(t *testing.T) { - server := testserver.StartWithETCD(t, api.DefaultModules) + server := testhost.Start(t) defer server.Close() createRadiusPlane(server) @@ -69,7 +68,7 @@ func Test_ResourceGroup_PUT_Update(t *testing.T) { } func Test_ResourceGroup_PUT_APIValidation(t *testing.T) { - server := testserver.StartWithETCD(t, api.DefaultModules) + server := testhost.Start(t) defer server.Close() createRadiusPlane(server) @@ -79,7 +78,7 @@ func Test_ResourceGroup_PUT_APIValidation(t *testing.T) { } func Test_ResourceGroup_GET_Empty(t *testing.T) { - server := testserver.StartWithETCD(t, api.DefaultModules) + server := testhost.Start(t) defer server.Close() createRadiusPlane(server) @@ -89,7 +88,7 @@ func Test_ResourceGroup_GET_Empty(t *testing.T) { } func Test_ResourceGroup_GET_Found(t *testing.T) { - server := testserver.StartWithETCD(t, api.DefaultModules) + server := testhost.Start(t) defer server.Close() createRadiusPlane(server) @@ -102,7 +101,7 @@ func Test_ResourceGroup_GET_Found(t *testing.T) { } func Test_ResourceGroup_LIST(t *testing.T) { - server := testserver.StartWithETCD(t, api.DefaultModules) + server := testhost.Start(t) defer server.Close() createRadiusPlane(server) diff --git a/pkg/ucp/integrationtests/resourceproviders/apiversions_test.go b/pkg/ucp/integrationtests/resourceproviders/apiversions_test.go index 8ba1d22fce..71619894fd 100644 --- a/pkg/ucp/integrationtests/resourceproviders/apiversions_test.go +++ b/pkg/ucp/integrationtests/resourceproviders/apiversions_test.go @@ -20,8 +20,7 @@ import ( "net/http" "testing" - "github.com/radius-project/radius/pkg/ucp/frontend/api" - "github.com/radius-project/radius/pkg/ucp/integrationtests/testserver" + "github.com/radius-project/radius/pkg/ucp/testhost" "github.com/stretchr/testify/require" ) @@ -31,7 +30,7 @@ const ( ) func Test_APIVersion_Lifecycle(t *testing.T) { - server := testserver.StartWithETCD(t, api.DefaultModules) + server := testhost.Start(t) defer server.Close() createRadiusPlane(server) diff --git a/pkg/ucp/integrationtests/resourceproviders/locations_test.go b/pkg/ucp/integrationtests/resourceproviders/locations_test.go index 64923fbc9a..7fdd82045a 100644 --- a/pkg/ucp/integrationtests/resourceproviders/locations_test.go +++ b/pkg/ucp/integrationtests/resourceproviders/locations_test.go @@ -20,8 +20,7 @@ import ( "net/http" "testing" - "github.com/radius-project/radius/pkg/ucp/frontend/api" - "github.com/radius-project/radius/pkg/ucp/integrationtests/testserver" + "github.com/radius-project/radius/pkg/ucp/testhost" "github.com/stretchr/testify/require" ) @@ -31,7 +30,7 @@ const ( ) func Test_Location_Lifecycle(t *testing.T) { - server := testserver.StartWithETCD(t, api.DefaultModules) + server := testhost.Start(t) defer server.Close() createRadiusPlane(server) diff --git a/pkg/ucp/integrationtests/resourceproviders/resourceproviders_test.go b/pkg/ucp/integrationtests/resourceproviders/resourceproviders_test.go index a67000cf8b..7ad93f4096 100644 --- a/pkg/ucp/integrationtests/resourceproviders/resourceproviders_test.go +++ b/pkg/ucp/integrationtests/resourceproviders/resourceproviders_test.go @@ -20,8 +20,7 @@ import ( "net/http" "testing" - "github.com/radius-project/radius/pkg/ucp/frontend/api" - "github.com/radius-project/radius/pkg/ucp/integrationtests/testserver" + "github.com/radius-project/radius/pkg/ucp/testhost" "github.com/stretchr/testify/require" ) @@ -31,7 +30,7 @@ const ( ) func Test_ResourceProvider_Lifecycle(t *testing.T) { - server := testserver.StartWithETCD(t, api.DefaultModules) + server := testhost.Start(t) defer server.Close() createRadiusPlane(server) @@ -61,7 +60,7 @@ func Test_ResourceProvider_Lifecycle(t *testing.T) { } func Test_ResourceProvider_CascadingDelete(t *testing.T) { - server := testserver.StartWithETCD(t, api.DefaultModules) + server := testhost.Start(t) defer server.Close() createRadiusPlane(server) diff --git a/pkg/ucp/integrationtests/resourceproviders/resourcetypes_test.go b/pkg/ucp/integrationtests/resourceproviders/resourcetypes_test.go index b8ae588548..6db34e64ce 100644 --- a/pkg/ucp/integrationtests/resourceproviders/resourcetypes_test.go +++ b/pkg/ucp/integrationtests/resourceproviders/resourcetypes_test.go @@ -20,8 +20,7 @@ import ( "net/http" "testing" - "github.com/radius-project/radius/pkg/ucp/frontend/api" - "github.com/radius-project/radius/pkg/ucp/integrationtests/testserver" + "github.com/radius-project/radius/pkg/ucp/testhost" "github.com/stretchr/testify/require" ) @@ -31,7 +30,7 @@ const ( ) func Test_ResourceType_Lifecycle(t *testing.T) { - server := testserver.StartWithETCD(t, api.DefaultModules) + server := testhost.Start(t) defer server.Close() createRadiusPlane(server) @@ -64,7 +63,7 @@ func Test_ResourceType_Lifecycle(t *testing.T) { } func Test_ResourceType_CascadingDelete(t *testing.T) { - server := testserver.StartWithETCD(t, api.DefaultModules) + server := testhost.Start(t) defer server.Close() createRadiusPlane(server) diff --git a/pkg/ucp/integrationtests/resourceproviders/summary_test.go b/pkg/ucp/integrationtests/resourceproviders/summary_test.go index 9fbfb19b35..096f3165ec 100644 --- a/pkg/ucp/integrationtests/resourceproviders/summary_test.go +++ b/pkg/ucp/integrationtests/resourceproviders/summary_test.go @@ -23,13 +23,12 @@ import ( "github.com/radius-project/radius/pkg/to" "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" - "github.com/radius-project/radius/pkg/ucp/frontend/api" - "github.com/radius-project/radius/pkg/ucp/integrationtests/testserver" + "github.com/radius-project/radius/pkg/ucp/testhost" "github.com/stretchr/testify/require" ) func Test_ResourceProviderSummary_Lifecycle(t *testing.T) { - server := testserver.StartWithETCD(t, api.DefaultModules) + server := testhost.Start(t) defer server.Close() createRadiusPlane(server) diff --git a/pkg/ucp/integrationtests/resourceproviders/util_test.go b/pkg/ucp/integrationtests/resourceproviders/util_test.go index efca2af228..3378a8281a 100644 --- a/pkg/ucp/integrationtests/resourceproviders/util_test.go +++ b/pkg/ucp/integrationtests/resourceproviders/util_test.go @@ -16,7 +16,7 @@ limitations under the License. package resourceproviders -import "github.com/radius-project/radius/pkg/ucp/integrationtests/testserver" +import "github.com/radius-project/radius/pkg/ucp/testhost" const ( radiusAPIVersion = "?api-version=2023-10-01-preview" @@ -56,7 +56,7 @@ const ( resourceProviderSummaryURL = "/planes/radius/local/providers/" + resourceProviderNamespace + radiusAPIVersion ) -func createRadiusPlane(server *testserver.TestServer) { +func createRadiusPlane(server *testhost.TestHost) { response := server.MakeFixtureRequest("PUT", radiusPlaneResourceURL, radiusPlaneRequestFixture) response.WaitForOperationComplete(nil) @@ -64,7 +64,7 @@ func createRadiusPlane(server *testserver.TestServer) { response.EqualsFixture(200, radiusPlaneResponseFixture) } -func createResourceProvider(server *testserver.TestServer) { +func createResourceProvider(server *testhost.TestHost) { response := server.MakeFixtureRequest("PUT", resourceProviderURL, resourceProviderRequestFixture) response.WaitForOperationComplete(nil) @@ -72,7 +72,7 @@ func createResourceProvider(server *testserver.TestServer) { response.EqualsFixture(200, resourceProviderResponseFixture) } -func deleteResourceProvider(server *testserver.TestServer) { +func deleteResourceProvider(server *testhost.TestHost) { response := server.MakeRequest("DELETE", resourceProviderURL, nil) response.WaitForOperationComplete(nil) @@ -80,7 +80,7 @@ func deleteResourceProvider(server *testserver.TestServer) { response.EqualsStatusCode(404) } -func createResourceType(server *testserver.TestServer) { +func createResourceType(server *testhost.TestHost) { response := server.MakeFixtureRequest("PUT", resourceTypeURL, resourceTypeRequestFixture) response.WaitForOperationComplete(nil) @@ -88,7 +88,7 @@ func createResourceType(server *testserver.TestServer) { response.EqualsFixture(200, resourceTypeResponseFixture) } -func deleteResourceType(server *testserver.TestServer) { +func deleteResourceType(server *testhost.TestHost) { response := server.MakeRequest("DELETE", resourceTypeURL, nil) response.WaitForOperationComplete(nil) @@ -96,7 +96,7 @@ func deleteResourceType(server *testserver.TestServer) { response.EqualsStatusCode(404) } -func createAPIVersion(server *testserver.TestServer) { +func createAPIVersion(server *testhost.TestHost) { response := server.MakeFixtureRequest("PUT", apiVersionURL, apiVersionRequestFixture) response.WaitForOperationComplete(nil) @@ -104,7 +104,7 @@ func createAPIVersion(server *testserver.TestServer) { response.EqualsFixture(200, apiVersionResponseFixture) } -func deleteAPIVersion(server *testserver.TestServer) { +func deleteAPIVersion(server *testhost.TestHost) { response := server.MakeRequest("DELETE", apiVersionURL, nil) response.WaitForOperationComplete(nil) @@ -112,7 +112,7 @@ func deleteAPIVersion(server *testserver.TestServer) { response.EqualsStatusCode(404) } -func createLocation(server *testserver.TestServer) { +func createLocation(server *testhost.TestHost) { response := server.MakeFixtureRequest("PUT", locationURL, locationRequestFixture) response.WaitForOperationComplete(nil) @@ -120,7 +120,7 @@ func createLocation(server *testserver.TestServer) { response.EqualsFixture(200, locationResponseFixture) } -func deleteLocation(server *testserver.TestServer) { +func deleteLocation(server *testhost.TestHost) { response := server.MakeRequest("DELETE", locationURL, nil) response.WaitForOperationComplete(nil) diff --git a/pkg/ucp/integrationtests/testrp/async.go b/pkg/ucp/integrationtests/testrp/async.go index 1fbcac1098..ce8cb52596 100644 --- a/pkg/ucp/integrationtests/testrp/async.go +++ b/pkg/ucp/integrationtests/testrp/async.go @@ -33,7 +33,7 @@ import ( "github.com/radius-project/radius/pkg/armrpc/servicecontext" "github.com/radius-project/radius/pkg/components/queue/queueprovider" "github.com/radius-project/radius/pkg/middleware" - "github.com/radius-project/radius/pkg/ucp/integrationtests/testserver" + "github.com/radius-project/radius/pkg/ucp/testhost" "github.com/radius-project/radius/test/testcontext" "github.com/stretchr/testify/require" ) @@ -50,7 +50,7 @@ func (b *BackendFuncController) Run(ctx context.Context, request *backend_ctrl.R } // AsyncResource creates an HTTP handler that can be used to test asynchronous resource lifecycle operations. -func AsyncResource(t *testing.T, ts *testserver.TestServer, rootScope string, put BackendFunc, delete BackendFunc) func(w http.ResponseWriter, r *http.Request) { +func AsyncResource(t *testing.T, ts *testhost.TestHost, rootScope string, put BackendFunc, delete BackendFunc) func(w http.ResponseWriter, r *http.Request) { rootScope = strings.ToLower(rootScope) ctx := testcontext.New(t) @@ -60,7 +60,7 @@ func AsyncResource(t *testing.T, ts *testserver.TestServer, rootScope string, pu resourceType := "System.Test/testResources" // We can share the database provider with the test server. - databaseClient, err := ts.Clients.DatabaseProvider.GetClient(ctx) + databaseClient, err := ts.Options().DatabaseProvider.GetClient(ctx) require.NoError(t, err) // Do not share the queue. diff --git a/pkg/ucp/integrationtests/testrp/sync.go b/pkg/ucp/integrationtests/testrp/sync.go index 8adcf7e1cd..af69509eef 100644 --- a/pkg/ucp/integrationtests/testrp/sync.go +++ b/pkg/ucp/integrationtests/testrp/sync.go @@ -30,13 +30,13 @@ import ( "github.com/radius-project/radius/pkg/armrpc/servicecontext" "github.com/radius-project/radius/pkg/components/queue/queueprovider" "github.com/radius-project/radius/pkg/middleware" - "github.com/radius-project/radius/pkg/ucp/integrationtests/testserver" + "github.com/radius-project/radius/pkg/ucp/testhost" "github.com/radius-project/radius/test/testcontext" "github.com/stretchr/testify/require" ) // SyncResource creates an HTTP handler that can be used to test synchronous resource lifecycle operations. -func SyncResource(t *testing.T, ts *testserver.TestServer, rootScope string) func(w http.ResponseWriter, r *http.Request) { +func SyncResource(t *testing.T, ts *testhost.TestHost, rootScope string) func(w http.ResponseWriter, r *http.Request) { rootScope = strings.ToLower(rootScope) ctx := testcontext.New(t) @@ -44,7 +44,7 @@ func SyncResource(t *testing.T, ts *testserver.TestServer, rootScope string) fun r.Use(servicecontext.ARMRequestCtx("", v1.LocationGlobal), middleware.LowercaseURLPath) // We can share the database provider with the test server. - databaseClient, err := ts.Clients.DatabaseProvider.GetClient(ctx) + databaseClient, err := ts.Options().DatabaseProvider.GetClient(ctx) require.NoError(t, err) // Do not share the queue. diff --git a/pkg/ucp/integrationtests/testserver/testserver.go b/pkg/ucp/integrationtests/testserver/testserver.go deleted file mode 100644 index 9c59873171..0000000000 --- a/pkg/ucp/integrationtests/testserver/testserver.go +++ /dev/null @@ -1,604 +0,0 @@ -/* -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 testserver - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "io" - "net" - "net/http" - "net/http/httptest" - "net/url" - "os" - "sync" - "testing" - "time" - - "github.com/go-chi/chi/v5" - "github.com/go-logr/logr" - "github.com/google/uuid" - "github.com/stretchr/testify/require" - etcdclient "go.etcd.io/etcd/client/v3" - "go.uber.org/mock/gomock" - - v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" - backend_ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" - "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" - "github.com/radius-project/radius/pkg/armrpc/asyncoperation/worker" - "github.com/radius-project/radius/pkg/armrpc/rpctest" - "github.com/radius-project/radius/pkg/armrpc/servicecontext" - "github.com/radius-project/radius/pkg/components/database" - "github.com/radius-project/radius/pkg/components/database/databaseprovider" - "github.com/radius-project/radius/pkg/components/queue" - "github.com/radius-project/radius/pkg/components/queue/queueprovider" - "github.com/radius-project/radius/pkg/components/secret" - "github.com/radius-project/radius/pkg/components/secret/secretprovider" - "github.com/radius-project/radius/pkg/middleware" - "github.com/radius-project/radius/pkg/sdk" - "github.com/radius-project/radius/pkg/ucp/backend" - "github.com/radius-project/radius/pkg/ucp/data" - "github.com/radius-project/radius/pkg/ucp/frontend/api" - "github.com/radius-project/radius/pkg/ucp/frontend/modules" - "github.com/radius-project/radius/pkg/ucp/hosting" - "github.com/radius-project/radius/pkg/ucp/hostoptions" - "github.com/radius-project/radius/pkg/ucp/server" - "github.com/radius-project/radius/pkg/validator" - "github.com/radius-project/radius/swagger" - "github.com/radius-project/radius/test/testcontext" -) - -// NoModules can be used to start a test server without any modules. This is useful for testing the server itself and core functionality -// like planes. -func NoModules(options modules.Options) []modules.Initializer { - return nil -} - -// TestServer can run a UCP server using the Go httptest package. It provides access to an isolated ETCD instances for storage -// of resources and secrets. Alteratively, it can also be used with gomock. -// -// Do not create a TestServer directly, use StartWithETCD or StartWithMocks instead. -type TestServer struct { - // BaseURL is the base URL of the server, including the path base. - BaseURL string - - // Clients gets access to the clients created by TestServer regardless of whether - // they are mocks. - Clients *TestServerClients - - // Mocks gets access to the mock clients. Will be nil if StartWithETCD is used. - Mocks *TestServerMocks - - // Server provides access to the test HTTP server. - Server *httptest.Server - - cancel context.CancelFunc - etcdService *data.EmbeddedETCDService - etcdClient *etcdclient.Client - t *testing.T - stoppedChan <-chan struct{} - shutdown sync.Once -} - -// TestServerClients provides access to the clients created by the TestServer. -type TestServerClients struct { - // QueueProvider is the queue client provider. - QueueProvider *queueprovider.QueueProvider - - // SecretProvider is the secret client provider. - SecretProvider *secretprovider.SecretProvider - - // DatabaseProvider is the database client provider. - DatabaseProvider *databaseprovider.DatabaseProvider -} - -// TestServerMocks provides access to mock instances created by the TestServer. -type TestServerMocks struct { - // Secrets is the mock secret client. - Secrets *secret.MockClient - - // Database is the mock database client. - Database *database.MockClient -} - -// Client provides access to an http.Client that can be used to send requests. Most tests should use the functionality -// like MakeRequest instead of testing the client directly. -func (ts *TestServer) Client() *http.Client { - return ts.Server.Client() -} - -// Close shuts down the server and will block until shutdown completes. -func (ts *TestServer) Close() { - // We're being picking about resource cleanup here, because unless we are picky we hit scalability - // problems in tests pretty quickly. - ts.shutdown.Do(func() { - ts.cancel() // Start ETCD shutdown - ts.Server.Close() // Stop HTTP server - - if ts.etcdClient != nil { - ts.etcdClient.Close() // Stop ETCD Client - } - - if ts.stoppedChan != nil { - <-ts.stoppedChan // ETCD stopped - } - }) -} - -// StartWithMocks creates and starts a new TestServer that used an mocks for storage. -func StartWithMocks(t *testing.T, configureModules func(options modules.Options) []modules.Initializer) *TestServer { - ctx, cancel := testcontext.NewWithCancel(t) - - // Generate a random base path to ensure we're handling it correctly. - pathBase := "/" + uuid.New().String() - - ctrl := gomock.NewController(t) - databaseClient := database.NewMockClient(ctrl) - databaseProvider := databaseprovider.FromClient(databaseClient) - - queueClient := queue.NewMockClient(ctrl) - queueProvider := queueprovider.New(queueprovider.QueueProviderOptions{Name: "System.Resources"}) - queueProvider.SetClient(queueClient) - - secretClient := secret.NewMockClient(ctrl) - secretProvider := secretprovider.NewSecretProvider(secretprovider.SecretProviderOptions{}) - secretProvider.SetClient(secretClient) - - statusManager := statusmanager.NewMockStatusManager(ctrl) - - router := chi.NewRouter() - router.Use(servicecontext.ARMRequestCtx(pathBase, "global")) - - app := http.Handler(router) - app = middleware.NormalizePath(app) - server := httptest.NewUnstartedServer(app) - server.Config.BaseContext = func(l net.Listener) context.Context { - return ctx - } - - specLoader, err := validator.LoadSpec(ctx, "ucp", swagger.SpecFilesUCP, []string{pathBase}, "") - require.NoError(t, err, "failed to load OpenAPI spec") - - options := modules.Options{ - Address: "localhost:9999", // Will be dynamically populated when server is started - PathBase: pathBase, - Config: &hostoptions.UCPConfig{}, - DatabaseProvider: databaseProvider, - SecretProvider: secretProvider, - SpecLoader: specLoader, - StatusManager: statusManager, - } - - if configureModules == nil { - configureModules = api.DefaultModules - } - - modules := configureModules(options) - - err = api.Register(ctx, router, modules, options) - require.NoError(t, err) - - logger := logr.FromContextOrDiscard(ctx) - logger.Info("Starting HTTP server...") - server.Start() - logger.Info(fmt.Sprintf("Started HTTP server on %s...", server.URL)) - - ucp := &TestServer{ - BaseURL: server.URL + pathBase, - Clients: &TestServerClients{ - QueueProvider: queueProvider, - SecretProvider: secretProvider, - DatabaseProvider: databaseProvider, - }, - Mocks: &TestServerMocks{ - Secrets: secretClient, - Database: databaseClient, - }, - Server: server, - cancel: cancel, - t: t, - } - - t.Cleanup(ucp.Close) - return ucp -} - -// StartWithETCD creates and starts a new TestServer that used an embedded ETCD instance for storage. -func StartWithETCD(t *testing.T, configureModules func(options modules.Options) []modules.Initializer) *TestServer { - config := hosting.NewAsyncValue[etcdclient.Client]() - etcd := data.NewEmbeddedETCDService(data.EmbeddedETCDServiceOptions{ - ClientConfigSink: config, - AssignRandomPorts: true, - Quiet: false, - }) - - ctx, cancel := testcontext.NewWithCancel(t) - t.Cleanup(cancel) - - stoppedChan := make(chan struct{}) - defer close(stoppedChan) - go func() { - // We can't pass the test logger into the etcd service because it is forbidden to log - // using the test logger after the test finishes. - // - // https://github.com/golang/go/issues/40343 - // - // If you need to see the logging output while you are testing, then comment out the next line - // and you'll be able to see the spam from etcd. - // - // This is caught by the race checker and will fail your pr if you do it. - ctx, cancel := context.WithCancel(context.Background()) - t.Cleanup(cancel) - - err := etcd.Run(ctx) - if err != nil { - t.Logf("error from etcd: %v", err) - } - }() - - databaseOptions := databaseprovider.Options{ - Provider: databaseprovider.TypeETCD, - ETCD: databaseprovider.ETCDOptions{ - InMemory: true, - Client: config, - }, - } - secretOptions := secretprovider.SecretProviderOptions{ - Provider: secretprovider.TypeETCDSecret, - ETCD: databaseOptions.ETCD, - } - queueOptions := queueprovider.QueueProviderOptions{ - Name: server.UCPProviderName, - Provider: queueprovider.TypeInmemory, - InMemory: &queueprovider.InMemoryQueueOptions{}, - } - - // Generate a random base path to ensure we're handling it correctly. - pathBase := "/" + uuid.New().String() - databaseProvider := databaseprovider.FromOptions(databaseOptions) - secretProvider := secretprovider.NewSecretProvider(secretOptions) - queueProvider := queueprovider.New(queueOptions) - - queueClient, err := queueProvider.GetClient(ctx) - require.NoError(t, err) - - router := chi.NewRouter() - router.Use(servicecontext.ARMRequestCtx(pathBase, "global")) - - app := middleware.NormalizePath(router) - server := httptest.NewUnstartedServer(app) - server.Config.BaseContext = func(l net.Listener) context.Context { - return ctx - } - - // Unfortunately the server doesn't populate 'server.URL' until it is started, so we have to build it ourselves. - address := "http://" + server.Listener.Addr().String() - connection, err := sdk.NewDirectConnection(address + pathBase) - require.NoError(t, err) - - databaseClient, err := databaseProvider.GetClient(ctx) - require.NoError(t, err) - - statusManager := statusmanager.New(databaseClient, queueClient, v1.LocationGlobal) - - specLoader, err := validator.LoadSpec(ctx, "ucp", swagger.SpecFilesUCP, []string{pathBase}, "") - require.NoError(t, err, "failed to load OpenAPI spec") - - options := modules.Options{ - Address: address, - PathBase: pathBase, - Config: &hostoptions.UCPConfig{}, - DatabaseProvider: databaseProvider, - SecretProvider: secretProvider, - SpecLoader: specLoader, - QueueProvider: queueProvider, - StatusManager: statusManager, - } - - if configureModules == nil { - configureModules = api.DefaultModules - } - - modules := configureModules(options) - - // The URL for the dynamic-rp needs to be specified in configuration, however not all of our tests - // need to use the dynamic-rp. We can just use a placeholder value here. - if options.Config.Routing.DefaultDownstreamEndpoint == "" { - options.Config.Routing.DefaultDownstreamEndpoint = "http://localhost:65535" - } - - defaultDownstream, err := url.Parse(options.Config.Routing.DefaultDownstreamEndpoint) - require.NoError(t, err) - - registry := worker.NewControllerRegistry() - err = backend.RegisterControllers(registry, connection, http.DefaultTransport, backend_ctrl.Options{DatabaseClient: databaseClient}, defaultDownstream) - require.NoError(t, err) - - w := worker.New(worker.Options{}, statusManager, queueClient, registry) - go func() { - err = w.Start(ctx) - require.NoError(t, err) - }() - - err = api.Register(ctx, router, modules, options) - require.NoError(t, err) - - logger := logr.FromContextOrDiscard(ctx) - logger.Info("Starting HTTP server...") - server.Start() - logger.Info(fmt.Sprintf("Started HTTP server on %s...", server.URL)) - - logger.Info("Connecting to data store...") - client, err := config.Get(ctx) - require.NoError(t, err, "failed to access etcd client") - _, err = client.Cluster.MemberList(ctx) - require.NoError(t, err, "failed to query etcd") - logger.Info("Connected to data store") - - // TODO: start worker - - ucp := &TestServer{ - BaseURL: server.URL + pathBase, - Clients: &TestServerClients{ - QueueProvider: queueProvider, - SecretProvider: secretProvider, - DatabaseProvider: databaseProvider, - }, - Server: server, - cancel: cancel, - etcdService: etcd, - etcdClient: client, - t: t, - stoppedChan: stoppedChan, - } - t.Cleanup(ucp.Close) - return ucp -} - -// TestResponse is return from requests made against a TestServer. Tests should use the functions defined -// on TestResponse for valiation. -type TestResponse struct { - Raw *http.Response - Body *bytes.Buffer - Error *v1.ErrorResponse - t *testing.T - server *TestServer -} - -// MakeFixtureRequest sends a request to the server using a file on disk as the payload (body). Use the fixture -// parameter to specify the path to a file. -func (ts *TestServer) MakeFixtureRequest(method string, pathAndQuery string, fixture string) *TestResponse { - body, err := os.ReadFile(fixture) - require.NoError(ts.t, err, "reading fixture failed") - return ts.MakeRequest(method, pathAndQuery, body) -} - -// MakeTypedRequest sends a request to the server by marshalling the provided object to JSON. -func (ts *TestServer) MakeTypedRequest(method string, pathAndQuery string, body any) *TestResponse { - if body == nil { - return ts.MakeRequest(method, pathAndQuery, nil) - } - - b, err := json.Marshal(body) - require.NoError(ts.t, err, "marshalling body failed") - return ts.MakeRequest(method, pathAndQuery, b) -} - -// MakeRequest sends a request to the server. -func (ts *TestServer) MakeRequest(method string, pathAndQuery string, body []byte) *TestResponse { - // Prepend the base path if this is a relative URL. - requestUrl := pathAndQuery - parsed, err := url.Parse(pathAndQuery) - require.NoError(ts.t, err, "parsing URL failed") - if !parsed.IsAbs() { - requestUrl = ts.BaseURL + pathAndQuery - } - - client := ts.Server.Client() - request, err := rpctest.NewHTTPRequestWithContent(context.Background(), method, requestUrl, body) - require.NoError(ts.t, err, "creating request failed") - - ctx := rpctest.NewARMRequestContext(request) - request = request.WithContext(ctx) - - response, err := client.Do(request) - require.NoError(ts.t, err, "sending request failed") - - // Buffer the response so we can read multiple times. - responseBuffer := &bytes.Buffer{} - _, err = io.Copy(responseBuffer, response.Body) - response.Body.Close() - require.NoError(ts.t, err, "copying response failed") - - response.Body = io.NopCloser(responseBuffer) - - // Pretty-print response for logs. - if len(responseBuffer.Bytes()) > 0 { - var data any - err = json.Unmarshal(responseBuffer.Bytes(), &data) - require.NoError(ts.t, err, "unmarshalling response failed") - - text, err := json.MarshalIndent(&data, "", " ") - require.NoError(ts.t, err, "marshalling response failed") - ts.t.Log("Response Body: \n" + string(text)) - } - - var errorResponse *v1.ErrorResponse - if response.StatusCode >= 400 { - // The response MUST be an arm error for a non-success status code. - errorResponse = &v1.ErrorResponse{} - err := json.Unmarshal(responseBuffer.Bytes(), &errorResponse) - require.NoError(ts.t, err, "unmarshalling error response failed - THIS IS A SERIOUS BUG. ALL ERROR RESPONSES MUST USE THE STANDARD FORMAT") - } - - return &TestResponse{Raw: response, Body: responseBuffer, Error: errorResponse, server: ts, t: ts.t} -} - -// EqualsErrorCode compares a TestResponse against an expected status code and error code. EqualsErrorCode assumes the response -// uses the ARM error format (required for our APIs). -func (tr *TestResponse) EqualsErrorCode(statusCode int, code string) { - require.Equal(tr.t, statusCode, tr.Raw.StatusCode, "status code did not match expected") - require.NotNil(tr.t, tr.Error, "expected an error but actual response did not contain one") - require.Equal(tr.t, code, tr.Error.Error.Code, "actual error code was different from expected") -} - -// EqualsFixture compares a TestResponse against an expected status code and body payload. Use the fixture parameter to specify -// the path to a file. -func (tr *TestResponse) EqualsFixture(statusCode int, fixture string) { - body, err := os.ReadFile(fixture) - require.NoError(tr.t, err, "reading fixture failed") - tr.EqualsResponse(statusCode, body) -} - -// EqualsStatusCode compares a TestResponse against an expected status code (ingnores the body payload). -func (tr *TestResponse) EqualsStatusCode(statusCode int) { - require.Equal(tr.t, statusCode, tr.Raw.StatusCode, "status code did not match expected") -} - -// EqualsFixture compares a TestResponse against an expected status code and body payload. -func (tr *TestResponse) EqualsResponse(statusCode int, body []byte) { - if len(body) == 0 { - require.Equal(tr.t, statusCode, tr.Raw.StatusCode, "status code did not match expected") - require.Empty(tr.t, tr.Body.Bytes(), "expected an empty response but actual response had a body") - return - } - - var expected map[string]any - err := json.Unmarshal(body, &expected) - require.NoError(tr.t, err, "unmarshalling expected response failed") - - var actual map[string]any - err = json.Unmarshal(tr.Body.Bytes(), &actual) - - tr.removeSystemData(actual) - - require.NoError(tr.t, err, "unmarshalling actual response failed") - require.EqualValues(tr.t, expected, actual, "response body did not match expected") - require.Equal(tr.t, statusCode, tr.Raw.StatusCode, "status code did not match expected") -} - -// EqualsValue compares a TestResponse against an expected status code and an response body. -// -// If the systemData propert is present in the response, it will be removed. -func (tr *TestResponse) EqualsValue(statusCode int, expected any) { - var actual map[string]any - err := json.Unmarshal(tr.Body.Bytes(), &actual) - require.NoError(tr.t, err, "unmarshalling actual response failed") - - // Convert expected input to map[string]any to compare with actual response. - expectedBytes, err := json.Marshal(expected) - require.NoError(tr.t, err, "marshalling expected response failed") - - var expectedMap map[string]any - err = json.Unmarshal(expectedBytes, &expectedMap) - require.NoError(tr.t, err, "unmarshalling expected response failed") - - tr.removeSystemData(expectedMap) - tr.removeSystemData(actual) - - require.EqualValues(tr.t, expectedMap, actual, "response body did not match expected") - require.Equal(tr.t, statusCode, tr.Raw.StatusCode, "status code did not match expected") -} - -// EqualsEmptyList compares a TestResponse against an expected status code and an empty resource list. -func (tr *TestResponse) EqualsEmptyList() { - expected := map[string]any{ - "value": []any{}, - } - - var actual map[string]any - err := json.Unmarshal(tr.Body.Bytes(), &actual) - - tr.removeSystemData(actual) - - require.NoError(tr.t, err, "unmarshalling actual response failed") - require.EqualValues(tr.t, expected, actual, "response body did not match expected") - require.Equal(tr.t, http.StatusOK, tr.Raw.StatusCode, "status code did not match expected") -} - -func (tr *TestResponse) ReadAs(obj any) { - tr.t.Helper() - - decoder := json.NewDecoder(tr.Body) - decoder.DisallowUnknownFields() - - err := decoder.Decode(obj) - require.NoError(tr.t, err, "unmarshalling expected response failed") -} - -func (tr *TestResponse) WaitForOperationComplete(timeout *time.Duration) *TestResponse { - if tr.Raw.StatusCode != http.StatusCreated && tr.Raw.StatusCode != http.StatusAccepted { - // Response is already terminal. - return tr - } - - if timeout == nil { - x := 30 * time.Second - timeout = &x - } - - timer := time.After(*timeout) - poller := time.NewTicker(1 * time.Second) - defer poller.Stop() - for { - select { - case <-timer: - tr.t.Fatalf("timed out waiting for operation to complete") - return nil // unreachable - case <-poller.C: - // The Location header should give us the operation status URL. - response := tr.server.MakeRequest(http.MethodGet, tr.Raw.Header.Get("Azure-AsyncOperation"), nil) - // To determine if the response is terminal we need to read the provisioning state field. - - operationStatus := v1.AsyncOperationStatus{} - response.ReadAs(&operationStatus) - if operationStatus.Status.IsTerminal() { - // Response is terminal. - return response - } - - continue - } - } -} - -func (tr *TestResponse) removeSystemData(responseBody map[string]any) { - // Delete systemData property if found, it's not stable so we don't include it in baselines. - _, ok := responseBody["systemData"] - if ok { - delete(responseBody, "systemData") - return - } - - value, ok := responseBody["value"] - if !ok { - return - } - - valueSlice, ok := value.([]any) - if !ok { - return - } - - for _, v := range valueSlice { - if vMap, ok := v.(map[string]any); ok { - tr.removeSystemData(vMap) - } - } -} diff --git a/pkg/ucp/options.go b/pkg/ucp/options.go new file mode 100644 index 0000000000..0e151f1e9d --- /dev/null +++ b/pkg/ucp/options.go @@ -0,0 +1,116 @@ +/* +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 ucp + +import ( + "context" + "fmt" + + "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" + "github.com/radius-project/radius/pkg/components/database/databaseprovider" + "github.com/radius-project/radius/pkg/components/queue/queueprovider" + "github.com/radius-project/radius/pkg/components/secret/secretprovider" + "github.com/radius-project/radius/pkg/kubeutil" + "github.com/radius-project/radius/pkg/sdk" + ucpconfig "github.com/radius-project/radius/pkg/ucp/config" + "github.com/radius-project/radius/pkg/ucp/frontend/modules" + "github.com/radius-project/radius/pkg/validator" + "github.com/radius-project/radius/swagger" + kube_rest "k8s.io/client-go/rest" +) + +// Options holds the configuration options and shared services for the UCP server. +// +// For testability, all fields on this struct MUST be constructed from the NewOptions function without any +// additional initialization required. +type Options struct { + // Config is the configuration for the server. + Config *Config + + // DatabaseProvider provides access to the database used for resource data. + DatabaseProvider *databaseprovider.DatabaseProvider + + // Modules is the list of modules to initialize. This will default to nil (implying the default set), and + // can be overridden by tests. + Modules []modules.Initializer + + // QueueProvider provides access to the message queue client. + QueueProvider *queueprovider.QueueProvider + + // SecretProvider provides access to secret store used for secret data. + SecretProvider *secretprovider.SecretProvider + + // SpecLoader is the loader for the OpenAPI spec. + SpecLoader *validator.Loader + + // StatusManager implements operations on async operation statuses. + StatusManager statusmanager.StatusManager + + // UCP is the connection to UCP + UCP sdk.Connection +} + +// NewOptions creates a new Options instance from the given configuration. +func NewOptions(ctx context.Context, config *Config) (*Options, error) { + var err error + options := Options{ + Config: config, + + Modules: nil, // Default to nil, which implies the default set of modules. + } + + options.DatabaseProvider = databaseprovider.FromOptions(config.Database) + options.QueueProvider = queueprovider.New(config.Queue) + options.SecretProvider = secretprovider.NewSecretProvider(config.Secrets) + + databaseClient, err := options.DatabaseProvider.GetClient(ctx) + if err != nil { + return nil, err + } + + queueClient, err := options.QueueProvider.GetClient(ctx) + if err != nil { + return nil, err + } + + options.StatusManager = statusmanager.New(databaseClient, queueClient, config.Environment.RoleLocation) + + options.SpecLoader, err = validator.LoadSpec(ctx, "ucp", swagger.SpecFilesUCP, []string{config.Server.PathBase}, "") + if err != nil { + return nil, err + } + + var cfg *kube_rest.Config + if config.UCP.Kind == ucpconfig.UCPConnectionKindKubernetes { + cfg, err = kubeutil.NewClientConfig(&kubeutil.ConfigOptions{ + // TODO: Allow to use custom context via configuration. - https://github.com/radius-project/radius/issues/5433 + ContextName: "", + QPS: kubeutil.DefaultServerQPS, + Burst: kubeutil.DefaultServerBurst, + }) + if err != nil { + return nil, fmt.Errorf("failed to get kubernetes config: %w", err) + } + } + + options.UCP, err = ucpconfig.NewConnectionFromUCPConfig(&config.UCP, cfg) + if err != nil { + return nil, err + } + + return &options, nil +} diff --git a/pkg/ucp/rest/objects.go b/pkg/ucp/rest/objects.go deleted file mode 100644 index 0558fc7c58..0000000000 --- a/pkg/ucp/rest/objects.go +++ /dev/null @@ -1,69 +0,0 @@ -/* -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 rest - -import "strings" - -type PlaneProperties struct { - ResourceProviders map[string]string `json:"resourceProviders" yaml:"resourceProviders"` // Used only for UCP native planes - Kind string `json:"kind" yaml:"kind"` - URL string `json:"url" yaml:"url"` // Used only for non UCP native planes and non AWS planes -} - -// Plane kinds -const ( - PlaneKindUCPNative = "UCPNative" - PlaneKindAzure = "Azure" - PlaneKindAWS = "AWS" -) - -type Plane struct { - ID string `json:"id" yaml:"id"` - Type string `json:"type" yaml:"type"` - Name string `json:"name" yaml:"name"` - Properties PlaneProperties `json:"properties" yaml:"properties"` -} - -// PlaneList represents a list of UCP planes in the ARM wire-format -type PlaneList struct { - Value []Plane `json:"value" yaml:"value"` -} - -// Resource represents a resource within a UCP resource group -type Resource struct { - ID string `json:"id" yaml:"id"` - Name string `json:"name" yaml:"name"` - ProvisioningState string `json:"provisioningState" yaml:"provisioningState"` - Type string `json:"type" yaml:"type"` -} - -// ResourceList represents a list of resources -type ResourceList struct { - Value []Resource `json:"value" yaml:"value"` -} - -// LookupResourceProvider searches through the ResourceProviders configured in UCP. -func (plane *Plane) LookupResourceProvider(key string) string { - var value string - for k, v := range plane.Properties.ResourceProviders { - if strings.EqualFold(k, key) { - value = v - break - } - } - return value -} diff --git a/pkg/ucp/server/server.go b/pkg/ucp/server/server.go index 375497a1cb..a656330a4a 100644 --- a/pkg/ucp/server/server.go +++ b/pkg/ucp/server/server.go @@ -17,192 +17,45 @@ limitations under the License. package server import ( - "errors" - "fmt" - "os" - "strings" - "time" - - hostopts "github.com/radius-project/radius/pkg/armrpc/hostoptions" "github.com/radius-project/radius/pkg/components/database/databaseprovider" - "github.com/radius-project/radius/pkg/components/queue/queueprovider" - "github.com/radius-project/radius/pkg/components/secret/secretprovider" - "github.com/radius-project/radius/pkg/kubeutil" - metricsprovider "github.com/radius-project/radius/pkg/metrics/provider" metricsservice "github.com/radius-project/radius/pkg/metrics/service" - profilerprovider "github.com/radius-project/radius/pkg/profiler/provider" profilerservice "github.com/radius-project/radius/pkg/profiler/service" - "github.com/radius-project/radius/pkg/sdk" "github.com/radius-project/radius/pkg/trace" + "github.com/radius-project/radius/pkg/ucp" "github.com/radius-project/radius/pkg/ucp/backend" - "github.com/radius-project/radius/pkg/ucp/config" "github.com/radius-project/radius/pkg/ucp/data" "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/rest" - "github.com/radius-project/radius/pkg/ucp/ucplog" - - kube_rest "k8s.io/client-go/rest" -) - -const ( - HTTPServerStopTimeout = time.Second * 10 - ServiceName = "ucp" ) -type Options struct { - Config *hostoptions.UCPConfig - Port string - DatabaseProviderOptions databaseprovider.Options - LoggingOptions ucplog.LoggingOptions - SecretProviderOptions secretprovider.SecretProviderOptions - QueueProviderOptions queueprovider.QueueProviderOptions - MetricsProviderOptions metricsprovider.MetricsProviderOptions - ProfilerProviderOptions profilerprovider.ProfilerProviderOptions - TracerProviderOptions trace.Options - TLSCertDir string - PathBase string - InitialPlanes []rest.Plane - Identity hostoptions.Identity - UCPConnection sdk.Connection - Location string -} - -const UCPProviderName = "System.Resources" - -// NewServerOptionsFromEnvironment creates a new Options struct from environment variables and returns it along with any errors. -func NewServerOptionsFromEnvironment(configFilePath string) (Options, error) { - basePath, ok := os.LookupEnv("BASE_PATH") - if ok && len(basePath) > 0 && (!strings.HasPrefix(basePath, "/") || strings.HasSuffix(basePath, "/")) { - return Options{}, errors.New("env: BASE_PATH must begin with '/' and must not end with '/'") - } - - tlsCertDir := os.Getenv("TLS_CERT_DIR") - port := os.Getenv("PORT") - if port == "" { - return Options{}, errors.New("UCP Port number must be set") - } - - opts, err := hostoptions.NewHostOptionsFromEnvironment(configFilePath) - if err != nil { - return Options{}, err - } - - storeOpts := opts.Config.DatabaseProvider - planes := opts.Config.Planes - secretOpts := opts.Config.SecretProvider - qproviderOpts := opts.Config.QueueProvider - metricsOpts := opts.Config.MetricsProvider - traceOpts := opts.Config.TracerProvider - profilerOpts := opts.Config.ProfilerProvider - loggingOpts := opts.Config.Logging - identity := opts.Config.Identity - // Set the default authentication method if AuthMethod is not set. - if identity.AuthMethod == "" { - identity.AuthMethod = hostoptions.AuthDefault - } - - location := opts.Config.Location - if location == "" { - location = "global" - } - - var cfg *kube_rest.Config - if opts.Config.UCP.Kind == config.UCPConnectionKindKubernetes { - cfg, err = kubeutil.NewClientConfig(&kubeutil.ConfigOptions{ - // TODO: Allow to use custom context via configuration. - https://github.com/radius-project/radius/issues/5433 - ContextName: "", - QPS: kubeutil.DefaultServerQPS, - Burst: kubeutil.DefaultServerBurst, - }) - if err != nil { - return Options{}, fmt.Errorf("failed to get kubernetes config: %w", err) - } - } - - ucpConn, err := config.NewConnectionFromUCPConfig(&opts.Config.UCP, cfg) - if err != nil { - return Options{}, err - } - - return Options{ - Config: opts.Config, - Port: port, - TLSCertDir: tlsCertDir, - PathBase: basePath, - DatabaseProviderOptions: storeOpts, - SecretProviderOptions: secretOpts, - QueueProviderOptions: qproviderOpts, - MetricsProviderOptions: metricsOpts, - TracerProviderOptions: traceOpts, - ProfilerProviderOptions: profilerOpts, - LoggingOptions: loggingOpts, - InitialPlanes: planes, - Identity: identity, - UCPConnection: ucpConn, - Location: location, - }, nil -} - // NewServer creates a new hosting.Host instance with services for API, EmbeddedETCD, Metrics, Profiler and Backend (if // enabled) based on the given Options. -func NewServer(options *Options) (*hosting.Host, error) { +func NewServer(options *ucp.Options) (*hosting.Host, error) { hostingServices := []hosting.Service{ - api.NewService(api.ServiceOptions{ - ProviderName: UCPProviderName, - Address: ":" + options.Port, - PathBase: options.PathBase, - Config: options.Config, - Location: options.Location, - TLSCertDir: options.TLSCertDir, - DatabaseProviderOptions: options.DatabaseProviderOptions, - SecretProviderOptions: options.SecretProviderOptions, - QueueProviderOptions: options.QueueProviderOptions, - InitialPlanes: options.InitialPlanes, - Identity: options.Identity, - UCPConnection: options.UCPConnection, - }), + api.NewService(options), + backend.NewService(options), } - if options.DatabaseProviderOptions.Provider == databaseprovider.TypeETCD && - options.DatabaseProviderOptions.ETCD.InMemory { - hostingServices = append(hostingServices, data.NewEmbeddedETCDService(data.EmbeddedETCDServiceOptions{ClientConfigSink: options.DatabaseProviderOptions.ETCD.Client})) + if options.Config.Database.Provider == databaseprovider.TypeETCD && + options.Config.Database.ETCD.InMemory { + hostingServices = append(hostingServices, data.NewEmbeddedETCDService(data.EmbeddedETCDServiceOptions{ClientConfigSink: options.Config.Database.ETCD.Client})) } - options.MetricsProviderOptions.ServiceName = ServiceName - if options.MetricsProviderOptions.Prometheus.Enabled { + if options.Config.Metrics.Prometheus.Enabled { metricOptions := metricsservice.HostOptions{ - Config: &options.MetricsProviderOptions, + Config: &options.Config.Metrics, } hostingServices = append(hostingServices, metricsservice.NewService(metricOptions)) } - if options.ProfilerProviderOptions.Enabled { + if options.Config.Profiler.Enabled { profilerOptions := profilerservice.HostOptions{ - Config: &options.ProfilerProviderOptions, + Config: &options.Config.Profiler, } hostingServices = append(hostingServices, profilerservice.NewService(profilerOptions)) } - backendServiceOptions := hostopts.HostOptions{ - - Config: &hostopts.ProviderConfig{ - Env: hostopts.EnvironmentOptions{ - RoleLocation: options.Config.Location, - }, - DatabaseProvider: options.DatabaseProviderOptions, - SecretProvider: options.SecretProviderOptions, - QueueProvider: options.QueueProviderOptions, - MetricsProvider: options.MetricsProviderOptions, - TracerProvider: options.TracerProviderOptions, - ProfilerProvider: options.ProfilerProviderOptions, - }, - } - hostingServices = append(hostingServices, backend.NewService(backendServiceOptions, *options.Config)) - - options.TracerProviderOptions.ServiceName = "ucp" - hostingServices = append(hostingServices, &trace.Service{Options: options.TracerProviderOptions}) + hostingServices = append(hostingServices, &trace.Service{Options: options.Config.Tracing}) return &hosting.Host{ Services: hostingServices, diff --git a/pkg/ucp/testhost/doc.go b/pkg/ucp/testhost/doc.go new file mode 100644 index 0000000000..7b000bd986 --- /dev/null +++ b/pkg/ucp/testhost/doc.go @@ -0,0 +1,18 @@ +/* +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. +*/ + +// testhost provides an implementation of a test server for UCP. +package testhost diff --git a/pkg/ucp/testhost/host.go b/pkg/ucp/testhost/host.go new file mode 100644 index 0000000000..36077bc967 --- /dev/null +++ b/pkg/ucp/testhost/host.go @@ -0,0 +1,218 @@ +/* +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 testhost + +import ( + "context" + "fmt" + "strings" + "testing" + + v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" + "github.com/radius-project/radius/pkg/armrpc/hostoptions" + aztoken "github.com/radius-project/radius/pkg/azure/tokencredentials" + "github.com/radius-project/radius/pkg/components/database" + "github.com/radius-project/radius/pkg/components/database/databaseprovider" + queue "github.com/radius-project/radius/pkg/components/queue" + "github.com/radius-project/radius/pkg/components/queue/queueprovider" + "github.com/radius-project/radius/pkg/components/secret" + "github.com/radius-project/radius/pkg/components/secret/secretprovider" + "github.com/radius-project/radius/pkg/components/testhost" + "github.com/radius-project/radius/pkg/sdk" + "github.com/radius-project/radius/pkg/ucp" + "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" + "github.com/radius-project/radius/pkg/ucp/config" + "github.com/radius-project/radius/pkg/ucp/frontend/modules" + "github.com/radius-project/radius/pkg/ucp/server" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" +) + +// TestHostOption can be used to configure the UCP options before the server is started. +type TestHostOption interface { + // Apply applies the configuration to the UCP options. + Apply(options *ucp.Options) +} + +// TestHostOptionFunc is a function that implements the TestHostOption interface. +type TestHostOptionFunc func(options *ucp.Options) + +// Apply applies the function to the UCP options. +func (f TestHostOptionFunc) Apply(options *ucp.Options) { + f(options) +} + +// NoModules is a TestHostOption that disables all UCP modules. +func NoModules() TestHostOptionFunc { + return func(options *ucp.Options) { + options.Modules = []modules.Initializer{} + } +} + +// TestServerMocks provides access to mock instances created by the TestServer. +type TestServerMocks struct { + // DatabaseClient is the mock database client. + DatabaseClient *database.MockClient + + // DatabaseProvider is the mock database provider. + DatabaseProvider *databaseprovider.DatabaseProvider + + // QueueClient is the mock queue client. + QueueClient *queue.MockClient + + // QueueProvider is the mock queue provider. + QueueProvider *queueprovider.QueueProvider + + // SecretClient is the mock secret client. + SecretClient *secret.MockClient + + // SecretProvider is the mock secret provider. + SecretProvider *secretprovider.SecretProvider + + // StatusManager is the mock status manager. + StatusManager *statusmanager.MockStatusManager +} + +// NewMocks creates a new set of mocks for the test server. +func NewMocks(t *testing.T) *TestServerMocks { + ctrl := gomock.NewController(t) + databaseClient := database.NewMockClient(ctrl) + + queueClient := queue.NewMockClient(ctrl) + queueProvider := queueprovider.New(queueprovider.QueueProviderOptions{Name: "System.Resources"}) + queueProvider.SetClient(queueClient) + + secretClient := secret.NewMockClient(ctrl) + secretProvider := secretprovider.NewSecretProvider(secretprovider.SecretProviderOptions{}) + secretProvider.SetClient(secretClient) + + statusManager := statusmanager.NewMockStatusManager(ctrl) + return &TestServerMocks{ + DatabaseClient: databaseClient, + DatabaseProvider: databaseprovider.FromClient(databaseClient), + QueueClient: queueClient, + QueueProvider: queueProvider, + SecretClient: secretClient, + SecretProvider: secretProvider, + StatusManager: statusManager, + } +} + +// Apply updates the UCP options to use the mocks. +func (m *TestServerMocks) Apply(options *ucp.Options) { + options.SecretProvider = m.SecretProvider + options.DatabaseProvider = m.DatabaseProvider + options.QueueProvider = m.QueueProvider + options.StatusManager = m.StatusManager +} + +// TestHost provides a test host for the UCP server. +type TestHost struct { + *testhost.TestHost + options *ucp.Options + + clientFactoryUCP *v20231001preview.ClientFactory +} + +// Internals provides access to the internal options of the server. This allows tests +// to access the data stores and manipulate the server state. +func (th *TestHost) Options() *ucp.Options { + return th.options +} + +// UCP provides access to the generated clients for the UCP API. +func (ts *TestHost) UCP() *v20231001preview.ClientFactory { + if ts.clientFactoryUCP == nil { + connection, err := sdk.NewDirectConnection(ts.BaseURL()) + require.NoError(ts.T(), err) + + ts.clientFactoryUCP, err = v20231001preview.NewClientFactory(&aztoken.AnonymousCredential{}, sdk.NewClientOptions(connection)) + require.NoError(ts.T(), err) + } + + return ts.clientFactoryUCP +} + +// Start creates and starts a new TestServer. +func Start(t *testing.T, opts ...TestHostOption) *TestHost { + config := &ucp.Config{ + Database: databaseprovider.Options{ + Provider: databaseprovider.TypeInMemory, + }, + Environment: hostoptions.EnvironmentOptions{ + Name: "test", + RoleLocation: v1.LocationGlobal, + }, + Queue: queueprovider.QueueProviderOptions{ + Provider: queueprovider.TypeInmemory, + Name: "ucp", + }, + Secrets: secretprovider.SecretProviderOptions{ + Provider: secretprovider.TypeInMemorySecret, + }, + Server: hostoptions.ServerOptions{ + // Initialized dynamically when the server is started. + }, + + UCP: config.UCPOptions{ + Kind: config.UCPConnectionKindDirect, + Direct: &config.UCPDirectConnectionOptions{ + Endpoint: "http://localhost:65000", // Initialized dynamically when the server is started. + }, + }, + } + + options, err := ucp.NewOptions(context.Background(), config) + require.NoError(t, err) + + for _, opt := range opts { + opt.Apply(options) + } + + return StartWithOptions(t, options) + +} + +func StartWithOptions(t *testing.T, options *ucp.Options) *TestHost { + options.Config.Server.Host = "localhost" + if options.Config.Server.Port == 0 { + options.Config.Server.Port = testhost.AllocateFreePort(t) + } + + baseURL := fmt.Sprintf( + "http://%s%s", + options.Config.Server.Address(), + options.Config.Server.PathBase) + baseURL = strings.TrimSuffix(baseURL, "/") + + options.Config.UCP.Kind = config.UCPConnectionKindDirect + options.Config.UCP.Direct = &config.UCPDirectConnectionOptions{Endpoint: baseURL} + + // Instantiate the UCP client now that we know the URL. + var err error + options.UCP, err = sdk.NewDirectConnection(baseURL) + require.NoError(t, err) + + host, err := server.NewServer(options) + require.NoError(t, err, "failed to create server") + + return &TestHost{ + TestHost: testhost.StartHost(t, host, baseURL), + options: options, + } +} From defd40b964d3b174d18b7cd1152a0ae1d98f3a41 Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Thu, 19 Dec 2024 14:03:26 -0800 Subject: [PATCH 13/37] Remove ETCd database and secret store (#8158) # Description These are unused now that we're using the simpler in-memory components for testing. Removing these simplifies the code and reduces our binary size by removing a chunky dependency. For reviewers, the ETCd support being removed is NOT what users use today. That's the 'apiserver' database and the 'kubernetes' secret store. There is no change in behavior in this PR, we're removing something unused. We're planning to use PostgreSQL for production-quality support in Radius, so we have no plans to use ETCd going forward. ## Type of change - This pull request is a minor refactor, code cleanup, test improvement, or other maintenance task and doesn't change the functionality of Radius (issue link optional). ## Contributor checklist Please verify that the PR meets the following requirements, where applicable: - [ ] An overview of proposed schema changes is included in a linked GitHub issue. - [ ] A design document PR is created in the [design-notes repository](https://github.com/radius-project/design-notes/), if new APIs are being introduced. - [ ] If applicable, design document has been reviewed and approved by Radius maintainers/approvers. - [ ] A PR for the [samples repository](https://github.com/radius-project/samples) is created, if existing samples are affected by the changes in this PR. - [ ] A PR for the [documentation repository](https://github.com/radius-project/docs) is created, if the changes in this PR affect the documentation or any user facing updates are made. - [ ] A PR for the [recipes repository](https://github.com/radius-project/recipes) is created, if existing recipes are affected by the changes in this PR. Signed-off-by: Ryan Nowak --- cmd/applications-rp/cmd/root.go | 16 - cmd/ucpd/cmd/root.go | 12 - go.mod | 25 -- go.sum | 71 ---- .../database/databaseprovider/factory.go | 22 -- .../database/databaseprovider/options.go | 20 - .../database/databaseprovider/types.go | 3 - .../database/etcdstore/etcdclient.go | 343 ------------------ .../database/etcdstore/etcdclient_test.go | 70 ---- pkg/components/secret/etcd/client.go | 89 ----- pkg/components/secret/etcd/client_test.go | 120 ------ .../secret/secretprovider/factory.go | 21 -- .../secret/secretprovider/options.go | 5 - pkg/components/secret/secretprovider/types.go | 3 - pkg/dynamicrp/server/server.go | 20 +- pkg/ucp/data/etcd.go | 244 ------------- pkg/ucp/hosting/asyncvalue.go | 107 ------ pkg/ucp/hosting/asyncvalue_test.go | 211 ----------- pkg/ucp/server/server.go | 10 +- 19 files changed, 2 insertions(+), 1410 deletions(-) delete mode 100644 pkg/components/database/etcdstore/etcdclient.go delete mode 100644 pkg/components/database/etcdstore/etcdclient_test.go delete mode 100644 pkg/components/secret/etcd/client.go delete mode 100644 pkg/components/secret/etcd/client_test.go delete mode 100644 pkg/ucp/data/etcd.go delete mode 100644 pkg/ucp/hosting/asyncvalue.go delete mode 100644 pkg/ucp/hosting/asyncvalue_test.go diff --git a/cmd/applications-rp/cmd/root.go b/cmd/applications-rp/cmd/root.go index 7df3495907..6e891b9f66 100644 --- a/cmd/applications-rp/cmd/root.go +++ b/cmd/applications-rp/cmd/root.go @@ -22,7 +22,6 @@ import ( "github.com/go-logr/logr" "github.com/spf13/cobra" - etcdclient "go.etcd.io/etcd/client/v3" runtimelog "sigs.k8s.io/controller-runtime/pkg/log" "github.com/radius-project/radius/pkg/armrpc/builder" @@ -33,8 +32,6 @@ import ( "github.com/radius-project/radius/pkg/server" "github.com/radius-project/radius/pkg/trace" - "github.com/radius-project/radius/pkg/components/database/databaseprovider" - "github.com/radius-project/radius/pkg/ucp/data" "github.com/radius-project/radius/pkg/ucp/hosting" "github.com/radius-project/radius/pkg/ucp/ucplog" @@ -79,19 +76,6 @@ var rootCmd = &cobra.Command{ // Must set the logger before using controller-runtime. runtimelog.SetLogger(logger) - if options.Config.DatabaseProvider.Provider == databaseprovider.TypeETCD && - options.Config.DatabaseProvider.ETCD.InMemory { - // For in-memory etcd we need to register another service to manage its lifecycle. - // - // The client will be initialized asynchronously. - logger.Info("Enabled in-memory etcd") - client := hosting.NewAsyncValue[etcdclient.Client]() - options.Config.DatabaseProvider.ETCD.Client = client - options.Config.SecretProvider.ETCD.Client = client - - hostingSvc = append(hostingSvc, data.NewEmbeddedETCDService(data.EmbeddedETCDServiceOptions{ClientConfigSink: client})) - } - builders, err := builders(options) if err != nil { return err diff --git a/cmd/ucpd/cmd/root.go b/cmd/ucpd/cmd/root.go index d99e885800..1face5384c 100644 --- a/cmd/ucpd/cmd/root.go +++ b/cmd/ucpd/cmd/root.go @@ -23,11 +23,9 @@ import ( "github.com/go-logr/logr" "github.com/spf13/cobra" - etcdclient "go.etcd.io/etcd/client/v3" runtimelog "sigs.k8s.io/controller-runtime/pkg/log" "github.com/radius-project/radius/pkg/armrpc/hostoptions" - "github.com/radius-project/radius/pkg/components/database/databaseprovider" "github.com/radius-project/radius/pkg/ucp" "github.com/radius-project/radius/pkg/ucp/hosting" "github.com/radius-project/radius/pkg/ucp/server" @@ -65,16 +63,6 @@ var rootCmd = &cobra.Command{ // Must set the logger before using controller-runtime. runtimelog.SetLogger(logger) - if options.Config.Database.Provider == databaseprovider.TypeETCD && - options.Config.Database.ETCD.InMemory { - // For in-memory etcd we need to register another service to manage its lifecycle. - // - // The client will be initialized asynchronously. - clientconfigSource := hosting.NewAsyncValue[etcdclient.Client]() - options.Config.Database.ETCD.Client = clientconfigSource - options.Config.Secrets.ETCD.Client = clientconfigSource - } - host, err := server.NewServer(options) if err != nil { return err diff --git a/go.mod b/go.mod index deb3f77454..894cfd1269 100644 --- a/go.mod +++ b/go.mod @@ -67,8 +67,6 @@ require ( github.com/stern/stern v1.31.0 github.com/stretchr/testify v1.10.0 github.com/wI2L/jsondiff v0.6.1 - go.etcd.io/etcd/client/v3 v3.5.17 - go.etcd.io/etcd/server/v3 v3.5.17 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 go.opentelemetry.io/contrib/instrumentation/runtime v0.58.0 go.opentelemetry.io/otel v1.33.0 @@ -185,13 +183,10 @@ require ( github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/aymanbagabas/go-udiff v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chai2010/gettext-go v1.0.3 // indirect github.com/cloudflare/circl v1.3.9 // indirect github.com/containerd/containerd v1.7.24 - github.com/coreos/go-semver v0.3.1 // indirect - github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect github.com/cyphar/filepath-securejoin v0.3.4 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc @@ -201,7 +196,6 @@ require ( github.com/docker/docker-credential-helpers v0.8.2 // indirect github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-metrics v0.0.1 // indirect - github.com/dustin/go-humanize v1.0.1 // indirect github.com/emicklei/go-restful/v3 v3.12.1 // indirect github.com/evanphx/json-patch v5.9.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.9.0 // indirect @@ -218,7 +212,6 @@ require ( github.com/go-openapi/validate v0.24.0 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang-jwt/jwt/v4 v4.5.1 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/btree v1.1.3 // indirect github.com/google/gofuzz v1.2.0 // indirect @@ -227,10 +220,6 @@ require ( github.com/gorilla/websocket v1.5.3 // indirect github.com/gosuri/uitable v0.0.4 // indirect github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect - github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect - github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect - github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-getter v1.7.6 @@ -243,7 +232,6 @@ require ( github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmoiron/sqlx v1.4.0 // indirect - github.com/jonboulle/clockwork v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.17.10 // indirect @@ -288,27 +276,15 @@ require ( github.com/sahilm/fuzzy v0.1.1 // indirect github.com/shopspring/decimal v1.4.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect - github.com/soheilhy/cmux v0.1.5 // indirect github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.7.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect - github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect - github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510 // indirect github.com/xlab/treeprint v1.2.0 // indirect github.com/zclconf/go-cty v1.15.1 // indirect - go.etcd.io/bbolt v1.3.11 // indirect - go.etcd.io/etcd/api/v3 v3.5.17 // indirect - go.etcd.io/etcd/client/pkg/v3 v3.5.17 // indirect - go.etcd.io/etcd/client/v2 v2.305.17 // indirect - go.etcd.io/etcd/pkg/v3 v3.5.17 // indirect - go.etcd.io/etcd/raft/v3 v3.5.17 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 // indirect - go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.31.0 // indirect golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 @@ -325,7 +301,6 @@ require ( google.golang.org/protobuf v1.35.2 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect k8s.io/apiserver v0.32.0 // indirect k8s.io/component-base v0.32.0 // indirect k8s.io/klog/v2 v2.130.1 // indirect diff --git a/go.sum b/go.sum index 1aec1ae6f9..519a2cdf86 100644 --- a/go.sum +++ b/go.sum @@ -321,7 +321,6 @@ github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiE github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8= github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA= -github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -338,8 +337,6 @@ github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembj github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= -github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= -github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -377,8 +374,6 @@ github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA= -github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= github.com/containerd/cgroups/v3 v3.0.2 h1:f5WFqIVSgo5IZmtTT3qVBo6TzI1ON6sycSBKkymb9L0= github.com/containerd/cgroups/v3 v3.0.2/go.mod h1:JUgITrzdFqp42uI2ryGA+ge0ap/nxzYgkGmIcetmErE= @@ -392,10 +387,6 @@ github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= -github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= -github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= -github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= -github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= @@ -432,8 +423,6 @@ github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQ github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= -github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= -github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU= @@ -494,10 +483,8 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2 github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs= github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -545,14 +532,11 @@ github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/goccy/go-yaml v1.15.10 h1:9exV2CDYm/FWHPptIIgcDiPQS+X/4uTR+HEl+GF9xJU= github.com/goccy/go-yaml v1.15.10/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= -github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -671,7 +655,6 @@ github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gosuri/uilive v0.0.4 h1:hUEBpQDj8D8jXgtCdBu7sWsy5sbW/5GhuO8KBwJ2jyY= @@ -680,14 +663,7 @@ github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= -github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -744,8 +720,6 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGw github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= -github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4= -github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= @@ -863,7 +837,6 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg= github.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= @@ -877,7 +850,6 @@ github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFz github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -930,15 +902,11 @@ github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A= github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= -github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= -github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -985,8 +953,6 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= -github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE= -github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk= github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= @@ -1003,8 +969,6 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510 h1:S2dVYn90KE98chqDkyE9Z4N61UnQd+KOfgp5Iu53llk= -github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -1023,22 +987,6 @@ github.com/zclconf/go-cty v1.15.1 h1:RgQYm4j2EvoBRXOPxhUvxPzRrGDo1eCOhHXuGfrj5S0 github.com/zclconf/go-cty v1.15.1/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= -go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0= -go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I= -go.etcd.io/etcd/api/v3 v3.5.17 h1:cQB8eb8bxwuxOilBpMJAEo8fAONyrdXTHUNcMd8yT1w= -go.etcd.io/etcd/api/v3 v3.5.17/go.mod h1:d1hvkRuXkts6PmaYk2Vrgqbv7H4ADfAKhyJqHNLJCB4= -go.etcd.io/etcd/client/pkg/v3 v3.5.17 h1:XxnDXAWq2pnxqx76ljWwiQ9jylbpC4rvkAeRVOUKKVw= -go.etcd.io/etcd/client/pkg/v3 v3.5.17/go.mod h1:4DqK1TKacp/86nJk4FLQqo6Mn2vvQFBmruW3pP14H/w= -go.etcd.io/etcd/client/v2 v2.305.17 h1:ajFukQfI//xY5VuSeuUw4TJ4WnNR2kAFfV/P0pDdPMs= -go.etcd.io/etcd/client/v2 v2.305.17/go.mod h1:EttKgEgvwikmXN+b7pkEWxDZr6sEaYsqCiS3k4fa/Vg= -go.etcd.io/etcd/client/v3 v3.5.17 h1:o48sINNeWz5+pjy/Z0+HKpj/xSnBkuVhVvXkjEXbqZY= -go.etcd.io/etcd/client/v3 v3.5.17/go.mod h1:j2d4eXTHWkT2ClBgnnEPm/Wuu7jsqku41v9DZ3OtjQo= -go.etcd.io/etcd/pkg/v3 v3.5.17 h1:1k2wZ+oDp41jrk3F9o15o8o7K3/qliBo0mXqxo1PKaE= -go.etcd.io/etcd/pkg/v3 v3.5.17/go.mod h1:FrztuSuaJG0c7RXCOzT08w+PCugh2kCQXmruNYCpCGA= -go.etcd.io/etcd/raft/v3 v3.5.17 h1:wHPW/b1oFBw/+HjDAQ9vfr17OIInejTIsmwMZpK1dNo= -go.etcd.io/etcd/raft/v3 v3.5.17/go.mod h1:uapEfOMPaJ45CqBYIraLO5+fqyIY2d57nFfxzFwy4D4= -go.etcd.io/etcd/server/v3 v3.5.17 h1:xykBwLZk9IdDsB8z8rMdCCPRvhrG+fwvARaGA0TRiyc= -go.etcd.io/etcd/server/v3 v3.5.17/go.mod h1:40sqgtGt6ZJNKm8nk8x6LexZakPu+NDl/DCgZTZ69Cc= go.mongodb.org/mongo-driver v1.15.1 h1:l+RvoUOoMXFmADTLfYDm7On9dRm7p4T80/lEQM+r7HU= go.mongodb.org/mongo-driver v1.15.1/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -1060,10 +1008,6 @@ go.opentelemetry.io/contrib/instrumentation/runtime v0.58.0 h1:GrcF8ABgnBHQFgp4z go.opentelemetry.io/contrib/instrumentation/runtime v0.58.0/go.mod h1:+kxR5prZLoFAJVXJWZKWO2e4PY2dYyXIRNklBuOyzpM= go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= go.opentelemetry.io/otel/exporters/prometheus v0.55.0 h1:sSPw658Lk2NWAv74lkD3B/RSDb+xRFx46GjkrL3VUZo= go.opentelemetry.io/otel/exporters/prometheus v0.55.0/go.mod h1:nC00vyCmQixoeaxF6KNyP42II/RHa9UdruK02qBmHvI= go.opentelemetry.io/otel/exporters/zipkin v1.33.0 h1:aFexjEJIw5kVz6vQwnsqCG/nTV/UpsZh7MtQwGmH1eI= @@ -1077,20 +1021,14 @@ go.opentelemetry.io/otel/sdk/metric v1.33.0/go.mod h1:dL5ykHZmm1B1nVRk9dDjChwDmt go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= -go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -1174,7 +1112,6 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= @@ -1182,7 +1119,6 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= @@ -1246,7 +1182,6 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1293,7 +1228,6 @@ golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1353,7 +1287,6 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1485,7 +1418,6 @@ google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= @@ -1641,8 +1573,6 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= -gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -1652,7 +1582,6 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= diff --git a/pkg/components/database/databaseprovider/factory.go b/pkg/components/database/databaseprovider/factory.go index 50c9bae764..c0b3555014 100644 --- a/pkg/components/database/databaseprovider/factory.go +++ b/pkg/components/database/databaseprovider/factory.go @@ -26,7 +26,6 @@ import ( store "github.com/radius-project/radius/pkg/components/database" "github.com/radius-project/radius/pkg/components/database/apiserverstore" ucpv1alpha1 "github.com/radius-project/radius/pkg/components/database/apiserverstore/api/ucp.dev/v1alpha1" - "github.com/radius-project/radius/pkg/components/database/etcdstore" "github.com/radius-project/radius/pkg/components/database/inmemory" "github.com/radius-project/radius/pkg/components/database/postgres" "github.com/radius-project/radius/pkg/kubeutil" @@ -40,7 +39,6 @@ type databaseClientFactoryFunc func(ctx context.Context, options Options) (store var databaseClientFactory = map[DatabaseProviderType]databaseClientFactoryFunc{ TypeAPIServer: initAPIServerClient, - TypeETCD: InitETCDClient, TypeInMemory: initInMemoryClient, TypePostgreSQL: initPostgreSQLClient, } @@ -81,26 +79,6 @@ func initAPIServerClient(ctx context.Context, opt Options) (store.Client, error) return client, nil } -// InitETCDClient checks if the ETCD client is in memory and if the client is not nil, then it initializes the database -// client and returns an ETCDClient. If either of these conditions are not met, an error is returned. -func InitETCDClient(ctx context.Context, opt Options) (store.Client, error) { - if !opt.ETCD.InMemory { - return nil, errors.New("failed to initialize etcd client: inmemory is the only supported mode for now") - } - if opt.ETCD.Client == nil { - return nil, errors.New("failed to initialize etcd client: ETCDOptions.Client is nil, this is a bug") - } - - // Initialize the database client once the etcd service has started - client, err := opt.ETCD.Client.Get(ctx) - if err != nil { - return nil, fmt.Errorf("failed to initialize etcd client: %w", err) - } - - etcdClient := etcdstore.NewETCDClient(client) - return etcdClient, nil -} - // initInMemoryClient creates a new in-memory store client. func initInMemoryClient(ctx context.Context, opt Options) (store.Client, error) { return inmemory.NewClient(), nil diff --git a/pkg/components/database/databaseprovider/options.go b/pkg/components/database/databaseprovider/options.go index 249e1b6447..c04f62f619 100644 --- a/pkg/components/database/databaseprovider/options.go +++ b/pkg/components/database/databaseprovider/options.go @@ -16,11 +16,6 @@ limitations under the License. package databaseprovider -import ( - "github.com/radius-project/radius/pkg/ucp/hosting" - etcdclient "go.etcd.io/etcd/client/v3" -) - // Options represents the database provider options. type Options struct { // Provider configures the database provider. @@ -29,9 +24,6 @@ type Options struct { // APIServer configures options for the Kubernetes APIServer store. Will be ignored if another store is configured. APIServer APIServerOptions `yaml:"apiserver,omitempty"` - // ETCD configures options for the etcd store. Will be ignored if another store is configured. - ETCD ETCDOptions `yaml:"etcd,omitempty"` - // InMemory configures options for the in-memory store. Will be ignored if another store is configured. InMemory InMemoryOptions `yaml:"inmemory,omitempty"` @@ -49,18 +41,6 @@ type APIServerOptions struct { Namespace string `yaml:"namespace"` } -// ETCDOptions represents options for the configuring the etcd store. -type ETCDOptions struct { - // InMemory configures the etcd store to run in-memory with the resource provider. This is not suitable for production use. - InMemory bool `yaml:"inmemory"` - - // Client is used to access the etcd client when running in memory. - // - // NOTE: when we run etcd in memory it will be registered as its own hosting.Service with its own startup/shutdown lifecyle. - // We need a way to share state between the etcd service and the things that want to consume it. This is that. - Client *hosting.AsyncValue[etcdclient.Client] `yaml:"-"` -} - // InMemoryOptions represents options for the in-memory store. type InMemoryOptions struct{} diff --git a/pkg/components/database/databaseprovider/types.go b/pkg/components/database/databaseprovider/types.go index 3a17486c72..3d3fc78f19 100644 --- a/pkg/components/database/databaseprovider/types.go +++ b/pkg/components/database/databaseprovider/types.go @@ -23,9 +23,6 @@ const ( // TypeAPIServer represents the Kubernetes APIServer provider. TypeAPIServer DatabaseProviderType = "apiserver" - // TypeETCD represents the etcd provider. - TypeETCD DatabaseProviderType = "etcd" - // TypeInMemory represents the in-memory provider. TypeInMemory DatabaseProviderType = "inmemory" diff --git a/pkg/components/database/etcdstore/etcdclient.go b/pkg/components/database/etcdstore/etcdclient.go deleted file mode 100644 index 96a7e1d281..0000000000 --- a/pkg/components/database/etcdstore/etcdclient.go +++ /dev/null @@ -1,343 +0,0 @@ -/* -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 etcdstore stores resources using etcd. Our usage for etcd is optimized for the kinds -// of hierarchical and type-based queries common in a resource provider. -// -// Our key prefix scheme builds a hierarchy using '|' as a separator as '|' is illegal in an -// UCP resource identifier. We take advantage of the natural usage of '/' in resource ids as -// a delimiter. -// -// The key of a resource can be mechanically constructed from its resource id by replacing 'providers' -// with the '|' separator (for a non-extension resource). We have no current support for extension resources. -// -// Keys are structured like the following example: -// -// scope|/planes/radius/local/|/resourceGroups/cool-group/ -// resource|/planes/radius/local/resourceGroups/cool-group/|/Applications.Core/applications/cool-app/ -// -// As a special case for scopes (like resource groups) we treat the last segment as the routing scope. -// -// The prefix (scope or resource) limits each query to either for scope or resources respectively. In our -// use cases for the store we never need to query scopes and resources at the same time. Separating these actions -// limits the number of results - we want to avoid cases where the query has to return a huge set of results. -// -// For example, the following query will be commonly executed and we don't want it to list all resources in the -// database: -// -// scope|/planes/ -// -// This scheme allows a variety of flexibility for querying/filtering with different scopes. We prefer -// query approaches that that involved client-side filtering to avoid the need for N+1 query strategies. -// Leading and trailing '/' characters are preserved on the key-segments to avoid ambiguity. -package etcdstore - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "strings" - - "github.com/radius-project/radius/pkg/components/database" - "github.com/radius-project/radius/pkg/components/database/databaseutil" - "github.com/radius-project/radius/pkg/ucp/resources" - "github.com/radius-project/radius/pkg/ucp/util/etag" - etcdclient "go.etcd.io/etcd/client/v3" -) - -const ( - SectionSeparator = "|" -) - -// NewETCDClient creates a new ETCDClient instance with the given etcdclient.Client. -func NewETCDClient(c *etcdclient.Client) *ETCDClient { - return &ETCDClient{client: c} -} - -var _ database.Client = (*ETCDClient)(nil) - -type ETCDClient struct { - client *etcdclient.Client -} - -// Query retrieves objects from the store that match the given query and filters, and returns them in a store.ObjectQueryResult. -func (c *ETCDClient) Query(ctx context.Context, query database.Query, options ...database.QueryOptions) (*database.ObjectQueryResult, error) { - if ctx == nil { - return nil, &database.ErrInvalid{Message: "invalid argument. 'ctx' is required"} - } - - err := query.Validate() - if err != nil { - return nil, &database.ErrInvalid{Message: fmt.Sprintf("invalid argument. Query is invalid: %s", err.Error())} - } - - key := keyFromQuery(query) - - // TODO: We don't place a limit/top value on the query right now so we get all - // results as a single page. This would be a nice future improvement - // - // https://stackoverflow.com/questions/44873514/etcd3-go-client-how-to-paginate-large-sets-of-keys - response, err := c.client.Get(ctx, key, etcdclient.WithPrefix()) - if err != nil { - return nil, err - } - - results := database.ObjectQueryResult{} - for _, kv := range response.Kvs { - if keyMatchesQuery(kv.Key, query) { - value := database.Object{} - err = json.Unmarshal(kv.Value, &value) - if err != nil { - return nil, err - } - - match, err := value.MatchesFilters(query.Filters) - if err != nil { - return nil, err - } else if !match { - continue - } - - value.ETag = etag.NewFromRevision(kv.ModRevision) - results.Items = append(results.Items, value) - } - } - - return &results, nil -} - -// Get checks if the provided context, id and options are valid, then retrieves the corresponding object from -// the store and returns it, or an error if the object is not found or an error occurs. -func (c *ETCDClient) Get(ctx context.Context, id string, options ...database.GetOptions) (*database.Object, error) { - if ctx == nil { - return nil, &database.ErrInvalid{Message: "invalid argument. 'ctx' is required"} - } - parsed, err := resources.Parse(id) - if err != nil { - return nil, &database.ErrInvalid{Message: "invalid argument. 'id' must be a valid resource id"} - } - if parsed.IsEmpty() { - return nil, &database.ErrInvalid{Message: "invalid argument. 'id' must not be empty"} - } - if parsed.IsResourceCollection() || parsed.IsScopeCollection() { - return nil, &database.ErrInvalid{Message: "invalid argument. 'id' must refer to a named resource, not a collection"} - } - - key := keyFromID(parsed) - response, err := c.client.Get(ctx, key) - if err != nil { - return nil, err - } - - if response.Count == 0 { - return nil, &database.ErrNotFound{ID: id} - } - - value := database.Object{} - err = json.Unmarshal(response.Kvs[0].Value, &value) - if err != nil { - return nil, err - } - - value.ETag = etag.NewFromRevision(response.Kvs[0].ModRevision) - - return &value, nil -} - -// Delete checks if the given resource ID is valid, and if so, deletes it from the store, returning an error if the -// resource does not exist or if an ETag is provided and does not match. -func (c *ETCDClient) Delete(ctx context.Context, id string, options ...database.DeleteOptions) error { - if ctx == nil { - return &database.ErrInvalid{Message: "invalid argument. 'ctx' is required"} - } - parsed, err := resources.Parse(id) - if err != nil { - return &database.ErrInvalid{Message: "invalid argument. 'id' must be a valid resource id"} - } - if parsed.IsEmpty() { - return &database.ErrInvalid{Message: "invalid argument. 'id' must not be empty"} - } - if parsed.IsResourceCollection() || parsed.IsScopeCollection() { - return &database.ErrInvalid{Message: "invalid argument. 'id' must refer to a named resource, not a collection"} - } - - key := keyFromID(parsed) - config := database.NewDeleteConfig(options...) - - // If we have an ETag then we do to execute a transaction. - if config.ETag != "" { - revision, err := etag.ParseRevision(config.ETag) - if err != nil { - // Treat an invalid ETag as a concurrency failure, since it will never match. - return &database.ErrConcurrency{} - } - - txn, err := c.client.Txn(ctx). - If(etcdclient.Compare(etcdclient.ModRevision(key), "=", revision)). - Then(etcdclient.OpDelete(key)). - Commit() - if err != nil { - return err - } - - if !txn.Succeeded { - return &database.ErrConcurrency{} - } - - response := txn.Responses[0].GetResponseDeleteRange() - if response.Deleted == 0 { - return &database.ErrNotFound{ID: id} - } else { - return nil - } - } - - // If we don't have an ETag then things are straightforward :) - response, err := c.client.Delete(ctx, key) - if err != nil { - return err - } - - if response.Deleted == 0 { - return &database.ErrNotFound{ID: id} - } - - return nil -} - -// Save checks the context and object parameters, parses the object ID, marshals the object into JSON, saves the object to -// the store, and sets the object's ETag. If an ETag is provided, a transaction is executed to ensure concurrency. -func (c *ETCDClient) Save(ctx context.Context, obj *database.Object, options ...database.SaveOptions) error { - if ctx == nil { - return &database.ErrInvalid{Message: "invalid argument. 'ctx' is required"} - } - if obj == nil { - return &database.ErrInvalid{Message: "invalid argument. 'obj' is required"} - } - - id := obj.Metadata.ID - parsed, err := resources.Parse(id) - if err != nil { - return err - } - - b, err := json.Marshal(obj) - if err != nil { - return err - } - - key := keyFromID(parsed) - config := database.NewSaveConfig(options...) - - // If we have an ETag then we do to execute a transaction. - if config.ETag != "" { - revision, err := etag.ParseRevision(config.ETag) - if err != nil { - // Treat an invalid ETag as a concurrency failure, since it will never match. - return &database.ErrConcurrency{} - } - - txn, err := c.client.Txn(ctx). - If(etcdclient.Compare(etcdclient.ModRevision(key), "=", revision)). - Then(etcdclient.OpPut(key, string(b))). - Commit() - if err != nil { - return err - } - - if !txn.Succeeded { - return &database.ErrConcurrency{} - } - - response := txn.Responses[0].GetResponsePut() - obj.ETag = etag.NewFromRevision(response.Header.Revision) - return nil - } - - // If we don't have an ETag then things are pretty straightforward. - response, err := c.client.Put(ctx, key, string(b)) - if err != nil { - return err - } - - obj.ETag = etag.NewFromRevision(response.Header.Revision) - - return nil -} - -// Client returns the etcdclient.Client instance stored in the ETCDClient struct. -func (c *ETCDClient) Client() *etcdclient.Client { - return c.client -} - -func idFromKey(key []byte) (resources.ID, error) { - parts := strings.Split(string(key), SectionSeparator) - // sample valid key: - // scope|/planes/radius/local/resourceGroups/cool-group/|/Applications.Core/applications/cool-app/ - if len(parts) != 3 { - return resources.ID{}, errors.New("the etcd key '%q' is invalid because it does not have 3 sections") - } - - switch parts[0] { - case databaseutil.ScopePrefix: - // The key might look like: - // scope|/planes/radius/local/|/resourceGroups/cool-group/ - return resources.Parse(parts[1] + strings.TrimPrefix(parts[2], resources.SegmentSeparator)) - - case databaseutil.ResourcePrefix: - // The key might look like: - // resource|/subscriptions/{guid}/resourceGroups/cool-group/|/Applications.Core/applications/cool-app/ - return resources.Parse(parts[1] + resources.ProvidersSegment + parts[2]) - - default: - return resources.ID{}, errors.New("the etcd key '%q' is invalid because it has the wrong prefix") - } -} - -// keyFromID returns the key to use for an ID. They key should be used as an exact match. -func keyFromID(id resources.ID) string { - prefix, rootScope, routingScope, _ := databaseutil.ExtractStorageParts(id) - return prefix + SectionSeparator + rootScope + SectionSeparator + routingScope -} - -// keyFromQuery returns the key to use for an for executing a query. The key should be used as a prefix. -func keyFromQuery(query database.Query) string { - // These patterns require a prefix match for us in ETCd. - // - // A recursive query will not be able to consider anything in the routing scope, so it - // always requires client-side filtering. - prefix := databaseutil.ResourcePrefix - if query.IsScopeQuery { - prefix = databaseutil.ScopePrefix - } - - if query.ScopeRecursive { - return prefix + SectionSeparator + databaseutil.NormalizePart(query.RootScope) - } else { - return prefix + SectionSeparator + databaseutil.NormalizePart(query.RootScope) + SectionSeparator + databaseutil.NormalizePart(query.RoutingScopePrefix) - } -} - -func keyMatchesQuery(key []byte, query database.Query) bool { - // Ignore invalid keys, we don't expect to find them. - id, err := idFromKey(key) - if err != nil { - return false - } - - return databaseutil.IDMatchesQuery(id, query) -} diff --git a/pkg/components/database/etcdstore/etcdclient_test.go b/pkg/components/database/etcdstore/etcdclient_test.go deleted file mode 100644 index 7fa57dd31f..0000000000 --- a/pkg/components/database/etcdstore/etcdclient_test.go +++ /dev/null @@ -1,70 +0,0 @@ -/* -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 etcdstore - -import ( - "context" - "testing" - - "github.com/radius-project/radius/pkg/ucp/data" - "github.com/radius-project/radius/pkg/ucp/hosting" - "github.com/stretchr/testify/require" - etcdclient "go.etcd.io/etcd/client/v3" - - "github.com/radius-project/radius/test/testcontext" - shared "github.com/radius-project/radius/test/ucp/storetest" -) - -func Test_ETCDClient(t *testing.T) { - ctx, cancel := testcontext.NewWithCancel(t) - t.Cleanup(cancel) - - config := hosting.NewAsyncValue[etcdclient.Client]() - service := data.NewEmbeddedETCDService(data.EmbeddedETCDServiceOptions{ClientConfigSink: config}) - - go func() { - // We can't pass the test logger into the etcd service because it is forbidden to log - // using the test logger after the test finishes. - // - // https://github.com/golang/go/issues/40343 - // - // If you need to see the logging output while you are testing, then comment out the next line - // and you'll be able to see the spam from etcd. - // - // This is caught by the race checker and will fail your pr if you do it. - ctx := context.Background() - _ = service.Run(ctx) - }() - - etcdc, err := config.Get(ctx) - require.NoError(t, err) - - client := NewETCDClient(etcdc) - - clear := func(t *testing.T) { - keys, err := etcdc.Get(ctx, "", etcdclient.WithKeysOnly(), etcdclient.WithPrefix()) - require.NoError(t, err) - - for _, kv := range keys.Kvs { - _, err = etcdc.Delete(ctx, string(kv.Key)) - require.NoError(t, err) - } - } - - // The actual test logic lives in a shared package, we're just doing the setup here. - shared.RunTest(t, client, clear) -} diff --git a/pkg/components/secret/etcd/client.go b/pkg/components/secret/etcd/client.go deleted file mode 100644 index 344cb0a4c4..0000000000 --- a/pkg/components/secret/etcd/client.go +++ /dev/null @@ -1,89 +0,0 @@ -/* -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 etcd - -import ( - "context" - - "github.com/radius-project/radius/pkg/components/secret" - "github.com/radius-project/radius/pkg/ucp/util" - etcdclient "go.etcd.io/etcd/client/v3" -) - -const ( - secretResourcePrefix = "secret|" -) - -var _ secret.Client = (*Client)(nil) - -// Client represents radius secret client to manage radius secret. -type Client struct { - ETCDClient *etcdclient.Client -} - -// Save checks if the name and value of the secret are valid and saves the value in etcd, returning an error if unsuccessful. -func (c *Client) Save(ctx context.Context, name string, value []byte) error { - if name == "" { - return &secret.ErrInvalid{Message: "invalid argument. 'name' is required"} - } - - if value == nil { - return &secret.ErrInvalid{Message: "invalid argument. 'value' is required"} - } - secretName := generateSecretResourceName(name) - - // We don't care about response while save, only if the operation is successful or not - _, err := c.ETCDClient.Put(ctx, secretName, string(value)) - if err != nil { - return err - } - return nil -} - -// Delete deletes a secret from the etcd store and returns an error if the secret is not found. -func (c *Client) Delete(ctx context.Context, name string) error { - secretName := generateSecretResourceName(name) - resp, err := c.ETCDClient.Delete(ctx, secretName) - if err != nil { - return err - } - if resp.Deleted == 0 { - return &secret.ErrNotFound{} - } - return nil -} - -// Get retrieves a secret from etcd given a name and returns it as a byte slice, or returns an error if the secret is -// not found or an invalid argument is provided. -func (c *Client) Get(ctx context.Context, name string) ([]byte, error) { - if name == "" { - return nil, &secret.ErrInvalid{Message: "invalid argument. 'name' is required"} - } - secretName := generateSecretResourceName(name) - resp, err := c.ETCDClient.Get(ctx, secretName) - if err != nil { - return nil, err - } - if resp.Count == 0 { - return nil, &secret.ErrNotFound{} - } - return resp.Kvs[0].Value, nil -} - -func generateSecretResourceName(name string) string { - return secretResourcePrefix + util.NormalizeStringToLower(name) -} diff --git a/pkg/components/secret/etcd/client_test.go b/pkg/components/secret/etcd/client_test.go deleted file mode 100644 index 29219e2f16..0000000000 --- a/pkg/components/secret/etcd/client_test.go +++ /dev/null @@ -1,120 +0,0 @@ -/* -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 etcd - -import ( - "context" - "encoding/json" - "strconv" - "testing" - - "github.com/radius-project/radius/pkg/components/secret" - "github.com/radius-project/radius/pkg/ucp/data" - "github.com/radius-project/radius/pkg/ucp/hosting" - "github.com/radius-project/radius/test/testcontext" - "github.com/stretchr/testify/require" - etcdclient "go.etcd.io/etcd/client/v3" -) - -const ( - testSecretName = "azure-azurecloud-default" -) - -func Test_ETCD(t *testing.T) { - config := hosting.NewAsyncValue[etcdclient.Client]() - service := data.NewEmbeddedETCDService(data.EmbeddedETCDServiceOptions{ClientConfigSink: config}) - - ctx, cancel := testcontext.NewWithCancel(t) - t.Cleanup(cancel) - - go func() { - // We can't pass the test logger into the etcd service because it is forbidden to log - // using the test logger after the test finishes. - // - // https://github.com/golang/go/issues/40343 - // - // If you need to see the logging output while you are testing, then comment out the next line - // and you'll be able to see the spam from etcd. - // - // This is caught by the race checker and will fail your pr if you do it. - ctx := context.Background() - _ = service.Run(ctx) - }() - - etcdc, err := config.Get(ctx) - require.NoError(t, err) - - runSaveTests(t, etcdc) - -} - -func runSaveTests(t *testing.T, etcdClient *etcdclient.Client) { - ctx := context.Background() - client := Client{ - ETCDClient: etcdClient, - } - testSecret, err := json.Marshal("test_secret") - require.NoError(t, err) - tests := []struct { - testName string - secretName string - secret []byte - response []byte - save bool - get bool - delete bool - err error - }{ - {"save-get-delete-secret-success", testSecretName, testSecret, []byte("test_secret"), true, true, true, nil}, - {"save-secret-empty-name", "", testSecret, nil, true, false, false, &secret.ErrInvalid{Message: "invalid argument. 'name' is required"}}, - {"save-secret-empty-secret", testSecretName, nil, nil, true, false, false, &secret.ErrInvalid{Message: "invalid argument. 'value' is required"}}, - {"delete-secret-without-save", testSecretName, nil, nil, false, false, true, &secret.ErrNotFound{}}, - {"get-secret-without-save", testSecretName, nil, nil, false, true, false, &secret.ErrNotFound{}}, - {"get-secret-with-empty-name", "", nil, nil, false, true, false, &secret.ErrInvalid{Message: "invalid argument. 'name' is required"}}, - } - for _, tt := range tests { - t.Run(tt.testName, func(t *testing.T) { - if tt.save { - err := client.Save(ctx, tt.secretName, tt.secret) - if tt.err == nil { - require.NoError(t, err) - } else { - require.Equal(t, err, tt.err) - } - } - if tt.get { - response, err := client.Get(ctx, tt.secretName) - if tt.err == nil { - require.NoError(t, err) - value, err := strconv.Unquote(string(response)) - require.NoError(t, err) - require.Equal(t, string(value), string(tt.response)) - } else { - require.Equal(t, err, tt.err) - } - } - if tt.delete { - err := client.Delete(ctx, tt.secretName) - if tt.err == nil { - require.NoError(t, err) - } else { - require.Equal(t, err, tt.err) - } - } - }) - } -} diff --git a/pkg/components/secret/secretprovider/factory.go b/pkg/components/secret/secretprovider/factory.go index f9ca30572e..68737c45ee 100644 --- a/pkg/components/secret/secretprovider/factory.go +++ b/pkg/components/secret/secretprovider/factory.go @@ -18,12 +18,8 @@ package secretprovider import ( "context" - "errors" - "github.com/radius-project/radius/pkg/components/database/databaseprovider" - "github.com/radius-project/radius/pkg/components/database/etcdstore" "github.com/radius-project/radius/pkg/components/secret" - "github.com/radius-project/radius/pkg/components/secret/etcd" "github.com/radius-project/radius/pkg/components/secret/inmemory" kubernetes_client "github.com/radius-project/radius/pkg/components/secret/kubernetes" "github.com/radius-project/radius/pkg/kubeutil" @@ -34,27 +30,10 @@ import ( type secretFactoryFunc func(context.Context, SecretProviderOptions) (secret.Client, error) var secretClientFactory = map[SecretProviderType]secretFactoryFunc{ - TypeETCDSecret: initETCDSecretClient, TypeKubernetesSecret: initKubernetesSecretClient, TypeInMemorySecret: initInMemorySecretClient, } -func initETCDSecretClient(ctx context.Context, opts SecretProviderOptions) (secret.Client, error) { - // etcd is a separate process run for development storage. - // data provider already creates an etcd process which can be re-used instead of a new process for secret. - client, err := databaseprovider.InitETCDClient(ctx, databaseprovider.Options{ - ETCD: opts.ETCD, - }) - if err != nil { - return nil, err - } - secretClient, ok := client.(*etcdstore.ETCDClient) - if !ok { - return nil, errors.New("no etcd Client detected") - } - return &etcd.Client{ETCDClient: secretClient.Client()}, nil -} - func initKubernetesSecretClient(ctx context.Context, opt SecretProviderOptions) (secret.Client, error) { s := scheme.Scheme cfg, err := kubeutil.NewClientConfig(&kubeutil.ConfigOptions{ diff --git a/pkg/components/secret/secretprovider/options.go b/pkg/components/secret/secretprovider/options.go index 9820a91f8a..712c756211 100644 --- a/pkg/components/secret/secretprovider/options.go +++ b/pkg/components/secret/secretprovider/options.go @@ -16,16 +16,11 @@ limitations under the License. package secretprovider -import "github.com/radius-project/radius/pkg/components/database/databaseprovider" - // SecretProviderOptions contains provider information of the secret. type SecretProviderOptions struct { // Provider configures the secret provider. Provider SecretProviderType `yaml:"provider"` - // ETCD configures options for the etcd secret store. - ETCD databaseprovider.ETCDOptions `yaml:"etcd,omitempty"` - // InMemory configures options for the in-memory secret store. InMemory struct{} `yaml:"inmemory,omitempty"` } diff --git a/pkg/components/secret/secretprovider/types.go b/pkg/components/secret/secretprovider/types.go index 66a0c77ff6..2fbbe25cfa 100644 --- a/pkg/components/secret/secretprovider/types.go +++ b/pkg/components/secret/secretprovider/types.go @@ -20,9 +20,6 @@ package secretprovider type SecretProviderType string const ( - // TypeETCDSecret represents the ETCD secret provider. - TypeETCDSecret SecretProviderType = "etcd" - // TypeKubernetesSecret represents the Kubernetes secret provider. TypeKubernetesSecret SecretProviderType = "kubernetes" diff --git a/pkg/dynamicrp/server/server.go b/pkg/dynamicrp/server/server.go index ff3fc804b5..d6e2cc24c1 100644 --- a/pkg/dynamicrp/server/server.go +++ b/pkg/dynamicrp/server/server.go @@ -17,37 +17,19 @@ limitations under the License. package server import ( - "time" - - "github.com/radius-project/radius/pkg/components/database/databaseprovider" "github.com/radius-project/radius/pkg/dynamicrp" "github.com/radius-project/radius/pkg/dynamicrp/backend" "github.com/radius-project/radius/pkg/dynamicrp/frontend" metricsservice "github.com/radius-project/radius/pkg/metrics/service" profilerservice "github.com/radius-project/radius/pkg/profiler/service" "github.com/radius-project/radius/pkg/trace" - "github.com/radius-project/radius/pkg/ucp/data" "github.com/radius-project/radius/pkg/ucp/hosting" ) -const ( - HTTPServerStopTimeout = time.Second * 10 - ServiceName = "dynamic-rp" -) - -const UCPProviderName = "System.Resources" - -// NewServer creates a new hosting.Host instance with services for API, EmbeddedETCD, Metrics, Profiler and Backend (if -// enabled) based on the given Options. +// NewServer initializes a host for UCP based on the provided options. func NewServer(options *dynamicrp.Options) (*hosting.Host, error) { services := []hosting.Service{} - // In-memory ETCD requires a service running in the process. - if options.Config.Database.Provider == databaseprovider.TypeETCD && - options.Config.Database.ETCD.InMemory { - services = append(services, data.NewEmbeddedETCDService(data.EmbeddedETCDServiceOptions{ClientConfigSink: options.Config.Database.ETCD.Client})) - } - // Metrics is provided via a service. if options.Config.Metrics.Prometheus.Enabled { services = append(services, metricsservice.NewService(metricsservice.HostOptions{ diff --git a/pkg/ucp/data/etcd.go b/pkg/ucp/data/etcd.go deleted file mode 100644 index 32c4095728..0000000000 --- a/pkg/ucp/data/etcd.go +++ /dev/null @@ -1,244 +0,0 @@ -/* -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 data - -import ( - "context" - "fmt" - "net" - "net/url" - "os" - "strings" - "time" - - "github.com/radius-project/radius/pkg/ucp/hosting" - "github.com/radius-project/radius/pkg/ucp/ucplog" - etcdclient "go.etcd.io/etcd/client/v3" - "go.etcd.io/etcd/server/v3/embed" -) - -const ( - ETCDStartTimeout = time.Second * 60 - ETCDStopTimeout = time.Second * 10 -) - -var _ hosting.Service = (*EmbeddedETCDService)(nil) - -type EmbeddedETCDServiceOptions struct { - ClientConfigSink *hosting.AsyncValue[etcdclient.Client] - - // AssignRandomPorts will choose random ports so that each instance of the etcd service - // is isolated. - AssignRandomPorts bool - - // Quiet will prevent etcd from logging to the console. - Quiet bool -} - -type EmbeddedETCDService struct { - options EmbeddedETCDServiceOptions - dirs []string -} - -// NewEmbeddedETCDService creates a new EmbeddedETCDService instance with the given options and returns a pointer to it. -func NewEmbeddedETCDService(options EmbeddedETCDServiceOptions) *EmbeddedETCDService { - return &EmbeddedETCDService{options: options} -} - -// Name returns a string "etcd" which is the name of the service. -func (s *EmbeddedETCDService) Name() string { - return "etcd" -} - -// "Run" creates a temporary directory for etcd to store data and wal files, assigns ports to avoid crosstalk when tests -// create multiple clusters, sets up logging, and starts an etcd server. If an error occurs, it will return an error. -func (s *EmbeddedETCDService) Run(ctx context.Context) error { - logger := ucplog.FromContextOrDiscard(ctx) - defer s.cleanup(ctx) - - config := embed.NewConfig() - - if s.options.AssignRandomPorts { - // We need to auto-assign ports to avoid crosstalk when tests create multiple clusters. ETCD uses - // hardcoded ports by default. - peerPort, clientPort, err := s.assignPorts() - if err != nil { - return fmt.Errorf("failed to assign listening ports for etcd: %w", err) - } - - logger.Info(fmt.Sprintf("etcd will listen on ports %d %d", *peerPort, *clientPort)) - - config.AdvertisePeerUrls = []url.URL{makeURL(*peerPort)} - config.ListenPeerUrls = []url.URL{makeURL(*peerPort)} - config.AdvertiseClientUrls = []url.URL{makeURL(*clientPort)} - config.ListenClientUrls = []url.URL{makeURL(*clientPort)} - - // Needs to be updated based on the ports that were chosen - config.ForceNewCluster = true - config.InitialCluster = config.InitialClusterFromName("default") - config.InitialClusterToken = fmt.Sprintf("cluster-%d", clientPort) - } - - // Using temp directories for storage - dataDir, err := os.MkdirTemp(os.TempDir(), "ucp-etcd-data-*") - if err != nil { - return fmt.Errorf("failed to create temporary data directory: %w", err) - } - s.dirs = append(s.dirs, dataDir) - - walDir, err := os.MkdirTemp(os.TempDir(), "ucp-etcd-wal-*") - if err != nil { - return fmt.Errorf("failed to create temporary wal directory: %w", err) - } - s.dirs = append(s.dirs, walDir) - - config.Dir = dataDir - config.WalDir = walDir - - // If we're using Zap we can just log to it directly. Otherwise send logging - // to the console. - zaplog := ucplog.Unwrap(logger) - if zaplog != nil { - config.ZapLoggerBuilder = embed.NewZapLoggerBuilder(zaplog.Named("etcd.server")) - } else if !s.options.Quiet { - config.LogLevel = "info" - config.LogOutputs = []string{"stdout"} - } else { - config.LogLevel = "fatal" - config.LogOutputs = []string{} - } - - // Use generated self-signed certs for authentication. - config.ClientAutoTLS = true - config.PeerAutoTLS = true - config.SelfSignedCertValidity = 1 // One year - - logger.Info("Starting etcd server") - server, err := embed.StartEtcd(config) - if err != nil { - if strings.HasPrefix(err.Error(), "listen tcp ") { - logger.Info("failed to start etcd server due to port conflict, assuming another instance of etcd is already running") - clientconfig := etcdclient.Config{ - Endpoints: []string{ - "http://localhost:2379", - }, - } - if zaplog != nil { - clientconfig.Logger = zaplog.Named("etcd.client") - } - - client, err := etcdclient.New(clientconfig) - if err != nil { - s.options.ClientConfigSink.PutErr(err) - } else { - s.options.ClientConfigSink.Put(client) - } - - <-ctx.Done() - } - - return err - } - logger.Info("Waiting for etcd server ready...") - - select { - case <-server.Server.ReadyNotify(): - logger.Info("Started etcd server") - break - case <-time.After(ETCDStartTimeout): - server.Server.Stop() // trigger a shutdown - s.cleanup(ctx) - return fmt.Errorf("etcd start timed out after %v", ETCDStartTimeout) - case err := <-server.Err(): - s.cleanup(ctx) - return fmt.Errorf("etcd start failed: %w", err) - } - - clientconfig := etcdclient.Config{ - Endpoints: server.Server.Cluster().ClientURLs(), - } - if zaplog != nil { - clientconfig.Logger = zaplog.Named("etcd.client") - } - - client, err := etcdclient.New(clientconfig) - if err != nil { - s.options.ClientConfigSink.PutErr(err) - } else { - s.options.ClientConfigSink.Put(client) - } - - <-ctx.Done() - - logger.Info("Stopping etcd...") - server.Close() - - select { - case <-server.Server.StopNotify(): - break - case <-time.After(ETCDStopTimeout): - server.Server.HardStop() - return fmt.Errorf("etcd stop timed out after %v", ETCDStopTimeout) - case err := <-server.Err(): - if err != nil { - return fmt.Errorf("etcd stop failed: %w", err) - } - } - - logger.Info("Stopped etcd") - return nil -} - -func (s *EmbeddedETCDService) cleanup(ctx context.Context) { - logger := ucplog.FromContextOrDiscard(ctx) - logger.Info("Cleaning up etcd directories") - for _, dir := range s.dirs { - err := os.RemoveAll(dir) - if err != nil { - logger.Error(err, "Failed to delete temp directory", "directory", dir) - } - } -} - -func makeURL(port int) url.URL { - u, err := url.Parse(fmt.Sprintf("http://localhost:%d", port)) - if err != nil { - // This should never happen. - panic(fmt.Sprintf("failed to parse URL: %v", err)) - } - - return *u -} - -func (s *EmbeddedETCDService) assignPorts() (*int, *int, error) { - listener1, err := net.Listen("tcp", ":0") - if err != nil { - return nil, nil, err - } - defer listener1.Close() - - listener2, err := net.Listen("tcp", ":0") - if err != nil { - return nil, nil, err - } - defer listener2.Close() - - port1 := listener1.Addr().(*net.TCPAddr).Port - port2 := listener2.Addr().(*net.TCPAddr).Port - - return &port1, &port2, nil -} diff --git a/pkg/ucp/hosting/asyncvalue.go b/pkg/ucp/hosting/asyncvalue.go deleted file mode 100644 index 675009f10e..0000000000 --- a/pkg/ucp/hosting/asyncvalue.go +++ /dev/null @@ -1,107 +0,0 @@ -/* -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 hosting - -import ( - "context" - "fmt" - "sync" -) - -// noCopy may be embedded into structs which must not be copied -// after the first use. -// -// See https://golang.org/issues/8005#issuecomment-190753527 -// for details. -type noCopy struct{} //nolint:golint,unused - -// Lock is a no-op used by -copylocks checker from `go vet`. -func (*noCopy) Lock() {} //nolint:golint,unused -func (*noCopy) Unlock() {} //nolint:golint,unused - -type AsyncValue[T any] struct { - noCopy noCopy //nolint - - Cond *sync.Cond - Value *T - Err error -} - -type result[T any] struct { - Value *T - Err error -} - -// NewAsyncValue creates a new AsyncValue object with a condition variable and a mutex. -func NewAsyncValue[T any]() *AsyncValue[T] { - return &AsyncValue[T]{Cond: &sync.Cond{L: &sync.Mutex{}}} -} - -// Get is a function that attempts to retrieve a value from a given context, and returns the value or an -// error if the context is done or an error occurs. -func (a *AsyncValue[T]) Get(ctx context.Context) (*T, error) { - - initialized := make(chan result[T], 1) - go func() { - a.Cond.L.Lock() - - defer func() { - a.Cond.L.Unlock() - }() - - for { - if a.Value != nil || a.Err != nil { - break - } - - // Not ready to proceed, wait to be woken up - a.Cond.Wait() - } - - initialized <- result[T]{Value: a.Value, Err: a.Err} - close(initialized) - }() - - select { - case <-ctx.Done(): - close(initialized) - return nil, fmt.Errorf("failed to retrieve value: %w", ctx.Err()) - - case result := <-initialized: - if result.Err != nil { - return nil, result.Err - } - return result.Value, nil - } -} - -// Put takes in a pointer to a value and sets it as the value of the AsyncValue, then broadcasts the change to -// any waiting goroutines. -func (a *AsyncValue[T]) Put(value *T) { - a.Cond.L.Lock() - a.Value = value - a.Cond.L.Unlock() - a.Cond.Broadcast() -} - -// PutErr sets an error value on the AsyncValue struct and broadcasts the condition variable to notify any waiting goroutines. -func (a *AsyncValue[T]) PutErr(err error) { - a.Cond.L.Lock() - a.Err = err - a.Cond.L.Unlock() - a.Cond.Broadcast() -} diff --git a/pkg/ucp/hosting/asyncvalue_test.go b/pkg/ucp/hosting/asyncvalue_test.go deleted file mode 100644 index ec19d4fbe8..0000000000 --- a/pkg/ucp/hosting/asyncvalue_test.go +++ /dev/null @@ -1,211 +0,0 @@ -/* -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 hosting - -import ( - "context" - "errors" - "sync" - "testing" - "time" - - "github.com/radius-project/radius/test/testcontext" - "github.com/stretchr/testify/require" -) - -const TestTimeout = time.Minute * 5 - -type testValue struct{} - -type testResult struct { - Value *testValue - Err error -} - -// Used to synchronize the test "workers" for our tests. -type synchronizer struct { - Value *AsyncValue[testValue] - - WorkerCount int - workersStarted *sync.WaitGroup - workersCompleted *sync.WaitGroup - - Done chan error - results chan testResult -} - -// NewSynchronizer creates a new synchronizer struct with a given worker count, initializes the waitgroups and results -// channel, and returns the synchronizer. -func NewSynchronizer(workerCount int) *synchronizer { - s := &synchronizer{ - Value: NewAsyncValue[testValue](), - WorkerCount: workerCount, - workersStarted: &sync.WaitGroup{}, - workersCompleted: &sync.WaitGroup{}, - results: make(chan testResult, workerCount), - } - - s.workersStarted.Add(workerCount) - s.workersCompleted.Add(workerCount) - - return s -} - -// Start launches a number of workers to get a value from a context and returns a test result with the value retrieved -// and any errors that occurred. If the test times out before the workers complete, an error is returned. -func (s *synchronizer) Start(ctx context.Context, t *testing.T) { - for i := 0; i < s.WorkerCount; i++ { - go func() { - s.workersStarted.Done() - - value, err := s.Value.Get(ctx) - s.results <- testResult{Value: value, Err: err} - s.workersCompleted.Done() - }() - } - - started := make(chan struct{}) - - go func() { - s.workersStarted.Wait() - started <- struct{}{} - close(started) - }() - - select { - case <-started: - return - case <-ctx.Done(): - require.Fail(t, "test timed out without completing") - return - } -} - -// WaitForWorkersCompleted creates a channel to wait for workers to complete and returns a channel of test -// results or an error if the test times out. -func (s *synchronizer) WaitForWorkersCompleted(ctx context.Context, t *testing.T) <-chan testResult { - completed := make(chan struct{}) - - go func() { - s.workersCompleted.Wait() - completed <- struct{}{} - close(completed) - - // TRICKY: this is the best place to close the results channel. - close(s.results) - }() - - select { - case <-completed: - return s.results - case <-ctx.Done(): - require.Fail(t, "test timed out without completing") - return nil - } -} - -func Test_Get_NoBlockingWhenValueSet_Value(t *testing.T) { - ctx, cancel := testcontext.NewWithDeadline(t, TestTimeout) - t.Cleanup(cancel) - - asyncValue := NewAsyncValue[testValue]() - - value := &testValue{} - asyncValue.Put(value) - - got, err := asyncValue.Get(ctx) - require.Equal(t, value, got) - require.NoError(t, err) -} - -func Test_Get_NoBlockingWhenValueSet_Err(t *testing.T) { - ctx, cancel := testcontext.NewWithDeadline(t, TestTimeout) - t.Cleanup(cancel) - - asyncValue := NewAsyncValue[testValue]() - - err := errors.New("OH noes...") - asyncValue.PutErr(err) - - got, goterr := asyncValue.Get(ctx) - require.Nil(t, got) - require.ErrorIs(t, err, goterr) -} - -func Test_Get_BlocksUntil_ValueSet(t *testing.T) { - ctx, cancel := testcontext.NewWithDeadline(t, TestTimeout) - t.Cleanup(cancel) - - s := NewSynchronizer(10) - s.Start(ctx, t) - - value := &testValue{} - s.Value.Put(value) - - results := s.WaitForWorkersCompleted(ctx, t) - - // Verify results - count := 0 - for result := range results { - count++ - require.Equal(t, testResult{Value: value}, result) - } - require.Equal(t, s.WorkerCount, count) -} - -func Test_Get_BlocksUntil_ErrSet(t *testing.T) { - ctx, cancel := testcontext.NewWithDeadline(t, TestTimeout) - t.Cleanup(cancel) - - s := NewSynchronizer(10) - s.Start(ctx, t) - - err := errors.New("OH noes...") - s.Value.PutErr(err) - - results := s.WaitForWorkersCompleted(ctx, t) - - // Verify results - count := 0 - for result := range results { - count++ - require.Equal(t, testResult{Err: err}, result) - } - require.Equal(t, s.WorkerCount, count) -} - -func Test_Get_BlocksUntil_Canceled(t *testing.T) { - // We need two contexts. We want to cancel the work done by the workers. - workerContext, workerCancel := testcontext.NewWithCancel(t) - ctx, cancel := testcontext.NewWithDeadline(t, TestTimeout) - t.Cleanup(cancel) - - s := NewSynchronizer(10) - s.Start(workerContext, t) - - workerCancel() - - results := s.WaitForWorkersCompleted(ctx, t) - - // Verify results - count := 0 - for result := range results { - count++ - require.Error(t, result.Err) // Workers see a wrapped error, not the exact error from the context. - } - require.Equal(t, s.WorkerCount, count) -} diff --git a/pkg/ucp/server/server.go b/pkg/ucp/server/server.go index a656330a4a..b7d3edc03f 100644 --- a/pkg/ucp/server/server.go +++ b/pkg/ucp/server/server.go @@ -17,30 +17,22 @@ limitations under the License. package server import ( - "github.com/radius-project/radius/pkg/components/database/databaseprovider" metricsservice "github.com/radius-project/radius/pkg/metrics/service" profilerservice "github.com/radius-project/radius/pkg/profiler/service" "github.com/radius-project/radius/pkg/trace" "github.com/radius-project/radius/pkg/ucp" "github.com/radius-project/radius/pkg/ucp/backend" - "github.com/radius-project/radius/pkg/ucp/data" "github.com/radius-project/radius/pkg/ucp/frontend/api" "github.com/radius-project/radius/pkg/ucp/hosting" ) -// NewServer creates a new hosting.Host instance with services for API, EmbeddedETCD, Metrics, Profiler and Backend (if -// enabled) based on the given Options. +// NewServer initializes a host for UCP based on the provided options. func NewServer(options *ucp.Options) (*hosting.Host, error) { hostingServices := []hosting.Service{ api.NewService(options), backend.NewService(options), } - if options.Config.Database.Provider == databaseprovider.TypeETCD && - options.Config.Database.ETCD.InMemory { - hostingServices = append(hostingServices, data.NewEmbeddedETCDService(data.EmbeddedETCDServiceOptions{ClientConfigSink: options.Config.Database.ETCD.Client})) - } - if options.Config.Metrics.Prometheus.Enabled { metricOptions := metricsservice.HostOptions{ Config: &options.Config.Metrics, From 6b8ef422c2fb6410e080e8ec8fe1e5f6f59f28d0 Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Fri, 20 Dec 2024 14:26:02 -0800 Subject: [PATCH 14/37] Add async operation support to dynamic-rp (#8161) # Description This change adds the async operationStatus and operationResult handlers to the dynamic-rp API. This change is significant, because like all functionality in dynamic-rp, it's dynamic and needs to work for any UDT resource provider rather than a fixed namespace. ## Type of change - This pull request adds or changes features of Radius and has an approved issue (issue link required). Fixes: #6688 ## Contributor checklist Please verify that the PR meets the following requirements, where applicable: - [ ] An overview of proposed schema changes is included in a linked GitHub issue. - [ ] A design document PR is created in the [design-notes repository](https://github.com/radius-project/design-notes/), if new APIs are being introduced. - [ ] If applicable, design document has been reviewed and approved by Radius maintainers/approvers. - [ ] A PR for the [samples repository](https://github.com/radius-project/samples) is created, if existing samples are affected by the changes in this PR. - [ ] A PR for the [documentation repository](https://github.com/radius-project/docs) is created, if the changes in this PR affect the documentation or any user facing updates are made. - [ ] A PR for the [recipes repository](https://github.com/radius-project/recipes) is created, if existing recipes are affected by the changes in this PR. Signed-off-by: Ryan Nowak --- pkg/armrpc/asyncoperation/worker/worker.go | 7 +- pkg/dynamicrp/frontend/operations.go | 88 +++++++++++++++++++ pkg/dynamicrp/frontend/routes.go | 35 +++++++- pkg/dynamicrp/frontend/service.go | 18 +++- .../dynamic/operations_test.go | 79 +++++++++++++++++ pkg/dynamicrp/testhost/host.go | 7 +- 6 files changed, 230 insertions(+), 4 deletions(-) create mode 100644 pkg/dynamicrp/frontend/operations.go create mode 100644 pkg/dynamicrp/integrationtest/dynamic/operations_test.go diff --git a/pkg/armrpc/asyncoperation/worker/worker.go b/pkg/armrpc/asyncoperation/worker/worker.go index 587068a3bc..c634a5eb15 100644 --- a/pkg/armrpc/asyncoperation/worker/worker.go +++ b/pkg/armrpc/asyncoperation/worker/worker.go @@ -267,7 +267,12 @@ func (w *AsyncRequestProcessWorker) runOperation(ctx context.Context, message *q result.SetFailed(armErr, false) } - logger.Info("Operation returned", "success", result.Error == nil, "provisioningState", result.ProvisioningState(), "err", result.Error) + // We need the if/else here to prevent a panic inside the logger. + if result.Error == nil { + logger.Info("Operation returned", "success", "true", "provisioningState", result.ProvisioningState()) + } else { + logger.Info("Operation returned", "success", "false", "provisioningState", result.ProvisioningState(), "err", result.Error) + } // There are two cases when asyncReqCtx is canceled. // 1. When the operation is timed out, w.completeOperation will be called in L186 diff --git a/pkg/dynamicrp/frontend/operations.go b/pkg/dynamicrp/frontend/operations.go new file mode 100644 index 0000000000..6de9c9af61 --- /dev/null +++ b/pkg/dynamicrp/frontend/operations.go @@ -0,0 +1,88 @@ +/* +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 frontend + +import ( + "net/http" + "strings" + + v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/armrpc/frontend/controller" + "github.com/radius-project/radius/pkg/armrpc/frontend/server" + "github.com/radius-project/radius/pkg/armrpc/rest" + "github.com/radius-project/radius/pkg/ucp/resources" +) + +// dynamicOperationHandler returns an http.Handler that can instantiate and run a controller.Controller for a dynamic resource. +// +// Usually when we register a route, we know up-front the resource type that will be handled by the controller. +// In the dynamic-rp use-case we don't know. We need to dynamically determine the resource type based on the URL. +// +// For example: +// +// Route: /planes/radius/{planeName}/providers/{providerNamespace}/locations/{locationName}/operationResults/{operationID} +// URL: /planes/radius/myplane/providers/Applications.Example/locations/global/operationResults/1234 +// Resource Type: Applications.Example/operationResults +// +// # OR +// +// Route: /planes/radius/{planeName}/resourceGroups/my-rg/providers/{providerNamespace}/{resourceType}/{resourceName} +// URL: /planes/radius/myplane/resourceGroups/my-rg/providers/Applications.Example/customService/my-service +// Resource Type: Applications.Example/customService +// +// This code ensures that the controller will be provided with the correct resource type. +func dynamicOperationHandler(method v1.OperationMethod, baseOptions controller.Options, factory func(opts controller.Options) (controller.Controller, error)) http.HandlerFunc { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + id, err := resources.Parse(r.URL.Path) + if err != nil { + result := rest.NewBadRequestResponse(err.Error()) + err = result.Apply(r.Context(), w, r) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } + + return + } + + operationType := v1.OperationType{Type: strings.ToUpper(id.Type()), Method: method} + + // Copy the options and initalize them dynamically for this type. + opts := baseOptions + opts.ResourceType = id.Type() + + // Special case the operation status and operation result types. + // + // This is special-casing that all of our resource providers do to store a single data row for both operation statuses and operation results. + if strings.HasSuffix(strings.ToLower(opts.ResourceType), "locations/operationstatuses") || strings.HasSuffix(strings.ToLower(opts.ResourceType), "locations/operationresults") { + opts.ResourceType = id.ProviderNamespace() + "/operationstatuses" + } + + ctrl, err := factory(opts) + if err != nil { + result := rest.NewBadRequestResponse(err.Error()) + err = result.Apply(r.Context(), w, r) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } + + return + } + + handler := server.HandlerForController(ctrl, operationType) + handler.ServeHTTP(w, r) + }) +} diff --git a/pkg/dynamicrp/frontend/routes.go b/pkg/dynamicrp/frontend/routes.go index 3988d532b3..ca18546e07 100644 --- a/pkg/dynamicrp/frontend/routes.go +++ b/pkg/dynamicrp/frontend/routes.go @@ -17,14 +17,47 @@ limitations under the License. package frontend import ( + "strings" + "github.com/go-chi/chi/v5" + v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/armrpc/frontend/controller" + "github.com/radius-project/radius/pkg/armrpc/frontend/defaultoperation" "github.com/radius-project/radius/pkg/validator" ) -func (s *Service) registerRoutes(r *chi.Mux) error { +func (s *Service) registerRoutes(r *chi.Mux, controllerOptions controller.Options) error { + // Return ARM errors for invalid requests. + r.NotFound(validator.APINotFoundHandler()) + r.MethodNotAllowed(validator.APIMethodNotAllowedHandler()) + // Return ARM errors for invalid requests. r.NotFound(validator.APINotFoundHandler()) r.MethodNotAllowed(validator.APIMethodNotAllowedHandler()) + pathBase := s.options.Config.Server.PathBase + if pathBase == "" { + pathBase = "/" + } + + if !strings.HasSuffix(pathBase, "/") { + pathBase = pathBase + "/" + } + + r.Route(pathBase+"planes/radius/{planeName}/providers/{providerNamespace}", func(r chi.Router) { + r.Route("/locations/{locationName}", func(r chi.Router) { + r.Get("/{or:operation[Rr]esults}/{operationID}", dynamicOperationHandler(v1.OperationGet, controllerOptions, makeGetOperationResultController)) + r.Get("/{os:operation[Ss]tatuses}/{operationID}", dynamicOperationHandler(v1.OperationGet, controllerOptions, makeGetOperationStatusController)) + }) + }) + return nil } + +func makeGetOperationResultController(opts controller.Options) (controller.Controller, error) { + return defaultoperation.NewGetOperationResult(opts) +} + +func makeGetOperationStatusController(opts controller.Options) (controller.Controller, error) { + return defaultoperation.NewGetOperationStatus(opts) +} diff --git a/pkg/dynamicrp/frontend/service.go b/pkg/dynamicrp/frontend/service.go index 73518751b5..c5ecb4c803 100644 --- a/pkg/dynamicrp/frontend/service.go +++ b/pkg/dynamicrp/frontend/service.go @@ -22,6 +22,7 @@ import ( "net" "net/http" + "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/servicecontext" "github.com/radius-project/radius/pkg/dynamicrp" "github.com/radius-project/radius/pkg/middleware" @@ -54,7 +55,22 @@ func (s *Service) Name() string { func (s *Service) initialize(ctx context.Context) (*http.Server, error) { r := chi.NewRouter() - err := s.registerRoutes(r) + databaseClient, err := s.options.DatabaseProvider.GetClient(ctx) + if err != nil { + return nil, fmt.Errorf("failed to get database client: %w", err) + } + + controllerOptions := controller.Options{ + Address: s.options.Config.Server.Address(), + PathBase: s.options.Config.Server.PathBase, + DatabaseClient: databaseClient, + StatusManager: s.options.StatusManager, + + KubeClient: nil, // Unused by DynamicRP + ResourceType: "", // Set dynamically + } + + err = s.registerRoutes(r, controllerOptions) if err != nil { return nil, fmt.Errorf("failed to register routes: %w", err) } diff --git a/pkg/dynamicrp/integrationtest/dynamic/operations_test.go b/pkg/dynamicrp/integrationtest/dynamic/operations_test.go new file mode 100644 index 0000000000..a69d8cfc5d --- /dev/null +++ b/pkg/dynamicrp/integrationtest/dynamic/operations_test.go @@ -0,0 +1,79 @@ +/* +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 dynamic + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/uuid" + v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" + "github.com/radius-project/radius/pkg/components/database" + "github.com/radius-project/radius/pkg/dynamicrp/testhost" + "github.com/radius-project/radius/test/testcontext" + "github.com/stretchr/testify/require" +) + +// This test covers the basic functionality of the operation status/result controllers. +// +// This test is synthetic because we don't have a real operation to test against. +func Test_Dynamic_OperationResultAndStatus(t *testing.T) { + ctx := testcontext.New(t) + dynamic, ucp := testhost.Start(t) + + // Setup a plane & resource provider & location + plane := createRadiusPlane(ucp) + createResourceProvider(ucp) + createLocation(ucp) + + // Now we can make a request to the operation result/status endpoints. + operationName := uuid.New().String() + + operationResultID := fmt.Sprintf("/planes/radius/%s/providers/%s/locations/global/operationResults/%s", *plane.Name, resourceProviderNamespace, operationName) + operationStatusID := fmt.Sprintf("/planes/radius/%s/providers/%s/locations/global/operationStatuses/%s", *plane.Name, resourceProviderNamespace, operationName) + + // This operation doesn't exist yet, so we should get a 404. + response := ucp.MakeRequest("GET", fmt.Sprintf("%s?api-version=%s", operationResultID, apiVersion), nil) + response.EqualsErrorCode(http.StatusNotFound, "NotFound") + + response = ucp.MakeRequest("GET", fmt.Sprintf("%s?api-version=%s", operationStatusID, apiVersion), nil) + response.EqualsErrorCode(http.StatusNotFound, "NotFound") + + // Now let's simulate the creation of an operation, by putting one in the database. + databaseClient, err := dynamic.Options().DatabaseProvider.GetClient(ctx) + require.NoError(t, err) + + operation := &statusmanager.Status{ + AsyncOperationStatus: v1.AsyncOperationStatus{ + ID: operationStatusID, + Name: operationName, + Status: v1.ProvisioningStateUpdating, + }, + } + + err = databaseClient.Save(ctx, &database.Object{Data: operation, Metadata: database.Metadata{ID: operationStatusID}}) + require.NoError(t, err) + + // Now let's query it again, we should find it. + response = ucp.MakeRequest("GET", fmt.Sprintf("%s?api-version=%s", operationResultID, apiVersion), nil) + response.EqualsStatusCode(http.StatusAccepted) + + response = ucp.MakeRequest("GET", fmt.Sprintf("%s?api-version=%s", operationStatusID, apiVersion), nil) + response.EqualsStatusCode(http.StatusOK) +} diff --git a/pkg/dynamicrp/testhost/host.go b/pkg/dynamicrp/testhost/host.go index d6b8045608..ab01efdc01 100644 --- a/pkg/dynamicrp/testhost/host.go +++ b/pkg/dynamicrp/testhost/host.go @@ -55,6 +55,11 @@ func (f TestHostOptionFunc) Apply(options *dynamicrp.Options) { // TestHost provides a test host for the dynamic-rp server. type TestHost struct { *testhost.TestHost + options *dynamicrp.Options +} + +func (th *TestHost) Options() *dynamicrp.Options { + return th.options } func Start(t *testing.T, opts ...TestHostOption) (*TestHost, *ucptesthost.TestHost) { @@ -124,7 +129,7 @@ func StartWithOptions(t *testing.T, options *dynamicrp.Options) (*TestHost, *ucp require.NoError(t, err, "failed to create server") th := testhost.StartHost(t, host, baseURL) - return &TestHost{th}, startUCP(t, baseURL, ucpPort) + return &TestHost{TestHost: th, options: options}, startUCP(t, baseURL, ucpPort) } func startUCP(t *testing.T, dynamicRPURL string, ucpPort int) *ucptesthost.TestHost { From c21408326b2755d3d3f3cfc50ea017c2ad5213a5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Dec 2024 19:09:56 -0800 Subject: [PATCH 15/37] Bump the all group with 11 updates (#8167) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the all group with 11 updates: | Package | From | To | | --- | --- | --- | | [github.com/Azure/secrets-store-csi-driver-provider-azure](https://github.com/Azure/secrets-store-csi-driver-provider-azure) | `1.6.0` | `1.6.1` | | [github.com/aws/aws-sdk-go-v2](https://github.com/aws/aws-sdk-go-v2) | `1.32.6` | `1.32.7` | | [github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2) | `1.28.6` | `1.28.7` | | [github.com/aws/aws-sdk-go-v2/credentials](https://github.com/aws/aws-sdk-go-v2) | `1.17.47` | `1.17.48` | | [github.com/aws/aws-sdk-go-v2/service/cloudcontrol](https://github.com/aws/aws-sdk-go-v2) | `1.23.2` | `1.23.3` | | [github.com/aws/aws-sdk-go-v2/service/cloudformation](https://github.com/aws/aws-sdk-go-v2) | `1.56.1` | `1.56.2` | | [github.com/aws/aws-sdk-go-v2/service/ec2](https://github.com/aws/aws-sdk-go-v2) | `1.198.0` | `1.198.1` | | [github.com/aws/aws-sdk-go-v2/service/ecr](https://github.com/aws/aws-sdk-go-v2) | `1.36.7` | `1.36.8` | | [github.com/aws/aws-sdk-go-v2/service/sts](https://github.com/aws/aws-sdk-go-v2) | `1.33.2` | `1.33.3` | | [github.com/goccy/go-yaml](https://github.com/goccy/go-yaml) | `1.15.10` | `1.15.13` | | [github.com/jackc/pgx/v5](https://github.com/jackc/pgx) | `5.7.1` | `5.7.2` | Updates `github.com/Azure/secrets-store-csi-driver-provider-azure` from 1.6.0 to 1.6.1

Release notes

Sourced from github.com/Azure/secrets-store-csi-driver-provider-azure's releases.

v1.6.1 - 2024-12-17

Changelog

Bug Fixes 🐞

  • b0bbcc49e248000de6336ab8352c73aeca78aaf7 fix: return order of certs when unable to construct chain (#1711)

Maintenance 🔧

  • 907a83f1553fed0d33ef949f4dadea88e0519936 chore: bump golang.org/x/crypto from 0.24.0 to 0.31.0 (#1712)
Commits
  • 4c9421b release: update manifest and helm charts for v1.6.1 (#1716)
  • b0bbcc4 fix: return order of certs when unable to construct chain (#1711)
  • 907a83f chore: bump golang.org/x/crypto from 0.24.0 to 0.31.0 (#1712)
  • See full diff in compare view

Updates `github.com/aws/aws-sdk-go-v2` from 1.32.6 to 1.32.7
Commits

Updates `github.com/aws/aws-sdk-go-v2/config` from 1.28.6 to 1.28.7
Commits

Updates `github.com/aws/aws-sdk-go-v2/credentials` from 1.17.47 to 1.17.48
Commits

Updates `github.com/aws/aws-sdk-go-v2/service/cloudcontrol` from 1.23.2 to 1.23.3
Commits

Updates `github.com/aws/aws-sdk-go-v2/service/cloudformation` from 1.56.1 to 1.56.2
Commits

Updates `github.com/aws/aws-sdk-go-v2/service/ec2` from 1.198.0 to 1.198.1
Commits

Updates `github.com/aws/aws-sdk-go-v2/service/ecr` from 1.36.7 to 1.36.8
Commits

Updates `github.com/aws/aws-sdk-go-v2/service/sts` from 1.33.2 to 1.33.3
Commits

Updates `github.com/goccy/go-yaml` from 1.15.10 to 1.15.13
Release notes

Sourced from github.com/goccy/go-yaml's releases.

1.15.13

What's Changed

Full Changelog: https://github.com/goccy/go-yaml/compare/v1.15.12...v1.15.13

1.15.12

What's Changed

Full Changelog: https://github.com/goccy/go-yaml/compare/v1.15.11...v1.15.12

1.15.11

What's Changed

Full Changelog: https://github.com/goccy/go-yaml/compare/v1.15.10...v1.15.11

Commits

Updates `github.com/jackc/pgx/v5` from 5.7.1 to 5.7.2
Changelog

Sourced from github.com/jackc/pgx/v5's changelog.

5.7.2 (December 21, 2024)

  • Fix prepared statement already exists on batch prepare failure
  • Add commit query to tx options (Lucas Hild)
  • Fix pgtype.Timestamp json unmarshal (Shean de Montigny-Desautels)
  • Add message body size limits in frontend and backend (zene)
  • Add xid8 type
  • Ensure planning encodes and scans cannot infinitely recurse
  • Implement pgtype.UUID.String() (Konstantin Grachev)
  • Switch from ExecParams to Exec in ValidateConnectTargetSessionAttrs functions (Alexander Rumyantsev)
  • Update golang.org/x/crypto
Commits
  • 24fbe35 Create changelog for v5.7.2
  • 3a1593b Merge pull request #2198 from alexandear/fix-nilness
  • 9d851d7 Fix integration benchmarks
  • dacffdc Merge pull request #2196 from alexandear/docs-improve-links
  • bc7c840 Merge pull request #2195 from LucasHild/master
  • 0436851 Handle errors in generate_certs
  • 2532927 Improve links in README
  • ad87d47 Merge pull request #2194 from alexandear/refactor/pgconn-tests
  • 7cf7bc6 Simplify pgconn tests by using T.TempDir
  • 3e6c719 Merge pull request #2189 from pankona/update-crypto
  • Additional commits viewable in compare view

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 34 ++++++++++++++--------------- go.sum | 68 +++++++++++++++++++++++++++++----------------------------- 2 files changed, 51 insertions(+), 51 deletions(-) diff --git a/go.mod b/go.mod index 894cfd1269..9c5b2d3dd2 100644 --- a/go.mod +++ b/go.mod @@ -12,17 +12,17 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions v1.3.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/servicebus/armservicebus/v2 v2.0.0-beta.3 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.6.0 - github.com/Azure/secrets-store-csi-driver-provider-azure v1.6.0 + github.com/Azure/secrets-store-csi-driver-provider-azure v1.6.1 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d github.com/agnivade/levenshtein v1.2.0 - github.com/aws/aws-sdk-go-v2 v1.32.6 - github.com/aws/aws-sdk-go-v2/config v1.28.6 - github.com/aws/aws-sdk-go-v2/credentials v1.17.47 - github.com/aws/aws-sdk-go-v2/service/cloudcontrol v1.23.2 - github.com/aws/aws-sdk-go-v2/service/cloudformation v1.56.1 - github.com/aws/aws-sdk-go-v2/service/ec2 v1.198.0 - github.com/aws/aws-sdk-go-v2/service/ecr v1.36.7 - github.com/aws/aws-sdk-go-v2/service/sts v1.33.2 + github.com/aws/aws-sdk-go-v2 v1.32.7 + github.com/aws/aws-sdk-go-v2/config v1.28.7 + github.com/aws/aws-sdk-go-v2/credentials v1.17.48 + github.com/aws/aws-sdk-go-v2/service/cloudcontrol v1.23.3 + github.com/aws/aws-sdk-go-v2/service/cloudformation v1.56.2 + github.com/aws/aws-sdk-go-v2/service/ec2 v1.198.1 + github.com/aws/aws-sdk-go-v2/service/ecr v1.36.8 + github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 github.com/aws/smithy-go v1.22.1 github.com/charmbracelet/bubbles v0.20.0 github.com/charmbracelet/bubbletea v1.2.4 @@ -44,7 +44,7 @@ require ( github.com/go-playground/locales v0.14.1 github.com/go-playground/universal-translator v0.18.1 github.com/go-playground/validator/v10 v10.23.0 - github.com/goccy/go-yaml v1.15.10 + github.com/goccy/go-yaml v1.15.13 github.com/gofrs/flock v0.12.1 github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.6.0 @@ -53,7 +53,7 @@ require ( github.com/hashicorp/hc-install v0.9.0 github.com/hashicorp/terraform-config-inspect v0.0.0-20240607080351-271db412dbcb github.com/hashicorp/terraform-exec v0.21.0 - github.com/jackc/pgx/v5 v5.7.1 + github.com/jackc/pgx/v5 v5.7.2 github.com/mattn/go-isatty v0.0.20 github.com/mitchellh/mapstructure v1.5.0 github.com/novln/docker-parser v1.0.0 @@ -173,13 +173,13 @@ require ( github.com/agext/levenshtein v1.2.3 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/atotto/clipboard v0.1.4 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.21 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.6 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.24.7 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.6 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/aymanbagabas/go-udiff v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect diff --git a/go.sum b/go.sum index 519a2cdf86..24ae1735d5 100644 --- a/go.sum +++ b/go.sum @@ -227,8 +227,8 @@ github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.6.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.6.0/go.mod h1:oDrbWx4ewMylP7xHivfgixbfGBT6APAwsSoHRKotnIc= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/Azure/secrets-store-csi-driver-provider-azure v1.6.0 h1:DqttF4idvkF8NYntJt4EMi2teV0pRIvHWuCWBrAVe7Q= -github.com/Azure/secrets-store-csi-driver-provider-azure v1.6.0/go.mod h1:uAB6K5EIxrBbeIDKhVL/NuVETQ1f502aGtxPNBVdDB8= +github.com/Azure/secrets-store-csi-driver-provider-azure v1.6.1 h1:zR4cKcy324OVIkpqy0i18v+OmQO5FRZFVjY0Pyrw7+c= +github.com/Azure/secrets-store-csi-driver-provider-azure v1.6.1/go.mod h1:QebCf2Tk+ju+9NyQw26qdfkk3x6Bb1yY9eqfM191vPw= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU= @@ -283,38 +283,38 @@ github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/aws/aws-sdk-go v1.54.6 h1:HEYUib3yTt8E6vxjMWM3yAq5b+qjj/6aKA62mkgux9g= github.com/aws/aws-sdk-go v1.54.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= -github.com/aws/aws-sdk-go-v2 v1.32.6 h1:7BokKRgRPuGmKkFMhEg/jSul+tB9VvXhcViILtfG8b4= -github.com/aws/aws-sdk-go-v2 v1.32.6/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U= -github.com/aws/aws-sdk-go-v2/config v1.28.6 h1:D89IKtGrs/I3QXOLNTH93NJYtDhm8SYa9Q5CsPShmyo= -github.com/aws/aws-sdk-go-v2/config v1.28.6/go.mod h1:GDzxJ5wyyFSCoLkS+UhGB0dArhb9mI+Co4dHtoTxbko= -github.com/aws/aws-sdk-go-v2/credentials v1.17.47 h1:48bA+3/fCdi2yAwVt+3COvmatZ6jUDNkDTIsqDiMUdw= -github.com/aws/aws-sdk-go-v2/credentials v1.17.47/go.mod h1:+KdckOejLW3Ks3b0E3b5rHsr2f9yuORBum0WPnE5o5w= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.21 h1:AmoU1pziydclFT/xRV+xXE/Vb8fttJCLRPv8oAkprc0= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.21/go.mod h1:AjUdLYe4Tgs6kpH4Bv7uMZo7pottoyHMn4eTcIcneaY= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25 h1:s/fF4+yDQDoElYhfIVvSNyeCydfbuTKzhxSXDXCPasU= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25/go.mod h1:IgPfDv5jqFIzQSNbUEMoitNooSMXjRSDkhXv8jiROvU= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25 h1:ZntTCl5EsYnhN/IygQEUugpdwbhdkom9uHcbCftiGgA= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25/go.mod h1:DBdPrgeocww+CSl1C8cEV8PN1mHMBhuCDLpXezyvWkE= +github.com/aws/aws-sdk-go-v2 v1.32.7 h1:ky5o35oENWi0JYWUZkB7WYvVPP+bcRF5/Iq7JWSb5Rw= +github.com/aws/aws-sdk-go-v2 v1.32.7/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U= +github.com/aws/aws-sdk-go-v2/config v1.28.7 h1:GduUnoTXlhkgnxTD93g1nv4tVPILbdNQOzav+Wpg7AE= +github.com/aws/aws-sdk-go-v2/config v1.28.7/go.mod h1:vZGX6GVkIE8uECSUHB6MWAUsd4ZcG2Yq/dMa4refR3M= +github.com/aws/aws-sdk-go-v2/credentials v1.17.48 h1:IYdLD1qTJ0zanRavulofmqut4afs45mOWEI+MzZtTfQ= +github.com/aws/aws-sdk-go-v2/credentials v1.17.48/go.mod h1:tOscxHN3CGmuX9idQ3+qbkzrjVIx32lqDSU1/0d/qXs= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22 h1:kqOrpojG71DxJm/KDPO+Z/y1phm1JlC8/iT+5XRmAn8= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22/go.mod h1:NtSFajXVVL8TA2QNngagVZmUtXciyrHOt7xgz4faS/M= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26 h1:I/5wmGMffY4happ8NOCuIUEWGUvvFp5NSeQcXl9RHcI= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26/go.mod h1:FR8f4turZtNy6baO0KJ5FJUmXH/cSkI9fOngs0yl6mA= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26 h1:zXFLuEuMMUOvEARXFUVJdfqZ4bvvSgdGRq/ATcrQxzM= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26/go.mod h1:3o2Wpy0bogG1kyOPrgkXA8pgIfEEv0+m19O9D5+W8y8= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= -github.com/aws/aws-sdk-go-v2/service/cloudcontrol v1.23.2 h1:xUD/6aoYwDsMmVl6J6Umkgk+QlkzDm1N0w2BukgXTMI= -github.com/aws/aws-sdk-go-v2/service/cloudcontrol v1.23.2/go.mod h1:NQSFnuiS7N4Leys2Mx/N0UMIWkMsXHBs3HEI4ElCSV8= -github.com/aws/aws-sdk-go-v2/service/cloudformation v1.56.1 h1:EqRhsrEoXFFyzcNuqQCF1g9rG9EA8K2EiUj6/eWClgk= -github.com/aws/aws-sdk-go-v2/service/cloudformation v1.56.1/go.mod h1:75rrfzgrN4Ol0m9Xo4+8S09KBoGAd1t6eafFHMt5wDI= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.198.0 h1:ivPJXmGlzAjgy0jLO9naExUWE8IM8lLRcRKLPBEx6Q0= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.198.0/go.mod h1:00zqVNJFK6UASrTnuvjJHJuaqUdkVz5tW8Ip+VhzuNg= -github.com/aws/aws-sdk-go-v2/service/ecr v1.36.7 h1:R+5XKIJga2K9Dkj0/iQ6fD/MBGo02oxGGFTc512lK/Q= -github.com/aws/aws-sdk-go-v2/service/ecr v1.36.7/go.mod h1:fDPQV/6ONOQOjvtKhtypIy1wcGLcKYtoK/lvZ9fyDGQ= +github.com/aws/aws-sdk-go-v2/service/cloudcontrol v1.23.3 h1:CqhKpcm7vRbGqIKq4EgIXyDpFUcNHCn0Y/OhnP0thgs= +github.com/aws/aws-sdk-go-v2/service/cloudcontrol v1.23.3/go.mod h1:+m2sovuoyrrRtwrMDH/RrBq66dpBfEW1BK/4t9VCHqE= +github.com/aws/aws-sdk-go-v2/service/cloudformation v1.56.2 h1:6USen+lDo8xYQutfnzhSeNLKEykNmBPfrcBmYKhLP38= +github.com/aws/aws-sdk-go-v2/service/cloudformation v1.56.2/go.mod h1:10A7sHyxlTZSB7419K2wq/1tn0x/K9/drbD2j8VRZVc= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.198.1 h1:YbNopxjd9baM83YEEmkaYHi+NuJt0AszeaSLqo0CVr0= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.198.1/go.mod h1:mwr3iRm8u1+kkEx4ftDM2Q6Yr0XQFBKrP036ng+k5Lk= +github.com/aws/aws-sdk-go-v2/service/ecr v1.36.8 h1:cPdeSR2y0BDAr2S054U4ERlJ5mM1OWYazW7Jm/o+b1o= +github.com/aws/aws-sdk-go-v2/service/ecr v1.36.8/go.mod h1:NqKnlZvLl4Tp2UH/GEc/nhbjmPQhwOXmLp2eldiszLM= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.6 h1:50+XsN70RS7dwJ2CkVNXzj7U2L1HKP8nqTd3XWEXBN4= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.6/go.mod h1:WqgLmwY7so32kG01zD8CPTJWVWM+TzJoOVHwTg4aPug= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.7 h1:rLnYAfXQ3YAccocshIH5mzNNwZBkBo+bP6EhIxak6Hw= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.7/go.mod h1:ZHtuQJ6t9A/+YDuxOLnbryAmITtr8UysSny3qcyvJTc= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.6 h1:JnhTZR3PiYDNKlXy50/pNeix9aGMo6lLpXwJ1mw8MD4= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.6/go.mod h1:URronUEGfXZN1VpdktPSD1EkAL9mfrV+2F4sjH38qOY= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.2 h1:s4074ZO1Hk8qv65GqNXqDjmkf4HSQqJukaLuuW0TpDA= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.2/go.mod h1:mVggCnIWoM09jP71Wh+ea7+5gAp53q+49wDFs1SW5z8= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 h1:8eUsivBQzZHqe/3FE+cqwfH+0p5Jo8PFM/QYQSmeZ+M= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7/go.mod h1:kLPQvGUmxn/fqiCrDeohwG33bq2pQpGeY62yRO6Nrh0= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 h1:CvuUmnXI7ebaUAhbJcDy9YQx8wHR69eZ9I7q5hszt/g= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.8/go.mod h1:XDeGv1opzwm8ubxddF0cgqkZWsyOtw4lr6dxwmb6YQg= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7 h1:F2rBfNAL5UyswqoeWv9zs74N/NanhK16ydHW1pahX6E= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7/go.mod h1:JfyQ0g2JG8+Krq0EuZNnRwX0mU0HrwY/tG6JNfcqh4k= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 h1:Xgv/hyNgvLda/M9l9qxXc4UFSgppnRczLxlMs5Ae/QY= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.3/go.mod h1:5Gn+d+VaaRgsjewpMvGazt0WfcFO+Md4wLOuBfGR9Bc= github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro= github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= @@ -530,8 +530,8 @@ github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= -github.com/goccy/go-yaml v1.15.10 h1:9exV2CDYm/FWHPptIIgcDiPQS+X/4uTR+HEl+GF9xJU= -github.com/goccy/go-yaml v1.15.10/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= +github.com/goccy/go-yaml v1.15.13 h1:Xd87Yddmr2rC1SLLTm2MNDcTjeO/GYo0JGiww6gSTDg= +github.com/goccy/go-yaml v1.15.13/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -708,8 +708,8 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.7.1 h1:x7SYsPBYDkHDksogeSmZZ5xzThcTgRz++I5E+ePFUcs= -github.com/jackc/pgx/v5 v5.7.1/go.mod h1:e7O26IywZZ+naJtWWos6i6fvWK+29etgITqrqHLfoZA= +github.com/jackc/pgx/v5 v5.7.2 h1:mLoDLV6sonKlvjIEsV56SkWNCnuNv531l94GaIzO+XI= +github.com/jackc/pgx/v5 v5.7.2/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= From 8c8a6d7f11b4b04c842dc804f6140fd5b059eca4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Dec 2024 20:49:46 -0800 Subject: [PATCH 16/37] Bump bicep-types from `7c34fe6` to `3676a8b` (#8168) Bumps [bicep-types](https://github.com/Azure/bicep-types) from `7c34fe6` to `3676a8b`.
Commits

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Yetkin Timocin --- bicep-types | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bicep-types b/bicep-types index 7c34fe65c7..3676a8bf68 160000 --- a/bicep-types +++ b/bicep-types @@ -1 +1 @@ -Subproject commit 7c34fe65c70469beda773d603805da0a6224ccc3 +Subproject commit 3676a8bf689e62780c64c79bdca42f1799958cd4 From c6b2fcebcc81e3130c9464254865612f85f2f558 Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Tue, 24 Dec 2024 14:08:44 -0800 Subject: [PATCH 17/37] Move shared services and host to components (#8160) # Description This change moves the shared host package and diagnostics services to the new `pkg/components` folder. Also simplifies and improves consistency. As part of this work, I made changes to the configuration file for consistency. Each of these diagnostics features now has a dedicated on/off switch. Where a service name is required, it is now specific in configuration rather than hardcoded in the code. Also made the names of configuration files consistent and reflective of their scenario. All of the configuration files used for development reflect the 'dev' terminology. All of the configuration files used for self-hosted Radius (in Kubernetes) reflect the 'self-hosted' terminology. ## Type of change - This pull request is a minor refactor, code cleanup, test improvement, or other maintenance task and doesn't change the functionality of Radius (issue link optional). ## Contributor checklist Please verify that the PR meets the following requirements, where applicable: - [ ] An overview of proposed schema changes is included in a linked GitHub issue. - [ ] A design document PR is created in the [design-notes repository](https://github.com/radius-project/design-notes/), if new APIs are being introduced. - [ ] If applicable, design document has been reviewed and approved by Radius maintainers/approvers. - [ ] A PR for the [samples repository](https://github.com/radius-project/samples) is created, if existing samples are affected by the changes in this PR. - [ ] A PR for the [documentation repository](https://github.com/radius-project/docs) is created, if the changes in this PR affect the documentation or any user facing updates are made. - [ ] A PR for the [recipes repository](https://github.com/radius-project/recipes) is created, if existing recipes are affected by the changes in this PR. Signed-off-by: Ryan Nowak --- .vscode/launch.json | 18 ------ ...f-hosted.yaml => applications-rp-dev.yaml} | 8 ++- cmd/applications-rp/cmd/root.go | 46 +++++++------- cmd/applications-rp/radius-dev.yaml | 43 ------------- cmd/controller/cmd/root.go | 14 +++-- ...r-self-hosted.yaml => controller-dev.yaml} | 10 ++- cmd/dynamic-rp/cmd/root.go | 2 +- cmd/dynamic-rp/dynamicrp-dev.yaml | 10 ++- cmd/rad/cmd/root.go | 26 ++++++-- cmd/ucpd/cmd/root.go | 4 +- cmd/ucpd/ucp-dev.yaml | 6 +- .../templates/controller/configmaps.yaml | 4 +- .../templates/dynamic-rp/configmaps.yaml | 4 +- deploy/Chart/templates/rp/configmaps.yaml | 6 +- deploy/Chart/templates/ucp/configmaps.yaml | 6 +- .../configSettings.md | 63 +------------------ .../statusmanager/statusmanager.go | 4 +- pkg/armrpc/asyncoperation/worker/worker.go | 4 +- pkg/armrpc/hostoptions/env.go | 29 ++------- pkg/armrpc/hostoptions/providerconfig.go | 34 +++++----- pkg/{ucp => components}/hosting/hosting.go | 0 .../hosting/hosting_test.go | 0 pkg/{ucp => components}/hosting/run.go | 0 pkg/{ => components}/metrics/README.md | 0 .../metrics/asyncoperationmetrics.go | 0 pkg/{ => components}/metrics/metrics.go | 0 .../metrics/metricsservice}/options.go | 24 ++++--- .../metricsservice}/prometheusexporter.go | 4 +- .../metrics/metricsservice}/service.go | 19 ++---- .../metrics/recipeenginemetrics.go | 0 pkg/{ => components}/metrics/types.go | 0 .../profiler/profilerservice}/service.go | 19 +++--- pkg/components/testhost/host.go | 2 +- pkg/{ => components}/trace/doc.go | 0 .../trace/traceservice}/options.go | 4 +- .../trace/traceservice/service.go} | 16 ++--- pkg/{ => components}/trace/util.go | 0 pkg/{ => components}/trace/util_test.go | 0 pkg/controller/service.go | 6 +- pkg/dynamicrp/config.go | 12 ++-- pkg/dynamicrp/server/server.go | 22 +++---- pkg/metrics/service/hostoptions.go | 35 ----------- .../processors/resourceclient.go | 2 +- pkg/profiler/provider/options.go | 23 ------- pkg/profiler/service/hostoptions.go | 34 ---------- pkg/recipes/driver/bicep.go | 2 +- pkg/recipes/engine/engine.go | 2 +- pkg/recipes/terraform/execute.go | 2 +- pkg/recipes/terraform/install.go | 2 +- pkg/recipes/terraform/module.go | 2 +- pkg/ucp/config.go | 12 ++-- pkg/ucp/frontend/api/server.go | 2 +- pkg/ucp/server/server.go | 28 ++++----- 53 files changed, 211 insertions(+), 404 deletions(-) rename cmd/applications-rp/{radius-self-hosted.yaml => applications-rp-dev.yaml} (91%) delete mode 100644 cmd/applications-rp/radius-dev.yaml rename cmd/controller/{controller-self-hosted.yaml => controller-dev.yaml} (70%) rename pkg/{ucp => components}/hosting/hosting.go (100%) rename pkg/{ucp => components}/hosting/hosting_test.go (100%) rename pkg/{ucp => components}/hosting/run.go (100%) rename pkg/{ => components}/metrics/README.md (100%) rename pkg/{ => components}/metrics/asyncoperationmetrics.go (100%) rename pkg/{ => components}/metrics/metrics.go (100%) rename pkg/{metrics/provider => components/metrics/metricsservice}/options.go (52%) rename pkg/{metrics/provider => components/metrics/metricsservice}/prometheusexporter.go (94%) rename pkg/{metrics/service => components/metrics/metricsservice}/service.go (81%) rename pkg/{ => components}/metrics/recipeenginemetrics.go (100%) rename pkg/{ => components}/metrics/types.go (100%) rename pkg/{profiler/service => components/profiler/profilerservice}/service.go (82%) rename pkg/{ => components}/trace/doc.go (100%) rename pkg/{trace => components/trace/traceservice}/options.go (90%) rename pkg/{trace/trace.go => components/trace/traceservice/service.go} (81%) rename pkg/{ => components}/trace/util.go (100%) rename pkg/{ => components}/trace/util_test.go (100%) delete mode 100644 pkg/metrics/service/hostoptions.go delete mode 100644 pkg/profiler/provider/options.go delete mode 100644 pkg/profiler/service/hostoptions.go diff --git a/.vscode/launch.json b/.vscode/launch.json index 899ef7aa1e..85a8aaa6fe 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -31,13 +31,6 @@ "mode": "auto", "preLaunchTask": "Build Radius (all)", "program": "${workspaceFolder}/cmd/applications-rp/main.go", - "args": [ - "--config-file", - "${workspaceFolder}/cmd/applications-rp/radius-self-hosted.yaml" - ], - "env": { - "RADIUS_ENV": "self-hosted" - } }, { "name": "Launch Dynamic RP", @@ -54,11 +47,6 @@ "mode": "auto", "preLaunchTask": "Build Radius (all)", "program": "${workspaceFolder}/cmd/ucpd/main.go", - "cwd": "${workspaceFolder}", - "args": [ - "--config-file", - "${workspaceFolder}/cmd/ucpd/ucp-dev.yaml" - ] }, { "name": "Launch Controller", @@ -67,16 +55,10 @@ "mode": "auto", "preLaunchTask": "Build Radius (all)", "program": "${workspaceFolder}/cmd/controller/main.go", - "cwd": "${workspaceFolder}", "args": [ - "--config-file", - "${workspaceFolder}/cmd/controller/controller-self-hosted.yaml", "--cert-dir", "" ], - "env": { - "RADIUS_ENV": "self-hosted" - } }, { "name": "Launch Deployment Engine", diff --git a/cmd/applications-rp/radius-self-hosted.yaml b/cmd/applications-rp/applications-rp-dev.yaml similarity index 91% rename from cmd/applications-rp/radius-self-hosted.yaml rename to cmd/applications-rp/applications-rp-dev.yaml index 121ee728d0..e2a1c7a6e6 100644 --- a/cmd/applications-rp/radius-self-hosted.yaml +++ b/cmd/applications-rp/applications-rp-dev.yaml @@ -8,7 +8,7 @@ # - Disables metrics and profiler # environment: - name: self-hosted + name: "dev" roleLocation: "global" databaseProvider: provider: "apiserver" @@ -24,8 +24,9 @@ queueProvider: secretProvider: provider: "kubernetes" metricsProvider: + enabled: false + serviceName: applications-rp prometheus: - enabled: true path: "/metrics" port: 9092 profilerProvider: @@ -49,7 +50,8 @@ logging: json: false # Tracing configuration tracerProvider: - serviceName: "applications.core" + enabled: false + serviceName: applications-rp zipkin: url: "http://localhost:9411/api/v2/spans" bicep: diff --git a/cmd/applications-rp/cmd/root.go b/cmd/applications-rp/cmd/root.go index 6e891b9f66..0a66a87cab 100644 --- a/cmd/applications-rp/cmd/root.go +++ b/cmd/applications-rp/cmd/root.go @@ -26,13 +26,13 @@ import ( "github.com/radius-project/radius/pkg/armrpc/builder" "github.com/radius-project/radius/pkg/armrpc/hostoptions" - metricsservice "github.com/radius-project/radius/pkg/metrics/service" - profilerservice "github.com/radius-project/radius/pkg/profiler/service" + "github.com/radius-project/radius/pkg/components/metrics/metricsservice" + "github.com/radius-project/radius/pkg/components/profiler/profilerservice" + "github.com/radius-project/radius/pkg/components/trace/traceservice" "github.com/radius-project/radius/pkg/recipes/controllerconfig" "github.com/radius-project/radius/pkg/server" - "github.com/radius-project/radius/pkg/trace" - "github.com/radius-project/radius/pkg/ucp/hosting" + "github.com/radius-project/radius/pkg/components/hosting" "github.com/radius-project/radius/pkg/ucp/ucplog" corerp_setup "github.com/radius-project/radius/pkg/corerp/setup" @@ -54,19 +54,6 @@ var rootCmd = &cobra.Command{ return err } - hostingSvc := []hosting.Service{} - - metricOptions := metricsservice.NewHostOptionsFromEnvironment(*options.Config) - metricOptions.Config.ServiceName = serviceName - if metricOptions.Config.Prometheus.Enabled { - hostingSvc = append(hostingSvc, metricsservice.NewService(metricOptions)) - } - - profilerOptions := profilerservice.NewHostOptionsFromEnvironment(*options.Config) - if profilerOptions.Config.Enabled { - hostingSvc = append(hostingSvc, profilerservice.NewService(profilerOptions)) - } - logger, flush, err := ucplog.NewLogger(serviceName, &options.Config.Logging) if err != nil { return err @@ -76,23 +63,32 @@ var rootCmd = &cobra.Command{ // Must set the logger before using controller-runtime. runtimelog.SetLogger(logger) + services := []hosting.Service{} + if options.Config.MetricsProvider.Enabled { + services = append(services, &metricsservice.Service{Options: &options.Config.MetricsProvider}) + } + + if options.Config.ProfilerProvider.Enabled { + services = append(services, &profilerservice.Service{Options: &options.Config.ProfilerProvider}) + } + + if options.Config.TracerProvider.Enabled { + services = append(services, &traceservice.Service{Options: &options.Config.TracerProvider}) + } + builders, err := builders(options) if err != nil { return err } - hostingSvc = append( - hostingSvc, + services = append( + services, server.NewAPIService(options, builders), server.NewAsyncWorker(options, builders), ) - tracerOpts := options.Config.TracerProvider - tracerOpts.ServiceName = serviceName - hostingSvc = append(hostingSvc, &trace.Service{Options: tracerOpts}) - host := &hosting.Host{ - Services: hostingSvc, + Services: services, } // Make the logger available to the services. @@ -107,7 +103,7 @@ var rootCmd = &cobra.Command{ func Execute() { // Let users override the configuration via `--config-file`. - rootCmd.Flags().String("config-file", fmt.Sprintf("radius-%s.yaml", hostoptions.Environment()), "The service configuration file.") + rootCmd.Flags().String("config-file", fmt.Sprintf("applications-rp-%s.yaml", hostoptions.Environment()), "The service configuration file.") cobra.CheckErr(rootCmd.ExecuteContext(context.Background())) } diff --git a/cmd/applications-rp/radius-dev.yaml b/cmd/applications-rp/radius-dev.yaml deleted file mode 100644 index c69667a7dc..0000000000 --- a/cmd/applications-rp/radius-dev.yaml +++ /dev/null @@ -1,43 +0,0 @@ -# This is an example of configuration file. -environment: - name: Dev - roleLocation: "global" -databaseProvider: - provider: "etcd" - etcd: - inmemory: true -queueProvider: - provider: inmemory - name: radius -profilerProvider: - enabled: true - port: 6060 -secretProvider: - provider: etcd - etcd: - inmemory: true -metricsProvider: - prometheus: - enabled: true - path: "/metrics" - port: 9090 -featureFlags: - - "PLACEHOLDER" -server: - host: "0.0.0.0" - port: 8080 - enableArmAuth: false -workerServer: - maxOperationConcurrency: 10 - maxOperationRetryCount: 2 -ucp: - kind: kubernetes - # Logging configuration -logging: - level: "info" - json: false -bicep: - deleteRetryCount: 20 - deleteRetryDelaySeconds: 60 -terraform: - path: "/terraform" \ No newline at end of file diff --git a/cmd/controller/cmd/root.go b/cmd/controller/cmd/root.go index 88137025c4..4ac4c42f5a 100644 --- a/cmd/controller/cmd/root.go +++ b/cmd/controller/cmd/root.go @@ -28,9 +28,9 @@ import ( "github.com/go-logr/logr" "github.com/radius-project/radius/pkg/armrpc/hostoptions" + "github.com/radius-project/radius/pkg/components/hosting" + "github.com/radius-project/radius/pkg/components/trace/traceservice" "github.com/radius-project/radius/pkg/controller" - "github.com/radius-project/radius/pkg/trace" - "github.com/radius-project/radius/pkg/ucp/hosting" "github.com/radius-project/radius/pkg/ucp/ucplog" "github.com/spf13/cobra" runtimelog "sigs.k8s.io/controller-runtime/pkg/log" @@ -62,11 +62,15 @@ var rootCmd = &cobra.Command{ logger.Info("Loaded options", "configfile", configFilePath) - host := &hosting.Host{Services: []hosting.Service{ - &trace.Service{Options: options.Config.TracerProvider}, + services := []hosting.Service{ &controller.Service{Options: options, TLSCertDir: tlsCertDir}, - }} + } + + if options.Config.TracerProvider.Enabled { + services = append(services, &traceservice.Service{Options: &options.Config.TracerProvider}) + } + host := &hosting.Host{Services: services} return hosting.RunWithInterrupts(ctx, host) }, } diff --git a/cmd/controller/controller-self-hosted.yaml b/cmd/controller/controller-dev.yaml similarity index 70% rename from cmd/controller/controller-self-hosted.yaml rename to cmd/controller/controller-dev.yaml index a3c4274dc5..b3f398d158 100644 --- a/cmd/controller/controller-self-hosted.yaml +++ b/cmd/controller/controller-dev.yaml @@ -1,14 +1,20 @@ environment: - name: Dev + name: "dev" roleLocation: "global" profilerProvider: enabled: true port: 6063 metricsProvider: + enabled: true + serviceName: "controller" prometheus: - enabled: true path: "/metrics" port: 9093 +tracerProvider: + enabled: false + serviceName: "controller" + zipkin: + url: "http://localhost:9411/api/v2/spans" server: host: "0.0.0.0" port: 8083 diff --git a/cmd/dynamic-rp/cmd/root.go b/cmd/dynamic-rp/cmd/root.go index 046ce4002d..1b5513ad7f 100644 --- a/cmd/dynamic-rp/cmd/root.go +++ b/cmd/dynamic-rp/cmd/root.go @@ -26,9 +26,9 @@ import ( runtimelog "sigs.k8s.io/controller-runtime/pkg/log" "github.com/radius-project/radius/pkg/armrpc/hostoptions" + "github.com/radius-project/radius/pkg/components/hosting" "github.com/radius-project/radius/pkg/dynamicrp" "github.com/radius-project/radius/pkg/dynamicrp/server" - "github.com/radius-project/radius/pkg/ucp/hosting" "github.com/radius-project/radius/pkg/ucp/ucplog" ) diff --git a/cmd/dynamic-rp/dynamicrp-dev.yaml b/cmd/dynamic-rp/dynamicrp-dev.yaml index 22f60a4fe7..0df8054d64 100644 --- a/cmd/dynamic-rp/dynamicrp-dev.yaml +++ b/cmd/dynamic-rp/dynamicrp-dev.yaml @@ -1,6 +1,6 @@ # This is an example of configuration file. environment: - name: Dev + name: "dev" roleLocation: "global" databaseProvider: provider: "apiserver" @@ -19,10 +19,16 @@ profilerProvider: enabled: false port: 6062 metricsProvider: + enabled: false + serviceName: "dynamic-rp" prometheus: - enabled: false path: "/metrics" port: 9092 +tracerProvider: + enabled: false + serviceName: "dynamic-rp" + zipkin: + url: "http://localhost:9411/api/v2/spans" server: host: "0.0.0.0" port: 8082 diff --git a/cmd/rad/cmd/root.go b/cmd/rad/cmd/root.go index 466c26e0b0..f81e8e78b8 100644 --- a/cmd/rad/cmd/root.go +++ b/cmd/rad/cmd/root.go @@ -82,8 +82,11 @@ import ( "github.com/radius-project/radius/pkg/cli/kubernetes/portforward" "github.com/radius-project/radius/pkg/cli/output" "github.com/radius-project/radius/pkg/cli/prompt" - "github.com/radius-project/radius/pkg/trace" "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/propagation" + "go.opentelemetry.io/otel/sdk/resource" + sdktrace "go.opentelemetry.io/otel/sdk/trace" + semconv "go.opentelemetry.io/otel/semconv/v1.17.0" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -144,9 +147,7 @@ func prettyPrintJSON(o any) (string, error) { func Execute() error { ctx := context.WithValue(context.Background(), ConfigHolderKey, ConfigHolder) - shutdown, err := trace.InitTracer(trace.Options{ - ServiceName: serviceName, - }) + shutdown, err := initTracer() if err != nil { fmt.Println("Error:", err) return err @@ -182,6 +183,23 @@ func Execute() error { return nil } +func initTracer() (func(context.Context) error, error) { + // Intialize the tracer provider + tp := sdktrace.NewTracerProvider( + sdktrace.WithSampler(sdktrace.AlwaysSample()), + sdktrace.WithResource(resource.NewWithAttributes( + semconv.SchemaURL, + semconv.ServiceNameKey.String(serviceName), + )), + ) + + // Set the tracer provider as "global" for the CLI process. + otel.SetTracerProvider(tp) + otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{})) + + return tp.Shutdown, nil +} + func init() { cobra.OnInitialize(initConfig) diff --git a/cmd/ucpd/cmd/root.go b/cmd/ucpd/cmd/root.go index 1face5384c..e108b4e9fb 100644 --- a/cmd/ucpd/cmd/root.go +++ b/cmd/ucpd/cmd/root.go @@ -26,8 +26,8 @@ import ( runtimelog "sigs.k8s.io/controller-runtime/pkg/log" "github.com/radius-project/radius/pkg/armrpc/hostoptions" + "github.com/radius-project/radius/pkg/components/hosting" "github.com/radius-project/radius/pkg/ucp" - "github.com/radius-project/radius/pkg/ucp/hosting" "github.com/radius-project/radius/pkg/ucp/server" "github.com/radius-project/radius/pkg/ucp/ucplog" ) @@ -75,6 +75,6 @@ var rootCmd = &cobra.Command{ func Execute() { // Let users override the configuration via `--config-file`. - rootCmd.Flags().String("config-file", fmt.Sprintf("radius-%s.yaml", hostoptions.Environment()), "The service configuration file.") + rootCmd.Flags().String("config-file", fmt.Sprintf("ucp-%s.yaml", hostoptions.Environment()), "The service configuration file.") cobra.CheckErr(rootCmd.ExecuteContext(context.Background())) } diff --git a/cmd/ucpd/ucp-dev.yaml b/cmd/ucpd/ucp-dev.yaml index a7561eeb7a..3bc09c4871 100644 --- a/cmd/ucpd/ucp-dev.yaml +++ b/cmd/ucpd/ucp-dev.yaml @@ -10,7 +10,7 @@ # - Disables metrics and profiler # environment: - name: Dev + name: "dev" roleLocation: "global" server: port: 9000 @@ -69,8 +69,9 @@ routing: # port is not the same as metrics configuration in radius-self-hosted.yaml # so that we can run both services in debug mode. metricsProvider: + enabled: false + serviceName: "ucp" prometheus: - enabled: false path: "/metrics" port: 9091 @@ -81,6 +82,7 @@ logging: # Tracing configuration tracerProvider: + enabled: false serviceName: "ucp" zipkin: url: "http://localhost:9411/api/v2/spans" \ No newline at end of file diff --git a/deploy/Chart/templates/controller/configmaps.yaml b/deploy/Chart/templates/controller/configmaps.yaml index 17fabbc717..281a05404e 100644 --- a/deploy/Chart/templates/controller/configmaps.yaml +++ b/deploy/Chart/templates/controller/configmaps.yaml @@ -36,8 +36,9 @@ data: kind: kubernetes metricsProvider: + enabled: true + serviceName: "controller" prometheus: - enabled: true path: "/metrics" port: 9090 @@ -50,6 +51,7 @@ data: {{- if and .Values.global.zipkin .Values.global.zipkin.url }} tracerProvider: + enabled: true serviceName: "controller" zipkin: url: {{ .Values.global.zipkin.url }} diff --git a/deploy/Chart/templates/dynamic-rp/configmaps.yaml b/deploy/Chart/templates/dynamic-rp/configmaps.yaml index 9dc3c2ddf2..231e81d2f4 100644 --- a/deploy/Chart/templates/dynamic-rp/configmaps.yaml +++ b/deploy/Chart/templates/dynamic-rp/configmaps.yaml @@ -25,8 +25,9 @@ data: context: "" namespace: "radius-system" metricsProvider: + enabled: true + serviceName: "dynamic-rp" prometheus: - enabled: true path: "/metrics" port: 9092 profilerProvider: @@ -47,6 +48,7 @@ data: json: true {{- if and .Values.global.zipkin .Values.global.zipkin.url }} tracerProvider: + enabled: true serviceName: "dynamic-rp" zipkin: url: {{ .Values.global.zipkin.url }} diff --git a/deploy/Chart/templates/rp/configmaps.yaml b/deploy/Chart/templates/rp/configmaps.yaml index 6429960c08..78aeb8ac98 100644 --- a/deploy/Chart/templates/rp/configmaps.yaml +++ b/deploy/Chart/templates/rp/configmaps.yaml @@ -25,8 +25,9 @@ data: context: "" namespace: "radius-system" metricsProvider: + enabled: true + serviceName: "applications-rp" prometheus: - enabled: true path: "/metrics" port: 9090 profilerProvider: @@ -47,7 +48,8 @@ data: json: true {{- if and .Values.global.zipkin .Values.global.zipkin.url }} tracerProvider: - serviceName: "applications.core" + enabled: true + serviceName: "applications-rp" zipkin: url: {{ .Values.global.zipkin.url }} {{- end }} diff --git a/deploy/Chart/templates/ucp/configmaps.yaml b/deploy/Chart/templates/ucp/configmaps.yaml index cc9968228c..5dcad4fae0 100644 --- a/deploy/Chart/templates/ucp/configmaps.yaml +++ b/deploy/Chart/templates/ucp/configmaps.yaml @@ -11,7 +11,7 @@ data: # Radius configuration file. # See https://github.com/radius-project/radius/blob/main/docs/contributing/contributing-code/contributing-code-control-plane/configSettings.md for more information. environment: - name: Dev + name: self-hosted roleLocation: "global" server: port: 9443 @@ -61,8 +61,9 @@ data: defaultDownstreamEndpoint: "http://dynamic-rp.radius-sytem:8082" metricsProvider: + enabled: true + serviceName: "ucp" prometheus: - enabled: true path: "/metrics" port: 9090 @@ -72,6 +73,7 @@ data: {{- if and .Values.global.zipkin .Values.global.zipkin.url }} tracerProvider: + enabled: true serviceName: "ucp" zipkin: url: {{ .Values.global.zipkin.url }} diff --git a/docs/contributing/contributing-code/contributing-code-control-plane/configSettings.md b/docs/contributing/contributing-code/contributing-code-control-plane/configSettings.md index 356140ac77..e95262fb0f 100644 --- a/docs/contributing/contributing-code/contributing-code-control-plane/configSettings.md +++ b/docs/contributing/contributing-code/contributing-code-control-plane/configSettings.md @@ -174,68 +174,7 @@ ucp: ## Example configuration files -Below are completed examples of possible configurations: - -### Applications.Core and Portable Resources' Providers -```yaml -environment: - name: self-hosted - roleLocation: "global" -databaseProvider: - provider: "apiserver" - apiserver: - context: "" - namespace: "radius-system" -queueProvider: - provider: "apiserver" - name: "radius" - apiserver: - context: "" - namespace: "radius-system" -metricsProvider: - prometheus: - enabled: true - path: "/metrics" - port: 9090 -server: - host: "0.0.0.0" - port: 5443 -workerServer: - maxOperationConcurrency: 10 - maxOperationRetryCount: 2 -ucp: - kind: kubernetes -``` - -### UCP -```yaml -location: 'global' -databaseProvider: - provider: "apiserver" - apiserver: - context: "" - namespace: "radius-system" -secretProvider: - provider: "kubernetes" -planes: - - id: "/planes/radius/local" - properties: - resourceProviders: - Applications.Core: "http://applications-rp.radius-system:5443" - Applications.Dapr: "http://applications-rp.radius-system:5443" - Applications.Datastores: "http://applications-rp.radius-system:5443" - Applications.Messaging: "http://applications-rp.radius-system:5443" - Microsoft.Resources: "http://bicep-de.radius-system:6443" - kind: "UCPNative" - - id: "/planes/aws/aws" - properties: - kind: "AWS" -metricsProvider: - prometheus: - enabled: true - path: "/metrics" - port: 9090 -``` +See the configuration files in `cmd//*.yaml` for examples of configuration files. ## Environment Variables diff --git a/pkg/armrpc/asyncoperation/statusmanager/statusmanager.go b/pkg/armrpc/asyncoperation/statusmanager/statusmanager.go index 091b80e1a2..bf0e0a2a7b 100644 --- a/pkg/armrpc/asyncoperation/statusmanager/statusmanager.go +++ b/pkg/armrpc/asyncoperation/statusmanager/statusmanager.go @@ -26,9 +26,9 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" "github.com/radius-project/radius/pkg/components/database" + "github.com/radius-project/radius/pkg/components/metrics" "github.com/radius-project/radius/pkg/components/queue" - "github.com/radius-project/radius/pkg/metrics" - "github.com/radius-project/radius/pkg/trace" + "github.com/radius-project/radius/pkg/components/trace" "github.com/radius-project/radius/pkg/ucp/resources" "github.com/google/uuid" diff --git a/pkg/armrpc/asyncoperation/worker/worker.go b/pkg/armrpc/asyncoperation/worker/worker.go index c634a5eb15..ec708631c5 100644 --- a/pkg/armrpc/asyncoperation/worker/worker.go +++ b/pkg/armrpc/asyncoperation/worker/worker.go @@ -29,10 +29,10 @@ import ( ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" manager "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" "github.com/radius-project/radius/pkg/components/database" + "github.com/radius-project/radius/pkg/components/metrics" "github.com/radius-project/radius/pkg/components/queue" + "github.com/radius-project/radius/pkg/components/trace" "github.com/radius-project/radius/pkg/logging" - "github.com/radius-project/radius/pkg/metrics" - "github.com/radius-project/radius/pkg/trace" "github.com/radius-project/radius/pkg/ucp/resources" "github.com/radius-project/radius/pkg/ucp/ucplog" diff --git a/pkg/armrpc/hostoptions/env.go b/pkg/armrpc/hostoptions/env.go index 8770254dd5..9da432fd54 100644 --- a/pkg/armrpc/hostoptions/env.go +++ b/pkg/armrpc/hostoptions/env.go @@ -22,25 +22,20 @@ import ( ) const ( - RadiusDevEnvironment = "dev" - RadiusSelfHostedDevEnvironment = "self-hosted-dev" - RadiusSelfHostedEnvironment = "self-hosted" - RadiusDogfood = "df-westus3" - RadiusCanaryEastUS2EUAP = "prod-eastus2euap" - RadiusCanaryCentralUS2EUAP = "prod-centralus2euap" - RadiusProdPrefix = "prod" + RadiusDevEnvironment = "dev" + RadiusSelfHostedEnvironment = "self-hosted" ) var currentEnv = RadiusDevEnvironment -// Environment returns the current environment name. +// Environment returns the current environment name. Can be configured by the RADIUS_ENV environment variables. Defaults to "dev" if not set. func Environment() string { return currentEnv } // IsDevelopment returns true if the current environment is development environment. func IsDevelopment() bool { - return strings.HasPrefix(Environment(), RadiusDevEnvironment) || strings.HasPrefix(Environment(), RadiusSelfHostedDevEnvironment) + return strings.HasPrefix(Environment(), RadiusDevEnvironment) } // IsSelfHosted returns true if the current environment is self-hosted environment. @@ -48,22 +43,6 @@ func IsSelfHosted() bool { return strings.HasPrefix(Environment(), RadiusSelfHostedEnvironment) } -// IsDogfood returns true if the current environment is dogfood environment. -func IsDogfood() bool { - return Environment() == RadiusDogfood -} - -// IsCanary returns true if the current environment is canary region. -func IsCanary() bool { - env := Environment() - return env == RadiusCanaryEastUS2EUAP || env == RadiusCanaryCentralUS2EUAP -} - -// IsProduction returns true if the current environment is production, but not canary. -func IsProduction() bool { - return !IsCanary() && strings.HasPrefix(Environment(), RadiusProdPrefix) -} - func init() { currentEnv = strings.TrimSpace(strings.ToLower(os.Getenv("RADIUS_ENV"))) if currentEnv == "" { diff --git a/pkg/armrpc/hostoptions/providerconfig.go b/pkg/armrpc/hostoptions/providerconfig.go index e8f19f1fb0..1708cf0f5e 100644 --- a/pkg/armrpc/hostoptions/providerconfig.go +++ b/pkg/armrpc/hostoptions/providerconfig.go @@ -20,32 +20,32 @@ import ( "fmt" "github.com/radius-project/radius/pkg/components/database/databaseprovider" + "github.com/radius-project/radius/pkg/components/metrics/metricsservice" + "github.com/radius-project/radius/pkg/components/profiler/profilerservice" "github.com/radius-project/radius/pkg/components/queue/queueprovider" "github.com/radius-project/radius/pkg/components/secret/secretprovider" + "github.com/radius-project/radius/pkg/components/trace/traceservice" - metricsprovider "github.com/radius-project/radius/pkg/metrics/provider" - profilerprovider "github.com/radius-project/radius/pkg/profiler/provider" - "github.com/radius-project/radius/pkg/trace" "github.com/radius-project/radius/pkg/ucp/config" "github.com/radius-project/radius/pkg/ucp/ucplog" ) // ProviderConfig includes the resource provider configuration. type ProviderConfig struct { - Env EnvironmentOptions `yaml:"environment"` - Identity IdentityOptions `yaml:"identity"` - DatabaseProvider databaseprovider.Options `yaml:"databaseProvider"` - SecretProvider secretprovider.SecretProviderOptions `yaml:"secretProvider"` - QueueProvider queueprovider.QueueProviderOptions `yaml:"queueProvider"` - Server *ServerOptions `yaml:"server,omitempty"` - WorkerServer *WorkerServerOptions `yaml:"workerServer,omitempty"` - MetricsProvider metricsprovider.MetricsProviderOptions `yaml:"metricsProvider"` - TracerProvider trace.Options `yaml:"tracerProvider"` - ProfilerProvider profilerprovider.ProfilerProviderOptions `yaml:"profilerProvider"` - UCP config.UCPOptions `yaml:"ucp"` - Logging ucplog.LoggingOptions `yaml:"logging"` - Bicep BicepOptions `yaml:"bicep,omitempty"` - Terraform TerraformOptions `yaml:"terraform,omitempty"` + Env EnvironmentOptions `yaml:"environment"` + Identity IdentityOptions `yaml:"identity"` + DatabaseProvider databaseprovider.Options `yaml:"databaseProvider"` + SecretProvider secretprovider.SecretProviderOptions `yaml:"secretProvider"` + QueueProvider queueprovider.QueueProviderOptions `yaml:"queueProvider"` + Server *ServerOptions `yaml:"server,omitempty"` + WorkerServer *WorkerServerOptions `yaml:"workerServer,omitempty"` + MetricsProvider metricsservice.Options `yaml:"metricsProvider"` + TracerProvider traceservice.Options `yaml:"tracerProvider"` + ProfilerProvider profilerservice.Options `yaml:"profilerProvider"` + UCP config.UCPOptions `yaml:"ucp"` + Logging ucplog.LoggingOptions `yaml:"logging"` + Bicep BicepOptions `yaml:"bicep,omitempty"` + Terraform TerraformOptions `yaml:"terraform,omitempty"` // FeatureFlags includes the list of feature flags. FeatureFlags []string `yaml:"featureFlags"` diff --git a/pkg/ucp/hosting/hosting.go b/pkg/components/hosting/hosting.go similarity index 100% rename from pkg/ucp/hosting/hosting.go rename to pkg/components/hosting/hosting.go diff --git a/pkg/ucp/hosting/hosting_test.go b/pkg/components/hosting/hosting_test.go similarity index 100% rename from pkg/ucp/hosting/hosting_test.go rename to pkg/components/hosting/hosting_test.go diff --git a/pkg/ucp/hosting/run.go b/pkg/components/hosting/run.go similarity index 100% rename from pkg/ucp/hosting/run.go rename to pkg/components/hosting/run.go diff --git a/pkg/metrics/README.md b/pkg/components/metrics/README.md similarity index 100% rename from pkg/metrics/README.md rename to pkg/components/metrics/README.md diff --git a/pkg/metrics/asyncoperationmetrics.go b/pkg/components/metrics/asyncoperationmetrics.go similarity index 100% rename from pkg/metrics/asyncoperationmetrics.go rename to pkg/components/metrics/asyncoperationmetrics.go diff --git a/pkg/metrics/metrics.go b/pkg/components/metrics/metrics.go similarity index 100% rename from pkg/metrics/metrics.go rename to pkg/components/metrics/metrics.go diff --git a/pkg/metrics/provider/options.go b/pkg/components/metrics/metricsservice/options.go similarity index 52% rename from pkg/metrics/provider/options.go rename to pkg/components/metrics/metricsservice/options.go index c61f24744c..71acfb7b7f 100644 --- a/pkg/metrics/provider/options.go +++ b/pkg/components/metrics/metricsservice/options.go @@ -14,17 +14,25 @@ See the License for the specific language governing permissions and limitations under the License. */ -package provider +package metricsservice -// MetricsProviderOptions represents the options of the providers for publishing metrics. -type MetricsProviderOptions struct { - ServiceName string `yaml:"serviceName,omitempty"` - Prometheus PrometheusOptions `yaml:"prometheus,omitempty"` +// Options represents the options of the providers for publishing metrics. +type Options struct { + // Enabled is the flag to enable the metrics service. + Enabled bool `yaml:"enabled"` + + // ServiceName is the name of the service. + ServiceName string `yaml:"serviceName,omitempty"` + + // Prometheus is the options for the prometheus metrics provider. + Prometheus *PrometheusOptions `yaml:"prometheus,omitempty"` } // PrometheusOptions represents prometheus metrics provider options. type PrometheusOptions struct { - Enabled bool `yaml:"enabled"` - Path string `yaml:"path"` - Port int `yaml:"port"` + // Path is the path where the prometheus metrics are exposed. + Path string `yaml:"path"` + + // Address is the address where the prometheus metrics are exposed. + Port int `yaml:"port"` } diff --git a/pkg/metrics/provider/prometheusexporter.go b/pkg/components/metrics/metricsservice/prometheusexporter.go similarity index 94% rename from pkg/metrics/provider/prometheusexporter.go rename to pkg/components/metrics/metricsservice/prometheusexporter.go index b7e4893cb8..eba3e45aca 100644 --- a/pkg/metrics/provider/prometheusexporter.go +++ b/pkg/components/metrics/metricsservice/prometheusexporter.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package provider +package metricsservice import ( "net/http" @@ -38,7 +38,7 @@ type PrometheusExporter struct { // NewPrometheusExporter creates a PrometheusExporter instance with a MeterProvider and a Handler, // and returns it along with any errors. -func NewPrometheusExporter(options *MetricsProviderOptions) (*PrometheusExporter, error) { +func NewPrometheusExporter(options *Options) (*PrometheusExporter, error) { exporter, err := prometheus.New() if err != nil { return nil, err diff --git a/pkg/metrics/service/service.go b/pkg/components/metrics/metricsservice/service.go similarity index 81% rename from pkg/metrics/service/service.go rename to pkg/components/metrics/metricsservice/service.go index 915205f22c..ad517f28a4 100644 --- a/pkg/metrics/service/service.go +++ b/pkg/components/metrics/metricsservice/service.go @@ -24,21 +24,14 @@ import ( "strconv" "time" - "github.com/radius-project/radius/pkg/metrics" - "github.com/radius-project/radius/pkg/metrics/provider" + "github.com/radius-project/radius/pkg/components/metrics" "github.com/radius-project/radius/pkg/ucp/ucplog" "go.opentelemetry.io/contrib/instrumentation/runtime" ) +// Service implements the metrics service. type Service struct { - Options HostOptions -} - -// NewService creates a new Service instance with the given HostOptions. -func NewService(options HostOptions) *Service { - return &Service{ - Options: options, - } + Options *Options } // Name returns the name of the metrics service. @@ -52,7 +45,7 @@ func (s *Service) Name() string { func (s *Service) Run(ctx context.Context) error { logger := ucplog.FromContextOrDiscard(ctx) - pme, err := provider.NewPrometheusExporter(s.Options.Config) + pme, err := NewPrometheusExporter(s.Options) if err != nil { return err } @@ -68,8 +61,8 @@ func (s *Service) Run(ctx context.Context) error { } mux := http.NewServeMux() - mux.HandleFunc(s.Options.Config.Prometheus.Path, pme.Handler.ServeHTTP) - metricsPort := strconv.Itoa(s.Options.Config.Prometheus.Port) + mux.HandleFunc(s.Options.Prometheus.Path, pme.Handler.ServeHTTP) + metricsPort := strconv.Itoa(s.Options.Prometheus.Port) server := &http.Server{ Addr: ":" + metricsPort, Handler: mux, diff --git a/pkg/metrics/recipeenginemetrics.go b/pkg/components/metrics/recipeenginemetrics.go similarity index 100% rename from pkg/metrics/recipeenginemetrics.go rename to pkg/components/metrics/recipeenginemetrics.go diff --git a/pkg/metrics/types.go b/pkg/components/metrics/types.go similarity index 100% rename from pkg/metrics/types.go rename to pkg/components/metrics/types.go diff --git a/pkg/profiler/service/service.go b/pkg/components/profiler/profilerservice/service.go similarity index 82% rename from pkg/profiler/service/service.go rename to pkg/components/profiler/profilerservice/service.go index 96a7668d70..4bd70352d6 100644 --- a/pkg/profiler/service/service.go +++ b/pkg/components/profiler/profilerservice/service.go @@ -26,15 +26,18 @@ import ( "github.com/radius-project/radius/pkg/ucp/ucplog" ) -type Service struct { - Options HostOptions +// Options represents the options for enabling pprof profiler. +type Options struct { + // Enabled is a flag to enable the profiler. + Enabled bool `yaml:"enabled,omitempty"` + + // Port is the port on which the profiler server listens. + Port int `yaml:"port,omitempty"` } -// NewService of profiler package returns a new Service with the configs needed -func NewService(options HostOptions) *Service { - return &Service{ - Options: options, - } +// Service is the profiler service. +type Service struct { + Options *Options } // Name returns the name of the profiler service. @@ -47,7 +50,7 @@ func (s *Service) Name() string { func (s *Service) Run(ctx context.Context) error { logger := ucplog.FromContextOrDiscard(ctx) - profilerPort := strconv.Itoa(s.Options.Config.Port) + profilerPort := strconv.Itoa(s.Options.Port) server := &http.Server{ Addr: ":" + profilerPort, BaseContext: func(ln net.Listener) context.Context { diff --git a/pkg/components/testhost/host.go b/pkg/components/testhost/host.go index 83f5f19cc9..0a01f98cc7 100644 --- a/pkg/components/testhost/host.go +++ b/pkg/components/testhost/host.go @@ -31,7 +31,7 @@ import ( "testing" "time" - "github.com/radius-project/radius/pkg/ucp/hosting" + "github.com/radius-project/radius/pkg/components/hosting" "github.com/radius-project/radius/test/testcontext" "github.com/stretchr/testify/require" ) diff --git a/pkg/trace/doc.go b/pkg/components/trace/doc.go similarity index 100% rename from pkg/trace/doc.go rename to pkg/components/trace/doc.go diff --git a/pkg/trace/options.go b/pkg/components/trace/traceservice/options.go similarity index 90% rename from pkg/trace/options.go rename to pkg/components/trace/traceservice/options.go index bf41bc760a..a063c477b7 100644 --- a/pkg/trace/options.go +++ b/pkg/components/trace/traceservice/options.go @@ -14,10 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -package trace +package traceservice // Options represents the trace options. type Options struct { + // Enabled configures whether tracing is enabled. + Enabled bool `yaml:"enabled"` // ServiceName represents the name of service. ServiceName string `yaml:"serviceName,omitempty"` // Zipkin represents zipkin options. diff --git a/pkg/trace/trace.go b/pkg/components/trace/traceservice/service.go similarity index 81% rename from pkg/trace/trace.go rename to pkg/components/trace/traceservice/service.go index 72bedc3438..84492973da 100644 --- a/pkg/trace/trace.go +++ b/pkg/components/trace/traceservice/service.go @@ -5,7 +5,7 @@ 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 + 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, @@ -13,7 +13,8 @@ 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 trace + +package traceservice import ( "context" @@ -28,7 +29,7 @@ import ( // Service implements the hosting.Service interface for the tracer. type Service struct { - Options Options + Options *Options } // Name gets the service name. @@ -38,7 +39,7 @@ func (s *Service) Name() string { // Run runs the tracer service. func (s *Service) Run(ctx context.Context) error { - shutdown, err := InitTracer(s.Options) + shutdown, err := initTracer(s.Options) if err != nil { return err } @@ -47,9 +48,8 @@ func (s *Service) Run(ctx context.Context) error { return shutdown(ctx) } -// InitTracer sets up a tracer provider with a sampler and resource attributes, and optionally registers a Zipkin exporter -// and batcher. It returns a shutdown function and an error if one occurs. -func InitTracer(opts Options) (func(context.Context) error, error) { +// initTracer configures a tracer provider with the given options. +func initTracer(opts *Options) (func(context.Context) error, error) { tp := sdktrace.NewTracerProvider( sdktrace.WithSampler(sdktrace.AlwaysSample()), sdktrace.WithResource(resource.NewWithAttributes( @@ -62,10 +62,12 @@ func InitTracer(opts Options) (func(context.Context) error, error) { if err != nil { return nil, err } + batcher := sdktrace.NewBatchSpanProcessor(exporter) tp.RegisterSpanProcessor(batcher) } + otel.SetTracerProvider(tp) otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{})) return tp.Shutdown, nil diff --git a/pkg/trace/util.go b/pkg/components/trace/util.go similarity index 100% rename from pkg/trace/util.go rename to pkg/components/trace/util.go diff --git a/pkg/trace/util_test.go b/pkg/components/trace/util_test.go similarity index 100% rename from pkg/trace/util_test.go rename to pkg/components/trace/util_test.go diff --git a/pkg/controller/service.go b/pkg/controller/service.go index 1c55ede895..422aee16d5 100644 --- a/pkg/controller/service.go +++ b/pkg/controller/service.go @@ -21,9 +21,9 @@ import ( "fmt" "github.com/radius-project/radius/pkg/armrpc/hostoptions" + "github.com/radius-project/radius/pkg/components/hosting" radappiov1alpha3 "github.com/radius-project/radius/pkg/controller/api/radapp.io/v1alpha3" "github.com/radius-project/radius/pkg/controller/reconciler" - "github.com/radius-project/radius/pkg/ucp/hosting" "github.com/radius-project/radius/pkg/ucp/ucplog" "k8s.io/apimachinery/pkg/runtime" @@ -64,8 +64,8 @@ func (*Service) Name() string { func (s *Service) Run(ctx context.Context) error { logger := ucplog.FromContextOrDiscard(ctx) - metricsAddr := "0" // Disable metrics - if s.Options.Config.MetricsProvider.Prometheus.Enabled { + metricsAddr := "0" // Setting the address to 0 will disable the metrics server. + if s.Options.Config.MetricsProvider.Enabled { metricsAddr = fmt.Sprintf(":%d", s.Options.Config.MetricsProvider.Prometheus.Port) } diff --git a/pkg/dynamicrp/config.go b/pkg/dynamicrp/config.go index 1ffa71bd47..8a658ae039 100644 --- a/pkg/dynamicrp/config.go +++ b/pkg/dynamicrp/config.go @@ -21,11 +21,11 @@ import ( "github.com/radius-project/radius/pkg/armrpc/hostoptions" "github.com/radius-project/radius/pkg/components/database/databaseprovider" + "github.com/radius-project/radius/pkg/components/metrics/metricsservice" + "github.com/radius-project/radius/pkg/components/profiler/profilerservice" "github.com/radius-project/radius/pkg/components/queue/queueprovider" "github.com/radius-project/radius/pkg/components/secret/secretprovider" - metricsprovider "github.com/radius-project/radius/pkg/metrics/provider" - profilerprovider "github.com/radius-project/radius/pkg/profiler/provider" - "github.com/radius-project/radius/pkg/trace" + "github.com/radius-project/radius/pkg/components/trace/traceservice" ucpconfig "github.com/radius-project/radius/pkg/ucp/config" "github.com/radius-project/radius/pkg/ucp/ucplog" "gopkg.in/yaml.v3" @@ -48,10 +48,10 @@ type Config struct { Logging ucplog.LoggingOptions `yaml:"logging"` // Metrics is the configuration for the metrics endpoint. - Metrics metricsprovider.MetricsProviderOptions `yaml:"metricsProvider"` + Metrics metricsservice.Options `yaml:"metricsProvider"` // Profiler is the configuration for the profiler endpoint. - Profiler profilerprovider.ProfilerProviderOptions `yaml:"profilerProvider"` + Profiler profilerservice.Options `yaml:"profilerProvider"` // Queue is the configuration for the message queue. Queue queueprovider.QueueProviderOptions `yaml:"queueProvider"` @@ -66,7 +66,7 @@ type Config struct { Terraform hostoptions.TerraformOptions `yaml:"terraform"` // Tracing is the configuration for the tracing system. - Tracing trace.Options `yaml:"tracerProvider"` + Tracing traceservice.Options `yaml:"tracerProvider"` // UCPConfig is the configuration for the connection to UCP. UCP ucpconfig.UCPOptions `yaml:"ucp"` diff --git a/pkg/dynamicrp/server/server.go b/pkg/dynamicrp/server/server.go index d6e2cc24c1..ad190b0d89 100644 --- a/pkg/dynamicrp/server/server.go +++ b/pkg/dynamicrp/server/server.go @@ -17,13 +17,13 @@ limitations under the License. package server import ( + "github.com/radius-project/radius/pkg/components/hosting" + "github.com/radius-project/radius/pkg/components/metrics/metricsservice" + "github.com/radius-project/radius/pkg/components/profiler/profilerservice" + "github.com/radius-project/radius/pkg/components/trace/traceservice" "github.com/radius-project/radius/pkg/dynamicrp" "github.com/radius-project/radius/pkg/dynamicrp/backend" "github.com/radius-project/radius/pkg/dynamicrp/frontend" - metricsservice "github.com/radius-project/radius/pkg/metrics/service" - profilerservice "github.com/radius-project/radius/pkg/profiler/service" - "github.com/radius-project/radius/pkg/trace" - "github.com/radius-project/radius/pkg/ucp/hosting" ) // NewServer initializes a host for UCP based on the provided options. @@ -31,22 +31,18 @@ func NewServer(options *dynamicrp.Options) (*hosting.Host, error) { services := []hosting.Service{} // Metrics is provided via a service. - if options.Config.Metrics.Prometheus.Enabled { - services = append(services, metricsservice.NewService(metricsservice.HostOptions{ - Config: &options.Config.Metrics, - })) + if options.Config.Metrics.Enabled { + services = append(services, &metricsservice.Service{Options: &options.Config.Metrics}) } // Profiling is provided via a service. if options.Config.Profiler.Enabled { - services = append(services, profilerservice.NewService(profilerservice.HostOptions{ - Config: &options.Config.Profiler, - })) + services = append(services, &profilerservice.Service{Options: &options.Config.Profiler}) } // Tracing is provided via a service. - if options.Config.Tracing.ServiceName != "" { - services = append(services, &trace.Service{Options: options.Config.Tracing}) + if options.Config.Tracing.Enabled { + services = append(services, &traceservice.Service{Options: &options.Config.Tracing}) } services = append(services, frontend.NewService(options)) diff --git a/pkg/metrics/service/hostoptions.go b/pkg/metrics/service/hostoptions.go deleted file mode 100644 index 009d3bea90..0000000000 --- a/pkg/metrics/service/hostoptions.go +++ /dev/null @@ -1,35 +0,0 @@ -/* -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 metricsservice - -import ( - "github.com/radius-project/radius/pkg/armrpc/hostoptions" - "github.com/radius-project/radius/pkg/metrics/provider" -) - -// HostOptions defines all of the settings that our metric's execution environment provides. -type HostOptions struct { - // Config is the bootstrap metrics configuration loaded from config file. - Config *provider.MetricsProviderOptions -} - -// NewHostOptionsFromEnvironment creates a new HostOptions object from a ProviderConfig object. -func NewHostOptionsFromEnvironment(options hostoptions.ProviderConfig) HostOptions { - return HostOptions{ - Config: &options.MetricsProvider, - } -} diff --git a/pkg/portableresources/processors/resourceclient.go b/pkg/portableresources/processors/resourceclient.go index f2e95261ad..f53140d5a0 100644 --- a/pkg/portableresources/processors/resourceclient.go +++ b/pkg/portableresources/processors/resourceclient.go @@ -28,8 +28,8 @@ import ( aztoken "github.com/radius-project/radius/pkg/azure/tokencredentials" "github.com/radius-project/radius/pkg/cli/clients" "github.com/radius-project/radius/pkg/cli/clients_new/generated" + "github.com/radius-project/radius/pkg/components/trace" "github.com/radius-project/radius/pkg/sdk" - "github.com/radius-project/radius/pkg/trace" "github.com/radius-project/radius/pkg/ucp/resources" resources_azure "github.com/radius-project/radius/pkg/ucp/resources/azure" resources_kubernetes "github.com/radius-project/radius/pkg/ucp/resources/kubernetes" diff --git a/pkg/profiler/provider/options.go b/pkg/profiler/provider/options.go deleted file mode 100644 index 4a2005a6f4..0000000000 --- a/pkg/profiler/provider/options.go +++ /dev/null @@ -1,23 +0,0 @@ -/* -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 provider - -// ProfilerProviderOptions represents the options for enabling pprof profiler. -type ProfilerProviderOptions struct { - Enabled bool `yaml:"enabled,omitempty"` - Port int `yaml:"port,omitempty"` -} diff --git a/pkg/profiler/service/hostoptions.go b/pkg/profiler/service/hostoptions.go deleted file mode 100644 index 795ac7d334..0000000000 --- a/pkg/profiler/service/hostoptions.go +++ /dev/null @@ -1,34 +0,0 @@ -/* -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 profilerservice - -import ( - "github.com/radius-project/radius/pkg/armrpc/hostoptions" - "github.com/radius-project/radius/pkg/profiler/provider" -) - -type HostOptions struct { - // Config is the bootstrap profiler configuration loaded from config file. - Config *provider.ProfilerProviderOptions -} - -// NewHostOptionsFromEnvironment of profiler/hostoptions package returns the HostOptions for profiler service. -func NewHostOptionsFromEnvironment(options hostoptions.ProviderConfig) HostOptions { - return HostOptions{ - Config: &options.ProfilerProvider, - } -} diff --git a/pkg/recipes/driver/bicep.go b/pkg/recipes/driver/bicep.go index 8e1e9dba4e..745d8b4d1c 100644 --- a/pkg/recipes/driver/bicep.go +++ b/pkg/recipes/driver/bicep.go @@ -30,8 +30,8 @@ import ( "golang.org/x/sync/errgroup" "oras.land/oras-go/v2/registry/remote" + "github.com/radius-project/radius/pkg/components/metrics" coredm "github.com/radius-project/radius/pkg/corerp/datamodel" - "github.com/radius-project/radius/pkg/metrics" "github.com/radius-project/radius/pkg/portableresources/datamodel" "github.com/radius-project/radius/pkg/portableresources/processors" "github.com/radius-project/radius/pkg/recipes" diff --git a/pkg/recipes/engine/engine.go b/pkg/recipes/engine/engine.go index 0a878b8af9..e981ed54e8 100644 --- a/pkg/recipes/engine/engine.go +++ b/pkg/recipes/engine/engine.go @@ -21,7 +21,7 @@ import ( "fmt" "time" - "github.com/radius-project/radius/pkg/metrics" + "github.com/radius-project/radius/pkg/components/metrics" "github.com/radius-project/radius/pkg/recipes" "github.com/radius-project/radius/pkg/recipes/configloader" recipedriver "github.com/radius-project/radius/pkg/recipes/driver" diff --git a/pkg/recipes/terraform/execute.go b/pkg/recipes/terraform/execute.go index 464e9b512d..623cd827af 100644 --- a/pkg/recipes/terraform/execute.go +++ b/pkg/recipes/terraform/execute.go @@ -27,8 +27,8 @@ import ( install "github.com/hashicorp/hc-install" "github.com/hashicorp/terraform-exec/tfexec" tfjson "github.com/hashicorp/terraform-json" + "github.com/radius-project/radius/pkg/components/metrics" "github.com/radius-project/radius/pkg/components/secret/secretprovider" - "github.com/radius-project/radius/pkg/metrics" "github.com/radius-project/radius/pkg/recipes/recipecontext" "github.com/radius-project/radius/pkg/recipes/terraform/config" "github.com/radius-project/radius/pkg/recipes/terraform/config/backends" diff --git a/pkg/recipes/terraform/install.go b/pkg/recipes/terraform/install.go index d318e3d90f..ffe8e9cbda 100644 --- a/pkg/recipes/terraform/install.go +++ b/pkg/recipes/terraform/install.go @@ -28,7 +28,7 @@ import ( "github.com/hashicorp/hc-install/releases" "github.com/hashicorp/hc-install/src" "github.com/hashicorp/terraform-exec/tfexec" - "github.com/radius-project/radius/pkg/metrics" + "github.com/radius-project/radius/pkg/components/metrics" "github.com/radius-project/radius/pkg/ucp/ucplog" "go.opentelemetry.io/otel/attribute" ) diff --git a/pkg/recipes/terraform/module.go b/pkg/recipes/terraform/module.go index 0df9b950f8..f25ff731d3 100644 --- a/pkg/recipes/terraform/module.go +++ b/pkg/recipes/terraform/module.go @@ -26,7 +26,7 @@ import ( getter "github.com/hashicorp/go-getter" "github.com/hashicorp/terraform-config-inspect/tfconfig" "github.com/hashicorp/terraform-exec/tfexec" - "github.com/radius-project/radius/pkg/metrics" + "github.com/radius-project/radius/pkg/components/metrics" "github.com/radius-project/radius/pkg/recipes" "github.com/radius-project/radius/pkg/recipes/recipecontext" "github.com/radius-project/radius/pkg/recipes/terraform/config" diff --git a/pkg/ucp/config.go b/pkg/ucp/config.go index 7ef4a8ff56..b4f248a6b6 100644 --- a/pkg/ucp/config.go +++ b/pkg/ucp/config.go @@ -21,11 +21,11 @@ import ( "github.com/radius-project/radius/pkg/armrpc/hostoptions" "github.com/radius-project/radius/pkg/components/database/databaseprovider" + "github.com/radius-project/radius/pkg/components/metrics/metricsservice" + "github.com/radius-project/radius/pkg/components/profiler/profilerservice" "github.com/radius-project/radius/pkg/components/queue/queueprovider" "github.com/radius-project/radius/pkg/components/secret/secretprovider" - metricsprovider "github.com/radius-project/radius/pkg/metrics/provider" - profilerprovider "github.com/radius-project/radius/pkg/profiler/provider" - "github.com/radius-project/radius/pkg/trace" + "github.com/radius-project/radius/pkg/components/trace/traceservice" ucpconfig "github.com/radius-project/radius/pkg/ucp/config" "github.com/radius-project/radius/pkg/ucp/ucplog" "gopkg.in/yaml.v3" @@ -51,10 +51,10 @@ type Config struct { Logging ucplog.LoggingOptions `yaml:"logging"` // Metrics is the configuration for the metrics endpoint. - Metrics metricsprovider.MetricsProviderOptions `yaml:"metricsProvider"` + Metrics metricsservice.Options `yaml:"metricsProvider"` // Profiler is the configuration for the profiler endpoint. - Profiler profilerprovider.ProfilerProviderOptions `yaml:"profilerProvider"` + Profiler profilerservice.Options `yaml:"profilerProvider"` // Routing is the configuration for UCP routing. Routing RoutingConfig `yaml:"routing"` @@ -69,7 +69,7 @@ type Config struct { Server hostoptions.ServerOptions `yaml:"server"` // Tracing is the configuration for the tracing system. - Tracing trace.Options `yaml:"tracerProvider"` + Tracing traceservice.Options `yaml:"tracerProvider"` // UCPConfig is the configuration for the connection to UCP. UCP ucpconfig.UCPOptions `yaml:"ucp"` diff --git a/pkg/ucp/frontend/api/server.go b/pkg/ucp/frontend/api/server.go index 3dc47ad9b3..581f8c77e5 100644 --- a/pkg/ucp/frontend/api/server.go +++ b/pkg/ucp/frontend/api/server.go @@ -29,6 +29,7 @@ import ( armrpc_controller "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/frontend/defaultoperation" "github.com/radius-project/radius/pkg/armrpc/servicecontext" + "github.com/radius-project/radius/pkg/components/hosting" "github.com/radius-project/radius/pkg/middleware" "github.com/radius-project/radius/pkg/ucp" "github.com/radius-project/radius/pkg/ucp/datamodel" @@ -38,7 +39,6 @@ import ( "github.com/radius-project/radius/pkg/ucp/frontend/modules" radius_frontend "github.com/radius-project/radius/pkg/ucp/frontend/radius" "github.com/radius-project/radius/pkg/ucp/frontend/versions" - "github.com/radius-project/radius/pkg/ucp/hosting" "github.com/radius-project/radius/pkg/ucp/resources" "github.com/radius-project/radius/pkg/ucp/ucplog" diff --git a/pkg/ucp/server/server.go b/pkg/ucp/server/server.go index b7d3edc03f..04871a8389 100644 --- a/pkg/ucp/server/server.go +++ b/pkg/ucp/server/server.go @@ -17,39 +17,35 @@ limitations under the License. package server import ( - metricsservice "github.com/radius-project/radius/pkg/metrics/service" - profilerservice "github.com/radius-project/radius/pkg/profiler/service" - "github.com/radius-project/radius/pkg/trace" + "github.com/radius-project/radius/pkg/components/hosting" + "github.com/radius-project/radius/pkg/components/metrics/metricsservice" + "github.com/radius-project/radius/pkg/components/profiler/profilerservice" + "github.com/radius-project/radius/pkg/components/trace/traceservice" "github.com/radius-project/radius/pkg/ucp" "github.com/radius-project/radius/pkg/ucp/backend" "github.com/radius-project/radius/pkg/ucp/frontend/api" - "github.com/radius-project/radius/pkg/ucp/hosting" ) // NewServer initializes a host for UCP based on the provided options. func NewServer(options *ucp.Options) (*hosting.Host, error) { - hostingServices := []hosting.Service{ + services := []hosting.Service{ api.NewService(options), backend.NewService(options), } - if options.Config.Metrics.Prometheus.Enabled { - metricOptions := metricsservice.HostOptions{ - Config: &options.Config.Metrics, - } - hostingServices = append(hostingServices, metricsservice.NewService(metricOptions)) + if options.Config.Metrics.Enabled { + services = append(services, &metricsservice.Service{Options: &options.Config.Metrics}) } if options.Config.Profiler.Enabled { - profilerOptions := profilerservice.HostOptions{ - Config: &options.Config.Profiler, - } - hostingServices = append(hostingServices, profilerservice.NewService(profilerOptions)) + services = append(services, &profilerservice.Service{Options: &options.Config.Profiler}) } - hostingServices = append(hostingServices, &trace.Service{Options: options.Config.Tracing}) + if options.Config.Tracing.Enabled { + services = append(services, &traceservice.Service{Options: &options.Config.Tracing}) + } return &hosting.Host{ - Services: hostingServices, + Services: services, }, nil } From 495289044fa769e2c8e0270c2b5063a46c4e5cd4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 Dec 2024 13:35:33 -0800 Subject: [PATCH 18/37] Bump the all group with 2 updates (#8175) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the all group with 2 updates: [github.com/aws/aws-sdk-go-v2/service/ecr](https://github.com/aws/aws-sdk-go-v2) and [github.com/go-git/go-git/v5](https://github.com/go-git/go-git). Updates `github.com/aws/aws-sdk-go-v2/service/ecr` from 1.36.8 to 1.38.0
Changelog

Sourced from github.com/aws/aws-sdk-go-v2/service/ecr's changelog.

Release (2023-07-31)

General Highlights

  • Feature: Adds support for smithy-modeled endpoint resolution. A new rules-based endpoint resolution will be added to the SDK which will supercede and deprecate existing endpoint resolution. Specifically, EndpointResolver will be deprecated while BaseEndpoint and EndpointResolverV2 will take its place. For more information, please see the Endpoints section in our Developer Guide.
  • Dependency Update: Updated to the latest SDK module versions

Module Highlights

  • github.com/aws/aws-sdk-go-v2/service/amplifyuibuilder: v1.12.0
    • Feature: Amplify Studio releases GraphQL support for codegen job action.
  • github.com/aws/aws-sdk-go-v2/service/autoscaling: v1.30.0
    • Feature: You can now configure an instance refresh to set its status to 'failed' when it detects that a specified CloudWatch alarm has gone into the ALARM state. You can also choose to roll back the instance refresh automatically when the alarm threshold is met.
  • github.com/aws/aws-sdk-go-v2/service/cleanrooms: v1.3.0
    • Feature: This release introduces custom SQL queries - an expanded set of SQL you can run. This release adds analysis templates, a new resource for storing pre-defined custom SQL queries ahead of time. This release also adds the Custom analysis rule, which lets you approve analysis templates for querying.
  • github.com/aws/aws-sdk-go-v2/service/codestarconnections: v1.15.0
    • Feature: New integration with the Gitlab provider type.
  • github.com/aws/aws-sdk-go-v2/service/drs: v1.15.0
    • Feature: Add support for in-aws right sizing
  • github.com/aws/aws-sdk-go-v2/service/inspector2: v1.16.0
    • Feature: This release adds 1 new API: BatchGetFindingDetails to retrieve enhanced vulnerability intelligence details for findings.
  • github.com/aws/aws-sdk-go-v2/service/lookoutequipment: v1.18.0
    • Feature: This release includes new import resource, model versioning and resource policy features.
  • github.com/aws/aws-sdk-go-v2/service/omics: v1.6.0
    • Feature: Add CreationType filter for ListReadSets
  • github.com/aws/aws-sdk-go-v2/service/rds: v1.49.0
    • Feature: This release adds support for Aurora MySQL local write forwarding, which allows for forwarding of write operations from reader DB instances to the writer DB instance.
  • github.com/aws/aws-sdk-go-v2/service/route53: v1.29.0
    • Feature: Amazon Route 53 now supports the Israel (Tel Aviv) Region (il-central-1) for latency records, geoproximity records, and private DNS for Amazon VPCs in that region.
  • github.com/aws/aws-sdk-go-v2/service/scheduler: v1.2.0
    • Feature: This release introduces automatic deletion of schedules in EventBridge Scheduler. If configured, EventBridge Scheduler automatically deletes a schedule after the schedule has completed its last invocation.

Release (2023-07-28.2)

Module Highlights

  • github.com/aws/aws-sdk-go-v2/service/applicationinsights: v1.18.0
    • Feature: This release enable customer to add/remove/update more than one workload for a component
  • github.com/aws/aws-sdk-go-v2/service/cloudformation: v1.33.0
    • Feature: This SDK release is for the feature launch of AWS CloudFormation RetainExceptOnCreate. It adds a new parameter retainExceptOnCreate in the following APIs: CreateStack, UpdateStack, RollbackStack, ExecuteChangeSet.
  • github.com/aws/aws-sdk-go-v2/service/cloudfront: v1.27.0
    • Feature: Add a new JavaScript runtime version for CloudFront Functions.
  • github.com/aws/aws-sdk-go-v2/service/connect: v1.62.0
    • Feature: This release adds support for new number types.
  • github.com/aws/aws-sdk-go-v2/service/kafka: v1.21.0
    • Feature: Amazon MSK has introduced new versions of ListClusterOperations and DescribeClusterOperation APIs. These v2 APIs provide information and insights into the ongoing operations of both MSK Provisioned and MSK Serverless clusters.
  • github.com/aws/aws-sdk-go-v2/service/pinpoint: v1.21.0
    • Feature: Added support for sending push notifications using the FCM v1 API with json credentials. Amazon Pinpoint customers can now deliver messages to Android devices using both FCM v1 API and the legacy FCM/GCM API

Release (2023-07-28)

General Highlights

  • Dependency Update: Updated to the latest SDK module versions

... (truncated)

Commits

Updates `github.com/go-git/go-git/v5` from 5.12.0 to 5.13.0
Release notes

Sourced from github.com/go-git/go-git/v5's releases.

v5.13.0

What's Changed

... (truncated)

Commits
  • 94bd4af Merge pull request #1261 from BeChris/issue680
  • 8b7f5ba Merge pull request #1262 from go-git/dependabot/go_modules/github.com/elazarl...
  • 41d80a0 build: bump github.com/elazarl/goproxy
  • 4998140 git: worktree_commit, sanitize author and commiter name and email before crea...
  • 9049625 Merge pull request #1260 from go-git/dependabot/github_actions/github/codeql-...
  • dae48b4 build: bump github/codeql-action from 3.27.9 to 3.28.0
  • 7d6fbc2 Merge pull request #1220 from BeChris/accept_uppercase_hexa_in_pktline_length
  • 62a77b7 plumbing: Fix invalid reference name error while cloning branches containing ...
  • 5e11196 plumbing: format/pktline, accept upercase hexadecimal value as pktline length...
  • 65f5e1a Merge pull request #1256 from go-git/dependabot/go_modules/golang-org-232a611e2d
  • Additional commits viewable in compare view

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 12 ++++++------ go.sum | 32 ++++++++++++++++---------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/go.mod b/go.mod index 9c5b2d3dd2..b8fddc62c4 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/cloudcontrol v1.23.3 github.com/aws/aws-sdk-go-v2/service/cloudformation v1.56.2 github.com/aws/aws-sdk-go-v2/service/ec2 v1.198.1 - github.com/aws/aws-sdk-go-v2/service/ecr v1.36.8 + github.com/aws/aws-sdk-go-v2/service/ecr v1.38.0 github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 github.com/aws/smithy-go v1.22.1 github.com/charmbracelet/bubbles v0.20.0 @@ -32,7 +32,7 @@ require ( github.com/dimchansky/utfbom v1.1.1 github.com/fatih/color v1.18.0 github.com/go-chi/chi/v5 v5.2.0 - github.com/go-git/go-git/v5 v5.12.0 + github.com/go-git/go-git/v5 v5.13.0 github.com/go-logr/logr v1.4.2 github.com/go-logr/zapr v1.3.0 github.com/go-openapi/errors v0.22.0 @@ -119,7 +119,7 @@ require ( github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect - github.com/go-git/go-billy/v5 v5.5.0 // indirect + github.com/go-git/go-billy/v5 v5.6.0 // indirect github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/gnostic-models v0.6.8 // indirect @@ -139,7 +139,7 @@ require ( github.com/sagikazarmark/locafero v0.6.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect - github.com/skeema/knownhosts v1.2.2 // indirect + github.com/skeema/knownhosts v1.3.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/tidwall/gjson v1.18.0 // indirect github.com/tidwall/match v1.1.1 // indirect @@ -169,7 +169,7 @@ require ( github.com/Masterminds/semver/v3 v3.3.0 // indirect github.com/Masterminds/sprig/v3 v3.3.0 // indirect github.com/Masterminds/squirrel v1.5.4 // indirect - github.com/ProtonMail/go-crypto v1.1.0-alpha.2-proton // indirect + github.com/ProtonMail/go-crypto v1.1.3 // indirect github.com/agext/levenshtein v1.2.3 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/atotto/clipboard v0.1.4 // indirect @@ -289,7 +289,7 @@ require ( golang.org/x/crypto v0.31.0 // indirect golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 golang.org/x/mod v0.21.0 // indirect - golang.org/x/net v0.32.0 // indirect + golang.org/x/net v0.33.0 // indirect golang.org/x/oauth2 v0.24.0 // indirect golang.org/x/sys v0.28.0 // indirect golang.org/x/term v0.27.0 // indirect diff --git a/go.sum b/go.sum index 24ae1735d5..cf2283073b 100644 --- a/go.sum +++ b/go.sum @@ -255,8 +255,8 @@ github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA github.com/Microsoft/hcsshim v0.12.4 h1:Ev7YUMHAHoWNm+aDSPzc5W9s6E2jyL1szpVDJeZ/Rr4= github.com/Microsoft/hcsshim v0.12.4/go.mod h1:Iyl1WVpZzr+UkzjekHZbV8o5Z9ZkxNGx6CtY2Qg/JVQ= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/ProtonMail/go-crypto v1.1.0-alpha.2-proton h1:HKz85FwoXx86kVtTvFke7rgHvq/HoloSUvW5semjFWs= -github.com/ProtonMail/go-crypto v1.1.0-alpha.2-proton/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= +github.com/ProtonMail/go-crypto v1.1.3 h1:nRBOetoydLeUb4nHajyO2bKqMLfWQ/ZPwkXqXxPxCFk= +github.com/ProtonMail/go-crypto v1.1.3/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= @@ -303,8 +303,8 @@ github.com/aws/aws-sdk-go-v2/service/cloudformation v1.56.2 h1:6USen+lDo8xYQutfn github.com/aws/aws-sdk-go-v2/service/cloudformation v1.56.2/go.mod h1:10A7sHyxlTZSB7419K2wq/1tn0x/K9/drbD2j8VRZVc= github.com/aws/aws-sdk-go-v2/service/ec2 v1.198.1 h1:YbNopxjd9baM83YEEmkaYHi+NuJt0AszeaSLqo0CVr0= github.com/aws/aws-sdk-go-v2/service/ec2 v1.198.1/go.mod h1:mwr3iRm8u1+kkEx4ftDM2Q6Yr0XQFBKrP036ng+k5Lk= -github.com/aws/aws-sdk-go-v2/service/ecr v1.36.8 h1:cPdeSR2y0BDAr2S054U4ERlJ5mM1OWYazW7Jm/o+b1o= -github.com/aws/aws-sdk-go-v2/service/ecr v1.36.8/go.mod h1:NqKnlZvLl4Tp2UH/GEc/nhbjmPQhwOXmLp2eldiszLM= +github.com/aws/aws-sdk-go-v2/service/ecr v1.38.0 h1:+1IqznlfeMCgFWoWAuwRqykVc6gGoUUQFGXai+77KWs= +github.com/aws/aws-sdk-go-v2/service/ecr v1.38.0/go.mod h1:NqKnlZvLl4Tp2UH/GEc/nhbjmPQhwOXmLp2eldiszLM= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 h1:8eUsivBQzZHqe/3FE+cqwfH+0p5Jo8PFM/QYQSmeZ+M= @@ -423,8 +423,8 @@ github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQ github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= -github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= -github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= +github.com/elazarl/goproxy v1.2.1 h1:njjgvO6cRG9rIqN2ebkqy6cQz2Njkx7Fsfv/zIZqgug= +github.com/elazarl/goproxy v1.2.1/go.mod h1:YfEbZtqP4AetfO6d40vWchF3znWX7C7Vd6ZMfdL8z64= github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU= github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= @@ -463,20 +463,20 @@ github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXE github.com/gabriel-vasile/mimetype v1.4.4 h1:QjV6pZ7/XZ7ryI2KuyeEDE8wnh7fHP9YnQy+R0LnH8I= github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSeEuJfJE+TtfvdB/s= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE= -github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8= +github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= +github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= github.com/go-chi/chi/v5 v5.2.0 h1:Aj1EtB0qR2Rdo2dG4O94RIU35w2lvQSj6BRA4+qwFL0= github.com/go-chi/chi/v5 v5.2.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk= github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= -github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= -github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= +github.com/go-git/go-billy/v5 v5.6.0 h1:w2hPNtoehvJIxR00Vb4xX94qHQi/ApZfX+nBE2Cjio8= +github.com/go-git/go-billy/v5 v5.6.0/go.mod h1:sFDq7xD3fn3E0GOwUSZqHo9lrkmx8xJhA0ZrfvjBRGM= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= -github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys= -github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY= +github.com/go-git/go-git/v5 v5.13.0 h1:vLn5wlGIh/X78El6r3Jr+30W16Blk0CTcxTYcYPWi5E= +github.com/go-git/go-git/v5 v5.13.0/go.mod h1:Wjo7/JyVKtQgUNdXYXIepzWfJQkUEIGvkvVkiXRR/zw= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -905,8 +905,8 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A= -github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= +github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY= +github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -1131,8 +1131,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= -golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= From 710509a3afa52bffd463d5553d4c2032729d0326 Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Tue, 31 Dec 2024 14:53:11 -0800 Subject: [PATCH 19/37] Implement resource provider API for dynamic rp (#8177) # Description This change implements the standard resource provider API for dynamic resources. This enables the dynamic-rp to serve the standard CRUDL API functionality for user-defined types. This change does NOT add recipe support, that will come in the next change. The pattern that's supported by this change is an "inert" resource which has no backend functionality. Recipe support will be a superset of this functionality. ## Type of change Part of: #6688 ## Contributor checklist Please verify that the PR meets the following requirements, where applicable: - [ ] An overview of proposed schema changes is included in a linked GitHub issue. - [ ] A design document PR is created in the [design-notes repository](https://github.com/radius-project/design-notes/), if new APIs are being introduced. - [ ] If applicable, design document has been reviewed and approved by Radius maintainers/approvers. - [ ] A PR for the [samples repository](https://github.com/radius-project/samples) is created, if existing samples are affected by the changes in this PR. - [ ] A PR for the [documentation repository](https://github.com/radius-project/docs) is created, if the changes in this PR affect the documentation or any user facing updates are made. - [ ] A PR for the [recipes repository](https://github.com/radius-project/recipes) is created, if existing recipes are affected by the changes in this PR. Signed-off-by: Ryan Nowak --- .github/workflows/lint.yaml | 1 + build/prettier.mk | 12 +- pkg/dynamicrp/api/dynamicresource.go | 38 ++++++ .../api/dynamicresource_conversion.go | 122 +++++++++++++++++ .../api/dynamicresource_conversion_test.go | 126 ++++++++++++++++++ .../testdata/dynamicresource-datamodel.json | 20 +++ .../testdata/dynamicresource-resource.json | 12 ++ .../backend/dynamicresource_controller.go | 80 +++++++++++ .../dynamicresource_controller_test.go | 73 ++++++++++ .../backend/inert_delete_controller.go | 45 +++++++ .../backend/inert_delete_controller_test.go | 55 ++++++++ pkg/dynamicrp/backend/inert_put_controller.go | 40 ++++++ .../backend/inert_put_controller_test.go | 41 ++++++ pkg/dynamicrp/backend/service.go | 13 +- .../converter/dynamicresource_converter.go | 52 ++++++++ pkg/dynamicrp/datamodel/dynamicresource.go | 34 +++++ pkg/dynamicrp/frontend/routes.go | 58 +++++++- .../integrationtest/dynamic/providers_test.go | 61 ++++++++- 18 files changed, 864 insertions(+), 19 deletions(-) create mode 100644 pkg/dynamicrp/api/dynamicresource.go create mode 100644 pkg/dynamicrp/api/dynamicresource_conversion.go create mode 100644 pkg/dynamicrp/api/dynamicresource_conversion_test.go create mode 100644 pkg/dynamicrp/api/testdata/dynamicresource-datamodel.json create mode 100644 pkg/dynamicrp/api/testdata/dynamicresource-resource.json create mode 100644 pkg/dynamicrp/backend/dynamicresource_controller.go create mode 100644 pkg/dynamicrp/backend/dynamicresource_controller_test.go create mode 100644 pkg/dynamicrp/backend/inert_delete_controller.go create mode 100644 pkg/dynamicrp/backend/inert_delete_controller_test.go create mode 100644 pkg/dynamicrp/backend/inert_put_controller.go create mode 100644 pkg/dynamicrp/backend/inert_put_controller_test.go create mode 100644 pkg/dynamicrp/datamodel/converter/dynamicresource_converter.go create mode 100644 pkg/dynamicrp/datamodel/dynamicresource.go diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 93cc92fcdd..685ec57e1f 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -104,6 +104,7 @@ jobs: node-version: "22" - name: Run `make format-check` id: format-check + continue-on-error: true run: | make format-check - name: Check for formatting failures diff --git a/build/prettier.mk b/build/prettier.mk index 5425a4508b..f680af93f9 100644 --- a/build/prettier.mk +++ b/build/prettier.mk @@ -14,16 +14,18 @@ # limitations under the License. # ------------------------------------------------------------ -.PHONY: prettier-check prettier-format me prettier +##@ Formatting (of JSON files) PRETTIER_VERSION := 3.3.3 -format-check: +.PHONY: format-check +format-check: ## Checks the formatting of JSON files. @echo "$(ARROW) Checking for formatting issues using prettier..." @echo "" - @npx prettier@$(PRETTIER_VERSION) --check "*/**/*.{ts,js,mjs,json}" + @npx --yes prettier@$(PRETTIER_VERSION) --check "*/**/*.{ts,js,mjs,json}" -format-write: +.PHONY: format-write +format-write: ## Updates the formatting of JSON files. @echo "$(ARROW) Reformatting files using prettier..." @echo "" - @npx prettier@$(PRETTIER_VERSION) --write "*/**/*.{ts,js,mjs,json}" + @npx --yes prettier@$(PRETTIER_VERSION) --write "*/**/*.{ts,js,mjs,json}" diff --git a/pkg/dynamicrp/api/dynamicresource.go b/pkg/dynamicrp/api/dynamicresource.go new file mode 100644 index 0000000000..35a95941c9 --- /dev/null +++ b/pkg/dynamicrp/api/dynamicresource.go @@ -0,0 +1,38 @@ +/* +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 api + +// DynamicResource is used as the versioned resource model for dynamic resources. +// +// A dynamic resource uses a user-provided OpenAPI specification to define the resource schema. Therefore, +// the properties of the resource are not known at compile time. +type DynamicResource struct { + // ID is the resource ID. + ID *string `json:"id"` + // Name is the resource name. + Name *string `json:"name"` + // Type is the resource type. + Type *string `json:"type"` + // Location is the resource location. + Location *string `json:"location"` + // Tags are the resource tags. + Tags map[string]*string `json:"tags,omitempty"` + // Properties stores the properties of the resource. + Properties map[string]any `json:"properties,omitempty"` + // SystemData stores the system data of the resource. + SystemData map[string]any `json:"systemData,omitempty"` +} diff --git a/pkg/dynamicrp/api/dynamicresource_conversion.go b/pkg/dynamicrp/api/dynamicresource_conversion.go new file mode 100644 index 0000000000..1934e3c041 --- /dev/null +++ b/pkg/dynamicrp/api/dynamicresource_conversion.go @@ -0,0 +1,122 @@ +/* +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 api + +import ( + "encoding/json" + "fmt" + + v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/dynamicrp/datamodel" + "github.com/radius-project/radius/pkg/to" +) + +const ( + // TODO + Version = "2023-01-01" +) + +// ConvertTo converts the versioned model to the datamodel. +func (d *DynamicResource) ConvertTo() (v1.DataModelInterface, error) { + // Note: we always round-trip the properties through JSON to ensure that the conversion is possible, and + // to make a defensive copy of the data. + bs, err := json.Marshal(d.Properties) + if err != nil { + return nil, fmt.Errorf("failed to marshal properties: %w", err) + } + + properties := map[string]any{} + err = json.Unmarshal(bs, &properties) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal properties: %w", err) + } + + dm := &datamodel.DynamicResource{ + BaseResource: v1.BaseResource{ + TrackedResource: v1.TrackedResource{ + ID: to.String(d.ID), + Name: to.String(d.Name), + Type: to.String(d.Type), + Location: to.String(d.Location), + Tags: to.StringMap(d.Tags), + }, + InternalMetadata: v1.InternalMetadata{ + UpdatedAPIVersion: Version, + }, + }, + Properties: properties, + } + + return dm, nil +} + +// ConvertFrom converts the datamodel to the versioned model. +func (d *DynamicResource) ConvertFrom(src v1.DataModelInterface) error { + dm, ok := src.(*datamodel.DynamicResource) + if !ok { + return v1.ErrInvalidModelConversion + } + + // Note: we always round-trip the properties through JSON to ensure that the conversion is possible, and + // to make a defensive copy of the data. + bs, err := json.Marshal(dm.Properties) + if err != nil { + return fmt.Errorf("failed to marshal properties: %w", err) + } + + properties := map[string]any{} + err = json.Unmarshal(bs, &properties) + if err != nil { + return fmt.Errorf("failed to unmarshal properties: %w", err) + } + + d.ID = &dm.ID + d.Name = &dm.Name + d.Type = &dm.Type + d.Location = &dm.Location + d.Tags = *to.StringMapPtr(dm.Tags) + d.SystemData = fromSystemDataDataModel(dm.SystemData) + d.Properties = properties + d.Properties["provisioningState"] = fromProvisioningStateDataModel(dm.AsyncProvisioningState) + + return nil +} + +func fromSystemDataDataModel(input v1.SystemData) map[string]any { + bs, err := json.Marshal(input) + if err != nil { + // This should never fail. We've designed the SystemData type to be serializable. + panic("marshalling system data failed: " + err.Error()) + } + + result := map[string]any{} + err = json.Unmarshal(bs, &result) + if err != nil { + // This should never fail. We've designed the SystemData type to be serializable. + panic("unmarshalling system data failed: " + err.Error()) + } + + return result +} + +func fromProvisioningStateDataModel(input v1.ProvisioningState) string { + if input == "" { + return string(v1.ProvisioningStateSucceeded) + } + + return string(input) +} diff --git a/pkg/dynamicrp/api/dynamicresource_conversion_test.go b/pkg/dynamicrp/api/dynamicresource_conversion_test.go new file mode 100644 index 0000000000..936448b6a1 --- /dev/null +++ b/pkg/dynamicrp/api/dynamicresource_conversion_test.go @@ -0,0 +1,126 @@ +/* +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 api + +import ( + "encoding/json" + "testing" + + v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/dynamicrp/datamodel" + "github.com/radius-project/radius/pkg/to" + "github.com/radius-project/radius/test/testutil" + + "github.com/stretchr/testify/require" +) + +func Test_DynamicResource_ConvertVersionedToDataModel(t *testing.T) { + conversionTests := []struct { + filename string + expected *datamodel.DynamicResource + err error + }{ + { + filename: "dynamicresource-resource.json", + expected: &datamodel.DynamicResource{ + BaseResource: v1.BaseResource{ + TrackedResource: v1.TrackedResource{ + ID: "/planes/radius/local/resourceGroups/test/providers/Applications.Test/testResources/testResource", + Name: "testResource", + Type: "Applications.Test/testResources", + Location: "global", + Tags: map[string]string{ + "env": "dev", + }, + }, + InternalMetadata: v1.InternalMetadata{ + UpdatedAPIVersion: Version, + }, + }, + Properties: map[string]any{ + "message": "Hello, world!", + }, + }, + }, + } + + for _, tt := range conversionTests { + t.Run(tt.filename, func(t *testing.T) { + rawPayload := testutil.ReadFixture(tt.filename) + r := &DynamicResource{} + err := json.Unmarshal(rawPayload, r) + require.NoError(t, err) + + dm, err := r.ConvertTo() + + if tt.err != nil { + require.ErrorIs(t, err, tt.err) + } else { + require.NoError(t, err) + ct := dm.(*datamodel.DynamicResource) + require.Equal(t, tt.expected, ct) + } + }) + } +} + +func Test_DynamicResource_ConvertDataModelToVersioned(t *testing.T) { + conversionTests := []struct { + filename string + expected *DynamicResource + err error + }{ + { + filename: "dynamicresource-datamodel.json", + expected: &DynamicResource{ + ID: to.Ptr("/planes/radius/local/resourceGroups/test/providers/Applications.Test/testResources/testResource"), + Name: to.Ptr("testResource"), + Type: to.Ptr("Applications.Test/testResources"), + Location: to.Ptr("global"), + Tags: map[string]*string{ + "env": to.Ptr("dev"), + }, + Properties: map[string]any{ + "provisioningState": fromProvisioningStateDataModel(v1.ProvisioningStateSucceeded), + "message": "Hello, world!", + }, + }, + }, + } + + for _, tt := range conversionTests { + t.Run(tt.filename, func(t *testing.T) { + rawPayload := testutil.ReadFixture(tt.filename) + dm := &datamodel.DynamicResource{} + err := json.Unmarshal(rawPayload, dm) + require.NoError(t, err) + + resource := &DynamicResource{} + err = resource.ConvertFrom(dm) + + // Avoid hardcoding the SystemData field in tests. + tt.expected.SystemData = fromSystemDataDataModel(dm.SystemData) + + if tt.err != nil { + require.ErrorIs(t, err, tt.err) + } else { + require.NoError(t, err) + require.Equal(t, tt.expected, resource) + } + }) + } +} diff --git a/pkg/dynamicrp/api/testdata/dynamicresource-datamodel.json b/pkg/dynamicrp/api/testdata/dynamicresource-datamodel.json new file mode 100644 index 0000000000..0b5c40958f --- /dev/null +++ b/pkg/dynamicrp/api/testdata/dynamicresource-datamodel.json @@ -0,0 +1,20 @@ +{ + "id": "/planes/radius/local/resourceGroups/test/providers/Applications.Test/testResources/testResource", + "name": "testResource", + "type": "Applications.Test/testResources", + "location": "global", + "systemData": { + "createdBy": "fakeid@live.com", + "createdByType": "User", + "createdAt": "2021-09-24T19:09:54.2403864Z", + "lastModifiedBy": "fakeid@live.com", + "lastModifiedByType": "User", + "lastModifiedAt": "2021-09-24T20:09:54.2403864Z" + }, + "tags": { + "env": "dev" + }, + "properties": { + "message": "Hello, world!" + } +} diff --git a/pkg/dynamicrp/api/testdata/dynamicresource-resource.json b/pkg/dynamicrp/api/testdata/dynamicresource-resource.json new file mode 100644 index 0000000000..2d544a69b9 --- /dev/null +++ b/pkg/dynamicrp/api/testdata/dynamicresource-resource.json @@ -0,0 +1,12 @@ +{ + "id": "/planes/radius/local/resourceGroups/test/providers/Applications.Test/testResources/testResource", + "name": "testResource", + "type": "Applications.Test/testResources", + "location": "global", + "tags": { + "env": "dev" + }, + "properties": { + "message": "Hello, world!" + } +} diff --git a/pkg/dynamicrp/backend/dynamicresource_controller.go b/pkg/dynamicrp/backend/dynamicresource_controller.go new file mode 100644 index 0000000000..0563d4dc6b --- /dev/null +++ b/pkg/dynamicrp/backend/dynamicresource_controller.go @@ -0,0 +1,80 @@ +/* +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 backend + +import ( + "context" + "fmt" + + v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" + "github.com/radius-project/radius/pkg/ucp/resources" +) + +// DynamicResourceController is the async operation controller to perform processing on dynamic resources. +// +// This controller will use the capabilities and the operation to determine the correct controller to use. +type DynamicResourceController struct { + ctrl.BaseController +} + +// NewDynamicResourceController creates a new DynamicResourcePutController. +func NewDynamicResourceController(opts ctrl.Options) (ctrl.Controller, error) { + return &DynamicResourceController{ + BaseController: ctrl.NewBaseAsyncController(opts), + }, nil +} + +// Run implements the async controller interface. +func (c *DynamicResourceController) Run(ctx context.Context, request *ctrl.Request) (ctrl.Result, error) { + // This is where we have the opportunity to branch out to different controllers based on: + // - The operation type. (eg: PUT, DELETE, etc) + // - The capabilities of the resource type. (eg: Does it support recipes?) + controller, err := c.selectController(request) + if err != nil { + return ctrl.Result{}, fmt.Errorf("failed to create controller: %w", err) + } + + return controller.Run(ctx, request) + +} + +func (c *DynamicResourceController) selectController(request *ctrl.Request) (ctrl.Controller, error) { + ot, ok := v1.ParseOperationType(request.OperationType) + if !ok { + return nil, fmt.Errorf("invalid operation type: %q", request.OperationType) + } + + id, err := resources.ParseResource(request.ResourceID) + if err != nil { + return nil, fmt.Errorf("invalid resource ID: %q", request.ResourceID) + } + + options := ctrl.Options{ + DatabaseClient: c.DatabaseClient(), + ResourceType: id.Type(), + } + + switch ot.Method { + case v1.OperationDelete: + return NewInertDeleteController(options) + case v1.OperationPut: + return NewInertPutController(options) + default: + return nil, fmt.Errorf("unsupported operation type: %q", request.OperationType) + } +} diff --git a/pkg/dynamicrp/backend/dynamicresource_controller_test.go b/pkg/dynamicrp/backend/dynamicresource_controller_test.go new file mode 100644 index 0000000000..7726165746 --- /dev/null +++ b/pkg/dynamicrp/backend/dynamicresource_controller_test.go @@ -0,0 +1,73 @@ +/* +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 backend + +import ( + "testing" + + v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" + "github.com/stretchr/testify/require" +) + +func Test_DynamicResourceController_selectController(t *testing.T) { + setup := func() *DynamicResourceController { + opts := ctrl.Options{} + controller, err := NewDynamicResourceController(opts) + require.NoError(t, err) + return controller.(*DynamicResourceController) + } + + t.Run("inert PUT", func(t *testing.T) { + controller := setup() + request := &ctrl.Request{ + ResourceID: "/planes/radius/local/resourceGroups/test-group/providers/Applications.Test/testResources/test-resource", + OperationType: v1.OperationType{Type: "Applications.Test/testResources", Method: v1.OperationPut}.String(), + } + + selected, err := controller.selectController(request) + require.NoError(t, err) + + require.IsType(t, &InertPutController{}, selected) + }) + + t.Run("inert DELETE", func(t *testing.T) { + controller := setup() + request := &ctrl.Request{ + ResourceID: "/planes/radius/local/resourceGroups/test-group/providers/Applications.Test/testResources/test-resource", + OperationType: v1.OperationType{Type: "Applications.Test/testResources", Method: v1.OperationDelete}.String(), + } + + selected, err := controller.selectController(request) + require.NoError(t, err) + + require.IsType(t, &InertDeleteController{}, selected) + }) + + t.Run("unknown operation", func(t *testing.T) { + controller := setup() + request := &ctrl.Request{ + ResourceID: "/planes/radius/local/resourceGroups/test-group/providers/Applications.Test/testResources/test-resource", + OperationType: v1.OperationType{Type: "Applications.Test/testResources", Method: v1.OperationGet}.String(), + } + + selected, err := controller.selectController(request) + require.Error(t, err) + require.Equal(t, "unsupported operation type: \"APPLICATIONS.TEST/TESTRESOURCES|GET\"", err.Error()) + require.Nil(t, selected) + }) +} diff --git a/pkg/dynamicrp/backend/inert_delete_controller.go b/pkg/dynamicrp/backend/inert_delete_controller.go new file mode 100644 index 0000000000..16289b93af --- /dev/null +++ b/pkg/dynamicrp/backend/inert_delete_controller.go @@ -0,0 +1,45 @@ +/* +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 backend + +import ( + "context" + + ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" +) + +// InertDeleteController is the async operation controller to perform DELETE processing on "inert" dynamic resources. +type InertDeleteController struct { + ctrl.BaseController +} + +// NewInertDeleteController creates a new InertDeleteController. +func NewInertDeleteController(opts ctrl.Options) (ctrl.Controller, error) { + return &InertDeleteController{ + BaseController: ctrl.NewBaseAsyncController(opts), + }, nil +} + +// Run implements the async controller interface. +func (c *InertDeleteController) Run(ctx context.Context, request *ctrl.Request) (ctrl.Result, error) { + err := c.DatabaseClient().Delete(ctx, request.ResourceID) + if err != nil { + return ctrl.Result{}, err + } + + return ctrl.Result{}, nil +} diff --git a/pkg/dynamicrp/backend/inert_delete_controller_test.go b/pkg/dynamicrp/backend/inert_delete_controller_test.go new file mode 100644 index 0000000000..9d77ad3846 --- /dev/null +++ b/pkg/dynamicrp/backend/inert_delete_controller_test.go @@ -0,0 +1,55 @@ +/* +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 backend + +import ( + "testing" + + ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" + "github.com/radius-project/radius/pkg/components/database" + "github.com/radius-project/radius/test/testcontext" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" +) + +func Test_InertDeleteController_Run(t *testing.T) { + setup := func() (*InertDeleteController, *database.MockClient) { + mockctrl := gomock.NewController(t) + databaseClient := database.NewMockClient(mockctrl) + + opts := ctrl.Options{ + DatabaseClient: databaseClient, + } + + controller, err := NewInertDeleteController(opts) + require.NoError(t, err) + return controller.(*InertDeleteController), databaseClient + } + + controller, databaseClient := setup() + + request := &ctrl.Request{ + ResourceID: "/planes/radius/testing/resourceGroups/test-group/providers/Applications.Test/exampleResources/my-example", + } + + // Controller needs to call delete on the resource. + databaseClient.EXPECT().Delete(gomock.Any(), request.ResourceID).Return(nil).Times(1) + + result, err := controller.Run(testcontext.New(t), request) + require.NoError(t, err) + require.Equal(t, ctrl.Result{}, result) +} diff --git a/pkg/dynamicrp/backend/inert_put_controller.go b/pkg/dynamicrp/backend/inert_put_controller.go new file mode 100644 index 0000000000..2895cf72c5 --- /dev/null +++ b/pkg/dynamicrp/backend/inert_put_controller.go @@ -0,0 +1,40 @@ +/* +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 backend + +import ( + "context" + + ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" +) + +// InertPutController is the async operation controller to perform PUT processing on "inert" dynamic resources. +type InertPutController struct { + ctrl.BaseController +} + +// NewInertPutController creates a new InertPutController. +func NewInertPutController(opts ctrl.Options) (ctrl.Controller, error) { + return &InertPutController{ + BaseController: ctrl.NewBaseAsyncController(opts), + }, nil +} + +// Run implements the async controller interface. +func (c *InertPutController) Run(ctx context.Context, request *ctrl.Request) (ctrl.Result, error) { + return ctrl.Result{}, nil +} diff --git a/pkg/dynamicrp/backend/inert_put_controller_test.go b/pkg/dynamicrp/backend/inert_put_controller_test.go new file mode 100644 index 0000000000..5060aac096 --- /dev/null +++ b/pkg/dynamicrp/backend/inert_put_controller_test.go @@ -0,0 +1,41 @@ +/* +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 backend + +import ( + "testing" + + ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" + "github.com/radius-project/radius/test/testcontext" + "github.com/stretchr/testify/require" +) + +func Test_InertPutController_Run(t *testing.T) { + setup := func() *InertPutController { + opts := ctrl.Options{} + controller, err := NewInertPutController(opts) + require.NoError(t, err) + return controller.(*InertPutController) + } + + controller := setup() + + request := &ctrl.Request{} + result, err := controller.Run(testcontext.New(t), request) + require.NoError(t, err) + require.Equal(t, ctrl.Result{}, result) +} diff --git a/pkg/dynamicrp/backend/service.go b/pkg/dynamicrp/backend/service.go index 710a0fccf0..834f1aad7c 100644 --- a/pkg/dynamicrp/backend/service.go +++ b/pkg/dynamicrp/backend/service.go @@ -19,7 +19,9 @@ package backend import ( "context" + ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller" "github.com/radius-project/radius/pkg/armrpc/asyncoperation/worker" + "github.com/radius-project/radius/pkg/dynamicrp" "github.com/radius-project/radius/pkg/recipes/controllerconfig" ) @@ -70,7 +72,7 @@ func (w *Service) Run(ctx context.Context) error { w.Service.QueueClient = queueClient w.Service.OperationStatusManager = w.options.StatusManager - err = w.registerControllers(ctx) + err = w.registerControllers() if err != nil { return err } @@ -78,7 +80,10 @@ func (w *Service) Run(ctx context.Context) error { return w.Start(ctx) } -func (w *Service) registerControllers(ctx context.Context) error { - // No controllers yet. - return nil +func (w *Service) registerControllers() error { + options := ctrl.Options{ + DatabaseClient: w.Service.DatabaseClient, + } + + return w.Service.Controllers().RegisterDefault(NewDynamicResourceController, options) } diff --git a/pkg/dynamicrp/datamodel/converter/dynamicresource_converter.go b/pkg/dynamicrp/datamodel/converter/dynamicresource_converter.go new file mode 100644 index 0000000000..6483c5e136 --- /dev/null +++ b/pkg/dynamicrp/datamodel/converter/dynamicresource_converter.go @@ -0,0 +1,52 @@ +/* +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 converter + +import ( + "encoding/json" + + v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/dynamicrp/api" + "github.com/radius-project/radius/pkg/dynamicrp/datamodel" +) + +// DynamicResourceDataModelFromVersioned converts version agnostic datamodel to versioned model. +func DynamicResourceDataModelToVersioned(model *datamodel.DynamicResource, version string) (v1.VersionedModelInterface, error) { + // NOTE: DynamicResource is used for all API versions. + // + // We don't/can't validate the API version here, that must be done before calling the API. + versioned := &api.DynamicResource{} + if err := versioned.ConvertFrom(model); err != nil { + return nil, err + } + return versioned, nil +} + +// DynamicResourceDataModelFromVersioned converts versioned model to datamodel. +func DynamicResourceDataModelFromVersioned(content []byte, version string) (*datamodel.DynamicResource, error) { + // NOTE: DynamicResource is used for all API versions. + // + // We don't/can't validate the API version here, that must be done before calling the API. + vm := &api.DynamicResource{} + if err := json.Unmarshal(content, vm); err != nil { + return nil, err + } + dm, err := vm.ConvertTo() + if err != nil { + return nil, err + } + return dm.(*datamodel.DynamicResource), nil +} diff --git a/pkg/dynamicrp/datamodel/dynamicresource.go b/pkg/dynamicrp/datamodel/dynamicresource.go new file mode 100644 index 0000000000..6b33c2e72f --- /dev/null +++ b/pkg/dynamicrp/datamodel/dynamicresource.go @@ -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 datamodel + +import ( + v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" +) + +var _ v1.ResourceDataModel = (*DynamicResource)(nil) + +// DynamicResource is used as the data model for dynamic resources (UDT). +// +// A dynamic resource uses a user-provided OpenAPI specification to define the resource schema. Therefore, +// the properties of the resource are not known at compile time. +type DynamicResource struct { + v1.BaseResource + + // Properties stores the properties of the resource being tracked. + Properties map[string]any `json:"properties"` +} diff --git a/pkg/dynamicrp/frontend/routes.go b/pkg/dynamicrp/frontend/routes.go index ca18546e07..dd387b3934 100644 --- a/pkg/dynamicrp/frontend/routes.go +++ b/pkg/dynamicrp/frontend/routes.go @@ -18,11 +18,14 @@ package frontend import ( "strings" + "time" "github.com/go-chi/chi/v5" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" "github.com/radius-project/radius/pkg/armrpc/frontend/controller" "github.com/radius-project/radius/pkg/armrpc/frontend/defaultoperation" + "github.com/radius-project/radius/pkg/dynamicrp/datamodel" + "github.com/radius-project/radius/pkg/dynamicrp/datamodel/converter" "github.com/radius-project/radius/pkg/validator" ) @@ -44,16 +47,63 @@ func (s *Service) registerRoutes(r *chi.Mux, controllerOptions controller.Option pathBase = pathBase + "/" } - r.Route(pathBase+"planes/radius/{planeName}/providers/{providerNamespace}", func(r chi.Router) { - r.Route("/locations/{locationName}", func(r chi.Router) { - r.Get("/{or:operation[Rr]esults}/{operationID}", dynamicOperationHandler(v1.OperationGet, controllerOptions, makeGetOperationResultController)) - r.Get("/{os:operation[Ss]tatuses}/{operationID}", dynamicOperationHandler(v1.OperationGet, controllerOptions, makeGetOperationStatusController)) + r.Route(pathBase+"planes/radius/{planeName}", func(r chi.Router) { + + // Plane-scoped + r.Route("/providers/{providerNamespace}", func(r chi.Router) { + + // Plane-scoped LIST operation + r.Get("/{resourceType}", dynamicOperationHandler(v1.OperationPlaneScopeList, controllerOptions, makeListResourceAtPlaneScopeController)) + + // Async operation status/results + r.Route("/locations/{locationName}", func(r chi.Router) { + r.Get("/{or:operation[Rr]esults}/{operationID}", dynamicOperationHandler(v1.OperationGet, controllerOptions, makeGetOperationResultController)) + r.Get("/{os:operation[Ss]tatuses}/{operationID}", dynamicOperationHandler(v1.OperationGet, controllerOptions, makeGetOperationStatusController)) + }) + }) + + // Resource-group-scoped + r.Route("/{rg:resource[gG]roups}/{resourceGroupName}/providers/{providerNamespace}/{resourceType}", func(r chi.Router) { + r.Get("/", dynamicOperationHandler(v1.OperationList, controllerOptions, makeListResourceAtResourceGroupScopeController)) + r.Get("/{resourceName}", dynamicOperationHandler(v1.OperationGet, controllerOptions, makeGetResourceController)) + r.Put("/{resourceName}", dynamicOperationHandler(v1.OperationPut, controllerOptions, makePutResourceController)) + r.Delete("/{resourceName}", dynamicOperationHandler(v1.OperationDelete, controllerOptions, makeDeleteResourceController)) }) }) return nil } +var dynamicResourceOptions = controller.ResourceOptions[datamodel.DynamicResource]{ + RequestConverter: converter.DynamicResourceDataModelFromVersioned, + ResponseConverter: converter.DynamicResourceDataModelToVersioned, + AsyncOperationRetryAfter: time.Second * 5, + AsyncOperationTimeout: time.Hour * 24, +} + +func makeListResourceAtPlaneScopeController(opts controller.Options) (controller.Controller, error) { + // At plane scope we list resources recursively to include all resource groups. + copy := dynamicResourceOptions + copy.ListRecursiveQuery = true + return defaultoperation.NewListResources(opts, copy) +} + +func makeListResourceAtResourceGroupScopeController(opts controller.Options) (controller.Controller, error) { + return defaultoperation.NewListResources(opts, dynamicResourceOptions) +} + +func makeGetResourceController(opts controller.Options) (controller.Controller, error) { + return defaultoperation.NewGetResource(opts, dynamicResourceOptions) +} + +func makePutResourceController(opts controller.Options) (controller.Controller, error) { + return defaultoperation.NewDefaultAsyncPut(opts, dynamicResourceOptions) +} + +func makeDeleteResourceController(opts controller.Options) (controller.Controller, error) { + return defaultoperation.NewDefaultAsyncDelete(opts, dynamicResourceOptions) +} + func makeGetOperationResultController(opts controller.Options) (controller.Controller, error) { return defaultoperation.NewGetOperationResult(opts) } diff --git a/pkg/dynamicrp/integrationtest/dynamic/providers_test.go b/pkg/dynamicrp/integrationtest/dynamic/providers_test.go index bcfda112ff..1554c14d47 100644 --- a/pkg/dynamicrp/integrationtest/dynamic/providers_test.go +++ b/pkg/dynamicrp/integrationtest/dynamic/providers_test.go @@ -42,7 +42,7 @@ const ( exampleResourcePlaneID = "/planes/radius/" + radiusPlaneName exampleResourceGroupID = exampleResourcePlaneID + "/resourceGroups/test-group" - exampleResourceID = exampleResourceGroupID + "/providers/Applications.Test/exampleResources/" + exampleResourceName + exampleResourceID = exampleResourceGroupID + "/providers/" + resourceProviderNamespace + "/" + resourceTypeName + "/" + exampleResourceName exampleResourceURL = exampleResourceID + "?api-version=" + apiVersion ) @@ -60,12 +60,61 @@ func Test_Dynamic_Resource_Lifecycle(t *testing.T) { // Setup a resource group where we can interact with the new resource type. createResourceGroup(ucp) - // We have not yet implemented any functionality for dynamic RP. + // Now let's test the basic CRUD operations on the new resource type. // - // This is the hello-worldiest of tests. We're just making sure that all - // of the infrastructure works. - response := ucp.MakeRequest(http.MethodGet, exampleResourceURL, nil) - response.EqualsErrorCode(404, "NotFound") + // This resource type DOES NOT support recipes, so it's "inert" and doesn't do anything in the backend. + resource := map[string]any{ + "properties": map[string]any{ + "foo": "bar", + }, + "tags": map[string]string{ + "costcenter": "12345", + }, + } + + // Create the resource + response := ucp.MakeTypedRequest(http.MethodPut, exampleResourceURL, resource) + response.WaitForOperationComplete(nil) + + // Now lets verify the resource was created successfully. + + expectedResource := map[string]any{ + "id": "/planes/radius/testing/resourcegroups/test-group/providers/Applications.Test/exampleResources/my-example", + "location": "global", + "name": "my-example", + "properties": map[string]any{ + "foo": "bar", + "provisioningState": "Succeeded", + }, + "tags": map[string]any{ + "costcenter": "12345", + }, + "type": "Applications.Test/exampleResources", + } + + expectedList := map[string]any{ + "value": []any{expectedResource}, + } + + // GET (single) + response = ucp.MakeRequest(http.MethodGet, exampleResourceURL, nil) + response.EqualsValue(200, expectedResource) + + // GET (list at plane-scope) + response = ucp.MakeRequest(http.MethodGet, "/planes/radius/testing/resourcegroups/test-group/providers/Applications.Test/exampleResources"+"?api-version="+apiVersion, nil) + response.EqualsValue(200, expectedList) + + // GET (list at resourcegroup-scope) + response = ucp.MakeRequest(http.MethodGet, "/planes/radius/testing/providers/Applications.Test/exampleResources"+"?api-version="+apiVersion, nil) + response.EqualsValue(200, expectedList) + + // Now lets delete the resource + response = ucp.MakeRequest(http.MethodDelete, exampleResourceURL, nil) + response.WaitForOperationComplete(nil) + + // Now we should get a 404 when trying to get the resource + response = ucp.MakeRequest(http.MethodGet, exampleResourceURL, nil) + response.EqualsErrorCode(404, v1.CodeNotFound) } func createRadiusPlane(server *ucptesthost.TestHost) v20231001preview.RadiusPlanesClientCreateOrUpdateResponse { From ed48e92268e2bd94e3ed3f48386f5590e3193980 Mon Sep 17 00:00:00 2001 From: Shruthi Kumar Date: Thu, 2 Jan 2025 14:11:45 -0800 Subject: [PATCH 20/37] Release candidate 0.42.0-rc1 (#8181) # Description Updating release version for release candidate ## Type of change - This pull request fixes a bug in Radius and has an approved issue (issue link required). - This pull request adds or changes features of Radius and has an approved issue (issue link required). - This pull request is a minor refactor, code cleanup, test improvement, or other maintenance task and doesn't change the functionality of Radius (issue link optional). Fixes: #issue_number ## Contributor checklist Please verify that the PR meets the following requirements, where applicable: - [ ] An overview of proposed schema changes is included in a linked GitHub issue. - [ ] A design document PR is created in the [design-notes repository](https://github.com/radius-project/design-notes/), if new APIs are being introduced. - [ ] If applicable, design document has been reviewed and approved by Radius maintainers/approvers. - [ ] A PR for the [samples repository](https://github.com/radius-project/samples) is created, if existing samples are affected by the changes in this PR. - [ ] A PR for the [documentation repository](https://github.com/radius-project/docs) is created, if the changes in this PR affect the documentation or any user facing updates are made. - [ ] A PR for the [recipes repository](https://github.com/radius-project/recipes) is created, if existing recipes are affected by the changes in this PR. Signed-off-by: sk593 --- versions.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/versions.yaml b/versions.yaml index 175427926b..e282d873a8 100644 --- a/versions.yaml +++ b/versions.yaml @@ -1,4 +1,6 @@ supported: + - channel: '0.42' + version: 'v0.42.0-rc1' - channel: '0.41' version: 'v0.41.0' deprecated: From 46dc40dfa4ec5d070b4937fee8c3a491504ef1f0 Mon Sep 17 00:00:00 2001 From: Lakshmi Javadekar <103459615+lakshmimsft@users.noreply.github.com> Date: Thu, 2 Jan 2025 15:20:54 -0800 Subject: [PATCH 21/37] Register Manifests during ucp startup sequence (#8120) # Description - Registering Manifests during ucp startup sequence. - New initializer service to help do that - using generated clients directly ## Type of change - This pull request fixes a bug in Radius and has an approved issue ([link)](https://dev.azure.com/azure-octo/Incubations/_workitems/edit/13647). Fixes: Part of UDT ## Contributor checklist Please verify that the PR meets the following requirements, where applicable: - [ ] An overview of proposed schema changes is included in a linked GitHub issue. - [ ] A design document PR is created in the [design-notes repository](https://github.com/radius-project/design-notes/), if new APIs are being introduced. - [ ] If applicable, design document has been reviewed and approved by Radius maintainers/approvers. - [ ] A PR for the [samples repository](https://github.com/radius-project/samples) is created, if existing samples are affected by the changes in this PR. - [ ] A PR for the [documentation repository](https://github.com/radius-project/docs) is created, if the changes in this PR affect the documentation or any user facing updates are made. - [ ] A PR for the [recipes repository](https://github.com/radius-project/recipes) is created, if existing recipes are affected by the changes in this PR. --------- Signed-off-by: lakshmimsft --- cmd/ucpd/ucp-dev.yaml | 2 + deploy/Chart/templates/ucp/configmaps.yaml | 1 + pkg/cli/cmd/resourceprovider/create/create.go | 98 ++--- .../resourceprovider/create/create_test.go | 109 ++---- pkg/cli/connections/factory.go | 38 +- pkg/cli/manifest/registermanifest.go | 168 ++++++++ pkg/cli/manifest/registermanifest_test.go | 160 ++++++++ pkg/cli/manifest/testclientfactory.go | 164 ++++++++ .../resourceprovider-valid1.yaml | 12 + .../resourceprovider-valid2.yaml | 12 + pkg/cli/workspaces/connection.go | 18 +- pkg/ucp/api/README.md | 1 - .../fake/zz_generated_apiversions_server.go | 310 +++++++++++++++ .../zz_generated_awscredentials_server.go | 295 ++++++++++++++ .../fake/zz_generated_awsplanes_server.go | 308 +++++++++++++++ .../zz_generated_azurecredentials_server.go | 295 ++++++++++++++ .../fake/zz_generated_azureplanes_server.go | 308 +++++++++++++++ .../fake/zz_generated_internal.go | 66 ++++ .../fake/zz_generated_locations_server.go | 294 ++++++++++++++ .../fake/zz_generated_planes_server.go | 118 ++++++ .../fake/zz_generated_radiusplanes_server.go | 308 +++++++++++++++ .../zz_generated_resourcegroups_server.go | 295 ++++++++++++++ .../zz_generated_resourceproviders_server.go | 362 ++++++++++++++++++ .../fake/zz_generated_resources_server.go | 134 +++++++ .../fake/zz_generated_resourcetypes_server.go | 294 ++++++++++++++ .../fake/zz_generated_server_factory.go | 150 ++++++++ .../fake/zz_generated_time_rfc3339.go | 114 ++++++ .../zz_generated_apiversions_client.go | 13 +- .../zz_generated_awscredentials_client.go | 17 +- .../zz_generated_awsplanes_client.go | 17 +- .../zz_generated_azurecredentials_client.go | 17 +- .../zz_generated_azureplanes_client.go | 17 +- .../zz_generated_locations_client.go | 13 +- .../zz_generated_planes_client.go | 1 + .../zz_generated_radiusplanes_client.go | 17 +- .../zz_generated_resourcegroups_client.go | 17 +- .../zz_generated_resourceproviders_client.go | 18 +- .../zz_generated_resources_client.go | 1 + .../zz_generated_resourcetypes_client.go | 13 +- pkg/ucp/config.go | 4 + pkg/ucp/initializer/service.go | 134 +++++++ .../resourceproviders_test.go | 38 ++ .../manifests/resourceprovider-valid1.yaml | 7 + ...ceprovider_manifest_list_responsebody.json | 14 + ...resourceprovider_manifest_requestbody.json | 5 + ...esourceprovider_manifest_responsebody.json | 10 + ...sourcetype_manifest_list_responsebody.json | 12 + .../resourcetype_manifest_requestbody.json | 5 + .../resourcetype_manifest_responsebody.json | 8 + .../resourceproviders/util_test.go | 20 + pkg/ucp/server/server.go | 3 + test/rp/rptest.go | 2 +- 52 files changed, 4635 insertions(+), 222 deletions(-) create mode 100644 pkg/cli/manifest/registermanifest.go create mode 100644 pkg/cli/manifest/registermanifest_test.go create mode 100644 pkg/cli/manifest/testclientfactory.go create mode 100644 pkg/cli/manifest/testdata/registerdirectory/resourceprovider-valid1.yaml create mode 100644 pkg/cli/manifest/testdata/registerdirectory/resourceprovider-valid2.yaml create mode 100644 pkg/ucp/api/v20231001preview/fake/zz_generated_apiversions_server.go create mode 100644 pkg/ucp/api/v20231001preview/fake/zz_generated_awscredentials_server.go create mode 100644 pkg/ucp/api/v20231001preview/fake/zz_generated_awsplanes_server.go create mode 100644 pkg/ucp/api/v20231001preview/fake/zz_generated_azurecredentials_server.go create mode 100644 pkg/ucp/api/v20231001preview/fake/zz_generated_azureplanes_server.go create mode 100644 pkg/ucp/api/v20231001preview/fake/zz_generated_internal.go create mode 100644 pkg/ucp/api/v20231001preview/fake/zz_generated_locations_server.go create mode 100644 pkg/ucp/api/v20231001preview/fake/zz_generated_planes_server.go create mode 100644 pkg/ucp/api/v20231001preview/fake/zz_generated_radiusplanes_server.go create mode 100644 pkg/ucp/api/v20231001preview/fake/zz_generated_resourcegroups_server.go create mode 100644 pkg/ucp/api/v20231001preview/fake/zz_generated_resourceproviders_server.go create mode 100644 pkg/ucp/api/v20231001preview/fake/zz_generated_resources_server.go create mode 100644 pkg/ucp/api/v20231001preview/fake/zz_generated_resourcetypes_server.go create mode 100644 pkg/ucp/api/v20231001preview/fake/zz_generated_server_factory.go create mode 100644 pkg/ucp/api/v20231001preview/fake/zz_generated_time_rfc3339.go create mode 100644 pkg/ucp/initializer/service.go create mode 100644 pkg/ucp/integrationtests/resourceproviders/testdata/manifests/resourceprovider-valid1.yaml create mode 100644 pkg/ucp/integrationtests/resourceproviders/testdata/resourceprovider_manifest_list_responsebody.json create mode 100644 pkg/ucp/integrationtests/resourceproviders/testdata/resourceprovider_manifest_requestbody.json create mode 100644 pkg/ucp/integrationtests/resourceproviders/testdata/resourceprovider_manifest_responsebody.json create mode 100644 pkg/ucp/integrationtests/resourceproviders/testdata/resourcetype_manifest_list_responsebody.json create mode 100644 pkg/ucp/integrationtests/resourceproviders/testdata/resourcetype_manifest_requestbody.json create mode 100644 pkg/ucp/integrationtests/resourceproviders/testdata/resourcetype_manifest_responsebody.json diff --git a/cmd/ucpd/ucp-dev.yaml b/cmd/ucpd/ucp-dev.yaml index 3bc09c4871..ddabe6bb0b 100644 --- a/cmd/ucpd/ucp-dev.yaml +++ b/cmd/ucpd/ucp-dev.yaml @@ -52,6 +52,8 @@ initialization: Applications.Datastores: "http://localhost:8080" Microsoft.Resources: "http://localhost:5017" kind: "UCPNative" + # This is the directory location which contains manifests to be registered. + manifestDirectory: "" identity: authMethod: default diff --git a/deploy/Chart/templates/ucp/configmaps.yaml b/deploy/Chart/templates/ucp/configmaps.yaml index 5dcad4fae0..2047e006a8 100644 --- a/deploy/Chart/templates/ucp/configmaps.yaml +++ b/deploy/Chart/templates/ucp/configmaps.yaml @@ -50,6 +50,7 @@ data: - id: "/planes/aws/aws" properties: kind: "AWS" + manifestDirectory: "" identity: authMethod: UCPCredential diff --git a/pkg/cli/cmd/resourceprovider/create/create.go b/pkg/cli/cmd/resourceprovider/create/create.go index b5c55b775f..162bef3c76 100644 --- a/pkg/cli/cmd/resourceprovider/create/create.go +++ b/pkg/cli/cmd/resourceprovider/create/create.go @@ -19,16 +19,15 @@ package create import ( "context" - 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" "github.com/radius-project/radius/pkg/cli/cmd/commonflags" "github.com/radius-project/radius/pkg/cli/cmd/resourceprovider/common" - "github.com/radius-project/radius/pkg/cli/connections" "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/to" + "github.com/radius-project/radius/pkg/sdk" "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" "github.com/spf13/cobra" ) @@ -70,22 +69,26 @@ rad resource-provider create --from-file /path/to/input.json // Runner is the Runner implementation for the `rad resource-provider create` command. type Runner struct { - ConnectionFactory connections.Factory - ConfigHolder *framework.ConfigHolder - Output output.Interface - Format string - Workspace *workspaces.Workspace + UCPClientFactory *v20231001preview.ClientFactory + ConfigHolder *framework.ConfigHolder + Output output.Interface + Format string + Workspace *workspaces.Workspace ResourceProviderManifestFilePath string ResourceProvider *manifest.ResourceProvider + Logger func(format string, args ...any) } // NewRunner creates an instance of the runner for the `rad resource-provider create` command. func NewRunner(factory framework.Factory) *Runner { + output := factory.GetOutput() return &Runner{ - ConnectionFactory: factory.GetConnectionFactory(), - ConfigHolder: factory.GetConfigHolder(), - Output: factory.GetOutput(), + ConfigHolder: factory.GetConfigHolder(), + Output: output, + Logger: func(format string, args ...any) { + output.LogInfo(format, args...) + }, } } @@ -114,76 +117,49 @@ func (r *Runner) Validate(cmd *cobra.Command, args []string) error { // Run runs the `rad resource-provider create` command. func (r *Runner) Run(ctx context.Context) error { - client, err := r.ConnectionFactory.CreateApplicationsManagementClient(ctx, *r.Workspace) - if err != nil { - return err + // 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 + } } - r.Output.LogInfo("Creating resource provider %s", r.ResourceProvider.Name) - _, err = client.CreateOrUpdateResourceProvider(ctx, "local", r.ResourceProvider.Name, &v20231001preview.ResourceProviderResource{ - Location: to.Ptr(v1.LocationGlobal), - Properties: &v20231001preview.ResourceProviderProperties{}, - }) - if err != nil { + // Proceed with registering manifests + if err := manifest.RegisterFile(ctx, r.UCPClientFactory, "local", r.ResourceProviderManifestFilePath, r.Logger); err != nil { return err } - // The location resource contains references to all of the resource types and API versions that the resource provider supports. - // We're instantiating the struct here so we can update it as we loop. - locationResource := v20231001preview.LocationResource{ - Properties: &v20231001preview.LocationProperties{ - ResourceTypes: map[string]*v20231001preview.LocationResourceType{}, - }, + response, err := r.UCPClientFactory.NewResourceProvidersClient().Get(ctx, "local", r.ResourceProvider.Name, nil) + if err != nil { + return err } - for resourceTypeName, resourceType := range r.ResourceProvider.Types { - r.Output.LogInfo("Creating resource type %s/%s", r.ResourceProvider.Name, resourceTypeName) - _, err := client.CreateOrUpdateResourceType(ctx, "local", r.ResourceProvider.Name, resourceTypeName, &v20231001preview.ResourceTypeResource{ - Properties: &v20231001preview.ResourceTypeProperties{ - DefaultAPIVersion: resourceType.DefaultAPIVersion, - }, - }) - if err != nil { - return err - } - - locationResourceType := &v20231001preview.LocationResourceType{ - APIVersions: map[string]map[string]any{}, - } - - for apiVersionName := range resourceType.APIVersions { - r.Output.LogInfo("Creating API Version %s/%s@%s", r.ResourceProvider.Name, resourceTypeName, apiVersionName) - _, err := client.CreateOrUpdateAPIVersion(ctx, "local", r.ResourceProvider.Name, resourceTypeName, apiVersionName, &v20231001preview.APIVersionResource{ - Properties: &v20231001preview.APIVersionProperties{}, - }) - if err != nil { - return err - } - - locationResourceType.APIVersions[apiVersionName] = map[string]any{} - } - - locationResource.Properties.ResourceTypes[resourceTypeName] = locationResourceType - } + // Add a blank line before printing the result. + r.Output.LogInfo("") - r.Output.LogInfo("Creating location %s/%s", r.ResourceProvider.Name, v1.LocationGlobal) - _, err = client.CreateOrUpdateLocation(ctx, "local", r.ResourceProvider.Name, v1.LocationGlobal, &locationResource) + err = r.Output.WriteFormatted(r.Format, response, common.GetResourceProviderTableFormat()) if err != nil { return err } - response, err := client.GetResourceProvider(ctx, "local", r.ResourceProvider.Name) + return nil +} + +func (r *Runner) initializeClientFactory(ctx context.Context, workspace *workspaces.Workspace) error { + connection, err := workspace.Connect(ctx) if err != nil { return err } - // Add a blank line before printing the result. - r.Output.LogInfo("") + clientOptions := sdk.NewClientOptions(connection) - err = r.Output.WriteFormatted(r.Format, response, common.GetResourceProviderTableFormat()) + clientFactory, err := v20231001preview.NewClientFactory(&aztoken.AnonymousCredential{}, clientOptions) if err != nil { return err } + r.UCPClientFactory = clientFactory return nil } diff --git a/pkg/cli/cmd/resourceprovider/create/create_test.go b/pkg/cli/cmd/resourceprovider/create/create_test.go index 83f7f317db..d74123e709 100644 --- a/pkg/cli/cmd/resourceprovider/create/create_test.go +++ b/pkg/cli/cmd/resourceprovider/create/create_test.go @@ -17,22 +17,17 @@ limitations under the License. package create import ( + "bytes" "context" + "fmt" "testing" - v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" - "github.com/radius-project/radius/pkg/cli/clients" - "github.com/radius-project/radius/pkg/cli/cmd/resourceprovider/common" - "github.com/radius-project/radius/pkg/cli/connections" "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/to" - "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" "github.com/radius-project/radius/test/radcli" "github.com/stretchr/testify/require" - "go.uber.org/mock/gomock" ) func Test_CommandValidation(t *testing.T) { @@ -68,101 +63,43 @@ func Test_Validate(t *testing.T) { ConfigHolder: framework.ConfigHolder{Config: config}, }, } + radcli.SharedValidateValidation(t, NewCommand, testcases) } func Test_Run(t *testing.T) { t.Run("Success: resource provider created", func(t *testing.T) { - ctrl := gomock.NewController(t) resourceProviderData, err := manifest.ReadFile("testdata/valid.yaml") require.NoError(t, err) - expectedResourceProvider := v20231001preview.ResourceProviderResource{ - Location: to.Ptr(v1.LocationGlobal), - Properties: &v20231001preview.ResourceProviderProperties{}, - } - expectedResourceType := v20231001preview.ResourceTypeResource{ - Properties: &v20231001preview.ResourceTypeProperties{}, - } - expectedAPIVersion := v20231001preview.APIVersionResource{ - Properties: &v20231001preview.APIVersionProperties{}, - } - expectedLocation := v20231001preview.LocationResource{ - Properties: &v20231001preview.LocationProperties{ - ResourceTypes: map[string]*v20231001preview.LocationResourceType{ - "testResources": { - APIVersions: map[string]map[string]any{ - "2025-01-01-preview": {}, - }, - }, - }, - }, - } + expectedResourceType := "testResources" + expectedAPIVersion := "2025-01-01-preview" - appManagementClient := clients.NewMockApplicationsManagementClient(ctrl) - appManagementClient.EXPECT(). - CreateOrUpdateResourceProvider(gomock.Any(), "local", "MyCompany.Resources", &expectedResourceProvider). - Return(expectedResourceProvider, nil). - Times(1) - appManagementClient.EXPECT(). - CreateOrUpdateResourceType(gomock.Any(), "local", "MyCompany.Resources", "testResources", &expectedResourceType). - Return(expectedResourceType, nil). - Times(1) - appManagementClient.EXPECT(). - CreateOrUpdateAPIVersion(gomock.Any(), "local", "MyCompany.Resources", "testResources", "2025-01-01-preview", &expectedAPIVersion). - Return(expectedAPIVersion, nil). - Times(1) - appManagementClient.EXPECT(). - CreateOrUpdateLocation(gomock.Any(), "local", "MyCompany.Resources", v1.LocationGlobal, &expectedLocation). - Return(expectedLocation, nil). - Times(1) - appManagementClient.EXPECT(). - GetResourceProvider(gomock.Any(), "local", "MyCompany.Resources"). - Return(expectedResourceProvider, nil). - Times(1) - - outputSink := &output.MockOutput{} + clientFactory, err := manifest.NewTestClientFactory() + require.NoError(t, err) + + var logBuffer bytes.Buffer + logger := func(format string, args ...any) { + fmt.Fprintf(&logBuffer, format+"\n", args...) + } runner := &Runner{ - ConnectionFactory: &connections.MockFactory{ApplicationsManagementClient: appManagementClient}, - Output: outputSink, - Workspace: &workspaces.Workspace{}, - ResourceProvider: resourceProviderData, - Format: "table", + UCPClientFactory: clientFactory, + Output: &output.MockOutput{}, + Workspace: &workspaces.Workspace{}, + ResourceProvider: resourceProviderData, + Format: "table", + Logger: logger, + ResourceProviderManifestFilePath: "testdata/valid.yaml", } err = runner.Run(context.Background()) require.NoError(t, err) - expectedOutput := []any{ - output.LogOutput{ - Format: "Creating resource provider %s", - Params: []any{"MyCompany.Resources"}, - }, - output.LogOutput{ - Format: "Creating resource type %s/%s", - Params: []any{"MyCompany.Resources", "testResources"}, - }, - output.LogOutput{ - Format: "Creating API Version %s/%s@%s", - Params: []any{"MyCompany.Resources", "testResources", "2025-01-01-preview"}, - }, - output.LogOutput{ - Format: "Creating location %s/%s", - Params: []any{"MyCompany.Resources", "global"}, - }, - output.LogOutput{ - Format: "", - Params: nil, - }, - output.FormattedOutput{ - Format: "table", - Obj: expectedResourceProvider, - Options: common.GetResourceProviderTableFormat(), - }, - } - require.Equal(t, expectedOutput, outputSink.Writes) + logOutput := logBuffer.String() + require.Contains(t, logOutput, fmt.Sprintf("Creating resource provider %s", resourceProviderData.Name)) + require.Contains(t, logOutput, fmt.Sprintf("Creating resource type %s/%s", resourceProviderData.Name, expectedResourceType)) + require.Contains(t, logOutput, fmt.Sprintf("Creating API Version %s/%s@%s", resourceProviderData.Name, expectedResourceType, expectedAPIVersion)) }) - } diff --git a/pkg/cli/connections/factory.go b/pkg/cli/connections/factory.go index 93b34f0142..cd7bda56dd 100644 --- a/pkg/cli/connections/factory.go +++ b/pkg/cli/connections/factory.go @@ -18,13 +18,11 @@ package connections import ( "context" - "errors" "fmt" aztoken "github.com/radius-project/radius/pkg/azure/tokencredentials" "github.com/radius-project/radius/pkg/cli/clients" "github.com/radius-project/radius/pkg/cli/clients_new/generated" - "github.com/radius-project/radius/pkg/cli/clierrors" cli_credential "github.com/radius-project/radius/pkg/cli/credential" "github.com/radius-project/radius/pkg/cli/deployment" "github.com/radius-project/radius/pkg/cli/kubernetes" @@ -55,18 +53,11 @@ type impl struct { // CreateDeploymentClient connects to a workspace, tests the connection, creates a deployment client and an operations // client, and returns them along with the resource group name. It returns an error if any of the steps fail. func (i *impl) CreateDeploymentClient(ctx context.Context, workspace workspaces.Workspace) (clients.DeploymentClient, error) { - connection, err := workspace.Connect() + connection, err := workspace.Connect(ctx) if err != nil { return nil, err } - err = sdk.TestConnection(ctx, connection) - if errors.Is(err, &sdk.ErrRadiusNotInstalled{}) { - return nil, clierrors.MessageWithCause(err, "Could not connect to Radius.") - } else if err != nil { - return nil, err - } - armClientOptions := sdk.NewClientOptions(connection) dc, err := sdkclients.NewResourceDeploymentsClient(&sdkclients.Options{ Cred: &aztoken.AnonymousCredential{}, @@ -102,18 +93,11 @@ func (i *impl) CreateDeploymentClient(ctx context.Context, workspace workspaces. // CreateDiagnosticsClient creates a DiagnosticsClient by connecting to a workspace, testing the connection, and creating // clients for applications, containers, environments, and gateways. If an error occurs, it is returned. func (i *impl) CreateDiagnosticsClient(ctx context.Context, workspace workspaces.Workspace) (clients.DiagnosticsClient, error) { - connection, err := workspace.Connect() + connection, err := workspace.Connect(ctx) if err != nil { return nil, err } - err = sdk.TestConnection(ctx, connection) - if errors.Is(err, &sdk.ErrRadiusNotInstalled{}) { - return nil, clierrors.MessageWithCause(err, "Could not connect to Radius.") - } else if err != nil { - return nil, err - } - connectionConfig, err := workspace.ConnectionConfig() if err != nil { return nil, err @@ -168,18 +152,11 @@ func (i *impl) CreateDiagnosticsClient(ctx context.Context, workspace workspaces // CreateApplicationsManagementClient connects to the workspace, tests the connection, and returns a // UCPApplicationsManagementClient if successful, or an error if unsuccessful. func (*impl) CreateApplicationsManagementClient(ctx context.Context, workspace workspaces.Workspace) (clients.ApplicationsManagementClient, error) { - connection, err := workspace.Connect() + connection, err := workspace.Connect(ctx) if err != nil { return nil, err } - err = sdk.TestConnection(ctx, connection) - if errors.Is(err, &sdk.ErrRadiusNotInstalled{}) { - return nil, clierrors.MessageWithCause(err, "Could not connect to Radius.") - } else if err != nil { - return nil, err - } - return &clients.UCPApplicationsManagementClient{ RootScope: workspace.Scope, ClientOptions: sdk.NewClientOptions(connection), @@ -192,18 +169,11 @@ func (*impl) CreateApplicationsManagementClient(ctx context.Context, workspace w // CreateCredentialManagementClient establishes a connection to a workspace, tests the connection, creates Azure and AWS // credential clients, and returns a UCPCredentialManagementClient. An error is returned if any of the steps fail. func (*impl) CreateCredentialManagementClient(ctx context.Context, workspace workspaces.Workspace) (cli_credential.CredentialManagementClient, error) { - connection, err := workspace.Connect() + connection, err := workspace.Connect(ctx) if err != nil { return nil, err } - err = sdk.TestConnection(ctx, connection) - if errors.Is(err, &sdk.ErrRadiusNotInstalled{}) { - return nil, clierrors.MessageWithCause(err, "Could not connect to Radius.") - } else if err != nil { - return nil, err - } - clientOptions := sdk.NewClientOptions(connection) azureCredentialClient, err := v20231001preview.NewAzureCredentialsClient(&aztoken.AnonymousCredential{}, clientOptions) diff --git a/pkg/cli/manifest/registermanifest.go b/pkg/cli/manifest/registermanifest.go new file mode 100644 index 0000000000..289f96d5e5 --- /dev/null +++ b/pkg/cli/manifest/registermanifest.go @@ -0,0 +1,168 @@ +/* +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 manifest + +import ( + "context" + "fmt" + "os" + "path/filepath" + + v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/to" + "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" +) + +// 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 + } + + logIfEnabled(logger, "Creating resource provider %s", resourceProvider.Name) + resourceProviderPoller, err := clientFactory.NewResourceProvidersClient().BeginCreateOrUpdate(ctx, planeName, resourceProvider.Name, v20231001preview.ResourceProviderResource{ + Location: to.Ptr(v1.LocationGlobal), + Properties: &v20231001preview.ResourceProviderProperties{}, + }, nil) + if err != nil { + return err + } + + _, err = resourceProviderPoller.PollUntilDone(ctx, nil) + if err != nil { + return err + } + + // The location resource contains references to all of the resource types and API versions that the resource provider supports. + // We're instantiating the struct here so we can update it as we loop. + locationResource := v20231001preview.LocationResource{ + Properties: &v20231001preview.LocationProperties{ + ResourceTypes: map[string]*v20231001preview.LocationResourceType{}, + }, + } + + for resourceTypeName, resourceType := range resourceProvider.Types { + logIfEnabled(logger, "Creating resource type %s/%s", resourceProvider.Name, resourceTypeName) + resourceTypePoller, err := clientFactory.NewResourceTypesClient().BeginCreateOrUpdate(ctx, planeName, resourceProvider.Name, resourceTypeName, v20231001preview.ResourceTypeResource{ + Properties: &v20231001preview.ResourceTypeProperties{ + DefaultAPIVersion: resourceType.DefaultAPIVersion, + }, + }, nil) + if err != nil { + return err + } + + _, err = resourceTypePoller.PollUntilDone(ctx, nil) + if err != nil { + return err + } + + locationResourceType := &v20231001preview.LocationResourceType{ + APIVersions: map[string]map[string]any{}, + } + + for apiVersionName := range resourceType.APIVersions { + logIfEnabled(logger, "Creating API Version %s/%s@%s", resourceProvider.Name, resourceTypeName, apiVersionName) + apiVersionsPoller, err := clientFactory.NewAPIVersionsClient().BeginCreateOrUpdate(ctx, planeName, resourceProvider.Name, resourceTypeName, apiVersionName, v20231001preview.APIVersionResource{ + Properties: &v20231001preview.APIVersionProperties{}, + }, nil) + if err != nil { + return err + } + + _, err = apiVersionsPoller.PollUntilDone(ctx, nil) + if err != nil { + return err + } + + locationResourceType.APIVersions[apiVersionName] = map[string]any{} + } + + locationResource.Properties.ResourceTypes[resourceTypeName] = locationResourceType + } + + logIfEnabled(logger, "Creating location %s/%s", 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 + } + + _, err = clientFactory.NewResourceProvidersClient().Get(ctx, planeName, resourceProvider.Name, nil) + if err != nil { + return err + } + + return nil +} + +// 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") + } + + info, err := os.Stat(directoryPath) + if err != nil { + return fmt.Errorf("failed to access manifest path %s: %w", directoryPath, err) + } + + if !info.IsDir() { + 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 + } + filePath := filepath.Join(directoryPath, fileInfo.Name()) + + logIfEnabled(logger, "Registering manifest %s", filePath) + err = RegisterFile(ctx, clientFactory, planeName, filePath, logger) + if err != nil { + return fmt.Errorf("failed to register manifest file %s: %w", filePath, err) + } + } + + 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 { + logger(format, args...) + } +} diff --git a/pkg/cli/manifest/registermanifest_test.go b/pkg/cli/manifest/registermanifest_test.go new file mode 100644 index 0000000000..cc2c1ec525 --- /dev/null +++ b/pkg/cli/manifest/registermanifest_test.go @@ -0,0 +1,160 @@ +/* +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 manifest + +import ( + "bytes" + "context" + "fmt" + "testing" + + // armpolicy "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/policy" + + "github.com/radius-project/radius/pkg/to" + "github.com/stretchr/testify/require" +) + +func TestRegisterDirectory(t *testing.T) { + tests := []struct { + name string + planeName string + directoryPath string + expectError bool + expectedErrorMessage string + expectedResourceProvider string + }{ + { + name: "Success", + planeName: "local", + directoryPath: "testdata/registerdirectory", + expectError: false, + expectedErrorMessage: "", + expectedResourceProvider: "MyCompany2.CompanyName2", + }, + { + name: "EmptyDirectoryPath", + planeName: "local", + directoryPath: "", + expectError: true, + expectedErrorMessage: "invalid manifest directory", + expectedResourceProvider: "", + }, + { + name: "InvalidDirectoryPath", + planeName: "local", + directoryPath: "#^$/invalid", + expectError: true, + expectedErrorMessage: "failed to access manifest path #^$/invalid: stat #^$/invalid: no such file or directory", + expectedResourceProvider: "", + }, + { + name: "FilePathInsteadOfDirectory", + planeName: "local", + directoryPath: "testdata/valid.yaml", + expectError: true, + expectedErrorMessage: "manifest path testdata/valid.yaml is not a directory", + expectedResourceProvider: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + clientFactory, err := NewTestClientFactory() + require.NoError(t, err) + + err = RegisterDirectory(context.Background(), clientFactory, tt.planeName, tt.directoryPath, nil) + if tt.expectError { + require.Error(t, err) + require.Contains(t, err.Error(), tt.expectedErrorMessage) + } else { + require.NoError(t, err) + + // Verify the expected resource provider exists + if tt.expectedResourceProvider != "" { + rp, err := clientFactory.NewResourceProvidersClient().Get(context.Background(), tt.planeName, tt.expectedResourceProvider, nil) + require.NoError(t, err, "Failed to retrieve the expected resource provider") + require.Equal(t, to.Ptr(tt.expectedResourceProvider), rp.Name) + } + } + }) + } +} + +func TestRegisterFile(t *testing.T) { + tests := []struct { + name string + planeName string + filePath string + expectError bool + expectedErrorMessage string + expectedResourceProvider string + expectedResourceTypeName string + expectedAPIVersion string + }{ + { + name: "Success", + planeName: "local", + filePath: "testdata/registerdirectory/resourceprovider-valid2.yaml", + expectError: false, + expectedErrorMessage: "", + expectedResourceProvider: "MyCompany2.CompanyName2", + expectedResourceTypeName: "testResource3", + expectedAPIVersion: "2025-01-01-preview", + }, + { + name: "EmptyDirectoryPath", + planeName: "local", + filePath: "", + expectError: true, + expectedErrorMessage: "invalid manifest file path", + expectedResourceProvider: "", + expectedResourceTypeName: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + clientFactory, err := NewTestClientFactory() + require.NoError(t, err, "Failed to create client factory") + + var logBuffer bytes.Buffer + logger := func(format string, args ...interface{}) { + fmt.Fprintf(&logBuffer, format+"\n", args...) + } + + err = RegisterFile(context.Background(), clientFactory, tt.planeName, tt.filePath, logger) + if tt.expectError { + require.Error(t, err) + require.Contains(t, err.Error(), tt.expectedErrorMessage) + } else { + require.NoError(t, err) + + // Verify the expected resource provider exists + if tt.expectedResourceProvider != "" { + rp, err := clientFactory.NewResourceProvidersClient().Get(context.Background(), tt.planeName, tt.expectedResourceProvider, nil) + require.NoError(t, err, "Failed to retrieve the expected resource provider") + require.Equal(t, to.Ptr(tt.expectedResourceProvider), rp.Name) + + logOutput := logBuffer.String() + require.Contains(t, logOutput, fmt.Sprintf("Creating resource type %s/%s", tt.expectedResourceProvider, tt.expectedResourceTypeName)) + require.Contains(t, logOutput, fmt.Sprintf("Creating API Version %s/%s@%s", tt.expectedResourceProvider, tt.expectedResourceTypeName, tt.expectedAPIVersion)) + } + } + }) + } +} diff --git a/pkg/cli/manifest/testclientfactory.go b/pkg/cli/manifest/testclientfactory.go new file mode 100644 index 0000000000..85e9117d1f --- /dev/null +++ b/pkg/cli/manifest/testclientfactory.go @@ -0,0 +1,164 @@ +/* +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 manifest + +import ( + "context" + "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" +) + +// NewTestClientFactory creates a new client factory for testing purposes. +func NewTestClientFactory() (*v20231001preview.ClientFactory, 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, // Add this parameter + ) (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 + }, + } + + 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 + }, + } + + serverFactory := ucpfake.ServerFactory{ + ResourceProvidersServer: resourceProvidersServer, + ResourceTypesServer: resourceTypesServer, + APIVersionsServer: apiVersionsServer, + LocationsServer: locationsServer, + } + + 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 +} diff --git a/pkg/cli/manifest/testdata/registerdirectory/resourceprovider-valid1.yaml b/pkg/cli/manifest/testdata/registerdirectory/resourceprovider-valid1.yaml new file mode 100644 index 0000000000..b76f369047 --- /dev/null +++ b/pkg/cli/manifest/testdata/registerdirectory/resourceprovider-valid1.yaml @@ -0,0 +1,12 @@ +name: MyCompany.CompanyName +types: + testResource1: + apiVersions: + "2025-01-01-preview": + schema: {} + capabilities: [] + testResource2: + apiVersions: + "2025-01-01-preview": + schema: {} + capabilities: [] diff --git a/pkg/cli/manifest/testdata/registerdirectory/resourceprovider-valid2.yaml b/pkg/cli/manifest/testdata/registerdirectory/resourceprovider-valid2.yaml new file mode 100644 index 0000000000..3ef2b3ffcc --- /dev/null +++ b/pkg/cli/manifest/testdata/registerdirectory/resourceprovider-valid2.yaml @@ -0,0 +1,12 @@ +name: MyCompany2.CompanyName2 +types: + testResource3: + apiVersions: + "2025-01-01-preview": + schema: {} + capabilities: ["Recipes"] + testResource4: + apiVersions: + "2025-01-01-preview": + schema: {} + capabilities: ["Recipes"] diff --git a/pkg/cli/workspaces/connection.go b/pkg/cli/workspaces/connection.go index 12addf527c..aff86392da 100644 --- a/pkg/cli/workspaces/connection.go +++ b/pkg/cli/workspaces/connection.go @@ -17,6 +17,8 @@ limitations under the License. package workspaces import ( + "context" + "errors" "fmt" "net/url" "strings" @@ -92,14 +94,26 @@ func (ws Workspace) ConnectionConfig() (ConnectionConfig, error) { } } -// Connect attempts to create a connection to the workspace using the connection configuration and returns the +// Connect attempts to create and test a connection to the workspace using the connection configuration and returns the // connection and an error if one occurs. -func (ws Workspace) Connect() (sdk.Connection, error) { +func (ws Workspace) Connect(ctx context.Context) (sdk.Connection, error) { connectionConfig, err := ws.ConnectionConfig() if err != nil { return nil, err } + connection, err := connectionConfig.Connect() + if err != nil { + return nil, err + } + + err = sdk.TestConnection(ctx, connection) + if errors.Is(err, &sdk.ErrRadiusNotInstalled{}) { + return nil, fmt.Errorf("could not connect to radius: %w", err) + } else if err != nil { + return nil, err + } + return connectionConfig.Connect() } diff --git a/pkg/ucp/api/README.md b/pkg/ucp/api/README.md index aa95d60849..169ee1afa6 100644 --- a/pkg/ucp/api/README.md +++ b/pkg/ucp/api/README.md @@ -51,7 +51,6 @@ module-version: 0.0.1 file-prefix: zz_generated_ license-header: "Licensed under the Apache License, Version 2.0 . See LICENSE in the repository root for license information.\nCode generated by Microsoft (R) AutoRest Code Generator.\nChanges may cause incorrect behavior and will be lost if the code is regenerated." azure-arm: true -generate-fakes: false ``` ### Output diff --git a/pkg/ucp/api/v20231001preview/fake/zz_generated_apiversions_server.go b/pkg/ucp/api/v20231001preview/fake/zz_generated_apiversions_server.go new file mode 100644 index 0000000000..0050096780 --- /dev/null +++ b/pkg/ucp/api/v20231001preview/fake/zz_generated_apiversions_server.go @@ -0,0 +1,310 @@ +// Licensed under the Apache License, Version 2.0 . See LICENSE in the repository root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package fake + +import ( + "context" + "errors" + "fmt" + azfake "github.com/Azure/azure-sdk-for-go/sdk/azcore/fake" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/fake/server" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" + "net/http" + "net/url" + "regexp" +) + +// APIVersionsServer is a fake server for instances of the v20231001preview.APIVersionsClient type. +type APIVersionsServer struct{ + // BeginCreateOrUpdate is the fake for method APIVersionsClient.BeginCreateOrUpdate + // HTTP status codes to indicate success: http.StatusOK, http.StatusCreated + BeginCreateOrUpdate func(ctx context.Context, planeName string, resourceProviderName string, resourceTypeName string, apiVersionName string, resource v20231001preview.APIVersionResource, options *v20231001preview.APIVersionsClientBeginCreateOrUpdateOptions) (resp azfake.PollerResponder[v20231001preview.APIVersionsClientCreateOrUpdateResponse], errResp azfake.ErrorResponder) + + // BeginDelete is the fake for method APIVersionsClient.BeginDelete + // HTTP status codes to indicate success: http.StatusOK, http.StatusAccepted, http.StatusNoContent + BeginDelete func(ctx context.Context, planeName string, resourceProviderName string, resourceTypeName string, apiVersionName string, options *v20231001preview.APIVersionsClientBeginDeleteOptions) (resp azfake.PollerResponder[v20231001preview.APIVersionsClientDeleteResponse], errResp azfake.ErrorResponder) + + // Get is the fake for method APIVersionsClient.Get + // HTTP status codes to indicate success: http.StatusOK + Get func(ctx context.Context, planeName string, resourceProviderName string, resourceTypeName string, apiVersionName string, options *v20231001preview.APIVersionsClientGetOptions) (resp azfake.Responder[v20231001preview.APIVersionsClientGetResponse], errResp azfake.ErrorResponder) + + // NewListPager is the fake for method APIVersionsClient.NewListPager + // HTTP status codes to indicate success: http.StatusOK + NewListPager func(planeName string, resourceProviderName string, resourceTypeName string, options *v20231001preview.APIVersionsClientListOptions) (resp azfake.PagerResponder[v20231001preview.APIVersionsClientListResponse]) + +} + +// NewAPIVersionsServerTransport creates a new instance of APIVersionsServerTransport with the provided implementation. +// The returned APIVersionsServerTransport instance is connected to an instance of v20231001preview.APIVersionsClient via the +// azcore.ClientOptions.Transporter field in the client's constructor parameters. +func NewAPIVersionsServerTransport(srv *APIVersionsServer) *APIVersionsServerTransport { + return &APIVersionsServerTransport{ + srv: srv, + beginCreateOrUpdate: newTracker[azfake.PollerResponder[v20231001preview.APIVersionsClientCreateOrUpdateResponse]](), + beginDelete: newTracker[azfake.PollerResponder[v20231001preview.APIVersionsClientDeleteResponse]](), + newListPager: newTracker[azfake.PagerResponder[v20231001preview.APIVersionsClientListResponse]](), + } +} + +// APIVersionsServerTransport connects instances of v20231001preview.APIVersionsClient to instances of APIVersionsServer. +// Don't use this type directly, use NewAPIVersionsServerTransport instead. +type APIVersionsServerTransport struct { + srv *APIVersionsServer + beginCreateOrUpdate *tracker[azfake.PollerResponder[v20231001preview.APIVersionsClientCreateOrUpdateResponse]] + beginDelete *tracker[azfake.PollerResponder[v20231001preview.APIVersionsClientDeleteResponse]] + newListPager *tracker[azfake.PagerResponder[v20231001preview.APIVersionsClientListResponse]] +} + +// Do implements the policy.Transporter interface for APIVersionsServerTransport. +func (a *APIVersionsServerTransport) Do(req *http.Request) (*http.Response, error) { + rawMethod := req.Context().Value(runtime.CtxAPINameKey{}) + method, ok := rawMethod.(string) + if !ok { + return nil, nonRetriableError{errors.New("unable to dispatch request, missing value for CtxAPINameKey")} + } + + return a.dispatchToMethodFake(req, method) +} + +func (a *APIVersionsServerTransport) dispatchToMethodFake(req *http.Request, method string) (*http.Response, error) { + resultChan := make(chan result) + defer close(resultChan) + + go func() { + var intercepted bool + var res result + if apiVersionsServerTransportInterceptor != nil { + res.resp, res.err, intercepted = apiVersionsServerTransportInterceptor.Do(req) + } + if !intercepted { + switch method { + case "APIVersionsClient.BeginCreateOrUpdate": + res.resp, res.err = a.dispatchBeginCreateOrUpdate(req) + case "APIVersionsClient.BeginDelete": + res.resp, res.err = a.dispatchBeginDelete(req) + case "APIVersionsClient.Get": + res.resp, res.err = a.dispatchGet(req) + case "APIVersionsClient.NewListPager": + res.resp, res.err = a.dispatchNewListPager(req) + default: + res.err = fmt.Errorf("unhandled API %s", method) + } + + } + select { + case resultChan <- res: + case <-req.Context().Done(): + } + }() + + select { + case <-req.Context().Done(): + return nil, req.Context().Err() + case res := <-resultChan: + return res.resp, res.err + } +} + +func (a *APIVersionsServerTransport) dispatchBeginCreateOrUpdate(req *http.Request) (*http.Response, error) { + if a.srv.BeginCreateOrUpdate == nil { + return nil, &nonRetriableError{errors.New("fake for method BeginCreateOrUpdate not implemented")} + } + beginCreateOrUpdate := a.beginCreateOrUpdate.get(req) + if beginCreateOrUpdate == nil { + const regexStr = `/planes/radius/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/providers/System\.Resources/resourceproviders/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/resourcetypes/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/apiversions/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 4 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + body, err := server.UnmarshalRequestAsJSON[v20231001preview.APIVersionResource](req) + if err != nil { + return nil, err + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + resourceProviderNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("resourceProviderName")]) + if err != nil { + return nil, err + } + resourceTypeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("resourceTypeName")]) + if err != nil { + return nil, err + } + apiVersionNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("apiVersionName")]) + if err != nil { + return nil, err + } + respr, errRespr := a.srv.BeginCreateOrUpdate(req.Context(), planeNameParam, resourceProviderNameParam, resourceTypeNameParam, apiVersionNameParam, body, nil) + if respErr := server.GetError(errRespr, req); respErr != nil { + return nil, respErr + } + beginCreateOrUpdate = &respr + a.beginCreateOrUpdate.add(req, beginCreateOrUpdate) + } + + resp, err := server.PollerResponderNext(beginCreateOrUpdate, req) + if err != nil { + return nil, err + } + + if !contains([]int{http.StatusOK, http.StatusCreated}, resp.StatusCode) { + a.beginCreateOrUpdate.remove(req) + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK, http.StatusCreated", resp.StatusCode)} + } + if !server.PollerResponderMore(beginCreateOrUpdate) { + a.beginCreateOrUpdate.remove(req) + } + + return resp, nil +} + +func (a *APIVersionsServerTransport) dispatchBeginDelete(req *http.Request) (*http.Response, error) { + if a.srv.BeginDelete == nil { + return nil, &nonRetriableError{errors.New("fake for method BeginDelete not implemented")} + } + beginDelete := a.beginDelete.get(req) + if beginDelete == nil { + const regexStr = `/planes/radius/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/providers/System\.Resources/resourceproviders/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/resourcetypes/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/apiversions/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 4 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + resourceProviderNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("resourceProviderName")]) + if err != nil { + return nil, err + } + resourceTypeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("resourceTypeName")]) + if err != nil { + return nil, err + } + apiVersionNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("apiVersionName")]) + if err != nil { + return nil, err + } + respr, errRespr := a.srv.BeginDelete(req.Context(), planeNameParam, resourceProviderNameParam, resourceTypeNameParam, apiVersionNameParam, nil) + if respErr := server.GetError(errRespr, req); respErr != nil { + return nil, respErr + } + beginDelete = &respr + a.beginDelete.add(req, beginDelete) + } + + resp, err := server.PollerResponderNext(beginDelete, req) + if err != nil { + return nil, err + } + + if !contains([]int{http.StatusOK, http.StatusAccepted, http.StatusNoContent}, resp.StatusCode) { + a.beginDelete.remove(req) + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK, http.StatusAccepted, http.StatusNoContent", resp.StatusCode)} + } + if !server.PollerResponderMore(beginDelete) { + a.beginDelete.remove(req) + } + + return resp, nil +} + +func (a *APIVersionsServerTransport) dispatchGet(req *http.Request) (*http.Response, error) { + if a.srv.Get == nil { + return nil, &nonRetriableError{errors.New("fake for method Get not implemented")} + } + const regexStr = `/planes/radius/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/providers/System\.Resources/resourceproviders/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/resourcetypes/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/apiversions/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 4 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + resourceProviderNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("resourceProviderName")]) + if err != nil { + return nil, err + } + resourceTypeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("resourceTypeName")]) + if err != nil { + return nil, err + } + apiVersionNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("apiVersionName")]) + if err != nil { + return nil, err + } + respr, errRespr := a.srv.Get(req.Context(), planeNameParam, resourceProviderNameParam, resourceTypeNameParam, apiVersionNameParam, nil) + if respErr := server.GetError(errRespr, req); respErr != nil { + return nil, respErr + } + respContent := server.GetResponseContent(respr) + if !contains([]int{http.StatusOK}, respContent.HTTPStatus) { + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK", respContent.HTTPStatus)} + } + resp, err := server.MarshalResponseAsJSON(respContent, server.GetResponse(respr).APIVersionResource, req) + if err != nil { + return nil, err + } + return resp, nil +} + +func (a *APIVersionsServerTransport) dispatchNewListPager(req *http.Request) (*http.Response, error) { + if a.srv.NewListPager == nil { + return nil, &nonRetriableError{errors.New("fake for method NewListPager not implemented")} + } + newListPager := a.newListPager.get(req) + if newListPager == nil { + const regexStr = `/planes/radius/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/providers/System\.Resources/resourceproviders/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/resourcetypes/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/apiversions` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 3 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + resourceProviderNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("resourceProviderName")]) + if err != nil { + return nil, err + } + resourceTypeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("resourceTypeName")]) + if err != nil { + return nil, err + } +resp := a.srv.NewListPager(planeNameParam, resourceProviderNameParam, resourceTypeNameParam, nil) + newListPager = &resp + a.newListPager.add(req, newListPager) + server.PagerResponderInjectNextLinks(newListPager, req, func(page *v20231001preview.APIVersionsClientListResponse, createLink func() string) { + page.NextLink = to.Ptr(createLink()) + }) + } + resp, err := server.PagerResponderNext(newListPager, req) + if err != nil { + return nil, err + } + if !contains([]int{http.StatusOK}, resp.StatusCode) { + a.newListPager.remove(req) + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK", resp.StatusCode)} + } + if !server.PagerResponderMore(newListPager) { + a.newListPager.remove(req) + } + return resp, nil +} + +// set this to conditionally intercept incoming requests to APIVersionsServerTransport +var apiVersionsServerTransportInterceptor interface { + // Do returns true if the server transport should use the returned response/error + Do(*http.Request) (*http.Response, error, bool) +} diff --git a/pkg/ucp/api/v20231001preview/fake/zz_generated_awscredentials_server.go b/pkg/ucp/api/v20231001preview/fake/zz_generated_awscredentials_server.go new file mode 100644 index 0000000000..bad6241902 --- /dev/null +++ b/pkg/ucp/api/v20231001preview/fake/zz_generated_awscredentials_server.go @@ -0,0 +1,295 @@ +// Licensed under the Apache License, Version 2.0 . See LICENSE in the repository root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package fake + +import ( + "context" + "errors" + "fmt" + azfake "github.com/Azure/azure-sdk-for-go/sdk/azcore/fake" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/fake/server" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" + "net/http" + "net/url" + "regexp" +) + +// AwsCredentialsServer is a fake server for instances of the v20231001preview.AwsCredentialsClient type. +type AwsCredentialsServer struct{ + // CreateOrUpdate is the fake for method AwsCredentialsClient.CreateOrUpdate + // HTTP status codes to indicate success: http.StatusOK, http.StatusCreated + CreateOrUpdate func(ctx context.Context, planeName string, credentialName string, resource v20231001preview.AwsCredentialResource, options *v20231001preview.AwsCredentialsClientCreateOrUpdateOptions) (resp azfake.Responder[v20231001preview.AwsCredentialsClientCreateOrUpdateResponse], errResp azfake.ErrorResponder) + + // Delete is the fake for method AwsCredentialsClient.Delete + // HTTP status codes to indicate success: http.StatusOK, http.StatusNoContent + Delete func(ctx context.Context, planeName string, credentialName string, options *v20231001preview.AwsCredentialsClientDeleteOptions) (resp azfake.Responder[v20231001preview.AwsCredentialsClientDeleteResponse], errResp azfake.ErrorResponder) + + // Get is the fake for method AwsCredentialsClient.Get + // HTTP status codes to indicate success: http.StatusOK + Get func(ctx context.Context, planeName string, credentialName string, options *v20231001preview.AwsCredentialsClientGetOptions) (resp azfake.Responder[v20231001preview.AwsCredentialsClientGetResponse], errResp azfake.ErrorResponder) + + // NewListPager is the fake for method AwsCredentialsClient.NewListPager + // HTTP status codes to indicate success: http.StatusOK + NewListPager func(planeName string, options *v20231001preview.AwsCredentialsClientListOptions) (resp azfake.PagerResponder[v20231001preview.AwsCredentialsClientListResponse]) + + // Update is the fake for method AwsCredentialsClient.Update + // HTTP status codes to indicate success: http.StatusOK + Update func(ctx context.Context, planeName string, credentialName string, properties v20231001preview.AwsCredentialResourceTagsUpdate, options *v20231001preview.AwsCredentialsClientUpdateOptions) (resp azfake.Responder[v20231001preview.AwsCredentialsClientUpdateResponse], errResp azfake.ErrorResponder) + +} + +// NewAwsCredentialsServerTransport creates a new instance of AwsCredentialsServerTransport with the provided implementation. +// The returned AwsCredentialsServerTransport instance is connected to an instance of v20231001preview.AwsCredentialsClient via the +// azcore.ClientOptions.Transporter field in the client's constructor parameters. +func NewAwsCredentialsServerTransport(srv *AwsCredentialsServer) *AwsCredentialsServerTransport { + return &AwsCredentialsServerTransport{ + srv: srv, + newListPager: newTracker[azfake.PagerResponder[v20231001preview.AwsCredentialsClientListResponse]](), + } +} + +// AwsCredentialsServerTransport connects instances of v20231001preview.AwsCredentialsClient to instances of AwsCredentialsServer. +// Don't use this type directly, use NewAwsCredentialsServerTransport instead. +type AwsCredentialsServerTransport struct { + srv *AwsCredentialsServer + newListPager *tracker[azfake.PagerResponder[v20231001preview.AwsCredentialsClientListResponse]] +} + +// Do implements the policy.Transporter interface for AwsCredentialsServerTransport. +func (a *AwsCredentialsServerTransport) Do(req *http.Request) (*http.Response, error) { + rawMethod := req.Context().Value(runtime.CtxAPINameKey{}) + method, ok := rawMethod.(string) + if !ok { + return nil, nonRetriableError{errors.New("unable to dispatch request, missing value for CtxAPINameKey")} + } + + return a.dispatchToMethodFake(req, method) +} + +func (a *AwsCredentialsServerTransport) dispatchToMethodFake(req *http.Request, method string) (*http.Response, error) { + resultChan := make(chan result) + defer close(resultChan) + + go func() { + var intercepted bool + var res result + if awsCredentialsServerTransportInterceptor != nil { + res.resp, res.err, intercepted = awsCredentialsServerTransportInterceptor.Do(req) + } + if !intercepted { + switch method { + case "AwsCredentialsClient.CreateOrUpdate": + res.resp, res.err = a.dispatchCreateOrUpdate(req) + case "AwsCredentialsClient.Delete": + res.resp, res.err = a.dispatchDelete(req) + case "AwsCredentialsClient.Get": + res.resp, res.err = a.dispatchGet(req) + case "AwsCredentialsClient.NewListPager": + res.resp, res.err = a.dispatchNewListPager(req) + case "AwsCredentialsClient.Update": + res.resp, res.err = a.dispatchUpdate(req) + default: + res.err = fmt.Errorf("unhandled API %s", method) + } + + } + select { + case resultChan <- res: + case <-req.Context().Done(): + } + }() + + select { + case <-req.Context().Done(): + return nil, req.Context().Err() + case res := <-resultChan: + return res.resp, res.err + } +} + +func (a *AwsCredentialsServerTransport) dispatchCreateOrUpdate(req *http.Request) (*http.Response, error) { + if a.srv.CreateOrUpdate == nil { + return nil, &nonRetriableError{errors.New("fake for method CreateOrUpdate not implemented")} + } + const regexStr = `/planes/aws/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/providers/System\.AWS/credentials/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 2 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + body, err := server.UnmarshalRequestAsJSON[v20231001preview.AwsCredentialResource](req) + if err != nil { + return nil, err + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + credentialNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("credentialName")]) + if err != nil { + return nil, err + } + respr, errRespr := a.srv.CreateOrUpdate(req.Context(), planeNameParam, credentialNameParam, body, nil) + if respErr := server.GetError(errRespr, req); respErr != nil { + return nil, respErr + } + respContent := server.GetResponseContent(respr) + if !contains([]int{http.StatusOK, http.StatusCreated}, respContent.HTTPStatus) { + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK, http.StatusCreated", respContent.HTTPStatus)} + } + resp, err := server.MarshalResponseAsJSON(respContent, server.GetResponse(respr).AwsCredentialResource, req) + if err != nil { + return nil, err + } + return resp, nil +} + +func (a *AwsCredentialsServerTransport) dispatchDelete(req *http.Request) (*http.Response, error) { + if a.srv.Delete == nil { + return nil, &nonRetriableError{errors.New("fake for method Delete not implemented")} + } + const regexStr = `/planes/aws/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/providers/System\.AWS/credentials/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 2 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + credentialNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("credentialName")]) + if err != nil { + return nil, err + } + respr, errRespr := a.srv.Delete(req.Context(), planeNameParam, credentialNameParam, nil) + if respErr := server.GetError(errRespr, req); respErr != nil { + return nil, respErr + } + respContent := server.GetResponseContent(respr) + if !contains([]int{http.StatusOK, http.StatusNoContent}, respContent.HTTPStatus) { + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK, http.StatusNoContent", respContent.HTTPStatus)} + } + resp, err := server.NewResponse(respContent, req, nil) + if err != nil { + return nil, err + } + return resp, nil +} + +func (a *AwsCredentialsServerTransport) dispatchGet(req *http.Request) (*http.Response, error) { + if a.srv.Get == nil { + return nil, &nonRetriableError{errors.New("fake for method Get not implemented")} + } + const regexStr = `/planes/aws/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/providers/System\.AWS/credentials/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 2 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + credentialNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("credentialName")]) + if err != nil { + return nil, err + } + respr, errRespr := a.srv.Get(req.Context(), planeNameParam, credentialNameParam, nil) + if respErr := server.GetError(errRespr, req); respErr != nil { + return nil, respErr + } + respContent := server.GetResponseContent(respr) + if !contains([]int{http.StatusOK}, respContent.HTTPStatus) { + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK", respContent.HTTPStatus)} + } + resp, err := server.MarshalResponseAsJSON(respContent, server.GetResponse(respr).AwsCredentialResource, req) + if err != nil { + return nil, err + } + return resp, nil +} + +func (a *AwsCredentialsServerTransport) dispatchNewListPager(req *http.Request) (*http.Response, error) { + if a.srv.NewListPager == nil { + return nil, &nonRetriableError{errors.New("fake for method NewListPager not implemented")} + } + newListPager := a.newListPager.get(req) + if newListPager == nil { + const regexStr = `/planes/aws/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/providers/System\.AWS/credentials` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 1 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } +resp := a.srv.NewListPager(planeNameParam, nil) + newListPager = &resp + a.newListPager.add(req, newListPager) + server.PagerResponderInjectNextLinks(newListPager, req, func(page *v20231001preview.AwsCredentialsClientListResponse, createLink func() string) { + page.NextLink = to.Ptr(createLink()) + }) + } + resp, err := server.PagerResponderNext(newListPager, req) + if err != nil { + return nil, err + } + if !contains([]int{http.StatusOK}, resp.StatusCode) { + a.newListPager.remove(req) + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK", resp.StatusCode)} + } + if !server.PagerResponderMore(newListPager) { + a.newListPager.remove(req) + } + return resp, nil +} + +func (a *AwsCredentialsServerTransport) dispatchUpdate(req *http.Request) (*http.Response, error) { + if a.srv.Update == nil { + return nil, &nonRetriableError{errors.New("fake for method Update not implemented")} + } + const regexStr = `/planes/aws/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/providers/System\.AWS/credentials/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 2 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + body, err := server.UnmarshalRequestAsJSON[v20231001preview.AwsCredentialResourceTagsUpdate](req) + if err != nil { + return nil, err + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + credentialNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("credentialName")]) + if err != nil { + return nil, err + } + respr, errRespr := a.srv.Update(req.Context(), planeNameParam, credentialNameParam, body, nil) + if respErr := server.GetError(errRespr, req); respErr != nil { + return nil, respErr + } + respContent := server.GetResponseContent(respr) + if !contains([]int{http.StatusOK}, respContent.HTTPStatus) { + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK", respContent.HTTPStatus)} + } + resp, err := server.MarshalResponseAsJSON(respContent, server.GetResponse(respr).AwsCredentialResource, req) + if err != nil { + return nil, err + } + return resp, nil +} + +// set this to conditionally intercept incoming requests to AwsCredentialsServerTransport +var awsCredentialsServerTransportInterceptor interface { + // Do returns true if the server transport should use the returned response/error + Do(*http.Request) (*http.Response, error, bool) +} diff --git a/pkg/ucp/api/v20231001preview/fake/zz_generated_awsplanes_server.go b/pkg/ucp/api/v20231001preview/fake/zz_generated_awsplanes_server.go new file mode 100644 index 0000000000..ccb43ad22e --- /dev/null +++ b/pkg/ucp/api/v20231001preview/fake/zz_generated_awsplanes_server.go @@ -0,0 +1,308 @@ +// Licensed under the Apache License, Version 2.0 . See LICENSE in the repository root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package fake + +import ( + "context" + "errors" + "fmt" + azfake "github.com/Azure/azure-sdk-for-go/sdk/azcore/fake" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/fake/server" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" + "net/http" + "net/url" + "regexp" +) + +// AwsPlanesServer is a fake server for instances of the v20231001preview.AwsPlanesClient type. +type AwsPlanesServer struct{ + // BeginCreateOrUpdate is the fake for method AwsPlanesClient.BeginCreateOrUpdate + // HTTP status codes to indicate success: http.StatusOK, http.StatusCreated + BeginCreateOrUpdate func(ctx context.Context, planeName string, resource v20231001preview.AwsPlaneResource, options *v20231001preview.AwsPlanesClientBeginCreateOrUpdateOptions) (resp azfake.PollerResponder[v20231001preview.AwsPlanesClientCreateOrUpdateResponse], errResp azfake.ErrorResponder) + + // BeginDelete is the fake for method AwsPlanesClient.BeginDelete + // HTTP status codes to indicate success: http.StatusOK, http.StatusAccepted, http.StatusNoContent + BeginDelete func(ctx context.Context, planeName string, options *v20231001preview.AwsPlanesClientBeginDeleteOptions) (resp azfake.PollerResponder[v20231001preview.AwsPlanesClientDeleteResponse], errResp azfake.ErrorResponder) + + // Get is the fake for method AwsPlanesClient.Get + // HTTP status codes to indicate success: http.StatusOK + Get func(ctx context.Context, planeName string, options *v20231001preview.AwsPlanesClientGetOptions) (resp azfake.Responder[v20231001preview.AwsPlanesClientGetResponse], errResp azfake.ErrorResponder) + + // NewListPager is the fake for method AwsPlanesClient.NewListPager + // HTTP status codes to indicate success: http.StatusOK + NewListPager func(options *v20231001preview.AwsPlanesClientListOptions) (resp azfake.PagerResponder[v20231001preview.AwsPlanesClientListResponse]) + + // BeginUpdate is the fake for method AwsPlanesClient.BeginUpdate + // HTTP status codes to indicate success: http.StatusOK, http.StatusAccepted + BeginUpdate func(ctx context.Context, planeName string, properties v20231001preview.AwsPlaneResourceTagsUpdate, options *v20231001preview.AwsPlanesClientBeginUpdateOptions) (resp azfake.PollerResponder[v20231001preview.AwsPlanesClientUpdateResponse], errResp azfake.ErrorResponder) + +} + +// NewAwsPlanesServerTransport creates a new instance of AwsPlanesServerTransport with the provided implementation. +// The returned AwsPlanesServerTransport instance is connected to an instance of v20231001preview.AwsPlanesClient via the +// azcore.ClientOptions.Transporter field in the client's constructor parameters. +func NewAwsPlanesServerTransport(srv *AwsPlanesServer) *AwsPlanesServerTransport { + return &AwsPlanesServerTransport{ + srv: srv, + beginCreateOrUpdate: newTracker[azfake.PollerResponder[v20231001preview.AwsPlanesClientCreateOrUpdateResponse]](), + beginDelete: newTracker[azfake.PollerResponder[v20231001preview.AwsPlanesClientDeleteResponse]](), + newListPager: newTracker[azfake.PagerResponder[v20231001preview.AwsPlanesClientListResponse]](), + beginUpdate: newTracker[azfake.PollerResponder[v20231001preview.AwsPlanesClientUpdateResponse]](), + } +} + +// AwsPlanesServerTransport connects instances of v20231001preview.AwsPlanesClient to instances of AwsPlanesServer. +// Don't use this type directly, use NewAwsPlanesServerTransport instead. +type AwsPlanesServerTransport struct { + srv *AwsPlanesServer + beginCreateOrUpdate *tracker[azfake.PollerResponder[v20231001preview.AwsPlanesClientCreateOrUpdateResponse]] + beginDelete *tracker[azfake.PollerResponder[v20231001preview.AwsPlanesClientDeleteResponse]] + newListPager *tracker[azfake.PagerResponder[v20231001preview.AwsPlanesClientListResponse]] + beginUpdate *tracker[azfake.PollerResponder[v20231001preview.AwsPlanesClientUpdateResponse]] +} + +// Do implements the policy.Transporter interface for AwsPlanesServerTransport. +func (a *AwsPlanesServerTransport) Do(req *http.Request) (*http.Response, error) { + rawMethod := req.Context().Value(runtime.CtxAPINameKey{}) + method, ok := rawMethod.(string) + if !ok { + return nil, nonRetriableError{errors.New("unable to dispatch request, missing value for CtxAPINameKey")} + } + + return a.dispatchToMethodFake(req, method) +} + +func (a *AwsPlanesServerTransport) dispatchToMethodFake(req *http.Request, method string) (*http.Response, error) { + resultChan := make(chan result) + defer close(resultChan) + + go func() { + var intercepted bool + var res result + if awsPlanesServerTransportInterceptor != nil { + res.resp, res.err, intercepted = awsPlanesServerTransportInterceptor.Do(req) + } + if !intercepted { + switch method { + case "AwsPlanesClient.BeginCreateOrUpdate": + res.resp, res.err = a.dispatchBeginCreateOrUpdate(req) + case "AwsPlanesClient.BeginDelete": + res.resp, res.err = a.dispatchBeginDelete(req) + case "AwsPlanesClient.Get": + res.resp, res.err = a.dispatchGet(req) + case "AwsPlanesClient.NewListPager": + res.resp, res.err = a.dispatchNewListPager(req) + case "AwsPlanesClient.BeginUpdate": + res.resp, res.err = a.dispatchBeginUpdate(req) + default: + res.err = fmt.Errorf("unhandled API %s", method) + } + + } + select { + case resultChan <- res: + case <-req.Context().Done(): + } + }() + + select { + case <-req.Context().Done(): + return nil, req.Context().Err() + case res := <-resultChan: + return res.resp, res.err + } +} + +func (a *AwsPlanesServerTransport) dispatchBeginCreateOrUpdate(req *http.Request) (*http.Response, error) { + if a.srv.BeginCreateOrUpdate == nil { + return nil, &nonRetriableError{errors.New("fake for method BeginCreateOrUpdate not implemented")} + } + beginCreateOrUpdate := a.beginCreateOrUpdate.get(req) + if beginCreateOrUpdate == nil { + const regexStr = `/planes/aws/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 1 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + body, err := server.UnmarshalRequestAsJSON[v20231001preview.AwsPlaneResource](req) + if err != nil { + return nil, err + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + respr, errRespr := a.srv.BeginCreateOrUpdate(req.Context(), planeNameParam, body, nil) + if respErr := server.GetError(errRespr, req); respErr != nil { + return nil, respErr + } + beginCreateOrUpdate = &respr + a.beginCreateOrUpdate.add(req, beginCreateOrUpdate) + } + + resp, err := server.PollerResponderNext(beginCreateOrUpdate, req) + if err != nil { + return nil, err + } + + if !contains([]int{http.StatusOK, http.StatusCreated}, resp.StatusCode) { + a.beginCreateOrUpdate.remove(req) + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK, http.StatusCreated", resp.StatusCode)} + } + if !server.PollerResponderMore(beginCreateOrUpdate) { + a.beginCreateOrUpdate.remove(req) + } + + return resp, nil +} + +func (a *AwsPlanesServerTransport) dispatchBeginDelete(req *http.Request) (*http.Response, error) { + if a.srv.BeginDelete == nil { + return nil, &nonRetriableError{errors.New("fake for method BeginDelete not implemented")} + } + beginDelete := a.beginDelete.get(req) + if beginDelete == nil { + const regexStr = `/planes/aws/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 1 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + respr, errRespr := a.srv.BeginDelete(req.Context(), planeNameParam, nil) + if respErr := server.GetError(errRespr, req); respErr != nil { + return nil, respErr + } + beginDelete = &respr + a.beginDelete.add(req, beginDelete) + } + + resp, err := server.PollerResponderNext(beginDelete, req) + if err != nil { + return nil, err + } + + if !contains([]int{http.StatusOK, http.StatusAccepted, http.StatusNoContent}, resp.StatusCode) { + a.beginDelete.remove(req) + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK, http.StatusAccepted, http.StatusNoContent", resp.StatusCode)} + } + if !server.PollerResponderMore(beginDelete) { + a.beginDelete.remove(req) + } + + return resp, nil +} + +func (a *AwsPlanesServerTransport) dispatchGet(req *http.Request) (*http.Response, error) { + if a.srv.Get == nil { + return nil, &nonRetriableError{errors.New("fake for method Get not implemented")} + } + const regexStr = `/planes/aws/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 1 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + respr, errRespr := a.srv.Get(req.Context(), planeNameParam, nil) + if respErr := server.GetError(errRespr, req); respErr != nil { + return nil, respErr + } + respContent := server.GetResponseContent(respr) + if !contains([]int{http.StatusOK}, respContent.HTTPStatus) { + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK", respContent.HTTPStatus)} + } + resp, err := server.MarshalResponseAsJSON(respContent, server.GetResponse(respr).AwsPlaneResource, req) + if err != nil { + return nil, err + } + return resp, nil +} + +func (a *AwsPlanesServerTransport) dispatchNewListPager(req *http.Request) (*http.Response, error) { + if a.srv.NewListPager == nil { + return nil, &nonRetriableError{errors.New("fake for method NewListPager not implemented")} + } + newListPager := a.newListPager.get(req) + if newListPager == nil { +resp := a.srv.NewListPager(nil) + newListPager = &resp + a.newListPager.add(req, newListPager) + server.PagerResponderInjectNextLinks(newListPager, req, func(page *v20231001preview.AwsPlanesClientListResponse, createLink func() string) { + page.NextLink = to.Ptr(createLink()) + }) + } + resp, err := server.PagerResponderNext(newListPager, req) + if err != nil { + return nil, err + } + if !contains([]int{http.StatusOK}, resp.StatusCode) { + a.newListPager.remove(req) + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK", resp.StatusCode)} + } + if !server.PagerResponderMore(newListPager) { + a.newListPager.remove(req) + } + return resp, nil +} + +func (a *AwsPlanesServerTransport) dispatchBeginUpdate(req *http.Request) (*http.Response, error) { + if a.srv.BeginUpdate == nil { + return nil, &nonRetriableError{errors.New("fake for method BeginUpdate not implemented")} + } + beginUpdate := a.beginUpdate.get(req) + if beginUpdate == nil { + const regexStr = `/planes/aws/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 1 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + body, err := server.UnmarshalRequestAsJSON[v20231001preview.AwsPlaneResourceTagsUpdate](req) + if err != nil { + return nil, err + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + respr, errRespr := a.srv.BeginUpdate(req.Context(), planeNameParam, body, nil) + if respErr := server.GetError(errRespr, req); respErr != nil { + return nil, respErr + } + beginUpdate = &respr + a.beginUpdate.add(req, beginUpdate) + } + + resp, err := server.PollerResponderNext(beginUpdate, req) + if err != nil { + return nil, err + } + + if !contains([]int{http.StatusOK, http.StatusAccepted}, resp.StatusCode) { + a.beginUpdate.remove(req) + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK, http.StatusAccepted", resp.StatusCode)} + } + if !server.PollerResponderMore(beginUpdate) { + a.beginUpdate.remove(req) + } + + return resp, nil +} + +// set this to conditionally intercept incoming requests to AwsPlanesServerTransport +var awsPlanesServerTransportInterceptor interface { + // Do returns true if the server transport should use the returned response/error + Do(*http.Request) (*http.Response, error, bool) +} diff --git a/pkg/ucp/api/v20231001preview/fake/zz_generated_azurecredentials_server.go b/pkg/ucp/api/v20231001preview/fake/zz_generated_azurecredentials_server.go new file mode 100644 index 0000000000..e4151d61d5 --- /dev/null +++ b/pkg/ucp/api/v20231001preview/fake/zz_generated_azurecredentials_server.go @@ -0,0 +1,295 @@ +// Licensed under the Apache License, Version 2.0 . See LICENSE in the repository root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package fake + +import ( + "context" + "errors" + "fmt" + azfake "github.com/Azure/azure-sdk-for-go/sdk/azcore/fake" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/fake/server" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" + "net/http" + "net/url" + "regexp" +) + +// AzureCredentialsServer is a fake server for instances of the v20231001preview.AzureCredentialsClient type. +type AzureCredentialsServer struct{ + // CreateOrUpdate is the fake for method AzureCredentialsClient.CreateOrUpdate + // HTTP status codes to indicate success: http.StatusOK, http.StatusCreated + CreateOrUpdate func(ctx context.Context, planeName string, credentialName string, resource v20231001preview.AzureCredentialResource, options *v20231001preview.AzureCredentialsClientCreateOrUpdateOptions) (resp azfake.Responder[v20231001preview.AzureCredentialsClientCreateOrUpdateResponse], errResp azfake.ErrorResponder) + + // Delete is the fake for method AzureCredentialsClient.Delete + // HTTP status codes to indicate success: http.StatusOK, http.StatusNoContent + Delete func(ctx context.Context, planeName string, credentialName string, options *v20231001preview.AzureCredentialsClientDeleteOptions) (resp azfake.Responder[v20231001preview.AzureCredentialsClientDeleteResponse], errResp azfake.ErrorResponder) + + // Get is the fake for method AzureCredentialsClient.Get + // HTTP status codes to indicate success: http.StatusOK + Get func(ctx context.Context, planeName string, credentialName string, options *v20231001preview.AzureCredentialsClientGetOptions) (resp azfake.Responder[v20231001preview.AzureCredentialsClientGetResponse], errResp azfake.ErrorResponder) + + // NewListPager is the fake for method AzureCredentialsClient.NewListPager + // HTTP status codes to indicate success: http.StatusOK + NewListPager func(planeName string, options *v20231001preview.AzureCredentialsClientListOptions) (resp azfake.PagerResponder[v20231001preview.AzureCredentialsClientListResponse]) + + // Update is the fake for method AzureCredentialsClient.Update + // HTTP status codes to indicate success: http.StatusOK + Update func(ctx context.Context, planeName string, credentialName string, properties v20231001preview.AzureCredentialResourceTagsUpdate, options *v20231001preview.AzureCredentialsClientUpdateOptions) (resp azfake.Responder[v20231001preview.AzureCredentialsClientUpdateResponse], errResp azfake.ErrorResponder) + +} + +// NewAzureCredentialsServerTransport creates a new instance of AzureCredentialsServerTransport with the provided implementation. +// The returned AzureCredentialsServerTransport instance is connected to an instance of v20231001preview.AzureCredentialsClient via the +// azcore.ClientOptions.Transporter field in the client's constructor parameters. +func NewAzureCredentialsServerTransport(srv *AzureCredentialsServer) *AzureCredentialsServerTransport { + return &AzureCredentialsServerTransport{ + srv: srv, + newListPager: newTracker[azfake.PagerResponder[v20231001preview.AzureCredentialsClientListResponse]](), + } +} + +// AzureCredentialsServerTransport connects instances of v20231001preview.AzureCredentialsClient to instances of AzureCredentialsServer. +// Don't use this type directly, use NewAzureCredentialsServerTransport instead. +type AzureCredentialsServerTransport struct { + srv *AzureCredentialsServer + newListPager *tracker[azfake.PagerResponder[v20231001preview.AzureCredentialsClientListResponse]] +} + +// Do implements the policy.Transporter interface for AzureCredentialsServerTransport. +func (a *AzureCredentialsServerTransport) Do(req *http.Request) (*http.Response, error) { + rawMethod := req.Context().Value(runtime.CtxAPINameKey{}) + method, ok := rawMethod.(string) + if !ok { + return nil, nonRetriableError{errors.New("unable to dispatch request, missing value for CtxAPINameKey")} + } + + return a.dispatchToMethodFake(req, method) +} + +func (a *AzureCredentialsServerTransport) dispatchToMethodFake(req *http.Request, method string) (*http.Response, error) { + resultChan := make(chan result) + defer close(resultChan) + + go func() { + var intercepted bool + var res result + if azureCredentialsServerTransportInterceptor != nil { + res.resp, res.err, intercepted = azureCredentialsServerTransportInterceptor.Do(req) + } + if !intercepted { + switch method { + case "AzureCredentialsClient.CreateOrUpdate": + res.resp, res.err = a.dispatchCreateOrUpdate(req) + case "AzureCredentialsClient.Delete": + res.resp, res.err = a.dispatchDelete(req) + case "AzureCredentialsClient.Get": + res.resp, res.err = a.dispatchGet(req) + case "AzureCredentialsClient.NewListPager": + res.resp, res.err = a.dispatchNewListPager(req) + case "AzureCredentialsClient.Update": + res.resp, res.err = a.dispatchUpdate(req) + default: + res.err = fmt.Errorf("unhandled API %s", method) + } + + } + select { + case resultChan <- res: + case <-req.Context().Done(): + } + }() + + select { + case <-req.Context().Done(): + return nil, req.Context().Err() + case res := <-resultChan: + return res.resp, res.err + } +} + +func (a *AzureCredentialsServerTransport) dispatchCreateOrUpdate(req *http.Request) (*http.Response, error) { + if a.srv.CreateOrUpdate == nil { + return nil, &nonRetriableError{errors.New("fake for method CreateOrUpdate not implemented")} + } + const regexStr = `/planes/azure/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/providers/System\.Azure/credentials/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 2 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + body, err := server.UnmarshalRequestAsJSON[v20231001preview.AzureCredentialResource](req) + if err != nil { + return nil, err + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + credentialNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("credentialName")]) + if err != nil { + return nil, err + } + respr, errRespr := a.srv.CreateOrUpdate(req.Context(), planeNameParam, credentialNameParam, body, nil) + if respErr := server.GetError(errRespr, req); respErr != nil { + return nil, respErr + } + respContent := server.GetResponseContent(respr) + if !contains([]int{http.StatusOK, http.StatusCreated}, respContent.HTTPStatus) { + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK, http.StatusCreated", respContent.HTTPStatus)} + } + resp, err := server.MarshalResponseAsJSON(respContent, server.GetResponse(respr).AzureCredentialResource, req) + if err != nil { + return nil, err + } + return resp, nil +} + +func (a *AzureCredentialsServerTransport) dispatchDelete(req *http.Request) (*http.Response, error) { + if a.srv.Delete == nil { + return nil, &nonRetriableError{errors.New("fake for method Delete not implemented")} + } + const regexStr = `/planes/azure/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/providers/System\.Azure/credentials/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 2 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + credentialNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("credentialName")]) + if err != nil { + return nil, err + } + respr, errRespr := a.srv.Delete(req.Context(), planeNameParam, credentialNameParam, nil) + if respErr := server.GetError(errRespr, req); respErr != nil { + return nil, respErr + } + respContent := server.GetResponseContent(respr) + if !contains([]int{http.StatusOK, http.StatusNoContent}, respContent.HTTPStatus) { + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK, http.StatusNoContent", respContent.HTTPStatus)} + } + resp, err := server.NewResponse(respContent, req, nil) + if err != nil { + return nil, err + } + return resp, nil +} + +func (a *AzureCredentialsServerTransport) dispatchGet(req *http.Request) (*http.Response, error) { + if a.srv.Get == nil { + return nil, &nonRetriableError{errors.New("fake for method Get not implemented")} + } + const regexStr = `/planes/azure/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/providers/System\.Azure/credentials/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 2 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + credentialNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("credentialName")]) + if err != nil { + return nil, err + } + respr, errRespr := a.srv.Get(req.Context(), planeNameParam, credentialNameParam, nil) + if respErr := server.GetError(errRespr, req); respErr != nil { + return nil, respErr + } + respContent := server.GetResponseContent(respr) + if !contains([]int{http.StatusOK}, respContent.HTTPStatus) { + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK", respContent.HTTPStatus)} + } + resp, err := server.MarshalResponseAsJSON(respContent, server.GetResponse(respr).AzureCredentialResource, req) + if err != nil { + return nil, err + } + return resp, nil +} + +func (a *AzureCredentialsServerTransport) dispatchNewListPager(req *http.Request) (*http.Response, error) { + if a.srv.NewListPager == nil { + return nil, &nonRetriableError{errors.New("fake for method NewListPager not implemented")} + } + newListPager := a.newListPager.get(req) + if newListPager == nil { + const regexStr = `/planes/azure/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/providers/System\.Azure/credentials` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 1 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } +resp := a.srv.NewListPager(planeNameParam, nil) + newListPager = &resp + a.newListPager.add(req, newListPager) + server.PagerResponderInjectNextLinks(newListPager, req, func(page *v20231001preview.AzureCredentialsClientListResponse, createLink func() string) { + page.NextLink = to.Ptr(createLink()) + }) + } + resp, err := server.PagerResponderNext(newListPager, req) + if err != nil { + return nil, err + } + if !contains([]int{http.StatusOK}, resp.StatusCode) { + a.newListPager.remove(req) + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK", resp.StatusCode)} + } + if !server.PagerResponderMore(newListPager) { + a.newListPager.remove(req) + } + return resp, nil +} + +func (a *AzureCredentialsServerTransport) dispatchUpdate(req *http.Request) (*http.Response, error) { + if a.srv.Update == nil { + return nil, &nonRetriableError{errors.New("fake for method Update not implemented")} + } + const regexStr = `/planes/azure/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/providers/System\.Azure/credentials/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 2 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + body, err := server.UnmarshalRequestAsJSON[v20231001preview.AzureCredentialResourceTagsUpdate](req) + if err != nil { + return nil, err + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + credentialNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("credentialName")]) + if err != nil { + return nil, err + } + respr, errRespr := a.srv.Update(req.Context(), planeNameParam, credentialNameParam, body, nil) + if respErr := server.GetError(errRespr, req); respErr != nil { + return nil, respErr + } + respContent := server.GetResponseContent(respr) + if !contains([]int{http.StatusOK}, respContent.HTTPStatus) { + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK", respContent.HTTPStatus)} + } + resp, err := server.MarshalResponseAsJSON(respContent, server.GetResponse(respr).AzureCredentialResource, req) + if err != nil { + return nil, err + } + return resp, nil +} + +// set this to conditionally intercept incoming requests to AzureCredentialsServerTransport +var azureCredentialsServerTransportInterceptor interface { + // Do returns true if the server transport should use the returned response/error + Do(*http.Request) (*http.Response, error, bool) +} diff --git a/pkg/ucp/api/v20231001preview/fake/zz_generated_azureplanes_server.go b/pkg/ucp/api/v20231001preview/fake/zz_generated_azureplanes_server.go new file mode 100644 index 0000000000..13f7a03c9a --- /dev/null +++ b/pkg/ucp/api/v20231001preview/fake/zz_generated_azureplanes_server.go @@ -0,0 +1,308 @@ +// Licensed under the Apache License, Version 2.0 . See LICENSE in the repository root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package fake + +import ( + "context" + "errors" + "fmt" + azfake "github.com/Azure/azure-sdk-for-go/sdk/azcore/fake" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/fake/server" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" + "net/http" + "net/url" + "regexp" +) + +// AzurePlanesServer is a fake server for instances of the v20231001preview.AzurePlanesClient type. +type AzurePlanesServer struct{ + // BeginCreateOrUpdate is the fake for method AzurePlanesClient.BeginCreateOrUpdate + // HTTP status codes to indicate success: http.StatusOK, http.StatusCreated + BeginCreateOrUpdate func(ctx context.Context, planeName string, resource v20231001preview.AzurePlaneResource, options *v20231001preview.AzurePlanesClientBeginCreateOrUpdateOptions) (resp azfake.PollerResponder[v20231001preview.AzurePlanesClientCreateOrUpdateResponse], errResp azfake.ErrorResponder) + + // BeginDelete is the fake for method AzurePlanesClient.BeginDelete + // HTTP status codes to indicate success: http.StatusOK, http.StatusAccepted, http.StatusNoContent + BeginDelete func(ctx context.Context, planeName string, options *v20231001preview.AzurePlanesClientBeginDeleteOptions) (resp azfake.PollerResponder[v20231001preview.AzurePlanesClientDeleteResponse], errResp azfake.ErrorResponder) + + // Get is the fake for method AzurePlanesClient.Get + // HTTP status codes to indicate success: http.StatusOK + Get func(ctx context.Context, planeName string, options *v20231001preview.AzurePlanesClientGetOptions) (resp azfake.Responder[v20231001preview.AzurePlanesClientGetResponse], errResp azfake.ErrorResponder) + + // NewListPager is the fake for method AzurePlanesClient.NewListPager + // HTTP status codes to indicate success: http.StatusOK + NewListPager func(options *v20231001preview.AzurePlanesClientListOptions) (resp azfake.PagerResponder[v20231001preview.AzurePlanesClientListResponse]) + + // BeginUpdate is the fake for method AzurePlanesClient.BeginUpdate + // HTTP status codes to indicate success: http.StatusOK, http.StatusAccepted + BeginUpdate func(ctx context.Context, planeName string, properties v20231001preview.AzurePlaneResourceTagsUpdate, options *v20231001preview.AzurePlanesClientBeginUpdateOptions) (resp azfake.PollerResponder[v20231001preview.AzurePlanesClientUpdateResponse], errResp azfake.ErrorResponder) + +} + +// NewAzurePlanesServerTransport creates a new instance of AzurePlanesServerTransport with the provided implementation. +// The returned AzurePlanesServerTransport instance is connected to an instance of v20231001preview.AzurePlanesClient via the +// azcore.ClientOptions.Transporter field in the client's constructor parameters. +func NewAzurePlanesServerTransport(srv *AzurePlanesServer) *AzurePlanesServerTransport { + return &AzurePlanesServerTransport{ + srv: srv, + beginCreateOrUpdate: newTracker[azfake.PollerResponder[v20231001preview.AzurePlanesClientCreateOrUpdateResponse]](), + beginDelete: newTracker[azfake.PollerResponder[v20231001preview.AzurePlanesClientDeleteResponse]](), + newListPager: newTracker[azfake.PagerResponder[v20231001preview.AzurePlanesClientListResponse]](), + beginUpdate: newTracker[azfake.PollerResponder[v20231001preview.AzurePlanesClientUpdateResponse]](), + } +} + +// AzurePlanesServerTransport connects instances of v20231001preview.AzurePlanesClient to instances of AzurePlanesServer. +// Don't use this type directly, use NewAzurePlanesServerTransport instead. +type AzurePlanesServerTransport struct { + srv *AzurePlanesServer + beginCreateOrUpdate *tracker[azfake.PollerResponder[v20231001preview.AzurePlanesClientCreateOrUpdateResponse]] + beginDelete *tracker[azfake.PollerResponder[v20231001preview.AzurePlanesClientDeleteResponse]] + newListPager *tracker[azfake.PagerResponder[v20231001preview.AzurePlanesClientListResponse]] + beginUpdate *tracker[azfake.PollerResponder[v20231001preview.AzurePlanesClientUpdateResponse]] +} + +// Do implements the policy.Transporter interface for AzurePlanesServerTransport. +func (a *AzurePlanesServerTransport) Do(req *http.Request) (*http.Response, error) { + rawMethod := req.Context().Value(runtime.CtxAPINameKey{}) + method, ok := rawMethod.(string) + if !ok { + return nil, nonRetriableError{errors.New("unable to dispatch request, missing value for CtxAPINameKey")} + } + + return a.dispatchToMethodFake(req, method) +} + +func (a *AzurePlanesServerTransport) dispatchToMethodFake(req *http.Request, method string) (*http.Response, error) { + resultChan := make(chan result) + defer close(resultChan) + + go func() { + var intercepted bool + var res result + if azurePlanesServerTransportInterceptor != nil { + res.resp, res.err, intercepted = azurePlanesServerTransportInterceptor.Do(req) + } + if !intercepted { + switch method { + case "AzurePlanesClient.BeginCreateOrUpdate": + res.resp, res.err = a.dispatchBeginCreateOrUpdate(req) + case "AzurePlanesClient.BeginDelete": + res.resp, res.err = a.dispatchBeginDelete(req) + case "AzurePlanesClient.Get": + res.resp, res.err = a.dispatchGet(req) + case "AzurePlanesClient.NewListPager": + res.resp, res.err = a.dispatchNewListPager(req) + case "AzurePlanesClient.BeginUpdate": + res.resp, res.err = a.dispatchBeginUpdate(req) + default: + res.err = fmt.Errorf("unhandled API %s", method) + } + + } + select { + case resultChan <- res: + case <-req.Context().Done(): + } + }() + + select { + case <-req.Context().Done(): + return nil, req.Context().Err() + case res := <-resultChan: + return res.resp, res.err + } +} + +func (a *AzurePlanesServerTransport) dispatchBeginCreateOrUpdate(req *http.Request) (*http.Response, error) { + if a.srv.BeginCreateOrUpdate == nil { + return nil, &nonRetriableError{errors.New("fake for method BeginCreateOrUpdate not implemented")} + } + beginCreateOrUpdate := a.beginCreateOrUpdate.get(req) + if beginCreateOrUpdate == nil { + const regexStr = `/planes/azure/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 1 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + body, err := server.UnmarshalRequestAsJSON[v20231001preview.AzurePlaneResource](req) + if err != nil { + return nil, err + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + respr, errRespr := a.srv.BeginCreateOrUpdate(req.Context(), planeNameParam, body, nil) + if respErr := server.GetError(errRespr, req); respErr != nil { + return nil, respErr + } + beginCreateOrUpdate = &respr + a.beginCreateOrUpdate.add(req, beginCreateOrUpdate) + } + + resp, err := server.PollerResponderNext(beginCreateOrUpdate, req) + if err != nil { + return nil, err + } + + if !contains([]int{http.StatusOK, http.StatusCreated}, resp.StatusCode) { + a.beginCreateOrUpdate.remove(req) + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK, http.StatusCreated", resp.StatusCode)} + } + if !server.PollerResponderMore(beginCreateOrUpdate) { + a.beginCreateOrUpdate.remove(req) + } + + return resp, nil +} + +func (a *AzurePlanesServerTransport) dispatchBeginDelete(req *http.Request) (*http.Response, error) { + if a.srv.BeginDelete == nil { + return nil, &nonRetriableError{errors.New("fake for method BeginDelete not implemented")} + } + beginDelete := a.beginDelete.get(req) + if beginDelete == nil { + const regexStr = `/planes/azure/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 1 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + respr, errRespr := a.srv.BeginDelete(req.Context(), planeNameParam, nil) + if respErr := server.GetError(errRespr, req); respErr != nil { + return nil, respErr + } + beginDelete = &respr + a.beginDelete.add(req, beginDelete) + } + + resp, err := server.PollerResponderNext(beginDelete, req) + if err != nil { + return nil, err + } + + if !contains([]int{http.StatusOK, http.StatusAccepted, http.StatusNoContent}, resp.StatusCode) { + a.beginDelete.remove(req) + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK, http.StatusAccepted, http.StatusNoContent", resp.StatusCode)} + } + if !server.PollerResponderMore(beginDelete) { + a.beginDelete.remove(req) + } + + return resp, nil +} + +func (a *AzurePlanesServerTransport) dispatchGet(req *http.Request) (*http.Response, error) { + if a.srv.Get == nil { + return nil, &nonRetriableError{errors.New("fake for method Get not implemented")} + } + const regexStr = `/planes/azure/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 1 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + respr, errRespr := a.srv.Get(req.Context(), planeNameParam, nil) + if respErr := server.GetError(errRespr, req); respErr != nil { + return nil, respErr + } + respContent := server.GetResponseContent(respr) + if !contains([]int{http.StatusOK}, respContent.HTTPStatus) { + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK", respContent.HTTPStatus)} + } + resp, err := server.MarshalResponseAsJSON(respContent, server.GetResponse(respr).AzurePlaneResource, req) + if err != nil { + return nil, err + } + return resp, nil +} + +func (a *AzurePlanesServerTransport) dispatchNewListPager(req *http.Request) (*http.Response, error) { + if a.srv.NewListPager == nil { + return nil, &nonRetriableError{errors.New("fake for method NewListPager not implemented")} + } + newListPager := a.newListPager.get(req) + if newListPager == nil { +resp := a.srv.NewListPager(nil) + newListPager = &resp + a.newListPager.add(req, newListPager) + server.PagerResponderInjectNextLinks(newListPager, req, func(page *v20231001preview.AzurePlanesClientListResponse, createLink func() string) { + page.NextLink = to.Ptr(createLink()) + }) + } + resp, err := server.PagerResponderNext(newListPager, req) + if err != nil { + return nil, err + } + if !contains([]int{http.StatusOK}, resp.StatusCode) { + a.newListPager.remove(req) + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK", resp.StatusCode)} + } + if !server.PagerResponderMore(newListPager) { + a.newListPager.remove(req) + } + return resp, nil +} + +func (a *AzurePlanesServerTransport) dispatchBeginUpdate(req *http.Request) (*http.Response, error) { + if a.srv.BeginUpdate == nil { + return nil, &nonRetriableError{errors.New("fake for method BeginUpdate not implemented")} + } + beginUpdate := a.beginUpdate.get(req) + if beginUpdate == nil { + const regexStr = `/planes/azure/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 1 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + body, err := server.UnmarshalRequestAsJSON[v20231001preview.AzurePlaneResourceTagsUpdate](req) + if err != nil { + return nil, err + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + respr, errRespr := a.srv.BeginUpdate(req.Context(), planeNameParam, body, nil) + if respErr := server.GetError(errRespr, req); respErr != nil { + return nil, respErr + } + beginUpdate = &respr + a.beginUpdate.add(req, beginUpdate) + } + + resp, err := server.PollerResponderNext(beginUpdate, req) + if err != nil { + return nil, err + } + + if !contains([]int{http.StatusOK, http.StatusAccepted}, resp.StatusCode) { + a.beginUpdate.remove(req) + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK, http.StatusAccepted", resp.StatusCode)} + } + if !server.PollerResponderMore(beginUpdate) { + a.beginUpdate.remove(req) + } + + return resp, nil +} + +// set this to conditionally intercept incoming requests to AzurePlanesServerTransport +var azurePlanesServerTransportInterceptor interface { + // Do returns true if the server transport should use the returned response/error + Do(*http.Request) (*http.Response, error, bool) +} diff --git a/pkg/ucp/api/v20231001preview/fake/zz_generated_internal.go b/pkg/ucp/api/v20231001preview/fake/zz_generated_internal.go new file mode 100644 index 0000000000..0c62457a83 --- /dev/null +++ b/pkg/ucp/api/v20231001preview/fake/zz_generated_internal.go @@ -0,0 +1,66 @@ +// Licensed under the Apache License, Version 2.0 . See LICENSE in the repository root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package fake + +import ( + "github.com/Azure/azure-sdk-for-go/sdk/azcore/fake/server" + "net/http" + "sync" +) + + +type result struct { + resp *http.Response + err error +} + +type nonRetriableError struct { + error +} + +func (nonRetriableError) NonRetriable() { + // marker method +} + +func contains[T comparable](s []T, v T) bool { + for _, vv := range s { + if vv == v { + return true + } + } + return false +} + +func newTracker[T any]() *tracker[T] { + return &tracker[T]{ + items: map[string]*T{}, + } +} + +type tracker[T any] struct { + items map[string]*T + mu sync.Mutex +} + +func (p *tracker[T]) get(req *http.Request) *T { + p.mu.Lock() + defer p.mu.Unlock() + if item, ok := p.items[server.SanitizePagerPollerPath(req.URL.Path)]; ok { + return item + } + return nil +} + +func (p *tracker[T]) add(req *http.Request, item *T) { + p.mu.Lock() + defer p.mu.Unlock() + p.items[server.SanitizePagerPollerPath(req.URL.Path)] = item +} + +func (p *tracker[T]) remove(req *http.Request) { + p.mu.Lock() + defer p.mu.Unlock() + delete(p.items, server.SanitizePagerPollerPath(req.URL.Path)) +} diff --git a/pkg/ucp/api/v20231001preview/fake/zz_generated_locations_server.go b/pkg/ucp/api/v20231001preview/fake/zz_generated_locations_server.go new file mode 100644 index 0000000000..cf97d09ddd --- /dev/null +++ b/pkg/ucp/api/v20231001preview/fake/zz_generated_locations_server.go @@ -0,0 +1,294 @@ +// Licensed under the Apache License, Version 2.0 . See LICENSE in the repository root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package fake + +import ( + "context" + "errors" + "fmt" + azfake "github.com/Azure/azure-sdk-for-go/sdk/azcore/fake" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/fake/server" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" + "net/http" + "net/url" + "regexp" +) + +// LocationsServer is a fake server for instances of the v20231001preview.LocationsClient type. +type LocationsServer struct{ + // BeginCreateOrUpdate is the fake for method LocationsClient.BeginCreateOrUpdate + // HTTP status codes to indicate success: http.StatusOK, http.StatusCreated + 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) + + // BeginDelete is the fake for method LocationsClient.BeginDelete + // HTTP status codes to indicate success: http.StatusOK, http.StatusAccepted, http.StatusNoContent + BeginDelete func(ctx context.Context, planeName string, resourceProviderName string, locationName string, options *v20231001preview.LocationsClientBeginDeleteOptions) (resp azfake.PollerResponder[v20231001preview.LocationsClientDeleteResponse], errResp azfake.ErrorResponder) + + // Get is the fake for method LocationsClient.Get + // HTTP status codes to indicate success: http.StatusOK + Get func(ctx context.Context, planeName string, resourceProviderName string, locationName string, options *v20231001preview.LocationsClientGetOptions) (resp azfake.Responder[v20231001preview.LocationsClientGetResponse], errResp azfake.ErrorResponder) + + // NewListPager is the fake for method LocationsClient.NewListPager + // HTTP status codes to indicate success: http.StatusOK + NewListPager func(planeName string, resourceProviderName string, options *v20231001preview.LocationsClientListOptions) (resp azfake.PagerResponder[v20231001preview.LocationsClientListResponse]) + +} + +// NewLocationsServerTransport creates a new instance of LocationsServerTransport with the provided implementation. +// The returned LocationsServerTransport instance is connected to an instance of v20231001preview.LocationsClient via the +// azcore.ClientOptions.Transporter field in the client's constructor parameters. +func NewLocationsServerTransport(srv *LocationsServer) *LocationsServerTransport { + return &LocationsServerTransport{ + srv: srv, + beginCreateOrUpdate: newTracker[azfake.PollerResponder[v20231001preview.LocationsClientCreateOrUpdateResponse]](), + beginDelete: newTracker[azfake.PollerResponder[v20231001preview.LocationsClientDeleteResponse]](), + newListPager: newTracker[azfake.PagerResponder[v20231001preview.LocationsClientListResponse]](), + } +} + +// LocationsServerTransport connects instances of v20231001preview.LocationsClient to instances of LocationsServer. +// Don't use this type directly, use NewLocationsServerTransport instead. +type LocationsServerTransport struct { + srv *LocationsServer + beginCreateOrUpdate *tracker[azfake.PollerResponder[v20231001preview.LocationsClientCreateOrUpdateResponse]] + beginDelete *tracker[azfake.PollerResponder[v20231001preview.LocationsClientDeleteResponse]] + newListPager *tracker[azfake.PagerResponder[v20231001preview.LocationsClientListResponse]] +} + +// Do implements the policy.Transporter interface for LocationsServerTransport. +func (l *LocationsServerTransport) Do(req *http.Request) (*http.Response, error) { + rawMethod := req.Context().Value(runtime.CtxAPINameKey{}) + method, ok := rawMethod.(string) + if !ok { + return nil, nonRetriableError{errors.New("unable to dispatch request, missing value for CtxAPINameKey")} + } + + return l.dispatchToMethodFake(req, method) +} + +func (l *LocationsServerTransport) dispatchToMethodFake(req *http.Request, method string) (*http.Response, error) { + resultChan := make(chan result) + defer close(resultChan) + + go func() { + var intercepted bool + var res result + if locationsServerTransportInterceptor != nil { + res.resp, res.err, intercepted = locationsServerTransportInterceptor.Do(req) + } + if !intercepted { + switch method { + case "LocationsClient.BeginCreateOrUpdate": + res.resp, res.err = l.dispatchBeginCreateOrUpdate(req) + case "LocationsClient.BeginDelete": + res.resp, res.err = l.dispatchBeginDelete(req) + case "LocationsClient.Get": + res.resp, res.err = l.dispatchGet(req) + case "LocationsClient.NewListPager": + res.resp, res.err = l.dispatchNewListPager(req) + default: + res.err = fmt.Errorf("unhandled API %s", method) + } + + } + select { + case resultChan <- res: + case <-req.Context().Done(): + } + }() + + select { + case <-req.Context().Done(): + return nil, req.Context().Err() + case res := <-resultChan: + return res.resp, res.err + } +} + +func (l *LocationsServerTransport) dispatchBeginCreateOrUpdate(req *http.Request) (*http.Response, error) { + if l.srv.BeginCreateOrUpdate == nil { + return nil, &nonRetriableError{errors.New("fake for method BeginCreateOrUpdate not implemented")} + } + beginCreateOrUpdate := l.beginCreateOrUpdate.get(req) + if beginCreateOrUpdate == nil { + const regexStr = `/planes/radius/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/providers/System\.Resources/resourceproviders/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/locations/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 3 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + body, err := server.UnmarshalRequestAsJSON[v20231001preview.LocationResource](req) + if err != nil { + return nil, err + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + resourceProviderNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("resourceProviderName")]) + if err != nil { + return nil, err + } + locationNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("locationName")]) + if err != nil { + return nil, err + } + respr, errRespr := l.srv.BeginCreateOrUpdate(req.Context(), planeNameParam, resourceProviderNameParam, locationNameParam, body, nil) + if respErr := server.GetError(errRespr, req); respErr != nil { + return nil, respErr + } + beginCreateOrUpdate = &respr + l.beginCreateOrUpdate.add(req, beginCreateOrUpdate) + } + + resp, err := server.PollerResponderNext(beginCreateOrUpdate, req) + if err != nil { + return nil, err + } + + if !contains([]int{http.StatusOK, http.StatusCreated}, resp.StatusCode) { + l.beginCreateOrUpdate.remove(req) + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK, http.StatusCreated", resp.StatusCode)} + } + if !server.PollerResponderMore(beginCreateOrUpdate) { + l.beginCreateOrUpdate.remove(req) + } + + return resp, nil +} + +func (l *LocationsServerTransport) dispatchBeginDelete(req *http.Request) (*http.Response, error) { + if l.srv.BeginDelete == nil { + return nil, &nonRetriableError{errors.New("fake for method BeginDelete not implemented")} + } + beginDelete := l.beginDelete.get(req) + if beginDelete == nil { + const regexStr = `/planes/radius/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/providers/System\.Resources/resourceproviders/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/locations/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 3 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + resourceProviderNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("resourceProviderName")]) + if err != nil { + return nil, err + } + locationNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("locationName")]) + if err != nil { + return nil, err + } + respr, errRespr := l.srv.BeginDelete(req.Context(), planeNameParam, resourceProviderNameParam, locationNameParam, nil) + if respErr := server.GetError(errRespr, req); respErr != nil { + return nil, respErr + } + beginDelete = &respr + l.beginDelete.add(req, beginDelete) + } + + resp, err := server.PollerResponderNext(beginDelete, req) + if err != nil { + return nil, err + } + + if !contains([]int{http.StatusOK, http.StatusAccepted, http.StatusNoContent}, resp.StatusCode) { + l.beginDelete.remove(req) + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK, http.StatusAccepted, http.StatusNoContent", resp.StatusCode)} + } + if !server.PollerResponderMore(beginDelete) { + l.beginDelete.remove(req) + } + + return resp, nil +} + +func (l *LocationsServerTransport) dispatchGet(req *http.Request) (*http.Response, error) { + if l.srv.Get == nil { + return nil, &nonRetriableError{errors.New("fake for method Get not implemented")} + } + const regexStr = `/planes/radius/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/providers/System\.Resources/resourceproviders/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/locations/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 3 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + resourceProviderNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("resourceProviderName")]) + if err != nil { + return nil, err + } + locationNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("locationName")]) + if err != nil { + return nil, err + } + respr, errRespr := l.srv.Get(req.Context(), planeNameParam, resourceProviderNameParam, locationNameParam, nil) + if respErr := server.GetError(errRespr, req); respErr != nil { + return nil, respErr + } + respContent := server.GetResponseContent(respr) + if !contains([]int{http.StatusOK}, respContent.HTTPStatus) { + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK", respContent.HTTPStatus)} + } + resp, err := server.MarshalResponseAsJSON(respContent, server.GetResponse(respr).LocationResource, req) + if err != nil { + return nil, err + } + return resp, nil +} + +func (l *LocationsServerTransport) dispatchNewListPager(req *http.Request) (*http.Response, error) { + if l.srv.NewListPager == nil { + return nil, &nonRetriableError{errors.New("fake for method NewListPager not implemented")} + } + newListPager := l.newListPager.get(req) + if newListPager == nil { + const regexStr = `/planes/radius/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/providers/System\.Resources/resourceproviders/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/locations` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 2 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + resourceProviderNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("resourceProviderName")]) + if err != nil { + return nil, err + } +resp := l.srv.NewListPager(planeNameParam, resourceProviderNameParam, nil) + newListPager = &resp + l.newListPager.add(req, newListPager) + server.PagerResponderInjectNextLinks(newListPager, req, func(page *v20231001preview.LocationsClientListResponse, createLink func() string) { + page.NextLink = to.Ptr(createLink()) + }) + } + resp, err := server.PagerResponderNext(newListPager, req) + if err != nil { + return nil, err + } + if !contains([]int{http.StatusOK}, resp.StatusCode) { + l.newListPager.remove(req) + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK", resp.StatusCode)} + } + if !server.PagerResponderMore(newListPager) { + l.newListPager.remove(req) + } + return resp, nil +} + +// set this to conditionally intercept incoming requests to LocationsServerTransport +var locationsServerTransportInterceptor interface { + // Do returns true if the server transport should use the returned response/error + Do(*http.Request) (*http.Response, error, bool) +} diff --git a/pkg/ucp/api/v20231001preview/fake/zz_generated_planes_server.go b/pkg/ucp/api/v20231001preview/fake/zz_generated_planes_server.go new file mode 100644 index 0000000000..b361998914 --- /dev/null +++ b/pkg/ucp/api/v20231001preview/fake/zz_generated_planes_server.go @@ -0,0 +1,118 @@ +// Licensed under the Apache License, Version 2.0 . See LICENSE in the repository root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package fake + +import ( + "errors" + "fmt" + azfake "github.com/Azure/azure-sdk-for-go/sdk/azcore/fake" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/fake/server" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" + "net/http" +) + +// PlanesServer is a fake server for instances of the v20231001preview.PlanesClient type. +type PlanesServer struct{ + // NewListPlanesPager is the fake for method PlanesClient.NewListPlanesPager + // HTTP status codes to indicate success: http.StatusOK + NewListPlanesPager func(options *v20231001preview.PlanesClientListPlanesOptions) (resp azfake.PagerResponder[v20231001preview.PlanesClientListPlanesResponse]) + +} + +// NewPlanesServerTransport creates a new instance of PlanesServerTransport with the provided implementation. +// The returned PlanesServerTransport instance is connected to an instance of v20231001preview.PlanesClient via the +// azcore.ClientOptions.Transporter field in the client's constructor parameters. +func NewPlanesServerTransport(srv *PlanesServer) *PlanesServerTransport { + return &PlanesServerTransport{ + srv: srv, + newListPlanesPager: newTracker[azfake.PagerResponder[v20231001preview.PlanesClientListPlanesResponse]](), + } +} + +// PlanesServerTransport connects instances of v20231001preview.PlanesClient to instances of PlanesServer. +// Don't use this type directly, use NewPlanesServerTransport instead. +type PlanesServerTransport struct { + srv *PlanesServer + newListPlanesPager *tracker[azfake.PagerResponder[v20231001preview.PlanesClientListPlanesResponse]] +} + +// Do implements the policy.Transporter interface for PlanesServerTransport. +func (p *PlanesServerTransport) Do(req *http.Request) (*http.Response, error) { + rawMethod := req.Context().Value(runtime.CtxAPINameKey{}) + method, ok := rawMethod.(string) + if !ok { + return nil, nonRetriableError{errors.New("unable to dispatch request, missing value for CtxAPINameKey")} + } + + return p.dispatchToMethodFake(req, method) +} + +func (p *PlanesServerTransport) dispatchToMethodFake(req *http.Request, method string) (*http.Response, error) { + resultChan := make(chan result) + defer close(resultChan) + + go func() { + var intercepted bool + var res result + if planesServerTransportInterceptor != nil { + res.resp, res.err, intercepted = planesServerTransportInterceptor.Do(req) + } + if !intercepted { + switch method { + case "PlanesClient.NewListPlanesPager": + res.resp, res.err = p.dispatchNewListPlanesPager(req) + default: + res.err = fmt.Errorf("unhandled API %s", method) + } + + } + select { + case resultChan <- res: + case <-req.Context().Done(): + } + }() + + select { + case <-req.Context().Done(): + return nil, req.Context().Err() + case res := <-resultChan: + return res.resp, res.err + } +} + +func (p *PlanesServerTransport) dispatchNewListPlanesPager(req *http.Request) (*http.Response, error) { + if p.srv.NewListPlanesPager == nil { + return nil, &nonRetriableError{errors.New("fake for method NewListPlanesPager not implemented")} + } + newListPlanesPager := p.newListPlanesPager.get(req) + if newListPlanesPager == nil { +resp := p.srv.NewListPlanesPager(nil) + newListPlanesPager = &resp + p.newListPlanesPager.add(req, newListPlanesPager) + server.PagerResponderInjectNextLinks(newListPlanesPager, req, func(page *v20231001preview.PlanesClientListPlanesResponse, createLink func() string) { + page.NextLink = to.Ptr(createLink()) + }) + } + resp, err := server.PagerResponderNext(newListPlanesPager, req) + if err != nil { + return nil, err + } + if !contains([]int{http.StatusOK}, resp.StatusCode) { + p.newListPlanesPager.remove(req) + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK", resp.StatusCode)} + } + if !server.PagerResponderMore(newListPlanesPager) { + p.newListPlanesPager.remove(req) + } + return resp, nil +} + +// set this to conditionally intercept incoming requests to PlanesServerTransport +var planesServerTransportInterceptor interface { + // Do returns true if the server transport should use the returned response/error + Do(*http.Request) (*http.Response, error, bool) +} diff --git a/pkg/ucp/api/v20231001preview/fake/zz_generated_radiusplanes_server.go b/pkg/ucp/api/v20231001preview/fake/zz_generated_radiusplanes_server.go new file mode 100644 index 0000000000..3271c005e4 --- /dev/null +++ b/pkg/ucp/api/v20231001preview/fake/zz_generated_radiusplanes_server.go @@ -0,0 +1,308 @@ +// Licensed under the Apache License, Version 2.0 . See LICENSE in the repository root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package fake + +import ( + "context" + "errors" + "fmt" + azfake "github.com/Azure/azure-sdk-for-go/sdk/azcore/fake" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/fake/server" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" + "net/http" + "net/url" + "regexp" +) + +// RadiusPlanesServer is a fake server for instances of the v20231001preview.RadiusPlanesClient type. +type RadiusPlanesServer struct{ + // BeginCreateOrUpdate is the fake for method RadiusPlanesClient.BeginCreateOrUpdate + // HTTP status codes to indicate success: http.StatusOK, http.StatusCreated + BeginCreateOrUpdate func(ctx context.Context, planeName string, resource v20231001preview.RadiusPlaneResource, options *v20231001preview.RadiusPlanesClientBeginCreateOrUpdateOptions) (resp azfake.PollerResponder[v20231001preview.RadiusPlanesClientCreateOrUpdateResponse], errResp azfake.ErrorResponder) + + // BeginDelete is the fake for method RadiusPlanesClient.BeginDelete + // HTTP status codes to indicate success: http.StatusOK, http.StatusAccepted, http.StatusNoContent + BeginDelete func(ctx context.Context, planeName string, options *v20231001preview.RadiusPlanesClientBeginDeleteOptions) (resp azfake.PollerResponder[v20231001preview.RadiusPlanesClientDeleteResponse], errResp azfake.ErrorResponder) + + // Get is the fake for method RadiusPlanesClient.Get + // HTTP status codes to indicate success: http.StatusOK + Get func(ctx context.Context, planeName string, options *v20231001preview.RadiusPlanesClientGetOptions) (resp azfake.Responder[v20231001preview.RadiusPlanesClientGetResponse], errResp azfake.ErrorResponder) + + // NewListPager is the fake for method RadiusPlanesClient.NewListPager + // HTTP status codes to indicate success: http.StatusOK + NewListPager func(options *v20231001preview.RadiusPlanesClientListOptions) (resp azfake.PagerResponder[v20231001preview.RadiusPlanesClientListResponse]) + + // BeginUpdate is the fake for method RadiusPlanesClient.BeginUpdate + // HTTP status codes to indicate success: http.StatusOK, http.StatusAccepted + BeginUpdate func(ctx context.Context, planeName string, properties v20231001preview.RadiusPlaneResourceTagsUpdate, options *v20231001preview.RadiusPlanesClientBeginUpdateOptions) (resp azfake.PollerResponder[v20231001preview.RadiusPlanesClientUpdateResponse], errResp azfake.ErrorResponder) + +} + +// NewRadiusPlanesServerTransport creates a new instance of RadiusPlanesServerTransport with the provided implementation. +// The returned RadiusPlanesServerTransport instance is connected to an instance of v20231001preview.RadiusPlanesClient via the +// azcore.ClientOptions.Transporter field in the client's constructor parameters. +func NewRadiusPlanesServerTransport(srv *RadiusPlanesServer) *RadiusPlanesServerTransport { + return &RadiusPlanesServerTransport{ + srv: srv, + beginCreateOrUpdate: newTracker[azfake.PollerResponder[v20231001preview.RadiusPlanesClientCreateOrUpdateResponse]](), + beginDelete: newTracker[azfake.PollerResponder[v20231001preview.RadiusPlanesClientDeleteResponse]](), + newListPager: newTracker[azfake.PagerResponder[v20231001preview.RadiusPlanesClientListResponse]](), + beginUpdate: newTracker[azfake.PollerResponder[v20231001preview.RadiusPlanesClientUpdateResponse]](), + } +} + +// RadiusPlanesServerTransport connects instances of v20231001preview.RadiusPlanesClient to instances of RadiusPlanesServer. +// Don't use this type directly, use NewRadiusPlanesServerTransport instead. +type RadiusPlanesServerTransport struct { + srv *RadiusPlanesServer + beginCreateOrUpdate *tracker[azfake.PollerResponder[v20231001preview.RadiusPlanesClientCreateOrUpdateResponse]] + beginDelete *tracker[azfake.PollerResponder[v20231001preview.RadiusPlanesClientDeleteResponse]] + newListPager *tracker[azfake.PagerResponder[v20231001preview.RadiusPlanesClientListResponse]] + beginUpdate *tracker[azfake.PollerResponder[v20231001preview.RadiusPlanesClientUpdateResponse]] +} + +// Do implements the policy.Transporter interface for RadiusPlanesServerTransport. +func (r *RadiusPlanesServerTransport) Do(req *http.Request) (*http.Response, error) { + rawMethod := req.Context().Value(runtime.CtxAPINameKey{}) + method, ok := rawMethod.(string) + if !ok { + return nil, nonRetriableError{errors.New("unable to dispatch request, missing value for CtxAPINameKey")} + } + + return r.dispatchToMethodFake(req, method) +} + +func (r *RadiusPlanesServerTransport) dispatchToMethodFake(req *http.Request, method string) (*http.Response, error) { + resultChan := make(chan result) + defer close(resultChan) + + go func() { + var intercepted bool + var res result + if radiusPlanesServerTransportInterceptor != nil { + res.resp, res.err, intercepted = radiusPlanesServerTransportInterceptor.Do(req) + } + if !intercepted { + switch method { + case "RadiusPlanesClient.BeginCreateOrUpdate": + res.resp, res.err = r.dispatchBeginCreateOrUpdate(req) + case "RadiusPlanesClient.BeginDelete": + res.resp, res.err = r.dispatchBeginDelete(req) + case "RadiusPlanesClient.Get": + res.resp, res.err = r.dispatchGet(req) + case "RadiusPlanesClient.NewListPager": + res.resp, res.err = r.dispatchNewListPager(req) + case "RadiusPlanesClient.BeginUpdate": + res.resp, res.err = r.dispatchBeginUpdate(req) + default: + res.err = fmt.Errorf("unhandled API %s", method) + } + + } + select { + case resultChan <- res: + case <-req.Context().Done(): + } + }() + + select { + case <-req.Context().Done(): + return nil, req.Context().Err() + case res := <-resultChan: + return res.resp, res.err + } +} + +func (r *RadiusPlanesServerTransport) dispatchBeginCreateOrUpdate(req *http.Request) (*http.Response, error) { + if r.srv.BeginCreateOrUpdate == nil { + return nil, &nonRetriableError{errors.New("fake for method BeginCreateOrUpdate not implemented")} + } + beginCreateOrUpdate := r.beginCreateOrUpdate.get(req) + if beginCreateOrUpdate == nil { + const regexStr = `/planes/radius/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 1 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + body, err := server.UnmarshalRequestAsJSON[v20231001preview.RadiusPlaneResource](req) + if err != nil { + return nil, err + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + respr, errRespr := r.srv.BeginCreateOrUpdate(req.Context(), planeNameParam, body, nil) + if respErr := server.GetError(errRespr, req); respErr != nil { + return nil, respErr + } + beginCreateOrUpdate = &respr + r.beginCreateOrUpdate.add(req, beginCreateOrUpdate) + } + + resp, err := server.PollerResponderNext(beginCreateOrUpdate, req) + if err != nil { + return nil, err + } + + if !contains([]int{http.StatusOK, http.StatusCreated}, resp.StatusCode) { + r.beginCreateOrUpdate.remove(req) + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK, http.StatusCreated", resp.StatusCode)} + } + if !server.PollerResponderMore(beginCreateOrUpdate) { + r.beginCreateOrUpdate.remove(req) + } + + return resp, nil +} + +func (r *RadiusPlanesServerTransport) dispatchBeginDelete(req *http.Request) (*http.Response, error) { + if r.srv.BeginDelete == nil { + return nil, &nonRetriableError{errors.New("fake for method BeginDelete not implemented")} + } + beginDelete := r.beginDelete.get(req) + if beginDelete == nil { + const regexStr = `/planes/radius/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 1 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + respr, errRespr := r.srv.BeginDelete(req.Context(), planeNameParam, nil) + if respErr := server.GetError(errRespr, req); respErr != nil { + return nil, respErr + } + beginDelete = &respr + r.beginDelete.add(req, beginDelete) + } + + resp, err := server.PollerResponderNext(beginDelete, req) + if err != nil { + return nil, err + } + + if !contains([]int{http.StatusOK, http.StatusAccepted, http.StatusNoContent}, resp.StatusCode) { + r.beginDelete.remove(req) + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK, http.StatusAccepted, http.StatusNoContent", resp.StatusCode)} + } + if !server.PollerResponderMore(beginDelete) { + r.beginDelete.remove(req) + } + + return resp, nil +} + +func (r *RadiusPlanesServerTransport) dispatchGet(req *http.Request) (*http.Response, error) { + if r.srv.Get == nil { + return nil, &nonRetriableError{errors.New("fake for method Get not implemented")} + } + const regexStr = `/planes/radius/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 1 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + respr, errRespr := r.srv.Get(req.Context(), planeNameParam, nil) + if respErr := server.GetError(errRespr, req); respErr != nil { + return nil, respErr + } + respContent := server.GetResponseContent(respr) + if !contains([]int{http.StatusOK}, respContent.HTTPStatus) { + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK", respContent.HTTPStatus)} + } + resp, err := server.MarshalResponseAsJSON(respContent, server.GetResponse(respr).RadiusPlaneResource, req) + if err != nil { + return nil, err + } + return resp, nil +} + +func (r *RadiusPlanesServerTransport) dispatchNewListPager(req *http.Request) (*http.Response, error) { + if r.srv.NewListPager == nil { + return nil, &nonRetriableError{errors.New("fake for method NewListPager not implemented")} + } + newListPager := r.newListPager.get(req) + if newListPager == nil { +resp := r.srv.NewListPager(nil) + newListPager = &resp + r.newListPager.add(req, newListPager) + server.PagerResponderInjectNextLinks(newListPager, req, func(page *v20231001preview.RadiusPlanesClientListResponse, createLink func() string) { + page.NextLink = to.Ptr(createLink()) + }) + } + resp, err := server.PagerResponderNext(newListPager, req) + if err != nil { + return nil, err + } + if !contains([]int{http.StatusOK}, resp.StatusCode) { + r.newListPager.remove(req) + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK", resp.StatusCode)} + } + if !server.PagerResponderMore(newListPager) { + r.newListPager.remove(req) + } + return resp, nil +} + +func (r *RadiusPlanesServerTransport) dispatchBeginUpdate(req *http.Request) (*http.Response, error) { + if r.srv.BeginUpdate == nil { + return nil, &nonRetriableError{errors.New("fake for method BeginUpdate not implemented")} + } + beginUpdate := r.beginUpdate.get(req) + if beginUpdate == nil { + const regexStr = `/planes/radius/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 1 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + body, err := server.UnmarshalRequestAsJSON[v20231001preview.RadiusPlaneResourceTagsUpdate](req) + if err != nil { + return nil, err + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + respr, errRespr := r.srv.BeginUpdate(req.Context(), planeNameParam, body, nil) + if respErr := server.GetError(errRespr, req); respErr != nil { + return nil, respErr + } + beginUpdate = &respr + r.beginUpdate.add(req, beginUpdate) + } + + resp, err := server.PollerResponderNext(beginUpdate, req) + if err != nil { + return nil, err + } + + if !contains([]int{http.StatusOK, http.StatusAccepted}, resp.StatusCode) { + r.beginUpdate.remove(req) + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK, http.StatusAccepted", resp.StatusCode)} + } + if !server.PollerResponderMore(beginUpdate) { + r.beginUpdate.remove(req) + } + + return resp, nil +} + +// set this to conditionally intercept incoming requests to RadiusPlanesServerTransport +var radiusPlanesServerTransportInterceptor interface { + // Do returns true if the server transport should use the returned response/error + Do(*http.Request) (*http.Response, error, bool) +} diff --git a/pkg/ucp/api/v20231001preview/fake/zz_generated_resourcegroups_server.go b/pkg/ucp/api/v20231001preview/fake/zz_generated_resourcegroups_server.go new file mode 100644 index 0000000000..c66d567bcd --- /dev/null +++ b/pkg/ucp/api/v20231001preview/fake/zz_generated_resourcegroups_server.go @@ -0,0 +1,295 @@ +// Licensed under the Apache License, Version 2.0 . See LICENSE in the repository root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package fake + +import ( + "context" + "errors" + "fmt" + azfake "github.com/Azure/azure-sdk-for-go/sdk/azcore/fake" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/fake/server" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" + "net/http" + "net/url" + "regexp" +) + +// ResourceGroupsServer is a fake server for instances of the v20231001preview.ResourceGroupsClient type. +type ResourceGroupsServer struct{ + // CreateOrUpdate is the fake for method ResourceGroupsClient.CreateOrUpdate + // HTTP status codes to indicate success: http.StatusOK, http.StatusCreated + CreateOrUpdate func(ctx context.Context, planeName string, resourceGroupName string, resource v20231001preview.ResourceGroupResource, options *v20231001preview.ResourceGroupsClientCreateOrUpdateOptions) (resp azfake.Responder[v20231001preview.ResourceGroupsClientCreateOrUpdateResponse], errResp azfake.ErrorResponder) + + // Delete is the fake for method ResourceGroupsClient.Delete + // HTTP status codes to indicate success: http.StatusOK, http.StatusNoContent + Delete func(ctx context.Context, planeName string, resourceGroupName string, options *v20231001preview.ResourceGroupsClientDeleteOptions) (resp azfake.Responder[v20231001preview.ResourceGroupsClientDeleteResponse], errResp azfake.ErrorResponder) + + // Get is the fake for method ResourceGroupsClient.Get + // HTTP status codes to indicate success: http.StatusOK + Get func(ctx context.Context, planeName string, resourceGroupName string, options *v20231001preview.ResourceGroupsClientGetOptions) (resp azfake.Responder[v20231001preview.ResourceGroupsClientGetResponse], errResp azfake.ErrorResponder) + + // NewListPager is the fake for method ResourceGroupsClient.NewListPager + // HTTP status codes to indicate success: http.StatusOK + NewListPager func(planeName string, options *v20231001preview.ResourceGroupsClientListOptions) (resp azfake.PagerResponder[v20231001preview.ResourceGroupsClientListResponse]) + + // Update is the fake for method ResourceGroupsClient.Update + // HTTP status codes to indicate success: http.StatusOK + Update func(ctx context.Context, planeName string, resourceGroupName string, properties v20231001preview.ResourceGroupResourceTagsUpdate, options *v20231001preview.ResourceGroupsClientUpdateOptions) (resp azfake.Responder[v20231001preview.ResourceGroupsClientUpdateResponse], errResp azfake.ErrorResponder) + +} + +// NewResourceGroupsServerTransport creates a new instance of ResourceGroupsServerTransport with the provided implementation. +// The returned ResourceGroupsServerTransport instance is connected to an instance of v20231001preview.ResourceGroupsClient via the +// azcore.ClientOptions.Transporter field in the client's constructor parameters. +func NewResourceGroupsServerTransport(srv *ResourceGroupsServer) *ResourceGroupsServerTransport { + return &ResourceGroupsServerTransport{ + srv: srv, + newListPager: newTracker[azfake.PagerResponder[v20231001preview.ResourceGroupsClientListResponse]](), + } +} + +// ResourceGroupsServerTransport connects instances of v20231001preview.ResourceGroupsClient to instances of ResourceGroupsServer. +// Don't use this type directly, use NewResourceGroupsServerTransport instead. +type ResourceGroupsServerTransport struct { + srv *ResourceGroupsServer + newListPager *tracker[azfake.PagerResponder[v20231001preview.ResourceGroupsClientListResponse]] +} + +// Do implements the policy.Transporter interface for ResourceGroupsServerTransport. +func (r *ResourceGroupsServerTransport) Do(req *http.Request) (*http.Response, error) { + rawMethod := req.Context().Value(runtime.CtxAPINameKey{}) + method, ok := rawMethod.(string) + if !ok { + return nil, nonRetriableError{errors.New("unable to dispatch request, missing value for CtxAPINameKey")} + } + + return r.dispatchToMethodFake(req, method) +} + +func (r *ResourceGroupsServerTransport) dispatchToMethodFake(req *http.Request, method string) (*http.Response, error) { + resultChan := make(chan result) + defer close(resultChan) + + go func() { + var intercepted bool + var res result + if resourceGroupsServerTransportInterceptor != nil { + res.resp, res.err, intercepted = resourceGroupsServerTransportInterceptor.Do(req) + } + if !intercepted { + switch method { + case "ResourceGroupsClient.CreateOrUpdate": + res.resp, res.err = r.dispatchCreateOrUpdate(req) + case "ResourceGroupsClient.Delete": + res.resp, res.err = r.dispatchDelete(req) + case "ResourceGroupsClient.Get": + res.resp, res.err = r.dispatchGet(req) + case "ResourceGroupsClient.NewListPager": + res.resp, res.err = r.dispatchNewListPager(req) + case "ResourceGroupsClient.Update": + res.resp, res.err = r.dispatchUpdate(req) + default: + res.err = fmt.Errorf("unhandled API %s", method) + } + + } + select { + case resultChan <- res: + case <-req.Context().Done(): + } + }() + + select { + case <-req.Context().Done(): + return nil, req.Context().Err() + case res := <-resultChan: + return res.resp, res.err + } +} + +func (r *ResourceGroupsServerTransport) dispatchCreateOrUpdate(req *http.Request) (*http.Response, error) { + if r.srv.CreateOrUpdate == nil { + return nil, &nonRetriableError{errors.New("fake for method CreateOrUpdate not implemented")} + } + const regexStr = `/planes/radius/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/resourcegroups/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 2 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + body, err := server.UnmarshalRequestAsJSON[v20231001preview.ResourceGroupResource](req) + if err != nil { + return nil, err + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + resourceGroupNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("resourceGroupName")]) + if err != nil { + return nil, err + } + respr, errRespr := r.srv.CreateOrUpdate(req.Context(), planeNameParam, resourceGroupNameParam, body, nil) + if respErr := server.GetError(errRespr, req); respErr != nil { + return nil, respErr + } + respContent := server.GetResponseContent(respr) + if !contains([]int{http.StatusOK, http.StatusCreated}, respContent.HTTPStatus) { + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK, http.StatusCreated", respContent.HTTPStatus)} + } + resp, err := server.MarshalResponseAsJSON(respContent, server.GetResponse(respr).ResourceGroupResource, req) + if err != nil { + return nil, err + } + return resp, nil +} + +func (r *ResourceGroupsServerTransport) dispatchDelete(req *http.Request) (*http.Response, error) { + if r.srv.Delete == nil { + return nil, &nonRetriableError{errors.New("fake for method Delete not implemented")} + } + const regexStr = `/planes/radius/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/resourcegroups/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 2 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + resourceGroupNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("resourceGroupName")]) + if err != nil { + return nil, err + } + respr, errRespr := r.srv.Delete(req.Context(), planeNameParam, resourceGroupNameParam, nil) + if respErr := server.GetError(errRespr, req); respErr != nil { + return nil, respErr + } + respContent := server.GetResponseContent(respr) + if !contains([]int{http.StatusOK, http.StatusNoContent}, respContent.HTTPStatus) { + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK, http.StatusNoContent", respContent.HTTPStatus)} + } + resp, err := server.NewResponse(respContent, req, nil) + if err != nil { + return nil, err + } + return resp, nil +} + +func (r *ResourceGroupsServerTransport) dispatchGet(req *http.Request) (*http.Response, error) { + if r.srv.Get == nil { + return nil, &nonRetriableError{errors.New("fake for method Get not implemented")} + } + const regexStr = `/planes/radius/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/resourcegroups/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 2 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + resourceGroupNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("resourceGroupName")]) + if err != nil { + return nil, err + } + respr, errRespr := r.srv.Get(req.Context(), planeNameParam, resourceGroupNameParam, nil) + if respErr := server.GetError(errRespr, req); respErr != nil { + return nil, respErr + } + respContent := server.GetResponseContent(respr) + if !contains([]int{http.StatusOK}, respContent.HTTPStatus) { + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK", respContent.HTTPStatus)} + } + resp, err := server.MarshalResponseAsJSON(respContent, server.GetResponse(respr).ResourceGroupResource, req) + if err != nil { + return nil, err + } + return resp, nil +} + +func (r *ResourceGroupsServerTransport) dispatchNewListPager(req *http.Request) (*http.Response, error) { + if r.srv.NewListPager == nil { + return nil, &nonRetriableError{errors.New("fake for method NewListPager not implemented")} + } + newListPager := r.newListPager.get(req) + if newListPager == nil { + const regexStr = `/planes/radius/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/resourcegroups` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 1 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } +resp := r.srv.NewListPager(planeNameParam, nil) + newListPager = &resp + r.newListPager.add(req, newListPager) + server.PagerResponderInjectNextLinks(newListPager, req, func(page *v20231001preview.ResourceGroupsClientListResponse, createLink func() string) { + page.NextLink = to.Ptr(createLink()) + }) + } + resp, err := server.PagerResponderNext(newListPager, req) + if err != nil { + return nil, err + } + if !contains([]int{http.StatusOK}, resp.StatusCode) { + r.newListPager.remove(req) + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK", resp.StatusCode)} + } + if !server.PagerResponderMore(newListPager) { + r.newListPager.remove(req) + } + return resp, nil +} + +func (r *ResourceGroupsServerTransport) dispatchUpdate(req *http.Request) (*http.Response, error) { + if r.srv.Update == nil { + return nil, &nonRetriableError{errors.New("fake for method Update not implemented")} + } + const regexStr = `/planes/radius/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/resourcegroups/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 2 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + body, err := server.UnmarshalRequestAsJSON[v20231001preview.ResourceGroupResourceTagsUpdate](req) + if err != nil { + return nil, err + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + resourceGroupNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("resourceGroupName")]) + if err != nil { + return nil, err + } + respr, errRespr := r.srv.Update(req.Context(), planeNameParam, resourceGroupNameParam, body, nil) + if respErr := server.GetError(errRespr, req); respErr != nil { + return nil, respErr + } + respContent := server.GetResponseContent(respr) + if !contains([]int{http.StatusOK}, respContent.HTTPStatus) { + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK", respContent.HTTPStatus)} + } + resp, err := server.MarshalResponseAsJSON(respContent, server.GetResponse(respr).ResourceGroupResource, req) + if err != nil { + return nil, err + } + return resp, nil +} + +// set this to conditionally intercept incoming requests to ResourceGroupsServerTransport +var resourceGroupsServerTransportInterceptor interface { + // Do returns true if the server transport should use the returned response/error + Do(*http.Request) (*http.Response, error, bool) +} diff --git a/pkg/ucp/api/v20231001preview/fake/zz_generated_resourceproviders_server.go b/pkg/ucp/api/v20231001preview/fake/zz_generated_resourceproviders_server.go new file mode 100644 index 0000000000..138f9aaceb --- /dev/null +++ b/pkg/ucp/api/v20231001preview/fake/zz_generated_resourceproviders_server.go @@ -0,0 +1,362 @@ +// Licensed under the Apache License, Version 2.0 . See LICENSE in the repository root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package fake + +import ( + "context" + "errors" + "fmt" + azfake "github.com/Azure/azure-sdk-for-go/sdk/azcore/fake" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/fake/server" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" + "net/http" + "net/url" + "regexp" +) + +// ResourceProvidersServer is a fake server for instances of the v20231001preview.ResourceProvidersClient type. +type ResourceProvidersServer struct{ + // BeginCreateOrUpdate is the fake for method ResourceProvidersClient.BeginCreateOrUpdate + // HTTP status codes to indicate success: http.StatusOK, http.StatusCreated + BeginCreateOrUpdate func(ctx context.Context, planeName string, resourceProviderName string, resource v20231001preview.ResourceProviderResource, options *v20231001preview.ResourceProvidersClientBeginCreateOrUpdateOptions) (resp azfake.PollerResponder[v20231001preview.ResourceProvidersClientCreateOrUpdateResponse], errResp azfake.ErrorResponder) + + // BeginDelete is the fake for method ResourceProvidersClient.BeginDelete + // HTTP status codes to indicate success: http.StatusOK, http.StatusAccepted, http.StatusNoContent + BeginDelete func(ctx context.Context, planeName string, resourceProviderName string, options *v20231001preview.ResourceProvidersClientBeginDeleteOptions) (resp azfake.PollerResponder[v20231001preview.ResourceProvidersClientDeleteResponse], errResp azfake.ErrorResponder) + + // Get is the fake for method ResourceProvidersClient.Get + // HTTP status codes to indicate success: http.StatusOK + Get func(ctx context.Context, planeName string, resourceProviderName string, options *v20231001preview.ResourceProvidersClientGetOptions) (resp azfake.Responder[v20231001preview.ResourceProvidersClientGetResponse], errResp azfake.ErrorResponder) + + // GetProviderSummary is the fake for method ResourceProvidersClient.GetProviderSummary + // HTTP status codes to indicate success: http.StatusOK + GetProviderSummary func(ctx context.Context, planeName string, resourceProviderName string, options *v20231001preview.ResourceProvidersClientGetProviderSummaryOptions) (resp azfake.Responder[v20231001preview.ResourceProvidersClientGetProviderSummaryResponse], errResp azfake.ErrorResponder) + + // NewListPager is the fake for method ResourceProvidersClient.NewListPager + // HTTP status codes to indicate success: http.StatusOK + NewListPager func(planeName string, options *v20231001preview.ResourceProvidersClientListOptions) (resp azfake.PagerResponder[v20231001preview.ResourceProvidersClientListResponse]) + + // NewListProviderSummariesPager is the fake for method ResourceProvidersClient.NewListProviderSummariesPager + // HTTP status codes to indicate success: http.StatusOK + NewListProviderSummariesPager func(planeName string, options *v20231001preview.ResourceProvidersClientListProviderSummariesOptions) (resp azfake.PagerResponder[v20231001preview.ResourceProvidersClientListProviderSummariesResponse]) + +} + +// NewResourceProvidersServerTransport creates a new instance of ResourceProvidersServerTransport with the provided implementation. +// The returned ResourceProvidersServerTransport instance is connected to an instance of v20231001preview.ResourceProvidersClient via the +// azcore.ClientOptions.Transporter field in the client's constructor parameters. +func NewResourceProvidersServerTransport(srv *ResourceProvidersServer) *ResourceProvidersServerTransport { + return &ResourceProvidersServerTransport{ + srv: srv, + beginCreateOrUpdate: newTracker[azfake.PollerResponder[v20231001preview.ResourceProvidersClientCreateOrUpdateResponse]](), + beginDelete: newTracker[azfake.PollerResponder[v20231001preview.ResourceProvidersClientDeleteResponse]](), + newListPager: newTracker[azfake.PagerResponder[v20231001preview.ResourceProvidersClientListResponse]](), + newListProviderSummariesPager: newTracker[azfake.PagerResponder[v20231001preview.ResourceProvidersClientListProviderSummariesResponse]](), + } +} + +// ResourceProvidersServerTransport connects instances of v20231001preview.ResourceProvidersClient to instances of ResourceProvidersServer. +// Don't use this type directly, use NewResourceProvidersServerTransport instead. +type ResourceProvidersServerTransport struct { + srv *ResourceProvidersServer + beginCreateOrUpdate *tracker[azfake.PollerResponder[v20231001preview.ResourceProvidersClientCreateOrUpdateResponse]] + beginDelete *tracker[azfake.PollerResponder[v20231001preview.ResourceProvidersClientDeleteResponse]] + newListPager *tracker[azfake.PagerResponder[v20231001preview.ResourceProvidersClientListResponse]] + newListProviderSummariesPager *tracker[azfake.PagerResponder[v20231001preview.ResourceProvidersClientListProviderSummariesResponse]] +} + +// Do implements the policy.Transporter interface for ResourceProvidersServerTransport. +func (r *ResourceProvidersServerTransport) Do(req *http.Request) (*http.Response, error) { + rawMethod := req.Context().Value(runtime.CtxAPINameKey{}) + method, ok := rawMethod.(string) + if !ok { + return nil, nonRetriableError{errors.New("unable to dispatch request, missing value for CtxAPINameKey")} + } + + return r.dispatchToMethodFake(req, method) +} + +func (r *ResourceProvidersServerTransport) dispatchToMethodFake(req *http.Request, method string) (*http.Response, error) { + resultChan := make(chan result) + defer close(resultChan) + + go func() { + var intercepted bool + var res result + if resourceProvidersServerTransportInterceptor != nil { + res.resp, res.err, intercepted = resourceProvidersServerTransportInterceptor.Do(req) + } + if !intercepted { + switch method { + case "ResourceProvidersClient.BeginCreateOrUpdate": + res.resp, res.err = r.dispatchBeginCreateOrUpdate(req) + case "ResourceProvidersClient.BeginDelete": + res.resp, res.err = r.dispatchBeginDelete(req) + case "ResourceProvidersClient.Get": + res.resp, res.err = r.dispatchGet(req) + case "ResourceProvidersClient.GetProviderSummary": + res.resp, res.err = r.dispatchGetProviderSummary(req) + case "ResourceProvidersClient.NewListPager": + res.resp, res.err = r.dispatchNewListPager(req) + case "ResourceProvidersClient.NewListProviderSummariesPager": + res.resp, res.err = r.dispatchNewListProviderSummariesPager(req) + default: + res.err = fmt.Errorf("unhandled API %s", method) + } + + } + select { + case resultChan <- res: + case <-req.Context().Done(): + } + }() + + select { + case <-req.Context().Done(): + return nil, req.Context().Err() + case res := <-resultChan: + return res.resp, res.err + } +} + +func (r *ResourceProvidersServerTransport) dispatchBeginCreateOrUpdate(req *http.Request) (*http.Response, error) { + if r.srv.BeginCreateOrUpdate == nil { + return nil, &nonRetriableError{errors.New("fake for method BeginCreateOrUpdate not implemented")} + } + beginCreateOrUpdate := r.beginCreateOrUpdate.get(req) + if beginCreateOrUpdate == nil { + const regexStr = `/planes/radius/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/providers/System\.Resources/resourceproviders/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 2 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + body, err := server.UnmarshalRequestAsJSON[v20231001preview.ResourceProviderResource](req) + if err != nil { + return nil, err + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + resourceProviderNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("resourceProviderName")]) + if err != nil { + return nil, err + } + respr, errRespr := r.srv.BeginCreateOrUpdate(req.Context(), planeNameParam, resourceProviderNameParam, body, nil) + if respErr := server.GetError(errRespr, req); respErr != nil { + return nil, respErr + } + beginCreateOrUpdate = &respr + r.beginCreateOrUpdate.add(req, beginCreateOrUpdate) + } + + resp, err := server.PollerResponderNext(beginCreateOrUpdate, req) + if err != nil { + return nil, err + } + + if !contains([]int{http.StatusOK, http.StatusCreated}, resp.StatusCode) { + r.beginCreateOrUpdate.remove(req) + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK, http.StatusCreated", resp.StatusCode)} + } + if !server.PollerResponderMore(beginCreateOrUpdate) { + r.beginCreateOrUpdate.remove(req) + } + + return resp, nil +} + +func (r *ResourceProvidersServerTransport) dispatchBeginDelete(req *http.Request) (*http.Response, error) { + if r.srv.BeginDelete == nil { + return nil, &nonRetriableError{errors.New("fake for method BeginDelete not implemented")} + } + beginDelete := r.beginDelete.get(req) + if beginDelete == nil { + const regexStr = `/planes/radius/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/providers/System\.Resources/resourceproviders/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 2 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + resourceProviderNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("resourceProviderName")]) + if err != nil { + return nil, err + } + respr, errRespr := r.srv.BeginDelete(req.Context(), planeNameParam, resourceProviderNameParam, nil) + if respErr := server.GetError(errRespr, req); respErr != nil { + return nil, respErr + } + beginDelete = &respr + r.beginDelete.add(req, beginDelete) + } + + resp, err := server.PollerResponderNext(beginDelete, req) + if err != nil { + return nil, err + } + + if !contains([]int{http.StatusOK, http.StatusAccepted, http.StatusNoContent}, resp.StatusCode) { + r.beginDelete.remove(req) + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK, http.StatusAccepted, http.StatusNoContent", resp.StatusCode)} + } + if !server.PollerResponderMore(beginDelete) { + r.beginDelete.remove(req) + } + + return resp, nil +} + +func (r *ResourceProvidersServerTransport) dispatchGet(req *http.Request) (*http.Response, error) { + if r.srv.Get == nil { + return nil, &nonRetriableError{errors.New("fake for method Get not implemented")} + } + const regexStr = `/planes/radius/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/providers/System\.Resources/resourceproviders/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 2 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + resourceProviderNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("resourceProviderName")]) + if err != nil { + return nil, err + } + respr, errRespr := r.srv.Get(req.Context(), planeNameParam, resourceProviderNameParam, nil) + if respErr := server.GetError(errRespr, req); respErr != nil { + return nil, respErr + } + respContent := server.GetResponseContent(respr) + if !contains([]int{http.StatusOK}, respContent.HTTPStatus) { + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK", respContent.HTTPStatus)} + } + resp, err := server.MarshalResponseAsJSON(respContent, server.GetResponse(respr).ResourceProviderResource, req) + if err != nil { + return nil, err + } + return resp, nil +} + +func (r *ResourceProvidersServerTransport) dispatchGetProviderSummary(req *http.Request) (*http.Response, error) { + if r.srv.GetProviderSummary == nil { + return nil, &nonRetriableError{errors.New("fake for method GetProviderSummary not implemented")} + } + const regexStr = `/planes/radius/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/providers/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 2 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + resourceProviderNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("resourceProviderName")]) + if err != nil { + return nil, err + } + respr, errRespr := r.srv.GetProviderSummary(req.Context(), planeNameParam, resourceProviderNameParam, nil) + if respErr := server.GetError(errRespr, req); respErr != nil { + return nil, respErr + } + respContent := server.GetResponseContent(respr) + if !contains([]int{http.StatusOK}, respContent.HTTPStatus) { + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK", respContent.HTTPStatus)} + } + resp, err := server.MarshalResponseAsJSON(respContent, server.GetResponse(respr).ResourceProviderSummary, req) + if err != nil { + return nil, err + } + return resp, nil +} + +func (r *ResourceProvidersServerTransport) dispatchNewListPager(req *http.Request) (*http.Response, error) { + if r.srv.NewListPager == nil { + return nil, &nonRetriableError{errors.New("fake for method NewListPager not implemented")} + } + newListPager := r.newListPager.get(req) + if newListPager == nil { + const regexStr = `/planes/radius/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/providers/System\.Resources/resourceproviders` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 1 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } +resp := r.srv.NewListPager(planeNameParam, nil) + newListPager = &resp + r.newListPager.add(req, newListPager) + server.PagerResponderInjectNextLinks(newListPager, req, func(page *v20231001preview.ResourceProvidersClientListResponse, createLink func() string) { + page.NextLink = to.Ptr(createLink()) + }) + } + resp, err := server.PagerResponderNext(newListPager, req) + if err != nil { + return nil, err + } + if !contains([]int{http.StatusOK}, resp.StatusCode) { + r.newListPager.remove(req) + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK", resp.StatusCode)} + } + if !server.PagerResponderMore(newListPager) { + r.newListPager.remove(req) + } + return resp, nil +} + +func (r *ResourceProvidersServerTransport) dispatchNewListProviderSummariesPager(req *http.Request) (*http.Response, error) { + if r.srv.NewListProviderSummariesPager == nil { + return nil, &nonRetriableError{errors.New("fake for method NewListProviderSummariesPager not implemented")} + } + newListProviderSummariesPager := r.newListProviderSummariesPager.get(req) + if newListProviderSummariesPager == nil { + const regexStr = `/planes/radius/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/providers` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 1 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } +resp := r.srv.NewListProviderSummariesPager(planeNameParam, nil) + newListProviderSummariesPager = &resp + r.newListProviderSummariesPager.add(req, newListProviderSummariesPager) + server.PagerResponderInjectNextLinks(newListProviderSummariesPager, req, func(page *v20231001preview.ResourceProvidersClientListProviderSummariesResponse, createLink func() string) { + page.NextLink = to.Ptr(createLink()) + }) + } + resp, err := server.PagerResponderNext(newListProviderSummariesPager, req) + if err != nil { + return nil, err + } + if !contains([]int{http.StatusOK}, resp.StatusCode) { + r.newListProviderSummariesPager.remove(req) + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK", resp.StatusCode)} + } + if !server.PagerResponderMore(newListProviderSummariesPager) { + r.newListProviderSummariesPager.remove(req) + } + return resp, nil +} + +// set this to conditionally intercept incoming requests to ResourceProvidersServerTransport +var resourceProvidersServerTransportInterceptor interface { + // Do returns true if the server transport should use the returned response/error + Do(*http.Request) (*http.Response, error, bool) +} diff --git a/pkg/ucp/api/v20231001preview/fake/zz_generated_resources_server.go b/pkg/ucp/api/v20231001preview/fake/zz_generated_resources_server.go new file mode 100644 index 0000000000..39f575e452 --- /dev/null +++ b/pkg/ucp/api/v20231001preview/fake/zz_generated_resources_server.go @@ -0,0 +1,134 @@ +// Licensed under the Apache License, Version 2.0 . See LICENSE in the repository root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package fake + +import ( + "errors" + "fmt" + azfake "github.com/Azure/azure-sdk-for-go/sdk/azcore/fake" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/fake/server" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" + "net/http" + "net/url" + "regexp" +) + +// ResourcesServer is a fake server for instances of the v20231001preview.ResourcesClient type. +type ResourcesServer struct{ + // NewListPager is the fake for method ResourcesClient.NewListPager + // HTTP status codes to indicate success: http.StatusOK + NewListPager func(planeName string, resourceGroupName string, options *v20231001preview.ResourcesClientListOptions) (resp azfake.PagerResponder[v20231001preview.ResourcesClientListResponse]) + +} + +// NewResourcesServerTransport creates a new instance of ResourcesServerTransport with the provided implementation. +// The returned ResourcesServerTransport instance is connected to an instance of v20231001preview.ResourcesClient via the +// azcore.ClientOptions.Transporter field in the client's constructor parameters. +func NewResourcesServerTransport(srv *ResourcesServer) *ResourcesServerTransport { + return &ResourcesServerTransport{ + srv: srv, + newListPager: newTracker[azfake.PagerResponder[v20231001preview.ResourcesClientListResponse]](), + } +} + +// ResourcesServerTransport connects instances of v20231001preview.ResourcesClient to instances of ResourcesServer. +// Don't use this type directly, use NewResourcesServerTransport instead. +type ResourcesServerTransport struct { + srv *ResourcesServer + newListPager *tracker[azfake.PagerResponder[v20231001preview.ResourcesClientListResponse]] +} + +// Do implements the policy.Transporter interface for ResourcesServerTransport. +func (r *ResourcesServerTransport) Do(req *http.Request) (*http.Response, error) { + rawMethod := req.Context().Value(runtime.CtxAPINameKey{}) + method, ok := rawMethod.(string) + if !ok { + return nil, nonRetriableError{errors.New("unable to dispatch request, missing value for CtxAPINameKey")} + } + + return r.dispatchToMethodFake(req, method) +} + +func (r *ResourcesServerTransport) dispatchToMethodFake(req *http.Request, method string) (*http.Response, error) { + resultChan := make(chan result) + defer close(resultChan) + + go func() { + var intercepted bool + var res result + if resourcesServerTransportInterceptor != nil { + res.resp, res.err, intercepted = resourcesServerTransportInterceptor.Do(req) + } + if !intercepted { + switch method { + case "ResourcesClient.NewListPager": + res.resp, res.err = r.dispatchNewListPager(req) + default: + res.err = fmt.Errorf("unhandled API %s", method) + } + + } + select { + case resultChan <- res: + case <-req.Context().Done(): + } + }() + + select { + case <-req.Context().Done(): + return nil, req.Context().Err() + case res := <-resultChan: + return res.resp, res.err + } +} + +func (r *ResourcesServerTransport) dispatchNewListPager(req *http.Request) (*http.Response, error) { + if r.srv.NewListPager == nil { + return nil, &nonRetriableError{errors.New("fake for method NewListPager not implemented")} + } + newListPager := r.newListPager.get(req) + if newListPager == nil { + const regexStr = `/planes/radius/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/resourcegroups/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/resources` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 2 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + resourceGroupNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("resourceGroupName")]) + if err != nil { + return nil, err + } +resp := r.srv.NewListPager(planeNameParam, resourceGroupNameParam, nil) + newListPager = &resp + r.newListPager.add(req, newListPager) + server.PagerResponderInjectNextLinks(newListPager, req, func(page *v20231001preview.ResourcesClientListResponse, createLink func() string) { + page.NextLink = to.Ptr(createLink()) + }) + } + resp, err := server.PagerResponderNext(newListPager, req) + if err != nil { + return nil, err + } + if !contains([]int{http.StatusOK}, resp.StatusCode) { + r.newListPager.remove(req) + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK", resp.StatusCode)} + } + if !server.PagerResponderMore(newListPager) { + r.newListPager.remove(req) + } + return resp, nil +} + +// set this to conditionally intercept incoming requests to ResourcesServerTransport +var resourcesServerTransportInterceptor interface { + // Do returns true if the server transport should use the returned response/error + Do(*http.Request) (*http.Response, error, bool) +} diff --git a/pkg/ucp/api/v20231001preview/fake/zz_generated_resourcetypes_server.go b/pkg/ucp/api/v20231001preview/fake/zz_generated_resourcetypes_server.go new file mode 100644 index 0000000000..d745f522d0 --- /dev/null +++ b/pkg/ucp/api/v20231001preview/fake/zz_generated_resourcetypes_server.go @@ -0,0 +1,294 @@ +// Licensed under the Apache License, Version 2.0 . See LICENSE in the repository root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package fake + +import ( + "context" + "errors" + "fmt" + azfake "github.com/Azure/azure-sdk-for-go/sdk/azcore/fake" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/fake/server" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" + "net/http" + "net/url" + "regexp" +) + +// ResourceTypesServer is a fake server for instances of the v20231001preview.ResourceTypesClient type. +type ResourceTypesServer struct{ + // BeginCreateOrUpdate is the fake for method ResourceTypesClient.BeginCreateOrUpdate + // HTTP status codes to indicate success: http.StatusOK, http.StatusCreated + 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) + + // BeginDelete is the fake for method ResourceTypesClient.BeginDelete + // HTTP status codes to indicate success: http.StatusOK, http.StatusAccepted, http.StatusNoContent + BeginDelete func(ctx context.Context, planeName string, resourceProviderName string, resourceTypeName string, options *v20231001preview.ResourceTypesClientBeginDeleteOptions) (resp azfake.PollerResponder[v20231001preview.ResourceTypesClientDeleteResponse], errResp azfake.ErrorResponder) + + // Get is the fake for method ResourceTypesClient.Get + // HTTP status codes to indicate success: http.StatusOK + Get func(ctx context.Context, planeName string, resourceProviderName string, resourceTypeName string, options *v20231001preview.ResourceTypesClientGetOptions) (resp azfake.Responder[v20231001preview.ResourceTypesClientGetResponse], errResp azfake.ErrorResponder) + + // NewListPager is the fake for method ResourceTypesClient.NewListPager + // HTTP status codes to indicate success: http.StatusOK + NewListPager func(planeName string, resourceProviderName string, options *v20231001preview.ResourceTypesClientListOptions) (resp azfake.PagerResponder[v20231001preview.ResourceTypesClientListResponse]) + +} + +// NewResourceTypesServerTransport creates a new instance of ResourceTypesServerTransport with the provided implementation. +// The returned ResourceTypesServerTransport instance is connected to an instance of v20231001preview.ResourceTypesClient via the +// azcore.ClientOptions.Transporter field in the client's constructor parameters. +func NewResourceTypesServerTransport(srv *ResourceTypesServer) *ResourceTypesServerTransport { + return &ResourceTypesServerTransport{ + srv: srv, + beginCreateOrUpdate: newTracker[azfake.PollerResponder[v20231001preview.ResourceTypesClientCreateOrUpdateResponse]](), + beginDelete: newTracker[azfake.PollerResponder[v20231001preview.ResourceTypesClientDeleteResponse]](), + newListPager: newTracker[azfake.PagerResponder[v20231001preview.ResourceTypesClientListResponse]](), + } +} + +// ResourceTypesServerTransport connects instances of v20231001preview.ResourceTypesClient to instances of ResourceTypesServer. +// Don't use this type directly, use NewResourceTypesServerTransport instead. +type ResourceTypesServerTransport struct { + srv *ResourceTypesServer + beginCreateOrUpdate *tracker[azfake.PollerResponder[v20231001preview.ResourceTypesClientCreateOrUpdateResponse]] + beginDelete *tracker[azfake.PollerResponder[v20231001preview.ResourceTypesClientDeleteResponse]] + newListPager *tracker[azfake.PagerResponder[v20231001preview.ResourceTypesClientListResponse]] +} + +// Do implements the policy.Transporter interface for ResourceTypesServerTransport. +func (r *ResourceTypesServerTransport) Do(req *http.Request) (*http.Response, error) { + rawMethod := req.Context().Value(runtime.CtxAPINameKey{}) + method, ok := rawMethod.(string) + if !ok { + return nil, nonRetriableError{errors.New("unable to dispatch request, missing value for CtxAPINameKey")} + } + + return r.dispatchToMethodFake(req, method) +} + +func (r *ResourceTypesServerTransport) dispatchToMethodFake(req *http.Request, method string) (*http.Response, error) { + resultChan := make(chan result) + defer close(resultChan) + + go func() { + var intercepted bool + var res result + if resourceTypesServerTransportInterceptor != nil { + res.resp, res.err, intercepted = resourceTypesServerTransportInterceptor.Do(req) + } + if !intercepted { + switch method { + case "ResourceTypesClient.BeginCreateOrUpdate": + res.resp, res.err = r.dispatchBeginCreateOrUpdate(req) + case "ResourceTypesClient.BeginDelete": + res.resp, res.err = r.dispatchBeginDelete(req) + case "ResourceTypesClient.Get": + res.resp, res.err = r.dispatchGet(req) + case "ResourceTypesClient.NewListPager": + res.resp, res.err = r.dispatchNewListPager(req) + default: + res.err = fmt.Errorf("unhandled API %s", method) + } + + } + select { + case resultChan <- res: + case <-req.Context().Done(): + } + }() + + select { + case <-req.Context().Done(): + return nil, req.Context().Err() + case res := <-resultChan: + return res.resp, res.err + } +} + +func (r *ResourceTypesServerTransport) dispatchBeginCreateOrUpdate(req *http.Request) (*http.Response, error) { + if r.srv.BeginCreateOrUpdate == nil { + return nil, &nonRetriableError{errors.New("fake for method BeginCreateOrUpdate not implemented")} + } + beginCreateOrUpdate := r.beginCreateOrUpdate.get(req) + if beginCreateOrUpdate == nil { + const regexStr = `/planes/radius/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/providers/System\.Resources/resourceproviders/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/resourcetypes/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 3 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + body, err := server.UnmarshalRequestAsJSON[v20231001preview.ResourceTypeResource](req) + if err != nil { + return nil, err + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + resourceProviderNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("resourceProviderName")]) + if err != nil { + return nil, err + } + resourceTypeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("resourceTypeName")]) + if err != nil { + return nil, err + } + respr, errRespr := r.srv.BeginCreateOrUpdate(req.Context(), planeNameParam, resourceProviderNameParam, resourceTypeNameParam, body, nil) + if respErr := server.GetError(errRespr, req); respErr != nil { + return nil, respErr + } + beginCreateOrUpdate = &respr + r.beginCreateOrUpdate.add(req, beginCreateOrUpdate) + } + + resp, err := server.PollerResponderNext(beginCreateOrUpdate, req) + if err != nil { + return nil, err + } + + if !contains([]int{http.StatusOK, http.StatusCreated}, resp.StatusCode) { + r.beginCreateOrUpdate.remove(req) + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK, http.StatusCreated", resp.StatusCode)} + } + if !server.PollerResponderMore(beginCreateOrUpdate) { + r.beginCreateOrUpdate.remove(req) + } + + return resp, nil +} + +func (r *ResourceTypesServerTransport) dispatchBeginDelete(req *http.Request) (*http.Response, error) { + if r.srv.BeginDelete == nil { + return nil, &nonRetriableError{errors.New("fake for method BeginDelete not implemented")} + } + beginDelete := r.beginDelete.get(req) + if beginDelete == nil { + const regexStr = `/planes/radius/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/providers/System\.Resources/resourceproviders/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/resourcetypes/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 3 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + resourceProviderNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("resourceProviderName")]) + if err != nil { + return nil, err + } + resourceTypeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("resourceTypeName")]) + if err != nil { + return nil, err + } + respr, errRespr := r.srv.BeginDelete(req.Context(), planeNameParam, resourceProviderNameParam, resourceTypeNameParam, nil) + if respErr := server.GetError(errRespr, req); respErr != nil { + return nil, respErr + } + beginDelete = &respr + r.beginDelete.add(req, beginDelete) + } + + resp, err := server.PollerResponderNext(beginDelete, req) + if err != nil { + return nil, err + } + + if !contains([]int{http.StatusOK, http.StatusAccepted, http.StatusNoContent}, resp.StatusCode) { + r.beginDelete.remove(req) + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK, http.StatusAccepted, http.StatusNoContent", resp.StatusCode)} + } + if !server.PollerResponderMore(beginDelete) { + r.beginDelete.remove(req) + } + + return resp, nil +} + +func (r *ResourceTypesServerTransport) dispatchGet(req *http.Request) (*http.Response, error) { + if r.srv.Get == nil { + return nil, &nonRetriableError{errors.New("fake for method Get not implemented")} + } + const regexStr = `/planes/radius/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/providers/System\.Resources/resourceproviders/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/resourcetypes/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 3 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + resourceProviderNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("resourceProviderName")]) + if err != nil { + return nil, err + } + resourceTypeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("resourceTypeName")]) + if err != nil { + return nil, err + } + respr, errRespr := r.srv.Get(req.Context(), planeNameParam, resourceProviderNameParam, resourceTypeNameParam, nil) + if respErr := server.GetError(errRespr, req); respErr != nil { + return nil, respErr + } + respContent := server.GetResponseContent(respr) + if !contains([]int{http.StatusOK}, respContent.HTTPStatus) { + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK", respContent.HTTPStatus)} + } + resp, err := server.MarshalResponseAsJSON(respContent, server.GetResponse(respr).ResourceTypeResource, req) + if err != nil { + return nil, err + } + return resp, nil +} + +func (r *ResourceTypesServerTransport) dispatchNewListPager(req *http.Request) (*http.Response, error) { + if r.srv.NewListPager == nil { + return nil, &nonRetriableError{errors.New("fake for method NewListPager not implemented")} + } + newListPager := r.newListPager.get(req) + if newListPager == nil { + const regexStr = `/planes/radius/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/providers/System\.Resources/resourceproviders/(?P[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/resourcetypes` + regex := regexp.MustCompile(regexStr) + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) + if matches == nil || len(matches) < 2 { + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) + } + planeNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("planeName")]) + if err != nil { + return nil, err + } + resourceProviderNameParam, err := url.PathUnescape(matches[regex.SubexpIndex("resourceProviderName")]) + if err != nil { + return nil, err + } +resp := r.srv.NewListPager(planeNameParam, resourceProviderNameParam, nil) + newListPager = &resp + r.newListPager.add(req, newListPager) + server.PagerResponderInjectNextLinks(newListPager, req, func(page *v20231001preview.ResourceTypesClientListResponse, createLink func() string) { + page.NextLink = to.Ptr(createLink()) + }) + } + resp, err := server.PagerResponderNext(newListPager, req) + if err != nil { + return nil, err + } + if !contains([]int{http.StatusOK}, resp.StatusCode) { + r.newListPager.remove(req) + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK", resp.StatusCode)} + } + if !server.PagerResponderMore(newListPager) { + r.newListPager.remove(req) + } + return resp, nil +} + +// set this to conditionally intercept incoming requests to ResourceTypesServerTransport +var resourceTypesServerTransportInterceptor interface { + // Do returns true if the server transport should use the returned response/error + Do(*http.Request) (*http.Response, error, bool) +} diff --git a/pkg/ucp/api/v20231001preview/fake/zz_generated_server_factory.go b/pkg/ucp/api/v20231001preview/fake/zz_generated_server_factory.go new file mode 100644 index 0000000000..90ceab79c8 --- /dev/null +++ b/pkg/ucp/api/v20231001preview/fake/zz_generated_server_factory.go @@ -0,0 +1,150 @@ +// Licensed under the Apache License, Version 2.0 . See LICENSE in the repository root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package fake + +import ( + "errors" + "fmt" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" + "net/http" + "strings" + "sync" +) + +// ServerFactory is a fake server for instances of the v20231001preview.ClientFactory type. +type ServerFactory struct { + // APIVersionsServer contains the fakes for client APIVersionsClient + APIVersionsServer APIVersionsServer + + // AwsCredentialsServer contains the fakes for client AwsCredentialsClient + AwsCredentialsServer AwsCredentialsServer + + // AwsPlanesServer contains the fakes for client AwsPlanesClient + AwsPlanesServer AwsPlanesServer + + // AzureCredentialsServer contains the fakes for client AzureCredentialsClient + AzureCredentialsServer AzureCredentialsServer + + // AzurePlanesServer contains the fakes for client AzurePlanesClient + AzurePlanesServer AzurePlanesServer + + // LocationsServer contains the fakes for client LocationsClient + LocationsServer LocationsServer + + // PlanesServer contains the fakes for client PlanesClient + PlanesServer PlanesServer + + // RadiusPlanesServer contains the fakes for client RadiusPlanesClient + RadiusPlanesServer RadiusPlanesServer + + // ResourceGroupsServer contains the fakes for client ResourceGroupsClient + ResourceGroupsServer ResourceGroupsServer + + // ResourceProvidersServer contains the fakes for client ResourceProvidersClient + ResourceProvidersServer ResourceProvidersServer + + // ResourceTypesServer contains the fakes for client ResourceTypesClient + ResourceTypesServer ResourceTypesServer + + // ResourcesServer contains the fakes for client ResourcesClient + ResourcesServer ResourcesServer + +} + +// NewServerFactoryTransport creates a new instance of ServerFactoryTransport with the provided implementation. +// The returned ServerFactoryTransport instance is connected to an instance of v20231001preview.ClientFactory via the +// azcore.ClientOptions.Transporter field in the client's constructor parameters. +func NewServerFactoryTransport(srv *ServerFactory) *ServerFactoryTransport { + return &ServerFactoryTransport{ + srv: srv, + } +} + +// ServerFactoryTransport connects instances of v20231001preview.ClientFactory to instances of ServerFactory. +// Don't use this type directly, use NewServerFactoryTransport instead. +type ServerFactoryTransport struct { + srv *ServerFactory + trMu sync.Mutex + trAPIVersionsServer *APIVersionsServerTransport + trAwsCredentialsServer *AwsCredentialsServerTransport + trAwsPlanesServer *AwsPlanesServerTransport + trAzureCredentialsServer *AzureCredentialsServerTransport + trAzurePlanesServer *AzurePlanesServerTransport + trLocationsServer *LocationsServerTransport + trPlanesServer *PlanesServerTransport + trRadiusPlanesServer *RadiusPlanesServerTransport + trResourceGroupsServer *ResourceGroupsServerTransport + trResourceProvidersServer *ResourceProvidersServerTransport + trResourceTypesServer *ResourceTypesServerTransport + trResourcesServer *ResourcesServerTransport +} + +// Do implements the policy.Transporter interface for ServerFactoryTransport. +func (s *ServerFactoryTransport) Do(req *http.Request) (*http.Response, error) { + rawMethod := req.Context().Value(runtime.CtxAPINameKey{}) + method, ok := rawMethod.(string) + if !ok { + return nil, nonRetriableError{errors.New("unable to dispatch request, missing value for CtxAPINameKey")} + } + + client := method[:strings.Index(method, ".")] + var resp *http.Response + var err error + + switch client { + case "APIVersionsClient": + initServer(s, &s.trAPIVersionsServer, func() *APIVersionsServerTransport { return NewAPIVersionsServerTransport(&s.srv.APIVersionsServer) }) + resp, err = s.trAPIVersionsServer.Do(req) + case "AwsCredentialsClient": + initServer(s, &s.trAwsCredentialsServer, func() *AwsCredentialsServerTransport { return NewAwsCredentialsServerTransport(&s.srv.AwsCredentialsServer) }) + resp, err = s.trAwsCredentialsServer.Do(req) + case "AwsPlanesClient": + initServer(s, &s.trAwsPlanesServer, func() *AwsPlanesServerTransport { return NewAwsPlanesServerTransport(&s.srv.AwsPlanesServer) }) + resp, err = s.trAwsPlanesServer.Do(req) + case "AzureCredentialsClient": + initServer(s, &s.trAzureCredentialsServer, func() *AzureCredentialsServerTransport { return NewAzureCredentialsServerTransport(&s.srv.AzureCredentialsServer) }) + resp, err = s.trAzureCredentialsServer.Do(req) + case "AzurePlanesClient": + initServer(s, &s.trAzurePlanesServer, func() *AzurePlanesServerTransport { return NewAzurePlanesServerTransport(&s.srv.AzurePlanesServer) }) + resp, err = s.trAzurePlanesServer.Do(req) + case "LocationsClient": + initServer(s, &s.trLocationsServer, func() *LocationsServerTransport { return NewLocationsServerTransport(&s.srv.LocationsServer) }) + resp, err = s.trLocationsServer.Do(req) + case "PlanesClient": + initServer(s, &s.trPlanesServer, func() *PlanesServerTransport { return NewPlanesServerTransport(&s.srv.PlanesServer) }) + resp, err = s.trPlanesServer.Do(req) + case "RadiusPlanesClient": + initServer(s, &s.trRadiusPlanesServer, func() *RadiusPlanesServerTransport { return NewRadiusPlanesServerTransport(&s.srv.RadiusPlanesServer) }) + resp, err = s.trRadiusPlanesServer.Do(req) + case "ResourceGroupsClient": + initServer(s, &s.trResourceGroupsServer, func() *ResourceGroupsServerTransport { return NewResourceGroupsServerTransport(&s.srv.ResourceGroupsServer) }) + resp, err = s.trResourceGroupsServer.Do(req) + case "ResourceProvidersClient": + initServer(s, &s.trResourceProvidersServer, func() *ResourceProvidersServerTransport { return NewResourceProvidersServerTransport(&s.srv.ResourceProvidersServer) }) + resp, err = s.trResourceProvidersServer.Do(req) + case "ResourceTypesClient": + initServer(s, &s.trResourceTypesServer, func() *ResourceTypesServerTransport { return NewResourceTypesServerTransport(&s.srv.ResourceTypesServer) }) + resp, err = s.trResourceTypesServer.Do(req) + case "ResourcesClient": + initServer(s, &s.trResourcesServer, func() *ResourcesServerTransport { return NewResourcesServerTransport(&s.srv.ResourcesServer) }) + resp, err = s.trResourcesServer.Do(req) + default: + err = fmt.Errorf("unhandled client %s", client) + } + + if err != nil { + return nil, err + } + + return resp, nil +} + +func initServer[T any](s *ServerFactoryTransport, dst **T, src func() *T) { + s.trMu.Lock() + if *dst == nil { + *dst = src() + } + s.trMu.Unlock() +} diff --git a/pkg/ucp/api/v20231001preview/fake/zz_generated_time_rfc3339.go b/pkg/ucp/api/v20231001preview/fake/zz_generated_time_rfc3339.go new file mode 100644 index 0000000000..83c75cddc6 --- /dev/null +++ b/pkg/ucp/api/v20231001preview/fake/zz_generated_time_rfc3339.go @@ -0,0 +1,114 @@ +// Licensed under the Apache License, Version 2.0 . See LICENSE in the repository root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package fake + + + +import ( + "encoding/json" + "fmt" + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "reflect" + "regexp" + "strings" + "time" +) + + + +// Azure reports time in UTC but it doesn't include the 'Z' time zone suffix in some cases. +var tzOffsetRegex = regexp.MustCompile(`(?:Z|z|\+|-)(?:\d+:\d+)*"*$`) + +const ( + utcDateTime = "2006-01-02T15:04:05.999999999" + utcDateTimeJSON = `"` + utcDateTime + `"` + utcDateTimeNoT = "2006-01-02 15:04:05.999999999" + utcDateTimeJSONNoT = `"` + utcDateTimeNoT + `"` + dateTimeNoT = `2006-01-02 15:04:05.999999999Z07:00` + dateTimeJSON = `"` + time.RFC3339Nano + `"` + dateTimeJSONNoT = `"` + dateTimeNoT + `"` +) + +type dateTimeRFC3339 time.Time + +func (t dateTimeRFC3339) MarshalJSON() ([]byte, error) { + tt := time.Time(t) + return tt.MarshalJSON() +} + +func (t dateTimeRFC3339) MarshalText() ([]byte, error) { + tt := time.Time(t) + return tt.MarshalText() +} + +func (t *dateTimeRFC3339) UnmarshalJSON(data []byte) error { + tzOffset := tzOffsetRegex.Match(data) + hasT := strings.Contains(string(data), "T") || strings.Contains(string(data), "t") + var layout string + if tzOffset && hasT { + layout = dateTimeJSON + } else if tzOffset { + layout = dateTimeJSONNoT + } else if hasT { + layout = utcDateTimeJSON + } else { + layout = utcDateTimeJSONNoT + } + return t.Parse(layout, string(data)) +} + +func (t *dateTimeRFC3339) UnmarshalText(data []byte) error { + if len(data) == 0 { + return nil + } + tzOffset := tzOffsetRegex.Match(data) + hasT := strings.Contains(string(data), "T") || strings.Contains(string(data), "t") + var layout string + if tzOffset && hasT { + layout = time.RFC3339Nano + } else if tzOffset { + layout = dateTimeNoT + } else if hasT { + layout = utcDateTime + } else { + layout = utcDateTimeNoT + } + return t.Parse(layout, string(data)) +} + +func (t *dateTimeRFC3339) Parse(layout, value string) error { + p, err := time.Parse(layout, strings.ToUpper(value)) + *t = dateTimeRFC3339(p) + return err +} + +func (t dateTimeRFC3339) String() string { + return time.Time(t).Format(time.RFC3339Nano) +} + + +func populateDateTimeRFC3339(m map[string]any, k string, t *time.Time) { + if t == nil { + return + } else if azcore.IsNullValue(t) { + m[k] = nil + return + } else if reflect.ValueOf(t).IsNil() { + return + } + m[k] = (*dateTimeRFC3339)(t) +} + +func unpopulateDateTimeRFC3339(data json.RawMessage, fn string, t **time.Time) error { + if data == nil || string(data) == "null" { + return nil + } + var aux dateTimeRFC3339 + if err := json.Unmarshal(data, &aux); err != nil { + return fmt.Errorf("struct field %s: %v", fn, err) + } + *t = (*time.Time)(&aux) + return nil +} diff --git a/pkg/ucp/api/v20231001preview/zz_generated_apiversions_client.go b/pkg/ucp/api/v20231001preview/zz_generated_apiversions_client.go index d050dd8ec8..7910252031 100644 --- a/pkg/ucp/api/v20231001preview/zz_generated_apiversions_client.go +++ b/pkg/ucp/api/v20231001preview/zz_generated_apiversions_client.go @@ -71,7 +71,9 @@ func (client *APIVersionsClient) BeginCreateOrUpdate(ctx context.Context, planeN // Generated from API version 2023-10-01-preview func (client *APIVersionsClient) createOrUpdate(ctx context.Context, planeName string, resourceProviderName string, resourceTypeName string, apiVersionName string, resource APIVersionResource, options *APIVersionsClientBeginCreateOrUpdateOptions) (*http.Response, error) { var err error - ctx, endSpan := runtime.StartSpan(ctx, "APIVersionsClient.BeginCreateOrUpdate", client.internal.Tracer(), nil) + const operationName = "APIVersionsClient.BeginCreateOrUpdate" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) defer func() { endSpan(err) }() req, err := client.createOrUpdateCreateRequest(ctx, planeName, resourceProviderName, resourceTypeName, apiVersionName, resource, options) if err != nil { @@ -154,7 +156,9 @@ func (client *APIVersionsClient) BeginDelete(ctx context.Context, planeName stri // Generated from API version 2023-10-01-preview func (client *APIVersionsClient) deleteOperation(ctx context.Context, planeName string, resourceProviderName string, resourceTypeName string, apiVersionName string, options *APIVersionsClientBeginDeleteOptions) (*http.Response, error) { var err error - ctx, endSpan := runtime.StartSpan(ctx, "APIVersionsClient.BeginDelete", client.internal.Tracer(), nil) + const operationName = "APIVersionsClient.BeginDelete" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) defer func() { endSpan(err) }() req, err := client.deleteCreateRequest(ctx, planeName, resourceProviderName, resourceTypeName, apiVersionName, options) if err != nil { @@ -212,7 +216,9 @@ func (client *APIVersionsClient) deleteCreateRequest(ctx context.Context, planeN // - options - APIVersionsClientGetOptions contains the optional parameters for the APIVersionsClient.Get method. func (client *APIVersionsClient) Get(ctx context.Context, planeName string, resourceProviderName string, resourceTypeName string, apiVersionName string, options *APIVersionsClientGetOptions) (APIVersionsClientGetResponse, error) { var err error - ctx, endSpan := runtime.StartSpan(ctx, "APIVersionsClient.Get", client.internal.Tracer(), nil) + const operationName = "APIVersionsClient.Get" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) defer func() { endSpan(err) }() req, err := client.getCreateRequest(ctx, planeName, resourceProviderName, resourceTypeName, apiVersionName, options) if err != nil { @@ -282,6 +288,7 @@ func (client *APIVersionsClient) NewListPager(planeName string, resourceProvider return page.NextLink != nil && len(*page.NextLink) > 0 }, Fetcher: func(ctx context.Context, page *APIVersionsClientListResponse) (APIVersionsClientListResponse, error) { + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, "APIVersionsClient.NewListPager") nextLink := "" if page != nil { nextLink = *page.NextLink diff --git a/pkg/ucp/api/v20231001preview/zz_generated_awscredentials_client.go b/pkg/ucp/api/v20231001preview/zz_generated_awscredentials_client.go index c460688e0a..20c91b7411 100644 --- a/pkg/ucp/api/v20231001preview/zz_generated_awscredentials_client.go +++ b/pkg/ucp/api/v20231001preview/zz_generated_awscredentials_client.go @@ -47,7 +47,9 @@ func NewAwsCredentialsClient(credential azcore.TokenCredential, options *arm.Cli // method. func (client *AwsCredentialsClient) CreateOrUpdate(ctx context.Context, planeName string, credentialName string, resource AwsCredentialResource, options *AwsCredentialsClientCreateOrUpdateOptions) (AwsCredentialsClientCreateOrUpdateResponse, error) { var err error - ctx, endSpan := runtime.StartSpan(ctx, "AwsCredentialsClient.CreateOrUpdate", client.internal.Tracer(), nil) + const operationName = "AwsCredentialsClient.CreateOrUpdate" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) defer func() { endSpan(err) }() req, err := client.createOrUpdateCreateRequest(ctx, planeName, credentialName, resource, options) if err != nil { @@ -105,7 +107,9 @@ func (client *AwsCredentialsClient) createOrUpdateHandleResponse(resp *http.Resp // - options - AwsCredentialsClientDeleteOptions contains the optional parameters for the AwsCredentialsClient.Delete method. func (client *AwsCredentialsClient) Delete(ctx context.Context, planeName string, credentialName string, options *AwsCredentialsClientDeleteOptions) (AwsCredentialsClientDeleteResponse, error) { var err error - ctx, endSpan := runtime.StartSpan(ctx, "AwsCredentialsClient.Delete", client.internal.Tracer(), nil) + const operationName = "AwsCredentialsClient.Delete" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) defer func() { endSpan(err) }() req, err := client.deleteCreateRequest(ctx, planeName, credentialName, options) if err != nil { @@ -150,7 +154,9 @@ func (client *AwsCredentialsClient) deleteCreateRequest(ctx context.Context, pla // - options - AwsCredentialsClientGetOptions contains the optional parameters for the AwsCredentialsClient.Get method. func (client *AwsCredentialsClient) Get(ctx context.Context, planeName string, credentialName string, options *AwsCredentialsClientGetOptions) (AwsCredentialsClientGetResponse, error) { var err error - ctx, endSpan := runtime.StartSpan(ctx, "AwsCredentialsClient.Get", client.internal.Tracer(), nil) + const operationName = "AwsCredentialsClient.Get" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) defer func() { endSpan(err) }() req, err := client.getCreateRequest(ctx, planeName, credentialName, options) if err != nil { @@ -207,6 +213,7 @@ func (client *AwsCredentialsClient) NewListPager(planeName string, options *AwsC return page.NextLink != nil && len(*page.NextLink) > 0 }, Fetcher: func(ctx context.Context, page *AwsCredentialsClientListResponse) (AwsCredentialsClientListResponse, error) { + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, "AwsCredentialsClient.NewListPager") nextLink := "" if page != nil { nextLink = *page.NextLink @@ -257,7 +264,9 @@ func (client *AwsCredentialsClient) listHandleResponse(resp *http.Response) (Aws // - options - AwsCredentialsClientUpdateOptions contains the optional parameters for the AwsCredentialsClient.Update method. func (client *AwsCredentialsClient) Update(ctx context.Context, planeName string, credentialName string, properties AwsCredentialResourceTagsUpdate, options *AwsCredentialsClientUpdateOptions) (AwsCredentialsClientUpdateResponse, error) { var err error - ctx, endSpan := runtime.StartSpan(ctx, "AwsCredentialsClient.Update", client.internal.Tracer(), nil) + const operationName = "AwsCredentialsClient.Update" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) defer func() { endSpan(err) }() req, err := client.updateCreateRequest(ctx, planeName, credentialName, properties, options) if err != nil { diff --git a/pkg/ucp/api/v20231001preview/zz_generated_awsplanes_client.go b/pkg/ucp/api/v20231001preview/zz_generated_awsplanes_client.go index df7b97bfb1..18444b3475 100644 --- a/pkg/ucp/api/v20231001preview/zz_generated_awsplanes_client.go +++ b/pkg/ucp/api/v20231001preview/zz_generated_awsplanes_client.go @@ -68,7 +68,9 @@ func (client *AwsPlanesClient) BeginCreateOrUpdate(ctx context.Context, planeNam // Generated from API version 2023-10-01-preview func (client *AwsPlanesClient) createOrUpdate(ctx context.Context, planeName string, resource AwsPlaneResource, options *AwsPlanesClientBeginCreateOrUpdateOptions) (*http.Response, error) { var err error - ctx, endSpan := runtime.StartSpan(ctx, "AwsPlanesClient.BeginCreateOrUpdate", client.internal.Tracer(), nil) + const operationName = "AwsPlanesClient.BeginCreateOrUpdate" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) defer func() { endSpan(err) }() req, err := client.createOrUpdateCreateRequest(ctx, planeName, resource, options) if err != nil { @@ -136,7 +138,9 @@ func (client *AwsPlanesClient) BeginDelete(ctx context.Context, planeName string // Generated from API version 2023-10-01-preview func (client *AwsPlanesClient) deleteOperation(ctx context.Context, planeName string, options *AwsPlanesClientBeginDeleteOptions) (*http.Response, error) { var err error - ctx, endSpan := runtime.StartSpan(ctx, "AwsPlanesClient.BeginDelete", client.internal.Tracer(), nil) + const operationName = "AwsPlanesClient.BeginDelete" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) defer func() { endSpan(err) }() req, err := client.deleteCreateRequest(ctx, planeName, options) if err != nil { @@ -179,7 +183,9 @@ func (client *AwsPlanesClient) deleteCreateRequest(ctx context.Context, planeNam // - options - AwsPlanesClientGetOptions contains the optional parameters for the AwsPlanesClient.Get method. func (client *AwsPlanesClient) Get(ctx context.Context, planeName string, options *AwsPlanesClientGetOptions) (AwsPlanesClientGetResponse, error) { var err error - ctx, endSpan := runtime.StartSpan(ctx, "AwsPlanesClient.Get", client.internal.Tracer(), nil) + const operationName = "AwsPlanesClient.Get" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) defer func() { endSpan(err) }() req, err := client.getCreateRequest(ctx, planeName, options) if err != nil { @@ -234,6 +240,7 @@ func (client *AwsPlanesClient) NewListPager(options *AwsPlanesClientListOptions) return page.NextLink != nil && len(*page.NextLink) > 0 }, Fetcher: func(ctx context.Context, page *AwsPlanesClientListResponse) (AwsPlanesClientListResponse, error) { + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, "AwsPlanesClient.NewListPager") nextLink := "" if page != nil { nextLink = *page.NextLink @@ -304,7 +311,9 @@ func (client *AwsPlanesClient) BeginUpdate(ctx context.Context, planeName string // Generated from API version 2023-10-01-preview func (client *AwsPlanesClient) update(ctx context.Context, planeName string, properties AwsPlaneResourceTagsUpdate, options *AwsPlanesClientBeginUpdateOptions) (*http.Response, error) { var err error - ctx, endSpan := runtime.StartSpan(ctx, "AwsPlanesClient.BeginUpdate", client.internal.Tracer(), nil) + const operationName = "AwsPlanesClient.BeginUpdate" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) defer func() { endSpan(err) }() req, err := client.updateCreateRequest(ctx, planeName, properties, options) if err != nil { diff --git a/pkg/ucp/api/v20231001preview/zz_generated_azurecredentials_client.go b/pkg/ucp/api/v20231001preview/zz_generated_azurecredentials_client.go index 7ff88801cc..10e95efd04 100644 --- a/pkg/ucp/api/v20231001preview/zz_generated_azurecredentials_client.go +++ b/pkg/ucp/api/v20231001preview/zz_generated_azurecredentials_client.go @@ -47,7 +47,9 @@ func NewAzureCredentialsClient(credential azcore.TokenCredential, options *arm.C // method. func (client *AzureCredentialsClient) CreateOrUpdate(ctx context.Context, planeName string, credentialName string, resource AzureCredentialResource, options *AzureCredentialsClientCreateOrUpdateOptions) (AzureCredentialsClientCreateOrUpdateResponse, error) { var err error - ctx, endSpan := runtime.StartSpan(ctx, "AzureCredentialsClient.CreateOrUpdate", client.internal.Tracer(), nil) + const operationName = "AzureCredentialsClient.CreateOrUpdate" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) defer func() { endSpan(err) }() req, err := client.createOrUpdateCreateRequest(ctx, planeName, credentialName, resource, options) if err != nil { @@ -105,7 +107,9 @@ func (client *AzureCredentialsClient) createOrUpdateHandleResponse(resp *http.Re // - options - AzureCredentialsClientDeleteOptions contains the optional parameters for the AzureCredentialsClient.Delete method. func (client *AzureCredentialsClient) Delete(ctx context.Context, planeName string, credentialName string, options *AzureCredentialsClientDeleteOptions) (AzureCredentialsClientDeleteResponse, error) { var err error - ctx, endSpan := runtime.StartSpan(ctx, "AzureCredentialsClient.Delete", client.internal.Tracer(), nil) + const operationName = "AzureCredentialsClient.Delete" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) defer func() { endSpan(err) }() req, err := client.deleteCreateRequest(ctx, planeName, credentialName, options) if err != nil { @@ -150,7 +154,9 @@ func (client *AzureCredentialsClient) deleteCreateRequest(ctx context.Context, p // - options - AzureCredentialsClientGetOptions contains the optional parameters for the AzureCredentialsClient.Get method. func (client *AzureCredentialsClient) Get(ctx context.Context, planeName string, credentialName string, options *AzureCredentialsClientGetOptions) (AzureCredentialsClientGetResponse, error) { var err error - ctx, endSpan := runtime.StartSpan(ctx, "AzureCredentialsClient.Get", client.internal.Tracer(), nil) + const operationName = "AzureCredentialsClient.Get" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) defer func() { endSpan(err) }() req, err := client.getCreateRequest(ctx, planeName, credentialName, options) if err != nil { @@ -208,6 +214,7 @@ func (client *AzureCredentialsClient) NewListPager(planeName string, options *Az return page.NextLink != nil && len(*page.NextLink) > 0 }, Fetcher: func(ctx context.Context, page *AzureCredentialsClientListResponse) (AzureCredentialsClientListResponse, error) { + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, "AzureCredentialsClient.NewListPager") nextLink := "" if page != nil { nextLink = *page.NextLink @@ -258,7 +265,9 @@ func (client *AzureCredentialsClient) listHandleResponse(resp *http.Response) (A // - options - AzureCredentialsClientUpdateOptions contains the optional parameters for the AzureCredentialsClient.Update method. func (client *AzureCredentialsClient) Update(ctx context.Context, planeName string, credentialName string, properties AzureCredentialResourceTagsUpdate, options *AzureCredentialsClientUpdateOptions) (AzureCredentialsClientUpdateResponse, error) { var err error - ctx, endSpan := runtime.StartSpan(ctx, "AzureCredentialsClient.Update", client.internal.Tracer(), nil) + const operationName = "AzureCredentialsClient.Update" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) defer func() { endSpan(err) }() req, err := client.updateCreateRequest(ctx, planeName, credentialName, properties, options) if err != nil { diff --git a/pkg/ucp/api/v20231001preview/zz_generated_azureplanes_client.go b/pkg/ucp/api/v20231001preview/zz_generated_azureplanes_client.go index f638c3c23a..aa247ba136 100644 --- a/pkg/ucp/api/v20231001preview/zz_generated_azureplanes_client.go +++ b/pkg/ucp/api/v20231001preview/zz_generated_azureplanes_client.go @@ -68,7 +68,9 @@ func (client *AzurePlanesClient) BeginCreateOrUpdate(ctx context.Context, planeN // Generated from API version 2023-10-01-preview func (client *AzurePlanesClient) createOrUpdate(ctx context.Context, planeName string, resource AzurePlaneResource, options *AzurePlanesClientBeginCreateOrUpdateOptions) (*http.Response, error) { var err error - ctx, endSpan := runtime.StartSpan(ctx, "AzurePlanesClient.BeginCreateOrUpdate", client.internal.Tracer(), nil) + const operationName = "AzurePlanesClient.BeginCreateOrUpdate" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) defer func() { endSpan(err) }() req, err := client.createOrUpdateCreateRequest(ctx, planeName, resource, options) if err != nil { @@ -136,7 +138,9 @@ func (client *AzurePlanesClient) BeginDelete(ctx context.Context, planeName stri // Generated from API version 2023-10-01-preview func (client *AzurePlanesClient) deleteOperation(ctx context.Context, planeName string, options *AzurePlanesClientBeginDeleteOptions) (*http.Response, error) { var err error - ctx, endSpan := runtime.StartSpan(ctx, "AzurePlanesClient.BeginDelete", client.internal.Tracer(), nil) + const operationName = "AzurePlanesClient.BeginDelete" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) defer func() { endSpan(err) }() req, err := client.deleteCreateRequest(ctx, planeName, options) if err != nil { @@ -179,7 +183,9 @@ func (client *AzurePlanesClient) deleteCreateRequest(ctx context.Context, planeN // - options - AzurePlanesClientGetOptions contains the optional parameters for the AzurePlanesClient.Get method. func (client *AzurePlanesClient) Get(ctx context.Context, planeName string, options *AzurePlanesClientGetOptions) (AzurePlanesClientGetResponse, error) { var err error - ctx, endSpan := runtime.StartSpan(ctx, "AzurePlanesClient.Get", client.internal.Tracer(), nil) + const operationName = "AzurePlanesClient.Get" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) defer func() { endSpan(err) }() req, err := client.getCreateRequest(ctx, planeName, options) if err != nil { @@ -234,6 +240,7 @@ func (client *AzurePlanesClient) NewListPager(options *AzurePlanesClientListOpti return page.NextLink != nil && len(*page.NextLink) > 0 }, Fetcher: func(ctx context.Context, page *AzurePlanesClientListResponse) (AzurePlanesClientListResponse, error) { + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, "AzurePlanesClient.NewListPager") nextLink := "" if page != nil { nextLink = *page.NextLink @@ -304,7 +311,9 @@ func (client *AzurePlanesClient) BeginUpdate(ctx context.Context, planeName stri // Generated from API version 2023-10-01-preview func (client *AzurePlanesClient) update(ctx context.Context, planeName string, properties AzurePlaneResourceTagsUpdate, options *AzurePlanesClientBeginUpdateOptions) (*http.Response, error) { var err error - ctx, endSpan := runtime.StartSpan(ctx, "AzurePlanesClient.BeginUpdate", client.internal.Tracer(), nil) + const operationName = "AzurePlanesClient.BeginUpdate" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) defer func() { endSpan(err) }() req, err := client.updateCreateRequest(ctx, planeName, properties, options) if err != nil { diff --git a/pkg/ucp/api/v20231001preview/zz_generated_locations_client.go b/pkg/ucp/api/v20231001preview/zz_generated_locations_client.go index ae865967fa..69cd3764d6 100644 --- a/pkg/ucp/api/v20231001preview/zz_generated_locations_client.go +++ b/pkg/ucp/api/v20231001preview/zz_generated_locations_client.go @@ -72,7 +72,9 @@ func (client *LocationsClient) BeginCreateOrUpdate(ctx context.Context, planeNam // Generated from API version 2023-10-01-preview func (client *LocationsClient) createOrUpdate(ctx context.Context, planeName string, resourceProviderName string, locationName string, resource LocationResource, options *LocationsClientBeginCreateOrUpdateOptions) (*http.Response, error) { var err error - ctx, endSpan := runtime.StartSpan(ctx, "LocationsClient.BeginCreateOrUpdate", client.internal.Tracer(), nil) + const operationName = "LocationsClient.BeginCreateOrUpdate" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) defer func() { endSpan(err) }() req, err := client.createOrUpdateCreateRequest(ctx, planeName, resourceProviderName, locationName, resource, options) if err != nil { @@ -150,7 +152,9 @@ func (client *LocationsClient) BeginDelete(ctx context.Context, planeName string // Generated from API version 2023-10-01-preview func (client *LocationsClient) deleteOperation(ctx context.Context, planeName string, resourceProviderName string, locationName string, options *LocationsClientBeginDeleteOptions) (*http.Response, error) { var err error - ctx, endSpan := runtime.StartSpan(ctx, "LocationsClient.BeginDelete", client.internal.Tracer(), nil) + const operationName = "LocationsClient.BeginDelete" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) defer func() { endSpan(err) }() req, err := client.deleteCreateRequest(ctx, planeName, resourceProviderName, locationName, options) if err != nil { @@ -203,7 +207,9 @@ func (client *LocationsClient) deleteCreateRequest(ctx context.Context, planeNam // - options - LocationsClientGetOptions contains the optional parameters for the LocationsClient.Get method. func (client *LocationsClient) Get(ctx context.Context, planeName string, resourceProviderName string, locationName string, options *LocationsClientGetOptions) (LocationsClientGetResponse, error) { var err error - ctx, endSpan := runtime.StartSpan(ctx, "LocationsClient.Get", client.internal.Tracer(), nil) + const operationName = "LocationsClient.Get" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) defer func() { endSpan(err) }() req, err := client.getCreateRequest(ctx, planeName, resourceProviderName, locationName, options) if err != nil { @@ -268,6 +274,7 @@ func (client *LocationsClient) NewListPager(planeName string, resourceProviderNa return page.NextLink != nil && len(*page.NextLink) > 0 }, Fetcher: func(ctx context.Context, page *LocationsClientListResponse) (LocationsClientListResponse, error) { + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, "LocationsClient.NewListPager") nextLink := "" if page != nil { nextLink = *page.NextLink diff --git a/pkg/ucp/api/v20231001preview/zz_generated_planes_client.go b/pkg/ucp/api/v20231001preview/zz_generated_planes_client.go index b98b126a9e..3b17a63a8f 100644 --- a/pkg/ucp/api/v20231001preview/zz_generated_planes_client.go +++ b/pkg/ucp/api/v20231001preview/zz_generated_planes_client.go @@ -43,6 +43,7 @@ func (client *PlanesClient) NewListPlanesPager(options *PlanesClientListPlanesOp return page.NextLink != nil && len(*page.NextLink) > 0 }, Fetcher: func(ctx context.Context, page *PlanesClientListPlanesResponse) (PlanesClientListPlanesResponse, error) { + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, "PlanesClient.NewListPlanesPager") nextLink := "" if page != nil { nextLink = *page.NextLink diff --git a/pkg/ucp/api/v20231001preview/zz_generated_radiusplanes_client.go b/pkg/ucp/api/v20231001preview/zz_generated_radiusplanes_client.go index 4c7910001b..cc2b4d362f 100644 --- a/pkg/ucp/api/v20231001preview/zz_generated_radiusplanes_client.go +++ b/pkg/ucp/api/v20231001preview/zz_generated_radiusplanes_client.go @@ -68,7 +68,9 @@ func (client *RadiusPlanesClient) BeginCreateOrUpdate(ctx context.Context, plane // Generated from API version 2023-10-01-preview func (client *RadiusPlanesClient) createOrUpdate(ctx context.Context, planeName string, resource RadiusPlaneResource, options *RadiusPlanesClientBeginCreateOrUpdateOptions) (*http.Response, error) { var err error - ctx, endSpan := runtime.StartSpan(ctx, "RadiusPlanesClient.BeginCreateOrUpdate", client.internal.Tracer(), nil) + const operationName = "RadiusPlanesClient.BeginCreateOrUpdate" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) defer func() { endSpan(err) }() req, err := client.createOrUpdateCreateRequest(ctx, planeName, resource, options) if err != nil { @@ -137,7 +139,9 @@ func (client *RadiusPlanesClient) BeginDelete(ctx context.Context, planeName str // Generated from API version 2023-10-01-preview func (client *RadiusPlanesClient) deleteOperation(ctx context.Context, planeName string, options *RadiusPlanesClientBeginDeleteOptions) (*http.Response, error) { var err error - ctx, endSpan := runtime.StartSpan(ctx, "RadiusPlanesClient.BeginDelete", client.internal.Tracer(), nil) + const operationName = "RadiusPlanesClient.BeginDelete" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) defer func() { endSpan(err) }() req, err := client.deleteCreateRequest(ctx, planeName, options) if err != nil { @@ -180,7 +184,9 @@ func (client *RadiusPlanesClient) deleteCreateRequest(ctx context.Context, plane // - options - RadiusPlanesClientGetOptions contains the optional parameters for the RadiusPlanesClient.Get method. func (client *RadiusPlanesClient) Get(ctx context.Context, planeName string, options *RadiusPlanesClientGetOptions) (RadiusPlanesClientGetResponse, error) { var err error - ctx, endSpan := runtime.StartSpan(ctx, "RadiusPlanesClient.Get", client.internal.Tracer(), nil) + const operationName = "RadiusPlanesClient.Get" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) defer func() { endSpan(err) }() req, err := client.getCreateRequest(ctx, planeName, options) if err != nil { @@ -235,6 +241,7 @@ func (client *RadiusPlanesClient) NewListPager(options *RadiusPlanesClientListOp return page.NextLink != nil && len(*page.NextLink) > 0 }, Fetcher: func(ctx context.Context, page *RadiusPlanesClientListResponse) (RadiusPlanesClientListResponse, error) { + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, "RadiusPlanesClient.NewListPager") nextLink := "" if page != nil { nextLink = *page.NextLink @@ -306,7 +313,9 @@ func (client *RadiusPlanesClient) BeginUpdate(ctx context.Context, planeName str // Generated from API version 2023-10-01-preview func (client *RadiusPlanesClient) update(ctx context.Context, planeName string, properties RadiusPlaneResourceTagsUpdate, options *RadiusPlanesClientBeginUpdateOptions) (*http.Response, error) { var err error - ctx, endSpan := runtime.StartSpan(ctx, "RadiusPlanesClient.BeginUpdate", client.internal.Tracer(), nil) + const operationName = "RadiusPlanesClient.BeginUpdate" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) defer func() { endSpan(err) }() req, err := client.updateCreateRequest(ctx, planeName, properties, options) if err != nil { diff --git a/pkg/ucp/api/v20231001preview/zz_generated_resourcegroups_client.go b/pkg/ucp/api/v20231001preview/zz_generated_resourcegroups_client.go index d15214b035..04d4d452e6 100644 --- a/pkg/ucp/api/v20231001preview/zz_generated_resourcegroups_client.go +++ b/pkg/ucp/api/v20231001preview/zz_generated_resourcegroups_client.go @@ -47,7 +47,9 @@ func NewResourceGroupsClient(credential azcore.TokenCredential, options *arm.Cli // method. func (client *ResourceGroupsClient) CreateOrUpdate(ctx context.Context, planeName string, resourceGroupName string, resource ResourceGroupResource, options *ResourceGroupsClientCreateOrUpdateOptions) (ResourceGroupsClientCreateOrUpdateResponse, error) { var err error - ctx, endSpan := runtime.StartSpan(ctx, "ResourceGroupsClient.CreateOrUpdate", client.internal.Tracer(), nil) + const operationName = "ResourceGroupsClient.CreateOrUpdate" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) defer func() { endSpan(err) }() req, err := client.createOrUpdateCreateRequest(ctx, planeName, resourceGroupName, resource, options) if err != nil { @@ -108,7 +110,9 @@ func (client *ResourceGroupsClient) createOrUpdateHandleResponse(resp *http.Resp // - options - ResourceGroupsClientDeleteOptions contains the optional parameters for the ResourceGroupsClient.Delete method. func (client *ResourceGroupsClient) Delete(ctx context.Context, planeName string, resourceGroupName string, options *ResourceGroupsClientDeleteOptions) (ResourceGroupsClientDeleteResponse, error) { var err error - ctx, endSpan := runtime.StartSpan(ctx, "ResourceGroupsClient.Delete", client.internal.Tracer(), nil) + const operationName = "ResourceGroupsClient.Delete" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) defer func() { endSpan(err) }() req, err := client.deleteCreateRequest(ctx, planeName, resourceGroupName, options) if err != nil { @@ -156,7 +160,9 @@ func (client *ResourceGroupsClient) deleteCreateRequest(ctx context.Context, pla // - options - ResourceGroupsClientGetOptions contains the optional parameters for the ResourceGroupsClient.Get method. func (client *ResourceGroupsClient) Get(ctx context.Context, planeName string, resourceGroupName string, options *ResourceGroupsClientGetOptions) (ResourceGroupsClientGetResponse, error) { var err error - ctx, endSpan := runtime.StartSpan(ctx, "ResourceGroupsClient.Get", client.internal.Tracer(), nil) + const operationName = "ResourceGroupsClient.Get" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) defer func() { endSpan(err) }() req, err := client.getCreateRequest(ctx, planeName, resourceGroupName, options) if err != nil { @@ -216,6 +222,7 @@ func (client *ResourceGroupsClient) NewListPager(planeName string, options *Reso return page.NextLink != nil && len(*page.NextLink) > 0 }, Fetcher: func(ctx context.Context, page *ResourceGroupsClientListResponse) (ResourceGroupsClientListResponse, error) { + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, "ResourceGroupsClient.NewListPager") nextLink := "" if page != nil { nextLink = *page.NextLink @@ -269,7 +276,9 @@ func (client *ResourceGroupsClient) listHandleResponse(resp *http.Response) (Res // - options - ResourceGroupsClientUpdateOptions contains the optional parameters for the ResourceGroupsClient.Update method. func (client *ResourceGroupsClient) Update(ctx context.Context, planeName string, resourceGroupName string, properties ResourceGroupResourceTagsUpdate, options *ResourceGroupsClientUpdateOptions) (ResourceGroupsClientUpdateResponse, error) { var err error - ctx, endSpan := runtime.StartSpan(ctx, "ResourceGroupsClient.Update", client.internal.Tracer(), nil) + const operationName = "ResourceGroupsClient.Update" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) defer func() { endSpan(err) }() req, err := client.updateCreateRequest(ctx, planeName, resourceGroupName, properties, options) if err != nil { diff --git a/pkg/ucp/api/v20231001preview/zz_generated_resourceproviders_client.go b/pkg/ucp/api/v20231001preview/zz_generated_resourceproviders_client.go index 1f69e27e93..964c587bb8 100644 --- a/pkg/ucp/api/v20231001preview/zz_generated_resourceproviders_client.go +++ b/pkg/ucp/api/v20231001preview/zz_generated_resourceproviders_client.go @@ -69,7 +69,9 @@ func (client *ResourceProvidersClient) BeginCreateOrUpdate(ctx context.Context, // Generated from API version 2023-10-01-preview func (client *ResourceProvidersClient) createOrUpdate(ctx context.Context, planeName string, resourceProviderName string, resource ResourceProviderResource, options *ResourceProvidersClientBeginCreateOrUpdateOptions) (*http.Response, error) { var err error - ctx, endSpan := runtime.StartSpan(ctx, "ResourceProvidersClient.BeginCreateOrUpdate", client.internal.Tracer(), nil) + const operationName = "ResourceProvidersClient.BeginCreateOrUpdate" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) defer func() { endSpan(err) }() req, err := client.createOrUpdateCreateRequest(ctx, planeName, resourceProviderName, resource, options) if err != nil { @@ -143,7 +145,9 @@ func (client *ResourceProvidersClient) BeginDelete(ctx context.Context, planeNam // Generated from API version 2023-10-01-preview func (client *ResourceProvidersClient) deleteOperation(ctx context.Context, planeName string, resourceProviderName string, options *ResourceProvidersClientBeginDeleteOptions) (*http.Response, error) { var err error - ctx, endSpan := runtime.StartSpan(ctx, "ResourceProvidersClient.BeginDelete", client.internal.Tracer(), nil) + const operationName = "ResourceProvidersClient.BeginDelete" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) defer func() { endSpan(err) }() req, err := client.deleteCreateRequest(ctx, planeName, resourceProviderName, options) if err != nil { @@ -191,7 +195,9 @@ func (client *ResourceProvidersClient) deleteCreateRequest(ctx context.Context, // - options - ResourceProvidersClientGetOptions contains the optional parameters for the ResourceProvidersClient.Get method. func (client *ResourceProvidersClient) Get(ctx context.Context, planeName string, resourceProviderName string, options *ResourceProvidersClientGetOptions) (ResourceProvidersClientGetResponse, error) { var err error - ctx, endSpan := runtime.StartSpan(ctx, "ResourceProvidersClient.Get", client.internal.Tracer(), nil) + const operationName = "ResourceProvidersClient.Get" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) defer func() { endSpan(err) }() req, err := client.getCreateRequest(ctx, planeName, resourceProviderName, options) if err != nil { @@ -251,7 +257,9 @@ func (client *ResourceProvidersClient) getHandleResponse(resp *http.Response) (R // method. func (client *ResourceProvidersClient) GetProviderSummary(ctx context.Context, planeName string, resourceProviderName string, options *ResourceProvidersClientGetProviderSummaryOptions) (ResourceProvidersClientGetProviderSummaryResponse, error) { var err error - ctx, endSpan := runtime.StartSpan(ctx, "ResourceProvidersClient.GetProviderSummary", client.internal.Tracer(), nil) + const operationName = "ResourceProvidersClient.GetProviderSummary" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) defer func() { endSpan(err) }() req, err := client.getProviderSummaryCreateRequest(ctx, planeName, resourceProviderName, options) if err != nil { @@ -312,6 +320,7 @@ func (client *ResourceProvidersClient) NewListPager(planeName string, options *R return page.NextLink != nil && len(*page.NextLink) > 0 }, Fetcher: func(ctx context.Context, page *ResourceProvidersClientListResponse) (ResourceProvidersClientListResponse, error) { + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, "ResourceProvidersClient.NewListPager") nextLink := "" if page != nil { nextLink = *page.NextLink @@ -368,6 +377,7 @@ func (client *ResourceProvidersClient) NewListProviderSummariesPager(planeName s return page.NextLink != nil && len(*page.NextLink) > 0 }, Fetcher: func(ctx context.Context, page *ResourceProvidersClientListProviderSummariesResponse) (ResourceProvidersClientListProviderSummariesResponse, error) { + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, "ResourceProvidersClient.NewListProviderSummariesPager") nextLink := "" if page != nil { nextLink = *page.NextLink diff --git a/pkg/ucp/api/v20231001preview/zz_generated_resources_client.go b/pkg/ucp/api/v20231001preview/zz_generated_resources_client.go index 5f7ad29ca7..6c331970ad 100644 --- a/pkg/ucp/api/v20231001preview/zz_generated_resources_client.go +++ b/pkg/ucp/api/v20231001preview/zz_generated_resources_client.go @@ -48,6 +48,7 @@ func (client *ResourcesClient) NewListPager(planeName string, resourceGroupName return page.NextLink != nil && len(*page.NextLink) > 0 }, Fetcher: func(ctx context.Context, page *ResourcesClientListResponse) (ResourcesClientListResponse, error) { + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, "ResourcesClient.NewListPager") nextLink := "" if page != nil { nextLink = *page.NextLink diff --git a/pkg/ucp/api/v20231001preview/zz_generated_resourcetypes_client.go b/pkg/ucp/api/v20231001preview/zz_generated_resourcetypes_client.go index c781373331..d3dba9d47d 100644 --- a/pkg/ucp/api/v20231001preview/zz_generated_resourcetypes_client.go +++ b/pkg/ucp/api/v20231001preview/zz_generated_resourcetypes_client.go @@ -70,7 +70,9 @@ func (client *ResourceTypesClient) BeginCreateOrUpdate(ctx context.Context, plan // Generated from API version 2023-10-01-preview func (client *ResourceTypesClient) createOrUpdate(ctx context.Context, planeName string, resourceProviderName string, resourceTypeName string, resource ResourceTypeResource, options *ResourceTypesClientBeginCreateOrUpdateOptions) (*http.Response, error) { var err error - ctx, endSpan := runtime.StartSpan(ctx, "ResourceTypesClient.BeginCreateOrUpdate", client.internal.Tracer(), nil) + const operationName = "ResourceTypesClient.BeginCreateOrUpdate" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) defer func() { endSpan(err) }() req, err := client.createOrUpdateCreateRequest(ctx, planeName, resourceProviderName, resourceTypeName, resource, options) if err != nil { @@ -149,7 +151,9 @@ func (client *ResourceTypesClient) BeginDelete(ctx context.Context, planeName st // Generated from API version 2023-10-01-preview func (client *ResourceTypesClient) deleteOperation(ctx context.Context, planeName string, resourceProviderName string, resourceTypeName string, options *ResourceTypesClientBeginDeleteOptions) (*http.Response, error) { var err error - ctx, endSpan := runtime.StartSpan(ctx, "ResourceTypesClient.BeginDelete", client.internal.Tracer(), nil) + const operationName = "ResourceTypesClient.BeginDelete" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) defer func() { endSpan(err) }() req, err := client.deleteCreateRequest(ctx, planeName, resourceProviderName, resourceTypeName, options) if err != nil { @@ -202,7 +206,9 @@ func (client *ResourceTypesClient) deleteCreateRequest(ctx context.Context, plan // - options - ResourceTypesClientGetOptions contains the optional parameters for the ResourceTypesClient.Get method. func (client *ResourceTypesClient) Get(ctx context.Context, planeName string, resourceProviderName string, resourceTypeName string, options *ResourceTypesClientGetOptions) (ResourceTypesClientGetResponse, error) { var err error - ctx, endSpan := runtime.StartSpan(ctx, "ResourceTypesClient.Get", client.internal.Tracer(), nil) + const operationName = "ResourceTypesClient.Get" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) defer func() { endSpan(err) }() req, err := client.getCreateRequest(ctx, planeName, resourceProviderName, resourceTypeName, options) if err != nil { @@ -267,6 +273,7 @@ func (client *ResourceTypesClient) NewListPager(planeName string, resourceProvid return page.NextLink != nil && len(*page.NextLink) > 0 }, Fetcher: func(ctx context.Context, page *ResourceTypesClientListResponse) (ResourceTypesClientListResponse, error) { + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, "ResourceTypesClient.NewListPager") nextLink := "" if page != nil { nextLink = *page.NextLink diff --git a/pkg/ucp/config.go b/pkg/ucp/config.go index b4f248a6b6..8580d022eb 100644 --- a/pkg/ucp/config.go +++ b/pkg/ucp/config.go @@ -108,6 +108,9 @@ type RoutingConfig struct { type InitializationConfig struct { // Planes is a list of planes to create at startup. Planes []Plane `yaml:"planes,omitempty"` + + // ManifestDirectory is the directory which contains manifests. + ManifestDirectory string `yaml:"manifestDirectory"` } // Plane is a configuration entry for a plane resource. This is used to create a plane resource at startup. @@ -125,6 +128,7 @@ type Plane struct { Properties PlaneProperties `json:"properties" yaml:"properties"` } +// PlaneProperties represents the properties of a plane resource. type PlaneProperties struct { // ResourceProviders is a map of resource provider namespaces to their respective addresses. // diff --git a/pkg/ucp/initializer/service.go b/pkg/ucp/initializer/service.go new file mode 100644 index 0000000000..dda23b5b2d --- /dev/null +++ b/pkg/ucp/initializer/service.go @@ -0,0 +1,134 @@ +/* +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 initializer + +import ( + "context" + "fmt" + "net" + "net/url" + "os" + "time" + + aztoken "github.com/radius-project/radius/pkg/azure/tokencredentials" + "github.com/radius-project/radius/pkg/cli/manifest" + "github.com/radius-project/radius/pkg/components/hosting" + "github.com/radius-project/radius/pkg/sdk" + "github.com/radius-project/radius/pkg/ucp" + "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" + "github.com/radius-project/radius/pkg/ucp/ucplog" +) + +// Service implements the hosting.Service interface for registering manifests. +type Service struct { + options *ucp.Options +} + +var _ hosting.Service = (*Service)(nil) + +// NewService creates a server to register manifests. +func NewService(options *ucp.Options) *Service { + return &Service{ + options: options, + } +} + +// Name gets this service name. +func (s *Service) Name() string { + return "initializer" +} + +func waitForServer(ctx context.Context, host, port string, retryInterval time.Duration, connectionTimeout 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, connectionTimeout) + 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 (w *Service) Run(ctx context.Context) error { + logger := ucplog.FromContextOrDiscard(ctx) + + manifestDir := w.options.Config.Initialization.ManifestDirectory + if manifestDir == "" { + logger.Info("No manifest directory specified, initialization is complete") + return nil + } + + if _, err := os.Stat(manifestDir); os.IsNotExist(err) { + return fmt.Errorf("manifest directory does not exist: %w", err) + } else if err != nil { + return fmt.Errorf("error checking manifest directory: %w", err) + } + + if w.options.UCP == nil || w.options.UCP.Endpoint() == "" { + return fmt.Errorf("connection to UCP is not set") + } + + // Parse the endpoint URL and extract host and port + parsedURL, err := url.Parse(w.options.UCP.Endpoint()) + if err != nil { + return fmt.Errorf("failed to parse endpoint URL: %w", err) + } + + hostName, port, err := net.SplitHostPort(parsedURL.Host) + if err != nil { + return fmt.Errorf("failed to split host and port: %w", err) + } + + // Attempt to connect to the server + err = waitForServer(ctx, hostName, port, 500*time.Millisecond, 500*time.Millisecond, 5*time.Second) + if err != nil { + return fmt.Errorf("failed to connect to server : %w", err) + } + + // Server is up, proceed to register manifests + clientOptions := sdk.NewClientOptions(w.options.UCP) + + clientFactory, err := v20231001preview.NewClientFactory(&aztoken.AnonymousCredential{}, clientOptions) + if err != nil { + return fmt.Errorf("error creating client factory: %w", err) + } + + loggerFunc := func(format string, args ...any) { + logger.Info(fmt.Sprintf(format, args...)) + } + + if err := manifest.RegisterDirectory(ctx, clientFactory, "local", manifestDir, loggerFunc); err != nil { + return fmt.Errorf("error registering manifests : %w", err) + } + + logger.Info("Successfully registered manifests", "directory", manifestDir) + + return nil +} diff --git a/pkg/ucp/integrationtests/resourceproviders/resourceproviders_test.go b/pkg/ucp/integrationtests/resourceproviders/resourceproviders_test.go index 7ad93f4096..a44b6fcd1d 100644 --- a/pkg/ucp/integrationtests/resourceproviders/resourceproviders_test.go +++ b/pkg/ucp/integrationtests/resourceproviders/resourceproviders_test.go @@ -19,7 +19,9 @@ package resourceproviders import ( "net/http" "testing" + "time" + "github.com/radius-project/radius/pkg/ucp" "github.com/radius-project/radius/pkg/ucp/testhost" "github.com/stretchr/testify/require" ) @@ -27,6 +29,14 @@ import ( const ( resourceProviderEmptyListResponseFixture = "testdata/resourceprovider_v20231001preview_emptylist_responsebody.json" resourceProviderListResponseFixture = "testdata/resourceprovider_v20231001preview_list_responsebody.json" + + manifestResourceProviderListResponseFixture = "testdata/resourceprovider_manifest_list_responsebody.json" + manifestResourceProviderResponseFixture = "testdata/resourceprovider_manifest_responsebody.json" + + manifestResourceTypeListResponseFixture = "testdata/resourcetype_manifest_list_responsebody.json" + + registerManifestWaitDuration = 30 * time.Second + registerManifestWaitInterval = 3 * time.Second ) func Test_ResourceProvider_Lifecycle(t *testing.T) { @@ -87,3 +97,31 @@ func Test_ResourceProvider_CascadingDelete(t *testing.T) { response.EqualsErrorCode(404, "NotFound") require.Equal(t, locationID, response.Error.Error.Target) } + +func Test_ResourceProvider_RegisterManifests(t *testing.T) { + server := testhost.Start(t, testhost.TestHostOptionFunc(func(options *ucp.Options) { + options.Config.Initialization.ManifestDirectory = "testdata/manifests" + })) + defer server.Close() + + createRadiusPlane(server) + + require.Eventuallyf(t, func() bool { + // Responses should contain the resource provider and resource type in the manifest + response := server.MakeRequest(http.MethodGet, manifestResourceProviderCollectionURL, nil) + response.EqualsFixture(200, manifestResourceProviderListResponseFixture) + + response = server.MakeRequest(http.MethodGet, manifestResourceProviderURL, nil) + response.EqualsFixture(200, manifestResourceProviderResponseFixture) + + response = server.MakeRequest(http.MethodGet, manifestResourceTypeCollectionURL, nil) + response.EqualsFixture(200, manifestResourceTypeListResponseFixture) + + response = server.MakeRequest(http.MethodGet, manifestResourceTypeURL, nil) + response.EqualsFixture(200, manifestResourceTypeResponseFixture) + + deleteManifestResourceProvider(server) + + return true + }, registerManifestWaitDuration, registerManifestWaitInterval, "manifest registration did not complete in time") +} diff --git a/pkg/ucp/integrationtests/resourceproviders/testdata/manifests/resourceprovider-valid1.yaml b/pkg/ucp/integrationtests/resourceproviders/testdata/manifests/resourceprovider-valid1.yaml new file mode 100644 index 0000000000..2d7d03ca75 --- /dev/null +++ b/pkg/ucp/integrationtests/resourceproviders/testdata/manifests/resourceprovider-valid1.yaml @@ -0,0 +1,7 @@ +name: TestProvider.TestCompany +types: + testResourcesAbc: + apiVersions: + "2023-10-01-preview": + schema: {} + capabilities: [] diff --git a/pkg/ucp/integrationtests/resourceproviders/testdata/resourceprovider_manifest_list_responsebody.json b/pkg/ucp/integrationtests/resourceproviders/testdata/resourceprovider_manifest_list_responsebody.json new file mode 100644 index 0000000000..e195fb5706 --- /dev/null +++ b/pkg/ucp/integrationtests/resourceproviders/testdata/resourceprovider_manifest_list_responsebody.json @@ -0,0 +1,14 @@ +{ + "value": [ + { + "id": "/planes/radius/local/providers/System.Resources/resourceproviders/TestProvider.TestCompany", + "location": "global", + "name": "TestProvider.TestCompany", + "properties": { + "provisioningState": "Succeeded" + }, + "tags": {}, + "type": "System.Resources/resourceproviders" + } + ] +} diff --git a/pkg/ucp/integrationtests/resourceproviders/testdata/resourceprovider_manifest_requestbody.json b/pkg/ucp/integrationtests/resourceproviders/testdata/resourceprovider_manifest_requestbody.json new file mode 100644 index 0000000000..a3aab8e0f6 --- /dev/null +++ b/pkg/ucp/integrationtests/resourceproviders/testdata/resourceprovider_manifest_requestbody.json @@ -0,0 +1,5 @@ +{ + "location": "global", + "tags": {}, + "properties": {} +} diff --git a/pkg/ucp/integrationtests/resourceproviders/testdata/resourceprovider_manifest_responsebody.json b/pkg/ucp/integrationtests/resourceproviders/testdata/resourceprovider_manifest_responsebody.json new file mode 100644 index 0000000000..c8e010d3a9 --- /dev/null +++ b/pkg/ucp/integrationtests/resourceproviders/testdata/resourceprovider_manifest_responsebody.json @@ -0,0 +1,10 @@ +{ + "id": "/planes/radius/local/providers/System.Resources/resourceproviders/TestProvider.TestCompany", + "location": "global", + "name": "TestProvider.TestCompany", + "properties": { + "provisioningState": "Succeeded" + }, + "tags": {}, + "type": "System.Resources/resourceproviders" +} diff --git a/pkg/ucp/integrationtests/resourceproviders/testdata/resourcetype_manifest_list_responsebody.json b/pkg/ucp/integrationtests/resourceproviders/testdata/resourcetype_manifest_list_responsebody.json new file mode 100644 index 0000000000..765c7f29cd --- /dev/null +++ b/pkg/ucp/integrationtests/resourceproviders/testdata/resourcetype_manifest_list_responsebody.json @@ -0,0 +1,12 @@ +{ + "value": [ + { + "id": "/planes/radius/local/providers/System.Resources/resourceproviders/TestProvider.TestCompany/resourcetypes/testResourcesAbc", + "name": "testResourcesAbc", + "properties": { + "provisioningState": "Succeeded" + }, + "type": "System.Resources/resourceproviders/resourcetypes" + } + ] +} diff --git a/pkg/ucp/integrationtests/resourceproviders/testdata/resourcetype_manifest_requestbody.json b/pkg/ucp/integrationtests/resourceproviders/testdata/resourcetype_manifest_requestbody.json new file mode 100644 index 0000000000..b4924988b0 --- /dev/null +++ b/pkg/ucp/integrationtests/resourceproviders/testdata/resourcetype_manifest_requestbody.json @@ -0,0 +1,5 @@ +{ + "properties": { + "defaultApiVersion": "2023-10-01" + } +} diff --git a/pkg/ucp/integrationtests/resourceproviders/testdata/resourcetype_manifest_responsebody.json b/pkg/ucp/integrationtests/resourceproviders/testdata/resourcetype_manifest_responsebody.json new file mode 100644 index 0000000000..e4bd9c9e25 --- /dev/null +++ b/pkg/ucp/integrationtests/resourceproviders/testdata/resourcetype_manifest_responsebody.json @@ -0,0 +1,8 @@ +{ + "id": "/planes/radius/local/providers/System.Resources/resourceproviders/TestProvider.TestCompany/resourcetypes/testResourcesAbc", + "name": "testResourcesAbc", + "properties": { + "provisioningState": "Succeeded" + }, + "type": "System.Resources/resourceproviders/resourcetypes" +} diff --git a/pkg/ucp/integrationtests/resourceproviders/util_test.go b/pkg/ucp/integrationtests/resourceproviders/util_test.go index 3378a8281a..79af09a374 100644 --- a/pkg/ucp/integrationtests/resourceproviders/util_test.go +++ b/pkg/ucp/integrationtests/resourceproviders/util_test.go @@ -54,6 +54,18 @@ const ( resourceProviderSummaryCollectionURL = "/planes/radius/local/providers" + radiusAPIVersion resourceProviderSummaryURL = "/planes/radius/local/providers/" + resourceProviderNamespace + radiusAPIVersion + + manifestNamespace = "TestProvider.TestCompany" + manifestResourceProviderID = "/planes/radius/local/providers/System.Resources/resourceproviders/" + manifestNamespace + manifestResourceProviderCollectionURL = "/planes/radius/local/providers/System.Resources/resourceproviders" + radiusAPIVersion + manifestResourceProviderURL = manifestResourceProviderID + radiusAPIVersion + + manifestResourceTypeName1 = "testResourcesAbc" + manifestResourceTypeID = manifestResourceProviderID + "/resourcetypes/" + manifestResourceTypeName1 + manifestResourceTypeCollectionURL = manifestResourceProviderID + "/resourcetypes" + radiusAPIVersion + manifestResourceTypeURL = manifestResourceTypeID + radiusAPIVersion + manifestResourceTypeRequestFixture = "testdata/resourcetype_manifest_requestbody.json" + manifestResourceTypeResponseFixture = "testdata/resourcetype_manifest_responsebody.json" ) func createRadiusPlane(server *testhost.TestHost) { @@ -80,6 +92,14 @@ func deleteResourceProvider(server *testhost.TestHost) { response.EqualsStatusCode(404) } +func deleteManifestResourceProvider(server *testhost.TestHost) { + response := server.MakeRequest("DELETE", manifestResourceProviderURL, nil) + response.WaitForOperationComplete(nil) + + response = server.MakeRequest("GET", manifestResourceProviderURL, nil) + response.EqualsStatusCode(404) +} + func createResourceType(server *testhost.TestHost) { response := server.MakeFixtureRequest("PUT", resourceTypeURL, resourceTypeRequestFixture) response.WaitForOperationComplete(nil) diff --git a/pkg/ucp/server/server.go b/pkg/ucp/server/server.go index 04871a8389..e89acb35c0 100644 --- a/pkg/ucp/server/server.go +++ b/pkg/ucp/server/server.go @@ -24,6 +24,7 @@ import ( "github.com/radius-project/radius/pkg/ucp" "github.com/radius-project/radius/pkg/ucp/backend" "github.com/radius-project/radius/pkg/ucp/frontend/api" + "github.com/radius-project/radius/pkg/ucp/initializer" ) // NewServer initializes a host for UCP based on the provided options. @@ -45,6 +46,8 @@ func NewServer(options *ucp.Options) (*hosting.Host, error) { services = append(services, &traceservice.Service{Options: &options.Config.Tracing}) } + services = append(services, initializer.NewService(options)) + return &hosting.Host{ Services: services, }, nil diff --git a/test/rp/rptest.go b/test/rp/rptest.go index 21dc72d435..5a05099002 100644 --- a/test/rp/rptest.go +++ b/test/rp/rptest.go @@ -174,7 +174,7 @@ func NewRPTestOptions(t *testing.T) RPTestOptions { client, err := connections.DefaultFactory.CreateApplicationsManagementClient(ctx, *workspace) require.NoError(t, err, "failed to create ApplicationsManagementClient") - connection, err := workspace.Connect() + connection, err := workspace.Connect(ctx) require.NoError(t, err, "failed to connect to workspace") customAction, err := clientv2.NewCustomActionClient("", &clientv2.Options{ From 75d093afe41184260d7af6bca92e82bf918bc345 Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Fri, 3 Jan 2025 23:38:08 -0800 Subject: [PATCH 22/37] Add capabilities to resource type API (#8182) # Description This change adds the 'capabilities' concept to the resource type API. - Capabilities enable resource types to indicate the schema and behaviors they support. - Capabilities enable clients like the `rad` CLI to understand the behaviors of resource types dynamically. For example, we're adding the `SupportsRecipes` capability. - All resource types that support recipes should declare this capability. This is how a UDT will opt-in to recipe functionality during provisioning. - The `rad` CLI functionality for `rad recipe register` can introspect the resource type to validate recipe support, rather than hardcoding which types have the support and which don't. ---- Description of the changes: - The manifests previously supported capabilities as part of the API version, we're moving this to the resource type for a simplification. - The manifest entry for capabilities wasn't sent to the server. Now it is. - Updated API, converters, and UCP functionality. ## Type of change - This pull request adds or changes features of Radius and has an approved issue (issue link required). Part of: #6688 ## Contributor checklist Please verify that the PR meets the following requirements, where applicable: - [ ] An overview of proposed schema changes is included in a linked GitHub issue. - [ ] A design document PR is created in the [design-notes repository](https://github.com/radius-project/design-notes/), if new APIs are being introduced. - [ ] If applicable, design document has been reviewed and approved by Radius maintainers/approvers. - [ ] A PR for the [samples repository](https://github.com/radius-project/samples) is created, if existing samples are affected by the changes in this PR. - [ ] A PR for the [documentation repository](https://github.com/radius-project/docs) is created, if the changes in this PR affect the documentation or any user facing updates are made. - [ ] A PR for the [recipes repository](https://github.com/radius-project/recipes) is created, if existing recipes are affected by the changes in this PR. Signed-off-by: Ryan Nowak --- .../built-in-providers/applications_core.yaml | 14 +++---- .../built-in-providers/applications_dapr.yaml | 8 ++-- .../applications_datastores.yaml | 6 +-- .../applications_messaging.yaml | 2 +- .../microsoft_resources.yaml | 2 +- .../testdata/missing-required-field.yaml | 2 +- .../create/testdata/valid.yaml | 2 +- pkg/cli/manifest/manifest.go | 6 +-- pkg/cli/manifest/manifest_test.go | 8 ++-- pkg/cli/manifest/registermanifest.go | 1 + pkg/cli/manifest/testdata/duplicate-key.yaml | 4 +- pkg/cli/manifest/testdata/invalid-yaml.yaml | 2 +- .../testdata/missing-required-field.yaml | 2 +- .../resourceprovider-valid1.yaml | 4 +- .../resourceprovider-valid2.yaml | 4 +- pkg/cli/manifest/testdata/valid.json | 6 +-- pkg/cli/manifest/testdata/valid.yaml | 2 +- .../resourceprovidersummary_conversion.go | 1 + ...resourceprovidersummary_conversion_test.go | 1 + .../resourcetype_conversion.go | 26 +++++++++++++ .../resourcetype_conversion_test.go | 37 +++++++++++++++++++ .../resourceprovidersummary_datamodel.json | 1 + .../testdata/resourcetype_datamodel.json | 1 + .../testdata/resourcetype_resource.json | 1 + .../v20231001preview/zz_generated_models.go | 6 +++ .../zz_generated_models_serde.go | 8 ++++ .../resourceproviders/resourcetype_put.go | 1 + pkg/ucp/datamodel/capabilities.go | 22 +++++++++++ pkg/ucp/datamodel/resourceprovidersummary.go | 3 ++ pkg/ucp/datamodel/resourcetype.go | 3 ++ .../resourceproviders/summary_test.go | 1 + .../manifests/resourceprovider-valid1.yaml | 2 +- ...sourcetype_manifest_list_responsebody.json | 3 +- .../resourcetype_manifest_responsebody.json | 3 +- ...pe_v20231001preview_list_responsebody.json | 1 + ...urcetype_v20231001preview_requestbody.json | 1 + ...rcetype_v20231001preview_responsebody.json | 1 + .../preview/2023-10-01-preview/openapi.json | 20 ++++++++++ .../ucp/noncloud/resourceprovider_test.go | 1 + .../noncloud/testdata/resourceprovider.yaml | 2 +- typespec/UCP/resourceproviders.tsp | 11 ++++++ 41 files changed, 191 insertions(+), 41 deletions(-) create mode 100644 pkg/ucp/datamodel/capabilities.go diff --git a/deploy/manifest/built-in-providers/applications_core.yaml b/deploy/manifest/built-in-providers/applications_core.yaml index f7b256f5ee..d105acaf01 100644 --- a/deploy/manifest/built-in-providers/applications_core.yaml +++ b/deploy/manifest/built-in-providers/applications_core.yaml @@ -4,34 +4,34 @@ types: apiVersions: "2025-01-01-preview": schema: {} - capabilities: [] + capabilities: [] applications: apiVersions: "2025-01-01-preview": schema: {} - capabilities: [] + capabilities: [] environments: apiVersions: "2025-01-01-preview": schema: {} - capabilities: [] + capabilities: [] gateways: apiVersions: "2025-01-01-preview": schema: {} - capabilities: [] + capabilities: [] secretStores: apiVersions: "2025-01-01-preview": schema: {} - capabilities: [] + capabilities: [] extenders: apiVersions: "2025-01-01-preview": schema: {} - capabilities: ["Recipes"] + capabilities: ["SupportsRecipes"] volumes: apiVersions: "2025-01-01-preview": schema: {} - capabilities: [] + capabilities: [] diff --git a/deploy/manifest/built-in-providers/applications_dapr.yaml b/deploy/manifest/built-in-providers/applications_dapr.yaml index 9df3cfaae3..3738e87643 100644 --- a/deploy/manifest/built-in-providers/applications_dapr.yaml +++ b/deploy/manifest/built-in-providers/applications_dapr.yaml @@ -4,19 +4,19 @@ types: apiVersions: "2025-01-01-preview": schema: {} - capabilities: ["Recipes"] + capabilities: ["SupportsRecipes"] pubSubBrokers: apiVersions: "2025-01-01-preview": schema: {} - capabilities: ["Recipes"] + capabilities: ["SupportsRecipes"] secretStores: apiVersions: "2025-01-01-preview": schema: {} - capabilities: ["Recipes"] + capabilities: ["SupportsRecipes"] stateStores: apiVersions: "2025-01-01-preview": schema: {} - capabilities: ["Recipes"] + capabilities: ["SupportsRecipes"] diff --git a/deploy/manifest/built-in-providers/applications_datastores.yaml b/deploy/manifest/built-in-providers/applications_datastores.yaml index 2be3e75447..b8eb7d65b7 100644 --- a/deploy/manifest/built-in-providers/applications_datastores.yaml +++ b/deploy/manifest/built-in-providers/applications_datastores.yaml @@ -4,14 +4,14 @@ types: apiVersions: "2025-01-01-preview": schema: {} - capabilities: ["Recipes"] + capabilities: ["SupportsRecipes"] sqlDatabases: apiVersions: "2025-01-01-preview": schema: {} - capabilities: ["Recipes"] + capabilities: ["SupportsRecipes"] redisCaches: apiVersions: "2025-01-01-preview": schema: {} - capabilities: ["Recipes"] + capabilities: ["SupportsRecipes"] diff --git a/deploy/manifest/built-in-providers/applications_messaging.yaml b/deploy/manifest/built-in-providers/applications_messaging.yaml index fe8d490b10..cac03e8aa4 100644 --- a/deploy/manifest/built-in-providers/applications_messaging.yaml +++ b/deploy/manifest/built-in-providers/applications_messaging.yaml @@ -4,4 +4,4 @@ types: apiVersions: "2025-01-01-preview": schema: {} - capabilities: ["Recipes"] + capabilities: ["SupportsRecipes"] diff --git a/deploy/manifest/built-in-providers/microsoft_resources.yaml b/deploy/manifest/built-in-providers/microsoft_resources.yaml index bd1e905818..b2c24733bb 100644 --- a/deploy/manifest/built-in-providers/microsoft_resources.yaml +++ b/deploy/manifest/built-in-providers/microsoft_resources.yaml @@ -4,5 +4,5 @@ types: apiVersions: "2025-01-01-preview": schema: {} - capabilities: [] + capabilities: [] diff --git a/pkg/cli/cmd/resourceprovider/create/testdata/missing-required-field.yaml b/pkg/cli/cmd/resourceprovider/create/testdata/missing-required-field.yaml index 15ab61b11d..9667c4c6e2 100644 --- a/pkg/cli/cmd/resourceprovider/create/testdata/missing-required-field.yaml +++ b/pkg/cli/cmd/resourceprovider/create/testdata/missing-required-field.yaml @@ -3,4 +3,4 @@ types: apiVersions: '2025-01-01-preview': schema: {} - capabilities: ["Recipes"] \ No newline at end of file + capabilities: ["SupportsRecipes"] \ No newline at end of file diff --git a/pkg/cli/cmd/resourceprovider/create/testdata/valid.yaml b/pkg/cli/cmd/resourceprovider/create/testdata/valid.yaml index 509d5f4e02..a6f26f7fb4 100644 --- a/pkg/cli/cmd/resourceprovider/create/testdata/valid.yaml +++ b/pkg/cli/cmd/resourceprovider/create/testdata/valid.yaml @@ -4,4 +4,4 @@ types: apiVersions: '2025-01-01-preview': schema: {} - capabilities: ["Recipes"] \ No newline at end of file + capabilities: ["SupportsRecipes"] \ No newline at end of file diff --git a/pkg/cli/manifest/manifest.go b/pkg/cli/manifest/manifest.go index 8fd653ab95..d7a8a5855e 100644 --- a/pkg/cli/manifest/manifest.go +++ b/pkg/cli/manifest/manifest.go @@ -27,6 +27,9 @@ type ResourceProvider struct { // ResourceType represents a resource type in a resource provider manifest. type ResourceType struct { + // Capabilities is a list of capabilities for the resource type. + Capabilities []string `yaml:"capabilities" validate:"dive,capability"` + // DefaultAPIVersion is the default API version for the resource type. DefaultAPIVersion *string `yaml:"defaultApiVersion,omitempty" validate:"omitempty,apiVersion"` @@ -40,7 +43,4 @@ type ResourceTypeAPIVersion struct { // TODO: this allows anything right now, and will be ignored. We'll improve this in // a future pull-request. Schema any `yaml:"schema" validate:"required"` - - // Capabilities is a list of capabilities for the resource type. - Capabilities []string `yaml:"capabilities" validate:"dive,capability"` } diff --git a/pkg/cli/manifest/manifest_test.go b/pkg/cli/manifest/manifest_test.go index 4ebdd300b4..23c11de187 100644 --- a/pkg/cli/manifest/manifest_test.go +++ b/pkg/cli/manifest/manifest_test.go @@ -29,10 +29,10 @@ func TestReadFileYAML(t *testing.T) { "testResources": { APIVersions: map[string]*ResourceTypeAPIVersion{ "2025-01-01-preview": { - Schema: map[string]any{}, - Capabilities: []string{"Recipes"}, + Schema: map[string]any{}, }, }, + Capabilities: []string{"SupportsRecipes"}, }, }, } @@ -70,10 +70,10 @@ func TestReadFileJSON(t *testing.T) { "testResources": { APIVersions: map[string]*ResourceTypeAPIVersion{ "2025-01-01-preview": { - Schema: map[string]any{}, - Capabilities: []string{"Recipes"}, + Schema: map[string]any{}, }, }, + Capabilities: []string{"SupportsRecipes"}, }, }, } diff --git a/pkg/cli/manifest/registermanifest.go b/pkg/cli/manifest/registermanifest.go index 289f96d5e5..9ac98c6d7f 100644 --- a/pkg/cli/manifest/registermanifest.go +++ b/pkg/cli/manifest/registermanifest.go @@ -66,6 +66,7 @@ func RegisterFile(ctx context.Context, clientFactory *v20231001preview.ClientFac logIfEnabled(logger, "Creating resource type %s/%s", resourceProvider.Name, resourceTypeName) resourceTypePoller, err := clientFactory.NewResourceTypesClient().BeginCreateOrUpdate(ctx, planeName, resourceProvider.Name, resourceTypeName, v20231001preview.ResourceTypeResource{ Properties: &v20231001preview.ResourceTypeProperties{ + Capabilities: to.SliceOfPtrs(resourceType.Capabilities...), DefaultAPIVersion: resourceType.DefaultAPIVersion, }, }, nil) diff --git a/pkg/cli/manifest/testdata/duplicate-key.yaml b/pkg/cli/manifest/testdata/duplicate-key.yaml index a68ff94087..33f81fb54d 100644 --- a/pkg/cli/manifest/testdata/duplicate-key.yaml +++ b/pkg/cli/manifest/testdata/duplicate-key.yaml @@ -4,10 +4,10 @@ types: apiVersions: '2025-01-01-preview': schema: {} - capabilities: ["Recipes"] + capabilities: ["SupportsRecipes"] types: testResources: apiVersions: '2025-01-01-preview': schema: {} - capabilities: ["Recipes"] \ No newline at end of file + capabilities: ["SupportsRecipes"] \ No newline at end of file diff --git a/pkg/cli/manifest/testdata/invalid-yaml.yaml b/pkg/cli/manifest/testdata/invalid-yaml.yaml index 67971f1a35..645b223d2c 100644 --- a/pkg/cli/manifest/testdata/invalid-yaml.yaml +++ b/pkg/cli/manifest/testdata/invalid-yaml.yaml @@ -5,4 +5,4 @@ types: apiVersions: '2025-01-01-preview': schema: {} - capabilities: ["Recipes"] \ No newline at end of file + capabilities: ["SupportsRecipes"] \ No newline at end of file diff --git a/pkg/cli/manifest/testdata/missing-required-field.yaml b/pkg/cli/manifest/testdata/missing-required-field.yaml index 15ab61b11d..5b12f38194 100644 --- a/pkg/cli/manifest/testdata/missing-required-field.yaml +++ b/pkg/cli/manifest/testdata/missing-required-field.yaml @@ -3,4 +3,4 @@ types: apiVersions: '2025-01-01-preview': schema: {} - capabilities: ["Recipes"] \ No newline at end of file + capabilities: ["SupportsRecipes"] \ No newline at end of file diff --git a/pkg/cli/manifest/testdata/registerdirectory/resourceprovider-valid1.yaml b/pkg/cli/manifest/testdata/registerdirectory/resourceprovider-valid1.yaml index b76f369047..0fa92d2251 100644 --- a/pkg/cli/manifest/testdata/registerdirectory/resourceprovider-valid1.yaml +++ b/pkg/cli/manifest/testdata/registerdirectory/resourceprovider-valid1.yaml @@ -4,9 +4,9 @@ types: apiVersions: "2025-01-01-preview": schema: {} - capabilities: [] + capabilities: [] testResource2: apiVersions: "2025-01-01-preview": schema: {} - capabilities: [] + capabilities: [] diff --git a/pkg/cli/manifest/testdata/registerdirectory/resourceprovider-valid2.yaml b/pkg/cli/manifest/testdata/registerdirectory/resourceprovider-valid2.yaml index 3ef2b3ffcc..a2ffd310e7 100644 --- a/pkg/cli/manifest/testdata/registerdirectory/resourceprovider-valid2.yaml +++ b/pkg/cli/manifest/testdata/registerdirectory/resourceprovider-valid2.yaml @@ -4,9 +4,9 @@ types: apiVersions: "2025-01-01-preview": schema: {} - capabilities: ["Recipes"] + capabilities: ["SupportsRecipes"] testResource4: apiVersions: "2025-01-01-preview": schema: {} - capabilities: ["Recipes"] + capabilities: ["SupportsRecipes"] diff --git a/pkg/cli/manifest/testdata/valid.json b/pkg/cli/manifest/testdata/valid.json index cd6e1e5705..92dff6df6a 100644 --- a/pkg/cli/manifest/testdata/valid.json +++ b/pkg/cli/manifest/testdata/valid.json @@ -4,10 +4,10 @@ "testResources": { "apiVersions": { "2025-01-01-preview": { - "schema": {}, - "capabilities": ["Recipes"] + "schema": {} } - } + }, + "capabilities": ["SupportsRecipes"] } } } diff --git a/pkg/cli/manifest/testdata/valid.yaml b/pkg/cli/manifest/testdata/valid.yaml index 509d5f4e02..a6f26f7fb4 100644 --- a/pkg/cli/manifest/testdata/valid.yaml +++ b/pkg/cli/manifest/testdata/valid.yaml @@ -4,4 +4,4 @@ types: apiVersions: '2025-01-01-preview': schema: {} - capabilities: ["Recipes"] \ No newline at end of file + capabilities: ["SupportsRecipes"] \ No newline at end of file diff --git a/pkg/ucp/api/v20231001preview/resourceprovidersummary_conversion.go b/pkg/ucp/api/v20231001preview/resourceprovidersummary_conversion.go index 871bc4646d..cbd7fc0ea2 100644 --- a/pkg/ucp/api/v20231001preview/resourceprovidersummary_conversion.go +++ b/pkg/ucp/api/v20231001preview/resourceprovidersummary_conversion.go @@ -48,6 +48,7 @@ func (dst *ResourceProviderSummary) ConvertFrom(src v1.DataModelInterface) error dst.ResourceTypes = map[string]*ResourceProviderSummaryResourceType{} for resourceTypeName, resourceType := range dm.Properties.ResourceTypes { dst.ResourceTypes[resourceTypeName] = &ResourceProviderSummaryResourceType{ + Capabilities: to.SliceOfPtrs(resourceType.Capabilities...), DefaultAPIVersion: resourceType.DefaultAPIVersion, APIVersions: map[string]map[string]any{}, } diff --git a/pkg/ucp/api/v20231001preview/resourceprovidersummary_conversion_test.go b/pkg/ucp/api/v20231001preview/resourceprovidersummary_conversion_test.go index b826c0837f..87303a4339 100644 --- a/pkg/ucp/api/v20231001preview/resourceprovidersummary_conversion_test.go +++ b/pkg/ucp/api/v20231001preview/resourceprovidersummary_conversion_test.go @@ -44,6 +44,7 @@ func Test_ResourceProviderSummary_DataModelToVersioned(t *testing.T) { }, ResourceTypes: map[string]*ResourceProviderSummaryResourceType{ "testResources": { + Capabilities: []*string{to.Ptr("SupportsRecipes")}, DefaultAPIVersion: to.Ptr("2025-01-01"), APIVersions: map[string]map[string]any{ "2025-01-01": {}, diff --git a/pkg/ucp/api/v20231001preview/resourcetype_conversion.go b/pkg/ucp/api/v20231001preview/resourcetype_conversion.go index 1ffa4d09f3..1c92ca56b5 100644 --- a/pkg/ucp/api/v20231001preview/resourcetype_conversion.go +++ b/pkg/ucp/api/v20231001preview/resourcetype_conversion.go @@ -17,6 +17,8 @@ limitations under the License. package v20231001preview import ( + "fmt" + v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" "github.com/radius-project/radius/pkg/to" "github.com/radius-project/radius/pkg/ucp/datamodel" @@ -39,7 +41,18 @@ func (src *ResourceTypeResource) ConvertTo() (v1.DataModelInterface, error) { }, } + capabilities := []string{} + for _, capability := range src.Properties.Capabilities { + err := validateCapability(capability) + if err != nil { + return nil, err + } + + capabilities = append(capabilities, *capability) + } + dst.Properties = datamodel.ResourceTypeProperties{ + Capabilities: capabilities, DefaultAPIVersion: src.Properties.DefaultAPIVersion, } @@ -61,8 +74,21 @@ func (dst *ResourceTypeResource) ConvertFrom(src v1.DataModelInterface) error { dst.Properties = &ResourceTypeProperties{ ProvisioningState: to.Ptr(ProvisioningState(dm.InternalMetadata.AsyncProvisioningState)), + Capabilities: to.SliceOfPtrs(dm.Properties.Capabilities...), DefaultAPIVersion: dm.Properties.DefaultAPIVersion, } return nil } + +func validateCapability(input *string) error { + if input == nil { + return v1.NewClientErrInvalidRequest("capability cannot be null") + } + + if *input == datamodel.CapabilitySupportsRecipes { + return nil + } + + return v1.NewClientErrInvalidRequest(fmt.Sprintf("capability %q is not recognized. Supported capabilities: %s", *input, datamodel.CapabilitySupportsRecipes)) +} diff --git a/pkg/ucp/api/v20231001preview/resourcetype_conversion_test.go b/pkg/ucp/api/v20231001preview/resourcetype_conversion_test.go index 0eeafe89fe..3b63b03ab0 100644 --- a/pkg/ucp/api/v20231001preview/resourcetype_conversion_test.go +++ b/pkg/ucp/api/v20231001preview/resourcetype_conversion_test.go @@ -48,6 +48,7 @@ func Test_ResourceType_VersionedToDataModel(t *testing.T) { }, }, Properties: datamodel.ResourceTypeProperties{ + Capabilities: []string{"SupportsRecipes"}, DefaultAPIVersion: to.Ptr("2025-01-01"), }, }, @@ -87,6 +88,7 @@ func Test_ResourceType_DataModelToVersioned(t *testing.T) { Name: to.Ptr("testResources"), Properties: &ResourceTypeProperties{ ProvisioningState: to.Ptr(ProvisioningStateSucceeded), + Capabilities: []*string{to.Ptr("SupportsRecipes")}, DefaultAPIVersion: to.Ptr("2025-01-01"), }, }, @@ -113,3 +115,38 @@ func Test_ResourceType_DataModelToVersioned(t *testing.T) { }) } } + +func Test_validateCapability(t *testing.T) { + tests := []struct { + name string + input *string + expectedErr error + }{ + { + name: "valid capability", + input: to.Ptr(datamodel.CapabilitySupportsRecipes), + }, + { + name: "invalid capability", + input: to.Ptr("InvalidCapability"), + expectedErr: v1.NewClientErrInvalidRequest("capability \"InvalidCapability\" is not recognized. Supported capabilities: SupportsRecipes"), + }, + { + name: "nil capability", + input: nil, + expectedErr: v1.NewClientErrInvalidRequest("capability cannot be null"), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateCapability(tt.input) + if tt.expectedErr != nil { + require.Error(t, err) + require.Equal(t, tt.expectedErr, err) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/pkg/ucp/api/v20231001preview/testdata/resourceprovidersummary_datamodel.json b/pkg/ucp/api/v20231001preview/testdata/resourceprovidersummary_datamodel.json index 0232263e81..35e7fe0b31 100644 --- a/pkg/ucp/api/v20231001preview/testdata/resourceprovidersummary_datamodel.json +++ b/pkg/ucp/api/v20231001preview/testdata/resourceprovidersummary_datamodel.json @@ -6,6 +6,7 @@ }, "resourceTypes": { "testResources": { + "capabilities": ["SupportsRecipes"], "defaultApiVersion": "2025-01-01", "apiVersions": { "2025-01-01": {} diff --git a/pkg/ucp/api/v20231001preview/testdata/resourcetype_datamodel.json b/pkg/ucp/api/v20231001preview/testdata/resourcetype_datamodel.json index 0fc5792733..477763eb90 100644 --- a/pkg/ucp/api/v20231001preview/testdata/resourcetype_datamodel.json +++ b/pkg/ucp/api/v20231001preview/testdata/resourcetype_datamodel.json @@ -4,6 +4,7 @@ "type": "System.Resources/resourceProviders/resourceTypes", "provisioningState": "Succeeded", "properties": { + "capabilities": ["SupportsRecipes"], "defaultApiVersion": "2025-01-01" } } diff --git a/pkg/ucp/api/v20231001preview/testdata/resourcetype_resource.json b/pkg/ucp/api/v20231001preview/testdata/resourcetype_resource.json index 240db3dab6..4a5816cfa7 100644 --- a/pkg/ucp/api/v20231001preview/testdata/resourcetype_resource.json +++ b/pkg/ucp/api/v20231001preview/testdata/resourcetype_resource.json @@ -2,6 +2,7 @@ "id": "/planes/radius/local/providers/System.Resources/resourceProviders/Applications.Test/resourceTypes/testResources", "name": "testResources", "properties": { + "capabilities": ["SupportsRecipes"], "defaultApiVersion": "2025-01-01" } } diff --git a/pkg/ucp/api/v20231001preview/zz_generated_models.go b/pkg/ucp/api/v20231001preview/zz_generated_models.go index 60d60656dd..dcb296b02c 100644 --- a/pkg/ucp/api/v20231001preview/zz_generated_models.go +++ b/pkg/ucp/api/v20231001preview/zz_generated_models.go @@ -726,12 +726,18 @@ type ResourceProviderSummaryResourceType struct { // REQUIRED; API versions supported by the resource type. APIVersions map[string]map[string]any +// The resource type capabilities. + Capabilities []*string + // The default api version for the resource type. DefaultAPIVersion *string } // ResourceTypeProperties - The properties of a resource type. type ResourceTypeProperties struct { +// The resource type capabilities. + Capabilities []*string + // The default api version for the resource type. DefaultAPIVersion *string diff --git a/pkg/ucp/api/v20231001preview/zz_generated_models_serde.go b/pkg/ucp/api/v20231001preview/zz_generated_models_serde.go index d14f7211b2..2f43ef0a74 100644 --- a/pkg/ucp/api/v20231001preview/zz_generated_models_serde.go +++ b/pkg/ucp/api/v20231001preview/zz_generated_models_serde.go @@ -1921,6 +1921,7 @@ func (r *ResourceProviderSummary) UnmarshalJSON(data []byte) error { func (r ResourceProviderSummaryResourceType) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) populate(objectMap, "apiVersions", r.APIVersions) + populate(objectMap, "capabilities", r.Capabilities) populate(objectMap, "defaultApiVersion", r.DefaultAPIVersion) return json.Marshal(objectMap) } @@ -1937,6 +1938,9 @@ func (r *ResourceProviderSummaryResourceType) UnmarshalJSON(data []byte) error { case "apiVersions": err = unpopulate(val, "APIVersions", &r.APIVersions) delete(rawMsg, key) + case "capabilities": + err = unpopulate(val, "Capabilities", &r.Capabilities) + delete(rawMsg, key) case "defaultApiVersion": err = unpopulate(val, "DefaultAPIVersion", &r.DefaultAPIVersion) delete(rawMsg, key) @@ -1951,6 +1955,7 @@ func (r *ResourceProviderSummaryResourceType) UnmarshalJSON(data []byte) error { // MarshalJSON implements the json.Marshaller interface for type ResourceTypeProperties. func (r ResourceTypeProperties) MarshalJSON() ([]byte, error) { objectMap := make(map[string]any) + populate(objectMap, "capabilities", r.Capabilities) populate(objectMap, "defaultApiVersion", r.DefaultAPIVersion) populate(objectMap, "provisioningState", r.ProvisioningState) return json.Marshal(objectMap) @@ -1965,6 +1970,9 @@ func (r *ResourceTypeProperties) UnmarshalJSON(data []byte) error { for key, val := range rawMsg { var err error switch key { + case "capabilities": + err = unpopulate(val, "Capabilities", &r.Capabilities) + delete(rawMsg, key) case "defaultApiVersion": err = unpopulate(val, "DefaultAPIVersion", &r.DefaultAPIVersion) delete(rawMsg, key) diff --git a/pkg/ucp/backend/controller/resourceproviders/resourcetype_put.go b/pkg/ucp/backend/controller/resourceproviders/resourcetype_put.go index ee1c02eae1..8fe08942af 100644 --- a/pkg/ucp/backend/controller/resourceproviders/resourcetype_put.go +++ b/pkg/ucp/backend/controller/resourceproviders/resourcetype_put.go @@ -78,6 +78,7 @@ func (c *ResourceTypePutController) updateSummary(id resources.ID, resourceType resourceTypeEntry = datamodel.ResourceProviderSummaryPropertiesResourceType{} } + resourceTypeEntry.Capabilities = resourceType.Properties.Capabilities resourceTypeEntry.DefaultAPIVersion = resourceType.Properties.DefaultAPIVersion summary.Properties.ResourceTypes[resourceTypeName] = resourceTypeEntry return nil diff --git a/pkg/ucp/datamodel/capabilities.go b/pkg/ucp/datamodel/capabilities.go new file mode 100644 index 0000000000..2bae1c6f9b --- /dev/null +++ b/pkg/ucp/datamodel/capabilities.go @@ -0,0 +1,22 @@ +/* +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 datamodel + +const ( + // CapabilitySupportsRecipes is a capability that indicates the resource type supports recipes. + CapabilitySupportsRecipes = "SupportsRecipes" +) diff --git a/pkg/ucp/datamodel/resourceprovidersummary.go b/pkg/ucp/datamodel/resourceprovidersummary.go index 244ea3144e..9e4cb8ab68 100644 --- a/pkg/ucp/datamodel/resourceprovidersummary.go +++ b/pkg/ucp/datamodel/resourceprovidersummary.go @@ -72,6 +72,9 @@ type ResourceProviderSummaryPropertiesResourceType struct { // DefaultAPIVersion is the default API version for the resource type. DefaultAPIVersion *string `json:"defaultApiVersion,omitempty"` + // Capabilities is the list of capabilities supported by the resource type. + Capabilities []string `json:"capabilities,omitempty"` + // APIVersions is the list of API versions available for the resource type. APIVersions map[string]ResourceProviderSummaryPropertiesAPIVersion `json:"apiVersions,omitempty"` } diff --git a/pkg/ucp/datamodel/resourcetype.go b/pkg/ucp/datamodel/resourcetype.go index 38dfda47e0..749c2f96c4 100644 --- a/pkg/ucp/datamodel/resourcetype.go +++ b/pkg/ucp/datamodel/resourcetype.go @@ -41,6 +41,9 @@ func (r *ResourceType) ResourceTypeName() string { // ResourceTypeProperties stores the properties of a resource type. type ResourceTypeProperties struct { + // Capabilities is the list of capabilities supported by the resource type. + Capabilities []string `json:"capabilities,omitempty"` + // DefaultAPIVersion is the default API version for this resource type. DefaultAPIVersion *string `json:"defaultApiVersion"` } diff --git a/pkg/ucp/integrationtests/resourceproviders/summary_test.go b/pkg/ucp/integrationtests/resourceproviders/summary_test.go index 096f3165ec..88f5e82907 100644 --- a/pkg/ucp/integrationtests/resourceproviders/summary_test.go +++ b/pkg/ucp/integrationtests/resourceproviders/summary_test.go @@ -64,6 +64,7 @@ func Test_ResourceProviderSummary_Lifecycle(t *testing.T) { createResourceType(server) expected.ResourceTypes[resourceTypeName] = &v20231001preview.ResourceProviderSummaryResourceType{ APIVersions: map[string]map[string]any{}, + Capabilities: []*string{to.Ptr("SupportsRecipes")}, DefaultAPIVersion: to.Ptr("2025-01-01"), } diff --git a/pkg/ucp/integrationtests/resourceproviders/testdata/manifests/resourceprovider-valid1.yaml b/pkg/ucp/integrationtests/resourceproviders/testdata/manifests/resourceprovider-valid1.yaml index 2d7d03ca75..4523f045df 100644 --- a/pkg/ucp/integrationtests/resourceproviders/testdata/manifests/resourceprovider-valid1.yaml +++ b/pkg/ucp/integrationtests/resourceproviders/testdata/manifests/resourceprovider-valid1.yaml @@ -4,4 +4,4 @@ types: apiVersions: "2023-10-01-preview": schema: {} - capabilities: [] + capabilities: [] diff --git a/pkg/ucp/integrationtests/resourceproviders/testdata/resourcetype_manifest_list_responsebody.json b/pkg/ucp/integrationtests/resourceproviders/testdata/resourcetype_manifest_list_responsebody.json index 765c7f29cd..76554d1f97 100644 --- a/pkg/ucp/integrationtests/resourceproviders/testdata/resourcetype_manifest_list_responsebody.json +++ b/pkg/ucp/integrationtests/resourceproviders/testdata/resourcetype_manifest_list_responsebody.json @@ -4,7 +4,8 @@ "id": "/planes/radius/local/providers/System.Resources/resourceproviders/TestProvider.TestCompany/resourcetypes/testResourcesAbc", "name": "testResourcesAbc", "properties": { - "provisioningState": "Succeeded" + "provisioningState": "Succeeded", + "capabilities": [] }, "type": "System.Resources/resourceproviders/resourcetypes" } diff --git a/pkg/ucp/integrationtests/resourceproviders/testdata/resourcetype_manifest_responsebody.json b/pkg/ucp/integrationtests/resourceproviders/testdata/resourcetype_manifest_responsebody.json index e4bd9c9e25..1a7ae15afc 100644 --- a/pkg/ucp/integrationtests/resourceproviders/testdata/resourcetype_manifest_responsebody.json +++ b/pkg/ucp/integrationtests/resourceproviders/testdata/resourcetype_manifest_responsebody.json @@ -2,7 +2,8 @@ "id": "/planes/radius/local/providers/System.Resources/resourceproviders/TestProvider.TestCompany/resourcetypes/testResourcesAbc", "name": "testResourcesAbc", "properties": { - "provisioningState": "Succeeded" + "provisioningState": "Succeeded", + "capabilities": [] }, "type": "System.Resources/resourceproviders/resourcetypes" } diff --git a/pkg/ucp/integrationtests/resourceproviders/testdata/resourcetype_v20231001preview_list_responsebody.json b/pkg/ucp/integrationtests/resourceproviders/testdata/resourcetype_v20231001preview_list_responsebody.json index cda2b3ea77..19f972fd14 100644 --- a/pkg/ucp/integrationtests/resourceproviders/testdata/resourcetype_v20231001preview_list_responsebody.json +++ b/pkg/ucp/integrationtests/resourceproviders/testdata/resourcetype_v20231001preview_list_responsebody.json @@ -4,6 +4,7 @@ "id": "/planes/radius/local/providers/System.Resources/resourceproviders/Applications.Test/resourcetypes/testResources", "name": "testResources", "properties": { + "capabilities": ["SupportsRecipes"], "defaultApiVersion": "2025-01-01", "provisioningState": "Succeeded" }, diff --git a/pkg/ucp/integrationtests/resourceproviders/testdata/resourcetype_v20231001preview_requestbody.json b/pkg/ucp/integrationtests/resourceproviders/testdata/resourcetype_v20231001preview_requestbody.json index 42aa792fb9..0c8531efe5 100644 --- a/pkg/ucp/integrationtests/resourceproviders/testdata/resourcetype_v20231001preview_requestbody.json +++ b/pkg/ucp/integrationtests/resourceproviders/testdata/resourcetype_v20231001preview_requestbody.json @@ -1,5 +1,6 @@ { "properties": { + "capabilities": ["SupportsRecipes"], "defaultApiVersion": "2025-01-01" } } diff --git a/pkg/ucp/integrationtests/resourceproviders/testdata/resourcetype_v20231001preview_responsebody.json b/pkg/ucp/integrationtests/resourceproviders/testdata/resourcetype_v20231001preview_responsebody.json index 24b4c65d72..a5ee8a3261 100644 --- a/pkg/ucp/integrationtests/resourceproviders/testdata/resourcetype_v20231001preview_responsebody.json +++ b/pkg/ucp/integrationtests/resourceproviders/testdata/resourcetype_v20231001preview_responsebody.json @@ -2,6 +2,7 @@ "id": "/planes/radius/local/providers/System.Resources/resourceproviders/Applications.Test/resourcetypes/testResources", "name": "testResources", "properties": { + "capabilities": ["SupportsRecipes"], "defaultApiVersion": "2025-01-01", "provisioningState": "Succeeded" }, diff --git a/swagger/specification/ucp/resource-manager/UCP/preview/2023-10-01-preview/openapi.json b/swagger/specification/ucp/resource-manager/UCP/preview/2023-10-01-preview/openapi.json index 089957ee01..a4a3bfbcbc 100644 --- a/swagger/specification/ucp/resource-manager/UCP/preview/2023-10-01-preview/openapi.json +++ b/swagger/specification/ucp/resource-manager/UCP/preview/2023-10-01-preview/openapi.json @@ -3381,6 +3381,12 @@ ], "x-ms-discriminator-value": "WorkloadIdentity" }, + "Capability": { + "type": "string", + "description": "A capability defines the behaviors and features that a resource type supports.", + "maxLength": 63, + "pattern": "^[A-Za-z][A-Za-z0-9]*$" + }, "CredentialStorageKind": { "type": "string", "description": "Credential store kinds supported.", @@ -3969,6 +3975,13 @@ "$ref": "#/definitions/ResourceTypeSummaryResultApiVersion" } }, + "capabilities": { + "type": "array", + "description": "The resource type capabilities.", + "items": { + "$ref": "#/definitions/Capability" + } + }, "defaultApiVersion": { "type": "string", "description": "The default api version for the resource type." @@ -3993,6 +4006,13 @@ "description": "The status of the asynchronous operation.", "readOnly": true }, + "capabilities": { + "type": "array", + "description": "The resource type capabilities.", + "items": { + "$ref": "#/definitions/Capability" + } + }, "defaultApiVersion": { "$ref": "#/definitions/ApiVersionNameString", "description": "The default api version for the resource type." diff --git a/test/functional-portable/ucp/noncloud/resourceprovider_test.go b/test/functional-portable/ucp/noncloud/resourceprovider_test.go index 9b490c3574..ba70e34263 100644 --- a/test/functional-portable/ucp/noncloud/resourceprovider_test.go +++ b/test/functional-portable/ucp/noncloud/resourceprovider_test.go @@ -51,6 +51,7 @@ func Test_ResourceProviderRegistration(t *testing.T) { "apiVersions": map[string]any{ expectedApiVersion: map[string]any{}, }, + "capabilities": []any{"SupportsRecipes"}, }, }, } diff --git a/test/functional-portable/ucp/noncloud/testdata/resourceprovider.yaml b/test/functional-portable/ucp/noncloud/testdata/resourceprovider.yaml index 0f68394943..c3348b0229 100644 --- a/test/functional-portable/ucp/noncloud/testdata/resourceprovider.yaml +++ b/test/functional-portable/ucp/noncloud/testdata/resourceprovider.yaml @@ -4,4 +4,4 @@ types: apiVersions: "2025-01-01-preview": schema: {} - capabilities: ["Recipes"] + capabilities: ["SupportsRecipes"] diff --git a/typespec/UCP/resourceproviders.tsp b/typespec/UCP/resourceproviders.tsp index adc706c63b..de52acf41f 100644 --- a/typespec/UCP/resourceproviders.tsp +++ b/typespec/UCP/resourceproviders.tsp @@ -86,12 +86,20 @@ model ResourceTypeResource name: ResourceTypeNameString; } +@doc("A capability defines the behaviors and features that a resource type supports.") +@maxLength(63) +@pattern("^[A-Za-z][A-Za-z0-9]*$") +scalar Capability extends string; + @doc("The properties of a resource type.") model ResourceTypeProperties { @doc("The status of the asynchronous operation.") @visibility("read") provisioningState?: ProvisioningState; + @doc("The resource type capabilities.") + capabilities?: Capability[]; + @doc("The default api version for the resource type.") defaultApiVersion?: ApiVersionNameString; } @@ -167,6 +175,9 @@ model ResourceProviderSummaryResourceType { @doc("API versions supported by the resource type.") apiVersions: Record; + @doc("The resource type capabilities.") + capabilities?: Capability[]; + @doc("The default api version for the resource type.") defaultApiVersion?: string; } From 04f30c3e571966d20340574d939d1e8e7e79649d Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Sun, 5 Jan 2025 10:40:53 -0800 Subject: [PATCH 23/37] Add recipe engine (#8180) # Description This change adds the recipe engine to the dynamic-rp. There's no usage of the recipe engine yet in this PR, it's a prerequisite for what's coming next. The main problem being solved is improving the initialization code so that it can be used in our integration tests. ## Type of change - This pull request is a minor refactor, code cleanup, test improvement, or other maintenance task and doesn't change the functionality of Radius (issue link optional). Part of: #6688 ## Contributor checklist Please verify that the PR meets the following requirements, where applicable: - [ ] An overview of proposed schema changes is included in a linked GitHub issue. - [ ] A design document PR is created in the [design-notes repository](https://github.com/radius-project/design-notes/), if new APIs are being introduced. - [ ] If applicable, design document has been reviewed and approved by Radius maintainers/approvers. - [ ] A PR for the [samples repository](https://github.com/radius-project/samples) is created, if existing samples are affected by the changes in this PR. - [ ] A PR for the [documentation repository](https://github.com/radius-project/docs) is created, if the changes in this PR affect the documentation or any user facing updates are made. - [ ] A PR for the [recipes repository](https://github.com/radius-project/recipes) is created, if existing recipes are affected by the changes in this PR. Signed-off-by: Ryan Nowak --- cmd/dynamic-rp/dynamicrp-dev.yaml | 2 + .../templates/dynamic-rp/configmaps.yaml | 2 + .../kubernetesclientprovider/types.go | 189 ++++++++++++++++++ pkg/dynamicrp/backend/service.go | 13 +- pkg/dynamicrp/config.go | 4 + pkg/dynamicrp/options.go | 162 +++++++++++---- pkg/dynamicrp/testhost/host.go | 8 + .../processors/resourceclient.go | 31 +-- .../processors/resourceclient_test.go | 41 ++-- pkg/recipes/controllerconfig/config.go | 16 +- pkg/recipes/driver/terraform.go | 6 +- pkg/recipes/terraform/execute.go | 37 +++- test/k8sutil/fake.go | 30 +++ 13 files changed, 451 insertions(+), 90 deletions(-) create mode 100644 pkg/components/kubernetesclient/kubernetesclientprovider/types.go diff --git a/cmd/dynamic-rp/dynamicrp-dev.yaml b/cmd/dynamic-rp/dynamicrp-dev.yaml index 0df8054d64..31ab67d13b 100644 --- a/cmd/dynamic-rp/dynamicrp-dev.yaml +++ b/cmd/dynamic-rp/dynamicrp-dev.yaml @@ -29,6 +29,8 @@ tracerProvider: serviceName: "dynamic-rp" zipkin: url: "http://localhost:9411/api/v2/spans" +kubernetes: + kind: default server: host: "0.0.0.0" port: 8082 diff --git a/deploy/Chart/templates/dynamic-rp/configmaps.yaml b/deploy/Chart/templates/dynamic-rp/configmaps.yaml index 231e81d2f4..7f4c9e0d74 100644 --- a/deploy/Chart/templates/dynamic-rp/configmaps.yaml +++ b/deploy/Chart/templates/dynamic-rp/configmaps.yaml @@ -35,6 +35,8 @@ data: port: 6062 secretProvider: provider: kubernetes + kubernetes: + kind: default server: host: "0.0.0.0" port: 8082 diff --git a/pkg/components/kubernetesclient/kubernetesclientprovider/types.go b/pkg/components/kubernetesclient/kubernetesclientprovider/types.go new file mode 100644 index 0000000000..8a05947917 --- /dev/null +++ b/pkg/components/kubernetesclient/kubernetesclientprovider/types.go @@ -0,0 +1,189 @@ +/* +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 kubernetesclientprovider + +import ( + "fmt" + + contourv1 "github.com/projectcontour/contour/apis/projectcontour/v1" + "github.com/radius-project/radius/pkg/kubeutil" + apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/client-go/discovery" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/kubernetes" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + runtimeclient "sigs.k8s.io/controller-runtime/pkg/client" + csidriver "sigs.k8s.io/secrets-store-csi-driver/apis/v1alpha1" + + // Import kubernetes auth plugins + _ "k8s.io/client-go/plugin/pkg/client/auth" +) + +const ( + errUnsetMockText = "the Kubernetes config is nil. This is likely a test with improperly set up mocks. Use %s to set the client for testing" + + // Kind + KindDefault ConnectionKind = "default" + KindNone ConnectionKind = "none" +) + +// ConnectionKind is the kind of connection to use for accessing Kubernetes. +type ConnectionKind string + +// Options holds the configuration options for the Kubernetes client provider. +type Options struct { + // Kind is the kind of connection to use. + Kind ConnectionKind `yaml:"kind"` +} + +// FromOptions creates a new Kubernetes client provider from the given options. +func FromOptions(options Options) (*KubernetesClientProvider, error) { + if options.Kind == KindNone { + return FromConfig(nil), nil + } else if options.Kind == KindDefault { + config, err := kubeutil.NewClientConfig(&kubeutil.ConfigOptions{ + QPS: kubeutil.DefaultServerQPS, + Burst: kubeutil.DefaultServerBurst, + }) + if err != nil { + return nil, err + } + + return FromConfig(config), nil + } + + return nil, fmt.Errorf("unknown connection kind: %s", options.Kind) +} + +// FromConfig creates a new Kubernetes client provider from the given config. +// +// For testing, pass a nil config, and then use the Set* methods to set the clients. +func FromConfig(config *rest.Config) *KubernetesClientProvider { + return &KubernetesClientProvider{ + config: config, + } +} + +// KubernetesClientProvider provides access to Kubernetes clients. +type KubernetesClientProvider struct { + config *rest.Config + + // These clients are all settable for testing purposes. + clientGoClient kubernetes.Interface + discoveryClient discovery.DiscoveryInterface + dynamicClient dynamic.Interface + runtimeClient runtimeclient.Client +} + +// Config returns the Kubernetes client provider's config. +func (k *KubernetesClientProvider) Config() *rest.Config { + return k.config +} + +// ClientGoClient returns a Kubernetes client-go client. +func (k *KubernetesClientProvider) ClientGoClient() (kubernetes.Interface, error) { + if k.clientGoClient != nil { + return k.clientGoClient, nil + } + + config := k.Config() + if config == nil { + return nil, fmt.Errorf(errUnsetMockText, "SetClientGoClient") + } + + return kubernetes.NewForConfig(config) +} + +// SetClientGoClient sets the Kubernetes client-go client. This is useful for testing. +func (k *KubernetesClientProvider) SetClientGoClient(client kubernetes.Interface) { + k.clientGoClient = client +} + +// DiscoveryClient returns a Kubernetes discovery client. +func (k *KubernetesClientProvider) DiscoveryClient() (discovery.DiscoveryInterface, error) { + if k.discoveryClient != nil { + return k.discoveryClient, nil + } + + config := k.Config() + if config == nil { + return nil, fmt.Errorf(errUnsetMockText, "SetDiscoveryClient") + } + + client, err := kubernetes.NewForConfig(config) + if err != nil { + return nil, err + } + + // Use legacy discovery client to avoid the issue of the staled GroupVersion discovery(api.ucp.dev/v1alpha3). + // TODO: Disable UseLegacyDiscovery once https://github.com/radius-project/radius/issues/5974 is resolved. + client.DiscoveryClient.UseLegacyDiscovery = true + return client, nil +} + +// SetDiscoveryClient sets the Kubernetes discovery client. This is useful for testing. +func (k *KubernetesClientProvider) SetDiscoveryClient(client discovery.DiscoveryInterface) { + k.discoveryClient = client +} + +// DynamicClient returns a Kubernetes dynamic client. +func (k *KubernetesClientProvider) DynamicClient() (dynamic.Interface, error) { + if k.dynamicClient != nil { + return k.dynamicClient, nil + } + + config := k.Config() + if config == nil { + return nil, fmt.Errorf(errUnsetMockText, "SetDiscoveryClient") + } + + return dynamic.NewForConfig(config) +} + +// SetDynamicClient sets the Kubernetes dynamic client. This is useful for testing. +func (k *KubernetesClientProvider) SetDynamicClient(client dynamic.Interface) { + k.dynamicClient = client +} + +// RuntimeClient returns a Kubernetes controller runtime client. +func (k *KubernetesClientProvider) RuntimeClient() (runtimeclient.Client, error) { + if k.runtimeClient != nil { + return k.runtimeClient, nil + } + + config := k.Config() + if config == nil { + return nil, fmt.Errorf(errUnsetMockText, "SetRuntimeClient") + } + + scheme := runtime.NewScheme() + + utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + utilruntime.Must(csidriver.AddToScheme(scheme)) + utilruntime.Must(apiextv1.AddToScheme(scheme)) + utilruntime.Must(contourv1.AddToScheme(scheme)) + + return runtimeclient.New(k.Config(), runtimeclient.Options{Scheme: scheme}) +} + +// SetRuntimeClient sets the Kubernetes controller runtime client. This is useful for testing. +func (k *KubernetesClientProvider) SetRuntimeClient(client runtimeclient.Client) { + k.runtimeClient = client +} diff --git a/pkg/dynamicrp/backend/service.go b/pkg/dynamicrp/backend/service.go index 834f1aad7c..ce3569b1dc 100644 --- a/pkg/dynamicrp/backend/service.go +++ b/pkg/dynamicrp/backend/service.go @@ -23,14 +23,14 @@ import ( "github.com/radius-project/radius/pkg/armrpc/asyncoperation/worker" "github.com/radius-project/radius/pkg/dynamicrp" - "github.com/radius-project/radius/pkg/recipes/controllerconfig" + "github.com/radius-project/radius/pkg/recipes/engine" ) // Service runs the backend for the dynamic-rp. type Service struct { worker.Service options *dynamicrp.Options - recipes *controllerconfig.RecipeControllerConfig + recipes engine.Engine } // NewService creates a new service to run the dynamic-rp backend. @@ -40,7 +40,7 @@ func NewService(options *dynamicrp.Options) *Service { Service: worker.Service{ // Will be initialized later }, - recipes: options.Recipes, + recipes: nil, // Will be initialized later } } @@ -58,6 +58,13 @@ func (w *Service) Run(ctx context.Context) error { w.Service.Options.MaxOperationRetryCount = *w.options.Config.Worker.MaxOperationRetryCount } + e, err := w.options.RecipeEngine() + if err != nil { + return err + } + + w.recipes = e + databaseClient, err := w.options.DatabaseProvider.GetClient(ctx) if err != nil { return err diff --git a/pkg/dynamicrp/config.go b/pkg/dynamicrp/config.go index 8a658ae039..64669670e4 100644 --- a/pkg/dynamicrp/config.go +++ b/pkg/dynamicrp/config.go @@ -21,6 +21,7 @@ import ( "github.com/radius-project/radius/pkg/armrpc/hostoptions" "github.com/radius-project/radius/pkg/components/database/databaseprovider" + "github.com/radius-project/radius/pkg/components/kubernetesclient/kubernetesclientprovider" "github.com/radius-project/radius/pkg/components/metrics/metricsservice" "github.com/radius-project/radius/pkg/components/profiler/profilerservice" "github.com/radius-project/radius/pkg/components/queue/queueprovider" @@ -44,6 +45,9 @@ type Config struct { // Environment is the configuration for the hosting environment. Environment hostoptions.EnvironmentOptions `yaml:"environment"` + // Kubernetes is the configuration for the Kubernetes client. + Kubernetes kubernetesclientprovider.Options `yaml:"kubernetes"` + // Logging is the configuration for the logging system. Logging ucplog.LoggingOptions `yaml:"logging"` diff --git a/pkg/dynamicrp/options.go b/pkg/dynamicrp/options.go index ac32ce8f62..00ca8121cb 100644 --- a/pkg/dynamicrp/options.go +++ b/pkg/dynamicrp/options.go @@ -18,17 +18,26 @@ package dynamicrp import ( "context" + "errors" "fmt" + "strconv" "github.com/radius-project/radius/pkg/armrpc/asyncoperation/statusmanager" + "github.com/radius-project/radius/pkg/azure/armauth" + aztoken "github.com/radius-project/radius/pkg/azure/tokencredentials" "github.com/radius-project/radius/pkg/components/database/databaseprovider" + "github.com/radius-project/radius/pkg/components/kubernetesclient/kubernetesclientprovider" "github.com/radius-project/radius/pkg/components/queue/queueprovider" "github.com/radius-project/radius/pkg/components/secret/secretprovider" - "github.com/radius-project/radius/pkg/kubeutil" - "github.com/radius-project/radius/pkg/recipes/controllerconfig" + "github.com/radius-project/radius/pkg/portableresources/processors" + "github.com/radius-project/radius/pkg/recipes" + "github.com/radius-project/radius/pkg/recipes/configloader" + "github.com/radius-project/radius/pkg/recipes/driver" + "github.com/radius-project/radius/pkg/recipes/engine" "github.com/radius-project/radius/pkg/sdk" + "github.com/radius-project/radius/pkg/sdk/clients" ucpconfig "github.com/radius-project/radius/pkg/ucp/config" - kube_rest "k8s.io/client-go/rest" + sdk_cred "github.com/radius-project/radius/pkg/ucp/credentials" ) // Options holds the configuration options and shared services for the DyanmicRP server. @@ -42,11 +51,14 @@ type Options struct { // DatabaseProvider provides access to the database. DatabaseProvider *databaseprovider.DatabaseProvider + // KubernetesProvider provides access to the Kubernetes clients. + KubernetesProvider *kubernetesclientprovider.KubernetesClientProvider + // QueueProvider provides access to the message queue client. QueueProvider *queueprovider.QueueProvider - // Recipes is the configuration for the recipe subsystem. - Recipes *controllerconfig.RecipeControllerConfig + // Recipes is the configuration for the recipe engine subsystem. + Recipes RecipeOptions // SecretProvider provides access to the secret storage system. SecretProvider *secretprovider.SecretProvider @@ -58,6 +70,19 @@ type Options struct { UCP sdk.Connection } +// RecipeOptions holds the configuration options for the recipe engine subsystem. +type RecipeOptions struct { + // ConfigurationLoader is the loader for recipe configurations. + ConfigurationLoader configloader.ConfigurationLoader + + // Drivers is a map of recipe driver names to driver constructors. If nil, the default drivers are used (Bicep, Terraform) will + // be used. + Drivers map[string]func(options *Options) (driver.Driver, error) + + // SecretsLoader provides access to secrets for recipes. + SecretsLoader configloader.SecretsLoader +} + // NewOptions creates a new Options instance from the given configuration. func NewOptions(ctx context.Context, config *Config) (*Options, error) { var err error @@ -81,46 +106,107 @@ func NewOptions(ctx context.Context, config *Config) (*Options, error) { options.StatusManager = statusmanager.New(databaseClient, queueClient, config.Environment.RoleLocation) - var cfg *kube_rest.Config - if config.UCP.Kind == ucpconfig.UCPConnectionKindKubernetes { - cfg, err = kubeutil.NewClientConfig(&kubeutil.ConfigOptions{ - // TODO: Allow to use custom context via configuration. - https://github.com/radius-project/radius/issues/5433 - ContextName: "", - QPS: kubeutil.DefaultServerQPS, - Burst: kubeutil.DefaultServerBurst, - }) - if err != nil { - return nil, fmt.Errorf("failed to get kubernetes config: %w", err) - } + options.KubernetesProvider, err = kubernetesclientprovider.FromOptions(config.Kubernetes) + if err != nil { + return nil, err } - options.UCP, err = ucpconfig.NewConnectionFromUCPConfig(&config.UCP, cfg) + options.UCP, err = ucpconfig.NewConnectionFromUCPConfig(&config.UCP, options.KubernetesProvider.Config()) if err != nil { return nil, err } - // TODO: This is the right place to initialize the recipe infrastructure. Unfortunately this - // has a dependency on Kubernetes right now, which isn't available for integration tests. - // - // We have a future work item to untangle this dependency and then this code can be uncommented. - // For now this is a placeholder/reminder of the code we need, and where to put it. - // - // The recipe infrastructure is tied to corerp's dependencies, so we need to create it here. - // recipes, err := controllerconfig.New(hostoptions.HostOptions{ - // Config: &hostoptions.ProviderConfig{ - // Bicep: config.Bicep, - // Env: config.Environment, - // Terraform: config.Terraform, - // UCP: config.UCP, - // }, - // K8sConfig: cfg, - // UCPConnection: options.UCP, - // }) - // if err != nil { - // return nil, err - // } + options.Recipes.ConfigurationLoader = configloader.NewEnvironmentLoader(sdk.NewClientOptions(options.UCP)) + options.Recipes.SecretsLoader = configloader.NewSecretStoreLoader(sdk.NewClientOptions(options.UCP)) + + // If this is set to nil, then the service will use the default recipe drivers. // - // options.Recipes = recipes + // This pattern allows us to override the drivers for testing. + options.Recipes.Drivers = nil return &options, nil } + +// RecipeEngine creates a new recipe engine from the options. +func (o *Options) RecipeEngine() (engine.Engine, error) { + var errs error + drivers := map[string]driver.Driver{} + + // Use the default drivers if not otherwise specified. + if o.Recipes.Drivers == nil { + o.Recipes.Drivers = map[string]func(options *Options) (driver.Driver, error){ + recipes.TemplateKindBicep: bicepDriver, + recipes.TemplateKindTerraform: terraformDriver, + } + } + + for name, driverConstructor := range o.Recipes.Drivers { + driver, err := driverConstructor(o) + if err != nil { + errs = errors.Join(errs, err) + continue + } + + drivers[name] = driver + } + + if errs != nil { + return nil, fmt.Errorf("failed to create recipe drivers: %w", errs) + } + + return engine.NewEngine(engine.Options{ + ConfigurationLoader: o.Recipes.ConfigurationLoader, + SecretsLoader: o.Recipes.SecretsLoader, + Drivers: drivers}), nil +} + +func bicepDriver(options *Options) (driver.Driver, error) { + deploymentEngineClient, err := clients.NewResourceDeploymentsClient(&clients.Options{ + Cred: &aztoken.AnonymousCredential{}, + BaseURI: options.UCP.Endpoint(), + ARMClientOptions: sdk.NewClientOptions(options.UCP), + }) + if err != nil { + return nil, err + } + + provider, err := sdk_cred.NewAzureCredentialProvider(options.SecretProvider, options.UCP, &aztoken.AnonymousCredential{}) + if err != nil { + return nil, err + } + + armConfig, err := armauth.NewArmConfig(&armauth.Options{CredentialProvider: provider}) + if err != nil { + return nil, err + } + + resourceClient := processors.NewResourceClient(armConfig, options.UCP, options.KubernetesProvider) + + bicepDeleteRetryCount, err := strconv.Atoi(options.Config.Bicep.DeleteRetryCount) + if err != nil { + return nil, err + } + + bicepDeleteRetryDeleteSeconds, err := strconv.Atoi(options.Config.Bicep.DeleteRetryDelaySeconds) + if err != nil { + return nil, err + } + + return driver.NewBicepDriver( + sdk.NewClientOptions(options.UCP), + deploymentEngineClient, + resourceClient, + driver.BicepOptions{ + DeleteRetryCount: bicepDeleteRetryCount, + DeleteRetryDelaySeconds: bicepDeleteRetryDeleteSeconds, + }), nil +} + +func terraformDriver(options *Options) (driver.Driver, error) { + return driver.NewTerraformDriver( + options.UCP, + options.SecretProvider, + driver.TerraformOptions{ + Path: options.Config.Terraform.Path, + }, *options.KubernetesProvider), nil +} diff --git a/pkg/dynamicrp/testhost/host.go b/pkg/dynamicrp/testhost/host.go index ab01efdc01..6387b37d99 100644 --- a/pkg/dynamicrp/testhost/host.go +++ b/pkg/dynamicrp/testhost/host.go @@ -26,11 +26,13 @@ import ( v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" "github.com/radius-project/radius/pkg/armrpc/hostoptions" "github.com/radius-project/radius/pkg/components/database/databaseprovider" + "github.com/radius-project/radius/pkg/components/kubernetesclient/kubernetesclientprovider" "github.com/radius-project/radius/pkg/components/queue/queueprovider" "github.com/radius-project/radius/pkg/components/secret/secretprovider" "github.com/radius-project/radius/pkg/components/testhost" "github.com/radius-project/radius/pkg/dynamicrp" "github.com/radius-project/radius/pkg/dynamicrp/server" + "github.com/radius-project/radius/pkg/recipes/driver" "github.com/radius-project/radius/pkg/sdk" "github.com/radius-project/radius/pkg/ucp" "github.com/radius-project/radius/pkg/ucp/config" @@ -71,6 +73,9 @@ func Start(t *testing.T, opts ...TestHostOption) (*TestHost, *ucptesthost.TestHo Name: "test", RoleLocation: v1.LocationGlobal, }, + Kubernetes: kubernetesclientprovider.Options{ + Kind: kubernetesclientprovider.KindNone, + }, Queue: queueprovider.QueueProviderOptions{ Provider: queueprovider.TypeInmemory, Name: "dynamic-rp", @@ -92,6 +97,9 @@ func Start(t *testing.T, opts ...TestHostOption) (*TestHost, *ucptesthost.TestHo options, err := dynamicrp.NewOptions(context.Background(), config) require.NoError(t, err) + // Prevent the default recipe drivers from being registered. + options.Recipes.Drivers = map[string]func(options *dynamicrp.Options) (driver.Driver, error){} + for _, opt := range opts { opt.Apply(options) } diff --git a/pkg/portableresources/processors/resourceclient.go b/pkg/portableresources/processors/resourceclient.go index f53140d5a0..6827a52a18 100644 --- a/pkg/portableresources/processors/resourceclient.go +++ b/pkg/portableresources/processors/resourceclient.go @@ -28,6 +28,7 @@ import ( aztoken "github.com/radius-project/radius/pkg/azure/tokencredentials" "github.com/radius-project/radius/pkg/cli/clients" "github.com/radius-project/radius/pkg/cli/clients_new/generated" + "github.com/radius-project/radius/pkg/components/kubernetesclient/kubernetesclientprovider" "github.com/radius-project/radius/pkg/components/trace" "github.com/radius-project/radius/pkg/sdk" "github.com/radius-project/radius/pkg/ucp/resources" @@ -38,7 +39,6 @@ import ( v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/discovery" runtime_client "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -51,16 +51,13 @@ type resourceClient struct { // connection is the connection to use for UCP resources. Override this for testing. connection sdk.Connection - // k8sClient is the Kubernetes client used to delete Kubernetes resources. Override this for testing. - k8sClient runtime_client.Client - - // k8sDiscoveryClient is the Kubernetes client to used for API version lookups on Kubernetes resources. Override this for testing. - k8sDiscoveryClient discovery.ServerResourcesInterface + // kubernetesClient is the Kubernetes client provider used to create Kubernetes clients. Override this for testing. + kubernetesClient *kubernetesclientprovider.KubernetesClientProvider } // NewResourceClient creates a new resourceClient instance with the given parameters. -func NewResourceClient(arm *armauth.ArmConfig, connection sdk.Connection, k8sClient runtime_client.Client, k8sDiscoveryClient discovery.ServerResourcesInterface) *resourceClient { - return &resourceClient{arm: arm, connection: connection, k8sClient: k8sClient, k8sDiscoveryClient: k8sDiscoveryClient} +func NewResourceClient(arm *armauth.ArmConfig, connection sdk.Connection, kubernetesClient *kubernetesclientprovider.KubernetesClientProvider) *resourceClient { + return &resourceClient{arm: arm, connection: connection, kubernetesClient: kubernetesClient} } // Delete attempts to delete a resource, either through UCP, Azure, or Kubernetes, depending on the resource type. @@ -234,7 +231,12 @@ func (c *resourceClient) deleteKubernetesResource(ctx context.Context, id resour }, } - err = runtime_client.IgnoreNotFound(c.k8sClient.Delete(ctx, &obj)) + runtimeClient, err := c.kubernetesClient.RuntimeClient() + if err != nil { + return err + } + + err = runtime_client.IgnoreNotFound(runtimeClient.Delete(ctx, &obj)) if err != nil { return err } @@ -245,14 +247,19 @@ func (c *resourceClient) deleteKubernetesResource(ctx context.Context, id resour func (c *resourceClient) lookupKubernetesAPIVersion(id resources.ID) (string, error) { group, kind, namespace, _ := resources_kubernetes.ToParts(id) var resourceLists []*v1.APIResourceList - var err error + + discoveryClient, err := c.kubernetesClient.DiscoveryClient() + if err != nil { + return "", err + } + if namespace == "" { - resourceLists, err = c.k8sDiscoveryClient.ServerPreferredResources() + resourceLists, err = discoveryClient.ServerPreferredResources() if err != nil { return "", fmt.Errorf("could not find API version for type %q: %w", id.Type(), err) } } else { - resourceLists, err = c.k8sDiscoveryClient.ServerPreferredNamespacedResources() + resourceLists, err = discoveryClient.ServerPreferredNamespacedResources() if err != nil { return "", fmt.Errorf("could not find API version for type %q: %w", id.Type(), err) } diff --git a/pkg/portableresources/processors/resourceclient_test.go b/pkg/portableresources/processors/resourceclient_test.go index 447400e511..25bd135b44 100644 --- a/pkg/portableresources/processors/resourceclient_test.go +++ b/pkg/portableresources/processors/resourceclient_test.go @@ -31,6 +31,7 @@ import ( "github.com/radius-project/radius/pkg/azure/armauth" "github.com/radius-project/radius/pkg/azure/clientv2" aztoken "github.com/radius-project/radius/pkg/azure/tokencredentials" + "github.com/radius-project/radius/pkg/components/kubernetesclient/kubernetesclientprovider" "github.com/radius-project/radius/pkg/sdk" "github.com/radius-project/radius/pkg/to" "github.com/radius-project/radius/test/k8sutil" @@ -51,7 +52,7 @@ const ( ) func Test_Delete_InvalidResourceID(t *testing.T) { - c := NewResourceClient(nil, nil, nil, nil) + c := NewResourceClient(nil, nil, nil) err := c.Delete(context.Background(), "invalid") require.Error(t, err) } @@ -68,7 +69,7 @@ func Test_Delete_ARM(t *testing.T) { server := httptest.NewServer(mux) defer server.Close() - c := NewResourceClient(newArmOptions(server.URL), nil, nil, nil) + c := NewResourceClient(newArmOptions(server.URL), nil, nil) c.armClientOptions = newClientOptions(server.Client(), server.URL) err := c.Delete(context.Background(), ARMResourceID) @@ -96,7 +97,7 @@ func Test_Delete_ARM(t *testing.T) { server := httptest.NewServer(mux) defer server.Close() - c := NewResourceClient(newArmOptions(server.URL), nil, nil, nil) + c := NewResourceClient(newArmOptions(server.URL), nil, nil) c.armClientOptions = newClientOptions(server.Client(), server.URL) err := c.Delete(context.Background(), ARMResourceID) @@ -119,7 +120,7 @@ func Test_Delete_ARM(t *testing.T) { server := httptest.NewServer(mux) defer server.Close() - c := NewResourceClient(newArmOptions(server.URL), nil, nil, nil) + c := NewResourceClient(newArmOptions(server.URL), nil, nil) c.armClientOptions = newClientOptions(server.Client(), server.URL) err := c.Delete(context.Background(), ARMResourceID) @@ -146,7 +147,7 @@ func Test_Delete_ARM(t *testing.T) { server := httptest.NewServer(mux) defer server.Close() - c := NewResourceClient(newArmOptions(server.URL), nil, nil, nil) + c := NewResourceClient(newArmOptions(server.URL), nil, nil) c.armClientOptions = newClientOptions(server.Client(), server.URL) err := c.Delete(context.Background(), ARMResourceID) @@ -160,7 +161,7 @@ func Test_Delete_ARM(t *testing.T) { server := httptest.NewServer(mux) defer server.Close() - c := NewResourceClient(newArmOptions(server.URL), nil, nil, nil) + c := NewResourceClient(newArmOptions(server.URL), nil, nil) c.armClientOptions = newClientOptions(server.Client(), server.URL) err := c.Delete(context.Background(), ARMResourceID) @@ -178,7 +179,7 @@ func Test_Delete_ARM(t *testing.T) { server := httptest.NewServer(mux) defer server.Close() - c := NewResourceClient(newArmOptions(server.URL), nil, nil, nil) + c := NewResourceClient(newArmOptions(server.URL), nil, nil) c.armClientOptions = newClientOptions(server.Client(), server.URL) err := c.Delete(context.Background(), ARMResourceID) @@ -202,7 +203,7 @@ func Test_Delete_ARM(t *testing.T) { server := httptest.NewServer(mux) defer server.Close() - c := NewResourceClient(newArmOptions(server.URL), nil, nil, nil) + c := NewResourceClient(newArmOptions(server.URL), nil, nil) c.armClientOptions = newClientOptions(server.Client(), server.URL) err := c.Delete(context.Background(), ARMResourceID) @@ -238,7 +239,11 @@ func Test_Delete_Kubernetes(t *testing.T) { }, } - c := NewResourceClient(nil, nil, client, dc) + kcp := kubernetesclientprovider.FromConfig(nil) + kcp.SetRuntimeClient(client) + kcp.SetDiscoveryClient(dc) + + c := NewResourceClient(nil, nil, kcp) err := c.Delete(context.Background(), KubernetesCoreGroupResourceID) require.NoError(t, err) @@ -266,7 +271,11 @@ func Test_Delete_Kubernetes(t *testing.T) { }, } - c := NewResourceClient(nil, nil, client, dc) + kcp := kubernetesclientprovider.FromConfig(nil) + kcp.SetRuntimeClient(client) + kcp.SetDiscoveryClient(dc) + + c := NewResourceClient(nil, nil, kcp) err := c.Delete(context.Background(), KubernetesCoreGroupResourceID) require.NoError(t, err) @@ -284,7 +293,11 @@ func Test_Delete_Kubernetes(t *testing.T) { Resources: []*metav1.APIResourceList{}, } - c := NewResourceClient(nil, nil, client, dc) + kcp := kubernetesclientprovider.FromConfig(nil) + kcp.SetRuntimeClient(client) + kcp.SetDiscoveryClient(dc) + + c := NewResourceClient(nil, nil, kcp) err := c.Delete(context.Background(), KubernetesCoreGroupResourceID) require.Error(t, err) @@ -303,7 +316,7 @@ func Test_Delete_UCP(t *testing.T) { connection, err := sdk.NewDirectConnection(server.URL) require.NoError(t, err) - c := NewResourceClient(nil, connection, nil, nil) + c := NewResourceClient(nil, connection, nil) err = c.Delete(context.Background(), AWSResourceID) require.NoError(t, err) @@ -319,7 +332,7 @@ func Test_Delete_UCP(t *testing.T) { connection, err := sdk.NewDirectConnection(server.URL) require.NoError(t, err) - c := NewResourceClient(nil, connection, nil, nil) + c := NewResourceClient(nil, connection, nil) err = c.Delete(context.Background(), AWSResourceID) require.NoError(t, err) @@ -339,7 +352,7 @@ func Test_Delete_UCP(t *testing.T) { connection, err := sdk.NewDirectConnection(server.URL) require.NoError(t, err) - c := NewResourceClient(nil, connection, nil, nil) + c := NewResourceClient(nil, connection, nil) err = c.Delete(context.Background(), AWSResourceID) require.Error(t, err) diff --git a/pkg/recipes/controllerconfig/config.go b/pkg/recipes/controllerconfig/config.go index 40fbd98a2d..6ae1e3a82d 100644 --- a/pkg/recipes/controllerconfig/config.go +++ b/pkg/recipes/controllerconfig/config.go @@ -21,8 +21,8 @@ import ( "github.com/radius-project/radius/pkg/armrpc/hostoptions" aztoken "github.com/radius-project/radius/pkg/azure/tokencredentials" + "github.com/radius-project/radius/pkg/components/kubernetesclient/kubernetesclientprovider" "github.com/radius-project/radius/pkg/components/secret/secretprovider" - "github.com/radius-project/radius/pkg/kubeutil" "github.com/radius-project/radius/pkg/portableresources/processors" "github.com/radius-project/radius/pkg/recipes" "github.com/radius-project/radius/pkg/recipes/configloader" @@ -34,8 +34,8 @@ import ( // RecipeControllerConfig is the configuration for the controllers which uses recipe. type RecipeControllerConfig struct { - // K8sClients is the collections of Kubernetes clients. - K8sClients *kubeutil.Clients + // Kubernetes provides access to the Kubernetes clients. + Kubernetes *kubernetesclientprovider.KubernetesClientProvider // ResourceClient is a client used by resource processors for interacting with UCP resources. ResourceClient processors.ResourceClient @@ -57,14 +57,12 @@ type RecipeControllerConfig struct { func New(options hostoptions.HostOptions) (*RecipeControllerConfig, error) { cfg := &RecipeControllerConfig{} var err error - cfg.K8sClients, err = kubeutil.NewClients(options.K8sConfig) - if err != nil { - return nil, err - } + + cfg.Kubernetes = kubernetesclientprovider.FromConfig(options.K8sConfig) cfg.UCPConnection = &options.UCPConnection - cfg.ResourceClient = processors.NewResourceClient(options.Arm, options.UCPConnection, cfg.K8sClients.RuntimeClient, cfg.K8sClients.DiscoveryClient) + cfg.ResourceClient = processors.NewResourceClient(options.Arm, options.UCPConnection, cfg.Kubernetes) clientOptions := sdk.NewClientOptions(options.UCPConnection) cfg.DeploymentEngineClient, err = clients.NewResourceDeploymentsClient(&clients.Options{ @@ -111,7 +109,7 @@ func New(options hostoptions.HostOptions) (*RecipeControllerConfig, error) { recipes.TemplateKindTerraform: driver.NewTerraformDriver(options.UCPConnection, secretprovider.NewSecretProvider(options.Config.SecretProvider), driver.TerraformOptions{ Path: options.Config.Terraform.Path, - }, cfg.K8sClients.ClientSet), + }, *cfg.Kubernetes), }, }) diff --git a/pkg/recipes/driver/terraform.go b/pkg/recipes/driver/terraform.go index 72387a4b8f..6f2aec5778 100644 --- a/pkg/recipes/driver/terraform.go +++ b/pkg/recipes/driver/terraform.go @@ -27,10 +27,10 @@ import ( "github.com/google/uuid" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" + "github.com/radius-project/radius/pkg/components/kubernetesclient/kubernetesclientprovider" "github.com/radius-project/radius/pkg/components/secret/secretprovider" rpv1 "github.com/radius-project/radius/pkg/rp/v1" "golang.org/x/exp/slices" - "k8s.io/client-go/kubernetes" "github.com/radius-project/radius/pkg/recipes" "github.com/radius-project/radius/pkg/recipes/terraform" @@ -48,9 +48,9 @@ import ( var _ Driver = (*terraformDriver)(nil) // NewTerraformDriver creates a new instance of driver to execute a Terraform recipe. -func NewTerraformDriver(ucpConn sdk.Connection, secretProvider *secretprovider.SecretProvider, options TerraformOptions, k8sClientSet kubernetes.Interface) Driver { +func NewTerraformDriver(ucpConn sdk.Connection, secretProvider *secretprovider.SecretProvider, options TerraformOptions, kubernetesClients kubernetesclientprovider.KubernetesClientProvider) Driver { return &terraformDriver{ - terraformExecutor: terraform.NewExecutor(ucpConn, secretProvider, k8sClientSet), + terraformExecutor: terraform.NewExecutor(ucpConn, secretProvider, kubernetesClients), options: options, } } diff --git a/pkg/recipes/terraform/execute.go b/pkg/recipes/terraform/execute.go index 623cd827af..5b85170a0a 100644 --- a/pkg/recipes/terraform/execute.go +++ b/pkg/recipes/terraform/execute.go @@ -27,6 +27,7 @@ import ( install "github.com/hashicorp/hc-install" "github.com/hashicorp/terraform-exec/tfexec" tfjson "github.com/hashicorp/terraform-json" + "github.com/radius-project/radius/pkg/components/kubernetesclient/kubernetesclientprovider" "github.com/radius-project/radius/pkg/components/metrics" "github.com/radius-project/radius/pkg/components/secret/secretprovider" "github.com/radius-project/radius/pkg/recipes/recipecontext" @@ -37,7 +38,6 @@ import ( "github.com/radius-project/radius/pkg/ucp/ucplog" "go.opentelemetry.io/otel/attribute" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" ) var ( @@ -48,8 +48,8 @@ var ( var _ TerraformExecutor = (*executor)(nil) // NewExecutor creates a new Executor with the given UCP connection and secret provider, to execute a Terraform recipe. -func NewExecutor(ucpConn sdk.Connection, secretProvider *secretprovider.SecretProvider, k8sClientSet kubernetes.Interface) *executor { - return &executor{ucpConn: ucpConn, secretProvider: secretProvider, k8sClientSet: k8sClientSet} +func NewExecutor(ucpConn sdk.Connection, secretProvider *secretprovider.SecretProvider, kubernetesClients kubernetesclientprovider.KubernetesClientProvider) *executor { + return &executor{ucpConn: ucpConn, secretProvider: secretProvider, kubernetesClients: kubernetesClients} } type executor struct { @@ -59,8 +59,8 @@ type executor struct { // secretProvider is the secret store provider used for managing credentials in UCP. secretProvider *secretprovider.SecretProvider - // k8sClientSet is the Kubernetes client. - k8sClientSet kubernetes.Interface + // kubernetesClients provides access to the Kubernetes clients. + kubernetesClients kubernetesclientprovider.KubernetesClientProvider } // Deploy installs Terraform, creates a working directory, generates a config, and runs Terraform init and @@ -104,7 +104,12 @@ func (e *executor) Deploy(ctx context.Context, options Options) (*tfjson.State, // Validate that the terraform state file backend source exists. // Currently only Kubernetes secret backend is supported, which is created by Terraform as a part of Terraform apply. - backendExists, err := backends.NewKubernetesBackend(e.k8sClientSet).ValidateBackendExists(ctx, backends.KubernetesBackendNamePrefix+kubernetesBackendSuffix) + kubernetesClient, err := e.kubernetesClients.ClientGoClient() + if err != nil { + return nil, fmt.Errorf("error getting kubernetes client: %w", err) + } + + backendExists, err := backends.NewKubernetesBackend(kubernetesClient).ValidateBackendExists(ctx, backends.KubernetesBackendNamePrefix+kubernetesBackendSuffix) if err != nil { return nil, fmt.Errorf("error retrieving kubernetes secret for terraform state: %w", err) } else if !backendExists { @@ -142,7 +147,12 @@ func (e *executor) Delete(ctx context.Context, options Options) error { // Before running terraform init and destroy, ensure that the Terraform state file storage source exists. // If the state file source has been deleted or wasn't created due to a failure during apply then // terraform initialization will fail due to missing backend source. - backendExists, err := backends.NewKubernetesBackend(e.k8sClientSet).ValidateBackendExists(ctx, backends.KubernetesBackendNamePrefix+kubernetesBackendSuffix) + kubernetesClient, err := e.kubernetesClients.ClientGoClient() + if err != nil { + return fmt.Errorf("error getting kubernetes client: %w", err) + } + + backendExists, err := backends.NewKubernetesBackend(kubernetesClient).ValidateBackendExists(ctx, backends.KubernetesBackendNamePrefix+kubernetesBackendSuffix) if err != nil { // Continue with the delete flow for all errors other than backend not found. // If it is an intermittent error then the delete flow will fail and should be retried from the client. @@ -160,7 +170,7 @@ func (e *executor) Delete(ctx context.Context, options Options) error { } // Delete the kubernetes secret created for terraform state file. - err = e.k8sClientSet.CoreV1(). + err = kubernetesClient.CoreV1(). Secrets(backends.RadiusNamespace). Delete(ctx, backends.KubernetesBackendNamePrefix+kubernetesBackendSuffix, metav1.DeleteOptions{}) if err != nil { @@ -214,14 +224,14 @@ func (e executor) setEnvironmentVariables(tf *tfexec.Terraform, options Options) recipeConfig := &options.EnvConfig.RecipeConfig var envVarUpdate bool - if recipeConfig != nil && recipeConfig.Env.AdditionalProperties != nil && len(recipeConfig.Env.AdditionalProperties) > 0 { + if len(recipeConfig.Env.AdditionalProperties) > 0 { envVarUpdate = true for key, value := range recipeConfig.Env.AdditionalProperties { envVars[key] = value } } - if recipeConfig != nil && recipeConfig.EnvSecrets != nil && len(recipeConfig.EnvSecrets) > 0 { + if len(recipeConfig.EnvSecrets) > 0 { for secretName, secretReference := range recipeConfig.EnvSecrets { // Extract secret value from the secrets input if secretData, ok := options.Secrets[secretReference.Source]; ok { @@ -282,7 +292,12 @@ func (e *executor) generateConfig(ctx context.Context, tf *tfexec.Terraform, opt return "", err } - backendConfig, err := tfConfig.AddTerraformBackend(options.ResourceRecipe, backends.NewKubernetesBackend(e.k8sClientSet)) + kubernetesClient, err := e.kubernetesClients.ClientGoClient() + if err != nil { + return "", fmt.Errorf("error getting kubernetes client: %w", err) + } + + backendConfig, err := tfConfig.AddTerraformBackend(options.ResourceRecipe, backends.NewKubernetesBackend(kubernetesClient)) if err != nil { return "", err } diff --git a/test/k8sutil/fake.go b/test/k8sutil/fake.go index ea45baba19..9faaa0d1bf 100644 --- a/test/k8sutil/fake.go +++ b/test/k8sutil/fake.go @@ -19,15 +19,20 @@ package k8sutil import ( "context" + openapi_v2 "github.com/google/gnostic-models/openapiv2" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/version" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/discovery" k8sfake "k8s.io/client-go/kubernetes/fake" + "k8s.io/client-go/openapi" + "k8s.io/client-go/rest" clienttesting "k8s.io/client-go/testing" ) @@ -105,6 +110,31 @@ type DiscoveryClient struct { APIGroup []*metav1.APIGroup } +// OpenAPISchema implements discovery.DiscoveryInterface. +func (d *DiscoveryClient) OpenAPISchema() (*openapi_v2.Document, error) { + panic("unimplemented") +} + +// OpenAPIV3 implements discovery.DiscoveryInterface. +func (d *DiscoveryClient) OpenAPIV3() openapi.Client { + panic("unimplemented") +} + +// RESTClient implements discovery.DiscoveryInterface. +func (d *DiscoveryClient) RESTClient() rest.Interface { + panic("unimplemented") +} + +// ServerVersion implements discovery.DiscoveryInterface. +func (d *DiscoveryClient) ServerVersion() (*version.Info, error) { + panic("unimplemented") +} + +// WithLegacy implements discovery.DiscoveryInterface. +func (d *DiscoveryClient) WithLegacy() discovery.DiscoveryInterface { + panic("unimplemented") +} + // ServerGroups returns a list of API groups supported by the server. func (d *DiscoveryClient) ServerGroups() (*metav1.APIGroupList, error) { return d.Groups, nil From fa1078fcbd140e133a45a5222019d79f5bbe37cb Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Sun, 5 Jan 2025 13:28:27 -0800 Subject: [PATCH 24/37] Add rad bicep publish-extension command (#8183) # Description This new command will transform a resource provider manifest into an extension that the Bicep compiler and editor can understand. The new command shells out to `bicep publish-extension`. Functional testing will be covered as part of the overall test plan for the UDT feature. ## Type of change - This pull request adds or changes features of Radius and has an approved issue (issue link required). Part of: #6688 ## Contributor checklist Please verify that the PR meets the following requirements, where applicable: - [ ] An overview of proposed schema changes is included in a linked GitHub issue. - [ ] A design document PR is created in the [design-notes repository](https://github.com/radius-project/design-notes/), if new APIs are being introduced. - [ ] If applicable, design document has been reviewed and approved by Radius maintainers/approvers. - [ ] A PR for the [samples repository](https://github.com/radius-project/samples) is created, if existing samples are affected by the changes in this PR. - [ ] A PR for the [documentation repository](https://github.com/radius-project/docs) is created, if the changes in this PR affect the documentation or any user facing updates are made. - [ ] A PR for the [recipes repository](https://github.com/radius-project/recipes) is created, if existing recipes are affected by the changes in this PR. Signed-off-by: Ryan Nowak --- cmd/rad/cmd/root.go | 4 + pkg/cli/bicep/bicep.go | 10 +- pkg/cli/cmd/bicep/publish/publish.go | 2 +- pkg/cli/cmd/bicep/publish/publish_test.go | 2 +- pkg/cli/cmd/bicep/publishextension/publish.go | 182 ++++++++++++++++++ .../bicep/publishextension/publish_test.go | 47 +++++ .../publishextension/testdata/invalid.yaml | 6 + .../publishextension/testdata/valid.yaml | 6 + 8 files changed, 254 insertions(+), 5 deletions(-) create mode 100644 pkg/cli/cmd/bicep/publishextension/publish.go create mode 100644 pkg/cli/cmd/bicep/publishextension/publish_test.go create mode 100644 pkg/cli/cmd/bicep/publishextension/testdata/invalid.yaml create mode 100644 pkg/cli/cmd/bicep/publishextension/testdata/valid.yaml diff --git a/cmd/rad/cmd/root.go b/cmd/rad/cmd/root.go index f81e8e78b8..87c9f9b563 100644 --- a/cmd/rad/cmd/root.go +++ b/cmd/rad/cmd/root.go @@ -36,6 +36,7 @@ import ( app_show "github.com/radius-project/radius/pkg/cli/cmd/app/show" app_status "github.com/radius-project/radius/pkg/cli/cmd/app/status" bicep_publish "github.com/radius-project/radius/pkg/cli/cmd/bicep/publish" + bicep_publishextension "github.com/radius-project/radius/pkg/cli/cmd/bicep/publishextension" credential "github.com/radius-project/radius/pkg/cli/cmd/credential" cmd_deploy "github.com/radius-project/radius/pkg/cli/cmd/deploy" env_create "github.com/radius-project/radius/pkg/cli/cmd/env/create" @@ -344,6 +345,9 @@ func initSubCommands() { bicepPublishCmd, _ := bicep_publish.NewCommand(framework) bicepCmd.AddCommand(bicepPublishCmd) + bicepPublishExtensionCmd, _ := bicep_publishextension.NewCommand(framework) + bicepCmd.AddCommand(bicepPublishExtensionCmd) + installCmd := install.NewCommand() RootCmd.AddCommand(installCmd) diff --git a/pkg/cli/bicep/bicep.go b/pkg/cli/bicep/bicep.go index a08bf78450..2ecd0f1d30 100644 --- a/pkg/cli/bicep/bicep.go +++ b/pkg/cli/bicep/bicep.go @@ -31,12 +31,16 @@ const ( retryDelaySecs = 5 ) +func GetBicepFilePath() (string, error) { + return tools.GetLocalFilepath(radBicepEnvVar, binaryName) +} + // IsBicepInstalled returns true if our local copy of bicep is installed // // IsBicepInstalled checks if the Bicep binary is installed on the local machine and returns a boolean and an error if one occurs. func IsBicepInstalled() (bool, error) { - filepath, err := tools.GetLocalFilepath(radBicepEnvVar, binaryName) + filepath, err := GetBicepFilePath() if err != nil { return false, err } @@ -53,7 +57,7 @@ func IsBicepInstalled() (bool, error) { // DeleteBicep cleans our local copy of bicep func DeleteBicep() error { - filepath, err := tools.GetLocalFilepath(radBicepEnvVar, binaryName) + filepath, err := GetBicepFilePath() if err != nil { return err } @@ -72,7 +76,7 @@ func DeleteBicep() error { // DownloadBicep() attempts to download a file from a given URI and save it to a local filepath, retrying up to 10 times if // the download fails. If an error occurs, an error is returned. func DownloadBicep() error { - filepath, err := tools.GetLocalFilepath(radBicepEnvVar, binaryName) + filepath, err := GetBicepFilePath() if err != nil { return err } diff --git a/pkg/cli/cmd/bicep/publish/publish.go b/pkg/cli/cmd/bicep/publish/publish.go index 2ae945201e..941cf78b00 100644 --- a/pkg/cli/cmd/bicep/publish/publish.go +++ b/pkg/cli/cmd/bicep/publish/publish.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package bicep +package publish import ( "bytes" diff --git a/pkg/cli/cmd/bicep/publish/publish_test.go b/pkg/cli/cmd/bicep/publish/publish_test.go index da3639b63a..b9e1aded4a 100644 --- a/pkg/cli/cmd/bicep/publish/publish_test.go +++ b/pkg/cli/cmd/bicep/publish/publish_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package bicep +package publish import ( "context" diff --git a/pkg/cli/cmd/bicep/publishextension/publish.go b/pkg/cli/cmd/bicep/publishextension/publish.go new file mode 100644 index 0000000000..91b34a2004 --- /dev/null +++ b/pkg/cli/cmd/bicep/publishextension/publish.go @@ -0,0 +1,182 @@ +/* +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 publishextension + +import ( + "context" + "errors" + "os" + "os/exec" + "path/filepath" + + "github.com/radius-project/radius/pkg/cli/bicep" + "github.com/radius-project/radius/pkg/cli/clierrors" + "github.com/radius-project/radius/pkg/cli/cmd/commonflags" + "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/spf13/cobra" +) + +// NewCommand creates a new instance of the `rad bicep publish-extension` command. +func NewCommand(factory framework.Factory) (*cobra.Command, framework.Runner) { + runner := NewRunner(factory) + + cmd := &cobra.Command{ + Use: "publish-extension", + Short: "Generate or publish a Bicep extension for a set of resource types.", + Long: `Generate or publish a Bicep extension for a set of resource types. +This command compiles a set of resource types (resource provider manifest) into a Bicep extension for local use or distribution. + +Bicep extensions enable extensibility for the Bicep language. This command can be used to generate and distribute Bicep support for resource types authored by users. Bicep extensions can be distributed using Open Container Initiative (OCI) registry, such as Azure Container Registry, Docker Hub, or GitHub Container Registry. See https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-extension for more information on Bicep extensions. + +Once an extension is been generated, it can be used locally or published to a container registry for distribution depending on the target specified. + +When publishing to an OCI registry it is expected the user runs docker login (or similar command) and has the proper permission to push to the target OCI registry. + `, + Example: ` +# Generate a Bicep extension to a local file +rad bicep publish-extension --from-file ./Example.Provider.yaml --target ./output.tgz + +# Publish a Bicep extension to a container registry +bicep publish-extension ./Example.Provider.yaml --target br:ghcr.io/myregistry/example-provider:v1 + `, + Args: cobra.ExactArgs(0), + RunE: framework.RunCommand(runner), + } + + commonflags.AddFromFileFlagVar(cmd, &runner.ResourceProviderManifestFilePath) + _ = cmd.MarkFlagRequired("from-file") + _ = cmd.MarkFlagFilename("from-file", "yaml", "json") + + cmd.Flags().StringVar(&runner.Target, "target", "", "The destination path file or OCI registry path. OCI registry paths use the format 'br:HOST/PATH:TAG'.") + _ = cmd.MarkFlagRequired("target") + return cmd, runner +} + +// Runner is the runner implementation for the `rad bicep publish-extension` command. +type Runner struct { + Output output.Interface + + ResourceProvider *manifest.ResourceProvider + ResourceProviderManifestFilePath string + Target string +} + +// NewRunner creates a new instance of the `rad bicep publish-extension` runner. +func NewRunner(factory framework.Factory) *Runner { + return &Runner{ + Output: factory.GetOutput(), + } +} + +// Validate validates the `rad bicep publish-extension` command. +func (r *Runner) Validate(cmd *cobra.Command, args []string) error { + // We read the resource provider manifest upfront to ensure it exists and is valid. + // + // The validation we implement in the `rad` CLI is the source of truth for the manifest. The + // manifest-to-bicep-extension tool does minimal validation, so we want to catch any issues + // early. + rp, err := manifest.ReadFile(r.ResourceProviderManifestFilePath) + if err != nil { + return clierrors.MessageWithCause(err, "Failed to read resource provider %q", r.ResourceProviderManifestFilePath) + } + + r.ResourceProvider = rp + + return nil +} + +// Run runs the `rad bicep publish-extension` command. +func (r *Runner) Run(ctx context.Context) error { + // This command ties together two separate shell commands: + // 1. We use NPX to run https://github.com/radius-project/bicep-tools/tree/main/packages/manifest-to-bicep-extension + // - This generates a Bicep extension "index" + // 2. We use `bicep publish-extension` to publish the extension "index" to the "target" + // + // 3. We can clean up the "index" directory after publishing. + + _, err := exec.LookPath("npx") + if errors.Is(err, exec.ErrNotFound) { + return clierrors.Message("The command 'npx' was not found on the PATH. Please install Node.js 16+ to use this command.") + } + + temp, err := os.MkdirTemp("", "bicep-extension-*") + if err != nil { + return err + } + + defer os.RemoveAll(temp) + + err = generateBicepExtensionIndex(ctx, r.ResourceProviderManifestFilePath, temp) + if err != nil { + return err + } + + err = publishExtension(ctx, temp, r.Target) + if err != nil { + return err + } + + r.Output.LogInfo("Successfully published Bicep extension %q to %q", r.ResourceProviderManifestFilePath, r.Target) + return nil +} + +func generateBicepExtensionIndex(ctx context.Context, inputFilePath string, outputDirectoryPath string) error { + // npx @radius-project/manifest-to-bicep-extension@alpha generate + args := []string{ + "@radius-project/manifest-to-bicep-extension@alpha", + "generate", + inputFilePath, + outputDirectoryPath, + } + cmd := exec.CommandContext(ctx, "npx", args...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + err := cmd.Run() + if err != nil { + return clierrors.MessageWithCause(err, "Failed to generate Bicep extension") + } + + return nil +} + +func publishExtension(ctx context.Context, inputDirectoryPath string, target string) error { + bicepFilePath, err := bicep.GetBicepFilePath() + if err != nil { + return err + } + + // rad-bicep publish-extension /index.json --target + args := []string{ + "publish-extension", + filepath.Join(inputDirectoryPath, "index.json"), + "--target", target, + } + cmd := exec.CommandContext(ctx, bicepFilePath, args...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + err = cmd.Run() + if err != nil { + return clierrors.MessageWithCause(err, "Failed to publish Bicep extension") + } + + return nil +} diff --git a/pkg/cli/cmd/bicep/publishextension/publish_test.go b/pkg/cli/cmd/bicep/publishextension/publish_test.go new file mode 100644 index 0000000000..d0cd222d2e --- /dev/null +++ b/pkg/cli/cmd/bicep/publishextension/publish_test.go @@ -0,0 +1,47 @@ +/* +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 publishextension + +import ( + "testing" + + "github.com/radius-project/radius/test/radcli" +) + +// NOTE: this command orchestrates other CLI commands, and so it's not very testable. This will be covered with +// functional tests. + +func TestRunner_Validate(t *testing.T) { + tests := []radcli.ValidateInput{ + { + Name: "Valid", + Input: []string{"--from-file", "testdata/valid.yaml", "--target", "./output.tgz"}, + ExpectedValid: true, + }, + { + Name: "Invalid: invalid manifest", + Input: []string{"--from-file", "testdata/invalid.yaml", "--target", "./output.tgz"}, + ExpectedValid: false, + }, + { + Name: "Invalid: missing required options", + Input: []string{"--from-file", "testdata/valid.yaml"}, + ExpectedValid: false, + }, + } + radcli.SharedValidateValidation(t, NewCommand, tests) +} diff --git a/pkg/cli/cmd/bicep/publishextension/testdata/invalid.yaml b/pkg/cli/cmd/bicep/publishextension/testdata/invalid.yaml new file mode 100644 index 0000000000..9d6143e20e --- /dev/null +++ b/pkg/cli/cmd/bicep/publishextension/testdata/invalid.yaml @@ -0,0 +1,6 @@ +name: MyCompany.Resources +types: + testResources dkdkkdkfkkd: + apiVersions: + '2025-01-01-preview': + schema: {} \ No newline at end of file diff --git a/pkg/cli/cmd/bicep/publishextension/testdata/valid.yaml b/pkg/cli/cmd/bicep/publishextension/testdata/valid.yaml new file mode 100644 index 0000000000..b00936860b --- /dev/null +++ b/pkg/cli/cmd/bicep/publishextension/testdata/valid.yaml @@ -0,0 +1,6 @@ +name: MyCompany.Resources +types: + testResources: + apiVersions: + '2025-01-01-preview': + schema: {} \ No newline at end of file From 763db89b0ec62b2d2370cd4d7ca0c71712b76e99 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Jan 2025 21:13:46 -0800 Subject: [PATCH 25/37] Bump the all group with 3 updates (#8192) --- go.mod | 14 +++++++------- go.sum | 28 ++++++++++++++-------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/go.mod b/go.mod index b8fddc62c4..5d3175e22d 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/cloudcontrol v1.23.3 github.com/aws/aws-sdk-go-v2/service/cloudformation v1.56.2 github.com/aws/aws-sdk-go-v2/service/ec2 v1.198.1 - github.com/aws/aws-sdk-go-v2/service/ecr v1.38.0 + github.com/aws/aws-sdk-go-v2/service/ecr v1.38.1 github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 github.com/aws/smithy-go v1.22.1 github.com/charmbracelet/bubbles v0.20.0 @@ -32,7 +32,7 @@ require ( github.com/dimchansky/utfbom v1.1.1 github.com/fatih/color v1.18.0 github.com/go-chi/chi/v5 v5.2.0 - github.com/go-git/go-git/v5 v5.13.0 + github.com/go-git/go-git/v5 v5.13.1 github.com/go-logr/logr v1.4.2 github.com/go-logr/zapr v1.3.0 github.com/go-openapi/errors v0.22.0 @@ -46,11 +46,12 @@ require ( github.com/go-playground/validator/v10 v10.23.0 github.com/goccy/go-yaml v1.15.13 github.com/gofrs/flock v0.12.1 + github.com/google/gnostic-models v0.6.8 github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.6.0 github.com/gosuri/uilive v0.0.4 github.com/hashicorp/go-retryablehttp v0.7.7 - github.com/hashicorp/hc-install v0.9.0 + github.com/hashicorp/hc-install v0.9.1 github.com/hashicorp/terraform-config-inspect v0.0.0-20240607080351-271db412dbcb github.com/hashicorp/terraform-exec v0.21.0 github.com/jackc/pgx/v5 v5.7.2 @@ -119,10 +120,9 @@ require ( github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect - github.com/go-git/go-billy/v5 v5.6.0 // indirect + github.com/go-git/go-billy/v5 v5.6.1 // indirect github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/google/gnostic-models v0.6.8 // indirect github.com/google/s2a-go v0.1.7 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/googleapis/gax-go/v2 v2.12.5 // indirect @@ -188,7 +188,7 @@ require ( github.com/cloudflare/circl v1.3.9 // indirect github.com/containerd/containerd v1.7.24 github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect - github.com/cyphar/filepath-securejoin v0.3.4 // indirect + github.com/cyphar/filepath-securejoin v0.3.6 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc github.com/docker/cli v26.1.4+incompatible // indirect github.com/docker/distribution v2.8.3+incompatible // indirect @@ -288,7 +288,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.31.0 // indirect golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 - golang.org/x/mod v0.21.0 // indirect + golang.org/x/mod v0.22.0 // indirect golang.org/x/net v0.33.0 // indirect golang.org/x/oauth2 v0.24.0 // indirect golang.org/x/sys v0.28.0 // indirect diff --git a/go.sum b/go.sum index cf2283073b..463454a444 100644 --- a/go.sum +++ b/go.sum @@ -303,8 +303,8 @@ github.com/aws/aws-sdk-go-v2/service/cloudformation v1.56.2 h1:6USen+lDo8xYQutfn github.com/aws/aws-sdk-go-v2/service/cloudformation v1.56.2/go.mod h1:10A7sHyxlTZSB7419K2wq/1tn0x/K9/drbD2j8VRZVc= github.com/aws/aws-sdk-go-v2/service/ec2 v1.198.1 h1:YbNopxjd9baM83YEEmkaYHi+NuJt0AszeaSLqo0CVr0= github.com/aws/aws-sdk-go-v2/service/ec2 v1.198.1/go.mod h1:mwr3iRm8u1+kkEx4ftDM2Q6Yr0XQFBKrP036ng+k5Lk= -github.com/aws/aws-sdk-go-v2/service/ecr v1.38.0 h1:+1IqznlfeMCgFWoWAuwRqykVc6gGoUUQFGXai+77KWs= -github.com/aws/aws-sdk-go-v2/service/ecr v1.38.0/go.mod h1:NqKnlZvLl4Tp2UH/GEc/nhbjmPQhwOXmLp2eldiszLM= +github.com/aws/aws-sdk-go-v2/service/ecr v1.38.1 h1:pCI3RIJnZEUs0evNm+pdDzvAp+YwpabUyQTPPvxO8oY= +github.com/aws/aws-sdk-go-v2/service/ecr v1.38.1/go.mod h1:NqKnlZvLl4Tp2UH/GEc/nhbjmPQhwOXmLp2eldiszLM= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 h1:8eUsivBQzZHqe/3FE+cqwfH+0p5Jo8PFM/QYQSmeZ+M= @@ -391,8 +391,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lV github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= -github.com/cyphar/filepath-securejoin v0.3.4 h1:VBWugsJh2ZxJmLFSM06/0qzQyiQX2Qs0ViKrUAcqdZ8= -github.com/cyphar/filepath-securejoin v0.3.4/go.mod h1:8s/MCNJREmFK0H02MF6Ihv1nakJe4L/w3WZLHNkvlYM= +github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM= +github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -423,8 +423,8 @@ github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQ github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= -github.com/elazarl/goproxy v1.2.1 h1:njjgvO6cRG9rIqN2ebkqy6cQz2Njkx7Fsfv/zIZqgug= -github.com/elazarl/goproxy v1.2.1/go.mod h1:YfEbZtqP4AetfO6d40vWchF3znWX7C7Vd6ZMfdL8z64= +github.com/elazarl/goproxy v1.2.3 h1:xwIyKHbaP5yfT6O9KIeYJR5549MXRQkoQMRXGztz8YQ= +github.com/elazarl/goproxy v1.2.3/go.mod h1:YfEbZtqP4AetfO6d40vWchF3znWX7C7Vd6ZMfdL8z64= github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU= github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= @@ -471,12 +471,12 @@ github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8b github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= -github.com/go-git/go-billy/v5 v5.6.0 h1:w2hPNtoehvJIxR00Vb4xX94qHQi/ApZfX+nBE2Cjio8= -github.com/go-git/go-billy/v5 v5.6.0/go.mod h1:sFDq7xD3fn3E0GOwUSZqHo9lrkmx8xJhA0ZrfvjBRGM= +github.com/go-git/go-billy/v5 v5.6.1 h1:u+dcrgaguSSkbjzHwelEjc0Yj300NUevrrPphk/SoRA= +github.com/go-git/go-billy/v5 v5.6.1/go.mod h1:0AsLr1z2+Uksi4NlElmMblP5rPcDZNRCD8ujZCRR2BE= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= -github.com/go-git/go-git/v5 v5.13.0 h1:vLn5wlGIh/X78El6r3Jr+30W16Blk0CTcxTYcYPWi5E= -github.com/go-git/go-git/v5 v5.13.0/go.mod h1:Wjo7/JyVKtQgUNdXYXIepzWfJQkUEIGvkvVkiXRR/zw= +github.com/go-git/go-git/v5 v5.13.1 h1:DAQ9APonnlvSWpvolXWIuV6Q6zXy2wHbN4cVlNR5Q+M= +github.com/go-git/go-git/v5 v5.13.1/go.mod h1:qryJB4cSBoq3FRoBRf5A77joojuBcmPJ0qu3XXXVixc= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -686,8 +686,8 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/hc-install v0.9.0 h1:2dIk8LcvANwtv3QZLckxcjyF5w8KVtiMxu6G6eLhghE= -github.com/hashicorp/hc-install v0.9.0/go.mod h1:+6vOP+mf3tuGgMApVYtmsnDoKWMDcFXeTxCACYZ8SFg= +github.com/hashicorp/hc-install v0.9.1 h1:gkqTfE3vVbafGQo6VZXcy2v5yoz2bE0+nhZXruCuODQ= +github.com/hashicorp/hc-install v0.9.1/go.mod h1:pWWvN/IrfeBK4XPeXXYkL6EjMufHkCK5DvwxeLKuBf0= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= github.com/hashicorp/hcl/v2 v2.21.0 h1:lve4q/o/2rqwYOgUg3y3V2YPyD1/zkCLGjIV74Jit14= @@ -1079,8 +1079,8 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= -golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= +golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= From 5832dea7c1530cc88dd7d0730f3012c677393d85 Mon Sep 17 00:00:00 2001 From: Vishwanath Hiremath <100623239+vishwahiremat@users.noreply.github.com> Date: Wed, 8 Jan 2025 02:58:13 +0530 Subject: [PATCH 26/37] Fix for picking the right helm chart version during release cut (#8196) # Description To find the correct helm chart version in our code, we search for the release major value but wildcard the patch value. We need to update the versioning logic during the Helm install to handle a few different cases: 1. If there is a pre-release version (i.e. -rc1, -rc2, etc), we want to find a helm chart version that is an exact match 2. If there is a full release version, we want to find a helm chart version that matches the release value but wildcards the patch value (in case we have a patch release) 3. For dev builds, the release version will always be `0.42.42-dev` so this value should be hardcoded as the version. ## Type of change - This pull request fixes a bug in Radius and has an approved issue (issue link required). - This pull request adds or changes features of Radius and has an approved issue (issue link required). - This pull request is a minor refactor, code cleanup, test improvement, or other maintenance task and doesn't change the functionality of Radius (issue link optional). Fixes: https://github.com/radius-project/radius/issues/8195 ## Contributor checklist Please verify that the PR meets the following requirements, where applicable: - [ ] An overview of proposed schema changes is included in a linked GitHub issue. - [ ] A design document PR is created in the [design-notes repository](https://github.com/radius-project/design-notes/), if new APIs are being introduced. - [ ] If applicable, design document has been reviewed and approved by Radius maintainers/approvers. - [ ] A PR for the [samples repository](https://github.com/radius-project/samples) is created, if existing samples are affected by the changes in this PR. - [ ] A PR for the [documentation repository](https://github.com/radius-project/docs) is created, if the changes in this PR affect the documentation or any user facing updates are made. - [ ] A PR for the [recipes repository](https://github.com/radius-project/recipes) is created, if existing recipes are affected by the changes in this PR. --------- Signed-off-by: Vishwanath Hiremath --- go.mod | 1 + go.sum | 2 ++ pkg/cli/helm/cluster.go | 9 ++++++++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 5d3175e22d..54d1c5b313 100644 --- a/go.mod +++ b/go.mod @@ -103,6 +103,7 @@ require ( cloud.google.com/go/iam v1.1.8 // indirect cloud.google.com/go/storage v1.42.0 // indirect dario.cat/mergo v1.0.1 // indirect + github.com/Masterminds/semver v1.5.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Microsoft/hcsshim v0.12.4 // indirect github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect diff --git a/go.sum b/go.sum index 463454a444..92a5b3f2ab 100644 --- a/go.sum +++ b/go.sum @@ -243,6 +243,8 @@ github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= +github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs= diff --git a/pkg/cli/helm/cluster.go b/pkg/cli/helm/cluster.go index c4e07a872f..3e16defa10 100644 --- a/pkg/cli/helm/cluster.go +++ b/pkg/cli/helm/cluster.go @@ -26,6 +26,7 @@ import ( "helm.sh/helm/v3/pkg/storage/driver" "k8s.io/cli-runtime/pkg/genericclioptions" + "github.com/Masterminds/semver" "github.com/radius-project/radius/pkg/version" ) @@ -51,7 +52,13 @@ func NewDefaultClusterOptions() ClusterOptions { // If this is an edge build, we'll use the latest available. chartVersion := version.ChartVersion() if !version.IsEdgeChannel() { - chartVersion = fmt.Sprintf("~%s", version.ChartVersion()) + // When the chart version is the final release, we should use the ~ operator to ensure we fetch the latest patch version. + // For the pre release, we should use the exact version. + ver, _ := semver.NewVersion(chartVersion) + preRelease := ver.Prerelease() + if preRelease == "" { + chartVersion = fmt.Sprintf("~%s", version.ChartVersion()) + } } return ClusterOptions{ From 8eb7f7b437e0bba609b618b38bfc556a22771f38 Mon Sep 17 00:00:00 2001 From: Vishwanath Hiremath <100623239+vishwahiremat@users.noreply.github.com> Date: Wed, 8 Jan 2025 22:03:09 +0530 Subject: [PATCH 27/37] Update release version v0.42.0-rc2 (#8202) # Description Update RC release 0.42 ## Type of change - This pull request fixes a bug in Radius and has an approved issue (issue link required). - This pull request adds or changes features of Radius and has an approved issue (issue link required). - This pull request is a minor refactor, code cleanup, test improvement, or other maintenance task and doesn't change the functionality of Radius (issue link optional). Fixes: #issue_number ## Contributor checklist Please verify that the PR meets the following requirements, where applicable: - [ ] An overview of proposed schema changes is included in a linked GitHub issue. - [ ] A design document PR is created in the [design-notes repository](https://github.com/radius-project/design-notes/), if new APIs are being introduced. - [ ] If applicable, design document has been reviewed and approved by Radius maintainers/approvers. - [ ] A PR for the [samples repository](https://github.com/radius-project/samples) is created, if existing samples are affected by the changes in this PR. - [ ] A PR for the [documentation repository](https://github.com/radius-project/docs) is created, if the changes in this PR affect the documentation or any user facing updates are made. - [ ] A PR for the [recipes repository](https://github.com/radius-project/recipes) is created, if existing recipes are affected by the changes in this PR. Signed-off-by: Vishwanath Hiremath --- versions.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/versions.yaml b/versions.yaml index e282d873a8..22fb619931 100644 --- a/versions.yaml +++ b/versions.yaml @@ -1,6 +1,6 @@ supported: - channel: '0.42' - version: 'v0.42.0-rc1' + version: 'v0.42.0-rc2' - channel: '0.41' version: 'v0.41.0' deprecated: From 3c28b260d3c7bb90a5a1f0d340b896b39f638bfd Mon Sep 17 00:00:00 2001 From: Nithya Subramanian <98416062+nithyatsu@users.noreply.github.com> Date: Wed, 8 Jan 2025 10:41:54 -0800 Subject: [PATCH 28/37] add dynamicrp and components info (#8201) # Description Add texts describing new folders - dynamicrp and components. ## Type of change - This pull request is a minor refactor, code cleanup, test improvement, or other maintenance task and doesn't change the functionality of Radius (issue link optional). ## Contributor checklist Please verify that the PR meets the following requirements, where applicable: - [ ] An overview of proposed schema changes is included in a linked GitHub issue. - [ ] A design document PR is created in the [design-notes repository](https://github.com/radius-project/design-notes/), if new APIs are being introduced. - [ ] If applicable, design document has been reviewed and approved by Radius maintainers/approvers. - [ ] A PR for the [samples repository](https://github.com/radius-project/samples) is created, if existing samples are affected by the changes in this PR. - [ ] A PR for the [documentation repository](https://github.com/radius-project/docs) is created, if the changes in this PR affect the documentation or any user facing updates are made. - [ ] A PR for the [recipes repository](https://github.com/radius-project/recipes) is created, if existing recipes are affected by the changes in this PR. --------- Signed-off-by: Karishma Chawla Co-authored-by: Karishma Chawla --- .../contributing-code/contributing-code-organization/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/contributing/contributing-code/contributing-code-organization/README.md b/docs/contributing/contributing-code/contributing-code-organization/README.md index ebcec4fd8d..dd7a5e9b63 100644 --- a/docs/contributing/contributing-code/contributing-code-organization/README.md +++ b/docs/contributing/contributing-code/contributing-code-organization/README.md @@ -28,10 +28,12 @@ In general you should ask for guidance before creating a new top-level folder in | `aws/` | Utility code and library integrations for working with AWS | | `azure/` | Utility code and library integrations for working with Azure | | `cli/` | Implementation code for the `rad` CLI | +| `components/` | Components and its folders hold the implementations of shared components used by the Radius control-plane services | | `controllers/` | Kubernetes controllers for Radius | | `corerp/` | Resource Provider implementation for `Applications.Core` resources | | `daprrp/` | Resource Provider implementation for `Applications.Dapr` resources | | `datastoresrp/` | Resource Provider implementation for `Applications.Datastores` resources | +| `dynamicrp/` | Implementation of the dynamic resource provider. The dynamicrp is responsible for managing the lifecycle of resources that are defined without their own resource provider implementation. | | `kubernetes/` | Utility code and library integrations for working with Kubernetes | | `kubeutil/` | Utility code and working with Kubernetes on client side | | `portableresources/` | Shared Resource Provider implementation for portable resources | 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 29/37] [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 } From 546d69aeaab7cc6bba0cbf7e30729a1434cbfbd2 Mon Sep 17 00:00:00 2001 From: Karishma Chawla Date: Wed, 8 Jan 2025 20:06:22 -0800 Subject: [PATCH 30/37] Updating contribution docs (#8212) # Description * Remove/hide GitHub Codespaces until it can be re-enabled on the repo. * Add a link to running control plane locally in how to learn guides. * Minor formatting fix. Signed-off-by: Karishma Chawla --- .../first-commit-00-prerequisites/index.md | 4 ++-- .../first-commit-01-development-tools/index.md | 2 +- .../contributing-code-prerequisites/README.md | 12 ++++++------ docs/contributing/how-to.md | 5 ++--- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/docs/contributing/contributing-code/contributing-code-first-commit/first-commit-00-prerequisites/index.md b/docs/contributing/contributing-code/contributing-code-first-commit/first-commit-00-prerequisites/index.md index 2a134403f7..9af8e10c48 100644 --- a/docs/contributing/contributing-code/contributing-code-first-commit/first-commit-00-prerequisites/index.md +++ b/docs/contributing/contributing-code/contributing-code-first-commit/first-commit-00-prerequisites/index.md @@ -17,9 +17,9 @@ On our supported OSes using a package manager to install these dependencies is a ## Required installs -We recommend the usage of either GitHub Codespaces or dev containers to setup your development environment. Here are the links that provide more details: +We recommend the usage of dev containers to setup your development environment. Here are the links that provide more details: -- [Getting started - GitHub Codespaces](../contributing-code-prerequisites/README.md#github-codespaces) + - [Getting started - Dev Containers](../contributing-code-prerequisites/README.md#vs-code-and-dev-container) However, you can also install all tools locally. This is the list of core dependencies to install for the most common tasks. In general we expect all contributors to have all of these tools present: diff --git a/docs/contributing/contributing-code/contributing-code-first-commit/first-commit-01-development-tools/index.md b/docs/contributing/contributing-code/contributing-code-first-commit/first-commit-01-development-tools/index.md index 9069dff969..bf41422160 100644 --- a/docs/contributing/contributing-code/contributing-code-first-commit/first-commit-01-development-tools/index.md +++ b/docs/contributing/contributing-code/contributing-code-first-commit/first-commit-01-development-tools/index.md @@ -15,7 +15,7 @@ Alternatively, you can choose whichever editor you are most comfortable for work - [Visual Studio Code](https://code.visualstudio.com/) - [Go extension](https://marketplace.visualstudio.com/items?itemName=golang.go) -> 📝 **Tip** - If your are using GitHub Codespaces or Dev Containers, the Go extension is already installed for you. You find more details on these options in the [Repository Prerequisites](../contributing-code-prerequisites/README.md). +> 📝 **Tip** - If your are using Dev Containers, the Go extension is already installed for you. You find more details on these options in the [Repository Prerequisites](../contributing-code-prerequisites/README.md). Install both of these and then follow the steps in the *Quick Start* for the Go extension. diff --git a/docs/contributing/contributing-code/contributing-code-prerequisites/README.md b/docs/contributing/contributing-code/contributing-code-prerequisites/README.md index f592e98bdc..e25eb078dc 100644 --- a/docs/contributing/contributing-code/contributing-code-prerequisites/README.md +++ b/docs/contributing/contributing-code/contributing-code-prerequisites/README.md @@ -2,13 +2,13 @@ This page lists the prerequisites for working with the repository. In general, you have three options to get your development setup up and running: -- Remotely using GitHub Codespaces + - Using Visual Studio Code and dev containers - Locally by installing all prerequisites on your machine We will walk you through the options in the following sections. -> 📝 **Tip** - We recommend the usage of GitHub Codespaces or dev containers in Visual Studio Code as they are the most convenient way to get started. +> 📝 **Tip** - We recommend the usage of dev containers in Visual Studio Code as they are the most convenient way to get started. Depending on the task you need to perform, you may need to install more tools, but basic prerequisites should be sufficient for most contributors to get started. @@ -22,9 +22,9 @@ If you get stuck with any development setup option, please ask for help in our [ ## Development environment - setup options -The following sections describe the aforementioned alternatives of development setups in more detail. We recommend to use of _GitHub Codespaces_ or _dev containers_ in VS Code as they are the most convenient way to get started. +The following sections describe the aforementioned alternatives of development setups in more detail. We recommend use of _dev containers_ in VS Code as they are the most convenient way to get started. -### GitHub Codespaces + ### VS Code and Dev Container @@ -72,7 +72,7 @@ Once the container is up and running you can start with your contribution. Contributing to Radius requires several tools to get started. This section lists them grouped by their context -> 📝 **Tip** - If your are using [GitHub Codespaces](#github-codespaces) or [VS Code and Dev Container](#vs-code-and-dev-container), all these tools are already in place and available out-of-the-box. +> 📝 **Tip** - If your are using [VS Code and Dev Container](#vs-code-and-dev-container), all these tools are already in place and available out-of-the-box. - This pull request is a minor refactor, code cleanup, test improvement, or other maintenance task and doesn't change the functionality of Radius (issue link optional). Fixes: #issue_number ## Contributor checklist Please verify that the PR meets the following requirements, where applicable: - [N/A] An overview of proposed schema changes is included in a linked GitHub issue. - [N/A] A design document PR is created in the [design-notes repository](https://github.com/radius-project/design-notes/), if new APIs are being introduced. - [N/A] If applicable, design document has been reviewed and approved by Radius maintainers/approvers. - [N/A] 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. - [N/A] 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. - [N/A] A PR for the [recipes repository](https://github.com/radius-project/recipes) is created, if existing recipes are affected by the changes in this PR. Signed-off-by: Karishma Chawla --- .vscode/launch.json | 2 +- .../running-controlplane-locally.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 85a8aaa6fe..0c2f7a8967 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -65,7 +65,7 @@ "type": "coreclr", "request": "launch", "preLaunchTask": "Build Deployment Engine", - "program": "${workspaceFolder}/../deployment-engine/src/DeploymentEngine/bin/Debug/net6.0/arm-de.dll", + "program": "${workspaceFolder}/../deployment-engine/src/DeploymentEngine/bin/Debug/net8.0/arm-de.dll", "args": [], "cwd": "${workspaceFolder}/../deployment-engine/src/DeploymentEngine", "stopAtEntry": false, diff --git a/docs/contributing/contributing-code/contributing-code-control-plane/running-controlplane-locally.md b/docs/contributing/contributing-code/contributing-code-control-plane/running-controlplane-locally.md index 6e26d51511..c6c96f9be9 100644 --- a/docs/contributing/contributing-code/contributing-code-control-plane/running-controlplane-locally.md +++ b/docs/contributing/contributing-code/contributing-code-control-plane/running-controlplane-locally.md @@ -29,7 +29,7 @@ If you need to manually test APIs you can reach them at the following endpoints 1. Create a Kubernetes cluster, or set your current context to a cluster you want to use. The debug configuration will use your current cluster for storing data. 2. Clone the `radius-project/radius` and `radius-project/deployment-engine` repo next to each other. 3. Run `git submodule update --init` in the `deployment-engine` repo. -4. Install .NET 6.0 SDK - . +4. Install .NET 8.0 SDK - . 5. Install C# VS Code extension - . 6. (Optional) Configure any cloud provider credentials you want to use for developing Radius. @@ -161,7 +161,7 @@ dotnet --list-runtimes dotnet --list-sdks ``` -Make sure you see a `6.0` entry in `--list-runtimes` for `Microsoft.AspNetCore.App` and a `6.0` or newer entry for `--list-sdks`. +Make sure you see a `8.0` entry in `--list-runtimes` for `Microsoft.AspNetCore.App` and a `8.0` or newer entry for `--list-sdks`. If you run into issues here, please re-read the prerequisites related to installing .NET. From 9e74e73911cc6ee44b558c96190356104ec47d4b Mon Sep 17 00:00:00 2001 From: Nick Beenham <1985327+superbeeny@users.noreply.github.com> Date: Thu, 9 Jan 2025 15:46:22 -0500 Subject: [PATCH 33/37] Adding Postgres Helm chart to rad init (#8072) # Description Adding Postgres installation to the Radius Helm chart ## Type of change - This pull request adds or changes features of Radius and has an approved issue (issue link required). Fixes: #8071 Updated issue: https://github.com/radius-project/radius/issues/8096 --------- Signed-off-by: Nick Beenham --- .../Chart/templates/database/configmaps.yaml | 13 ++ deploy/Chart/templates/database/service.yaml | 16 +++ .../templates/database/serviceaccount.yaml | 8 ++ .../Chart/templates/database/statefulset.yaml | 52 ++++++++ deploy/Chart/values.yaml | 14 +++ .../noncloud/resources/sql_test.go | 118 ------------------ .../datastoresrp-resources-sqldb-manual.bicep | 104 --------------- .../datastoresrp-resources-sqldb-recipe.bicep | 94 -------------- 8 files changed, 103 insertions(+), 316 deletions(-) create mode 100644 deploy/Chart/templates/database/configmaps.yaml create mode 100644 deploy/Chart/templates/database/service.yaml create mode 100644 deploy/Chart/templates/database/serviceaccount.yaml create mode 100644 deploy/Chart/templates/database/statefulset.yaml delete mode 100644 test/functional-portable/datastoresrp/noncloud/resources/sql_test.go delete mode 100644 test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-resources-sqldb-manual.bicep delete mode 100644 test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-resources-sqldb-recipe.bicep diff --git a/deploy/Chart/templates/database/configmaps.yaml b/deploy/Chart/templates/database/configmaps.yaml new file mode 100644 index 0000000000..6c902390ff --- /dev/null +++ b/deploy/Chart/templates/database/configmaps.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: database-secret + namespace: "{{ .Release.Namespace }}" + labels: + control-plane: database + app.kubernetes.io/name: database + app.kubernetes.io/part-of: radius +data: + POSTGRES_DB: ps_db + POSTGRES_USER: ps_user + POSTGRES_PASSWORD: SecurePassword \ No newline at end of file diff --git a/deploy/Chart/templates/database/service.yaml b/deploy/Chart/templates/database/service.yaml new file mode 100644 index 0000000000..c10a9818fd --- /dev/null +++ b/deploy/Chart/templates/database/service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + name: database + namespace: "{{ .Release.Namespace }}" + labels: + app.kubernetes.io/name: database + app.kubernetes.io/part-of: radius +spec: + ports: + - port: 5432 + name: postgres + protocol: TCP + targetPort: 5432 + selector: + app.kubernetes.io/name: database \ No newline at end of file diff --git a/deploy/Chart/templates/database/serviceaccount.yaml b/deploy/Chart/templates/database/serviceaccount.yaml new file mode 100644 index 0000000000..617e26e0eb --- /dev/null +++ b/deploy/Chart/templates/database/serviceaccount.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: database + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: database + app.kubernetes.io/part-of: radius diff --git a/deploy/Chart/templates/database/statefulset.yaml b/deploy/Chart/templates/database/statefulset.yaml new file mode 100644 index 0000000000..33d32f2be6 --- /dev/null +++ b/deploy/Chart/templates/database/statefulset.yaml @@ -0,0 +1,52 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: database + namespace: "{{ .Release.Namespace }}" + labels: + control-plane: database + app.kubernetes.io/name: database + app.kubernetes.io/part-of: radius +spec: + serviceName: "database" + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: database + template: + metadata: + labels: + control-plane: database + app.kubernetes.io/name: database + app.kubernetes.io/part-of: radius + spec: + serviceAccountName: database + containers: + - name: database + securityContext: + allowPrivilegeEscalation: false + image: "{{ .Values.database.image }}:{{ .Values.database.tag }}" + imagePullPolicy: IfNotPresent + resources: + requests: + memory: "{{ .Values.database.resources.requests.memory }}" + cpu: "{{ .Values.database.resources.requests.cpu }}" + limits: + memory: "{{ .Values.database.resources.limits.memory }}" + cpu: "{{ .Values.database.resources.limits.cpu }}" + envFrom: + - configMapRef: + name: database-secret + ports: + - containerPort: 5432 + name: postgres + + volumeClaimTemplates: + - metadata: + name: database + spec: + accessModes: ["ReadWriteOnce"] + storageClassName: "{{ .Values.database.storageClassName }}" + resources: + requests: + storage: {{ .Values.database.storageSize }} \ No newline at end of file diff --git a/deploy/Chart/values.yaml b/deploy/Chart/values.yaml index d750295570..d22d987f26 100644 --- a/deploy/Chart/values.yaml +++ b/deploy/Chart/values.yaml @@ -112,3 +112,17 @@ dashboard: memory: "60Mi" limits: memory: "300Mi" + +database: + image: ghcr.io/radius-project/mirror/postgres + tag: latest + storageClassName: "standard" # set to the storage class name if required + # Minimum resource requirements, may need to revisit and scale. + storageSize: "1Gi" + resources: + requests: + cpu: "2" + memory: "512Mi" + limits: + cpu: "2" + memory: "1024Mi" \ No newline at end of file diff --git a/test/functional-portable/datastoresrp/noncloud/resources/sql_test.go b/test/functional-portable/datastoresrp/noncloud/resources/sql_test.go deleted file mode 100644 index f1f265114f..0000000000 --- a/test/functional-portable/datastoresrp/noncloud/resources/sql_test.go +++ /dev/null @@ -1,118 +0,0 @@ -/* -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 resource_test - -import ( - "runtime" - "testing" - - "github.com/radius-project/radius/test/rp" - "github.com/radius-project/radius/test/step" - "github.com/radius-project/radius/test/testutil" - "github.com/radius-project/radius/test/validation" -) - -func Test_SQLDatabase_Manual(t *testing.T) { - // https://github.com/microsoft/mssql-docker/issues/668 - if runtime.GOARCH == "arm64" { - t.Skip("skipping Test_SQL, unsupported architecture") - } - template := "testdata/datastoresrp-resources-sqldb-manual.bicep" - name := "dsrp-resources-sql" - appNamespace := "default-dsrp-resources-sql" - - test := rp.NewRPTest(t, name, []rp.TestStep{ - { - Executor: step.NewDeployExecutor(template, testutil.GetMagpieImage()), - RPResources: &validation.RPResourceSet{ - Resources: []validation.RPResource{ - { - Name: name, - Type: validation.ApplicationsResource, - }, - { - Name: "sql-app-ctnr", - Type: validation.ContainersResource, - App: name, - }, - { - Name: "sql-db", - Type: validation.SQLDatabasesResource, - App: name, - }, - { - Name: "sql-ctnr", - Type: validation.ContainersResource, - App: name, - }, - }, - }, - K8sObjects: &validation.K8sObjectSet{ - Namespaces: map[string][]validation.K8sObject{ - appNamespace: { - validation.NewK8sPodForResource(name, "sql-app-ctnr"), - validation.NewK8sPodForResource(name, "sql-ctnr"), - }, - }, - }, - }, - }) - - test.Test(t) -} - -// Test_SQLDatabase_Recipe validates: -// the creation of a sql database from recipe -// container using the sqlDatabases portable resource to connect to the sql database resource -func Test_SQLDatabase_Recipe(t *testing.T) { - template := "testdata/datastoresrp-resources-sqldb-recipe.bicep" - name := "dsrp-resources-sqldb-recipe" - appNamespace := "dsrp-resources-sqldb-recipe-app" - test := rp.NewRPTest(t, name, []rp.TestStep{ - { - Executor: step.NewDeployExecutor(template, testutil.GetMagpieImage(), testutil.GetBicepRecipeRegistry(), testutil.GetBicepRecipeVersion()), - RPResources: &validation.RPResourceSet{ - Resources: []validation.RPResource{ - { - Name: "dsrp-resources-env-sql-recipe-env", - Type: validation.EnvironmentsResource, - }, - { - Name: "dsrp-resources-sqldb-recipe", - Type: validation.ApplicationsResource, - App: name, - }, - { - Name: "sql-recipe-app-ctnr", - Type: validation.ContainersResource, - App: name, - }, - }, - }, - K8sObjects: &validation.K8sObjectSet{ - Namespaces: map[string][]validation.K8sObject{ - appNamespace: { - validation.NewK8sPodForResource(name, "sql-recipe-app-ctnr").ValidateLabels(false), - validation.NewK8sPodForResource(name, "sql-recipe-resource").ValidateLabels(false), - }, - }, - }, - }, - }) - - test.Test(t) -} diff --git a/test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-resources-sqldb-manual.bicep b/test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-resources-sqldb-manual.bicep deleted file mode 100644 index 1439c50243..0000000000 --- a/test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-resources-sqldb-manual.bicep +++ /dev/null @@ -1,104 +0,0 @@ -extension radius - -@description('Specifies the location for resources.') -param location string = 'global' - -@description('Specifies the image for the container resource.') -param magpieImage string - -@description('Specifies the port for the container resource.') -param magpiePort int = 3000 - -@description('Specifies the environment for resources.') -param environment string = 'test' - -@description('Specifies the image for the sql container resource.') -param sqlImage string = 'mcr.microsoft.com/mssql/server:2019-latest' - -@description('Specifies the port for the container resource.') -param sqlPort int = 1433 - -@description('Specifies the SQL username.') -param username string = 'sa' - -@description('Specifies the SQL password.') -@secure() -param password string = newGuid() - -resource app 'Applications.Core/applications@2023-10-01-preview' = { - name: 'dsrp-resources-sql' - location: location - properties: { - environment: environment - } -} - -resource webapp 'Applications.Core/containers@2023-10-01-preview' = { - name: 'sql-app-ctnr' - location: location - properties: { - application: app.id - connections: { - sql: { - source: db.id - } - } - container: { - image: magpieImage - env: { - CONNECTION_SQL_CONNECTIONSTRING: { - value: db.listSecrets().connectionString - } - } - readinessProbe: { - kind: 'httpGet' - containerPort: magpiePort - path: '/healthz' - } - } - } -} - -resource db 'Applications.Datastores/sqlDatabases@2023-10-01-preview' = { - name: 'sql-db' - location: location - properties: { - application: app.id - environment: environment - server: 'sql-ctnr' - database: 'master' - resourceProvisioning: 'manual' - port: sqlPort - username: username - secrets: { - password: password - } - } -} - -resource sqlContainer 'Applications.Core/containers@2023-10-01-preview' = { - name: 'sql-ctnr' - location: location - properties: { - application: app.id - container: { - image: sqlImage - env: { - ACCEPT_EULA: { - value: 'Y' - } - MSSQL_PID: { - value: 'Developer' - } - MSSQL_SA_PASSWORD: { - value: password - } - } - ports: { - sql: { - containerPort: sqlPort - } - } - } - } -} diff --git a/test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-resources-sqldb-recipe.bicep b/test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-resources-sqldb-recipe.bicep deleted file mode 100644 index dbd75d2e1b..0000000000 --- a/test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-resources-sqldb-recipe.bicep +++ /dev/null @@ -1,94 +0,0 @@ -extension radius - -@description('Specifies the location for resources.') -param location string = 'global' - -@description('Specifies the image for the container resource.') -param magpieImage string - -@description('Specifies the port for the container resource.') -param magpiePort int = 3000 - -@description('Specifies the SQL username.') -param username string = 'sa' - -@description('Specifies the SQL password.') -@secure() -param password string = newGuid() - -param registry string - -param version string - -resource env 'Applications.Core/environments@2023-10-01-preview' = { - name: 'dsrp-resources-env-sql-recipe-env' - location: 'global' - properties: { - compute: { - kind: 'kubernetes' - resourceId: 'self' - namespace: 'dsrp-resources-env-sql-recipe-env' - } - recipes: { - 'Applications.Datastores/sqlDatabases': { - default: { - templateKind: 'bicep' - templatePath: '${registry}/test/testrecipes/test-bicep-recipes/sqldb-recipe:${version}' - parameters: { - username: username - password: password - } - } - } - } - } -} - -resource app 'Applications.Core/applications@2023-10-01-preview' = { - name: 'dsrp-resources-sqldb-recipe' - location: location - properties: { - environment: env.id - extensions: [ - { - kind: 'kubernetesNamespace' - namespace: 'dsrp-resources-sqldb-recipe-app' - } - ] - } -} - -resource webapp 'Applications.Core/containers@2023-10-01-preview' = { - name: 'sql-recipe-app-ctnr' - location: location - properties: { - application: app.id - connections: { - sql: { - source: db.id - } - } - container: { - image: magpieImage - env: { - CONNECTION_SQL_CONNECTIONSTRING: { - value: db.listSecrets().connectionString - } - } - readinessProbe: { - kind: 'httpGet' - containerPort: magpiePort - path: '/healthz' - } - } - } -} - -resource db 'Applications.Datastores/sqlDatabases@2023-10-01-preview' = { - name: 'sql-db-recipe' - location: location - properties: { - application: app.id - environment: env.id - } -} From a7f5a83fc9ff36afac666cd7d9ef3e9f98ae0502 Mon Sep 17 00:00:00 2001 From: Justin Maksimczuk <34260268+justinmaks@users.noreply.github.com> Date: Thu, 9 Jan 2025 16:22:54 -0500 Subject: [PATCH 34/37] add: Fuzz test for resource id parser (#8214) # Description This PR adds a fuzz test to the resource ID parser. This does not close the mentioned issue due to it not being integrated to testing pipeline. ## Type of change - This pull request is a minor refactor, code cleanup, test improvement, or other maintenance task and doesn't change the functionality of Radius (issue link optional). Part of: #8105 ## Contributor checklist Please verify that the PR meets the following requirements, where applicable: - [x] An overview of proposed schema changes is included in a linked GitHub issue. - [x] A design document PR is created in the [design-notes repository](https://github.com/radius-project/design-notes/), if new APIs are being introduced. - [x] If applicable, design document has been reviewed and approved by Radius maintainers/approvers. - [x] 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. - [x] 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. - [x] A PR for the [recipes repository](https://github.com/radius-project/recipes) is created, if existing recipes are affected by the changes in this PR. --------- Signed-off-by: Maksimczuk --- pkg/ucp/resources/id_test.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pkg/ucp/resources/id_test.go b/pkg/ucp/resources/id_test.go index edcc0c6b30..7d9d8afc3e 100644 --- a/pkg/ucp/resources/id_test.go +++ b/pkg/ucp/resources/id_test.go @@ -1228,3 +1228,12 @@ func Test_ParseProviderScope(t *testing.T) { }) } } + +// Fuzz testing function for resource IDs to ensure user input is handled correctly. +func Fuzz_ResourceIDs(f *testing.F) { + f.Add("/planes/radius/local/resourceGroups/test-rg/providers/Applications.Datastores/mongoDatabases/mongo-database-0") + f.Fuzz(func(t *testing.T, id string) { + _, _ = Parse(id) + + }) +} From 845f88db70b21d5fb3ef4e45519caeee71c6ff00 Mon Sep 17 00:00:00 2001 From: Vishwanath Hiremath <100623239+vishwahiremat@users.noreply.github.com> Date: Fri, 10 Jan 2025 03:53:08 +0530 Subject: [PATCH 35/37] Update release version v0.42.0 (#8216) # Description Update release version v0.42.0 ## Type of change - This pull request fixes a bug in Radius and has an approved issue (issue link required). - This pull request adds or changes features of Radius and has an approved issue (issue link required). - This pull request is a minor refactor, code cleanup, test improvement, or other maintenance task and doesn't change the functionality of Radius (issue link optional). Fixes: #issue_number ## Contributor checklist Please verify that the PR meets the following requirements, where applicable: - [ ] An overview of proposed schema changes is included in a linked GitHub issue. - [ ] A design document PR is created in the [design-notes repository](https://github.com/radius-project/design-notes/), if new APIs are being introduced. - [ ] If applicable, design document has been reviewed and approved by Radius maintainers/approvers. - [ ] A PR for the [samples repository](https://github.com/radius-project/samples) is created, if existing samples are affected by the changes in this PR. - [ ] A PR for the [documentation repository](https://github.com/radius-project/docs) is created, if the changes in this PR affect the documentation or any user facing updates are made. - [ ] A PR for the [recipes repository](https://github.com/radius-project/recipes) is created, if existing recipes are affected by the changes in this PR. --------- Signed-off-by: Vishwanath Hiremath --- docs/release-notes/v0.42.0.md | 83 +++++++++++++++++++++++++++++++++++ versions.yaml | 4 +- 2 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 docs/release-notes/v0.42.0.md diff --git a/docs/release-notes/v0.42.0.md b/docs/release-notes/v0.42.0.md new file mode 100644 index 0000000000..fe0d83f611 --- /dev/null +++ b/docs/release-notes/v0.42.0.md @@ -0,0 +1,83 @@ +## Announcing Radius v0.42.0 + +Today we're happy to announce the release of Radius v0.42.0. Check out the [highlights](#highlights) below, along with the [full changelog](#full-changelog) for more details. + +We would like to extend our thanks to all the [new](#new-contributors) and existing contributors who helped make this release possible! + +## Intro to Radius + +If you're new to Radius, check out our website, [radapp.io](https://radapp.io), for more information. Also visit our [getting started guide](https://docs.radapp.io/getting-started/) to learn how to install Radius and create your first app. + +## Highlights + +None + +## Breaking changes + +None + +## New contributors + +None + +## Upgrading to Radius v0.42.0 + +During our preview stage, an upgrade to Radius v0.42.0 requires a full reinstallation of the Radius control-plane, rad CLI, and all Radius apps. Stay tuned for an in-place upgrade path in the future. + +1. Delete any environments you have created: + ```bash + rad env delete + ``` +2. Uninstall the previous version of the Radius control-plane: + ```bash + rad uninstall kubernetes + ``` +3. Visit the [Radius installation guide](https://docs.radapp.io/getting-started/install/) to install the latest CLI, or download a binary below +4. Install the latest version of the Radius control-plane: + ```bash + rad install kubernetes + ``` + +## Full changelog + +* Bump the all group across 1 directory with 13 updates by @dependabot in https://github.com/radius-project/radius/pull/8111 +* Bump bicep-types from `ba8eaca` to `c86fc24` by @dependabot in https://github.com/radius-project/radius/pull/8109 +* Bump mikefarah/yq from 4.44.5 to 4.44.6 in the all group by @dependabot in https://github.com/radius-project/radius/pull/8110 +* Bump golang.org/x/crypto from 0.25.0 to 0.31.0 in /test/magpiego in the go_modules group across 1 directory by @dependabot in https://github.com/radius-project/radius/pull/8123 +* Remove CosmosDB database provider by @rynowak in https://github.com/radius-project/radius/pull/8116 +* enable irsa on cicd by @nithyatsu in https://github.com/radius-project/radius/pull/8052 +* Update Notification Condition for Scheduled Test Runs by @kachawla in https://github.com/radius-project/radius/pull/8115 +* Bump golang.org/x/crypto from 0.28.0 to 0.31.0 in the go_modules group by @dependabot in https://github.com/radius-project/radius/pull/8124 +* Changing permissions of UCP - removing unused permissions by @ytimocin in https://github.com/radius-project/radius/pull/8080 +* Bump bicep-types from `c86fc24` to `7c34fe6` by @dependabot in https://github.com/radius-project/radius/pull/8134 +* Simplify database interface by @rynowak in https://github.com/radius-project/radius/pull/8126 +* Bump aws-actions/configure-aws-credentials from 1.7.0 to 4.0.2 in the all group by @dependabot in https://github.com/radius-project/radius/pull/8136 +* Bump the all group across 1 directory with 21 updates by @dependabot in https://github.com/radius-project/radius/pull/8142 +* Rename database APIs by @rynowak in https://github.com/radius-project/radius/pull/8143 +* Move database and similar packages to components by @rynowak in https://github.com/radius-project/radius/pull/8148 +* Refactor and implement shared integration test host by @rynowak in https://github.com/radius-project/radius/pull/8112 +* Remove ETCd database and secret store by @rynowak in https://github.com/radius-project/radius/pull/8158 +* Add async operation support to dynamic-rp by @rynowak in https://github.com/radius-project/radius/pull/8161 +* Bump the all group with 11 updates by @dependabot in https://github.com/radius-project/radius/pull/8167 +* Bump bicep-types from `7c34fe6` to `3676a8b` by @dependabot in https://github.com/radius-project/radius/pull/8168 +* Move shared services and host to components by @rynowak in https://github.com/radius-project/radius/pull/8160 +* Bump the all group with 2 updates by @dependabot in https://github.com/radius-project/radius/pull/8175 +* Implement resource provider API for dynamic rp by @rynowak in https://github.com/radius-project/radius/pull/8177 +* Release candidate 0.42.0-rc1 by @sk593 in https://github.com/radius-project/radius/pull/8181 +* Register Manifests during ucp startup sequence by @lakshmimsft in https://github.com/radius-project/radius/pull/8120 +* Add capabilities to resource type API by @rynowak in https://github.com/radius-project/radius/pull/8182 +* Add recipe engine by @rynowak in https://github.com/radius-project/radius/pull/8180 +* Add rad bicep publish-extension command by @rynowak in https://github.com/radius-project/radius/pull/8183 +* Bump the all group with 3 updates by @dependabot in https://github.com/radius-project/radius/pull/8192 +* Fix for picking the right helm chart version during release cut by @vishwahiremat in https://github.com/radius-project/radius/pull/8196 +* Update release version v0.42.0-rc2 by @vishwahiremat in https://github.com/radius-project/radius/pull/8202 +* add dynamicrp and components info by @nithyatsu in https://github.com/radius-project/radius/pull/8201 +* [UDT] add create resource type command by @nithyatsu in https://github.com/radius-project/radius/pull/8104 +* Updating contribution docs by @kachawla in https://github.com/radius-project/radius/pull/8212 +* Implement fix for failures caused in test logging by @rynowak in https://github.com/radius-project/radius/pull/8190 +* Update instructions and config for required local dotnet version by @kachawla in https://github.com/radius-project/radius/pull/8213 +* Adding Postgres Helm chart to rad init by @superbeeny in https://github.com/radius-project/radius/pull/8072 + + +**Full Changelog**: https://github.com/radius-project/radius/compare/v0.41.0...v0.42.0 + diff --git a/versions.yaml b/versions.yaml index 22fb619931..7b5f4417ed 100644 --- a/versions.yaml +++ b/versions.yaml @@ -1,9 +1,9 @@ supported: - channel: '0.42' - version: 'v0.42.0-rc2' + version: 'v0.42.0' +deprecated: - channel: '0.41' version: 'v0.41.0' -deprecated: - channel: '0.40' version: 'v0.40.0' - channel: '0.39' From f7143480c7c68c79d250e08a2285c7ea82fd04f8 Mon Sep 17 00:00:00 2001 From: Karishma Chawla Date: Mon, 13 Jan 2025 18:08:33 -0800 Subject: [PATCH 36/37] Revert "Adding Postgres Helm chart to rad init (#8072)" (#8241) # Description This reverts commit 9e74e73911cc6ee44b558c96190356104ec47d4b. Reverting this commit as it is breaking long running tests. Needs to be investigated and checked in again with a fix. ``` Events: Type Reason Age From Message ---- ------ ---- ---- ------- Warning FailedScheduling 47m (x10 over 76m) default-scheduler 0/5 nodes are available: pod has unbound immediate PersistentVolumeClaims. preemption: 0/5 nodes are available: 5 Preemption is not helpful for scheduling.. Warning FailedScheduling 7m17s (x8 over 42m) default-scheduler 0/4 nodes are available: pod has unbound immediate PersistentVolumeClaims. preemption: 0/4 nodes are available: 4 Preemption is not helpful for scheduling.. Normal NotTriggerScaleUp 3m39s (x840 over 144m) cluster-autoscaler pod didn't trigger scale-up: 2 pod has unbound immediate PersistentVolumeClaims ``` ``` % kubectl get pvc -n radius-system NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE database-database-0 Pending standard 3d11h ``` No matching persistent volume to bind the pvc to - ``` % kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pvc-3bc9bae6-18a1-41ed-b78e-01c347df6f68 1Gi RWO Delete Bound dapr-system/dapr-scheduler-data-dir-dapr-scheduler-server-0 default 116d ``` More details can be found here: https://github.com/radius-project/radius/issues/8238. Signed-off-by: Karishma Chawla --- .../Chart/templates/database/configmaps.yaml | 13 -- deploy/Chart/templates/database/service.yaml | 16 --- .../templates/database/serviceaccount.yaml | 8 -- .../Chart/templates/database/statefulset.yaml | 52 -------- deploy/Chart/values.yaml | 14 --- .../noncloud/resources/sql_test.go | 118 ++++++++++++++++++ .../datastoresrp-resources-sqldb-manual.bicep | 104 +++++++++++++++ .../datastoresrp-resources-sqldb-recipe.bicep | 94 ++++++++++++++ 8 files changed, 316 insertions(+), 103 deletions(-) delete mode 100644 deploy/Chart/templates/database/configmaps.yaml delete mode 100644 deploy/Chart/templates/database/service.yaml delete mode 100644 deploy/Chart/templates/database/serviceaccount.yaml delete mode 100644 deploy/Chart/templates/database/statefulset.yaml create mode 100644 test/functional-portable/datastoresrp/noncloud/resources/sql_test.go create mode 100644 test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-resources-sqldb-manual.bicep create mode 100644 test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-resources-sqldb-recipe.bicep diff --git a/deploy/Chart/templates/database/configmaps.yaml b/deploy/Chart/templates/database/configmaps.yaml deleted file mode 100644 index 6c902390ff..0000000000 --- a/deploy/Chart/templates/database/configmaps.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: database-secret - namespace: "{{ .Release.Namespace }}" - labels: - control-plane: database - app.kubernetes.io/name: database - app.kubernetes.io/part-of: radius -data: - POSTGRES_DB: ps_db - POSTGRES_USER: ps_user - POSTGRES_PASSWORD: SecurePassword \ No newline at end of file diff --git a/deploy/Chart/templates/database/service.yaml b/deploy/Chart/templates/database/service.yaml deleted file mode 100644 index c10a9818fd..0000000000 --- a/deploy/Chart/templates/database/service.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: database - namespace: "{{ .Release.Namespace }}" - labels: - app.kubernetes.io/name: database - app.kubernetes.io/part-of: radius -spec: - ports: - - port: 5432 - name: postgres - protocol: TCP - targetPort: 5432 - selector: - app.kubernetes.io/name: database \ No newline at end of file diff --git a/deploy/Chart/templates/database/serviceaccount.yaml b/deploy/Chart/templates/database/serviceaccount.yaml deleted file mode 100644 index 617e26e0eb..0000000000 --- a/deploy/Chart/templates/database/serviceaccount.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -kind: ServiceAccount -metadata: - name: database - namespace: {{ .Release.Namespace }} - labels: - app.kubernetes.io/name: database - app.kubernetes.io/part-of: radius diff --git a/deploy/Chart/templates/database/statefulset.yaml b/deploy/Chart/templates/database/statefulset.yaml deleted file mode 100644 index 33d32f2be6..0000000000 --- a/deploy/Chart/templates/database/statefulset.yaml +++ /dev/null @@ -1,52 +0,0 @@ -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: database - namespace: "{{ .Release.Namespace }}" - labels: - control-plane: database - app.kubernetes.io/name: database - app.kubernetes.io/part-of: radius -spec: - serviceName: "database" - replicas: 1 - selector: - matchLabels: - app.kubernetes.io/name: database - template: - metadata: - labels: - control-plane: database - app.kubernetes.io/name: database - app.kubernetes.io/part-of: radius - spec: - serviceAccountName: database - containers: - - name: database - securityContext: - allowPrivilegeEscalation: false - image: "{{ .Values.database.image }}:{{ .Values.database.tag }}" - imagePullPolicy: IfNotPresent - resources: - requests: - memory: "{{ .Values.database.resources.requests.memory }}" - cpu: "{{ .Values.database.resources.requests.cpu }}" - limits: - memory: "{{ .Values.database.resources.limits.memory }}" - cpu: "{{ .Values.database.resources.limits.cpu }}" - envFrom: - - configMapRef: - name: database-secret - ports: - - containerPort: 5432 - name: postgres - - volumeClaimTemplates: - - metadata: - name: database - spec: - accessModes: ["ReadWriteOnce"] - storageClassName: "{{ .Values.database.storageClassName }}" - resources: - requests: - storage: {{ .Values.database.storageSize }} \ No newline at end of file diff --git a/deploy/Chart/values.yaml b/deploy/Chart/values.yaml index d22d987f26..d750295570 100644 --- a/deploy/Chart/values.yaml +++ b/deploy/Chart/values.yaml @@ -112,17 +112,3 @@ dashboard: memory: "60Mi" limits: memory: "300Mi" - -database: - image: ghcr.io/radius-project/mirror/postgres - tag: latest - storageClassName: "standard" # set to the storage class name if required - # Minimum resource requirements, may need to revisit and scale. - storageSize: "1Gi" - resources: - requests: - cpu: "2" - memory: "512Mi" - limits: - cpu: "2" - memory: "1024Mi" \ No newline at end of file diff --git a/test/functional-portable/datastoresrp/noncloud/resources/sql_test.go b/test/functional-portable/datastoresrp/noncloud/resources/sql_test.go new file mode 100644 index 0000000000..f1f265114f --- /dev/null +++ b/test/functional-portable/datastoresrp/noncloud/resources/sql_test.go @@ -0,0 +1,118 @@ +/* +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 resource_test + +import ( + "runtime" + "testing" + + "github.com/radius-project/radius/test/rp" + "github.com/radius-project/radius/test/step" + "github.com/radius-project/radius/test/testutil" + "github.com/radius-project/radius/test/validation" +) + +func Test_SQLDatabase_Manual(t *testing.T) { + // https://github.com/microsoft/mssql-docker/issues/668 + if runtime.GOARCH == "arm64" { + t.Skip("skipping Test_SQL, unsupported architecture") + } + template := "testdata/datastoresrp-resources-sqldb-manual.bicep" + name := "dsrp-resources-sql" + appNamespace := "default-dsrp-resources-sql" + + test := rp.NewRPTest(t, name, []rp.TestStep{ + { + Executor: step.NewDeployExecutor(template, testutil.GetMagpieImage()), + RPResources: &validation.RPResourceSet{ + Resources: []validation.RPResource{ + { + Name: name, + Type: validation.ApplicationsResource, + }, + { + Name: "sql-app-ctnr", + Type: validation.ContainersResource, + App: name, + }, + { + Name: "sql-db", + Type: validation.SQLDatabasesResource, + App: name, + }, + { + Name: "sql-ctnr", + Type: validation.ContainersResource, + App: name, + }, + }, + }, + K8sObjects: &validation.K8sObjectSet{ + Namespaces: map[string][]validation.K8sObject{ + appNamespace: { + validation.NewK8sPodForResource(name, "sql-app-ctnr"), + validation.NewK8sPodForResource(name, "sql-ctnr"), + }, + }, + }, + }, + }) + + test.Test(t) +} + +// Test_SQLDatabase_Recipe validates: +// the creation of a sql database from recipe +// container using the sqlDatabases portable resource to connect to the sql database resource +func Test_SQLDatabase_Recipe(t *testing.T) { + template := "testdata/datastoresrp-resources-sqldb-recipe.bicep" + name := "dsrp-resources-sqldb-recipe" + appNamespace := "dsrp-resources-sqldb-recipe-app" + test := rp.NewRPTest(t, name, []rp.TestStep{ + { + Executor: step.NewDeployExecutor(template, testutil.GetMagpieImage(), testutil.GetBicepRecipeRegistry(), testutil.GetBicepRecipeVersion()), + RPResources: &validation.RPResourceSet{ + Resources: []validation.RPResource{ + { + Name: "dsrp-resources-env-sql-recipe-env", + Type: validation.EnvironmentsResource, + }, + { + Name: "dsrp-resources-sqldb-recipe", + Type: validation.ApplicationsResource, + App: name, + }, + { + Name: "sql-recipe-app-ctnr", + Type: validation.ContainersResource, + App: name, + }, + }, + }, + K8sObjects: &validation.K8sObjectSet{ + Namespaces: map[string][]validation.K8sObject{ + appNamespace: { + validation.NewK8sPodForResource(name, "sql-recipe-app-ctnr").ValidateLabels(false), + validation.NewK8sPodForResource(name, "sql-recipe-resource").ValidateLabels(false), + }, + }, + }, + }, + }) + + test.Test(t) +} diff --git a/test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-resources-sqldb-manual.bicep b/test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-resources-sqldb-manual.bicep new file mode 100644 index 0000000000..1439c50243 --- /dev/null +++ b/test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-resources-sqldb-manual.bicep @@ -0,0 +1,104 @@ +extension radius + +@description('Specifies the location for resources.') +param location string = 'global' + +@description('Specifies the image for the container resource.') +param magpieImage string + +@description('Specifies the port for the container resource.') +param magpiePort int = 3000 + +@description('Specifies the environment for resources.') +param environment string = 'test' + +@description('Specifies the image for the sql container resource.') +param sqlImage string = 'mcr.microsoft.com/mssql/server:2019-latest' + +@description('Specifies the port for the container resource.') +param sqlPort int = 1433 + +@description('Specifies the SQL username.') +param username string = 'sa' + +@description('Specifies the SQL password.') +@secure() +param password string = newGuid() + +resource app 'Applications.Core/applications@2023-10-01-preview' = { + name: 'dsrp-resources-sql' + location: location + properties: { + environment: environment + } +} + +resource webapp 'Applications.Core/containers@2023-10-01-preview' = { + name: 'sql-app-ctnr' + location: location + properties: { + application: app.id + connections: { + sql: { + source: db.id + } + } + container: { + image: magpieImage + env: { + CONNECTION_SQL_CONNECTIONSTRING: { + value: db.listSecrets().connectionString + } + } + readinessProbe: { + kind: 'httpGet' + containerPort: magpiePort + path: '/healthz' + } + } + } +} + +resource db 'Applications.Datastores/sqlDatabases@2023-10-01-preview' = { + name: 'sql-db' + location: location + properties: { + application: app.id + environment: environment + server: 'sql-ctnr' + database: 'master' + resourceProvisioning: 'manual' + port: sqlPort + username: username + secrets: { + password: password + } + } +} + +resource sqlContainer 'Applications.Core/containers@2023-10-01-preview' = { + name: 'sql-ctnr' + location: location + properties: { + application: app.id + container: { + image: sqlImage + env: { + ACCEPT_EULA: { + value: 'Y' + } + MSSQL_PID: { + value: 'Developer' + } + MSSQL_SA_PASSWORD: { + value: password + } + } + ports: { + sql: { + containerPort: sqlPort + } + } + } + } +} diff --git a/test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-resources-sqldb-recipe.bicep b/test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-resources-sqldb-recipe.bicep new file mode 100644 index 0000000000..dbd75d2e1b --- /dev/null +++ b/test/functional-portable/datastoresrp/noncloud/resources/testdata/datastoresrp-resources-sqldb-recipe.bicep @@ -0,0 +1,94 @@ +extension radius + +@description('Specifies the location for resources.') +param location string = 'global' + +@description('Specifies the image for the container resource.') +param magpieImage string + +@description('Specifies the port for the container resource.') +param magpiePort int = 3000 + +@description('Specifies the SQL username.') +param username string = 'sa' + +@description('Specifies the SQL password.') +@secure() +param password string = newGuid() + +param registry string + +param version string + +resource env 'Applications.Core/environments@2023-10-01-preview' = { + name: 'dsrp-resources-env-sql-recipe-env' + location: 'global' + properties: { + compute: { + kind: 'kubernetes' + resourceId: 'self' + namespace: 'dsrp-resources-env-sql-recipe-env' + } + recipes: { + 'Applications.Datastores/sqlDatabases': { + default: { + templateKind: 'bicep' + templatePath: '${registry}/test/testrecipes/test-bicep-recipes/sqldb-recipe:${version}' + parameters: { + username: username + password: password + } + } + } + } + } +} + +resource app 'Applications.Core/applications@2023-10-01-preview' = { + name: 'dsrp-resources-sqldb-recipe' + location: location + properties: { + environment: env.id + extensions: [ + { + kind: 'kubernetesNamespace' + namespace: 'dsrp-resources-sqldb-recipe-app' + } + ] + } +} + +resource webapp 'Applications.Core/containers@2023-10-01-preview' = { + name: 'sql-recipe-app-ctnr' + location: location + properties: { + application: app.id + connections: { + sql: { + source: db.id + } + } + container: { + image: magpieImage + env: { + CONNECTION_SQL_CONNECTIONSTRING: { + value: db.listSecrets().connectionString + } + } + readinessProbe: { + kind: 'httpGet' + containerPort: magpiePort + path: '/healthz' + } + } + } +} + +resource db 'Applications.Datastores/sqlDatabases@2023-10-01-preview' = { + name: 'sql-db-recipe' + location: location + properties: { + application: app.id + environment: env.id + } +} From d56c8665ca6f6ba7156b3864c17fb2723f056b4b Mon Sep 17 00:00:00 2001 From: Yetkin Timocin Date: Wed, 15 Jan 2025 11:42:27 -0800 Subject: [PATCH 37/37] Disabling cache in the setup-go steps of CI/CD (#8147) # Description Because the cache already exists on the GitHub runners we use, disabling cache in the setup-go steps of our CI/CD pipelines is expected solve the issue of seeing bunch of logs that say "**cannot open: file exists**". The official documentation of setup-go and the part that is relevant: https://github.com/actions/setup-go#caching-dependency-files-and-build-outputs. This points out that setup-go does caching for us. If the cache is already there, we will get all the logs in our workflows. There is no disadvantage of this approach, as far as I know, since the cache is already there. I actually ran into this [comment](https://github.com/actions/setup-go/issues/403#issuecomment-2299976549) that says that disabling cache in setup-go step improved the speed of the said step. We can always update our workflows if we see any other disadvantages. Our runners: https://github.com/radius-project/radius/actions/runners. References: - https://github.com/actions/setup-go/issues/403 - https://github.com/actions/setup-go/issues/403#issuecomment-2299976549 - https://github.com/actions/setup-go/issues/314#issuecomment-1379132756 ## Type of change - This pull request fixes a bug in Radius and has an approved issue (issue link required). Fixes: #7790 ## Contributor checklist Please verify that the PR meets the following requirements, where applicable: - [ ] An overview of proposed schema changes is included in a linked GitHub issue. - [ ] A design document PR is created in the [design-notes repository](https://github.com/radius-project/design-notes/), if new APIs are being introduced. - [ ] If applicable, design document has been reviewed and approved by Radius maintainers/approvers. - [ ] A PR for the [samples repository](https://github.com/radius-project/samples) is created, if existing samples are affected by the changes in this PR. - [ ] A PR for the [documentation repository](https://github.com/radius-project/docs) is created, if the changes in this PR affect the documentation or any user facing updates are made. - [ ] A PR for the [recipes repository](https://github.com/radius-project/recipes) is created, if existing recipes are affected by the changes in this PR. Signed-off-by: ytimocin --- .github/actions/setup-rad-cli/action.yaml | 1 + .github/workflows/functional-test-cloud.yaml | 1 + .github/workflows/functional-test-noncloud.yaml | 1 + 3 files changed, 3 insertions(+) diff --git a/.github/actions/setup-rad-cli/action.yaml b/.github/actions/setup-rad-cli/action.yaml index 1f1054acd5..02623c9cb2 100644 --- a/.github/actions/setup-rad-cli/action.yaml +++ b/.github/actions/setup-rad-cli/action.yaml @@ -7,6 +7,7 @@ runs: uses: actions/setup-go@v5 with: go-version: ${{ env.GOVER }} + cache: false - name: Get Go Cache path shell: bash id: go-cache-paths diff --git a/.github/workflows/functional-test-cloud.yaml b/.github/workflows/functional-test-cloud.yaml index 9b7d89faa1..a15b0c6e22 100644 --- a/.github/workflows/functional-test-cloud.yaml +++ b/.github/workflows/functional-test-cloud.yaml @@ -493,6 +493,7 @@ jobs: uses: actions/setup-go@v5 with: go-version: ${{ env.GOVER }} + cache: false - name: Get Go Cache path id: go-cache-paths diff --git a/.github/workflows/functional-test-noncloud.yaml b/.github/workflows/functional-test-noncloud.yaml index 870dde5d84..a0af5c4844 100644 --- a/.github/workflows/functional-test-noncloud.yaml +++ b/.github/workflows/functional-test-noncloud.yaml @@ -179,6 +179,7 @@ jobs: uses: actions/setup-go@v5 with: go-version: ${{ env.GOVER }} + cache: false - name: Get Go Cache path id: go-cache-paths