From a062a0bfbe02019c02d8eb71425395aaed15dfd2 Mon Sep 17 00:00:00 2001 From: andres-portainer <91705312+andres-portainer@users.noreply.github.com> Date: Thu, 4 May 2023 13:24:04 -0300 Subject: [PATCH] feat(resourcecontrol): add support for transactions EE-5431 (#8901) --- .../resourcecontrol/resourcecontrol.go | 13 +- api/dataservices/resourcecontrol/tx.go | 113 ++++++++++++++++++ api/datastore/services_tx.go | 4 +- 3 files changed, 125 insertions(+), 5 deletions(-) create mode 100644 api/dataservices/resourcecontrol/tx.go diff --git a/api/dataservices/resourcecontrol/resourcecontrol.go b/api/dataservices/resourcecontrol/resourcecontrol.go index 77deba188..208f9456e 100644 --- a/api/dataservices/resourcecontrol/resourcecontrol.go +++ b/api/dataservices/resourcecontrol/resourcecontrol.go @@ -8,10 +8,8 @@ import ( "github.com/rs/zerolog/log" ) -const ( - // BucketName represents the name of the bucket where this service stores data. - BucketName = "resource_control" -) +// BucketName represents the name of the bucket where this service stores data. +const BucketName = "resource_control" // Service represents a service for managing environment(endpoint) data. type Service struct { @@ -34,6 +32,13 @@ func NewService(connection portainer.Connection) (*Service, error) { }, nil } +func (service *Service) Tx(tx portainer.Transaction) ServiceTx { + return ServiceTx{ + service: service, + tx: tx, + } +} + // ResourceControl returns a ResourceControl object by ID func (service *Service) ResourceControl(ID portainer.ResourceControlID) (*portainer.ResourceControl, error) { var resourceControl portainer.ResourceControl diff --git a/api/dataservices/resourcecontrol/tx.go b/api/dataservices/resourcecontrol/tx.go new file mode 100644 index 000000000..485fabcda --- /dev/null +++ b/api/dataservices/resourcecontrol/tx.go @@ -0,0 +1,113 @@ +package resourcecontrol + +import ( + "fmt" + + portainer "github.com/portainer/portainer/api" + + "github.com/rs/zerolog/log" +) + +type ServiceTx struct { + service *Service + tx portainer.Transaction +} + +func (service ServiceTx) BucketName() string { + return BucketName +} + +// ResourceControl returns a ResourceControl object by ID +func (service ServiceTx) ResourceControl(ID portainer.ResourceControlID) (*portainer.ResourceControl, error) { + var resourceControl portainer.ResourceControl + identifier := service.service.connection.ConvertToKey(int(ID)) + + err := service.tx.GetObject(BucketName, identifier, &resourceControl) + if err != nil { + return nil, err + } + + return &resourceControl, nil +} + +// ResourceControlByResourceIDAndType returns a ResourceControl object by checking if the resourceID is equal +// to the main ResourceID or in SubResourceIDs. It also performs a check on the resource type. Return nil +// if no ResourceControl was found. +func (service ServiceTx) ResourceControlByResourceIDAndType(resourceID string, resourceType portainer.ResourceControlType) (*portainer.ResourceControl, error) { + var resourceControl *portainer.ResourceControl + stop := fmt.Errorf("ok") + err := service.tx.GetAll( + BucketName, + &portainer.ResourceControl{}, + func(obj interface{}) (interface{}, error) { + rc, ok := obj.(*portainer.ResourceControl) + if !ok { + log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to ResourceControl object") + return nil, fmt.Errorf("Failed to convert to ResourceControl object: %s", obj) + } + + if rc.ResourceID == resourceID && rc.Type == resourceType { + resourceControl = rc + return nil, stop + } + + for _, subResourceID := range rc.SubResourceIDs { + if subResourceID == resourceID { + resourceControl = rc + return nil, stop + } + } + + return &portainer.ResourceControl{}, nil + }) + if err == stop { + return resourceControl, nil + } + + return nil, err +} + +// ResourceControls returns all the ResourceControl objects +func (service ServiceTx) ResourceControls() ([]portainer.ResourceControl, error) { + var rcs = make([]portainer.ResourceControl, 0) + + err := service.tx.GetAll( + BucketName, + &portainer.ResourceControl{}, + func(obj interface{}) (interface{}, error) { + rc, ok := obj.(*portainer.ResourceControl) + if !ok { + log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to ResourceControl object") + return nil, fmt.Errorf("Failed to convert to ResourceControl object: %s", obj) + } + + rcs = append(rcs, *rc) + + return &portainer.ResourceControl{}, nil + }) + + return rcs, err +} + +// CreateResourceControl creates a new ResourceControl object +func (service ServiceTx) Create(resourceControl *portainer.ResourceControl) error { + return service.tx.CreateObject( + BucketName, + func(id uint64) (int, interface{}) { + resourceControl.ID = portainer.ResourceControlID(id) + return int(resourceControl.ID), resourceControl + }, + ) +} + +// UpdateResourceControl saves a ResourceControl object. +func (service ServiceTx) UpdateResourceControl(ID portainer.ResourceControlID, resourceControl *portainer.ResourceControl) error { + identifier := service.service.connection.ConvertToKey(int(ID)) + return service.tx.UpdateObject(BucketName, identifier, resourceControl) +} + +// DeleteResourceControl deletes a ResourceControl object by ID +func (service ServiceTx) DeleteResourceControl(ID portainer.ResourceControlID) error { + identifier := service.service.connection.ConvertToKey(int(ID)) + return service.tx.DeleteObject(BucketName, identifier) +} diff --git a/api/datastore/services_tx.go b/api/datastore/services_tx.go index b7bb5b96a..e58f5953e 100644 --- a/api/datastore/services_tx.go +++ b/api/datastore/services_tx.go @@ -47,7 +47,9 @@ func (tx *StoreTx) Registry() dataservices.RegistryService { return nil } -func (tx *StoreTx) ResourceControl() dataservices.ResourceControlService { return nil } +func (tx *StoreTx) ResourceControl() dataservices.ResourceControlService { + return tx.store.ResourceControlService.Tx(tx.tx) +} func (tx *StoreTx) Role() dataservices.RoleService { return tx.store.RoleService.Tx(tx.tx)