diff --git a/common/subscriber.go b/common/subscriber.go index 9adf7d5f9..ae4f6126d 100644 --- a/common/subscriber.go +++ b/common/subscriber.go @@ -107,8 +107,9 @@ func (self *IdentitySubscription) Diff(rdm *RouterDataModel, sink DiffSink) { adapter := cmp.Reporter(diffReporter) cmp.Diff(currentState, self, cmpopts.IgnoreUnexported( - sync.Mutex{}, IdentitySubscription{}, - ConfigType{}, DataStateConfigType{}, + sync.Mutex{}, IdentitySubscription{}, IdentityService{}, + Config{}, ConfigType{}, + DataStateConfig{}, DataStateConfigType{}, Identity{}, DataStateIdentity{}, Service{}, DataStateService{}, ServicePolicy{}, DataStateServicePolicy{}, diff --git a/controller/db/config_store.go b/controller/db/config_store.go index 56afd70d2..4a2a2ab55 100644 --- a/controller/db/config_store.go +++ b/controller/db/config_store.go @@ -23,6 +23,7 @@ import ( "github.com/openziti/storage/boltz" "github.com/openziti/ziti/common/eid" "go.etcd.io/bbolt" + "slices" ) const ( @@ -132,6 +133,20 @@ func (store *configStoreImpl) DeleteById(ctx boltz.MutateContext, id string) err return err } + // clear config from referencing services + for _, serviceId := range store.GetRelatedEntitiesIdList(ctx.Tx(), id, EntityTypeServices) { + service, err := store.stores.edgeService.LoadById(ctx.Tx(), serviceId) + if err != nil { + return fmt.Errorf("error loading service %s to clear reference to config %s (%w)", serviceId, id, err) + } + service.Configs = slices.DeleteFunc(service.Configs, func(s string) bool { + return s == id + }) + if err = store.stores.edgeService.Update(ctx, service, nil); err != nil { + return fmt.Errorf("error updating service %s to clear reference to config %s (%w)", serviceId, id, err) + } + } + err := store.symbolIdentityServices.Map(ctx.Tx(), []byte(id), func(mapCtx *boltz.MapContext) { keys, err := boltz.DecodeStringSlice(mapCtx.Value()) if err != nil { diff --git a/controller/internal/routes/config_type_router.go b/controller/internal/routes/config_type_router.go index 064a97e88..c73b183e6 100644 --- a/controller/internal/routes/config_type_router.go +++ b/controller/internal/routes/config_type_router.go @@ -21,10 +21,10 @@ import ( "github.com/go-openapi/runtime/middleware" "github.com/openziti/edge-api/rest_management_api_server/operations/config" "github.com/openziti/ziti/controller/env" + "github.com/openziti/ziti/controller/fields" "github.com/openziti/ziti/controller/internal/permissions" "github.com/openziti/ziti/controller/model" "github.com/openziti/ziti/controller/response" - "github.com/openziti/ziti/controller/fields" ) func init() { @@ -68,7 +68,7 @@ func (r *ConfigTypeRouter) Register(ae *env.AppEnv) { }) ae.ManagementApi.ConfigListConfigsForConfigTypeHandler = config.ListConfigsForConfigTypeHandlerFunc(func(params config.ListConfigsForConfigTypeParams, _ interface{}) middleware.Responder { - return ae.IsAllowed(func(ae *env.AppEnv, rc *response.RequestContext) { r.ListConfigs(ae, rc, params) }, params.HTTPRequest, "", "", permissions.IsAdmin()) + return ae.IsAllowed(func(ae *env.AppEnv, rc *response.RequestContext) { r.ListConfigs(ae, rc) }, params.HTTPRequest, params.ID, "", permissions.IsAdmin()) }) } @@ -125,6 +125,6 @@ func (r *ConfigTypeRouter) Patch(ae *env.AppEnv, rc *response.RequestContext, pa }) } -func (r *ConfigTypeRouter) ListConfigs(ae *env.AppEnv, rc *response.RequestContext, params config.ListConfigsForConfigTypeParams) { +func (r *ConfigTypeRouter) ListConfigs(ae *env.AppEnv, rc *response.RequestContext) { ListAssociationWithHandler[*model.ConfigType, *model.Config](ae, rc, ae.Managers.ConfigType, ae.Managers.Config, MapConfigToRestEntity) } diff --git a/controller/internal/routes/edge_router_policy_router.go b/controller/internal/routes/edge_router_policy_router.go index f389557a8..92b074f32 100644 --- a/controller/internal/routes/edge_router_policy_router.go +++ b/controller/internal/routes/edge_router_policy_router.go @@ -20,10 +20,10 @@ import ( "github.com/go-openapi/runtime/middleware" "github.com/openziti/edge-api/rest_management_api_server/operations/edge_router_policy" "github.com/openziti/ziti/controller/env" + "github.com/openziti/ziti/controller/fields" "github.com/openziti/ziti/controller/internal/permissions" "github.com/openziti/ziti/controller/model" "github.com/openziti/ziti/controller/response" - "github.com/openziti/ziti/controller/fields" ) func init() { diff --git a/controller/sync_strats/sync_instant.go b/controller/sync_strats/sync_instant.go index 8c4d144e5..7009f8120 100644 --- a/controller/sync_strats/sync_instant.go +++ b/controller/sync_strats/sync_instant.go @@ -1907,6 +1907,11 @@ func (strategy *InstantStrategy) inspect(val string) (bool, *string, error) { strVal := fmt.Sprintf("%d", idx) return true, &strVal, nil } + if val == "data-model-index" { + idx := strategy.indexProvider.CurrentIndex() + strVal := fmt.Sprintf("%d", idx) + return true, &strVal, nil + } return false, nil, nil } diff --git a/go.mod b/go.mod index cb1faa42e..3608d13db 100644 --- a/go.mod +++ b/go.mod @@ -50,7 +50,7 @@ require ( github.com/mitchellh/mapstructure v1.5.0 github.com/natefinch/lumberjack v2.0.0+incompatible github.com/openziti/agent v1.0.22 - github.com/openziti/channel/v3 v3.0.20 + github.com/openziti/channel/v3 v3.0.22 github.com/openziti/cobra-to-md v1.0.1 github.com/openziti/edge-api v0.26.36 github.com/openziti/foundation/v2 v2.0.55 @@ -88,7 +88,7 @@ require ( golang.org/x/sync v0.10.0 golang.org/x/sys v0.28.0 golang.org/x/text v0.21.0 - google.golang.org/protobuf v1.35.2 + google.golang.org/protobuf v1.36.0 gopkg.in/AlecAivazis/survey.v1 v1.8.8 gopkg.in/go-jose/go-jose.v2 v2.6.3 gopkg.in/resty.v1 v1.12.0 diff --git a/go.sum b/go.sum index 775114fd7..d66e8143c 100644 --- a/go.sum +++ b/go.sum @@ -571,8 +571,8 @@ github.com/openziti-incubator/cf v0.0.3 h1:JKs55DbaIxl87nI/Ra/3DHMiz5iaPpu8JjsuN github.com/openziti-incubator/cf v0.0.3/go.mod h1:6abCY06bCjKmK2I9kohij+cp9uXIPFiFwSCNZPdMk8E= github.com/openziti/agent v1.0.22 h1:lcehAQb3CnM0HbV9XWjnAxhRFrs77thztHnXKkp59qc= github.com/openziti/agent v1.0.22/go.mod h1:Mr52m7/r6XA8S+ojWnMo8Ut44eH/gyX4ln1m5v7rw98= -github.com/openziti/channel/v3 v3.0.20 h1:Z3IkqyaSCskDEZErM9xliwzykzws4ETeDooUfM1Nkmg= -github.com/openziti/channel/v3 v3.0.20/go.mod h1:PeLtdfKg5Og3sPDjlDDvsi1SHWA6CmTF9yPJqiFOhLs= +github.com/openziti/channel/v3 v3.0.22 h1:Z7VilNKX04f3lvAw5JHZ1S64bt5o7JtnVhm2qTc8WxE= +github.com/openziti/channel/v3 v3.0.22/go.mod h1:KwRSjcqkUZsEFZZWFvKOH2ihEB/SIy6OgXJddVvN400= github.com/openziti/cobra-to-md v1.0.1 h1:WRinNoIRmwWUSJm+pSNXMjOrtU48oxXDZgeCYQfVXxE= github.com/openziti/cobra-to-md v1.0.1/go.mod h1:FjCpk/yzHF7/r28oSTNr5P57yN5VolpdAtS/g7KNi2c= github.com/openziti/dilithium v0.3.5 h1:+envGNzxc3OyVPiuvtxivQmCsOjdZjtOMLpQBeMz7eM= @@ -1275,8 +1275,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= -google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.0 h1:mjIs9gYtt56AzC4ZaffQuh88TZurBGhIJMBZGSxNerQ= +google.golang.org/protobuf v1.36.0/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/AlecAivazis/survey.v1 v1.8.8 h1:5UtTowJZTz1j7NxVzDGKTz6Lm9IWm8DDF6b7a2wq9VY= gopkg.in/AlecAivazis/survey.v1 v1.8.8/go.mod h1:CaHjv79TCgAvXMSFJSVgonHXYWxnhzI3eoHtnX5UgUo= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= diff --git a/ziti/cmd/edge/list.go b/ziti/cmd/edge/list.go index fc5a6282e..f28060b15 100644 --- a/ziti/cmd/edge/list.go +++ b/ziti/cmd/edge/list.go @@ -101,7 +101,7 @@ func newListCmd(out io.Writer, errOut io.Writer) *cobra.Command { cmd.AddCommand(newListCmdForEntityType("posture-check-types", runListPostureCheckTypes, newOptions())) configTypeListRootCmd := newEntityListRootCmd("config-type") - configTypeListRootCmd.AddCommand(newSubListCmdForEntityType("config-type", "configs", outputConfigs, newOptions())) + configTypeListRootCmd.AddCommand(newSubListCmdForEntityType("config-types", "configs", outputConfigs, newOptions())) configListRootCmd := newEntityListRootCmd("config") configListRootCmd.AddCommand(newSubListCmdForEntityType("configs", "services", outputServices, newOptions())) diff --git a/ziti/cmd/fabric/inspect.go b/ziti/cmd/fabric/inspect.go index a6406516d..2c1afa903 100644 --- a/ziti/cmd/fabric/inspect.go +++ b/ziti/cmd/fabric/inspect.go @@ -36,6 +36,7 @@ func newInspectCmd(p common.OptionsProvider) *cobra.Command { cmd.AddCommand(action.newInspectSubCmd(p, "router-messaging", "gets information about pending router peer updates and terminator validations")) cmd.AddCommand(action.newInspectSubCmd(p, "router-data-model", "gets information about the router data model")) cmd.AddCommand(action.newInspectSubCmd(p, "router-data-model-index", "gets current index of the router data model")) + cmd.AddCommand(action.newInspectSubCmd(p, "data-model-index", "gets current index of the controller data model")) cmd.AddCommand(action.newInspectSubCmd(p, "router-controllers", "gets information about the state of a router's connections to its controllers")) cmd.AddCommand(action.newInspectSubCmd(p, "terminator-costs", "gets information about terminator dynamic costs")) cmd.AddCommand(action.newInspectSubCmd(p, inspectCommon.RouterIdentityConnectionStatusesKey, "gets information about controller identity state")) diff --git a/zititest/go.mod b/zititest/go.mod index 372a66e07..3bdcccf10 100644 --- a/zititest/go.mod +++ b/zititest/go.mod @@ -12,15 +12,15 @@ require ( github.com/google/uuid v1.6.0 github.com/michaelquigley/pfxlog v0.6.10 github.com/openziti/agent v1.0.22 - github.com/openziti/channel/v3 v3.0.20 + github.com/openziti/channel/v3 v3.0.22 github.com/openziti/edge-api v0.26.36 - github.com/openziti/fablab v0.5.78-0.20241204213521-407c14614fe3 + github.com/openziti/fablab v0.5.82 github.com/openziti/foundation/v2 v2.0.55 github.com/openziti/identity v1.0.93 github.com/openziti/sdk-golang v0.23.44 github.com/openziti/storage v0.3.13 github.com/openziti/transport/v2 v2.0.157 - github.com/openziti/ziti v0.28.3 + github.com/openziti/ziti v1.2.2 github.com/orcaman/concurrent-map/v2 v2.0.1 github.com/pkg/errors v0.9.1 github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 @@ -29,7 +29,7 @@ require ( github.com/stretchr/testify v1.10.0 go.etcd.io/bbolt v1.3.11 golang.org/x/net v0.32.0 - google.golang.org/protobuf v1.35.2 + google.golang.org/protobuf v1.36.0 gopkg.in/yaml.v2 v2.4.0 ) @@ -103,7 +103,7 @@ require ( github.com/influxdata/influxdb-client-go/v2 v2.14.0 // indirect github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d // indirect github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect - github.com/jedib0t/go-pretty/v6 v6.6.2 // indirect + github.com/jedib0t/go-pretty/v6 v6.6.5 // indirect github.com/jessevdk/go-flags v1.6.1 // indirect github.com/jinzhu/copier v0.4.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect diff --git a/zititest/go.sum b/zititest/go.sum index b9ce1eb49..2fa43da3a 100644 --- a/zititest/go.sum +++ b/zititest/go.sum @@ -415,8 +415,8 @@ github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d h1:/WZ github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 h1:W9WBk7wlPfJLvMCdtV4zPulc4uCPrlywQOmbFOhgQNU= github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= -github.com/jedib0t/go-pretty/v6 v6.6.2 h1:27bLj3nRODzaiA7tPIxy9UVWHoPspFfME9XxgwiiNsM= -github.com/jedib0t/go-pretty/v6 v6.6.2/go.mod h1:zbn98qrYlh95FIhwwsbIip0LYpwSG8SUOScs+v9/t0E= +github.com/jedib0t/go-pretty/v6 v6.6.5 h1:9PgMJOVBedpgYLI56jQRJYqngxYAAzfEUua+3NgSqAo= +github.com/jedib0t/go-pretty/v6 v6.6.5/go.mod h1:Uq/HrbhuFty5WSVNfjpQQe47x16RwVGXIveNGEyGtHs= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/jeremija/gosubmit v0.2.7 h1:At0OhGCFGPXyjPYAsCchoBUhE099pcBXmsb4iZqROIc= github.com/jeremija/gosubmit v0.2.7/go.mod h1:Ui+HS073lCFREXBbdfrJzMB57OI/bdxTiLtrDHHhFPI= @@ -593,16 +593,16 @@ github.com/openziti-incubator/cf v0.0.3 h1:JKs55DbaIxl87nI/Ra/3DHMiz5iaPpu8JjsuN github.com/openziti-incubator/cf v0.0.3/go.mod h1:6abCY06bCjKmK2I9kohij+cp9uXIPFiFwSCNZPdMk8E= github.com/openziti/agent v1.0.22 h1:lcehAQb3CnM0HbV9XWjnAxhRFrs77thztHnXKkp59qc= github.com/openziti/agent v1.0.22/go.mod h1:Mr52m7/r6XA8S+ojWnMo8Ut44eH/gyX4ln1m5v7rw98= -github.com/openziti/channel/v3 v3.0.20 h1:Z3IkqyaSCskDEZErM9xliwzykzws4ETeDooUfM1Nkmg= -github.com/openziti/channel/v3 v3.0.20/go.mod h1:PeLtdfKg5Og3sPDjlDDvsi1SHWA6CmTF9yPJqiFOhLs= +github.com/openziti/channel/v3 v3.0.22 h1:Z7VilNKX04f3lvAw5JHZ1S64bt5o7JtnVhm2qTc8WxE= +github.com/openziti/channel/v3 v3.0.22/go.mod h1:KwRSjcqkUZsEFZZWFvKOH2ihEB/SIy6OgXJddVvN400= github.com/openziti/cobra-to-md v1.0.1 h1:WRinNoIRmwWUSJm+pSNXMjOrtU48oxXDZgeCYQfVXxE= github.com/openziti/cobra-to-md v1.0.1/go.mod h1:FjCpk/yzHF7/r28oSTNr5P57yN5VolpdAtS/g7KNi2c= github.com/openziti/dilithium v0.3.5 h1:+envGNzxc3OyVPiuvtxivQmCsOjdZjtOMLpQBeMz7eM= github.com/openziti/dilithium v0.3.5/go.mod h1:XONq1iK6te/WwNzkgZHfIDHordMPqb0hMwJ8bs9EfSk= github.com/openziti/edge-api v0.26.36 h1:zy2DjmIz/B+WxPpIzhFOAxi/LhM/yeKa8s1Vz2h8cQk= github.com/openziti/edge-api v0.26.36/go.mod h1:sYHVpm26Jr1u7VooNJzTb2b2nGSlmCHMnbGC8XfWSng= -github.com/openziti/fablab v0.5.78-0.20241204213521-407c14614fe3 h1:k4+7Dtfq3FIy1MYq/gbE7vlqQQOHIlN7W3jcL3JuT90= -github.com/openziti/fablab v0.5.78-0.20241204213521-407c14614fe3/go.mod h1:iU3FK+P3tQzU5zPuqHpgk8OXeynmfg5l43B+h+rrOiE= +github.com/openziti/fablab v0.5.82 h1:PYe/vKib/lnQt7f9LhAlzxvd9ZixXctji9A9fUrD9Bg= +github.com/openziti/fablab v0.5.82/go.mod h1:i5KsLUMsXOPsTQLZZSvmSzgJMrXv18PpTIVWL8pTyjs= github.com/openziti/foundation/v2 v2.0.55 h1:Z3rsZper7DQtBB4H63nS/U40eR5IwsapKQ1xQ8G7IHQ= github.com/openziti/foundation/v2 v2.0.55/go.mod h1:NG3OI19NT9gtoligfxvbMgTDs8lsJukxNA7N9Ts2WqY= github.com/openziti/identity v1.0.93 h1:+onWAwfMat4hGkx0VDNHOSzkRaXAL10vGaaiGvt2tao= @@ -1311,8 +1311,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= -google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.0 h1:mjIs9gYtt56AzC4ZaffQuh88TZurBGhIJMBZGSxNerQ= +google.golang.org/protobuf v1.36.0/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/AlecAivazis/survey.v1 v1.8.8 h1:5UtTowJZTz1j7NxVzDGKTz6Lm9IWm8DDF6b7a2wq9VY= gopkg.in/AlecAivazis/survey.v1 v1.8.8/go.mod h1:CaHjv79TCgAvXMSFJSVgonHXYWxnhzI3eoHtnX5UgUo= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= diff --git a/zititest/models/router-data-model-test/configs/ctrl.yml.tmpl b/zititest/models/router-data-model-test/configs/ctrl.yml.tmpl index 84129df07..503ab825b 100644 --- a/zititest/models/router-data-model-test/configs/ctrl.yml.tmpl +++ b/zititest/models/router-data-model-test/configs/ctrl.yml.tmpl @@ -41,6 +41,7 @@ events: jsonLogger: subscriptions: - type: entityChange + propagateAlways: true # - type: edge.apiSessions # - type: edge.entityCounts # interval: 15s diff --git a/zititest/models/router-data-model-test/main.go b/zititest/models/router-data-model-test/main.go index d39ba07ac..fa0b6de36 100644 --- a/zititest/models/router-data-model-test/main.go +++ b/zititest/models/router-data-model-test/main.go @@ -255,7 +255,7 @@ var m = &model.Model{ var tasks []parallel.LabeledTask for range 100 { - task := createNewService(ctrls.getCtrl("ctrl1")) + task := createNewService(ctrls.getCtrl("ctrl1"), nil) tasks = append(tasks, task) } return parallel.ExecuteLabeled(tasks, 2, nil) diff --git a/zititest/models/router-data-model-test/validation.go b/zititest/models/router-data-model-test/validation.go index 152bda414..ad4950e6e 100644 --- a/zititest/models/router-data-model-test/validation.go +++ b/zititest/models/router-data-model-test/validation.go @@ -35,7 +35,9 @@ import ( "github.com/openziti/ziti/zititest/zitilab/models" "google.golang.org/protobuf/proto" "io" + "math" "math/rand" + "slices" "strings" "sync" "time" @@ -99,9 +101,13 @@ func sowChaos(run model.Run) error { return err } - var tasks []parallel.LabeledTask var err error + tasks, lastTasks, err := getServiceAndConfigChaosTasks(run, ctrls) + if err != nil { + return err + } + applyTasks := func(f func(run model.Run, ctrls *CtrlClients) ([]parallel.LabeledTask, error)) { var t []parallel.LabeledTask if err == nil { @@ -113,7 +119,6 @@ func sowChaos(run model.Run) error { } applyTasks(getRestartTasks) - applyTasks(getServiceChaosTasks) applyTasks(getIdentityChaosTasks) applyTasks(getServicePolicyChaosTasks) @@ -122,6 +127,7 @@ func sowChaos(run model.Run) error { } chaos.Randomize(tasks) + tasks = append(tasks, lastTasks...) retryPolicy := func(task parallel.LabeledTask, attempt int, err error) parallel.ErrorAction { var apiErr util.ApiErrorPayload @@ -232,65 +238,243 @@ func newBoolPtr() *bool { return &b } -func getServiceChaosTasks(_ model.Run, ctrls *CtrlClients) ([]parallel.LabeledTask, error) { - svcs, err := models.ListServices(ctrls.getRandomCtrl(), "limit none", 15*time.Second) - if err != nil { - return nil, err +type taskGenerationContext struct { + ctrls *CtrlClients + + configTypes []*rest_model.ConfigTypeDetail + configs []*rest_model.ConfigDetail + services []*rest_model.ServiceDetail + + configTypesDeleted map[string]struct{} + configsDeleted map[string]struct{} + servicesDeleted map[string]struct{} + + tasks []parallel.LabeledTask + lastTasks []parallel.LabeledTask + + configIdx int + + err error +} + +func (self *taskGenerationContext) loadEntities() { + self.configTypes, self.err = models.ListConfigTypes(self.ctrls.getRandomCtrl(), `not (name contains ".v1" or id = "host.v2") limit none`, 15*time.Second) + if self.err != nil { + return } - chaos.Randomize(svcs) + chaos.Randomize(self.configTypes) - var result []parallel.LabeledTask + self.configs, self.err = models.ListConfigs(self.ctrls.getRandomCtrl(), "limit none", 15*time.Second) + if self.err != nil { + return + } + chaos.Randomize(self.configs) - for i := 0; i < 5; i++ { - result = append(result, parallel.TaskWithLabel("delete.service", fmt.Sprintf("delete service %s", *svcs[i].ID), func() error { - return models.DeleteService(ctrls.getRandomCtrl(), *svcs[i].ID, 15*time.Second) - })) + self.services, self.err = models.ListServices(self.ctrls.getRandomCtrl(), "limit none", 15*time.Second) + if self.err != nil { + return } + chaos.Randomize(self.services) +} - for i := 5; i < 10; i++ { - result = append(result, parallel.TaskWithLabel("modify.service", fmt.Sprintf("modify service %s", *svcs[i].ID), func() error { - svc := svcs[i] - svc.RoleAttributes = getRoleAttributesAsAttrPtr(3) - svc.Name = newId() - return models.UpdateServiceFromDetail(ctrls.getRandomCtrl(), svc, 15*time.Second) - })) +func (self *taskGenerationContext) getConfigTypeId() string { + if len(self.configTypes)-len(self.configTypesDeleted) < 1 { + return "" } - for i := 0; i < 5; i++ { - result = append(result, createNewService(ctrls.getRandomCtrl())) + for { + idx := rand.Intn(len(self.configTypes)) + configType := self.configTypes[idx] + if _, deleted := self.configTypesDeleted[*configType.ID]; !deleted { + return *configType.ID + } } +} - return result, nil +func (self *taskGenerationContext) getTwoValidConfigs() []string { + if len(self.configs)-len(self.configsDeleted) < 1 { + return nil + } + var first *rest_model.ConfigDetail + for { + if self.configIdx >= len(self.configs) { + self.configIdx = 0 + } + next := self.configs[self.configIdx] + self.configIdx++ + if _, deleted := self.configsDeleted[*next.ID]; deleted { + continue + } + if first == nil { + first = next + continue + } + if first == next { + return []string{*first.ID} + } + if *first.ConfigTypeID == *next.ConfigTypeID { + continue + } + return []string{*first.ID, *next.ID} + } } -func getConfigTypeChaosTasks(_ model.Run, ctrls *CtrlClients) ([]parallel.LabeledTask, error) { - entities, err := models.ListConfigTypes(ctrls.getRandomCtrl(), "limit none", 15*time.Second) - if err != nil { - return nil, err +func (self *taskGenerationContext) generateConfigTypeTasks() { + if self.err != nil { + return } - chaos.Randomize(entities) - var result []parallel.LabeledTask + if scenarioCounter%5 == 0 { // only add/remove config types every 5th iteration + for i := 0; i < min(2, len(self.configTypes)); i++ { + entityId := *self.configTypes[i].ID + self.configTypesDeleted[entityId] = struct{}{} + self.lastTasks = append(self.lastTasks, parallel.TaskWithLabel("delete.config-type", fmt.Sprintf("delete config type %s", entityId), func() error { + return models.DeleteConfigType(self.ctrls.getRandomCtrl(), entityId, 15*time.Second) + })) + } + } - for i := 0; i < 5; i++ { - result = append(result, parallel.TaskWithLabel("delete.config-type", fmt.Sprintf("delete config type %s", *entities[i].ID), func() error { - return models.DeleteConfigType(ctrls.getRandomCtrl(), *entities[i].ID, 15*time.Second) + for i := 2; i < min(7, len(self.configTypes)); i++ { + entity := self.configTypes[i] + entityId := *entity.ID + self.tasks = append(self.tasks, parallel.TaskWithLabel("modify.config-type", fmt.Sprintf("modify config type %s", entityId), func() error { + entity.Name = newId() + return models.UpdateConfigTypeFromDetail(self.ctrls.getRandomCtrl(), entity, 15*time.Second) })) } - for i := 5; i < 10; i++ { - result = append(result, parallel.TaskWithLabel("modify.config-type", fmt.Sprintf("modify config type %s", *entities[i].ID), func() error { - entity := entities[i] + createConfigTypesCount := 15 - (len(self.configTypes) - len(self.configTypesDeleted)) // target 25 configs available + for i := 0; i < createConfigTypesCount; i++ { + self.tasks = append(self.tasks, createNewConfigType(self.ctrls.getRandomCtrl())) + } +} + +func (self *taskGenerationContext) generateConfigTasks() { + if self.err != nil { + return + } + + if scenarioCounter%3 == 0 && len(self.configs) > 2 { // only delete configs every third iteration + for i := 0; i < 2; i++ { + entityId := *self.configs[i].ID + self.lastTasks = append(self.lastTasks, parallel.TaskWithLabel("delete.config", fmt.Sprintf("delete config %s", entityId), func() error { + return models.DeleteConfig(self.ctrls.getRandomCtrl(), entityId, 15*time.Second) + })) + } + } + + // delete any configs used by config types to be deleted + if len(self.configTypesDeleted) > 0 { + for _, config := range self.configs { + if _, deleted := self.configsDeleted[*config.ID]; deleted { + continue + } + if _, deleted := self.configTypesDeleted[*config.ConfigTypeID]; deleted { + entityId := *config.ID + self.configsDeleted[entityId] = struct{}{} + self.lastTasks = append(self.lastTasks, parallel.TaskWithLabel("delete.config", fmt.Sprintf("delete config %s", entityId), func() error { + return models.DeleteConfig(self.ctrls.getRandomCtrl(), entityId, 15*time.Second) + })) + } + } + } + + for i := 2; i < min(7, len(self.configs)); i++ { + entityId := *self.configs[i].ID + self.tasks = append(self.tasks, parallel.TaskWithLabel("modify.config", fmt.Sprintf("modify config %s", entityId), func() error { + entity := self.configs[i] entity.Name = newId() - return models.UpdateConfigTypeFromDetail(ctrls.getRandomCtrl(), entity, 15*time.Second) + entity.Data = map[string]interface{}{ + "hostname": fmt.Sprintf("https://%s.com", uuid.NewString()), + "protocol": func() string { + if rand.Int()%2 == 0 { + return "tcp" + } + return "udp" + }(), + "port": rand.Intn(32000), + } + return models.UpdateConfigFromDetail(self.ctrls.getRandomCtrl(), entity, 15*time.Second) })) } - for i := 0; i < 5; i++ { - result = append(result, createNewService(ctrls.getRandomCtrl())) + if len(self.configTypes) > 0 { + createConfigCount := 25 - (len(self.configs) - len(self.configsDeleted)) // target 25 configs available + for i := 0; i < createConfigCount; i++ { + self.tasks = append(self.tasks, createNewConfig(self.ctrls.getRandomCtrl(), self.getConfigTypeId())) + } + } +} + +func (self *taskGenerationContext) generateServiceTasks() { + if self.err != nil { + return } - return result, nil + for i := 0; i < min(5, len(self.services)); i++ { + entityId := *self.services[i].ID + self.servicesDeleted[entityId] = struct{}{} + self.tasks = append(self.tasks, parallel.TaskWithLabel("delete.service", fmt.Sprintf("delete service %s", entityId), func() error { + return models.DeleteService(self.ctrls.getRandomCtrl(), entityId, 15*time.Second) + })) + } + + modifyServiceTargetIndex := min(10, len(self.services)) + if len(self.configsDeleted) > 0 { + modifyServiceTargetIndex = len(self.services) + } + for i := 5; i < modifyServiceTargetIndex; i++ { + service := self.services[i] + doModify := true + if len(self.configsDeleted) > 0 { + doModify = false + for _, configId := range service.Configs { + if _, deleted := self.configsDeleted[configId]; deleted { + doModify = true + break + } + } + } + if doModify { + self.tasks = append(self.tasks, parallel.TaskWithLabel("modify.service", fmt.Sprintf("modify service %s", *service.ID), func() error { + service.RoleAttributes = getRoleAttributesAsAttrPtr(3) + service.Name = newId() + service.Configs = self.getTwoValidConfigs() + return models.UpdateServiceFromDetail(self.ctrls.getRandomCtrl(), service, 15*time.Second) + })) + } + } + + createServicesTarget := 100 - (len(self.services) - len(self.servicesDeleted)) + for i := 0; i < createServicesTarget; i++ { + self.tasks = append(self.tasks, createNewService(self.ctrls.getRandomCtrl(), self.getTwoValidConfigs())) + } +} + +func (self *taskGenerationContext) getResults() ([]parallel.LabeledTask, []parallel.LabeledTask, error) { + if self.err != nil { + return nil, nil, self.err + } + + // need to delete configs first, then config types + slices.Reverse(self.lastTasks) + return self.tasks, self.lastTasks, nil +} + +func getServiceAndConfigChaosTasks(_ model.Run, ctrls *CtrlClients) ([]parallel.LabeledTask, []parallel.LabeledTask, error) { + ctx := &taskGenerationContext{ + ctrls: ctrls, + configTypesDeleted: map[string]struct{}{}, + configsDeleted: map[string]struct{}{}, + servicesDeleted: map[string]struct{}{}, + } + + ctx.loadEntities() + ctx.generateConfigTypeTasks() + ctx.generateConfigTasks() + ctx.generateServiceTasks() + + return ctx.getResults() } func getIdentityChaosTasks(r model.Run, ctrls *CtrlClients) ([]parallel.LabeledTask, error) { @@ -366,10 +550,10 @@ func getServicePolicyChaosTasks(_ model.Run, ctrls *CtrlClients) ([]parallel.Lab return result, nil } -func createNewService(ctrl *zitirest.Clients) parallel.LabeledTask { +func createNewService(ctrl *zitirest.Clients, configs []string) parallel.LabeledTask { return parallel.TaskWithLabel("create.service", "create new service", func() error { svc := &rest_model.ServiceCreate{ - Configs: nil, + Configs: configs, EncryptionRequired: newBoolPtr(), Name: newId(), RoleAttributes: getRoleAttributes(3), @@ -379,6 +563,64 @@ func createNewService(ctrl *zitirest.Clients) parallel.LabeledTask { }) } +func createNewConfigType(ctrl *zitirest.Clients) parallel.LabeledTask { + return parallel.TaskWithLabel("create.config-type", "create new config type", func() error { + entity := &rest_model.ConfigTypeCreate{ + Name: newId(), + Schema: map[string]interface{}{ + "$id": "https://edge.openziti.org/schemas/test.config.json", + "type": "object", + "additionalProperties": false, + "required": []interface{}{ + "hostname", + "port", + }, + "properties": map[string]interface{}{ + "protocol": map[string]interface{}{ + "type": []interface{}{ + "string", + "null", + }, + "enum": []interface{}{ + "tcp", + "udp", + }, + }, + "hostname": map[string]interface{}{ + "type": "string", + }, + "port": map[string]interface{}{ + "type": "integer", + "minimum": float64(0), + "maximum": float64(math.MaxUint16), + }, + }, + }, + } + return models.CreateConfigType(ctrl, entity, 15*time.Second) + }) +} + +func createNewConfig(ctrl *zitirest.Clients, configTypeId string) parallel.LabeledTask { + return parallel.TaskWithLabel("create.config", "create new config", func() error { + entity := &rest_model.ConfigCreate{ + Name: newId(), + ConfigTypeID: &configTypeId, + Data: map[string]interface{}{ + "hostname": fmt.Sprintf("https://%s.com", uuid.NewString()), + "protocol": func() string { + if rand.Int()%2 == 0 { + return "tcp" + } + return "udp" + }(), + "port": rand.Intn(32000), + }, + } + return models.CreateConfig(ctrl, entity, 15*time.Second) + }) +} + func createNewIdentity(ctrl *zitirest.Clients) parallel.LabeledTask { isAdmin := false identityType := rest_model.IdentityTypeDefault