From 45de26bfe96dc3c97589e467b33400c45315893a Mon Sep 17 00:00:00 2001 From: Louis Ruch Date: Mon, 8 Jan 2024 22:14:24 -0800 Subject: [PATCH] feat(scope): Add API for Attach/Detach StoragePolicy to scope --- api/scopes/custom.go | 138 ++++++++++++++++++ .../handlers/scopes/scope_service.go | 2 + .../handlers/scopes/scope_service_test.go | 2 +- 3 files changed, 141 insertions(+), 1 deletion(-) diff --git a/api/scopes/custom.go b/api/scopes/custom.go index f09384d3e..606f40154 100644 --- a/api/scopes/custom.go +++ b/api/scopes/custom.go @@ -5,6 +5,7 @@ package scopes import ( "context" + "errors" "fmt" "net/url" @@ -215,3 +216,140 @@ func (c *Client) DestroyKeyVersion(ctx context.Context, scopeId string, keyVersi target.response = resp return target, nil } + +type ( + AttachStoragePolicyResult = ScopeReadResult + DetachStoragePolicyResult = ScopeReadResult +) + +// AttachStoragePolicy attaches the provided storagePolicyId to the provided scopeId. +func (c *Client) AttachStoragePolicy(ctx context.Context, scopeId string, version uint32, storagePolicyId string, opt ...Option) (*AttachStoragePolicyResult, error) { + if scopeId == "" { + return nil, errors.New("empty scopeId value passed into AttachStoragePolicy request") + } + if storagePolicyId == "" { + return nil, errors.New("empty storagePolicyId value passed into AttachStoragePolicy request") + } + if c.client == nil { + return nil, errors.New("nil client") + } + + opts, apiOpts := getOpts(opt...) + + if version == 0 { + if !opts.withAutomaticVersioning { + return nil, errors.New("zero version number passed into AttachStoragePolicy request") + } + existingScope, existingErr := c.Read(ctx, scopeId, append([]Option{WithSkipCurlOutput(true)}, opt...)...) + if existingErr != nil { + if api.AsServerError(existingErr) != nil { + return nil, fmt.Errorf("error from controller when performing initial check-and-set read: %w", existingErr) + } + return nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr) + } + if existingScope == nil { + return nil, errors.New("nil resource response found when performing initial check-and-set read") + } + if existingScope.Item == nil { + return nil, errors.New("nil resource found when performing initial check-and-set read") + } + version = existingScope.Item.Version + } + + opts.postMap["version"] = version + opts.postMap["storage_policy_id"] = storagePolicyId + + req, err := c.client.NewRequest(ctx, "POST", fmt.Sprintf("scopes/%s:attach-storage-policy", url.PathEscape(scopeId)), opts.postMap, apiOpts...) + if err != nil { + return nil, fmt.Errorf("error creating AttachStoragePolicy request: %w", err) + } + + if len(opts.queryMap) > 0 { + q := url.Values{} + for k, v := range opts.queryMap { + q.Add(k, v) + } + req.URL.RawQuery = q.Encode() + } + + resp, err := c.client.Do(req) + if err != nil { + return nil, fmt.Errorf("error performing client request during AttachStoragePolicy call: %w", err) + } + + s := new(AttachStoragePolicyResult) + s.Item = new(Scope) + apiErr, err := resp.Decode(s.Item) + if err != nil { + return nil, fmt.Errorf("error decoding AttachStoragePolicyResult response: %w", err) + } + if apiErr != nil { + return nil, apiErr + } + s.response = resp + return s, nil +} + +// DetachStoragePolicy detaches storage policy from the provided scopeId if one is attached. +func (c *Client) DetachStoragePolicy(ctx context.Context, scopeId string, version uint32, opt ...Option) (*AttachStoragePolicyResult, error) { + if scopeId == "" { + return nil, errors.New("empty scopeId value passed into DetachStoragePolicy request") + } + if c.client == nil { + return nil, errors.New("nil client") + } + + opts, apiOpts := getOpts(opt...) + + if version == 0 { + if !opts.withAutomaticVersioning { + return nil, errors.New("zero version number passed into DetachStoragePolicy request") + } + existingScope, existingErr := c.Read(ctx, scopeId, append([]Option{WithSkipCurlOutput(true)}, opt...)...) + if existingErr != nil { + if api.AsServerError(existingErr) != nil { + return nil, fmt.Errorf("error from controller when performing initial check-and-set read: %w", existingErr) + } + return nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr) + } + if existingScope == nil { + return nil, errors.New("nil resource response found when performing initial check-and-set read") + } + if existingScope.Item == nil { + return nil, errors.New("nil resource found when performing initial check-and-set read") + } + version = existingScope.Item.Version + } + + opts.postMap["version"] = version + + req, err := c.client.NewRequest(ctx, "POST", fmt.Sprintf("scopes/%s:detach-storage-policy", url.PathEscape(scopeId)), opts.postMap, apiOpts...) + if err != nil { + return nil, fmt.Errorf("error creating DetachStoragePolicy request: %w", err) + } + + if len(opts.queryMap) > 0 { + q := url.Values{} + for k, v := range opts.queryMap { + q.Add(k, v) + } + req.URL.RawQuery = q.Encode() + } + + resp, err := c.client.Do(req) + if err != nil { + return nil, fmt.Errorf("error performing client request during DetachStoragePolicy call: %w", err) + } + + s := new(DetachStoragePolicyResult) + s.Item = new(Scope) + apiErr, err := resp.Decode(s.Item) + if err != nil { + return nil, fmt.Errorf("error decoding DetachStoragePolicyResult response: %w", err) + } + if apiErr != nil { + return nil, apiErr + } + s.response = resp + return s, nil +} diff --git a/internal/daemon/controller/handlers/scopes/scope_service.go b/internal/daemon/controller/handlers/scopes/scope_service.go index 65233680b..010d6b7e0 100644 --- a/internal/daemon/controller/handlers/scopes/scope_service.go +++ b/internal/daemon/controller/handlers/scopes/scope_service.go @@ -58,6 +58,8 @@ var ( action.Read, action.Update, action.Delete, + action.AttachStoragePolicy, + action.DetachStoragePolicy, ) // CollectionActions contains the set of actions that can be performed on diff --git a/internal/daemon/controller/handlers/scopes/scope_service_test.go b/internal/daemon/controller/handlers/scopes/scope_service_test.go index f66ba70e6..1024adaa7 100644 --- a/internal/daemon/controller/handlers/scopes/scope_service_test.go +++ b/internal/daemon/controller/handlers/scopes/scope_service_test.go @@ -46,7 +46,7 @@ import ( "google.golang.org/protobuf/types/known/wrapperspb" ) -var testAuthorizedActions = []string{"no-op", "read", "update", "delete"} +var testAuthorizedActions = []string{"no-op", "read", "update", "delete", "attach-storage-policy", "detach-storage-policy"} func createDefaultScopesRepoAndKms(t *testing.T) (*iam.Scope, *iam.Scope, func() (*iam.Repository, error), *kms.Kms) { t.Helper()