2020-08-20 13:56:52 -04:00
package static
import (
"context"
"errors"
"fmt"
"strings"
"github.com/hashicorp/boundary/internal/db"
dbcommon "github.com/hashicorp/boundary/internal/db/common"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/oplog"
)
// CreateSet inserts s into the repository and returns a new HostSet
// containing the host set's PublicId. s is not changed. s must contain a
// valid CatalogId. s must not contain a PublicId. The PublicId is
// generated and assigned by this method. opt is ignored.
//
// Both s.Name and s.Description are optional. If s.Name is set, it must be
// unique within s.CatalogId.
func ( r * Repository ) CreateSet ( ctx context . Context , scopeId string , s * HostSet , opt ... Option ) ( * HostSet , error ) {
if s == nil {
2020-08-25 12:18:14 -04:00
return nil , fmt . Errorf ( "create: static host set: %w" , db . ErrInvalidParameter )
2020-08-20 13:56:52 -04:00
}
if s . HostSet == nil {
2020-08-25 12:18:14 -04:00
return nil , fmt . Errorf ( "create: static host set: embedded HostSet: %w" , db . ErrInvalidParameter )
2020-08-20 13:56:52 -04:00
}
if s . CatalogId == "" {
return nil , fmt . Errorf ( "create: static host set: no catalog id: %w" , db . ErrInvalidParameter )
}
if s . PublicId != "" {
return nil , fmt . Errorf ( "create: static host set: public id not empty: %w" , db . ErrInvalidParameter )
}
if scopeId == "" {
2020-08-25 12:18:14 -04:00
return nil , fmt . Errorf ( "create: static host set: no scopeId: %w" , db . ErrInvalidParameter )
2020-08-20 13:56:52 -04:00
}
s = s . clone ()
2020-09-24 03:31:26 -04:00
opts := getOpts ( opt ... )
if opts . withPublicId != "" {
if ! strings . HasPrefix ( opts . withPublicId , HostSetPrefix + "_" ) {
return nil , fmt . Errorf ( "create: static host set: passed-in public ID %q has wrong prefix, should be %q: %w" , opts . withPublicId , HostSetPrefix , db . ErrInvalidPublicId )
}
s . PublicId = opts . withPublicId
} else {
id , err := newHostSetId ()
if err != nil {
return nil , fmt . Errorf ( "create: static host set: %w" , err )
}
s . PublicId = id
2020-08-20 13:56:52 -04:00
}
oplogWrapper , err := r . kms . GetWrapper ( ctx , scopeId , kms . KeyPurposeOplog )
if err != nil {
return nil , fmt . Errorf ( "create: static host set: unable to get oplog wrapper: %w" , err )
}
var newHostSet * HostSet
_ , err = r . writer . DoTx ( ctx , db . StdRetryCnt , db . ExpBackoff {},
func ( _ db . Reader , w db . Writer ) error {
newHostSet = s . clone ()
return w . Create ( ctx , newHostSet , db . WithOplog ( oplogWrapper , s . oplog ( oplog . OpType_OP_TYPE_CREATE )))
},
)
if err != nil {
if db . IsUniqueError ( err ) {
return nil , fmt . Errorf ( "create: static host set: in catalog: %s: name %s already exists: %w" ,
s . CatalogId , s . Name , db . ErrNotUnique )
}
return nil , fmt . Errorf ( "create: static host set: in catalog: %s: %w" , s . CatalogId , err )
}
return newHostSet , nil
}
// UpdateSet updates the repository entry for s.PublicId with the values in
// s for the fields listed in fieldMaskPaths. It returns a new HostSet
2020-08-26 12:31:18 -04:00
// containing the updated values, the hosts assigned to the host set, and a
// count of the number of records updated. s is not changed.
2020-08-20 13:56:52 -04:00
//
// s must contain a valid PublicId. Only s.Name and s.Description can be
// updated. If s.Name is set to a non-empty string, it must be unique
// within s.CatalogId.
//
// An attribute of s will be set to NULL in the database if the attribute
// in s is the zero value and it is included in fieldMaskPaths.
2020-08-26 12:31:18 -04:00
//
// The WithLimit option can be used to limit the number of hosts returned.
// All other options are ignored.
func ( r * Repository ) UpdateSet ( ctx context . Context , scopeId string , s * HostSet , version uint32 , fieldMaskPaths [] string , opt ... Option ) ( * HostSet , [] * Host , int , error ) {
2020-08-20 13:56:52 -04:00
if s == nil {
2020-08-26 12:31:18 -04:00
return nil , nil , db . NoRowsAffected , fmt . Errorf ( "update: static host set: %w" , db . ErrInvalidParameter )
2020-08-20 13:56:52 -04:00
}
if s . HostSet == nil {
2020-08-26 12:31:18 -04:00
return nil , nil , db . NoRowsAffected , fmt . Errorf ( "update: static host set: embedded HostSet: %w" , db . ErrInvalidParameter )
2020-08-20 13:56:52 -04:00
}
if s . PublicId == "" {
2020-08-26 12:31:18 -04:00
return nil , nil , db . NoRowsAffected , fmt . Errorf ( "update: static host set: missing public id: %w" , db . ErrInvalidParameter )
2020-08-20 13:56:52 -04:00
}
if version == 0 {
2020-08-26 12:31:18 -04:00
return nil , nil , db . NoRowsAffected , fmt . Errorf ( "update: static host set: no version supplied: %w" , db . ErrInvalidParameter )
2020-08-20 13:56:52 -04:00
}
if scopeId == "" {
2020-08-26 12:31:18 -04:00
return nil , nil , db . NoRowsAffected , fmt . Errorf ( "update: static host set: no scopeId: %w" , db . ErrInvalidParameter )
2020-08-20 13:56:52 -04:00
}
for _ , f := range fieldMaskPaths {
switch {
case strings . EqualFold ( "Name" , f ):
case strings . EqualFold ( "Description" , f ):
default :
2020-08-26 12:31:18 -04:00
return nil , nil , db . NoRowsAffected , fmt . Errorf ( "update: static host set: field: %s: %w" , f , db . ErrInvalidFieldMask )
2020-08-20 13:56:52 -04:00
}
}
var dbMask , nullFields [] string
dbMask , nullFields = dbcommon . BuildUpdatePaths (
map [ string ] interface {}{
"Name" : s . Name ,
"Description" : s . Description ,
},
fieldMaskPaths ,
2020-09-19 10:31:49 -04:00
nil ,
2020-08-20 13:56:52 -04:00
)
if len ( dbMask ) == 0 && len ( nullFields ) == 0 {
2020-08-26 12:31:18 -04:00
return nil , nil , db . NoRowsAffected , fmt . Errorf ( "update: static host set: %w" , db . ErrEmptyFieldMask )
}
opts := getOpts ( opt ... )
limit := r . defaultLimit
if opts . withLimit != 0 {
// non-zero signals an override of the default limit for the repo.
limit = opts . withLimit
2020-08-20 13:56:52 -04:00
}
oplogWrapper , err := r . kms . GetWrapper ( ctx , scopeId , kms . KeyPurposeOplog )
if err != nil {
2020-08-26 12:31:18 -04:00
return nil , nil , db . NoRowsAffected , fmt . Errorf ( "update: static host set: unable to get oplog wrapper: %w" , err )
2020-08-20 13:56:52 -04:00
}
var rowsUpdated int
var returnedHostSet * HostSet
2020-08-26 12:31:18 -04:00
var hosts [] * Host
2020-08-20 13:56:52 -04:00
_ , err = r . writer . DoTx ( ctx , db . StdRetryCnt , db . ExpBackoff {},
2020-08-26 12:31:18 -04:00
func ( reader db . Reader , w db . Writer ) error {
2020-08-20 13:56:52 -04:00
returnedHostSet = s . clone ()
var err error
rowsUpdated , err = w . Update ( ctx , returnedHostSet , dbMask , nullFields ,
db . WithOplog ( oplogWrapper , s . oplog ( oplog . OpType_OP_TYPE_UPDATE )),
db . WithVersion ( & version ))
if err == nil && rowsUpdated > 1 {
return db . ErrMultipleRecords
}
2020-08-26 12:31:18 -04:00
if err != nil {
return err
}
hosts , err = getHosts ( ctx , reader , s . PublicId , limit )
2020-08-20 13:56:52 -04:00
return err
},
)
if err != nil {
if db . IsUniqueError ( err ) {
2020-08-26 12:31:18 -04:00
return nil , nil , db . NoRowsAffected , fmt . Errorf ( "update: static host set: %s: name %s already exists: %w" ,
2020-08-20 13:56:52 -04:00
s . PublicId , s . Name , db . ErrNotUnique )
}
2020-08-26 12:31:18 -04:00
return nil , nil , db . NoRowsAffected , fmt . Errorf ( "update: static host set: %s: %w" , s . PublicId , err )
2020-08-20 13:56:52 -04:00
}
2020-08-26 12:31:18 -04:00
return returnedHostSet , hosts , rowsUpdated , nil
2020-08-20 13:56:52 -04:00
}
2020-08-26 12:31:18 -04:00
// LookupSet will look up a host set in the repository and return the host
// set and the hosts assigned to the host set. If the host set is not
// found, it will return nil, nil, nil. The WithLimit option can be used to
// limit the number of hosts returned. All other options are ignored.
func ( r * Repository ) LookupSet ( ctx context . Context , publicId string , opt ... Option ) ( * HostSet , [] * Host , error ) {
2020-08-20 13:56:52 -04:00
if publicId == "" {
2020-08-26 12:31:18 -04:00
return nil , nil , fmt . Errorf ( "lookup: static host set: missing public id %w" , db . ErrInvalidParameter )
2020-08-20 13:56:52 -04:00
}
2020-08-26 12:31:18 -04:00
opts := getOpts ( opt ... )
limit := r . defaultLimit
if opts . withLimit != 0 {
// non-zero signals an override of the default limit for the repo.
limit = opts . withLimit
}
2020-08-20 13:56:52 -04:00
s := allocHostSet ()
s . PublicId = publicId
2020-08-26 12:31:18 -04:00
var hosts [] * Host
_ , err := r . writer . DoTx ( ctx , db . StdRetryCnt , db . ExpBackoff {}, func ( reader db . Reader , _ db . Writer ) error {
if err := reader . LookupByPublicId ( ctx , s ); err != nil {
if errors . Is ( err , db . ErrRecordNotFound ) {
s = nil
return nil
}
return err
2020-08-20 13:56:52 -04:00
}
2020-08-26 12:31:18 -04:00
var err error
hosts , err = getHosts ( ctx , reader , s . PublicId , limit )
return err
})
if err != nil {
return nil , nil , fmt . Errorf ( "lookup: static host set: failed %w for %s" , err , publicId )
2020-08-20 13:56:52 -04:00
}
2020-08-26 12:31:18 -04:00
return s , hosts , nil
2020-08-20 13:56:52 -04:00
}
// ListSets returns a slice of HostSets for the catalogId. WithLimit is the
// only option supported.
func ( r * Repository ) ListSets ( ctx context . Context , catalogId string , opt ... Option ) ([] * HostSet , error ) {
if catalogId == "" {
return nil , fmt . Errorf ( "list: static host set: missing catalog id: %w" , db . ErrInvalidParameter )
}
opts := getOpts ( opt ... )
limit := r . defaultLimit
if opts . withLimit != 0 {
// non-zero signals an override of the default limit for the repo.
limit = opts . withLimit
}
var sets [] * HostSet
err := r . reader . SearchWhere ( ctx , & sets , "catalog_id = ?" , [] interface {}{ catalogId }, db . WithLimit ( limit ))
if err != nil {
return nil , fmt . Errorf ( "list: static host set: %w" , err )
}
return sets , nil
}
// DeleteSet deletes the host set for the provided id from the repository
// returning a count of the number of records deleted. All options are
// ignored.
func ( r * Repository ) DeleteSet ( ctx context . Context , scopeId string , publicId string , opt ... Option ) ( int , error ) {
if publicId == "" {
return db . NoRowsAffected , fmt . Errorf ( "delete: static host set: missing public id: %w" , db . ErrInvalidParameter )
}
if scopeId == "" {
2020-08-25 12:18:14 -04:00
return db . NoRowsAffected , fmt . Errorf ( "delete: static host set: no scopeId: %w" , db . ErrInvalidParameter )
2020-08-20 13:56:52 -04:00
}
s := allocHostSet ()
s . PublicId = publicId
oplogWrapper , err := r . kms . GetWrapper ( ctx , scopeId , kms . KeyPurposeOplog )
if err != nil {
return db . NoRowsAffected , fmt . Errorf ( "delete: static host set: unable to get oplog wrapper: %w" , err )
}
var rowsDeleted int
_ , err = r . writer . DoTx (
ctx , db . StdRetryCnt , db . ExpBackoff {},
func ( _ db . Reader , w db . Writer ) ( err error ) {
ds := s . clone ()
rowsDeleted , err = w . Delete ( ctx , ds , db . WithOplog ( oplogWrapper , s . oplog ( oplog . OpType_OP_TYPE_DELETE )))
if err == nil && rowsDeleted > 1 {
return db . ErrMultipleRecords
}
return err
},
)
if err != nil {
return db . NoRowsAffected , fmt . Errorf ( "delete: static host set: %s: %w" , publicId , err )
}
return rowsDeleted , nil
}