diff --git a/api/hostsets/host_set.gen.go b/api/hostsets/host_set.gen.go index 36a8b6c274..612c9b81b3 100644 --- a/api/hostsets/host_set.gen.go +++ b/api/hostsets/host_set.gen.go @@ -14,20 +14,21 @@ import ( ) type HostSet struct { - Id string `json:"id,omitempty"` - HostCatalogId string `json:"host_catalog_id,omitempty"` - Scope *scopes.ScopeInfo `json:"scope,omitempty"` - Plugin *plugins.PluginInfo `json:"plugin,omitempty"` - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` - CreatedTime time.Time `json:"created_time,omitempty"` - UpdatedTime time.Time `json:"updated_time,omitempty"` - Version uint32 `json:"version,omitempty"` - Type string `json:"type,omitempty"` - HostIds []string `json:"host_ids,omitempty"` - PreferredEndpoints []string `json:"preferred_endpoints,omitempty"` - Attributes map[string]interface{} `json:"attributes,omitempty"` - AuthorizedActions []string `json:"authorized_actions,omitempty"` + Id string `json:"id,omitempty"` + HostCatalogId string `json:"host_catalog_id,omitempty"` + Scope *scopes.ScopeInfo `json:"scope,omitempty"` + Plugin *plugins.PluginInfo `json:"plugin,omitempty"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + CreatedTime time.Time `json:"created_time,omitempty"` + UpdatedTime time.Time `json:"updated_time,omitempty"` + Version uint32 `json:"version,omitempty"` + Type string `json:"type,omitempty"` + HostIds []string `json:"host_ids,omitempty"` + PreferredEndpoints []string `json:"preferred_endpoints,omitempty"` + SyncIntervalSeconds int32 `json:"sync_interval_seconds,omitempty"` + Attributes map[string]interface{} `json:"attributes,omitempty"` + AuthorizedActions []string `json:"authorized_actions,omitempty"` response *api.Response } diff --git a/api/hostsets/option.gen.go b/api/hostsets/option.gen.go index eafe5b7fa5..a2b25f4dd4 100644 --- a/api/hostsets/option.gen.go +++ b/api/hostsets/option.gen.go @@ -121,3 +121,15 @@ func DefaultPreferredEndpoints() Option { o.postMap["preferred_endpoints"] = nil } } + +func WithSyncIntervalSeconds(inSyncIntervalSeconds int32) Option { + return func(o *options) { + o.postMap["sync_interval_seconds"] = inSyncIntervalSeconds + } +} + +func DefaultSyncIntervalSeconds() Option { + return func(o *options) { + o.postMap["sync_interval_seconds"] = nil + } +} diff --git a/globals/fields.go b/globals/fields.go index 1987041b3d..6844111354 100644 --- a/globals/fields.go +++ b/globals/fields.go @@ -59,6 +59,7 @@ const ( ApplicationCredentialSourceIdsField = "application_credential_source_ids" ApplicationCredentialSourcesField = "application_credential_sources" PreferredEndpointsField = "preferred_endpoints" + SyncIntervalSecondsField = "sync_interval_seconds" PluginIdField = "plugin_id" PluginField = "plugin" PluginNameField = "plugin_name" diff --git a/internal/cmd/commands/hostsetscmd/funcs.go b/internal/cmd/commands/hostsetscmd/funcs.go index 532501bf71..c6477b38eb 100644 --- a/internal/cmd/commands/hostsetscmd/funcs.go +++ b/internal/cmd/commands/hostsetscmd/funcs.go @@ -215,6 +215,11 @@ func (c *Command) printListTable(items []*hostsets.HostSet) string { fmt.Sprintf(" Description: %s", item.Description), ) } + if item.SyncIntervalSeconds != 0 { + output = append(output, + fmt.Sprintf(" Sync Interval: %d seconds", item.SyncIntervalSeconds), + ) + } if len(item.AuthorizedActions) > 0 { output = append(output, " Authorized Actions:", @@ -256,6 +261,9 @@ func printItemTable(result api.GenericResult) string { if item.PreferredEndpoints != nil { nonAttributeMap["Preferred Endpoints"] = item.PreferredEndpoints } + if item.SyncIntervalSeconds != 0 { + nonAttributeMap["Sync Interval"] = fmt.Sprintf("%d seconds", item.SyncIntervalSeconds) + } maxLength := base.MaxAttributesLength(nonAttributeMap, item.Attributes, keySubstMap) diff --git a/internal/cmd/commands/hostsetscmd/plugin_funcs.go b/internal/cmd/commands/hostsetscmd/plugin_funcs.go index fca90f4443..5777357ce5 100644 --- a/internal/cmd/commands/hostsetscmd/plugin_funcs.go +++ b/internal/cmd/commands/hostsetscmd/plugin_funcs.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/boundary/api/hostsets" "github.com/hashicorp/boundary/internal/cmd/base" "github.com/hashicorp/boundary/internal/libs/endpoint" + "github.com/hashicorp/go-secure-stdlib/parseutil" ) func init() { @@ -16,12 +17,13 @@ func init() { type extraPluginCmdVars struct { flagPreferredEndpoints []string + flagSyncInterval string } func extraPluginActionsFlagsMapFuncImpl() map[string][]string { return map[string][]string{ - "create": {"preferred-endpoint"}, - "update": {"preferred-endpoint"}, + "create": {"preferred-endpoint", "sync-interval"}, + "update": {"preferred-endpoint", "sync-interval"}, } } @@ -68,8 +70,19 @@ func extraPluginFlagsFuncImpl(c *PluginCommand, set *base.FlagSets, f *base.Flag `or "dns:", specifying which IP address or DNS name out ` + `of a host's available possibilities should be preferred. May be specified ` + `multiple times, which will build up an in-order set of preferences. ` + - `If no preferences are specified, a value will be chosen _at random_ from ` + - `all available values. May not be valid for all plugin types.`, + `If no preferences are specified, a value will be chosen from among all ` + + `available values using a built-in priority order. May not be valid ` + + `for all plugin types.`, + }) + case "sync-interval": + fs.StringVar(&base.StringVar{ + Name: "sync-interval", + Target: &c.flagSyncInterval, + Usage: `An interger number of seconds, or a string such as "400s", "5m", or "6h", ` + + "indicating the amount of time that should elapse between syncs of the host set. " + + "The interval will be applied to the end of the previous sync operation, not the start. " + + "Setting to any negative value will disable syncing for that host set; setting to null " + + "will cause the set to use Boundary's default. The default may change between releases.", }) } } @@ -93,5 +106,19 @@ func extraPluginFlagsHandlingFuncImpl(c *PluginCommand, _ *base.FlagSets, opts * *opts = append(*opts, hostsets.WithPreferredEndpoints(c.flagPreferredEndpoints)) } + switch c.flagSyncInterval { + case "": + case "null": + *opts = append(*opts, hostsets.DefaultSyncIntervalSeconds()) + + default: + interval, err := parseutil.ParseDurationSecond(c.flagSyncInterval) + if err != nil { + c.UI.Error(fmt.Sprintf("Unable to successfully parse given sync interval: %s", err)) + return false + } + *opts = append(*opts, hostsets.WithSyncIntervalSeconds(int32(interval.Seconds()))) + } + return true } diff --git a/internal/db/read_writer.go b/internal/db/read_writer.go index 8287b2c5a6..26a3ebf436 100644 --- a/internal/db/read_writer.go +++ b/internal/db/read_writer.go @@ -1137,7 +1137,7 @@ func (rw *Db) LookupWhere(ctx context.Context, resource interface{}, where strin // // Supports the WithLimit option. If WithLimit < 0, then unlimited results are returned. // If WithLimit == 0, then default limits are used for results. -// Supports the WithOrder option. +// Supports the WithOrder and WithDebug options. func (rw *Db) SearchWhere(ctx context.Context, resources interface{}, where string, args []interface{}, opt ...Option) error { const op = "db.SearchWhere" opts := GetOpts(opt...) @@ -1155,6 +1155,9 @@ func (rw *Db) SearchWhere(ctx context.Context, resources interface{}, where stri if opts.withOrder != "" { db = db.Order(opts.withOrder) } + if opts.withDebug { + db = db.Debug() + } // Perform limiting switch { case opts.WithLimit < 0: // any negative number signals unlimited results diff --git a/internal/db/schema/migrations/oss/postgres/20/05_plugin_host.up.sql b/internal/db/schema/migrations/oss/postgres/20/05_plugin_host.up.sql index fefe23f65d..f212e1acaa 100644 --- a/internal/db/schema/migrations/oss/postgres/20/05_plugin_host.up.sql +++ b/internal/db/schema/migrations/oss/postgres/20/05_plugin_host.up.sql @@ -125,6 +125,11 @@ begin; update_time wt_timestamp, last_sync_time wt_timestamp, need_sync bool not null, + sync_interval_seconds int + constraint sync_interval_seconds_not_equal_zero + check(sync_interval_seconds != 0) + constraint sync_interval_seconds_not_less_then_negative_one + check(sync_interval_seconds >= -1), version wt_version, attributes bytea not null, constraint host_plugin_set_catalog_id_name_uq @@ -148,7 +153,7 @@ begin; for each row execute procedure default_create_time(); create trigger immutable_columns before update on host_plugin_set - for each row execute procedure immutable_columns('public_id', 'catalog_id','create_time'); + for each row execute procedure immutable_columns('public_id', 'catalog_id', 'create_time'); create trigger insert_host_set_subtype before insert on host_plugin_set for each row execute procedure insert_host_set_subtype(); diff --git a/internal/db/schema/migrations/oss/postgres/20/06_preferred_endpoints.up.sql b/internal/db/schema/migrations/oss/postgres/20/06_preferred_endpoints.up.sql index 94c8a4ad03..2a8ef89cca 100644 --- a/internal/db/schema/migrations/oss/postgres/20/06_preferred_endpoints.up.sql +++ b/internal/db/schema/migrations/oss/postgres/20/06_preferred_endpoints.up.sql @@ -60,6 +60,7 @@ create view host_plugin_host_set_with_value_obj as hs.update_time, hs.last_sync_time, hs.need_sync, + hs.sync_interval_seconds, hs.version, hs.attributes, -- the string_agg(..) column will be null if there are no associated value objects diff --git a/internal/gen/controller.swagger.json b/internal/gen/controller.swagger.json index 441143e832..983b3a9a70 100644 --- a/internal/gen/controller.swagger.json +++ b/internal/gen/controller.swagger.json @@ -4322,7 +4322,12 @@ "items": { "type": "string" }, - "description": "An ordered list of endpoint preferences used to choose from among\nmultiple possible endpoints for a host." + "description": "An ordered list of endpoint preferences used to choose from among\nmultiple possible endpoints for a host. Preferences are specified by\n\"cidr:\u003cvalid IPv4/6 CIDR\u003e\" or \"dns:\u003cglobbed name\u003e\", specifying which IP\naddress or DNS name out of a host's available possibilities should be\npreferred. If no preferences are specified, a value will be chosen from\namong all avialable values using a built-in priority order. May not be\nvalid for all plugin types." + }, + "sync_interval_seconds": { + "type": "integer", + "format": "int32", + "description": "An interger number of seconds indicating the amount of time that should\nelapse between syncs of the host set. The interval will be applied to the\nend of the previous sync operation, not the start. Setting to any\nnegative value will disable syncing for that host set; setting to zero\nwill cause the set to use Boundary's default. The default may change\nbetween releases. May not be valid for all plugin types." }, "attributes": { "type": "object", diff --git a/internal/host/plugin/host_set.go b/internal/host/plugin/host_set.go index 56efc07b7e..193d9b845f 100644 --- a/internal/host/plugin/host_set.go +++ b/internal/host/plugin/host_set.go @@ -17,9 +17,10 @@ import ( // A HostSet is a collection of hosts from the set's catalog. type HostSet struct { *store.HostSet - PluginId string `gorm:"-"` - HostIds []string `gorm:"-"` - tableName string `gorm:"-"` + PluginId string `gorm:"-"` + HostIds []string `gorm:"-"` + PreferredEndpoints []string `gorm:"-"` + tableName string `gorm:"-"` } // NewHostSet creates a new in memory HostSet assigned to catalogId. Attributes, @@ -35,12 +36,13 @@ func NewHostSet(ctx context.Context, catalogId string, opt ...Option) (*HostSet, set := &HostSet{ HostSet: &store.HostSet{ - CatalogId: catalogId, - Name: opts.withName, - Description: opts.withDescription, - Attributes: attrs, - PreferredEndpoints: opts.withPreferredEndpoints, + CatalogId: catalogId, + Name: opts.withName, + Description: opts.withDescription, + SyncIntervalSeconds: opts.withSyncIntervalSeconds, + Attributes: attrs, }, + PreferredEndpoints: opts.withPreferredEndpoints, } return set, nil @@ -69,7 +71,8 @@ func allocHostSet() *HostSet { func (s *HostSet) clone() *HostSet { cp := proto.Clone(s.HostSet) hs := &HostSet{ - HostSet: cp.(*store.HostSet), + HostSet: cp.(*store.HostSet), + PreferredEndpoints: s.PreferredEndpoints, } if s.Attributes != nil && len(s.Attributes) == 0 && hs.Attributes == nil { hs.Attributes = []byte{} @@ -92,19 +95,20 @@ func (s *HostSet) oplog(op oplog.OpType) oplog.Metadata { // hostSetAgg is a view that aggregates the host set's value objects in to // string fields delimited with the aggregateDelimiter of "|" type hostSetAgg struct { - PublicId string `gorm:"primary_key"` - CatalogId string - PluginId string - Name string - Description string - CreateTime *timestamp.Timestamp - UpdateTime *timestamp.Timestamp - LastSyncTime *timestamp.Timestamp - NeedSync bool - Version uint32 - Attributes []byte - PreferredEndpoints string - HostIds string + PublicId string `gorm:"primary_key"` + CatalogId string + PluginId string + Name string + Description string + CreateTime *timestamp.Timestamp + UpdateTime *timestamp.Timestamp + LastSyncTime *timestamp.Timestamp + NeedSync bool + SyncIntervalSeconds int32 + Version uint32 + Attributes []byte + PreferredEndpoints string + HostIds string } func (agg *hostSetAgg) toHostSet(ctx context.Context) (*HostSet, error) { @@ -121,6 +125,7 @@ func (agg *hostSetAgg) toHostSet(ctx context.Context) (*HostSet, error) { hs.UpdateTime = agg.UpdateTime hs.LastSyncTime = agg.LastSyncTime hs.NeedSync = agg.NeedSync + hs.SyncIntervalSeconds = agg.SyncIntervalSeconds hs.Version = agg.Version hs.Attributes = agg.Attributes if agg.HostIds != "" { diff --git a/internal/host/plugin/host_set_test.go b/internal/host/plugin/host_set_test.go index ac9a0d4cb6..ae9144cd58 100644 --- a/internal/host/plugin/host_set_test.go +++ b/internal/host/plugin/host_set_test.go @@ -150,10 +150,10 @@ func TestHostSet_Create(t *testing.T) { }, want: &HostSet{ HostSet: &store.HostSet{ - CatalogId: cat.GetPublicId(), - PreferredEndpoints: []string{"cidr:1.2.3.4"}, - Attributes: []byte{}, + CatalogId: cat.GetPublicId(), + Attributes: []byte{}, }, + PreferredEndpoints: []string{"cidr:1.2.3.4"}, }, }, } diff --git a/internal/host/plugin/job_set_sync.go b/internal/host/plugin/job_set_sync.go index 3f7fd6cbfa..1b63da46c2 100644 --- a/internal/host/plugin/job_set_sync.go +++ b/internal/host/plugin/job_set_sync.go @@ -96,7 +96,7 @@ func (r *SetSyncJob) Run(ctx context.Context) error { // Fetch all sets that will reach their sync point within the syncWindow. // This is done to avoid constantly scheduling the set sync job when there // are multiple sets to sync in sequence. - err := r.reader.SearchWhere(ctx, &setAggs, `need_sync or last_sync_time <= wt_add_seconds_to_now(?)`, []interface{}{-1 * setSyncJobRunInterval.Seconds()}, db.WithLimit(r.limit)) + err := r.reader.SearchWhere(ctx, &setAggs, setSyncJobQuery, []interface{}{-1 * setSyncJobRunInterval.Seconds()}, db.WithLimit(r.limit)) if err != nil { return errors.Wrap(ctx, err, op) } @@ -152,23 +152,35 @@ func nextSync(j scheduler.Job) (time.Duration, error) { } defer rows.Close() - for rows.Next() { - type NextResync struct { - SyncNow bool - ResyncIn time.Duration - } - var n NextResync - err = r.ScanRows(rows, &n) - if err != nil { - return 0, errors.WrapDeprecated(err, op) - } - if n.SyncNow || n.ResyncIn < 0 { - // If we are past the next renewal time, return 0 to schedule immediately - return 0, nil - } - return n.ResyncIn * time.Second, nil + if !rows.Next() { + return setSyncJobRunInterval, nil + } + + type NextResync struct { + SyncNow bool + SyncIntervalSeconds int32 + ResyncIn time.Duration } - return setSyncJobRunInterval, nil + var n NextResync + err = r.ScanRows(rows, &n) + if err != nil { + return 0, errors.WrapDeprecated(err, op) + } + switch { + case n.SyncNow: + // Immediate + return 0, nil + case n.SyncIntervalSeconds < 0: + // In this case automatic syncing is disabled; we still sync if SyncNow + // but otherwise do not. We schedule the job at the default cadence but + // it will do nothing, just calculate a next run time to ensure it + // should stay disabled. + return setSyncJobRunInterval, nil + case n.ResyncIn < 0: + // Immediate + return 0, nil + } + return n.ResyncIn * time.Second, nil } // syncSets retrieves from their plugins all the host and membership information @@ -226,7 +238,7 @@ func (r *SetSyncJob) syncSets(ctx context.Context, setIds []string) error { if !ok { si = &setInfo{} } - si.preferredEndpoint = endpoint.WithPreferenceOrder(s.GetPreferredEndpoints()) + si.preferredEndpoint = endpoint.WithPreferenceOrder(s.PreferredEndpoints) si.plgSet, err = toPluginSet(ctx, s) if err != nil { return errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("converting set %q to plugin set", s.GetPublicId()))) @@ -288,7 +300,7 @@ func (r *SetSyncJob) syncSets(ctx context.Context, setIds []string) error { } if _, err := r.upsertHosts(ctx, ci.storeCat, catSetIds, resp.GetHosts()); err != nil { - errors.Wrap(ctx, err, op, errors.WithMsg("upserting hosts")) + return errors.Wrap(ctx, err, op, errors.WithMsg("upserting hosts")) } updateSyncDataQuery := ` diff --git a/internal/host/plugin/job_set_sync_test.go b/internal/host/plugin/job_set_sync_test.go index 5eb3cb5103..66e8705d5b 100644 --- a/internal/host/plugin/job_set_sync_test.go +++ b/internal/host/plugin/job_set_sync_test.go @@ -13,8 +13,8 @@ import ( hostplg "github.com/hashicorp/boundary/internal/plugin/host" "github.com/hashicorp/boundary/internal/scheduler" plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + assertpkg "github.com/stretchr/testify/assert" + requirepkg "github.com/stretchr/testify/require" ) func TestNewSetSyncJob(t *testing.T) { @@ -109,7 +109,7 @@ func TestNewSetSyncJob(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - assert, require := assert.New(t), require.New(t) + assert, require := assertpkg.New(t), requirepkg.New(t) got, err := newSetSyncJob(ctx, tt.args.r, tt.args.w, tt.args.kms, tt.args.plgm, tt.options...) if tt.wantErr { @@ -130,7 +130,7 @@ func TestNewSetSyncJob(t *testing.T) { func TestSetSyncJob_Run(t *testing.T) { t.Parallel() - assert, require := assert.New(t), require.New(t) + assert, require := assertpkg.New(t), requirepkg.New(t) ctx := context.Background() conn, _ := db.TestSetup(t, "postgres") rw := db.New(conn) @@ -215,6 +215,136 @@ func TestSetSyncJob_Run(t *testing.T) { require.NoError(rw.LookupByPublicId(ctx, hs)) assert.Greater(hs.GetLastSyncTime().AsTime().UnixNano(), firstSyncTime.AsTime().UnixNano()) assert.False(hs.GetNeedSync()) + + // Now, run a battery of tests with values for SyncIntervalSeconds + type setArgs struct { + syncIntervalSeconds int32 + lastSyncTime *timestamp.Timestamp + needsSync bool + } + tests := []struct { + name string + setArgs setArgs + expectSync bool + }{ + { + name: "never-synced-before-needs-sync-false", + setArgs: setArgs{ + lastSyncTime: timestamp.New(time.Unix(0, 0)), + needsSync: false, + }, + expectSync: true, + }, + { + name: "never-synced-before-needs-sync-true", + setArgs: setArgs{ + lastSyncTime: timestamp.New(time.Unix(0, 0)), + needsSync: true, + }, + expectSync: true, + }, + { + name: "never-synced-before-sync-disabled", + setArgs: setArgs{ + syncIntervalSeconds: -1, + lastSyncTime: timestamp.New(time.Unix(0, 0)), + needsSync: true, + }, + expectSync: true, + }, + { + name: "synced-just-now", + setArgs: setArgs{ + lastSyncTime: timestamp.Now(), + needsSync: false, + }, + expectSync: false, + }, + { + name: "synced-just-now-need-sync", + setArgs: setArgs{ + lastSyncTime: timestamp.Now(), + needsSync: true, + }, + expectSync: true, + }, + { + name: "synced-just-now-need-sync-but-sync-disabled", + setArgs: setArgs{ + syncIntervalSeconds: -1, + lastSyncTime: timestamp.Now(), + needsSync: true, + }, + expectSync: true, + }, + { + name: "synced-30-seconds-ago-default-time", + setArgs: setArgs{ + lastSyncTime: timestamp.New(time.Now().Add(-60 * time.Second)), + needsSync: false, + }, + expectSync: false, + }, + { + name: "synced-30-seconds-ago-custom-time", + setArgs: setArgs{ + syncIntervalSeconds: 5, + lastSyncTime: timestamp.New(time.Now().Add(-60 * time.Second)), + needsSync: false, + }, + expectSync: true, + }, + { + name: "synced-30-seconds-ago-custom-larger-time", + setArgs: setArgs{ + syncIntervalSeconds: 90, + lastSyncTime: timestamp.New(time.Now().Add(-60 * time.Second)), + needsSync: false, + }, + expectSync: false, + }, + { + name: "synced-30-seconds-ago-custom-larger-time-need-sync", + setArgs: setArgs{ + syncIntervalSeconds: 60, + lastSyncTime: timestamp.New(time.Now().Add(-60 * time.Second)), + needsSync: true, + }, + expectSync: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert, require := assertpkg.New(t), requirepkg.New(t) + + // Update set + hs.LastSyncTime = tt.setArgs.lastSyncTime + hs.NeedSync = tt.setArgs.needsSync + hs.SyncIntervalSeconds = tt.setArgs.syncIntervalSeconds + fieldMaskPaths := []string{"LastSyncTime", "NeedSync"} + var setToNullPaths []string + if hs.SyncIntervalSeconds == 0 { + setToNullPaths = []string{"SyncIntervalSeconds"} + } else { + fieldMaskPaths = append(fieldMaskPaths, "SyncIntervalSeconds") + } + count, err := rw.Update(ctx, hs, fieldMaskPaths, setToNullPaths) + require.NoError(err) + assert.Equal(1, count) + + // Run job + err = r.Run(context.Background()) + require.NoError(err) + + // Validate results + var expNum int + if tt.expectSync { + expNum = 1 + } + assert.Equal(expNum, r.numSets) + assert.Equal(expNum, r.numProcessed) + }) + } } func TestSetSyncJob_NextRunIn(t *testing.T) { @@ -235,8 +365,9 @@ func TestSetSyncJob_NextRunIn(t *testing.T) { hostSet := TestSet(t, conn, kmsCache, sched, catalog, plgm) type setArgs struct { - lastSyncTime *timestamp.Timestamp - needsSync bool + syncIntervalSeconds int32 + lastSyncTime *timestamp.Timestamp + needsSync bool } tests := []struct { name string @@ -252,6 +383,15 @@ func TestSetSyncJob_NextRunIn(t *testing.T) { }, want: 0, }, + { + name: "never-synced-before-with-sync-interval", + setArgs: setArgs{ + syncIntervalSeconds: 60, + lastSyncTime: timestamp.New(time.Unix(0, 0)), + needsSync: false, + }, + want: 0, + }, { name: "synced-just-now", setArgs: setArgs{ @@ -260,6 +400,15 @@ func TestSetSyncJob_NextRunIn(t *testing.T) { }, want: setSyncJobRunInterval, }, + { + name: "synced-just-now-with-sync-interval", + setArgs: setArgs{ + syncIntervalSeconds: 180, + lastSyncTime: timestamp.Now(), + needsSync: false, + }, + want: 3 * time.Minute, + }, { name: "synced-just-now-need-sync", setArgs: setArgs{ @@ -268,24 +417,67 @@ func TestSetSyncJob_NextRunIn(t *testing.T) { }, want: 0, }, + { + name: "synced-just-now-need-sync-with-sync-interval", + setArgs: setArgs{ + syncIntervalSeconds: 60, + lastSyncTime: timestamp.Now(), + needsSync: true, + }, + want: 0, + }, + { + name: "synced-a-bit-ago", + setArgs: setArgs{ + lastSyncTime: timestamp.New(time.Now().Add(-4 * time.Minute)), + needsSync: false, + }, + want: time.Until(time.Now().Add(setSyncJobRunInterval - (4 * time.Minute))), + }, + { + name: "synced-a-bit-ago-with-sync-interval", + setArgs: setArgs{ + syncIntervalSeconds: 300, + lastSyncTime: timestamp.New(time.Now().Add(-4 * time.Minute)), + needsSync: false, + }, + want: time.Minute, + }, + { + name: "automatic-sync-disabled", + setArgs: setArgs{ + syncIntervalSeconds: -1, + lastSyncTime: timestamp.New(time.Now().Add(-4 * time.Minute)), + needsSync: false, + }, + want: setSyncJobRunInterval, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - assert, require := assert.New(t), require.New(t) + assert, require := assertpkg.New(t), requirepkg.New(t) r, err := newSetSyncJob(ctx, rw, rw, kmsCache, plgm) assert.NoError(err) require.NotNil(r) hostSet.NeedSync = tt.setArgs.needsSync hostSet.LastSyncTime = tt.setArgs.lastSyncTime - _, err = rw.Update(ctx, hostSet, []string{"LastSyncTime", "NeedSync"}, nil) + hostSet.SyncIntervalSeconds = tt.setArgs.syncIntervalSeconds + fieldMaskPaths := []string{"LastSyncTime", "NeedSync"} + var setToNullPaths []string + if hostSet.SyncIntervalSeconds == 0 { + setToNullPaths = []string{"SyncIntervalSeconds"} + } else { + fieldMaskPaths = append(fieldMaskPaths, "SyncIntervalSeconds") + } + _, err = rw.Update(ctx, hostSet, fieldMaskPaths, setToNullPaths) require.NoError(err) got, err := r.NextRunIn() require.NoError(err) - // Round to time.Minute to account for lost time between updating set and determining next run - assert.Equal(tt.want.Round(time.Minute), got.Round(time.Minute)) + // Round to five seconds to account for lost time between updating set and determining next run + assert.Equal(tt.want.Round(5*time.Second), got.Round(5*time.Second)) }) } } diff --git a/internal/host/plugin/options.go b/internal/host/plugin/options.go index e84d85702a..6536cebcf1 100644 --- a/internal/host/plugin/options.go +++ b/internal/host/plugin/options.go @@ -16,17 +16,18 @@ type Option func(*options) // options = how options are represented type options struct { - withPublicId string - withPluginId string - withName string - withDescription string - withAttributes *structpb.Struct - withSecrets *structpb.Struct - withPreferredEndpoints []string - withIpAddresses []string - withDnsNames []string - withLimit int - withSetIds []string + withPublicId string + withPluginId string + withName string + withDescription string + withAttributes *structpb.Struct + withSecrets *structpb.Struct + withPreferredEndpoints []string + withSyncIntervalSeconds int32 + withIpAddresses []string + withDnsNames []string + withLimit int + withSetIds []string } func getDefaultOptions() options { @@ -84,6 +85,13 @@ func WithPreferredEndpoints(with []string) Option { } } +// WithSyncIntervalSeconds provides an optional sync interval, in seconds +func WithSyncIntervalSeconds(with int32) Option { + return func(o *options) { + o.withSyncIntervalSeconds = with + } +} + // withIpAddresses provides an optional list of ip addresses. func withIpAddresses(with []string) Option { return func(o *options) { diff --git a/internal/host/plugin/options_test.go b/internal/host/plugin/options_test.go index fce504999b..e55321cf11 100644 --- a/internal/host/plugin/options_test.go +++ b/internal/host/plugin/options_test.go @@ -20,6 +20,12 @@ func Test_GetOpts(t *testing.T) { testOpts.withName = "test" assert.Equal(t, opts, testOpts) }) + t.Run("WithSyncIntervalSeconds", func(t *testing.T) { + opts := getOpts(WithSyncIntervalSeconds(5)) + testOpts := getDefaultOptions() + testOpts.withSyncIntervalSeconds = 5 + assert.Equal(t, opts, testOpts) + }) t.Run("WithPluginId", func(t *testing.T) { opts := getOpts(withPluginId("test")) testOpts := getDefaultOptions() diff --git a/internal/host/plugin/query.go b/internal/host/plugin/query.go index 792f4b4abd..700a126cc5 100644 --- a/internal/host/plugin/query.go +++ b/internal/host/plugin/query.go @@ -19,10 +19,28 @@ delete from host_plugin_catalog_secret setSyncNextRunInQuery = ` select - need_sync as sync_now, - extract(epoch from (least(now(), last_sync_time) + ?) - now())::int as resync_in + need_sync as sync_now, + sync_interval_seconds, + case + when sync_interval_seconds is null + then + extract(epoch from (least(now(), last_sync_time) + ?) - now())::int + when sync_interval_seconds > 0 + then + extract(epoch from (least(now(), last_sync_time)) - now())::int + sync_interval_seconds + else + 0 + end resync_in from host_plugin_set order by need_sync desc, last_sync_time desc limit 1; +` + + setSyncJobQuery = ` +need_sync + or +sync_interval_seconds is null and last_sync_time <= wt_add_seconds_to_now(?) + or +sync_interval_seconds > 0 and wt_add_seconds(sync_interval_seconds, last_sync_time) <= current_timestamp ` ) diff --git a/internal/host/plugin/repository_host_set.go b/internal/host/plugin/repository_host_set.go index a4f2bfd94b..7461155937 100644 --- a/internal/host/plugin/repository_host_set.go +++ b/internal/host/plugin/repository_host_set.go @@ -49,6 +49,9 @@ func (r *Repository) CreateSet(ctx context.Context, scopeId string, s *HostSet, if scopeId == "" { return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "no scope id") } + if s.SyncIntervalSeconds < -1 { + return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "invalid sync interval") + } if s.Attributes == nil { return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "nil attributes") } @@ -779,7 +782,7 @@ func (r *Repository) Endpoints(ctx context.Context, setIds []string) ([]*host.En h := ha.toHost() for _, sId := range hostIdToSetIds[h.GetPublicId()] { s := setIdToSet[sId] - pref, err := endpoint.NewPreferencer(ctx, endpoint.WithPreferenceOrder(s.GetPreferredEndpoints())) + pref, err := endpoint.NewPreferencer(ctx, endpoint.WithPreferenceOrder(s.PreferredEndpoints)) if err != nil { return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("getting preferencer for set %q", sId))) } diff --git a/internal/host/plugin/repository_host_set_test.go b/internal/host/plugin/repository_host_set_test.go index c3d71e9143..5142418814 100644 --- a/internal/host/plugin/repository_host_set_test.go +++ b/internal/host/plugin/repository_host_set_test.go @@ -46,12 +46,12 @@ func TestRepository_CreateSet(t *testing.T) { attrs := []byte{} tests := []struct { - name string - in *HostSet - opts []Option - want *HostSet + name string + in *HostSet + opts []Option + want *HostSet wantPluginCalled bool - wantIsErr errors.Code + wantIsErr errors.Code }{ { name: "nil-HostSet", @@ -91,6 +91,17 @@ func TestRepository_CreateSet(t *testing.T) { }, wantIsErr: errors.InvalidParameter, }, + { + name: "invalid-sync-interval-too-negative", + in: &HostSet{ + HostSet: &store.HostSet{ + CatalogId: catalog.PublicId, + SyncIntervalSeconds: -99, + Attributes: attrs, + }, + }, + wantIsErr: errors.InvalidParameter, + }, { name: "valid-no-options", in: &HostSet{ @@ -111,17 +122,17 @@ func TestRepository_CreateSet(t *testing.T) { name: "valid-preferred-endpoints", in: &HostSet{ HostSet: &store.HostSet{ - CatalogId: catalog.PublicId, - Attributes: attrs, - PreferredEndpoints: []string{"cidr:1.2.3.4/32", "dns:a.b.c"}, + CatalogId: catalog.PublicId, + Attributes: attrs, }, + PreferredEndpoints: []string{"cidr:1.2.3.4/32", "dns:a.b.c"}, }, want: &HostSet{ HostSet: &store.HostSet{ - CatalogId: catalog.PublicId, - Attributes: attrs, - PreferredEndpoints: []string{"cidr:1.2.3.4/32", "dns:a.b.c"}, + CatalogId: catalog.PublicId, + Attributes: attrs, }, + PreferredEndpoints: []string{"cidr:1.2.3.4/32", "dns:a.b.c"}, }, wantPluginCalled: true, }, @@ -143,6 +154,42 @@ func TestRepository_CreateSet(t *testing.T) { }, wantPluginCalled: true, }, + { + name: "valid-sync-interval-disabled", + in: &HostSet{ + HostSet: &store.HostSet{ + CatalogId: catalog.PublicId, + SyncIntervalSeconds: -1, + Attributes: attrs, + }, + }, + want: &HostSet{ + HostSet: &store.HostSet{ + CatalogId: catalog.PublicId, + SyncIntervalSeconds: -1, + Attributes: attrs, + }, + }, + wantPluginCalled: true, + }, + { + name: "valid-sync-interval-positive", + in: &HostSet{ + HostSet: &store.HostSet{ + CatalogId: catalog.PublicId, + SyncIntervalSeconds: 60, + Attributes: attrs, + }, + }, + want: &HostSet{ + HostSet: &store.HostSet{ + CatalogId: catalog.PublicId, + SyncIntervalSeconds: 60, + Attributes: attrs, + }, + }, + wantPluginCalled: true, + }, { name: "valid-unimplemented-plugin", in: &HostSet{ @@ -187,7 +234,7 @@ func TestRepository_CreateSet(t *testing.T) { Description: ("test-description-repo"), Attributes: func() []byte { st, err := structpb.NewStruct(map[string]interface{}{ - "k1": "foo", + "k1": "foo", "removed": nil, }) require.NoError(t, err) @@ -1129,7 +1176,7 @@ func TestRepository_LookupSet(t *testing.T) { return &plgpb.ListHostsResponse{}, nil }, }), - }) + }, WithSyncIntervalSeconds(5)) hostSetId, err := newHostSetId(ctx) require.NoError(t, err) @@ -1192,9 +1239,9 @@ func TestRepository_Endpoints(t *testing.T) { } catalog := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId()) - hostSet10 := TestSet(t, conn, kms, sched, catalog, plgm, WithPreferredEndpoints([]string{"cidr:10.0.0.1/24"})) - hostSet192 := TestSet(t, conn, kms, sched, catalog, plgm, WithPreferredEndpoints([]string{"cidr:192.168.0.1/24"})) - hostSet100 := TestSet(t, conn, kms, sched, catalog, plgm, WithPreferredEndpoints([]string{"cidr:100.100.100.100/24"})) + hostSet10 := TestSet(t, conn, kms, sched, catalog, plgm, WithName("hostSet10"), WithPreferredEndpoints([]string{"cidr:10.0.0.1/24"})) + hostSet192 := TestSet(t, conn, kms, sched, catalog, plgm, WithName("hostSet192"), WithPreferredEndpoints([]string{"cidr:192.168.0.1/24"})) + hostSet100 := TestSet(t, conn, kms, sched, catalog, plgm, WithName("hostSet100"), WithPreferredEndpoints([]string{"cidr:100.100.100.100/24"})) hostlessSet := TestSet(t, conn, kms, sched, hostlessCatalog, plgm) h1 := TestHost(t, conn, catalog.GetPublicId(), "test", withIpAddresses([]string{"10.0.0.5", "192.168.0.5"})) diff --git a/internal/host/plugin/store/host.pb.go b/internal/host/plugin/store/host.pb.go index 8b61078c5e..3100beb713 100644 --- a/internal/host/plugin/store/host.pb.go +++ b/internal/host/plugin/store/host.pb.go @@ -195,6 +195,9 @@ type HostSet struct { // preferred_endpoints stores string preference values // @inject_tag: `gorm:"-"` PreferredEndpoints []string `protobuf:"bytes,11,rep,name=preferred_endpoints,json=preferredEndpoints,proto3" json:"preferred_endpoints,omitempty" gorm:"-"` + // Sync interval is a value representing a duration in seconds + // @inject_tag: `gorm:"default:null"` + SyncIntervalSeconds int32 `protobuf:"varint,12,opt,name=sync_interval_seconds,json=syncIntervalSeconds,proto3" json:"sync_interval_seconds,omitempty" gorm:"default:null"` } func (x *HostSet) Reset() { @@ -306,6 +309,13 @@ func (x *HostSet) GetPreferredEndpoints() []string { return nil } +func (x *HostSet) GetSyncIntervalSeconds() int32 { + if x != nil { + return x.SyncIntervalSeconds + } + return 0 +} + type HostCatalogSecret struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -659,7 +669,7 @@ var file_controller_storage_host_plugin_store_v1_host_proto_rawDesc = []byte{ 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x61, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0xa1, 0x04, 0x0a, 0x07, 0x48, 0x6f, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0x87, 0x05, 0x0a, 0x07, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, @@ -693,61 +703,67 @@ var file_controller_storage_host_plugin_store_v1_host_proto_rawDesc = []byte{ 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x13, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x70, 0x72, 0x65, 0x66, 0x65, - 0x72, 0x72, 0x65, 0x64, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x22, 0x98, 0x02, - 0x0a, 0x11, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x53, 0x65, 0x63, - 0x72, 0x65, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, - 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x72, 0x72, 0x65, 0x64, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x64, 0x0a, + 0x15, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, + 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05, 0x42, 0x30, 0xc2, 0xdd, + 0x29, 0x2c, 0x0a, 0x13, 0x53, 0x79, 0x6e, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, + 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x15, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x52, 0x13, + 0x73, 0x79, 0x6e, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x6f, + 0x6e, 0x64, 0x73, 0x22, 0x98, 0x02, 0x0a, 0x11, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, + 0x6c, 0x6f, 0x67, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, + 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, + 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, + 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, + 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x74, + 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, + 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, + 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x22, 0x8d, + 0x03, 0x0a, 0x04, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, + 0x63, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, + 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, + 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, - 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, - 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, - 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x65, - 0x63, 0x72, 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x22, 0x8d, 0x03, 0x0a, 0x04, 0x48, 0x6f, 0x73, - 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x1f, - 0x0a, 0x0b, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x64, 0x12, - 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, - 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, - 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, - 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, - 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x12, 0x18, - 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x70, 0x5f, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, - 0x69, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x64, - 0x6e, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, - 0x64, 0x6e, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x5e, 0x0a, 0x0d, 0x48, 0x6f, 0x73, 0x74, - 0x53, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x6f, 0x73, - 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x68, 0x6f, 0x73, 0x74, - 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x73, 0x65, 0x74, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, - 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, - 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x42, 0x40, 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, - 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x73, - 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, + 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, + 0x6f, 0x67, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, + 0x0a, 0x0c, 0x69, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x09, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, + 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x6e, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x0a, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x64, 0x6e, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x5e, + 0x0a, 0x0d, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, + 0x17, 0x0a, 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x68, 0x6f, 0x73, 0x74, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x73, 0x65, 0x74, 0x5f, + 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x65, 0x74, 0x49, 0x64, 0x12, + 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x42, 0x40, + 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, + 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2f, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/internal/host/static/store/static.pb.go b/internal/host/static/store/static.pb.go index 6388b485b2..aad9a2418d 100644 --- a/internal/host/static/store/static.pb.go +++ b/internal/host/static/store/static.pb.go @@ -433,6 +433,56 @@ func (x *HostSetMember) GetCatalogId() string { return "" } +// These fields are not implemented yet on the host set. They are captured +// here for the purpose of identifying the mask maps which are on the top level +// api set resource but aren't present in the static host set storage. +type UnimplementedSetFields struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + SyncIntervalSeconds int32 `protobuf:"varint,22,opt,name=sync_interval_seconds,json=syncIntervalSeconds,proto3" json:"sync_interval_seconds,omitempty"` +} + +func (x *UnimplementedSetFields) Reset() { + *x = UnimplementedSetFields{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_storage_host_static_store_v1_static_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UnimplementedSetFields) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UnimplementedSetFields) ProtoMessage() {} + +func (x *UnimplementedSetFields) ProtoReflect() protoreflect.Message { + mi := &file_controller_storage_host_static_store_v1_static_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UnimplementedSetFields.ProtoReflect.Descriptor instead. +func (*UnimplementedSetFields) Descriptor() ([]byte, []int) { + return file_controller_storage_host_static_store_v1_static_proto_rawDescGZIP(), []int{4} +} + +func (x *UnimplementedSetFields) GetSyncIntervalSeconds() int32 { + if x != nil { + return x.SyncIntervalSeconds + } + return 0 +} + var File_controller_storage_host_static_store_v1_static_proto protoreflect.FileDescriptor var file_controller_storage_host_static_store_v1_static_proto_rawDesc = []byte{ @@ -524,7 +574,15 @@ var file_controller_storage_host_static_store_v1_static_proto_rawDesc = []byte{ 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x65, 0x74, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, - 0x49, 0x64, 0x42, 0x40, 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x49, 0x64, 0x22, 0x7e, 0x0a, 0x16, 0x55, 0x6e, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x65, 0x64, 0x53, 0x65, 0x74, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x64, 0x0a, 0x15, + 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, 0x65, + 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x16, 0x20, 0x01, 0x28, 0x05, 0x42, 0x30, 0xc2, 0xdd, 0x29, + 0x2c, 0x0a, 0x13, 0x53, 0x79, 0x6e, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x53, + 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x15, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x52, 0x13, 0x73, + 0x79, 0x6e, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x6f, 0x6e, + 0x64, 0x73, 0x42, 0x40, 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, @@ -543,21 +601,22 @@ func file_controller_storage_host_static_store_v1_static_proto_rawDescGZIP() []b return file_controller_storage_host_static_store_v1_static_proto_rawDescData } -var file_controller_storage_host_static_store_v1_static_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_controller_storage_host_static_store_v1_static_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_controller_storage_host_static_store_v1_static_proto_goTypes = []interface{}{ - (*HostCatalog)(nil), // 0: controller.storage.host.static.store.v1.HostCatalog - (*Host)(nil), // 1: controller.storage.host.static.store.v1.Host - (*HostSet)(nil), // 2: controller.storage.host.static.store.v1.HostSet - (*HostSetMember)(nil), // 3: controller.storage.host.static.store.v1.HostSetMember - (*timestamp.Timestamp)(nil), // 4: controller.storage.timestamp.v1.Timestamp + (*HostCatalog)(nil), // 0: controller.storage.host.static.store.v1.HostCatalog + (*Host)(nil), // 1: controller.storage.host.static.store.v1.Host + (*HostSet)(nil), // 2: controller.storage.host.static.store.v1.HostSet + (*HostSetMember)(nil), // 3: controller.storage.host.static.store.v1.HostSetMember + (*UnimplementedSetFields)(nil), // 4: controller.storage.host.static.store.v1.UnimplementedSetFields + (*timestamp.Timestamp)(nil), // 5: controller.storage.timestamp.v1.Timestamp } var file_controller_storage_host_static_store_v1_static_proto_depIdxs = []int32{ - 4, // 0: controller.storage.host.static.store.v1.HostCatalog.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 4, // 1: controller.storage.host.static.store.v1.HostCatalog.update_time:type_name -> controller.storage.timestamp.v1.Timestamp - 4, // 2: controller.storage.host.static.store.v1.Host.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 4, // 3: controller.storage.host.static.store.v1.Host.update_time:type_name -> controller.storage.timestamp.v1.Timestamp - 4, // 4: controller.storage.host.static.store.v1.HostSet.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 4, // 5: controller.storage.host.static.store.v1.HostSet.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 5, // 0: controller.storage.host.static.store.v1.HostCatalog.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 5, // 1: controller.storage.host.static.store.v1.HostCatalog.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 5, // 2: controller.storage.host.static.store.v1.Host.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 5, // 3: controller.storage.host.static.store.v1.Host.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 5, // 4: controller.storage.host.static.store.v1.HostSet.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 5, // 5: controller.storage.host.static.store.v1.HostSet.update_time:type_name -> controller.storage.timestamp.v1.Timestamp 6, // [6:6] is the sub-list for method output_type 6, // [6:6] is the sub-list for method input_type 6, // [6:6] is the sub-list for extension type_name @@ -619,6 +678,18 @@ func file_controller_storage_host_static_store_v1_static_proto_init() { return nil } } + file_controller_storage_host_static_store_v1_static_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UnimplementedSetFields); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -626,7 +697,7 @@ func file_controller_storage_host_static_store_v1_static_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_controller_storage_host_static_store_v1_static_proto_rawDesc, NumEnums: 0, - NumMessages: 4, + NumMessages: 5, NumExtensions: 0, NumServices: 0, }, diff --git a/internal/libs/patchstruct/patchstruct_test.go b/internal/libs/patchstruct/patchstruct_test.go index c7a972ba21..efb8f192f8 100644 --- a/internal/libs/patchstruct/patchstruct_test.go +++ b/internal/libs/patchstruct/patchstruct_test.go @@ -81,7 +81,7 @@ var testCases = []testCase{ }, { name: "nested with nil", - dst: nil, + dst: nil, src: map[string]interface{}{ "a": "b", "nested": map[string]interface{}{ diff --git a/internal/proto/local/controller/api/resources/hostsets/v1/host_set.proto b/internal/proto/local/controller/api/resources/hostsets/v1/host_set.proto index cb28046e02..126f733927 100644 --- a/internal/proto/local/controller/api/resources/hostsets/v1/host_set.proto +++ b/internal/proto/local/controller/api/resources/hostsets/v1/host_set.proto @@ -48,9 +48,22 @@ message HostSet { repeated string host_ids = 100 [json_name="host_ids"]; // An ordered list of endpoint preferences used to choose from among - // multiple possible endpoints for a host. + // multiple possible endpoints for a host. Preferences are specified by + // "cidr:" or "dns:", specifying which IP + // address or DNS name out of a host's available possibilities should be + // preferred. If no preferences are specified, a value will be chosen from + // among all avialable values using a built-in priority order. May not be + // valid for all plugin types. repeated string preferred_endpoints = 101 [json_name="preferred_endpoints", (custom_options.v1.generate_sdk_option) = true]; + // An interger number of seconds indicating the amount of time that should + // elapse between syncs of the host set. The interval will be applied to the + // end of the previous sync operation, not the start. Setting to any + // negative value will disable syncing for that host set; setting to zero + // will cause the set to use Boundary's default. The default may change + // between releases. May not be valid for all plugin types. + google.protobuf.Int32Value sync_interval_seconds = 102 [json_name="sync_interval_seconds", (custom_options.v1.generate_sdk_option) = true, (custom_options.v1.mask_mapping) = {this:"sync_interval_seconds" that: "SyncIntervalSeconds"}]; + // The attributes that are applicable for the specific Host Set type. google.protobuf.Struct attributes = 110 [(custom_options.v1.generate_sdk_option) = true]; diff --git a/internal/proto/local/controller/storage/host/plugin/store/v1/host.proto b/internal/proto/local/controller/storage/host/plugin/store/v1/host.proto index 645cc0c045..5fa3c8337c 100644 --- a/internal/proto/local/controller/storage/host/plugin/store/v1/host.proto +++ b/internal/proto/local/controller/storage/host/plugin/store/v1/host.proto @@ -93,6 +93,10 @@ message HostSet { // preferred_endpoints stores string preference values // @inject_tag: `gorm:"-"` repeated string preferred_endpoints = 11; + + // Sync interval is a value representing a duration in seconds + // @inject_tag: `gorm:"default:null"` + int32 sync_interval_seconds = 12 [(custom_options.v1.mask_mapping) = {this: "SyncIntervalSeconds" that: "sync_interval_seconds"}]; } message HostCatalogSecret { diff --git a/internal/proto/local/controller/storage/host/static/store/v1/static.proto b/internal/proto/local/controller/storage/host/static/store/v1/static.proto index 13895f7b58..daf3849da8 100644 --- a/internal/proto/local/controller/storage/host/static/store/v1/static.proto +++ b/internal/proto/local/controller/storage/host/static/store/v1/static.proto @@ -117,3 +117,10 @@ message HostSetMember { // @inject_tag: `gorm:"default:null"` string catalog_id = 3; } + +// These fields are not implemented yet on the host set. They are captured +// here for the purpose of identifying the mask maps which are on the top level +// api set resource but aren't present in the static host set storage. +message UnimplementedSetFields { + int32 sync_interval_seconds = 22 [(custom_options.v1.mask_mapping) = {this: "SyncIntervalSeconds" that: "sync_interval_seconds"}]; +} \ No newline at end of file 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 1bd93bf6e4..36acd7c732 100644 --- a/internal/servers/controller/handlers/host_sets/host_set_service.go +++ b/internal/servers/controller/handlers/host_sets/host_set_service.go @@ -11,7 +11,7 @@ import ( "github.com/hashicorp/boundary/internal/host/plugin" plugstore "github.com/hashicorp/boundary/internal/host/plugin/store" "github.com/hashicorp/boundary/internal/host/static" - "github.com/hashicorp/boundary/internal/host/static/store" + staticstore "github.com/hashicorp/boundary/internal/host/static/store" "github.com/hashicorp/boundary/internal/libs/endpoint" "github.com/hashicorp/boundary/internal/perms" hostplugin "github.com/hashicorp/boundary/internal/plugin/host" @@ -32,7 +32,7 @@ import ( ) var ( - maskManager handlers.MaskManager + maskManager = map[subtypes.Subtype]handlers.MaskManager{} // IdActions contains the set of actions that can be performed on // individual resources @@ -64,7 +64,10 @@ var ( func init() { var err error - if maskManager, err = handlers.NewMaskManager(handlers.MaskDestination{&store.HostSet{}}, handlers.MaskSource{&pb.HostSet{}}); err != nil { + if maskManager[static.Subtype], err = handlers.NewMaskManager(handlers.MaskDestination{&staticstore.HostSet{}, &staticstore.UnimplementedSetFields{}}, handlers.MaskSource{&pb.HostSet{}}); err != nil { + panic(err) + } + if maskManager[plugin.Subtype], err = handlers.NewMaskManager(handlers.MaskDestination{&plugstore.HostSet{}}, handlers.MaskSource{&pb.HostSet{}}); err != nil { panic(err) } } @@ -517,7 +520,7 @@ func (s Service) updateInRepo(ctx context.Context, scopeId, catalogId string, re return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("Unable to build host set for update")) } h.PublicId = req.GetId() - dbMask := maskManager.Translate(req.GetUpdateMask().GetPaths()) + dbMask := maskManager[static.Subtype].Translate(req.GetUpdateMask().GetPaths()) if len(dbMask) == 0 { 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."}) } @@ -817,7 +820,10 @@ func toProto(ctx context.Context, in host.Set, hosts []host.Host, opt ...handler switch h := in.(type) { case *plugin.HostSet: if outputFields.Has(globals.PreferredEndpointsField) { - out.PreferredEndpoints = h.GetPreferredEndpoints() + out.PreferredEndpoints = h.PreferredEndpoints + } + if outputFields.Has(globals.SyncIntervalSecondsField) && h.GetSyncIntervalSeconds() != 0 { + out.SyncIntervalSeconds = &wrapperspb.Int32Value{Value: h.GetSyncIntervalSeconds()} } if outputFields.Has(globals.AttributesField) { attrs := &structpb.Struct{} @@ -865,6 +871,9 @@ func toStoragePluginSet(ctx context.Context, catalogId string, item *pb.HostSet) if item.GetPreferredEndpoints() != nil { opts = append(opts, plugin.WithPreferredEndpoints(item.GetPreferredEndpoints())) } + if item.GetSyncIntervalSeconds() != nil { + opts = append(opts, plugin.WithSyncIntervalSeconds(item.GetSyncIntervalSeconds().GetValue())) + } hs, err := plugin.NewHostSet(ctx, catalogId, opts...) if err != nil { return nil, errors.Wrap(ctx, err, op, errors.WithMsg("Unable to build host set for creation")) @@ -904,6 +913,11 @@ func validateCreateRequest(ctx context.Context, req *pbs.CreateHostSetRequest) e if req.GetItem().GetType() != "" && req.GetItem().GetType() != plugin.Subtype.String() { badFields[globals.TypeField] = "Doesn't match the parent resource's type." } + if val := req.GetItem().GetSyncIntervalSeconds(); val != nil { + if val.GetValue() == 0 || val.GetValue() < -1 { + badFields[globals.SyncIntervalSecondsField] = "Must be -1 or a positive integer." + } + } } return badFields }) @@ -923,6 +937,12 @@ func validateUpdateRequest(ctx context.Context, req *pbs.UpdateHostSetRequest) e if req.GetItem().GetType() != "" && req.GetItem().GetType() != static.Subtype.String() { badFields[globals.TypeField] = "Cannot modify the resource type." } + case plugin.Subtype: + if val := req.GetItem().GetSyncIntervalSeconds(); val != nil { + if val.GetValue() == 0 || val.GetValue() < -1 { + badFields[globals.SyncIntervalSecondsField] = "Must be -1 or a positive integer." + } + } } return badFields }, static.HostSetPrefix, plugin.HostSetPrefix) 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 1122ee407f..5d0c7ae4d3 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 @@ -37,6 +37,7 @@ import ( "google.golang.org/protobuf/testing/protocmp" "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/timestamppb" + "google.golang.org/protobuf/types/known/wrapperspb" ) var testAuthorizedActions = map[subtypes.Subtype][]string{ @@ -168,7 +169,7 @@ func TestGet_Plugin(t *testing.T) { } hc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId()) - hs := plugin.TestSet(t, conn, kms, sche, hc, plgm, plugin.WithPreferredEndpoints(prefEndpoints)) + hs := plugin.TestSet(t, conn, kms, sche, hc, plgm, plugin.WithPreferredEndpoints(prefEndpoints), plugin.WithSyncIntervalSeconds(-1)) toMerge := &pbs.GetHostSetRequest{} @@ -184,8 +185,9 @@ func TestGet_Plugin(t *testing.T) { Name: plg.GetName(), Description: plg.GetDescription(), }, - PreferredEndpoints: prefEndpoints, - AuthorizedActions: testAuthorizedActions[plugin.Subtype], + PreferredEndpoints: prefEndpoints, + SyncIntervalSeconds: &wrappers.Int32Value{Value: -1}, + AuthorizedActions: testAuthorizedActions[plugin.Subtype], } cases := []struct { @@ -373,7 +375,7 @@ func TestList_Plugin(t *testing.T) { var wantHs []*pb.HostSet for i := 0; i < 10; i++ { - h := plugin.TestSet(t, conn, kms, sche, hc, plgm, plugin.WithPreferredEndpoints(preferredEndpoints)) + h := plugin.TestSet(t, conn, kms, sche, hc, plgm, plugin.WithPreferredEndpoints(preferredEndpoints), plugin.WithSyncIntervalSeconds(5)) wantHs = append(wantHs, &pb.HostSet{ Id: h.GetPublicId(), HostCatalogId: h.GetCatalogId(), @@ -383,12 +385,13 @@ func TestList_Plugin(t *testing.T) { Name: plg.GetName(), Description: plg.GetDescription(), }, - CreatedTime: h.GetCreateTime().GetTimestamp(), - UpdatedTime: h.GetUpdateTime().GetTimestamp(), - Version: h.GetVersion(), - Type: plugin.Subtype.String(), - AuthorizedActions: testAuthorizedActions[plugin.Subtype], - PreferredEndpoints: preferredEndpoints, + CreatedTime: h.GetCreateTime().GetTimestamp(), + UpdatedTime: h.GetUpdateTime().GetTimestamp(), + Version: h.GetVersion(), + Type: plugin.Subtype.String(), + AuthorizedActions: testAuthorizedActions[plugin.Subtype], + PreferredEndpoints: preferredEndpoints, + SyncIntervalSeconds: &wrappers.Int32Value{Value: 5}, }) } @@ -856,10 +859,11 @@ func TestCreate_Plugin(t *testing.T) { { name: "No Attributes", req: &pbs.CreateHostSetRequest{Item: &pb.HostSet{ - HostCatalogId: hc.GetPublicId(), - Name: &wrappers.StringValue{Value: "No Attributes"}, - Description: &wrappers.StringValue{Value: "desc"}, - Type: plugin.Subtype.String(), + HostCatalogId: hc.GetPublicId(), + Name: &wrappers.StringValue{Value: "No Attributes"}, + Description: &wrappers.StringValue{Value: "desc"}, + Type: plugin.Subtype.String(), + SyncIntervalSeconds: &wrapperspb.Int32Value{Value: -1}, }}, res: &pbs.CreateHostSetResponse{ Uri: fmt.Sprintf("host-sets/%s_", plugin.HostSetPrefix), @@ -871,21 +875,23 @@ func TestCreate_Plugin(t *testing.T) { Name: plg.GetName(), Description: plg.GetDescription(), }, - Name: &wrappers.StringValue{Value: "No Attributes"}, - Description: &wrappers.StringValue{Value: "desc"}, - Type: plugin.Subtype.String(), - AuthorizedActions: testAuthorizedActions[plugin.Subtype], + Name: &wrappers.StringValue{Value: "No Attributes"}, + Description: &wrappers.StringValue{Value: "desc"}, + Type: plugin.Subtype.String(), + AuthorizedActions: testAuthorizedActions[plugin.Subtype], + SyncIntervalSeconds: &wrapperspb.Int32Value{Value: -1}, }, }, }, { name: "With Attributes", req: &pbs.CreateHostSetRequest{Item: &pb.HostSet{ - HostCatalogId: hc.GetPublicId(), - Name: &wrappers.StringValue{Value: "With Attributes"}, - Description: &wrappers.StringValue{Value: "desc"}, - Type: plugin.Subtype.String(), - Attributes: testInputAttrs, + HostCatalogId: hc.GetPublicId(), + Name: &wrappers.StringValue{Value: "With Attributes"}, + Description: &wrappers.StringValue{Value: "desc"}, + Type: plugin.Subtype.String(), + SyncIntervalSeconds: &wrapperspb.Int32Value{Value: 90}, + Attributes: testInputAttrs, }}, res: &pbs.CreateHostSetResponse{ Uri: fmt.Sprintf("host-sets/%s_", plugin.HostSetPrefix), @@ -897,11 +903,12 @@ func TestCreate_Plugin(t *testing.T) { Name: plg.GetName(), Description: plg.GetDescription(), }, - Name: &wrappers.StringValue{Value: "With Attributes"}, - Description: &wrappers.StringValue{Value: "desc"}, - Type: plugin.Subtype.String(), - AuthorizedActions: testAuthorizedActions[plugin.Subtype], - Attributes: testOutputAttrs, + Name: &wrappers.StringValue{Value: "With Attributes"}, + Description: &wrappers.StringValue{Value: "desc"}, + Type: plugin.Subtype.String(), + SyncIntervalSeconds: &wrapperspb.Int32Value{Value: 90}, + AuthorizedActions: testAuthorizedActions[plugin.Subtype], + Attributes: testOutputAttrs, }, }, }, diff --git a/sdk/pbs/controller/api/resources/hostsets/host_set.pb.go b/sdk/pbs/controller/api/resources/hostsets/host_set.pb.go index ff4bcb274f..6c883deded 100644 --- a/sdk/pbs/controller/api/resources/hostsets/host_set.pb.go +++ b/sdk/pbs/controller/api/resources/hostsets/host_set.pb.go @@ -56,8 +56,20 @@ type HostSet struct { // Output only. A list of Hosts in this Host Set. HostIds []string `protobuf:"bytes,100,rep,name=host_ids,proto3" json:"host_ids,omitempty"` // An ordered list of endpoint preferences used to choose from among - // multiple possible endpoints for a host. + // multiple possible endpoints for a host. Preferences are specified by + // "cidr:" or "dns:", specifying which IP + // address or DNS name out of a host's available possibilities should be + // preferred. If no preferences are specified, a value will be chosen from + // among all avialable values using a built-in priority order. May not be + // valid for all plugin types. PreferredEndpoints []string `protobuf:"bytes,101,rep,name=preferred_endpoints,proto3" json:"preferred_endpoints,omitempty"` + // An interger number of seconds indicating the amount of time that should + // elapse between syncs of the host set. The interval will be applied to the + // end of the previous sync operation, not the start. Setting to any + // negative value will disable syncing for that host set; setting to zero + // will cause the set to use Boundary's default. The default may change + // between releases. May not be valid for all plugin types. + SyncIntervalSeconds *wrapperspb.Int32Value `protobuf:"bytes,102,opt,name=sync_interval_seconds,proto3" json:"sync_interval_seconds,omitempty"` // The attributes that are applicable for the specific Host Set type. Attributes *structpb.Struct `protobuf:"bytes,110,opt,name=attributes,proto3" json:"attributes,omitempty"` // Output only. The available actions on this resource for this user. @@ -180,6 +192,13 @@ func (x *HostSet) GetPreferredEndpoints() []string { return nil } +func (x *HostSet) GetSyncIntervalSeconds() *wrapperspb.Int32Value { + if x != nil { + return x.SyncIntervalSeconds + } + return nil +} + func (x *HostSet) GetAttributes() *structpb.Struct { if x != nil { return x.Attributes @@ -217,7 +236,7 @@ var file_controller_api_resources_hostsets_v1_host_set_proto_rawDesc = []byte{ 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2a, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xef, 0x05, 0x0a, 0x07, 0x48, 0x6f, 0x73, + 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf9, 0x06, 0x0a, 0x07, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x28, 0x0a, 0x0f, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x68, @@ -257,20 +276,28 @@ var file_controller_api_resources_hostsets_v1_host_set_proto_rawDesc = []byte{ 0x64, 0x73, 0x12, 0x36, 0x0a, 0x13, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x65, 0x20, 0x03, 0x28, 0x09, 0x42, 0x04, 0xa0, 0xda, 0x29, 0x01, 0x52, 0x13, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, - 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x3d, 0x0a, 0x0a, 0x61, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x6e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x42, 0x04, 0xa0, 0xda, 0x29, 0x01, 0x52, 0x0a, 0x61, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x12, 0x61, 0x75, 0x74, - 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, - 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, - 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x52, 0x5a, 0x50, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, - 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, - 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, - 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x68, 0x6f, 0x73, - 0x74, 0x73, 0x65, 0x74, 0x73, 0x3b, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, 0x73, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x87, 0x01, 0x0a, 0x15, 0x73, + 0x79, 0x6e, 0x63, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x63, + 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x66, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74, + 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x34, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, + 0x2c, 0x0a, 0x15, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, + 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x13, 0x53, 0x79, 0x6e, 0x63, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x52, 0x15, 0x73, + 0x79, 0x6e, 0x63, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x63, + 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x3d, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x73, 0x18, 0x6e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, + 0x74, 0x42, 0x04, 0xa0, 0xda, 0x29, 0x01, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, + 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x52, 0x5a, 0x50, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, + 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, 0x73, 0x3b, + 0x68, 0x6f, 0x73, 0x74, 0x73, 0x65, 0x74, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -292,7 +319,8 @@ var file_controller_api_resources_hostsets_v1_host_set_proto_goTypes = []interfa (*plugins.PluginInfo)(nil), // 2: controller.api.resources.plugins.v1.PluginInfo (*wrapperspb.StringValue)(nil), // 3: google.protobuf.StringValue (*timestamppb.Timestamp)(nil), // 4: google.protobuf.Timestamp - (*structpb.Struct)(nil), // 5: google.protobuf.Struct + (*wrapperspb.Int32Value)(nil), // 5: google.protobuf.Int32Value + (*structpb.Struct)(nil), // 6: google.protobuf.Struct } var file_controller_api_resources_hostsets_v1_host_set_proto_depIdxs = []int32{ 1, // 0: controller.api.resources.hostsets.v1.HostSet.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo @@ -301,12 +329,13 @@ var file_controller_api_resources_hostsets_v1_host_set_proto_depIdxs = []int32{ 3, // 3: controller.api.resources.hostsets.v1.HostSet.description:type_name -> google.protobuf.StringValue 4, // 4: controller.api.resources.hostsets.v1.HostSet.created_time:type_name -> google.protobuf.Timestamp 4, // 5: controller.api.resources.hostsets.v1.HostSet.updated_time:type_name -> google.protobuf.Timestamp - 5, // 6: controller.api.resources.hostsets.v1.HostSet.attributes:type_name -> google.protobuf.Struct - 7, // [7:7] is the sub-list for method output_type - 7, // [7:7] is the sub-list for method input_type - 7, // [7:7] is the sub-list for extension type_name - 7, // [7:7] is the sub-list for extension extendee - 0, // [0:7] is the sub-list for field type_name + 5, // 6: controller.api.resources.hostsets.v1.HostSet.sync_interval_seconds:type_name -> google.protobuf.Int32Value + 6, // 7: controller.api.resources.hostsets.v1.HostSet.attributes:type_name -> google.protobuf.Struct + 8, // [8:8] is the sub-list for method output_type + 8, // [8:8] is the sub-list for method input_type + 8, // [8:8] is the sub-list for extension type_name + 8, // [8:8] is the sub-list for extension extendee + 0, // [0:8] is the sub-list for field type_name } func init() { file_controller_api_resources_hostsets_v1_host_set_proto_init() }