feat(storage): Add support for Storage Bucket API

This commit is contained in:
Louis Ruch authored and Timothy Messier committed 2023-05-31 12:18:14 +00:00
1 parent feb87e717e
commit 8ea61054cd
27 files changed
+3380 -142

No files matched your search

+15 -13
View File
@@ -15,22 +15,24 @@ import (
"github.com/hashicorp/boundary/internal/plugin"
"github.com/hashicorp/boundary/internal/server"
"github.com/hashicorp/boundary/internal/session"
pluginstorage "github.com/hashicorp/boundary/internal/storage/plugin"
)
type (
AuthTokenRepoFactory = oidc.AuthTokenRepoFactory
VaultCredentialRepoFactory = func() (*vault.Repository, error)
StaticCredentialRepoFactory = func() (*credstatic.Repository, error)
IamRepoFactory = iam.IamRepoFactory
OidcAuthRepoFactory = oidc.OidcRepoFactory
LdapAuthRepoFactory = ldap.RepoFactory
PasswordAuthRepoFactory func() (*password.Repository, error)
ServersRepoFactory func() (*server.Repository, error)
StaticRepoFactory func() (*static.Repository, error)
PluginHostRepoFactory func() (*pluginhost.Repository, error)
HostPluginRepoFactory func() (*plugin.Repository, error)
ConnectionRepoFactory func() (*session.ConnectionRepository, error)
WorkerAuthRepoStorageFactory func() (*server.WorkerAuthRepositoryStorage, error)
AuthTokenRepoFactory = oidc.AuthTokenRepoFactory
VaultCredentialRepoFactory = func() (*vault.Repository, error)
StaticCredentialRepoFactory = func() (*credstatic.Repository, error)
IamRepoFactory = iam.IamRepoFactory
OidcAuthRepoFactory = oidc.OidcRepoFactory
LdapAuthRepoFactory = ldap.RepoFactory
PasswordAuthRepoFactory func() (*password.Repository, error)
ServersRepoFactory func() (*server.Repository, error)
StaticRepoFactory func() (*static.Repository, error)
PluginHostRepoFactory func() (*pluginhost.Repository, error)
PluginRepoFactory func() (*plugin.Repository, error)
ConnectionRepoFactory func() (*session.ConnectionRepository, error)
WorkerAuthRepoStorageFactory func() (*server.WorkerAuthRepositoryStorage, error)
PluginStorageBucketRepoFactory func() (*pluginstorage.Repository, error)
)
// Downstreamers provides at least a minimum interface that must be met by a
+22 -18
View File
@@ -41,6 +41,7 @@ import (
"github.com/hashicorp/boundary/internal/server"
serversjob "github.com/hashicorp/boundary/internal/server/job"
"github.com/hashicorp/boundary/internal/session"
pluginstorage "github.com/hashicorp/boundary/internal/storage/plugin"
"github.com/hashicorp/boundary/internal/target"
"github.com/hashicorp/boundary/internal/types/scope"
host_plugin_assets "github.com/hashicorp/boundary/plugins/host"
@@ -118,21 +119,22 @@ type Controller struct {
apiGrpcGatewayTicket string
// Repo factory methods
AuthTokenRepoFn common.AuthTokenRepoFactory
VaultCredentialRepoFn common.VaultCredentialRepoFactory
StaticCredentialRepoFn common.StaticCredentialRepoFactory
IamRepoFn common.IamRepoFactory
OidcRepoFn common.OidcAuthRepoFactory
LdapRepoFn common.LdapAuthRepoFactory
PasswordAuthRepoFn common.PasswordAuthRepoFactory
ServersRepoFn common.ServersRepoFactory
SessionRepoFn session.RepositoryFactory
ConnectionRepoFn common.ConnectionRepoFactory
StaticHostRepoFn common.StaticRepoFactory
PluginHostRepoFn common.PluginHostRepoFactory
HostPluginRepoFn common.HostPluginRepoFactory
TargetRepoFn target.RepositoryFactory
WorkerAuthRepoStorageFn common.WorkerAuthRepoStorageFactory
AuthTokenRepoFn common.AuthTokenRepoFactory
VaultCredentialRepoFn common.VaultCredentialRepoFactory
StaticCredentialRepoFn common.StaticCredentialRepoFactory
IamRepoFn common.IamRepoFactory
OidcRepoFn common.OidcAuthRepoFactory
LdapRepoFn common.LdapAuthRepoFactory
PasswordAuthRepoFn common.PasswordAuthRepoFactory
ServersRepoFn common.ServersRepoFactory
SessionRepoFn session.RepositoryFactory
ConnectionRepoFn common.ConnectionRepoFactory
StaticHostRepoFn common.StaticRepoFactory
PluginHostRepoFn common.PluginHostRepoFactory
PluginStorageBucketRepoFn common.PluginStorageBucketRepoFactory
PluginRepoFn common.PluginRepoFactory
TargetRepoFn target.RepositoryFactory
WorkerAuthRepoStorageFn common.WorkerAuthRepoStorageFactory
scheduler *scheduler.Scheduler
@@ -261,8 +263,7 @@ func New(ctx context.Context, conf *Config) (*Controller, error) {
plugin.WithDescription("Provides an initial loopback storage and host plugin in Boundary"),
plugin.WithPublicId(conf.DevLoopbackPluginId),
}
// TODO(storage): Register loopback as storage supported
if _, err = conf.RegisterPlugin(ctx, "loopback", plg, []plugin.PluginType{plugin.PluginTypeHost}, opts...); err != nil {
if _, err = conf.RegisterPlugin(ctx, "loopback", plg, []plugin.PluginType{plugin.PluginTypeHost, plugin.PluginTypeStorage}, opts...); err != nil {
return nil, err
}
case base.EnabledPluginHostAzure:
@@ -376,9 +377,12 @@ func New(ctx context.Context, conf *Config) (*Controller, error) {
c.PluginHostRepoFn = func() (*pluginhost.Repository, error) {
return pluginhost.NewRepository(dbase, dbase, c.kms, c.scheduler, c.conf.HostPlugins)
}
c.HostPluginRepoFn = func() (*plugin.Repository, error) {
c.PluginRepoFn = func() (*plugin.Repository, error) {
return plugin.NewRepository(ctx, dbase, dbase, c.kms)
}
c.PluginStorageBucketRepoFn = func() (*pluginstorage.Repository, error) {
return pluginstorage.NewRepository(ctx, dbase, dbase, c.kms)
}
c.AuthTokenRepoFn = func() (*authtoken.Repository, error) {
return authtoken.NewRepository(dbase, dbase, c.kms,
authtoken.WithTokenTimeToLiveDuration(c.conf.RawConfig.Controller.AuthTokenTimeToLiveDuration),
+17 -1
View File
@@ -36,6 +36,7 @@ import (
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/roles"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/scopes"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/sessions"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/storage_buckets"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/targets"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/users"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/workers"
@@ -133,7 +134,7 @@ func (c *Controller) registerGrpcServices(s *grpc.Server) error {
currentServices := s.GetServiceInfo()
if _, ok := currentServices[services.HostCatalogService_ServiceDesc.ServiceName]; !ok {
hcs, err := host_catalogs.NewService(c.StaticHostRepoFn, c.PluginHostRepoFn, c.HostPluginRepoFn, c.IamRepoFn)
hcs, err := host_catalogs.NewService(c.StaticHostRepoFn, c.PluginHostRepoFn, c.PluginRepoFn, c.IamRepoFn)
if err != nil {
return fmt.Errorf("failed to create host catalog handler service: %w", err)
}
@@ -188,6 +189,18 @@ func (c *Controller) registerGrpcServices(s *grpc.Server) error {
}
services.RegisterUserServiceServer(s, us)
}
if _, ok := currentServices[services.StorageBucketService_ServiceDesc.ServiceName]; !ok {
sbs, err := storage_buckets.NewServiceFn(
c.baseContext,
c.PluginStorageBucketRepoFn,
c.IamRepoFn,
c.PluginRepoFn,
c.ControllerExtension)
if err != nil {
return fmt.Errorf("failed to create storage bucket handler service: %w", err)
}
services.RegisterStorageBucketServiceServer(s, sbs)
}
if _, ok := currentServices[services.TargetService_ServiceDesc.ServiceName]; !ok {
ts, err := targets.NewService(
c.baseContext,
@@ -332,6 +345,9 @@ func registerGrpcGatewayEndpoints(ctx context.Context, gwMux *runtime.ServeMux,
if err := services.RegisterSessionRecordingServiceHandlerFromEndpoint(ctx, gwMux, gatewayTarget, dialOptions); err != nil {
return fmt.Errorf("failed to register session recording service handler: %w", err)
}
if err := services.RegisterStorageBucketServiceHandlerFromEndpoint(ctx, gwMux, gatewayTarget, dialOptions); err != nil {
return fmt.Errorf("failed to register storage bucket handler: %w", err)
}
return nil
}
@@ -132,6 +132,8 @@ func TestHandleImplementedPaths(t *testing.T) {
"v1/scopes/someid",
"v1/sessions",
"v1/sessions/someid",
"v1/storage-buckets",
"v1/storage-buckets/someid",
"v1/targets",
"v1/targets/someid",
"v1/users",
@@ -148,6 +150,7 @@ func TestHandleImplementedPaths(t *testing.T) {
"v1/hosts",
"v1/roles",
"v1/scopes",
"v1/storage-buckets",
"v1/targets",
"v1/users",
@@ -190,6 +193,7 @@ func TestHandleImplementedPaths(t *testing.T) {
"v1/hosts/someid",
"v1/roles/someid",
"v1/scopes/someid",
"v1/storage-buckets/someid",
"v1/targets/someid",
"v1/users/someid",
},
@@ -203,6 +207,7 @@ func TestHandleImplementedPaths(t *testing.T) {
"v1/hosts/someid",
"v1/roles/someid",
"v1/scopes/someid",
"v1/storage-buckets/someid",
"v1/targets/someid",
"v1/users/someid",
},
@@ -88,7 +88,7 @@ type Service struct {
staticRepoFn common.StaticRepoFactory
pluginHostRepoFn common.PluginHostRepoFactory
pluginRepoFn common.HostPluginRepoFactory
pluginRepoFn common.PluginRepoFactory
iamRepoFn common.IamRepoFactory
}
@@ -96,7 +96,7 @@ var _ pbs.HostCatalogServiceServer = (*Service)(nil)
// NewService returns a host catalog Service which handles host catalog related requests to boundary and uses the provided
// repositories for storage and retrieval.
func NewService(repoFn common.StaticRepoFactory, pluginHostRepoFn common.PluginHostRepoFactory, hostPluginRepoFn common.HostPluginRepoFactory, iamRepoFn common.IamRepoFactory) (Service, error) {
func NewService(repoFn common.StaticRepoFactory, pluginHostRepoFn common.PluginHostRepoFactory, hostPluginRepoFn common.PluginRepoFactory, iamRepoFn common.IamRepoFactory) (Service, error) {
const op = "host_catalogs.NewService"
if repoFn == nil {
return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing static repository")
@@ -21,6 +21,7 @@ import (
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/host_catalogs"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/roles"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/sessions"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/storage_buckets"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/targets"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/users"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/workers"
@@ -67,22 +68,24 @@ var (
scopeCollectionTypeMapMap = map[string]map[resource.Type]action.ActionSet{
scope.Global.String(): {
resource.AuthMethod: authmethods.CollectionActions,
resource.AuthToken: authtokens.CollectionActions,
resource.Group: groups.CollectionActions,
resource.Role: roles.CollectionActions,
resource.Scope: CollectionActions,
resource.User: users.CollectionActions,
resource.Worker: workers.CollectionActions,
resource.AuthMethod: authmethods.CollectionActions,
resource.StorageBucket: storage_buckets.CollectionActions,
resource.AuthToken: authtokens.CollectionActions,
resource.Group: groups.CollectionActions,
resource.Role: roles.CollectionActions,
resource.Scope: CollectionActions,
resource.User: users.CollectionActions,
resource.Worker: workers.CollectionActions,
},
scope.Org.String(): {
resource.AuthMethod: authmethods.CollectionActions,
resource.AuthToken: authtokens.CollectionActions,
resource.Group: groups.CollectionActions,
resource.Role: roles.CollectionActions,
resource.Scope: CollectionActions,
resource.User: users.CollectionActions,
resource.AuthMethod: authmethods.CollectionActions,
resource.StorageBucket: storage_buckets.CollectionActions,
resource.AuthToken: authtokens.CollectionActions,
resource.Group: groups.CollectionActions,
resource.Role: roles.CollectionActions,
resource.Scope: CollectionActions,
resource.User: users.CollectionActions,
},
scope.Project.String(): {
@@ -0,0 +1,74 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package storage_buckets
import (
"context"
"github.com/hashicorp/boundary/internal/daemon/controller/common"
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
"github.com/hashicorp/boundary/internal/globals"
"github.com/hashicorp/boundary/internal/types/action"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
var (
// IdActions contains the set of actions that can be performed on
// individual resources
IdActions = action.ActionSet{
action.NoOp,
action.Read,
action.Update,
action.Delete,
}
// CollectionActions contains the set of actions that can be performed on
// this collection
CollectionActions = action.ActionSet{
action.Create,
action.List,
}
)
// NewServiceFn returns a storage bucket service which is not implemented in OSS
var NewServiceFn = func(ctx context.Context,
pluginStorageRepoFn common.PluginStorageBucketRepoFactory,
iamRepoFn common.IamRepoFactory,
pluginRepoFn common.PluginRepoFactory,
controllerExt globals.ControllerExtension,
) (pbs.StorageBucketServiceServer, error) {
return Service{}, nil
}
type Service struct {
pbs.UnsafeStorageBucketServiceServer
}
var _ pbs.StorageBucketServiceServer = (*Service)(nil)
// ListStorageBuckets implements the interface pbs.StorageBucketServiceServer.
func (s Service) ListStorageBuckets(ctx context.Context, req *pbs.ListStorageBucketsRequest) (*pbs.ListStorageBucketsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "storage buckets are an Enterprise-only feature")
}
// GetStorageBucket implements the interface pbs.StorageBucketServiceServer.
func (s Service) GetStorageBucket(ctx context.Context, req *pbs.GetStorageBucketRequest) (*pbs.GetStorageBucketResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "storage buckets are an Enterprise-only feature")
}
// CreateStorageBucket implements the interface pbs.StorageBucketServiceServer.
func (s Service) CreateStorageBucket(ctx context.Context, req *pbs.CreateStorageBucketRequest) (*pbs.CreateStorageBucketResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "storage buckets are an Enterprise-only feature")
}
// UpdateStorageBucket implements the interface pbs.StorageBucketServiceServer.
func (s Service) UpdateStorageBucket(ctx context.Context, req *pbs.UpdateStorageBucketRequest) (*pbs.UpdateStorageBucketResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "storage buckets are an Enterprise-only feature")
}
// DeleteStorageBucket implements the interface pbs.StorageBucketServiceServer.
func (s Service) DeleteStorageBucket(ctx context.Context, req *pbs.DeleteStorageBucketRequest) (*pbs.DeleteStorageBucketResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "storage buckets are an Enterprise-only feature")
}
@@ -0,0 +1,62 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package storage_buckets
import (
"context"
"testing"
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
func TestService(t *testing.T) {
t.Parallel()
ctx := context.Background()
assert, require := assert.New(t), require.New(t)
s := Service{}
gotCreate, err := s.CreateStorageBucket(ctx, &pbs.CreateStorageBucketRequest{})
require.Nil(gotCreate)
require.Error(err)
gotStatus, ok := status.FromError(err)
require.True(ok)
assert.Equal(gotStatus.Code(), codes.Unimplemented)
assert.Equal(gotStatus.Message(), "storage buckets are an Enterprise-only feature")
gotUpdate, err := s.UpdateStorageBucket(ctx, &pbs.UpdateStorageBucketRequest{})
require.Nil(gotUpdate)
require.Error(err)
gotStatus, ok = status.FromError(err)
require.True(ok)
assert.Equal(gotStatus.Code(), codes.Unimplemented)
assert.Equal(gotStatus.Message(), "storage buckets are an Enterprise-only feature")
gotGet, err := s.GetStorageBucket(ctx, &pbs.GetStorageBucketRequest{})
require.Nil(gotGet)
require.Error(err)
gotStatus, ok = status.FromError(err)
require.True(ok)
assert.Equal(gotStatus.Code(), codes.Unimplemented)
assert.Equal(gotStatus.Message(), "storage buckets are an Enterprise-only feature")
gotDelete, err := s.DeleteStorageBucket(ctx, &pbs.DeleteStorageBucketRequest{})
require.Nil(gotDelete)
require.Error(err)
gotStatus, ok = status.FromError(err)
require.True(ok)
assert.Equal(gotStatus.Code(), codes.Unimplemented)
assert.Equal(gotStatus.Message(), "storage buckets are an Enterprise-only feature")
got, err := s.ListStorageBuckets(ctx, &pbs.ListStorageBucketsRequest{})
require.Nil(got)
require.Error(err)
gotStatus, ok = status.FromError(err)
require.True(ok)
assert.Equal(gotStatus.Code(), codes.Unimplemented)
assert.Equal(gotStatus.Message(), "storage buckets are an Enterprise-only feature")
}