From 8da596e9d5f62301d9f49d6e4cc7ee6c33e7a561 Mon Sep 17 00:00:00 2001 From: gregnuttall Date: Thu, 1 Dec 2022 12:15:50 +0000 Subject: [PATCH 01/10] ignore vendor folder --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 0da1903..197c610 100644 --- a/.gitignore +++ b/.gitignore @@ -270,3 +270,4 @@ $RECYCLE.BIN/ # End of https://www.gitignore.io/api/vim,linux,macos,emacs,windows,terraform,intellij+all,visualstudiocode bin/ +vendor From c5f4e6204eac86579e4d4836e489af36d93f64ad Mon Sep 17 00:00:00 2001 From: gregnuttall Date: Wed, 30 Nov 2022 14:14:52 +0000 Subject: [PATCH 02/10] adds methods and structs for active active subscription --- service/subscriptions/model.go | 32 ++++++++++++++++++++++---------- service/subscriptions/service.go | 17 +++++++++++++++++ 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/service/subscriptions/model.go b/service/subscriptions/model.go index db40c73..a8e1fee 100644 --- a/service/subscriptions/model.go +++ b/service/subscriptions/model.go @@ -8,6 +8,7 @@ import ( type CreateSubscription struct { Name *string `json:"name,omitempty"` + DeploymentType *string `json:"deploymentType,omitempty"` DryRun *bool `json:"dryRun,omitempty"` PaymentMethodID *int `json:"paymentMethodId,omitempty"` PaymentMethod *string `json:"paymentMethod,omitempty"` @@ -51,16 +52,17 @@ func (o CreateNetworking) String() string { } type CreateDatabase struct { - Name *string `json:"name,omitempty"` - Protocol *string `json:"protocol,omitempty"` - MemoryLimitInGB *float64 `json:"memoryLimitInGb,omitempty"` - SupportOSSClusterAPI *bool `json:"supportOSSClusterApi,omitempty"` - DataPersistence *string `json:"dataPersistence,omitempty"` - Replication *bool `json:"replication,omitempty"` - ThroughputMeasurement *CreateThroughput `json:"throughputMeasurement,omitempty"` - Modules []*CreateModules `json:"modules,omitempty"` - Quantity *int `json:"quantity,omitempty"` - AverageItemSizeInBytes *int `json:"averageItemSizeInBytes,omitempty"` + Name *string `json:"name,omitempty"` + Protocol *string `json:"protocol,omitempty"` + MemoryLimitInGB *float64 `json:"memoryLimitInGb,omitempty"` + SupportOSSClusterAPI *bool `json:"supportOSSClusterApi,omitempty"` + DataPersistence *string `json:"dataPersistence,omitempty"` + Replication *bool `json:"replication,omitempty"` + ThroughputMeasurement *CreateThroughput `json:"throughputMeasurement,omitempty"` + LocalThroughputMeasurement []*CreateLocalThroughput `json:"localThroughputMeasurement,omitempty"` + Modules []*CreateModules `json:"modules,omitempty"` + Quantity *int `json:"quantity,omitempty"` + AverageItemSizeInBytes *int `json:"averageItemSizeInBytes,omitempty"` } func (o CreateDatabase) String() string { @@ -76,6 +78,16 @@ func (o CreateThroughput) String() string { return internal.ToString(o) } +type CreateLocalThroughput struct { + Region *string `json:"region,omitempty"` + WriteOperationsPerSecond *int `json:"writeOperationsPerSecond"` + ReadOperationsPerSecond *int `json:"readOperationsPerSecond"` +} + +func (o CreateLocalThroughput) String() string { + return internal.ToString(o) +} + type CreateModules struct { Name *string `json:"name,omitempty"` } diff --git a/service/subscriptions/service.go b/service/subscriptions/service.go index 223b410..c8c8fc8 100644 --- a/service/subscriptions/service.go +++ b/service/subscriptions/service.go @@ -188,6 +188,23 @@ func (a *API) CreateVPCPeering(ctx context.Context, id int, create CreateVPCPeer return id, nil } +func (a *API) CreateActiveActiveVPCPeering(ctx context.Context, id int, create CreateVPCPeering) (int, error) { + var task taskResponse + err := a.client.Post(ctx, fmt.Sprintf("create peering for subscription %d", id), fmt.Sprintf("/subscriptions/%d/regions/peerings/", id), create, &task) + if err != nil { + return 0, wrap404Error(id, err) + } + + a.logger.Printf("Waiting for subscription %d peering details to be retrieved", id) + + id, err = a.task.WaitForResourceId(ctx, *task.ID) + if err != nil { + return 0, err + } + + return id, nil +} + // DeleteVPCPeering destroys an existing VPC peering connection. func (a *API) DeleteVPCPeering(ctx context.Context, subscription int, peering int) error { var task taskResponse From 907998de581e057a0e9b6bf931b3b5b8bd30f15e Mon Sep 17 00:00:00 2001 From: gregnuttall Date: Mon, 5 Dec 2022 11:06:32 +0000 Subject: [PATCH 03/10] Adds AA database create and update --- service/databases/model.go | 74 ++++++++++++++++++++++++++++++++++++ service/databases/service.go | 36 ++++++++++++++++++ 2 files changed, 110 insertions(+) diff --git a/service/databases/model.go b/service/databases/model.go index 5770b21..3a86a78 100644 --- a/service/databases/model.go +++ b/service/databases/model.go @@ -41,6 +41,24 @@ func (o CreateDatabase) String() string { return internal.ToString(o) } +type CreateActiveActiveDatabase struct { + DryRun *bool `json:"dryRun,omitempty"` + Name *string `json:"name,omitempty"` + Protocol *string `json:"protocol,omitempty"` + MemoryLimitInGB *float64 `json:"memoryLimitInGb,omitempty"` + SupportOSSClusterAPI *bool `json:"supportOSSClusterApi,omitempty"` + UseExternalEndpointForOSSClusterAPI *bool `json:"useExternalEndpointForOSSClusterApi,omitempty"` + GlobalDataPersistence *string `json:"dataPersistence,omitempty"` + GlobalSourceIP []*string `json:"sourceIp,omitempty"` + GlobalPassword *string `json:"password,omitempty"` + GlobalAlerts []*CreateAlert `json:"alerts,omitempty"` + LocalThroughputMeasurement []*LocalThroughput `json:"localThroughputMeasurement,omitempty"` +} + +func (o CreateActiveActiveDatabase) String() string { + return internal.ToString(o) +} + type CreateThroughputMeasurement struct { By *string `json:"by,omitempty"` Value *int `json:"value,omitempty"` @@ -67,6 +85,7 @@ func (o CreateModule) String() string { return internal.ToString(o) } +// TODO: do we need a separate ActiveActiveDatabase type as well? type Database struct { ID *int `json:"databaseId,omitempty"` Name *string `json:"name,omitempty"` @@ -182,6 +201,25 @@ func (o UpdateDatabase) String() string { return internal.ToString(o) } +type UpdateActiveActiveDatabase struct { + DryRun *bool `json:"dryRun,omitempty"` + MemoryLimitInGB *float64 `json:"memoryLimitInGb,omitempty"` + SupportOSSClusterAPI *bool `json:"supportOSSClusterApi,omitempty"` + UseExternalEndpointForOSSClusterAPI *bool `json:"useExternalEndpointForOSSClusterApi,omitempty"` + ClientSSLCertificate *string `json:"clientSslCertificate,omitempty"` + EnableTls *bool `json:"enableTls,omitempty"` + GlobalDataPersistence *string `json:"globalDataPersistence,omitempty"` + GlobalPassword *string `json:"globalPassword,omitempty"` + GlobalSourceIP []*string `json:"globalSourceIp,omitempty"` + GlobalAlerts []*UpdateAlert `json:"globalAlerts,omitempty"` + Regions []*LocalRegionProperties `json:"regions,omitempty"` + DataEvictionPolicy *string `json:"dataEvictionPolicy,omitempty"` +} + +func (o UpdateActiveActiveDatabase) String() string { + return internal.ToString(o) +} + type UpdateThroughputMeasurement struct { By *string `json:"by,omitempty"` Value *int `json:"value,omitempty"` @@ -200,6 +238,42 @@ func (o UpdateAlert) String() string { return internal.ToString(o) } +type LocalRegionProperties struct { + Region *string `json:"region,omitempty"` + RemoteBackup *DatabaseBackupConfig `json:"remoteBackup,omitempty"` + LocalThroughputMeasurement *LocalThroughput `json:"localThroughputMeasurement,omitempty"` + DataPersistence *string `json:"dataPersistence,omitempty"` + Password *string `json:"password,omitempty"` + SourceIP []*string `json:"sourceIp,omitempty"` + Alerts []*UpdateAlert `json:"alerts,omitempty"` +} + +func (o LocalRegionProperties) String() string { + return internal.ToString(o) +} + +type LocalThroughput struct { + Region *string `json:"region,omitempty"` + WriteOperationsPerSecond *int `json:"writeOperationsPerSecond,omitempty"` + ReadOperationsPerSecond *int `json:"readOperationsPerSecond,omitempty"` +} + +func (o LocalThroughput) String() string { + return internal.ToString(o) +} + +type DatabaseBackupConfig struct { + Active *bool `json:"active,omitempty"` + Interval *string `json:"interval,omitempty"` + TimeUTC *string `json:"timeUTC,omitempty"` + StorageType *string `json:"storageType,omitempty"` + StoragePath *string `json:"storagePath,omitempty"` +} + +func (o DatabaseBackupConfig) String() string { + return internal.ToString(o) +} + type Import struct { SourceType *string `json:"sourceType,omitempty"` ImportFromURI []*string `json:"importFromUri,omitempty"` diff --git a/service/databases/service.go b/service/databases/service.go index 7de7fb2..a90938e 100644 --- a/service/databases/service.go +++ b/service/databases/service.go @@ -56,6 +56,24 @@ func (a *API) Create(ctx context.Context, subscription int, db CreateDatabase) ( return id, nil } +// Create will create a new database for the subscription and return the identifier of the database. +func (a *API) ActiveActiveCreate(ctx context.Context, subscription int, db CreateActiveActiveDatabase) (int, error) { + var task taskResponse + err := a.client.Post(ctx, fmt.Sprintf("create database for subscription %d", subscription), fmt.Sprintf("/subscriptions/%d/databases", subscription), db, &task) + if err != nil { + return 0, err + } + + a.logger.Printf("Waiting for new database for subscription %d to finish being created", subscription) + + id, err := a.task.WaitForResourceId(ctx, *task.ID) + if err != nil { + return 0, err + } + + return id, nil +} + // List will return a ListDatabase that is capable of paging through all of the databases associated with a // subscription. func (a *API) List(ctx context.Context, subscription int) *ListDatabase { @@ -91,6 +109,24 @@ func (a *API) Update(ctx context.Context, subscription int, database int, update return nil } +// Update will update certain values of an existing database. +func (a *API) ActiveActiveUpdate(ctx context.Context, subscription int, database int, update UpdateActiveActiveDatabase) error { + var task taskResponse + err := a.client.Put(ctx, fmt.Sprintf("update database %d for subscription %d", database, subscription), fmt.Sprintf("/subscriptions/%d/databases/%d/regions", subscription, database), update, &task) + if err != nil { + return err + } + + a.logger.Printf("Waiting for database %d for subscription %d to finish being updated", database, subscription) + + err = a.task.Wait(ctx, *task.ID) + if err != nil { + return err + } + + return nil +} + // Delete will destroy an existing database. func (a *API) Delete(ctx context.Context, subscription int, database int) error { var task taskResponse From a384ef959af221674349bb5dbe8ae739a0f27b4b Mon Sep 17 00:00:00 2001 From: gregnuttall Date: Mon, 5 Dec 2022 11:10:55 +0000 Subject: [PATCH 04/10] Adds regions --- client.go | 4 ++++ service/regions/model.go | 33 ++++++++++++++++++++++++++++ service/regions/regions.go | 2 ++ service/regions/service.go | 44 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 83 insertions(+) create mode 100644 service/regions/model.go create mode 100644 service/regions/regions.go create mode 100644 service/regions/service.go diff --git a/client.go b/client.go index d06e190..dcb334a 100644 --- a/client.go +++ b/client.go @@ -14,6 +14,7 @@ import ( "github.com/RedisLabs/rediscloud-go-api/service/account" "github.com/RedisLabs/rediscloud-go-api/service/cloud_accounts" "github.com/RedisLabs/rediscloud-go-api/service/databases" + "github.com/RedisLabs/rediscloud-go-api/service/regions" "github.com/RedisLabs/rediscloud-go-api/service/subscriptions" ) @@ -22,6 +23,7 @@ type Client struct { CloudAccount *cloud_accounts.API Database *databases.API Subscription *subscriptions.API + Regions *regions.API } func NewClient(configs ...Option) (*Client, error) { @@ -53,12 +55,14 @@ func NewClient(configs ...Option) (*Client, error) { c := cloud_accounts.NewAPI(client, t, config.logger) d := databases.NewAPI(client, t, config.logger) s := subscriptions.NewAPI(client, t, config.logger) + r := regions.NewAPI(client, t, config.logger) return &Client{ Account: a, CloudAccount: c, Database: d, Subscription: s, + Regions: r, }, nil } diff --git a/service/regions/model.go b/service/regions/model.go new file mode 100644 index 0000000..7e3806a --- /dev/null +++ b/service/regions/model.go @@ -0,0 +1,33 @@ +package regions + +import ( + "github.com/RedisLabs/rediscloud-go-api/internal" +) + +type Regions struct { + SubscriptionId *int `json:"subscriptionId,omitempty"` + Regions []*Region `json:"regions,omitempty"` +} + +func (o Regions) String() string { + return internal.ToString(o) +} + +type Region struct { + RegionId *int `json:"regionId,omitempty"` + Region *string `json:"region,omitempty"` + DeploymentCIDR *string `json:"deploymentCIDR,omitempty"` + VpcId *string `json:"vpcId,omitempty"` + Databases []*Database `json:"databases,omitempty"` +} + +func (o Region) String() string { + return internal.ToString(o) +} + +type Database struct { + DatabaseId *int `json:"databaseId,omitempty"` + DatabaseName *string `json:"DatabaseName,omitempty"` + ReadOperationsPerSecond *int `json:"readOperationsPerSecond,omitempty"` + WriteOperationsPerSecond *int `json:"writeOperationsPerSecond,omitempty"` +} diff --git a/service/regions/regions.go b/service/regions/regions.go new file mode 100644 index 0000000..39a63f5 --- /dev/null +++ b/service/regions/regions.go @@ -0,0 +1,2 @@ +// Package regions contains the API calls for the Active-Active Subscription regions service. +package regions diff --git a/service/regions/service.go b/service/regions/service.go new file mode 100644 index 0000000..d276f64 --- /dev/null +++ b/service/regions/service.go @@ -0,0 +1,44 @@ +package regions + +import ( + "context" + "fmt" +) + +type Log interface { + Printf(format string, args ...interface{}) +} + +type HttpClient interface { + Get(ctx context.Context, name, path string, responseBody interface{}) error + Post(ctx context.Context, name, path string, requestBody interface{}, responseBody interface{}) error + Put(ctx context.Context, name, path string, requestBody interface{}, responseBody interface{}) error + Delete(ctx context.Context, name, path string, responseBody interface{}) error +} + +type Task interface { + WaitForResourceId(ctx context.Context, id string) (int, error) + WaitForResource(ctx context.Context, id string, resource interface{}) error + Wait(ctx context.Context, id string) error +} + +type API struct { + client HttpClient + task Task + logger Log +} + +func NewAPI(client HttpClient, task Task, logger Log) *API { + return &API{client: client, task: task, logger: logger} +} + +// List will list all of a given subscription's active-active regions. +func (a API) List(ctx context.Context, subId int) (*Regions, error) { + var response Regions + err := a.client.Get(ctx, "list regions", fmt.Sprintf("/subscriptions/%d/regions", subId), &response) + if err != nil { + return nil, err + } + + return &response, nil +} From b41c217b6cece184ad37f2f2a65cb70118669810 Mon Sep 17 00:00:00 2001 From: Peter Vegh Date: Thu, 15 Dec 2022 09:51:58 +0000 Subject: [PATCH 05/10] Regions API calls and structs --- internal/http_client.go | 4 +++ service/regions/model.go | 30 +++++++++++++++++++++ service/regions/service.go | 55 +++++++++++++++++++++++++++++++++++++- 3 files changed, 88 insertions(+), 1 deletion(-) diff --git a/internal/http_client.go b/internal/http_client.go index 6840e04..6d4407d 100644 --- a/internal/http_client.go +++ b/internal/http_client.go @@ -44,6 +44,10 @@ func (c *HttpClient) Delete(ctx context.Context, name, path string, responseBody return c.connection(ctx, http.MethodDelete, name, path, nil, nil, responseBody) } +func (c *HttpClient) DeleteWithQuery(ctx context.Context, name, path string, requestBody interface{}, responseBody interface{}) error { + return c.connection(ctx, http.MethodDelete, name, path, nil, requestBody, responseBody) +} + func (c *HttpClient) connection(ctx context.Context, method, name, path string, query url.Values, requestBody interface{}, responseBody interface{}) error { parsed := new(url.URL) *parsed = *c.baseUrl diff --git a/service/regions/model.go b/service/regions/model.go index 7e3806a..221d1eb 100644 --- a/service/regions/model.go +++ b/service/regions/model.go @@ -16,6 +16,7 @@ func (o Regions) String() string { type Region struct { RegionId *int `json:"regionId,omitempty"` Region *string `json:"region,omitempty"` + RecreateRegion *bool `json:"-"` DeploymentCIDR *string `json:"deploymentCIDR,omitempty"` VpcId *string `json:"vpcId,omitempty"` Databases []*Database `json:"databases,omitempty"` @@ -31,3 +32,32 @@ type Database struct { ReadOperationsPerSecond *int `json:"readOperationsPerSecond,omitempty"` WriteOperationsPerSecond *int `json:"writeOperationsPerSecond,omitempty"` } + +type CreateRegion struct { + Region *string `json:"region,omitempty"` + DeploymentCIDR *string `json:"deploymentCIDR,omitempty"` + DryRun *bool `json:"dryRun,omitempty"` + Databases []*CreateDatabase `json:"databases,omitempty"` +} + +type DeleteRegion struct { + Region *string `json:"region,omitempty"` +} +type DeleteRegions struct { + Regions []*DeleteRegion `json:"regions,omitempty"` +} + +type CreateLocalThroughput struct { + Region *string `json:"region,omitempty"` + WriteOperationsPerSecond *int `json:"writeOperationsPerSecond"` + ReadOperationsPerSecond *int `json:"readOperationsPerSecond"` +} + +type CreateDatabase struct { + Name *string `json:"name,omitempty"` + LocalThroughputMeasurement *CreateLocalThroughput `json:"localThroughputMeasurement,omitempty"` +} + +type taskResponse struct { + ID *string `json:"taskId,omitempty"` +} diff --git a/service/regions/service.go b/service/regions/service.go index d276f64..49ea36a 100644 --- a/service/regions/service.go +++ b/service/regions/service.go @@ -3,6 +3,9 @@ package regions import ( "context" "fmt" + "net/http" + + "github.com/RedisLabs/rediscloud-go-api/internal" ) type Log interface { @@ -13,7 +16,7 @@ type HttpClient interface { Get(ctx context.Context, name, path string, responseBody interface{}) error Post(ctx context.Context, name, path string, requestBody interface{}, responseBody interface{}) error Put(ctx context.Context, name, path string, requestBody interface{}, responseBody interface{}) error - Delete(ctx context.Context, name, path string, responseBody interface{}) error + DeleteWithQuery(ctx context.Context, name, path string, requestBody interface{}, responseBody interface{}) error } type Task interface { @@ -32,6 +35,24 @@ func NewAPI(client HttpClient, task Task, logger Log) *API { return &API{client: client, task: task, logger: logger} } +// Create will create a new subscription. +func (a *API) Create(ctx context.Context, subId int, region CreateRegion) (int, error) { + var task taskResponse + err := a.client.Post(ctx, "create subscription", fmt.Sprintf("/subscriptions/%d/regions", subId), region, &task) + if err != nil { + return 0, err + } + + a.logger.Printf("Waiting for task %s to finish creating the subscription", task) + + id, err := a.task.WaitForResourceId(ctx, *task.ID) + if err != nil { + return 0, err + } + + return id, nil +} + // List will list all of a given subscription's active-active regions. func (a API) List(ctx context.Context, subId int) (*Regions, error) { var response Regions @@ -42,3 +63,35 @@ func (a API) List(ctx context.Context, subId int) (*Regions, error) { return &response, nil } + +func (a *API) DeleteWithQuery(ctx context.Context, id int, regions DeleteRegions) error { + var task taskResponse + err := a.client.DeleteWithQuery(ctx, fmt.Sprintf("delete region %d", id), fmt.Sprintf("/subscriptions/%d/regions/", id), regions, &task) + if err != nil { + return err + } + + a.logger.Printf("Waiting for region %d to finish being deleted", id) + + err = a.task.Wait(ctx, *task.ID) + if err != nil { + return err + } + + return nil +} + +type NotFound struct { + id int +} + +func (f *NotFound) Error() string { + return fmt.Sprintf("subscription %d not found", f.id) +} + +func wrap404Error(id int, err error) error { + if v, ok := err.(*internal.HTTPError); ok && v.StatusCode == http.StatusNotFound { + return &NotFound{id: id} + } + return err +} From ef0152631e5c6ff416685b7f2f5af1c36d563e7b Mon Sep 17 00:00:00 2001 From: Greg Nuttall <102314841+greg-oc@users.noreply.github.com> Date: Thu, 15 Dec 2022 16:22:36 +0000 Subject: [PATCH 06/10] put active active methods and structs in their own files (#90) --- service/databases/model.go | 73 ---------- service/databases/model_active_active.go | 148 ++++++++++++++++++++ service/databases/service.go | 36 ----- service/databases/service_active_active.go | 153 +++++++++++++++++++++ 4 files changed, 301 insertions(+), 109 deletions(-) create mode 100644 service/databases/model_active_active.go create mode 100644 service/databases/service_active_active.go diff --git a/service/databases/model.go b/service/databases/model.go index 3a86a78..eb4989e 100644 --- a/service/databases/model.go +++ b/service/databases/model.go @@ -41,24 +41,6 @@ func (o CreateDatabase) String() string { return internal.ToString(o) } -type CreateActiveActiveDatabase struct { - DryRun *bool `json:"dryRun,omitempty"` - Name *string `json:"name,omitempty"` - Protocol *string `json:"protocol,omitempty"` - MemoryLimitInGB *float64 `json:"memoryLimitInGb,omitempty"` - SupportOSSClusterAPI *bool `json:"supportOSSClusterApi,omitempty"` - UseExternalEndpointForOSSClusterAPI *bool `json:"useExternalEndpointForOSSClusterApi,omitempty"` - GlobalDataPersistence *string `json:"dataPersistence,omitempty"` - GlobalSourceIP []*string `json:"sourceIp,omitempty"` - GlobalPassword *string `json:"password,omitempty"` - GlobalAlerts []*CreateAlert `json:"alerts,omitempty"` - LocalThroughputMeasurement []*LocalThroughput `json:"localThroughputMeasurement,omitempty"` -} - -func (o CreateActiveActiveDatabase) String() string { - return internal.ToString(o) -} - type CreateThroughputMeasurement struct { By *string `json:"by,omitempty"` Value *int `json:"value,omitempty"` @@ -201,25 +183,6 @@ func (o UpdateDatabase) String() string { return internal.ToString(o) } -type UpdateActiveActiveDatabase struct { - DryRun *bool `json:"dryRun,omitempty"` - MemoryLimitInGB *float64 `json:"memoryLimitInGb,omitempty"` - SupportOSSClusterAPI *bool `json:"supportOSSClusterApi,omitempty"` - UseExternalEndpointForOSSClusterAPI *bool `json:"useExternalEndpointForOSSClusterApi,omitempty"` - ClientSSLCertificate *string `json:"clientSslCertificate,omitempty"` - EnableTls *bool `json:"enableTls,omitempty"` - GlobalDataPersistence *string `json:"globalDataPersistence,omitempty"` - GlobalPassword *string `json:"globalPassword,omitempty"` - GlobalSourceIP []*string `json:"globalSourceIp,omitempty"` - GlobalAlerts []*UpdateAlert `json:"globalAlerts,omitempty"` - Regions []*LocalRegionProperties `json:"regions,omitempty"` - DataEvictionPolicy *string `json:"dataEvictionPolicy,omitempty"` -} - -func (o UpdateActiveActiveDatabase) String() string { - return internal.ToString(o) -} - type UpdateThroughputMeasurement struct { By *string `json:"by,omitempty"` Value *int `json:"value,omitempty"` @@ -238,42 +201,6 @@ func (o UpdateAlert) String() string { return internal.ToString(o) } -type LocalRegionProperties struct { - Region *string `json:"region,omitempty"` - RemoteBackup *DatabaseBackupConfig `json:"remoteBackup,omitempty"` - LocalThroughputMeasurement *LocalThroughput `json:"localThroughputMeasurement,omitempty"` - DataPersistence *string `json:"dataPersistence,omitempty"` - Password *string `json:"password,omitempty"` - SourceIP []*string `json:"sourceIp,omitempty"` - Alerts []*UpdateAlert `json:"alerts,omitempty"` -} - -func (o LocalRegionProperties) String() string { - return internal.ToString(o) -} - -type LocalThroughput struct { - Region *string `json:"region,omitempty"` - WriteOperationsPerSecond *int `json:"writeOperationsPerSecond,omitempty"` - ReadOperationsPerSecond *int `json:"readOperationsPerSecond,omitempty"` -} - -func (o LocalThroughput) String() string { - return internal.ToString(o) -} - -type DatabaseBackupConfig struct { - Active *bool `json:"active,omitempty"` - Interval *string `json:"interval,omitempty"` - TimeUTC *string `json:"timeUTC,omitempty"` - StorageType *string `json:"storageType,omitempty"` - StoragePath *string `json:"storagePath,omitempty"` -} - -func (o DatabaseBackupConfig) String() string { - return internal.ToString(o) -} - type Import struct { SourceType *string `json:"sourceType,omitempty"` ImportFromURI []*string `json:"importFromUri,omitempty"` diff --git a/service/databases/model_active_active.go b/service/databases/model_active_active.go new file mode 100644 index 0000000..8153d46 --- /dev/null +++ b/service/databases/model_active_active.go @@ -0,0 +1,148 @@ +package databases + +import ( + "time" + + "github.com/RedisLabs/rediscloud-go-api/internal" +) + +type ActiveActiveDatabase struct { + ID *int `json:"databaseId,omitempty"` + Name *string `json:"name,omitempty"` + Protocol *string `json:"protocol,omitempty"` + Status *string `json:"status,omitempty"` + MemoryStorage *string `json:"memoryStorage,omitempty"` + ActiveActiveRedis *bool `json:"activeActiveRedis,omitempty"` + ActivatedOn *time.Time `json:"activatedOn,omitempty"` + LastModified *time.Time `json:"lastModified,omitempty"` + SupportOSSClusterAPI *bool `json:"supportOSSClusterApi,omitempty"` + UseExternalEndpointForOSSClusterAPI *bool `json:"useExternalEndpointForOSSClusterApi,omitempty"` + Replication *bool `json:"replication,omitempty"` + DataEvictionPolicy *string `json:"dataEvictionPolicy,omitempty"` + CrdbDatabases []*CrdbDatabase `json:"crdbDatabases,omitempty"` +} + +func (o ActiveActiveDatabase) String() string { + return internal.ToString(o) +} + +type CrdbDatabase struct { + Provider *string `json:"provider,omitempty"` + Region *string `json:"region,omitempty"` + RedisVersionCompliance *string `json:"redisVersionCompliance,omitempty"` + PublicEndpoint *string `json:"publicEndpoint,omitempty"` + PrivateEndpoint *string `json:"privateEndpoint,omitempty"` + MemoryLimitInGB *float64 `json:"memoryLimitInGb,omitempty"` + MemoryUsedInMB *float64 `json:"memoryUsedInMb,omitempty"` + ReadOperationsPerSecond *int `json:"readOperationsPerSecond,omitempty"` + WriteOperationsPerSecond *int `json:"writeOperationsPerSecond,omitempty"` + DataPersistence *string `json:"dataPersistence,omitempty"` + Alerts []*Alert `json:"alerts,omitempty"` + Security *Security `json:"security,omitempty"` + Backup *Backup `json:"backup,omitempty"` +} + +func (o CrdbDatabase) String() string { + return internal.ToString(o) +} + +type Backup struct { + Enabled *bool `json:"enableRemoteBackup,omitempty"` + Interval *string `json:"interval,omitempty"` + Destination *string `json:"destination,omitempty"` +} + +func (o Backup) String() string { + return internal.ToString(o) +} + +type CreateActiveActiveDatabase struct { + DryRun *bool `json:"dryRun,omitempty"` + Name *string `json:"name,omitempty"` + Protocol *string `json:"protocol,omitempty"` + MemoryLimitInGB *float64 `json:"memoryLimitInGb,omitempty"` + SupportOSSClusterAPI *bool `json:"supportOSSClusterApi,omitempty"` + UseExternalEndpointForOSSClusterAPI *bool `json:"useExternalEndpointForOSSClusterApi,omitempty"` + GlobalDataPersistence *string `json:"dataPersistence,omitempty"` + GlobalSourceIP []*string `json:"sourceIp,omitempty"` + GlobalPassword *string `json:"password,omitempty"` + GlobalAlerts []*CreateAlert `json:"alerts,omitempty"` + LocalThroughputMeasurement []*LocalThroughput `json:"localThroughputMeasurement,omitempty"` +} + +func (o CreateActiveActiveDatabase) String() string { + return internal.ToString(o) +} + +type LocalThroughput struct { + Region *string `json:"region,omitempty"` + WriteOperationsPerSecond *int `json:"writeOperationsPerSecond,omitempty"` + ReadOperationsPerSecond *int `json:"readOperationsPerSecond,omitempty"` +} + +func (o LocalThroughput) String() string { + return internal.ToString(o) +} + +type UpdateActiveActiveDatabase struct { + DryRun *bool `json:"dryRun,omitempty"` + MemoryLimitInGB *float64 `json:"memoryLimitInGb,omitempty"` + SupportOSSClusterAPI *bool `json:"supportOSSClusterApi,omitempty"` + UseExternalEndpointForOSSClusterAPI *bool `json:"useExternalEndpointForOSSClusterApi,omitempty"` + ClientSSLCertificate *string `json:"clientSslCertificate,omitempty"` + EnableTls *bool `json:"enableTls,omitempty"` + GlobalDataPersistence *string `json:"globalDataPersistence,omitempty"` + GlobalPassword *string `json:"globalPassword,omitempty"` + GlobalSourceIP []*string `json:"globalSourceIp,omitempty"` + GlobalAlerts []*UpdateAlert `json:"globalAlerts,omitempty"` + Regions []*LocalRegionProperties `json:"regions,omitempty"` + DataEvictionPolicy *string `json:"dataEvictionPolicy,omitempty"` +} + +func (o UpdateActiveActiveDatabase) String() string { + return internal.ToString(o) +} + +type LocalRegionProperties struct { + Region *string `json:"region,omitempty"` + RemoteBackup *DatabaseBackupConfig `json:"remoteBackup,omitempty"` + LocalThroughputMeasurement *LocalThroughput `json:"localThroughputMeasurement,omitempty"` + DataPersistence *string `json:"dataPersistence,omitempty"` + Password *string `json:"password,omitempty"` + SourceIP []*string `json:"sourceIp,omitempty"` + Alerts []*UpdateAlert `json:"alerts,omitempty"` +} + +func (o LocalRegionProperties) String() string { + return internal.ToString(o) +} + +type DatabaseBackupConfig struct { + Active *bool `json:"active,omitempty"` + Interval *string `json:"interval,omitempty"` + TimeUTC *string `json:"timeUTC,omitempty"` + StorageType *string `json:"storageType,omitempty"` + StoragePath *string `json:"storagePath,omitempty"` +} + +func (o DatabaseBackupConfig) String() string { + return internal.ToString(o) +} + +type listActiveActiveDatabaseResponse struct { + AccountId *string `json:"accountId,omitempty"` + Subscription []*listActiveActiveDbSubscription `json:"subscription,omitempty"` +} + +func (o listActiveActiveDatabaseResponse) String() string { + return internal.ToString(o) +} + +type listActiveActiveDbSubscription struct { + ID *int `json:"subscriptionId,omitempty"` + Databases []*ActiveActiveDatabase `json:"databases,omitempty"` +} + +func (o listActiveActiveDbSubscription) String() string { + return internal.ToString(o) +} diff --git a/service/databases/service.go b/service/databases/service.go index a90938e..7de7fb2 100644 --- a/service/databases/service.go +++ b/service/databases/service.go @@ -56,24 +56,6 @@ func (a *API) Create(ctx context.Context, subscription int, db CreateDatabase) ( return id, nil } -// Create will create a new database for the subscription and return the identifier of the database. -func (a *API) ActiveActiveCreate(ctx context.Context, subscription int, db CreateActiveActiveDatabase) (int, error) { - var task taskResponse - err := a.client.Post(ctx, fmt.Sprintf("create database for subscription %d", subscription), fmt.Sprintf("/subscriptions/%d/databases", subscription), db, &task) - if err != nil { - return 0, err - } - - a.logger.Printf("Waiting for new database for subscription %d to finish being created", subscription) - - id, err := a.task.WaitForResourceId(ctx, *task.ID) - if err != nil { - return 0, err - } - - return id, nil -} - // List will return a ListDatabase that is capable of paging through all of the databases associated with a // subscription. func (a *API) List(ctx context.Context, subscription int) *ListDatabase { @@ -109,24 +91,6 @@ func (a *API) Update(ctx context.Context, subscription int, database int, update return nil } -// Update will update certain values of an existing database. -func (a *API) ActiveActiveUpdate(ctx context.Context, subscription int, database int, update UpdateActiveActiveDatabase) error { - var task taskResponse - err := a.client.Put(ctx, fmt.Sprintf("update database %d for subscription %d", database, subscription), fmt.Sprintf("/subscriptions/%d/databases/%d/regions", subscription, database), update, &task) - if err != nil { - return err - } - - a.logger.Printf("Waiting for database %d for subscription %d to finish being updated", database, subscription) - - err = a.task.Wait(ctx, *task.ID) - if err != nil { - return err - } - - return nil -} - // Delete will destroy an existing database. func (a *API) Delete(ctx context.Context, subscription int, database int) error { var task taskResponse diff --git a/service/databases/service_active_active.go b/service/databases/service_active_active.go new file mode 100644 index 0000000..fd564cb --- /dev/null +++ b/service/databases/service_active_active.go @@ -0,0 +1,153 @@ +package databases + +import ( + "context" + "fmt" + "net/http" + "strconv" + + "github.com/RedisLabs/rediscloud-go-api/internal" + "github.com/RedisLabs/rediscloud-go-api/redis" +) + +// Create will create a new database for the subscription and return the identifier of the database. +func (a *API) ActiveActiveCreate(ctx context.Context, subscription int, db CreateActiveActiveDatabase) (int, error) { + var task taskResponse + err := a.client.Post(ctx, fmt.Sprintf("create database for subscription %d", subscription), fmt.Sprintf("/subscriptions/%d/databases", subscription), db, &task) + if err != nil { + return 0, err + } + + a.logger.Printf("Waiting for new database for subscription %d to finish being created", subscription) + + id, err := a.task.WaitForResourceId(ctx, *task.ID) + if err != nil { + return 0, err + } + + return id, nil +} + +// Update will update certain values of an existing database. +func (a *API) ActiveActiveUpdate(ctx context.Context, subscription int, database int, update UpdateActiveActiveDatabase) error { + var task taskResponse + err := a.client.Put(ctx, fmt.Sprintf("update database %d for subscription %d", database, subscription), fmt.Sprintf("/subscriptions/%d/databases/%d/regions", subscription, database), update, &task) + if err != nil { + return err + } + + a.logger.Printf("Waiting for database %d for subscription %d to finish being updated", database, subscription) + + err = a.task.Wait(ctx, *task.ID) + if err != nil { + return err + } + + return nil +} + +// List will return a ListDatabase that is capable of paging through all of the databases associated with a +// subscription. +func (a *API) ListActiveActive(ctx context.Context, subscription int) *ListActiveActiveDatabase { + return newListActiveActiveDatabase(ctx, a.client, subscription, 100) +} + +// Get will retrieve an existing database. +func (a *API) GetActiveActive(ctx context.Context, subscription int, database int) (*ActiveActiveDatabase, error) { + var db ActiveActiveDatabase + err := a.client.Get(ctx, fmt.Sprintf("get database %d for subscription %d", subscription, database), fmt.Sprintf("/subscriptions/%d/databases/%d", subscription, database), &db) + if err != nil { + return nil, wrap404Error(subscription, database, err) + } + + return &db, nil +} + +type ListActiveActiveDatabase struct { + client HttpClient + subscription int + ctx context.Context + pageSize int + + offset int + page []*ActiveActiveDatabase + err error + fin bool + value *ActiveActiveDatabase +} + +func newListActiveActiveDatabase(ctx context.Context, client HttpClient, subscription int, pageSize int) *ListActiveActiveDatabase { + return &ListActiveActiveDatabase{client: client, subscription: subscription, ctx: ctx, pageSize: pageSize} +} + +// Next attempts to retrieve the next page of databases and will return false if no more databases were found. +// Any error that occurs within this function can be retrieved from the `Err()` function. +func (d *ListActiveActiveDatabase) Next() bool { + if d.err != nil { + return false + } + + if d.fin { + return false + } + + if len(d.page) == 0 { + if err := d.nextPage(); err != nil { + d.setError(err) + return false + } + } + + d.updateValue() + + return true +} + +// Value returns the current page of databases. +func (d *ListActiveActiveDatabase) Value() *ActiveActiveDatabase { + return d.value +} + +// Err returns any error that occurred while trying to retrieve the next page of databases. +func (d *ListActiveActiveDatabase) Err() error { + return d.err +} + +func (d *ListActiveActiveDatabase) nextPage() error { + u := fmt.Sprintf("/subscriptions/%d/databases", d.subscription) + q := map[string][]string{ + "limit": {strconv.Itoa(d.pageSize)}, + "offset": {strconv.Itoa(d.offset)}, + } + + var list listActiveActiveDatabaseResponse + err := d.client.GetWithQuery(d.ctx, fmt.Sprintf("list databases for %d", d.subscription), u, q, &list) + if err != nil { + return err + } + + if len(list.Subscription) != 1 || redis.IntValue(list.Subscription[0].ID) != d.subscription { + return fmt.Errorf("server didn't respond with just a single subscription") + } + + d.page = list.Subscription[0].Databases + d.offset += d.pageSize + + return nil +} + +func (d *ListActiveActiveDatabase) updateValue() { + d.value = d.page[0] + d.page = d.page[1:] +} + +func (d *ListActiveActiveDatabase) setError(err error) { + if httpErr, ok := err.(*internal.HTTPError); ok && httpErr.StatusCode == http.StatusNotFound { + d.fin = true + } else { + d.err = err + } + + d.page = nil + d.value = nil +} From 548cfb21a403805c546095886b8b8f0adccf53d0 Mon Sep 17 00:00:00 2001 From: Greg Nuttall <102314841+greg-oc@users.noreply.github.com> Date: Wed, 4 Jan 2023 09:19:01 +0000 Subject: [PATCH 07/10] Active Active Database (#91) * put active active methods and structs in their own files * add data eviction policy to aa create * redact global_password field --- client.go | 4 +++- client_test.go | 11 ++++++----- service/databases/model_active_active.go | 1 + 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/client.go b/client.go index dcb334a..16020f6 100644 --- a/client.go +++ b/client.go @@ -211,7 +211,9 @@ func prettyPrint(data []byte) string { // redactPasswords: Redacts password values from a JSON message. func redactPasswords(data string) string { m1 := regexp.MustCompile(`\"password\"\s*:\s*\"(?:[^"\\]|\\.)*\"`) - return m1.ReplaceAllString(data, "\"password\": \"REDACTED\"") + output := m1.ReplaceAllString(data, "\"password\": \"REDACTED\"") + m2 := regexp.MustCompile(`\"global_password\"\s*:\s*\"(?:[^"\\]|\\.)*\"`) + return m2.ReplaceAllString(output, "\"global_password\": \"REDACTED\"") } func escapePath(path string) string { diff --git a/client_test.go b/client_test.go index 49a30d6..901f0b6 100644 --- a/client_test.go +++ b/client_test.go @@ -252,8 +252,8 @@ func TestCredentialTripper_RedactPasswordFromNestedBody(t *testing.T) { ProtoMajor: 1, ProtoMinor: 1, Header: map[string][]string{}, - Body: ioutil.NopCloser(bytes.NewBufferString(`{"security": {"password":"pass"}}`)), - ContentLength: 33, + Body: ioutil.NopCloser(bytes.NewBufferString(`{"security": {"password":"pass", "global_password":"globalpass"}}`)), + ContentLength: 65, Host: "example.org", } expected := &http.Response{ @@ -262,7 +262,7 @@ func TestCredentialTripper_RedactPasswordFromNestedBody(t *testing.T) { ProtoMajor: 1, ProtoMinor: 1, Header: map[string][]string{}, - Body: ioutil.NopCloser(bytes.NewBufferString(`{"security": {"password":"REDACTED"}}`)), + Body: ioutil.NopCloser(bytes.NewBufferString(`{"security": {"password":"REDACTED", "global_password":"REDACTED"}}`)), } mockTripper.On("RoundTrip", request).Return(expected, nil) @@ -285,13 +285,14 @@ func TestCredentialTripper_RedactPasswordFromNestedBody(t *testing.T) { POST /foo/bar HTTP/1.1 Host: example.org User-Agent: test-user-agent -Content-Length: 33 +Content-Length: 65 Accept: application/json Accept-Encoding: gzip { "security": { - "password": "REDACTED" + "password": "REDACTED", + "global_password": "REDACTED" } }`, mockLogger.log[0]) } diff --git a/service/databases/model_active_active.go b/service/databases/model_active_active.go index 8153d46..e8a0493 100644 --- a/service/databases/model_active_active.go +++ b/service/databases/model_active_active.go @@ -63,6 +63,7 @@ type CreateActiveActiveDatabase struct { MemoryLimitInGB *float64 `json:"memoryLimitInGb,omitempty"` SupportOSSClusterAPI *bool `json:"supportOSSClusterApi,omitempty"` UseExternalEndpointForOSSClusterAPI *bool `json:"useExternalEndpointForOSSClusterApi,omitempty"` + DataEvictionPolicy *string `json:"dataEvictionPolicy,omitempty"` GlobalDataPersistence *string `json:"dataPersistence,omitempty"` GlobalSourceIP []*string `json:"sourceIp,omitempty"` GlobalPassword *string `json:"password,omitempty"` From 03fc67fc13dda3d87f56cf8a5f248a0747095218 Mon Sep 17 00:00:00 2001 From: byron-oc <119585385+byron-oc@users.noreply.github.com> Date: Wed, 11 Jan 2023 14:08:21 +0000 Subject: [PATCH 08/10] feat: add support for active-active VPC peerings (#93) * add create/list/delete active-active regions aws * wip * fix: read `vpcCidr` from API response for active-active peering * feat: add `deploymentType` field to subscriptions Co-authored-by: Ben Gesoff --- service/subscriptions/model.go | 53 ++++++++++++++++++++++++++++++++ service/subscriptions/service.go | 46 ++++++++++++++++++++++++++- 2 files changed, 98 insertions(+), 1 deletion(-) diff --git a/service/subscriptions/model.go b/service/subscriptions/model.go index a8e1fee..9b30135 100644 --- a/service/subscriptions/model.go +++ b/service/subscriptions/model.go @@ -109,6 +109,7 @@ type Subscription struct { ID *int `json:"id,omitempty"` Name *string `json:"name,omitempty"` Status *string `json:"status,omitempty"` + DeploymentType *string `json:"deploymentType,omitempty"` PaymentMethod *string `json:"paymentMethodType,omitempty"` PaymentMethodID *int `json:"paymentMethodId,omitempty"` MemoryStorage *string `json:"memoryStorage,omitempty"` @@ -186,6 +187,21 @@ func (o CreateVPCPeering) String() string { return internal.ToString(o) } +type CreateActiveActiveVPCPeering struct { + SourceRegion *string `json:"sourceRegion,omitempty"` + DestinationRegion *string `json:"destinationRegion,omitempty"` + AWSAccountID *string `json:"awsAccountId,omitempty"` + VPCId *string `json:"vpcId,omitempty"` + VPCCidrs []*string `json:"vpcCidrs,omitempty"` + Provider *string `json:"provider,omitempty"` + VPCProjectUID *string `json:"vpcProjectUid,omitempty"` + VPCNetworkName *string `json:"vpcNetworkName,omitempty"` +} + +func (o CreateActiveActiveVPCPeering) String() string { + return internal.ToString(o) +} + type listVpcPeering struct { Peerings []*VPCPeering `json:"peerings"` } @@ -209,6 +225,40 @@ func (o VPCPeering) String() string { return internal.ToString(o) } +type listActiveActiveVpcPeering struct { + SubscriptionId *int `json:"subscriptionId,omitempty"` + Regions []*ActiveActiveVpcRegion `json:"regions,omitempty"` +} + +type ActiveActiveVpcRegion struct { + ID *int `json:"id,omitempty"` + SourceRegion *string `json:"region,omitempty"` + VPCPeerings []*ActiveActiveVPCPeering `json:"vpcPeerings,omitempty"` +} + +type ActiveActiveVPCPeering struct { + ID *int `json:"id,omitempty"` + Status *string `json:"status,omitempty"` + RegionId *int `json:"regionId,omitempty"` + RegionName *string `json:"regionName,omitempty"` + AWSAccountID *string `json:"awsAccountId,omitempty"` + AWSPeeringID *string `json:"awsPeeringUid,omitempty"` + VPCId *string `json:"vpcUid,omitempty"` + VPCCidrs []*string `json:"vpcCidrs,omitempty"` + VPCCidr *string `json:"vpcCidr,omitempty"` + GCPProjectUID *string `json:"vpcProjectUid,omitempty"` + NetworkName *string `json:"vpcNetworkName,omitempty"` + RedisProjectUID *string `json:"redisProjectUid,omitempty"` + RedisNetworkName *string `json:"redisNetworkName,omitempty"` + CloudPeeringID *string `json:"cloudPeeringId,omitempty"` + SourceRegion *string `json:"sourceRegion,omitempty"` + DestinationRegion *string `json:"destinationRegion,omitempty"` +} + +func (o ActiveActiveVPCPeering) String() string { + return internal.ToString(o) +} + type listSubscriptionResponse struct { Subscriptions []*Subscription `json:"subscriptions"` } @@ -249,4 +299,7 @@ const ( VPCPeeringStatusPendingAcceptance = "pending-acceptance" // Failed value of the `Status` field in `VPCPeering` VPCPeeringStatusFailed = "failed" + + SubscriptionDeploymentTypeSingleRegion = "single-region" + SubscriptionDeploymentTypeActiveActive = "active-active" ) diff --git a/service/subscriptions/service.go b/service/subscriptions/service.go index c8c8fc8..7af3dfa 100644 --- a/service/subscriptions/service.go +++ b/service/subscriptions/service.go @@ -170,6 +170,33 @@ func (a *API) ListVPCPeering(ctx context.Context, id int) ([]*VPCPeering, error) return peering.Peerings, nil } +func (a *API) ListActiveActiveVPCPeering(ctx context.Context, id int) ([]*ActiveActiveVpcRegion, error) { + var task taskResponse + err := a.client.Get(ctx, fmt.Sprintf("get peerings for subscription %d", id), fmt.Sprintf("/subscriptions/%d/regions/peerings/", id), &task) + if err != nil { + return nil, wrap404Error(id, err) + } + + a.logger.Printf("Waiting for subscription %d peering details to be retrieved", id) + + var peering listActiveActiveVpcPeering + err = a.task.WaitForResource(ctx, *task.ID, &peering) + if err != nil { + return nil, err + } + + // add vpcCidr to vpcCidrs slice if it exists + for i, region := range peering.Regions { + for j, vpcPeering := range region.VPCPeerings { + if vpcPeering.VPCCidr != nil { + peering.Regions[i].VPCPeerings[j].VPCCidrs = append(peering.Regions[i].VPCPeerings[j].VPCCidrs, vpcPeering.VPCCidr) + } + } + } + + return peering.Regions, nil +} + // CreateVPCPeering creates a new VPC peering from the subscription VPC and returns the identifier of the VPC peering. func (a *API) CreateVPCPeering(ctx context.Context, id int, create CreateVPCPeering) (int, error) { var task taskResponse @@ -188,7 +215,7 @@ func (a *API) CreateVPCPeering(ctx context.Context, id int, create CreateVPCPeer return id, nil } -func (a *API) CreateActiveActiveVPCPeering(ctx context.Context, id int, create CreateVPCPeering) (int, error) { +func (a *API) CreateActiveActiveVPCPeering(ctx context.Context, id int, create CreateActiveActiveVPCPeering) (int, error) { var task taskResponse err := a.client.Post(ctx, fmt.Sprintf("create peering for subscription %d", id), fmt.Sprintf("/subscriptions/%d/regions/peerings/", id), create, &task) if err != nil { @@ -223,6 +250,23 @@ func (a *API) DeleteVPCPeering(ctx context.Context, subscription int, peering in return nil } +func (a *API) DeleteActiveActiveVPCPeering(ctx context.Context, subscription int, peering int) error { + var task taskResponse + err := a.client.Delete(ctx, fmt.Sprintf("deleting peering %d for subscription %d", peering, subscription), fmt.Sprintf("/subscriptions/%d/regions/peerings/%d", subscription, peering), &task) + if err != nil { + return err + } + + a.logger.Printf("Waiting for peering %d for subscription %d to be deleted", peering, subscription) + + err = a.task.Wait(ctx, *task.ID) + if err != nil { + return err + } + + return nil +} + func wrap404Error(id int, err error) error { if v, ok := err.(*internal.HTTPError); ok && v.StatusCode == http.StatusNotFound { return &NotFound{id: id} From 8c3bea8492d181c772a378892d178a0e65f3ae7f Mon Sep 17 00:00:00 2001 From: Ben Gesoff Date: Thu, 2 Feb 2023 09:06:19 +0000 Subject: [PATCH 09/10] fix: wrap regions errors when subscription not found (#95) --- service/regions/service.go | 23 ++++++++--------------- service/subscriptions/model.go | 4 ++-- service/subscriptions/service.go | 2 +- 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/service/regions/service.go b/service/regions/service.go index 49ea36a..7dc89d4 100644 --- a/service/regions/service.go +++ b/service/regions/service.go @@ -6,6 +6,7 @@ import ( "net/http" "github.com/RedisLabs/rediscloud-go-api/internal" + "github.com/RedisLabs/rediscloud-go-api/service/subscriptions" ) type Log interface { @@ -35,15 +36,15 @@ func NewAPI(client HttpClient, task Task, logger Log) *API { return &API{client: client, task: task, logger: logger} } -// Create will create a new subscription. +// Create will create a new region func (a *API) Create(ctx context.Context, subId int, region CreateRegion) (int, error) { var task taskResponse - err := a.client.Post(ctx, "create subscription", fmt.Sprintf("/subscriptions/%d/regions", subId), region, &task) + err := a.client.Post(ctx, "create subscription region", fmt.Sprintf("/subscriptions/%d/regions", subId), region, &task) if err != nil { - return 0, err + return 0, wrap404Error(subId, err) } - a.logger.Printf("Waiting for task %s to finish creating the subscription", task) + a.logger.Printf("Waiting for task %s to finish creating the subscription region", task) id, err := a.task.WaitForResourceId(ctx, *task.ID) if err != nil { @@ -58,7 +59,7 @@ func (a API) List(ctx context.Context, subId int) (*Regions, error) { var response Regions err := a.client.Get(ctx, "list regions", fmt.Sprintf("/subscriptions/%d/regions", subId), &response) if err != nil { - return nil, err + return nil, wrap404Error(subId, err) } return &response, nil @@ -68,7 +69,7 @@ func (a *API) DeleteWithQuery(ctx context.Context, id int, regions DeleteRegions var task taskResponse err := a.client.DeleteWithQuery(ctx, fmt.Sprintf("delete region %d", id), fmt.Sprintf("/subscriptions/%d/regions/", id), regions, &task) if err != nil { - return err + return wrap404Error(id, err) } a.logger.Printf("Waiting for region %d to finish being deleted", id) @@ -81,17 +82,9 @@ func (a *API) DeleteWithQuery(ctx context.Context, id int, regions DeleteRegions return nil } -type NotFound struct { - id int -} - -func (f *NotFound) Error() string { - return fmt.Sprintf("subscription %d not found", f.id) -} - func wrap404Error(id int, err error) error { if v, ok := err.(*internal.HTTPError); ok && v.StatusCode == http.StatusNotFound { - return &NotFound{id: id} + return &subscriptions.NotFound{ID: id} } return err } diff --git a/service/subscriptions/model.go b/service/subscriptions/model.go index 9b30135..9b7a168 100644 --- a/service/subscriptions/model.go +++ b/service/subscriptions/model.go @@ -272,11 +272,11 @@ func (o taskResponse) String() string { } type NotFound struct { - id int + ID int } func (f *NotFound) Error() string { - return fmt.Sprintf("subscription %d not found", f.id) + return fmt.Sprintf("subscription %d not found", f.ID) } const ( diff --git a/service/subscriptions/service.go b/service/subscriptions/service.go index 7af3dfa..cc95bf7 100644 --- a/service/subscriptions/service.go +++ b/service/subscriptions/service.go @@ -269,7 +269,7 @@ func (a *API) DeleteActiveActiveVPCPeering(ctx context.Context, subscription int func wrap404Error(id int, err error) error { if v, ok := err.(*internal.HTTPError); ok && v.StatusCode == http.StatusNotFound { - return &NotFound{id: id} + return &NotFound{ID: id} } return err } From 763d4bf41d7325213f4d0563103bf10f13e18a19 Mon Sep 17 00:00:00 2001 From: Ben Gesoff Date: Thu, 16 Feb 2023 15:44:48 +0000 Subject: [PATCH 10/10] refactor: remove vpcCidrs attribute from AA peerings --- service/subscriptions/model.go | 47 ++++++++++++++++---------------- service/subscriptions/service.go | 9 ------ 2 files changed, 23 insertions(+), 33 deletions(-) diff --git a/service/subscriptions/model.go b/service/subscriptions/model.go index 9b7a168..77a0308 100644 --- a/service/subscriptions/model.go +++ b/service/subscriptions/model.go @@ -188,14 +188,14 @@ func (o CreateVPCPeering) String() string { } type CreateActiveActiveVPCPeering struct { - SourceRegion *string `json:"sourceRegion,omitempty"` - DestinationRegion *string `json:"destinationRegion,omitempty"` - AWSAccountID *string `json:"awsAccountId,omitempty"` - VPCId *string `json:"vpcId,omitempty"` - VPCCidrs []*string `json:"vpcCidrs,omitempty"` - Provider *string `json:"provider,omitempty"` - VPCProjectUID *string `json:"vpcProjectUid,omitempty"` - VPCNetworkName *string `json:"vpcNetworkName,omitempty"` + SourceRegion *string `json:"sourceRegion,omitempty"` + DestinationRegion *string `json:"destinationRegion,omitempty"` + AWSAccountID *string `json:"awsAccountId,omitempty"` + VPCId *string `json:"vpcId,omitempty"` + VPCCidr *string `json:"vpcCidr,omitempty"` + Provider *string `json:"provider,omitempty"` + VPCProjectUID *string `json:"vpcProjectUid,omitempty"` + VPCNetworkName *string `json:"vpcNetworkName,omitempty"` } func (o CreateActiveActiveVPCPeering) String() string { @@ -237,22 +237,21 @@ type ActiveActiveVpcRegion struct { } type ActiveActiveVPCPeering struct { - ID *int `json:"id,omitempty"` - Status *string `json:"status,omitempty"` - RegionId *int `json:"regionId,omitempty"` - RegionName *string `json:"regionName,omitempty"` - AWSAccountID *string `json:"awsAccountId,omitempty"` - AWSPeeringID *string `json:"awsPeeringUid,omitempty"` - VPCId *string `json:"vpcUid,omitempty"` - VPCCidrs []*string `json:"vpcCidrs,omitempty"` - VPCCidr *string `json:"vpcCidr,omitempty"` - GCPProjectUID *string `json:"vpcProjectUid,omitempty"` - NetworkName *string `json:"vpcNetworkName,omitempty"` - RedisProjectUID *string `json:"redisProjectUid,omitempty"` - RedisNetworkName *string `json:"redisNetworkName,omitempty"` - CloudPeeringID *string `json:"cloudPeeringId,omitempty"` - SourceRegion *string `json:"sourceRegion,omitempty"` - DestinationRegion *string `json:"destinationRegion,omitempty"` + ID *int `json:"id,omitempty"` + Status *string `json:"status,omitempty"` + RegionId *int `json:"regionId,omitempty"` + RegionName *string `json:"regionName,omitempty"` + AWSAccountID *string `json:"awsAccountId,omitempty"` + AWSPeeringID *string `json:"awsPeeringUid,omitempty"` + VPCId *string `json:"vpcUid,omitempty"` + VPCCidr *string `json:"vpcCidr,omitempty"` + GCPProjectUID *string `json:"vpcProjectUid,omitempty"` + NetworkName *string `json:"vpcNetworkName,omitempty"` + RedisProjectUID *string `json:"redisProjectUid,omitempty"` + RedisNetworkName *string `json:"redisNetworkName,omitempty"` + CloudPeeringID *string `json:"cloudPeeringId,omitempty"` + SourceRegion *string `json:"sourceRegion,omitempty"` + DestinationRegion *string `json:"destinationRegion,omitempty"` } func (o ActiveActiveVPCPeering) String() string { diff --git a/service/subscriptions/service.go b/service/subscriptions/service.go index cc95bf7..2809f27 100644 --- a/service/subscriptions/service.go +++ b/service/subscriptions/service.go @@ -185,15 +185,6 @@ func (a *API) ListActiveActiveVPCPeering(ctx context.Context, id int) ([]*Active return nil, err } - // add vpcCidr to vpcCidrs slice if it exists - for i, region := range peering.Regions { - for j, vpcPeering := range region.VPCPeerings { - if vpcPeering.VPCCidr != nil { - peering.Regions[i].VPCPeerings[j].VPCCidrs = append(peering.Regions[i].VPCPeerings[j].VPCCidrs, vpcPeering.VPCCidr) - } - } - } - return peering.Regions, nil }