Skip to content

Commit

Permalink
Improve config and JSONSchema
Browse files Browse the repository at this point in the history
  • Loading branch information
mszostok committed Oct 10, 2023
1 parent 16a028a commit 60b9e29
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 54 deletions.
4 changes: 2 additions & 2 deletions cmd/executor/thread-mate/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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
}

Expand Down
4 changes: 4 additions & 0 deletions internal/executor/thread-mate/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@ type (
Export *ExportCmd `arg:"subcommand:export"`
}

// ExportCmd represents the "export" subcommand.
ExportCmd struct {
Activity *ExportActivityCmd
}

// ExportActivityCmd represents the options for the "export activity" subcommand.
ExportActivityCmd struct {
Type string `arg:"--type"`
}

// ResolveCmd represents the "resolve" subcommand.
ResolveCmd struct {
ID string `arg:"--id"`
Expand Down
35 changes: 24 additions & 11 deletions internal/executor/thread-mate/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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()
Expand All @@ -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,
},
}

Expand Down
87 changes: 54 additions & 33 deletions internal/executor/thread-mate/jsonschema.json
Original file line number Diff line number Diff line change
@@ -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"
]
}
16 changes: 10 additions & 6 deletions internal/executor/thread-mate/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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{}
Expand Down Expand Up @@ -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
}

Expand Down
9 changes: 9 additions & 0 deletions pkg/config/testdata/TestLoadConfigSuccess/config.golden.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ communications:
- k8s-events
executors:
- k8s-tools
textMessageBindings:
type: ""
messages: []
token: xoxb-token-from-env
socketSlack:
enabled: true
Expand All @@ -64,6 +67,9 @@ communications:
- k8s-events
executors:
- k8s-tools
textMessageBindings:
type: ""
messages: []
botToken: xoxb-token-from-env
appToken: xapp-token-from-env
mattermost:
Expand All @@ -82,6 +88,9 @@ communications:
- k8s-events
executors:
- k8s-tools
textMessageBindings:
type: ""
messages: []
discord:
enabled: false
token: DISCORD_TOKEN
Expand Down
4 changes: 2 additions & 2 deletions proto/executor.proto
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@ message MessageContext {
}

message UserContext {
string mention = 1;
string mention = 1;
string displayName = 2;
}

message ExecuteResponse {
bytes message = 1;
repeated bytes messages = 2;
repeated bytes messages = 2;
}

message MetadataResponse {
Expand Down

0 comments on commit 60b9e29

Please sign in to comment.