diff --git a/internal/host/plugin/repository_host_set.go b/internal/host/plugin/repository_host_set.go index f2b23db503..9388519aac 100644 --- a/internal/host/plugin/repository_host_set.go +++ b/internal/host/plugin/repository_host_set.go @@ -290,13 +290,9 @@ func (r *Repository) UpdateSet(ctx context.Context, scopeId string, s *HostSet, if updateAttributes { dbMask = append(dbMask, "attributes") - if s.Attributes != nil { - newSet.Attributes, err = patchstruct.PatchBytes(newSet.Attributes, s.Attributes) - if err != nil { - return nil, nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("error in set attribute JSON")) - } - } else { - newSet.Attributes = make([]byte, 0) + newSet.Attributes, err = patchstruct.PatchBytes(newSet.Attributes, s.Attributes) + if err != nil { + return nil, nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("error in set attribute JSON")) } // Flag the record as needing a sync since we've updated diff --git a/internal/servers/controller/handlers/host_sets/host_set_service.go b/internal/servers/controller/handlers/host_sets/host_set_service.go index 9c47f7e658..03cf4c011a 100644 --- a/internal/servers/controller/handlers/host_sets/host_set_service.go +++ b/internal/servers/controller/handlers/host_sets/host_set_service.go @@ -553,7 +553,7 @@ func (s Service) updatePluginInRepo(ctx context.Context, scopeId string, req *pb return nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("Unable to build host set for update")) } h.PublicId = req.GetId() - dbMask := maskManager[plugin.Subtype].Translate(req.GetUpdateMask().GetPaths(), "attributes.") + dbMask := maskManager[plugin.Subtype].Translate(req.GetUpdateMask().GetPaths(), "attributes") 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."}) } diff --git a/internal/servers/controller/handlers/host_sets/host_set_service_test.go b/internal/servers/controller/handlers/host_sets/host_set_service_test.go index c28f6241e0..29a8cd64bb 100644 --- a/internal/servers/controller/handlers/host_sets/host_set_service_test.go +++ b/internal/servers/controller/handlers/host_sets/host_set_service_test.go @@ -1570,6 +1570,20 @@ func TestUpdate_Plugin(t *testing.T) { assert.Equal(t, map[string]interface{}{"newkey": "newvalue"}, in.GetAttributes().AsMap()) }, }, + { + name: "Set attributes null", + masks: []string{"attributes"}, + changes: []updateFn{ + clearReadOnlyFields(), + updateAttrs(func() *structpb.Struct { + ret, _ := structpb.NewStruct(nil) + return ret + }()), + }, + check: func(t *testing.T, in *pb.HostSet) { + assert.Equal(t, (*structpb.Struct)(nil), in.GetAttributes()) + }, + }, { name: "Update preferred endpoints", masks: []string{"preferred_endpoints"}, diff --git a/testing/controller/controller.go b/testing/controller/controller.go index cdd432d638..1729e5ef0e 100644 --- a/testing/controller/controller.go +++ b/testing/controller/controller.go @@ -9,6 +9,26 @@ import ( wrapping "github.com/hashicorp/go-kms-wrapping" ) +type option struct { + tcOptions *controller.TestControllerOpts + setWithConfigFile bool + setWithConfigText bool + setDisableAuthMethodCreation bool + setDisableDatabaseCreation bool + setDisableDatabaseDestruction bool + setDefaultPasswordAuthMethodId bool + setDefaultOidcAuthMethodId bool + setDefaultLoginName bool + setDefaultPassword bool + setRootKms bool + setWorkerAuthKms bool + setRecoveryKms bool + setDatabaseUrl bool + setEnableTemplatedDatabase bool +} + +type Option func(*option) error + func getOpts(opt ...Option) (*controller.TestControllerOpts, error) { opts := &option{ tcOptions: &controller.TestControllerOpts{}, @@ -18,11 +38,15 @@ func getOpts(opt ...Option) (*controller.TestControllerOpts, error) { return nil, err } } + opts.tcOptions.DisableDatabaseTemplate = !opts.setEnableTemplatedDatabase if opts.setWithConfigFile && opts.setWithConfigText { return nil, fmt.Errorf("Cannot provide both WithConfigFile and WithConfigText") } var setDbParams bool - if opts.setDefaultPasswordAuthMethodId || opts.setDefaultOidcAuthMethodId || opts.setDefaultLoginName || opts.setDefaultPassword { + if opts.setDefaultPasswordAuthMethodId || + opts.setDefaultOidcAuthMethodId || + opts.setDefaultLoginName || + opts.setDefaultPassword { setDbParams = true } if opts.setDisableAuthMethodCreation { @@ -38,25 +62,6 @@ func getOpts(opt ...Option) (*controller.TestControllerOpts, error) { return opts.tcOptions, nil } -type option struct { - tcOptions *controller.TestControllerOpts - setWithConfigFile bool - setWithConfigText bool - setDisableAuthMethodCreation bool - setDisableDatabaseCreation bool - setDisableDatabaseDestruction bool - setDefaultPasswordAuthMethodId bool - setDefaultOidcAuthMethodId bool - setDefaultLoginName bool - setDefaultPassword bool - setRootKms bool - setWorkerAuthKms bool - setRecoveryKms bool - setDatabaseUrl bool -} - -type Option func(*option) error - // WithConfigFile provides the given ConfigFile to the built TestController. // This option cannot be used if WithConfigText is used. func WithConfigFile(f string) Option { @@ -184,6 +189,20 @@ func WithDatabaseUrl(url string) Option { } } +// WithEnableTemplatedDatabase enables usage of a local database test instance +// with a template. Generally outside tests will probably want to use the +// non-templated database, so this defaults to enabling instead of disabling. +func WithEnableTemplatedDatabase(enable bool) Option { + return func(c *option) error { + c.setEnableTemplatedDatabase = enable + return nil + } +} + +type TestController struct { + *controller.TestController +} + // NewTestController blocks until a new TestController is created, returns the url for the TestController and a function // that can be called to tear down the controller after it has been used for testing. func NewTestController(t *testing.T, opt ...Option) *TestController { @@ -194,7 +213,3 @@ func NewTestController(t *testing.T, opt ...Option) *TestController { tc := controller.NewTestController(t, conf) return &TestController{TestController: tc} } - -type TestController struct { - *controller.TestController -}