2021-09-13 15:02:04 -07:00
// Copyright (c) HashiCorp, Inc.
2021-11-11 11:28:08 -05:00
// SPDX-License-Identifier: BUSL-1.1
2023-02-02 17:15:17 -08:00
2020-08-21 10:57:30 -07:00
package host_sets
import (
"context"
"fmt"
2021-05-04 19:57:35 -05:00
"github.com/hashicorp/boundary/globals"
2022-05-09 12:45:10 -07:00
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
"github.com/hashicorp/boundary/internal/daemon/controller/common"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
2020-11-05 16:54:03 -05:00
"github.com/hashicorp/boundary/internal/errors"
2020-08-21 10:57:30 -07:00
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
"github.com/hashicorp/boundary/internal/host"
2023-04-24 19:27:41 -04:00
hostplugin "github.com/hashicorp/boundary/internal/host/plugin"
2021-09-28 16:19:32 -04:00
plugstore "github.com/hashicorp/boundary/internal/host/plugin/store"
2020-08-21 10:57:30 -07:00
"github.com/hashicorp/boundary/internal/host/static"
2021-11-11 11:28:08 -05:00
staticstore "github.com/hashicorp/boundary/internal/host/static/store"
2021-09-20 12:48:38 -04:00
"github.com/hashicorp/boundary/internal/libs/endpoint"
2023-12-13 19:47:34 -08:00
"github.com/hashicorp/boundary/internal/listtoken"
"github.com/hashicorp/boundary/internal/pagination"
2021-01-20 10:53:01 -05:00
"github.com/hashicorp/boundary/internal/perms"
2023-04-24 19:27:41 -04:00
"github.com/hashicorp/boundary/internal/plugin"
2021-05-04 19:57:35 -05:00
"github.com/hashicorp/boundary/internal/requests"
2020-09-01 12:31:54 -04:00
"github.com/hashicorp/boundary/internal/types/action"
2020-09-04 14:48:11 -07:00
"github.com/hashicorp/boundary/internal/types/resource"
2021-11-03 12:39:41 -04:00
"github.com/hashicorp/boundary/internal/types/subtypes"
2021-08-26 10:44:41 -04:00
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostsets"
2021-09-24 13:16:13 -07:00
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/plugins"
2021-07-14 17:37:40 -07:00
"github.com/hashicorp/go-secure-stdlib/strutil"
2023-10-11 18:26:16 +00:00
"golang.org/x/exp/maps"
2020-08-21 10:57:30 -07:00
"google.golang.org/grpc/codes"
2021-09-21 09:18:49 -07:00
"google.golang.org/protobuf/proto"
2021-09-13 15:02:04 -07:00
"google.golang.org/protobuf/types/known/structpb"
2020-08-21 10:57:30 -07:00
"google.golang.org/protobuf/types/known/wrapperspb"
)
2021-01-20 10:53:01 -05:00
var (
2023-11-15 19:55:46 -05:00
maskManager = map [ globals . Subtype ] handlers . MaskManager {}
2021-01-20 10:53:01 -05:00
// IdActions contains the set of actions that can be performed on
// individual resources
2023-11-15 19:55:46 -05:00
idActionsTypeMap = map [ globals . Subtype ] action . ActionSet {
2023-10-31 13:44:17 +00:00
static . Subtype : action . NewActionSet (
2021-11-03 12:39:41 -04:00
action . NoOp ,
action . Read ,
action . Update ,
action . Delete ,
action . AddHosts ,
action . SetHosts ,
action . RemoveHosts ,
2023-10-31 13:44:17 +00:00
),
hostplugin . Subtype : action . NewActionSet (
2021-11-03 12:39:41 -04:00
action . NoOp ,
action . Read ,
action . Update ,
action . Delete ,
2023-10-31 13:44:17 +00:00
),
2021-01-20 10:53:01 -05:00
}
// CollectionActions contains the set of actions that can be performed on
// this collection
2023-10-31 13:44:17 +00:00
CollectionActions = action . NewActionSet (
2021-01-20 10:53:01 -05:00
action . Create ,
action . List ,
2023-10-31 13:44:17 +00:00
)
2021-01-20 10:53:01 -05:00
)
2020-08-21 10:57:30 -07:00
func init () {
var err error
2023-11-02 16:03:47 -04:00
if maskManager [ static . Subtype ], err = handlers . NewMaskManager (
2023-06-06 17:09:08 -04:00
context . Background (),
handlers . MaskDestination { & staticstore . HostSet {}, & staticstore . UnimplementedSetFields {}},
handlers . MaskSource { & pb . HostSet {}},
); err != nil {
2021-11-11 11:28:08 -05:00
panic ( err )
}
2023-11-02 16:03:47 -04:00
if maskManager [ hostplugin . Subtype ], err = handlers . NewMaskManager (
2023-06-06 17:09:08 -04:00
context . Background (),
handlers . MaskDestination { & plugstore . HostSet {}},
handlers . MaskSource { & pb . HostSet {}},
); err != nil {
2020-08-21 10:57:30 -07:00
panic ( err )
}
2023-10-11 18:26:16 +00:00
// TODO: refactor to remove idActionsMap and CollectionActions package variables
action . RegisterResource ( resource . HostSet , action . Union ( maps . Values ( idActionsTypeMap ) ... ), CollectionActions )
2020-08-21 10:57:30 -07:00
}
type Service struct {
2022-08-30 14:22:10 -04:00
pbs . UnsafeHostSetServiceServer
2020-11-11 14:41:05 -05:00
2020-08-21 10:57:30 -07:00
staticRepoFn common . StaticRepoFactory
2021-09-13 15:02:04 -07:00
pluginRepoFn common . PluginHostRepoFactory
2023-12-13 19:47:34 -08:00
maxPageSize uint
2020-08-21 10:57:30 -07:00
}
2022-08-30 14:22:10 -04:00
var _ pbs . HostSetServiceServer = ( * Service )( nil )
2020-08-21 10:57:30 -07:00
2020-10-08 12:36:01 -07:00
// NewService returns a host set Service which handles host set related requests to boundary and uses the provided
2020-08-21 10:57:30 -07:00
// repositories for storage and retrieval.
2023-12-13 19:47:34 -08:00
func NewService ( ctx context . Context , staticRepoFn common . StaticRepoFactory , pluginRepoFn common . PluginHostRepoFactory , maxPageSize uint ) ( Service , error ) {
2021-04-16 12:06:03 -05:00
const op = "host_sets.NewService"
2021-09-13 15:02:04 -07:00
if staticRepoFn == nil {
2023-06-06 17:09:08 -04:00
return Service {}, errors . New ( ctx , errors . InvalidParameter , op , "missing static repository" )
2020-08-21 10:57:30 -07:00
}
2021-09-13 15:02:04 -07:00
if pluginRepoFn == nil {
2023-06-06 17:09:08 -04:00
return Service {}, errors . New ( ctx , errors . InvalidParameter , op , "missing hostplugin repository" )
2021-09-13 15:02:04 -07:00
}
2023-12-13 19:47:34 -08:00
if maxPageSize == 0 {
maxPageSize = uint ( globals . DefaultMaxPageSize )
}
return Service { staticRepoFn : staticRepoFn , pluginRepoFn : pluginRepoFn , maxPageSize : maxPageSize }, nil
2020-08-21 10:57:30 -07:00
}
2020-09-01 12:31:54 -04:00
func ( s Service ) ListHostSets ( ctx context . Context , req * pbs . ListHostSetsRequest ) ( * pbs . ListHostSetsResponse , error ) {
2021-09-20 12:48:38 -04:00
return s . ListHostSetsWithOptions ( ctx , req )
}
func ( s Service ) ListHostSetsWithOptions ( ctx context . Context , req * pbs . ListHostSetsRequest , opt ... host . Option ) ( * pbs . ListHostSetsResponse , error ) {
2023-12-13 19:47:34 -08:00
const op = "host_sets.(Service).ListHostSets"
2023-06-06 17:09:08 -04:00
if err := validateListRequest ( ctx , req ); err != nil {
2023-12-13 19:47:34 -08:00
return nil , errors . Wrap ( ctx , err , op )
2020-08-21 10:57:30 -07:00
}
2020-09-04 14:48:11 -07:00
_ , authResults := s . parentAndAuthResult ( ctx , req . GetHostCatalogId (), action . List )
2020-09-01 12:31:54 -04:00
if authResults . Error != nil {
return nil , authResults . Error
}
2023-12-13 19:47:34 -08:00
pageSize := int ( s . maxPageSize )
// Use the requested page size only if it is smaller than
// the configured max.
if req . GetPageSize () != 0 && uint ( req . GetPageSize ()) < s . maxPageSize {
pageSize = int ( req . GetPageSize ())
}
var filterItemFn func ( ctx context . Context , item host . Set , plg * plugin . Plugin ) ( bool , error )
switch {
case req . GetFilter () != "" :
// Only use a filter if we need to
filter , err := handlers . NewFilter ( ctx , req . GetFilter ())
if err != nil {
return nil , err
}
// TODO: replace the need for this function with some way to convert the `filter`
// to a domain type. This would allow filtering to happen in the domain, and we could
// remove this callback altogether.
filterItemFn = func ( ctx context . Context , item host . Set , plg * plugin . Plugin ) ( bool , error ) {
outputOpts , ok := newOutputOpts ( ctx , item , toPluginInfo ( plg ), authResults )
if ! ok {
return false , nil
}
pbItem , err := toProto ( ctx , item , nil , outputOpts ... )
if err != nil {
return false , err
}
filterable , err := subtypes . Filterable ( ctx , pbItem )
if err != nil {
return false , err
}
return filter . Match ( filterable ), nil
}
default :
filterItemFn = func ( ctx context . Context , item host . Set , plg * plugin . Plugin ) ( bool , error ) {
return true , nil
}
2021-05-04 19:57:35 -05:00
}
2023-12-13 19:47:34 -08:00
grantsHash , err := authResults . GrantsHash ( ctx )
2021-02-22 10:09:25 -07:00
if err != nil {
return nil , err
}
2021-05-04 19:57:35 -05:00
2023-12-13 19:47:34 -08:00
var listResp * pagination . ListResponse [ host . Set ]
var sortBy string
var hsplg * plugin . Plugin
switch globals . ResourceInfoFromPrefix ( req . GetHostCatalogId ()). Subtype {
case static . Subtype :
// Wrap the filter item func that takes a plugin, since the static host set
// domain does not use a plugin.
staticFilterItemFn := func ( ctx context . Context , item host . Set ) ( bool , error ) {
return filterItemFn ( ctx , item , nil )
2021-05-04 19:57:35 -05:00
}
2023-12-13 19:47:34 -08:00
repo , err := s . staticRepoFn ()
if err != nil {
return nil , errors . Wrap ( ctx , err , op )
}
if req . GetListToken () == "" {
sortBy = "created_time"
listResp , err = static . ListHostSets ( ctx , grantsHash , pageSize , staticFilterItemFn , repo , req . GetHostCatalogId ())
if err != nil {
return nil , err
}
} else {
listToken , err := handlers . ParseListToken ( ctx , req . GetListToken (), resource . HostSet , grantsHash )
if err != nil {
return nil , err
}
switch st := listToken . Subtype .( type ) {
case * listtoken . PaginationToken :
sortBy = "created_time"
listResp , err = static . ListHostSetsPage ( ctx , grantsHash , pageSize , staticFilterItemFn , listToken , repo , req . GetHostCatalogId ())
if err != nil {
return nil , err
}
case * listtoken . StartRefreshToken :
sortBy = "updated_time"
listResp , err = static . ListHostSetsRefresh ( ctx , grantsHash , pageSize , staticFilterItemFn , listToken , repo , req . GetHostCatalogId ())
if err != nil {
return nil , err
}
case * listtoken . RefreshToken :
sortBy = "updated_time"
listResp , err = static . ListHostSetsRefreshPage ( ctx , grantsHash , pageSize , staticFilterItemFn , listToken , repo , req . GetHostCatalogId ())
if err != nil {
return nil , err
}
default :
return nil , handlers . ApiErrorWithCodeAndMessage ( codes . InvalidArgument , "unexpected list token subtype: %T" , st )
}
2021-05-04 19:57:35 -05:00
}
2023-12-13 19:47:34 -08:00
case hostplugin . Subtype :
repo , err := s . pluginRepoFn ()
if err != nil {
return nil , errors . Wrap ( ctx , err , op )
}
if req . GetListToken () == "" {
sortBy = "created_time"
listResp , hsplg , err = hostplugin . ListHostSets ( ctx , grantsHash , pageSize , filterItemFn , repo , req . GetHostCatalogId ())
if err != nil {
return nil , err
}
} else {
listToken , err := handlers . ParseListToken ( ctx , req . GetListToken (), resource . HostSet , grantsHash )
if err != nil {
return nil , err
}
switch st := listToken . Subtype .( type ) {
case * listtoken . PaginationToken :
sortBy = "created_time"
listResp , hsplg , err = hostplugin . ListHostSetsPage ( ctx , grantsHash , pageSize , filterItemFn , listToken , repo , req . GetHostCatalogId ())
if err != nil {
return nil , err
}
case * listtoken . StartRefreshToken :
sortBy = "updated_time"
listResp , hsplg , err = hostplugin . ListHostSetsRefresh ( ctx , grantsHash , pageSize , filterItemFn , listToken , repo , req . GetHostCatalogId ())
if err != nil {
return nil , err
}
case * listtoken . RefreshToken :
sortBy = "updated_time"
listResp , hsplg , err = hostplugin . ListHostSetsRefreshPage ( ctx , grantsHash , pageSize , filterItemFn , listToken , repo , req . GetHostCatalogId ())
if err != nil {
return nil , err
}
default :
return nil , handlers . ApiErrorWithCodeAndMessage ( codes . InvalidArgument , "unexpected list token subtype: %T" , st )
}
}
}
plg := toPluginInfo ( hsplg )
finalItems := make ([] * pb . HostSet , 0 , len ( listResp . Items ))
for _ , item := range listResp . Items {
outputOpts , ok := newOutputOpts ( ctx , item , plg , authResults )
if ! ok {
continue
2021-09-24 13:16:13 -07:00
}
2023-12-13 19:47:34 -08:00
pbItem , err := toProto ( ctx , item , nil , outputOpts ... )
2021-05-04 19:57:35 -05:00
if err != nil {
2023-12-13 19:47:34 -08:00
continue
2021-05-04 19:57:35 -05:00
}
2023-12-13 19:47:34 -08:00
finalItems = append ( finalItems , pbItem )
}
respType := "delta"
if listResp . CompleteListing {
respType = "complete"
}
resp := & pbs . ListHostSetsResponse {
Items : finalItems ,
EstItemCount : uint32 ( listResp . EstimatedItemCount ),
RemovedIds : listResp . DeletedIds ,
ResponseType : respType ,
SortBy : sortBy ,
SortDir : "desc" ,
}
2021-05-04 19:57:35 -05:00
2023-12-13 19:47:34 -08:00
if listResp . ListToken != nil {
resp . ListToken , err = handlers . MarshalListToken ( ctx , listResp . ListToken , pbs . ResourceType_RESOURCE_TYPE_HOST_SET )
2022-04-13 12:58:33 -04:00
if err != nil {
return nil , err
}
2020-08-21 10:57:30 -07:00
}
2023-12-13 19:47:34 -08:00
return resp , nil
2020-08-21 10:57:30 -07:00
}
// GetHostSet implements the interface pbs.HostSetServiceServer.
func ( s Service ) GetHostSet ( ctx context . Context , req * pbs . GetHostSetRequest ) ( * pbs . GetHostSetResponse , error ) {
2021-05-04 19:57:35 -05:00
const op = "host_sets.(Service).GetHostSet"
2020-08-21 10:57:30 -07:00
if err := validateGetRequest ( req ); err != nil {
return nil , err
}
2020-09-04 14:48:11 -07:00
_ , authResults := s . parentAndAuthResult ( ctx , req . GetId (), action . Read )
2020-09-01 12:31:54 -04:00
if authResults . Error != nil {
return nil , authResults . Error
}
2021-09-24 13:16:13 -07:00
hs , hosts , plg , err := s . getFromRepo ( ctx , req . GetId ())
2020-08-21 10:57:30 -07:00
if err != nil {
return nil , err
}
2021-05-04 19:57:35 -05:00
outputFields , ok := requests . OutputFields ( ctx )
if ! ok {
2021-07-29 18:52:56 -06:00
return nil , errors . New ( ctx , errors . Internal , op , "no request context found" )
2021-05-04 19:57:35 -05:00
}
2021-09-24 13:16:13 -07:00
outputOpts := make ([] handlers . Option , 0 , 4 )
2023-01-06 17:27:19 -06:00
outputOpts = append ( outputOpts , handlers . WithOutputFields ( outputFields ))
2021-05-04 19:57:35 -05:00
if outputFields . Has ( globals . ScopeField ) {
outputOpts = append ( outputOpts , handlers . WithScope ( authResults . Scope ))
}
if outputFields . Has ( globals . AuthorizedActionsField ) {
2023-11-15 19:55:46 -05:00
idActions := idActionsTypeMap [ globals . ResourceInfoFromPrefix ( req . GetId ()). Subtype ]
2021-11-03 12:39:41 -04:00
outputOpts = append ( outputOpts , handlers . WithAuthorizedActions ( authResults . FetchActionSetForId ( ctx , hs . GetPublicId (), idActions ). Strings ()))
2021-05-04 19:57:35 -05:00
}
2021-09-24 13:16:13 -07:00
if plg != nil {
outputOpts = append ( outputOpts , handlers . WithPlugin ( plg ))
}
2021-05-04 19:57:35 -05:00
item , err := toProto ( ctx , hs , hosts , outputOpts ... )
if err != nil {
return nil , err
}
return & pbs . GetHostSetResponse { Item : item }, nil
2020-08-21 10:57:30 -07:00
}
// CreateHostSet implements the interface pbs.HostSetServiceServer.
func ( s Service ) CreateHostSet ( ctx context . Context , req * pbs . CreateHostSetRequest ) ( * pbs . CreateHostSetResponse , error ) {
2021-05-04 19:57:35 -05:00
const op = "host_sets.(Service).CreateHostSet"
2021-09-20 12:48:38 -04:00
if err := validateCreateRequest ( ctx , req ); err != nil {
2020-08-21 10:57:30 -07:00
return nil , err
}
2021-06-22 22:26:26 -04:00
_ , authResults := s . parentAndAuthResult ( ctx , req . GetItem (). GetHostCatalogId (), action . Create )
2020-09-01 12:31:54 -04:00
if authResults . Error != nil {
return nil , authResults . Error
}
2021-09-24 13:16:13 -07:00
hs , plg , err := s . createInRepo ( ctx , authResults . Scope . GetId (), req . GetItem (). GetHostCatalogId (), req . GetItem ())
2020-08-21 10:57:30 -07:00
if err != nil {
return nil , err
}
2021-05-04 19:57:35 -05:00
outputFields , ok := requests . OutputFields ( ctx )
if ! ok {
2021-07-29 18:52:56 -06:00
return nil , errors . New ( ctx , errors . Internal , op , "no request context found" )
2021-05-04 19:57:35 -05:00
}
outputOpts := make ([] handlers . Option , 0 , 3 )
2023-01-06 17:27:19 -06:00
outputOpts = append ( outputOpts , handlers . WithOutputFields ( outputFields ))
2021-05-04 19:57:35 -05:00
if outputFields . Has ( globals . ScopeField ) {
outputOpts = append ( outputOpts , handlers . WithScope ( authResults . Scope ))
}
if outputFields . Has ( globals . AuthorizedActionsField ) {
2023-11-15 19:55:46 -05:00
idActions := idActionsTypeMap [ globals . ResourceInfoFromPrefix ( req . GetItem (). GetHostCatalogId ()). Subtype ]
2021-11-03 12:39:41 -04:00
outputOpts = append ( outputOpts , handlers . WithAuthorizedActions ( authResults . FetchActionSetForId ( ctx , hs . GetPublicId (), idActions ). Strings ()))
2021-05-04 19:57:35 -05:00
}
2021-09-24 13:16:13 -07:00
if plg != nil {
outputOpts = append ( outputOpts , handlers . WithPlugin ( plg ))
}
2021-05-04 19:57:35 -05:00
item , err := toProto ( ctx , hs , nil , outputOpts ... )
if err != nil {
return nil , err
}
2020-08-21 10:57:30 -07:00
return & pbs . CreateHostSetResponse {
2021-05-04 19:57:35 -05:00
Item : item ,
Uri : fmt . Sprintf ( "host-sets/%s" , item . GetId ()),
2020-08-21 10:57:30 -07:00
}, nil
}
// UpdateHostSet implements the interface pbs.HostSetServiceServer.
func ( s Service ) UpdateHostSet ( ctx context . Context , req * pbs . UpdateHostSetRequest ) ( * pbs . UpdateHostSetResponse , error ) {
2021-05-04 19:57:35 -05:00
const op = "host_sets.(Service).UpdateHostSet"
2021-09-20 12:48:38 -04:00
if err := validateUpdateRequest ( ctx , req ); err != nil {
2020-08-21 10:57:30 -07:00
return nil , err
}
2020-09-04 14:48:11 -07:00
cat , authResults := s . parentAndAuthResult ( ctx , req . GetId (), action . Update )
2020-09-01 12:31:54 -04:00
if authResults . Error != nil {
return nil , authResults . Error
}
2021-11-11 14:32:15 -07:00
hs , hosts , plg , err := s . updateInRepo ( ctx , authResults . Scope . GetId (), cat . GetPublicId (), req )
2021-05-04 19:57:35 -05:00
if err != nil {
return nil , err
}
outputFields , ok := requests . OutputFields ( ctx )
if ! ok {
2021-07-29 18:52:56 -06:00
return nil , errors . New ( ctx , errors . Internal , op , "no request context found" )
2021-05-04 19:57:35 -05:00
}
outputOpts := make ([] handlers . Option , 0 , 3 )
2023-01-06 17:27:19 -06:00
outputOpts = append ( outputOpts , handlers . WithOutputFields ( outputFields ))
2021-11-11 14:32:15 -07:00
if plg != nil {
outputOpts = append ( outputOpts , handlers . WithPlugin ( plg ))
}
2021-05-04 19:57:35 -05:00
if outputFields . Has ( globals . ScopeField ) {
outputOpts = append ( outputOpts , handlers . WithScope ( authResults . Scope ))
}
if outputFields . Has ( globals . AuthorizedActionsField ) {
2023-11-15 19:55:46 -05:00
idActions := idActionsTypeMap [ globals . ResourceInfoFromPrefix ( req . GetId ()). Subtype ]
2021-11-03 12:39:41 -04:00
outputOpts = append ( outputOpts , handlers . WithAuthorizedActions ( authResults . FetchActionSetForId ( ctx , hs . GetPublicId (), idActions ). Strings ()))
2021-05-04 19:57:35 -05:00
}
item , err := toProto ( ctx , hs , hosts , outputOpts ... )
2020-08-21 10:57:30 -07:00
if err != nil {
return nil , err
}
2021-05-04 19:57:35 -05:00
return & pbs . UpdateHostSetResponse { Item : item }, nil
2020-08-21 10:57:30 -07:00
}
// DeleteHostSet implements the interface pbs.HostSetServiceServer.
func ( s Service ) DeleteHostSet ( ctx context . Context , req * pbs . DeleteHostSetRequest ) ( * pbs . DeleteHostSetResponse , error ) {
if err := validateDeleteRequest ( req ); err != nil {
return nil , err
}
2020-09-04 14:48:11 -07:00
_ , authResults := s . parentAndAuthResult ( ctx , req . GetId (), action . Delete )
2020-09-01 12:31:54 -04:00
if authResults . Error != nil {
return nil , authResults . Error
}
2020-09-08 23:57:26 -04:00
_ , err := s . deleteFromRepo ( ctx , authResults . Scope . GetId (), req . GetId ())
2020-08-21 10:57:30 -07:00
if err != nil {
return nil , err
}
2021-04-26 19:38:55 -05:00
return nil , nil
2020-08-21 10:57:30 -07:00
}
// AddHostSetHosts implements the interface pbs.HostSetServiceServer.
func ( s Service ) AddHostSetHosts ( ctx context . Context , req * pbs . AddHostSetHostsRequest ) ( * pbs . AddHostSetHostsResponse , error ) {
2021-05-04 19:57:35 -05:00
const op = "host_sets.(Service).AddHostSetHosts"
2020-08-21 10:57:30 -07:00
if err := validateAddRequest ( req ); err != nil {
return nil , err
}
2021-09-22 11:01:51 -07:00
_ , authResults := s . parentAndAuthResult ( ctx , req . GetId (), action . AddHosts )
2020-09-01 12:31:54 -04:00
if authResults . Error != nil {
return nil , authResults . Error
}
2021-05-04 19:57:35 -05:00
hs , hosts , err := s . addInRepo ( ctx , authResults . Scope . GetId (), req . GetId (), req . GetHostIds (), req . GetVersion ())
if err != nil {
return nil , err
}
outputFields , ok := requests . OutputFields ( ctx )
if ! ok {
2021-07-29 18:52:56 -06:00
return nil , errors . New ( ctx , errors . Internal , op , "no request context found" )
2021-05-04 19:57:35 -05:00
}
outputOpts := make ([] handlers . Option , 0 , 3 )
2023-01-06 17:27:19 -06:00
outputOpts = append ( outputOpts , handlers . WithOutputFields ( outputFields ))
2021-05-04 19:57:35 -05:00
if outputFields . Has ( globals . ScopeField ) {
outputOpts = append ( outputOpts , handlers . WithScope ( authResults . Scope ))
}
if outputFields . Has ( globals . AuthorizedActionsField ) {
2023-11-15 19:55:46 -05:00
idActions := idActionsTypeMap [ globals . ResourceInfoFromPrefix ( req . GetId ()). Subtype ]
2021-11-03 12:39:41 -04:00
outputOpts = append ( outputOpts , handlers . WithAuthorizedActions ( authResults . FetchActionSetForId ( ctx , hs . GetPublicId (), idActions ). Strings ()))
2021-05-04 19:57:35 -05:00
}
item , err := toProto ( ctx , hs , hosts , outputOpts ... )
2020-08-21 10:57:30 -07:00
if err != nil {
return nil , err
}
2021-05-04 19:57:35 -05:00
return & pbs . AddHostSetHostsResponse { Item : item }, nil
2020-08-21 10:57:30 -07:00
}
// SetHostSetHosts implements the interface pbs.HostSetServiceServer.
func ( s Service ) SetHostSetHosts ( ctx context . Context , req * pbs . SetHostSetHostsRequest ) ( * pbs . SetHostSetHostsResponse , error ) {
2021-05-04 19:57:35 -05:00
const op = "host_sets.(Service).SetHostSetHosts"
2020-08-21 10:57:30 -07:00
if err := validateSetRequest ( req ); err != nil {
return nil , err
}
2021-09-22 11:01:51 -07:00
_ , authResults := s . parentAndAuthResult ( ctx , req . GetId (), action . SetHosts )
2020-09-01 12:31:54 -04:00
if authResults . Error != nil {
return nil , authResults . Error
}
2021-05-04 19:57:35 -05:00
hs , hosts , err := s . setInRepo ( ctx , authResults . Scope . GetId (), req . GetId (), req . GetHostIds (), req . GetVersion ())
2020-08-21 10:57:30 -07:00
if err != nil {
return nil , err
}
2021-05-04 19:57:35 -05:00
outputFields , ok := requests . OutputFields ( ctx )
if ! ok {
2021-07-29 18:52:56 -06:00
return nil , errors . New ( ctx , errors . Internal , op , "no request context found" )
2021-05-04 19:57:35 -05:00
}
outputOpts := make ([] handlers . Option , 0 , 3 )
2023-01-06 17:27:19 -06:00
outputOpts = append ( outputOpts , handlers . WithOutputFields ( outputFields ))
2021-05-04 19:57:35 -05:00
if outputFields . Has ( globals . ScopeField ) {
outputOpts = append ( outputOpts , handlers . WithScope ( authResults . Scope ))
}
if outputFields . Has ( globals . AuthorizedActionsField ) {
2023-11-15 19:55:46 -05:00
idActions := idActionsTypeMap [ globals . ResourceInfoFromPrefix ( req . GetId ()). Subtype ]
2021-11-03 12:39:41 -04:00
outputOpts = append ( outputOpts , handlers . WithAuthorizedActions ( authResults . FetchActionSetForId ( ctx , hs . GetPublicId (), idActions ). Strings ()))
2021-05-04 19:57:35 -05:00
}
item , err := toProto ( ctx , hs , hosts , outputOpts ... )
if err != nil {
return nil , err
}
return & pbs . SetHostSetHostsResponse { Item : item }, nil
2020-08-21 10:57:30 -07:00
}
// RemoveHostSetHosts implements the interface pbs.HostSetServiceServer.
func ( s Service ) RemoveHostSetHosts ( ctx context . Context , req * pbs . RemoveHostSetHostsRequest ) ( * pbs . RemoveHostSetHostsResponse , error ) {
2021-05-04 19:57:35 -05:00
const op = "host_sets.(Service).RemoveHostSetHosts"
2020-08-21 10:57:30 -07:00
if err := validateRemoveRequest ( req ); err != nil {
return nil , err
}
2021-09-22 11:01:51 -07:00
_ , authResults := s . parentAndAuthResult ( ctx , req . GetId (), action . RemoveHosts )
2020-09-01 12:31:54 -04:00
if authResults . Error != nil {
return nil , authResults . Error
}
2021-05-04 19:57:35 -05:00
hs , hosts , err := s . removeInRepo ( ctx , authResults . Scope . GetId (), req . GetId (), req . GetHostIds (), req . GetVersion ())
2020-08-21 10:57:30 -07:00
if err != nil {
return nil , err
}
2021-05-04 19:57:35 -05:00
outputFields , ok := requests . OutputFields ( ctx )
if ! ok {
2021-07-29 18:52:56 -06:00
return nil , errors . New ( ctx , errors . Internal , op , "no request context found" )
2021-05-04 19:57:35 -05:00
}
outputOpts := make ([] handlers . Option , 0 , 3 )
2023-01-06 17:27:19 -06:00
outputOpts = append ( outputOpts , handlers . WithOutputFields ( outputFields ))
2021-05-04 19:57:35 -05:00
if outputFields . Has ( globals . ScopeField ) {
outputOpts = append ( outputOpts , handlers . WithScope ( authResults . Scope ))
}
if outputFields . Has ( globals . AuthorizedActionsField ) {
2023-11-15 19:55:46 -05:00
idActions := idActionsTypeMap [ globals . ResourceInfoFromPrefix ( req . GetId ()). Subtype ]
2021-11-03 12:39:41 -04:00
outputOpts = append ( outputOpts , handlers . WithAuthorizedActions ( authResults . FetchActionSetForId ( ctx , hs . GetPublicId (), idActions ). Strings ()))
2021-05-04 19:57:35 -05:00
}
item , err := toProto ( ctx , hs , hosts , outputOpts ... )
if err != nil {
return nil , err
}
return & pbs . RemoveHostSetHostsResponse { Item : item }, nil
2020-08-21 10:57:30 -07:00
}
2021-09-24 13:16:13 -07:00
func ( s Service ) getFromRepo ( ctx context . Context , id string ) ( host . Set , [] host . Host , * plugins . PluginInfo , error ) {
2021-09-13 15:02:04 -07:00
var hs host . Set
var hl [] host . Host
2021-09-24 13:16:13 -07:00
var plg * plugins . PluginInfo
2023-11-15 19:55:46 -05:00
switch globals . ResourceInfoFromPrefix ( id ). Subtype {
2023-11-02 16:03:47 -04:00
case static . Subtype :
2021-09-13 15:02:04 -07:00
repo , err := s . staticRepoFn ()
if err != nil {
2021-09-24 13:16:13 -07:00
return nil , nil , nil , err
2021-09-13 15:02:04 -07:00
}
hset , hosts , err := repo . LookupSet ( ctx , id )
if err != nil {
2021-09-24 13:16:13 -07:00
return nil , nil , nil , err
2021-09-13 15:02:04 -07:00
}
if hset == nil {
2021-09-24 13:16:13 -07:00
return nil , nil , nil , handlers . NotFoundErrorf ( "Host Set %q doesn't exist." , id )
2021-09-13 15:02:04 -07:00
}
for _ , h := range hosts {
hl = append ( hl , h )
}
hs = hset
2023-11-02 16:03:47 -04:00
case hostplugin . Subtype :
2021-09-13 15:02:04 -07:00
repo , err := s . pluginRepoFn ()
if err != nil {
2021-09-24 13:16:13 -07:00
return nil , nil , nil , err
2021-09-13 15:02:04 -07:00
}
2021-10-29 15:35:06 -04:00
hset , hsplg , err := repo . LookupSet ( ctx , id )
2021-09-13 15:02:04 -07:00
if err != nil {
2021-09-24 13:16:13 -07:00
return nil , nil , nil , err
2021-09-13 15:02:04 -07:00
}
if hset == nil {
2021-09-24 13:16:13 -07:00
return nil , nil , nil , handlers . NotFoundErrorf ( "Host Set %q doesn't exist." , id )
2021-09-13 15:02:04 -07:00
}
hs = hset
2021-09-24 13:16:13 -07:00
plg = toPluginInfo ( hsplg )
2021-10-29 15:35:06 -04:00
for _ , h := range hset . HostIds {
2023-04-24 19:27:41 -04:00
hl = append ( hl , & hostplugin . Host {
2021-09-28 16:19:32 -04:00
Host : & plugstore . Host {
PublicId : h ,
CatalogId : hset . CatalogId ,
},
})
}
2020-08-21 10:57:30 -07:00
}
2021-09-24 13:16:13 -07:00
return hs , hl , plg , nil
2020-08-21 10:57:30 -07:00
}
2022-07-20 16:24:46 -07:00
func ( s Service ) createInRepo ( ctx context . Context , projectId , catalogId string , item * pb . HostSet ) ( host . Set , * plugins . PluginInfo , error ) {
2021-04-16 12:06:03 -05:00
const op = "host_sets.(Service).createInRepo"
2021-09-13 15:02:04 -07:00
if item == nil {
2021-09-24 13:16:13 -07:00
return nil , nil , errors . New ( ctx , errors . InvalidParameter , op , "missing item" )
2020-08-21 10:57:30 -07:00
}
2021-09-13 15:02:04 -07:00
var hSet host . Set
2021-09-24 13:16:13 -07:00
var plg * plugins . PluginInfo
2023-11-15 19:55:46 -05:00
switch globals . ResourceInfoFromPrefix ( catalogId ). Subtype {
2023-11-02 16:03:47 -04:00
case static . Subtype :
2021-09-13 15:02:04 -07:00
h , err := toStorageStaticSet ( ctx , catalogId , item )
if err != nil {
2021-09-24 13:16:13 -07:00
return nil , nil , err
2021-09-13 15:02:04 -07:00
}
repo , err := s . staticRepoFn ()
if err != nil {
2021-09-24 13:16:13 -07:00
return nil , nil , err
2021-09-13 15:02:04 -07:00
}
2022-07-20 16:24:46 -07:00
out , err := repo . CreateSet ( ctx , projectId , h )
2021-09-13 15:02:04 -07:00
if err != nil {
2021-09-24 13:16:13 -07:00
return nil , nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to create host set" ))
2021-09-13 15:02:04 -07:00
}
if out == nil {
2021-09-24 13:16:13 -07:00
return nil , nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "Unable to create host set but no error returned from repository." )
2021-09-13 15:02:04 -07:00
}
hSet = out
2023-11-02 16:03:47 -04:00
case hostplugin . Subtype :
2021-09-13 15:02:04 -07:00
h , err := toStoragePluginSet ( ctx , catalogId , item )
2021-09-20 12:48:38 -04:00
if err != nil {
2021-09-24 13:16:13 -07:00
return nil , nil , err
2021-09-20 12:48:38 -04:00
}
2021-09-13 15:02:04 -07:00
repo , err := s . pluginRepoFn ()
if err != nil {
2021-09-24 13:16:13 -07:00
return nil , nil , err
2021-09-13 15:02:04 -07:00
}
2022-07-20 16:24:46 -07:00
out , hsplg , err := repo . CreateSet ( ctx , projectId , h )
2021-09-13 15:02:04 -07:00
if err != nil {
2021-09-24 13:16:13 -07:00
return nil , nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to create host set" ))
2021-09-13 15:02:04 -07:00
}
if out == nil {
2021-09-24 13:16:13 -07:00
return nil , nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "Unable to create host set but no error returned from repository." )
2021-09-13 15:02:04 -07:00
}
hSet = out
2021-09-24 13:16:13 -07:00
plg = toPluginInfo ( hsplg )
2021-09-13 15:02:04 -07:00
default :
2021-09-24 13:16:13 -07:00
return nil , nil , errors . New ( ctx , errors . InvalidParameter , op , "unrecognized catalog type" )
2020-08-21 10:57:30 -07:00
}
2021-09-24 13:16:13 -07:00
return hSet , plg , nil
2020-08-21 10:57:30 -07:00
}
2022-07-20 16:24:46 -07:00
func ( s Service ) updateStaticInRepo ( ctx context . Context , projectId , catalogId string , req * pbs . UpdateHostSetRequest ) ( host . Set , [] host . Host , error ) {
2021-11-11 14:32:15 -07:00
const op = "host_sets.(Service).updateStaticInRepo"
2021-09-13 15:02:04 -07:00
item := req . GetItem ()
h , err := toStorageStaticSet ( ctx , catalogId , item )
2020-08-21 10:57:30 -07:00
if err != nil {
2021-07-29 18:52:56 -06:00
return nil , nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "Unable to build host set for update" ))
2020-08-21 10:57:30 -07:00
}
2021-09-13 15:02:04 -07:00
h . PublicId = req . GetId ()
2023-11-02 16:03:47 -04:00
dbMask := maskManager [ static . Subtype ]. Translate ( req . GetUpdateMask (). GetPaths ())
2020-08-21 10:57:30 -07:00
if len ( dbMask ) == 0 {
2021-05-04 19:57:35 -05:00
return nil , nil , handlers . InvalidArgumentErrorf ( "No valid fields included in the update mask." , map [ string ] string { "update_mask" : "No valid fields provided in the update mask." })
2020-08-21 10:57:30 -07:00
}
repo , err := s . staticRepoFn ()
if err != nil {
2021-05-04 19:57:35 -05:00
return nil , nil , err
2020-08-21 10:57:30 -07:00
}
2022-07-20 16:24:46 -07:00
out , m , rowsUpdated , err := repo . UpdateSet ( ctx , projectId , h , item . GetVersion (), dbMask )
2020-08-21 10:57:30 -07:00
if err != nil {
2021-07-29 18:52:56 -06:00
return nil , nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to update host set" ))
2020-08-21 10:57:30 -07:00
}
if rowsUpdated == 0 {
2021-09-13 15:02:04 -07:00
return nil , nil , handlers . NotFoundErrorf ( "Host Set %q doesn't exist or incorrect version provided." , req . GetId ())
2020-08-21 10:57:30 -07:00
}
2021-09-13 15:02:04 -07:00
var hl [] host . Host
for _ , h := range m {
hl = append ( hl , h )
}
return out , hl , nil
2020-08-21 10:57:30 -07:00
}
2022-07-20 16:24:46 -07:00
func ( s Service ) updatePluginInRepo ( ctx context . Context , projectId string , req * pbs . UpdateHostSetRequest ) ( host . Set , [] host . Host , * plugins . PluginInfo , error ) {
2021-11-11 14:32:15 -07:00
const op = "host_sets.(Service).updatePluginInRepo"
item := req . GetItem ()
h , err := toStoragePluginSet ( ctx , "" , item )
if err != nil {
return nil , nil , nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "Unable to build host set for update" ))
}
h . PublicId = req . GetId ()
2023-11-02 16:03:47 -04:00
dbMask := maskManager [ hostplugin . Subtype ]. Translate ( req . GetUpdateMask (). GetPaths (), "attributes" )
2021-11-11 14:32:15 -07:00
if len ( dbMask ) == 0 {
return nil , nil , nil , handlers . InvalidArgumentErrorf ( "No valid fields included in the update mask." , map [ string ] string { "update_mask" : "No valid fields provided in the update mask." })
}
repo , err := s . pluginRepoFn ()
if err != nil {
return nil , nil , nil , err
}
2022-07-20 16:24:46 -07:00
out , hosts , plg , rowsUpdated , err := repo . UpdateSet ( ctx , projectId , h , item . GetVersion (), dbMask )
2021-11-11 14:32:15 -07:00
if err != nil {
return nil , nil , nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to update host set" ))
}
if rowsUpdated == 0 {
return nil , nil , nil , handlers . NotFoundErrorf ( "Host Set %q doesn't exist or incorrect version provided." , req . GetId ())
}
var hl [] host . Host
for _ , h := range hosts {
hl = append ( hl , h )
}
return out , hl , toPluginInfo ( plg ), nil
}
2022-07-20 16:24:46 -07:00
func ( s Service ) updateInRepo ( ctx context . Context , projectId , catalogId string , req * pbs . UpdateHostSetRequest ) ( hs host . Set , hosts [] host . Host , plg * plugins . PluginInfo , err error ) {
2021-11-11 14:32:15 -07:00
const op = "host_sets.(Service).updateInRepo"
2023-11-15 19:55:46 -05:00
switch globals . ResourceInfoFromPrefix ( req . GetId ()). Subtype {
2023-11-02 16:03:47 -04:00
case static . Subtype :
2022-07-20 16:24:46 -07:00
hs , hosts , err = s . updateStaticInRepo ( ctx , projectId , catalogId , req )
2023-11-02 16:03:47 -04:00
case hostplugin . Subtype :
2022-07-20 16:24:46 -07:00
hs , hosts , plg , err = s . updatePluginInRepo ( ctx , projectId , req )
2021-11-11 14:32:15 -07:00
}
return
}
2022-07-20 16:24:46 -07:00
func ( s Service ) deleteFromRepo ( ctx context . Context , projectId , id string ) ( bool , error ) {
2021-04-16 12:06:03 -05:00
const op = "host_sets.(Service).deleteFromRepo"
2021-10-25 09:37:05 -07:00
rows := 0
2023-11-15 19:55:46 -05:00
switch globals . ResourceInfoFromPrefix ( id ). Subtype {
2023-11-02 16:03:47 -04:00
case static . Subtype :
2021-10-25 09:37:05 -07:00
repo , err := s . staticRepoFn ()
if err != nil {
return false , err
}
2022-07-20 16:24:46 -07:00
rows , err = repo . DeleteSet ( ctx , projectId , id )
2021-10-25 09:37:05 -07:00
if err != nil {
return false , errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to delete host" ))
}
2023-11-02 16:03:47 -04:00
case hostplugin . Subtype :
2021-10-25 09:37:05 -07:00
repo , err := s . pluginRepoFn ()
if err != nil {
return false , err
}
2022-07-20 16:24:46 -07:00
rows , err = repo . DeleteSet ( ctx , projectId , id )
2021-10-25 09:37:05 -07:00
if err != nil {
return false , errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to delete host" ))
}
2020-08-21 10:57:30 -07:00
}
return rows > 0 , nil
}
2023-12-13 19:47:34 -08:00
func newOutputOpts ( ctx context . Context , item host . Set , plg * plugins . PluginInfo , authResults auth . VerifyResults ) ([] handlers . Option , bool ) {
res := perms . Resource {
2025-01-24 10:23:33 -08:00
ScopeId : authResults . Scope . Id ,
ParentScopeId : authResults . Scope . ParentScopeId ,
Id : item . GetPublicId (),
Type : resource . HostSet ,
Pin : item . GetCatalogId (),
2023-12-13 19:47:34 -08:00
}
res . Id = item . GetPublicId ()
idActions := idActionsTypeMap [ globals . ResourceInfoFromPrefix ( item . GetPublicId ()). Subtype ]
authorizedActions := authResults . FetchActionSetForId ( ctx , item . GetPublicId (), idActions , auth . WithResource ( & res )). Strings ()
if len ( authorizedActions ) == 0 {
return nil , false
}
outputFields := authResults . FetchOutputFields ( res , action . List ). SelfOrDefaults ( authResults . UserId )
outputOpts := make ([] handlers . Option , 0 , 3 )
outputOpts = append ( outputOpts , handlers . WithOutputFields ( outputFields ))
if outputFields . Has ( globals . ScopeField ) {
outputOpts = append ( outputOpts , handlers . WithScope ( authResults . Scope ))
}
if outputFields . Has ( globals . AuthorizedActionsField ) {
outputOpts = append ( outputOpts , handlers . WithAuthorizedActions ( authorizedActions ))
}
if plg != nil {
outputOpts = append ( outputOpts , handlers . WithPlugin ( plg ))
2020-08-21 10:57:30 -07:00
}
2023-12-13 19:47:34 -08:00
return outputOpts , true
2020-08-21 10:57:30 -07:00
}
2022-07-20 16:24:46 -07:00
func ( s Service ) addInRepo ( ctx context . Context , projectId , setId string , hostIds [] string , version uint32 ) ( * static . HostSet , [] host . Host , error ) {
2021-04-16 12:06:03 -05:00
const op = "host_sets.(Service).addInRepo"
2020-08-21 10:57:30 -07:00
repo , err := s . staticRepoFn ()
if err != nil {
2021-05-04 19:57:35 -05:00
return nil , nil , err
2020-08-21 10:57:30 -07:00
}
2022-07-20 16:24:46 -07:00
_ , err = repo . AddSetMembers ( ctx , projectId , setId , version , strutil . RemoveDuplicates ( hostIds , false ))
2020-08-21 10:57:30 -07:00
if err != nil {
2021-07-29 18:52:56 -06:00
return nil , nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "Unable to add hosts to host set" ))
2020-08-26 11:04:26 -07:00
}
out , m , err := repo . LookupSet ( ctx , setId )
if err != nil {
2021-07-29 18:52:56 -06:00
return nil , nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to look up host set after adding hosts" ))
2020-08-21 10:57:30 -07:00
}
if out == nil {
2021-05-04 19:57:35 -05:00
return nil , nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "Unable to lookup host set after adding hosts to it." )
2020-08-21 10:57:30 -07:00
}
2021-09-13 15:02:04 -07:00
var hl [] host . Host
for _ , h := range m {
hl = append ( hl , h )
}
return out , hl , nil
2020-08-21 10:57:30 -07:00
}
2022-07-20 16:24:46 -07:00
func ( s Service ) setInRepo ( ctx context . Context , projectId , setId string , hostIds [] string , version uint32 ) ( * static . HostSet , [] host . Host , error ) {
2021-04-16 12:06:03 -05:00
const op = "host_sets.(Service).setInRepo"
2020-08-21 10:57:30 -07:00
repo , err := s . staticRepoFn ()
if err != nil {
2021-07-29 18:52:56 -06:00
return nil , nil , errors . Wrap ( ctx , err , op )
2020-08-21 10:57:30 -07:00
}
2022-07-20 16:24:46 -07:00
_ , _ , err = repo . SetSetMembers ( ctx , projectId , setId , version , strutil . RemoveDuplicates ( hostIds , false ))
2020-08-26 11:04:26 -07:00
if err != nil {
2021-07-29 18:52:56 -06:00
return nil , nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "Unable to set hosts in host set" ))
2020-08-26 11:04:26 -07:00
}
2020-08-21 10:57:30 -07:00
2020-08-26 11:04:26 -07:00
out , m , err := repo . LookupSet ( ctx , setId )
2020-08-21 10:57:30 -07:00
if err != nil {
2021-07-29 18:52:56 -06:00
return nil , nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to look up host set after setting hosts" ))
2020-08-21 10:57:30 -07:00
}
if out == nil {
2021-05-04 19:57:35 -05:00
return nil , nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "Unable to lookup host set after setting hosts for it." )
2020-08-21 10:57:30 -07:00
}
2021-09-13 15:02:04 -07:00
var hl [] host . Host
for _ , h := range m {
hl = append ( hl , h )
}
return out , hl , nil
2020-08-21 10:57:30 -07:00
}
2022-07-20 16:24:46 -07:00
func ( s Service ) removeInRepo ( ctx context . Context , projectId , setId string , hostIds [] string , version uint32 ) ( * static . HostSet , [] host . Host , error ) {
2021-04-16 12:06:03 -05:00
const op = "host_sets.(Service).removeInRepo"
2020-08-21 10:57:30 -07:00
repo , err := s . staticRepoFn ()
if err != nil {
2021-07-29 18:52:56 -06:00
return nil , nil , errors . Wrap ( ctx , err , op )
2020-08-21 10:57:30 -07:00
}
2022-07-20 16:24:46 -07:00
_ , err = repo . DeleteSetMembers ( ctx , projectId , setId , version , strutil . RemoveDuplicates ( hostIds , false ))
2020-08-21 10:57:30 -07:00
if err != nil {
2021-07-29 18:52:56 -06:00
return nil , nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "Unable to remove hosts from host set" ))
2020-08-26 11:04:26 -07:00
}
out , m , err := repo . LookupSet ( ctx , setId )
if err != nil {
2021-07-29 18:52:56 -06:00
return nil , nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to look up host set" ))
2020-08-21 10:57:30 -07:00
}
if out == nil {
2021-05-04 19:57:35 -05:00
return nil , nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "Unable to lookup host set after removing hosts from it." )
2020-08-21 10:57:30 -07:00
}
2021-09-13 15:02:04 -07:00
var hl [] host . Host
for _ , h := range m {
hl = append ( hl , h )
}
return out , hl , nil
2020-08-21 10:57:30 -07:00
}
2021-09-13 15:02:04 -07:00
func ( s Service ) parentAndAuthResult ( ctx context . Context , id string , a action . Type ) ( host . Catalog , auth . VerifyResults ) {
2020-09-04 14:48:11 -07:00
res := auth . VerifyResults {}
2021-09-13 15:02:04 -07:00
staticRepo , err := s . staticRepoFn ()
if err != nil {
res . Error = err
return nil , res
}
pluginRepo , err := s . pluginRepoFn ()
2020-09-04 14:48:11 -07:00
if err != nil {
res . Error = err
return nil , res
}
var parentId string
opts := [] auth . Option { auth . WithType ( resource . HostSet ), auth . WithAction ( a )}
switch a {
case action . List , action . Create :
parentId = id
default :
2021-09-13 15:02:04 -07:00
var set host . Set
2023-11-15 19:55:46 -05:00
switch globals . ResourceInfoFromPrefix ( id ). Subtype {
2023-11-02 16:03:47 -04:00
case static . Subtype :
2021-09-13 15:02:04 -07:00
ss , _ , err := staticRepo . LookupSet ( ctx , id )
if err != nil {
res . Error = err
return nil , res
}
if ss == nil {
res . Error = handlers . NotFoundError ()
return nil , res
}
set = ss
2023-11-02 16:03:47 -04:00
case hostplugin . Subtype :
2021-10-29 15:35:06 -04:00
ps , _ , err := pluginRepo . LookupSet ( ctx , id )
2021-09-13 15:02:04 -07:00
if err != nil {
res . Error = err
return nil , res
}
if ps == nil {
res . Error = handlers . NotFoundError ()
return nil , res
}
set = ps
}
parentId = set . GetCatalogId ()
opts = append ( opts , auth . WithId ( id ))
}
var cat host . Catalog
2023-11-15 19:55:46 -05:00
switch globals . ResourceInfoFromPrefix ( id ). Subtype {
2023-11-02 16:03:47 -04:00
case static . Subtype :
2021-09-13 15:02:04 -07:00
cs , err := staticRepo . LookupCatalog ( ctx , parentId )
2020-09-04 14:48:11 -07:00
if err != nil {
res . Error = err
return nil , res
}
2021-09-13 15:02:04 -07:00
if cs == nil {
2020-09-08 23:57:26 -04:00
res . Error = handlers . NotFoundError ()
2020-09-04 14:48:11 -07:00
return nil , res
}
2021-09-13 15:02:04 -07:00
cat = cs
2023-11-02 16:03:47 -04:00
case hostplugin . Subtype :
2021-09-24 13:16:13 -07:00
pc , _ , err := pluginRepo . LookupCatalog ( ctx , parentId )
2021-09-13 15:02:04 -07:00
if err != nil {
res . Error = err
return nil , res
}
if pc == nil {
res . Error = handlers . NotFoundError ()
return nil , res
}
cat = pc
2020-09-04 14:48:11 -07:00
}
2022-07-20 16:24:46 -07:00
opts = append ( opts , auth . WithScopeId ( cat . GetProjectId ()), auth . WithPin ( parentId ))
2020-09-04 14:48:11 -07:00
return cat , auth . Verify ( ctx , opts ... )
}
2023-04-24 19:27:41 -04:00
func toPluginInfo ( plg * plugin . Plugin ) * plugins . PluginInfo {
2021-09-24 13:16:13 -07:00
if plg == nil {
return nil
}
return & plugins . PluginInfo {
Id : plg . GetPublicId (),
Name : plg . GetName (),
Description : plg . GetDescription (),
}
}
2021-09-13 15:02:04 -07:00
func toProto ( ctx context . Context , in host . Set , hosts [] host . Host , opt ... handlers . Option ) ( * pb . HostSet , error ) {
const op = "host_set_service.toProto"
2021-05-04 19:57:35 -05:00
opts := handlers . GetOpts ( opt ... )
if opts . WithOutputFields == nil {
return nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "output fields not found when building hostset proto" )
}
outputFields := * opts . WithOutputFields
out := pb . HostSet {}
if outputFields . Has ( globals . IdField ) {
out . Id = in . GetPublicId ()
}
if outputFields . Has ( globals . HostCatalogIdField ) {
out . HostCatalogId = in . GetCatalogId ()
}
if outputFields . Has ( globals . TypeField ) {
2021-09-24 13:16:13 -07:00
switch in .( type ) {
case * static . HostSet :
2023-11-02 16:03:47 -04:00
out . Type = static . Subtype . String ()
2023-04-24 19:27:41 -04:00
case * hostplugin . HostSet :
2023-11-02 16:03:47 -04:00
out . Type = hostplugin . Subtype . String ()
2021-09-13 15:02:04 -07:00
}
2021-05-04 19:57:35 -05:00
}
if outputFields . Has ( globals . DescriptionField ) && in . GetDescription () != "" {
out . Description = & wrapperspb . StringValue { Value : in . GetDescription ()}
2020-08-21 10:57:30 -07:00
}
2021-05-04 19:57:35 -05:00
if outputFields . Has ( globals . NameField ) && in . GetName () != "" {
out . Name = & wrapperspb . StringValue { Value : in . GetName ()}
2020-08-21 10:57:30 -07:00
}
2021-05-04 19:57:35 -05:00
if outputFields . Has ( globals . CreatedTimeField ) {
out . CreatedTime = in . GetCreateTime (). GetTimestamp ()
2020-08-21 10:57:30 -07:00
}
2021-05-04 19:57:35 -05:00
if outputFields . Has ( globals . UpdatedTimeField ) {
out . UpdatedTime = in . GetUpdateTime (). GetTimestamp ()
}
if outputFields . Has ( globals . VersionField ) {
out . Version = in . GetVersion ()
}
if outputFields . Has ( globals . ScopeField ) {
out . Scope = opts . WithScope
}
2021-09-24 13:16:13 -07:00
if outputFields . Has ( globals . PluginField ) {
out . Plugin = opts . WithPlugin
}
2021-05-04 19:57:35 -05:00
if outputFields . Has ( globals . AuthorizedActionsField ) {
out . AuthorizedActions = opts . WithAuthorizedActions
}
if outputFields . Has ( globals . HostIdsField ) {
for _ , h := range hosts {
out . HostIds = append ( out . HostIds , h . GetPublicId ())
}
2020-08-21 10:57:30 -07:00
}
2021-09-13 15:02:04 -07:00
2021-09-20 12:48:38 -04:00
switch h := in .( type ) {
2023-04-24 19:27:41 -04:00
case * hostplugin . HostSet :
2021-09-20 12:48:38 -04:00
if outputFields . Has ( globals . PreferredEndpointsField ) {
2021-11-11 11:28:08 -05:00
out . PreferredEndpoints = h . PreferredEndpoints
}
if outputFields . Has ( globals . SyncIntervalSecondsField ) && h . GetSyncIntervalSeconds () != 0 {
out . SyncIntervalSeconds = & wrapperspb . Int32Value { Value : h . GetSyncIntervalSeconds ()}
2021-09-20 12:48:38 -04:00
}
if outputFields . Has ( globals . AttributesField ) {
2021-09-21 09:18:49 -07:00
attrs := & structpb . Struct {}
err := proto . Unmarshal ( h . Attributes , attrs )
2021-09-13 15:02:04 -07:00
if err != nil {
return nil , errors . Wrap ( ctx , err , op )
}
2021-09-21 09:18:49 -07:00
if len ( attrs . GetFields ()) > 0 {
2022-04-13 12:58:33 -04:00
out . Attrs = & pb . HostSet_Attributes {
Attributes : attrs ,
}
2021-09-13 15:02:04 -07:00
}
}
}
2021-05-04 19:57:35 -05:00
return & out , nil
2020-08-21 10:57:30 -07:00
}
2021-09-13 15:02:04 -07:00
func toStorageStaticSet ( ctx context . Context , catalogId string , item * pb . HostSet ) ( * static . HostSet , error ) {
const op = "host_set_service.toStorageStaticSet"
var opts [] static . Option
if item . GetName () != nil {
opts = append ( opts , static . WithName ( item . GetName (). GetValue ()))
}
if item . GetDescription () != nil {
opts = append ( opts , static . WithDescription ( item . GetDescription (). GetValue ()))
}
2023-06-06 17:09:08 -04:00
hs , err := static . NewHostSet ( ctx , catalogId , opts ... )
2021-09-13 15:02:04 -07:00
if err != nil {
return nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "Unable to build host set for creation" ))
}
return hs , nil
}
2023-04-24 19:27:41 -04:00
func toStoragePluginSet ( ctx context . Context , catalogId string , item * pb . HostSet ) ( * hostplugin . HostSet , error ) {
2021-09-13 15:02:04 -07:00
const op = "host_set_service.toStoragePluginSet"
2023-04-24 19:27:41 -04:00
var opts [] hostplugin . Option
2021-09-13 15:02:04 -07:00
if item . GetName () != nil {
2023-04-24 19:27:41 -04:00
opts = append ( opts , hostplugin . WithName ( item . GetName (). GetValue ()))
2021-09-13 15:02:04 -07:00
}
if item . GetDescription () != nil {
2023-04-24 19:27:41 -04:00
opts = append ( opts , hostplugin . WithDescription ( item . GetDescription (). GetValue ()))
2021-09-13 15:02:04 -07:00
}
if item . GetAttributes () != nil {
2023-04-24 19:27:41 -04:00
opts = append ( opts , hostplugin . WithAttributes ( item . GetAttributes ()))
2021-09-13 15:02:04 -07:00
}
2021-09-20 12:48:38 -04:00
if item . GetPreferredEndpoints () != nil {
2023-04-24 19:27:41 -04:00
opts = append ( opts , hostplugin . WithPreferredEndpoints ( item . GetPreferredEndpoints ()))
2021-09-20 12:48:38 -04:00
}
2021-11-11 11:28:08 -05:00
if item . GetSyncIntervalSeconds () != nil {
2023-04-24 19:27:41 -04:00
opts = append ( opts , hostplugin . WithSyncIntervalSeconds ( item . GetSyncIntervalSeconds (). GetValue ()))
2021-11-11 11:28:08 -05:00
}
2023-04-24 19:27:41 -04:00
hs , err := hostplugin . NewHostSet ( ctx , catalogId , opts ... )
2021-09-13 15:02:04 -07:00
if err != nil {
return nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "Unable to build host set for creation" ))
}
return hs , nil
}
2020-08-21 10:57:30 -07:00
// A validateX method should exist for each method above. These methods do not make calls to any backing service but enforce
// requirements on the structure of the request. They verify that:
2022-08-25 09:12:24 -07:00
// - The path passed in is correctly formatted
// - All required parameters are set
// - There are no conflicting parameters provided
// - The type asserted by the ID and/or field is known
// - If relevant, the type derived from the id prefix matches what is claimed by the type field
2020-08-21 10:57:30 -07:00
func validateGetRequest ( req * pbs . GetHostSetRequest ) error {
2023-03-10 15:06:39 -05:00
return handlers . ValidateGetRequest ( handlers . NoopValidatorFn , req , globals . StaticHostSetPrefix , globals . PluginHostSetPrefix , globals . PluginHostSetPreviousPrefix )
2020-08-21 10:57:30 -07:00
}
2021-09-20 12:48:38 -04:00
func validateCreateRequest ( ctx context . Context , req * pbs . CreateHostSetRequest ) error {
2020-08-21 10:57:30 -07:00
return handlers . ValidateCreateRequest ( req . GetItem (), func () map [ string ] string {
badFields := map [ string ] string {}
2023-03-10 15:06:39 -05:00
if ! handlers . ValidId ( handlers . Id ( req . GetItem (). GetHostCatalogId ()), globals . StaticHostCatalogPrefix , globals . PluginHostCatalogPrefix , globals . PluginHostCatalogPreviousPrefix ) {
2021-09-24 13:16:13 -07:00
badFields [ globals . HostCatalogIdField ] = "The field is incorrectly formatted."
2020-08-21 10:57:30 -07:00
}
2021-09-20 12:48:38 -04:00
if len ( req . GetItem (). GetPreferredEndpoints ()) > 0 {
_ , err := endpoint . NewPreferencer ( ctx , endpoint . WithPreferenceOrder ( req . GetItem (). GetPreferredEndpoints ()))
if err != nil {
badFields [ globals . PreferredEndpointsField ] = fmt . Errorf ( "Error parsing preferred endpoints: %w." , err ). Error ()
}
}
2023-11-15 19:55:46 -05:00
switch globals . ResourceInfoFromPrefix ( req . GetItem (). GetHostCatalogId ()). Subtype {
2023-11-02 16:03:47 -04:00
case static . Subtype :
if req . GetItem (). GetType () != "" && req . GetItem (). GetType () != static . Subtype . String () {
2021-09-24 13:16:13 -07:00
badFields [ globals . TypeField ] = "Doesn't match the parent resource's type."
2020-08-21 10:57:30 -07:00
}
2021-11-11 14:32:15 -07:00
if len ( req . GetItem (). PreferredEndpoints ) > 0 {
badFields [ globals . PreferredEndpointsField ] = "This field is not yet supported for static host sets."
}
2023-11-02 16:03:47 -04:00
case hostplugin . Subtype :
if req . GetItem (). GetType () != "" && req . GetItem (). GetType () != hostplugin . Subtype . String () {
2021-09-24 13:16:13 -07:00
badFields [ globals . TypeField ] = "Doesn't match the parent resource's type."
2021-09-13 15:02:04 -07:00
}
2021-11-11 11:28:08 -05:00
if val := req . GetItem (). GetSyncIntervalSeconds (); val != nil {
if val . GetValue () == 0 || val . GetValue () < - 1 {
badFields [ globals . SyncIntervalSecondsField ] = "Must be -1 or a positive integer."
}
}
2020-08-21 10:57:30 -07:00
}
return badFields
})
}
2021-09-20 12:48:38 -04:00
func validateUpdateRequest ( ctx context . Context , req * pbs . UpdateHostSetRequest ) error {
2021-04-08 20:54:32 -04:00
return handlers . ValidateUpdateRequest ( req , req . GetItem (), func () map [ string ] string {
2020-08-21 10:57:30 -07:00
badFields := map [ string ] string {}
2021-09-20 12:48:38 -04:00
if len ( req . GetItem (). GetPreferredEndpoints ()) > 0 {
_ , err := endpoint . NewPreferencer ( ctx , endpoint . WithPreferenceOrder ( req . GetItem (). GetPreferredEndpoints ()))
if err != nil {
badFields [ globals . PreferredEndpointsField ] = fmt . Errorf ( "Error parsing preferred endpoints: %w." , err ). Error ()
}
}
2023-11-15 19:55:46 -05:00
switch globals . ResourceInfoFromPrefix ( req . GetId ()). Subtype {
2023-11-02 16:03:47 -04:00
case static . Subtype :
if req . GetItem (). GetType () != "" && req . GetItem (). GetType () != static . Subtype . String () {
2021-09-20 12:48:38 -04:00
badFields [ globals . TypeField ] = "Cannot modify the resource type."
2020-09-08 16:04:19 -07:00
}
2023-11-02 16:03:47 -04:00
case hostplugin . Subtype :
2021-11-11 11:28:08 -05:00
if val := req . GetItem (). GetSyncIntervalSeconds (); val != nil {
if val . GetValue () == 0 || val . GetValue () < - 1 {
badFields [ globals . SyncIntervalSecondsField ] = "Must be -1 or a positive integer."
}
}
2020-08-21 10:57:30 -07:00
}
return badFields
2023-03-10 15:06:39 -05:00
}, globals . StaticHostSetPrefix , globals . PluginHostSetPrefix , globals . PluginHostSetPreviousPrefix )
2020-08-21 10:57:30 -07:00
}
func validateDeleteRequest ( req * pbs . DeleteHostSetRequest ) error {
2023-03-10 15:06:39 -05:00
return handlers . ValidateDeleteRequest ( handlers . NoopValidatorFn , req , globals . StaticHostSetPrefix , globals . PluginHostSetPrefix , globals . PluginHostSetPreviousPrefix )
2020-08-21 10:57:30 -07:00
}
2023-06-06 17:09:08 -04:00
func validateListRequest ( ctx context . Context , req * pbs . ListHostSetsRequest ) error {
2020-08-21 10:57:30 -07:00
badFields := map [ string ] string {}
2023-03-10 15:06:39 -05:00
if ! handlers . ValidId ( handlers . Id ( req . GetHostCatalogId ()), globals . StaticHostCatalogPrefix , globals . PluginHostCatalogPrefix , globals . PluginHostCatalogPreviousPrefix ) {
2021-09-24 13:16:13 -07:00
badFields [ globals . HostCatalogIdField ] = "The field is incorrectly formatted."
2020-08-21 10:57:30 -07:00
}
2023-06-06 17:09:08 -04:00
if _ , err := handlers . NewFilter ( ctx , req . GetFilter ()); err != nil {
2021-09-24 13:16:13 -07:00
badFields [ globals . FilterField ] = fmt . Sprintf ( "This field could not be parsed. %v" , err )
2021-02-22 10:09:25 -07:00
}
2020-08-21 10:57:30 -07:00
if len ( badFields ) > 0 {
return handlers . InvalidArgumentErrorf ( "Improperly formatted identifier." , badFields )
}
return nil
}
func validateAddRequest ( req * pbs . AddHostSetHostsRequest ) error {
badFields := map [ string ] string {}
2023-03-10 15:06:39 -05:00
if ! handlers . ValidId ( handlers . Id ( req . GetId ()), globals . StaticHostSetPrefix ) {
2021-09-24 13:16:13 -07:00
badFields [ globals . IdField ] = "Incorrectly formatted identifier."
2020-08-21 10:57:30 -07:00
}
if req . GetVersion () == 0 {
2021-09-24 13:16:13 -07:00
badFields [ globals . VersionField ] = "Required field."
2020-08-21 10:57:30 -07:00
}
if len ( req . GetHostIds ()) == 0 {
2021-09-24 13:16:13 -07:00
badFields [ globals . HostIdsField ] = "Must be non-empty."
2020-08-21 10:57:30 -07:00
}
2020-09-01 12:31:54 -04:00
for _ , id := range req . GetHostIds () {
2023-03-10 15:06:39 -05:00
if ! handlers . ValidId ( handlers . Id ( id ), globals . StaticHostPrefix ) {
2021-09-24 13:16:13 -07:00
badFields [ globals . HostIdsField ] = "Incorrectly formatted host identifier."
2020-09-01 12:31:54 -04:00
break
}
}
2020-08-21 10:57:30 -07:00
if len ( badFields ) > 0 {
return handlers . InvalidArgumentErrorf ( "Errors in provided fields." , badFields )
}
return nil
}
func validateSetRequest ( req * pbs . SetHostSetHostsRequest ) error {
badFields := map [ string ] string {}
2023-03-10 15:06:39 -05:00
if ! handlers . ValidId ( handlers . Id ( req . GetId ()), globals . StaticHostSetPrefix ) {
2021-09-24 13:16:13 -07:00
badFields [ globals . IdField ] = "Incorrectly formatted identifier."
2020-08-21 10:57:30 -07:00
}
if req . GetVersion () == 0 {
2021-09-24 13:16:13 -07:00
badFields [ globals . VersionField ] = "Required field."
2020-08-21 10:57:30 -07:00
}
2020-09-01 12:31:54 -04:00
for _ , id := range req . GetHostIds () {
2023-03-10 15:06:39 -05:00
if ! handlers . ValidId ( handlers . Id ( id ), globals . StaticHostPrefix ) {
2021-09-24 13:16:13 -07:00
badFields [ globals . HostIdsField ] = "Incorrectly formatted host identifier."
2020-09-01 12:31:54 -04:00
break
}
}
2020-08-21 10:57:30 -07:00
if len ( badFields ) > 0 {
return handlers . InvalidArgumentErrorf ( "Errors in provided fields." , badFields )
}
return nil
}
func validateRemoveRequest ( req * pbs . RemoveHostSetHostsRequest ) error {
badFields := map [ string ] string {}
2023-03-10 15:06:39 -05:00
if ! handlers . ValidId ( handlers . Id ( req . GetId ()), globals . StaticHostSetPrefix ) {
2021-09-24 13:16:13 -07:00
badFields [ globals . IdField ] = "Incorrectly formatted identifier."
2020-08-21 10:57:30 -07:00
}
if req . GetVersion () == 0 {
2021-09-24 13:16:13 -07:00
badFields [ globals . VersionField ] = "Required field."
2020-08-21 10:57:30 -07:00
}
if len ( req . GetHostIds ()) == 0 {
2021-09-24 13:16:13 -07:00
badFields [ globals . HostIdsField ] = "Must be non-empty."
2020-08-21 10:57:30 -07:00
}
2020-09-01 12:31:54 -04:00
for _ , id := range req . GetHostIds () {
2023-03-10 15:06:39 -05:00
if ! handlers . ValidId ( handlers . Id ( id ), globals . StaticHostPrefix ) {
2021-09-24 13:16:13 -07:00
badFields [ globals . HostIdsField ] = "Incorrectly formatted host identifier."
2020-09-01 12:31:54 -04:00
break
}
}
2020-08-21 10:57:30 -07:00
if len ( badFields ) > 0 {
return handlers . InvalidArgumentErrorf ( "Errors in provided fields." , badFields )
}
return nil
}