From 854f7c0d1f6dcb3ac82a75fd9f6b77523a6f2489 Mon Sep 17 00:00:00 2001 From: Mateusz Szostok Date: Tue, 10 Oct 2023 22:30:20 +0200 Subject: [PATCH] Improve config and JSONSchema --- cmd/executor/thread-mate/main.go | 4 +- internal/executor/thread-mate/config.go | 35 +++++--- internal/executor/thread-mate/jsonschema.json | 87 ++++++++++++------- internal/executor/thread-mate/service.go | 16 ++-- 4 files changed, 90 insertions(+), 52 deletions(-) diff --git a/cmd/executor/thread-mate/main.go b/cmd/executor/thread-mate/main.go index 8e61e4a8fb..156cfa5a14 100644 --- a/cmd/executor/thread-mate/main.go +++ b/cmd/executor/thread-mate/main.go @@ -43,7 +43,7 @@ func (*ThreadMateExecutor) Metadata(context.Context) (api.MetadataOutput, error) } func (t *ThreadMateExecutor) init(cfg thmate.Config, kubeconfig []byte) (*thmate.ThreadMate, error) { - svc, ok := t.once.Load(cfg.RoundRobinGroupName) + svc, ok := t.once.Load(cfg.RoundRobin.GroupName) if ok { return svc.(*thmate.ThreadMate), nil } @@ -61,7 +61,7 @@ func (t *ThreadMateExecutor) init(cfg thmate.Config, kubeconfig []byte) (*thmate newSvc := thmate.New(cfg, cfgDumper) newSvc.Start() - t.once.Store(cfg.RoundRobinGroupName, newSvc) + t.once.Store(cfg.RoundRobin.GroupName, newSvc) return newSvc, nil } diff --git a/internal/executor/thread-mate/config.go b/internal/executor/thread-mate/config.go index 032f8e2c80..86095e91da 100644 --- a/internal/executor/thread-mate/config.go +++ b/internal/executor/thread-mate/config.go @@ -20,12 +20,20 @@ var defaultRoundRobinMessage string // Config holds the executor configuration. type Config struct { - RoundRobinGroupName string `yaml:"roundRobinGroupName"` - Assignees []string `yaml:"assignees"` - Logger config.Logger `yaml:"log"` - DataSyncInterval time.Duration `yaml:"dataSyncInterval"` - Pick PickConfig `yaml:"pick"` - ConfigMapNamespace string `yaml:"configMapNamespace"` + RoundRobin RoundRobinConfig `yaml:"roundRobin"` + Logger config.Logger `yaml:"log"` + Pick PickConfig `yaml:"pick"` + + Persistence PersistenceConfig `yaml:"persistence"` +} + +type PersistenceConfig struct { + SyncInterval time.Duration `yaml:"syncInterval"` + ConfigMapNamespace string `yaml:"configMapNamespace"` +} +type RoundRobinConfig struct { + Assignees []string `yaml:"assignees"` + GroupName string `yaml:"groupName"` } type PickConfig struct { @@ -36,10 +44,10 @@ type PickConfig struct { // Validate validates the configuration parameters. func (c *Config) Validate() error { issues := multierror.New() - if c.RoundRobinGroupName == "" { + if c.RoundRobin.GroupName == "" { issues = multierror.Append(issues, errors.New("the round robin group name cannot be empty")) } - if len(c.Assignees) == 0 { + if len(c.RoundRobin.Assignees) == 0 { issues = multierror.Append(issues, errors.New("the assignees list cannot be empty")) } return issues.ErrorOrNil() @@ -48,11 +56,16 @@ func (c *Config) Validate() error { // MergeConfigs merges the configuration. func MergeConfigs(configs []*executor.Config) (Config, error) { defaults := Config{ - RoundRobinGroupName: "default", - DataSyncInterval: 5 * time.Second, - ConfigMapNamespace: "botkube", + RoundRobin: RoundRobinConfig{ + GroupName: "default", + }, + Persistence: PersistenceConfig{ + SyncInterval: 5 * time.Second, + ConfigMapNamespace: "botkube", + }, Pick: PickConfig{ MessagesTemplate: defaultRoundRobinMessage, + UserCooldownTime: 3 * time.Minute, }, } diff --git a/internal/executor/thread-mate/jsonschema.json b/internal/executor/thread-mate/jsonschema.json index ea4103c0d2..58de2a1c1e 100644 --- a/internal/executor/thread-mate/jsonschema.json +++ b/internal/executor/thread-mate/jsonschema.json @@ -1,51 +1,72 @@ { "uiSchema": { - "assignees": { - "ui:classNames": "non-orderable", - "ui:options": { - "orderable": false - }, - "items": { + "roundRobin": { + "assignees": { + "ui:classNames": "non-orderable", "ui:options": { - "label": false + "orderable": false + }, + "items": { + "ui:options": { + "label": false + } } } } }, "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", - "description": "Streamlines managing assignment for incidents or user support", "properties": { - "roundRobinGroupName": { - "type": "string", - "default": "default", - "title": "Round Robin Group Name", - "description": "The group name serves as a unique identifier for managing the round-robin order of assignees. Make it unique among all active Thread-Mate plugins." - }, - "assignees": { - "type": "array", - "description": "List of assignees provided in the format {id}:{display name}, such as 'U02KKBR5PE1:Matt'.", - "items": { - "type": "string" + "roundRobin": { + "type": "object", + "properties": { + "assignees": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 + }, + "groupName": { + "type": "string", + "default": "default" + } }, - "title": "Assignees" + "required": [ + "assignees" + ] }, - "dataSyncInterval": { - "type": "string", - "format": "duration", - "default": "5s", - "title": "Data Sync Interval" + "pick": { + "type": "object", + "properties": { + "userCooldownTime": { + "type": "string", + "format": "duration", + "default": "3m" + }, + "messagesTemplate": { + "type": "string" + } + } }, - "configMapNamespace": { - "type": "string", - "default": "botkube", - "title": "Config Map Namespace" + "persistence": { + "type": "object", + "properties": { + "syncInterval": { + "type": "string", + "format": "duration", + "default": "5s" + }, + "configMapNamespace": { + "type": "string", + "default": "botkube" + } + } } }, "required": [ - "roundRobinGroupName", - "assignees", - "dataSyncInterval", - "configMapNamespace" + "roundRobin", + "pick", + "persistence" ] } diff --git a/internal/executor/thread-mate/service.go b/internal/executor/thread-mate/service.go index 95997e3a68..a4380f559a 100644 --- a/internal/executor/thread-mate/service.go +++ b/internal/executor/thread-mate/service.go @@ -49,7 +49,7 @@ type ThreadMate struct { // New creates a new instance of ThreadMate. func New(cfg Config, cfgDumper *ConfigMapDumper) *ThreadMate { var assignees []Assignee - for _, item := range cfg.Assignees { + for _, item := range cfg.RoundRobin.Assignees { id, displayName, found := strings.Cut(item, ":") if !found { displayName = id @@ -80,7 +80,7 @@ func (t *ThreadMate) Start() { t.resolvedThreads = t.tryToGetConfigMapData(resolvedCMName) go func() { - for range time.Tick(t.cfg.DataSyncInterval) { + for range time.Tick(t.cfg.Persistence.SyncInterval) { t.tryToDump(ongoingCMName, &t.ongoingThreads) t.tryToDump(resolvedCMName, &t.resolvedThreads) @@ -185,6 +185,10 @@ func (t *ThreadMate) renderPickMessage(assignee Assignee, msg executor.Message) } func (t *ThreadMate) pickCooldownElapsed(userID string) bool { + if t.cfg.Pick.UserCooldownTime == 0 { + return true + } + now := time.Now() lastActivity, loaded := t.lastProcessedActivity.LoadOrStore(userID, now) if !loaded { @@ -474,7 +478,7 @@ func (t *ThreadMate) Export(export *ExportCmd) api.Message { } func (t *ThreadMate) tryToGetConfigMapData(name string) Threads { - resolvedRawData, err := t.cfgDumper.Get(t.cfg.ConfigMapNamespace, name) + resolvedRawData, err := t.cfgDumper.Get(t.cfg.Persistence.ConfigMapNamespace, name) if err != nil { t.log.WithError(err).WithField("threads", name).Debug("Cannot fetch threads, starting fresh...") return Threads{} @@ -511,13 +515,13 @@ func (t *ThreadMate) tryToDump(name string, in *Threads) { } raw, err := json.Marshal(newData) if err != nil { - t.log.WithError(err).WithField("threads", name).Errorf("Cannot marshal threads, will repeat in %d...", t.cfg.DataSyncInterval) + t.log.WithError(err).WithField("threads", name).Errorf("Cannot marshal threads, will repeat in %d...", t.cfg.Persistence.SyncInterval) return } - err = t.cfgDumper.SaveOrUpdate(t.cfg.ConfigMapNamespace, fmt.Sprintf("%s-%s", name, t.cfg.RoundRobinGroupName), string(raw)) + err = t.cfgDumper.SaveOrUpdate(t.cfg.Persistence.ConfigMapNamespace, fmt.Sprintf("%s-%s", name, t.cfg.RoundRobin.GroupName), string(raw)) if err != nil { - t.log.WithError(err).WithField("threads", name).Errorf("Cannot dump threads, will repeat in %d...", t.cfg.DataSyncInterval) + t.log.WithError(err).WithField("threads", name).Errorf("Cannot dump threads, will repeat in %d...", t.cfg.Persistence.SyncInterval) return }