Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions command/cmdUpdate.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,30 @@ package command

import (
"flag"
"strings"
"time"

"github.com/StackAdapt/systags/manager"
)

type UpdateCommand struct {
baseCommand
timeout time.Duration
timeout time.Duration
retry time.Duration
requredKeys StringArray
}

type StringArray []string

// String is the method to format the flag's value, part of the flag.Value interface.
func (s *StringArray) String() string {
return strings.Join(*s, ",")
}

// Set is the method to set the flag value, part of the flag.Value interface.
func (s *StringArray) Set(value string) error {
*s = strings.Split(value, ",")
return nil
}

func NewUpdateCommand() *UpdateCommand {
Expand All @@ -22,6 +38,10 @@ func NewUpdateCommand() *UpdateCommand {

cmd.flagSet.DurationVar(&cmd.timeout, "t", 5*time.Second, "")
cmd.flagSet.DurationVar(&cmd.timeout, "timeout", 5*time.Second, "")
cmd.flagSet.DurationVar(&cmd.retry, "r", 0*time.Second, "")
cmd.flagSet.DurationVar(&cmd.retry, "retry", 0*time.Second, "")
cmd.flagSet.Var(&cmd.requredKeys, "k", "")
cmd.flagSet.Var(&cmd.requredKeys, "requried_keys", "")

// Don't print unneeded usage
cmd.flagSet.Usage = func() {}
Expand All @@ -36,7 +56,7 @@ func (cmd *UpdateCommand) Apply(m *manager.Manager) error {
return err
}

err = m.UpdateRemote(cmd.timeout)
err = m.UpdateRemote(cmd.timeout, cmd.retry, cmd.requredKeys)
if err != nil {
return err
}
Expand Down
57 changes: 51 additions & 6 deletions manager/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,8 +264,11 @@ func (m *Manager) SaveFiles() error {

// UpdateRemote fetches remote tags from the instance
// it's currently running on. This operation may take
// some time to complete, controlled by timeout.
func (m *Manager) UpdateRemote(timeout time.Duration) error {
// some time to complete, controlled by timeout. If a
// retry duration is provided, this function will auto
// retry for the duration if empty tags are returned.
// A bounded exponential backoff strategy is employed.
func (m *Manager) UpdateRemote(timeout time.Duration, retry time.Duration, requiredKeys []string) error {

// TODO:
// At the moment, only AWS is supported, but if you want
Expand All @@ -274,12 +277,54 @@ func (m *Manager) UpdateRemote(timeout time.Duration) error {
// which cloud provider is being used and add the feature
// to update the tags similar to how it's done now in AWS.

result, err := getAwsTags(m.logger, timeout)
if err != nil {
return err
var err error
var res Tags

// Sleep duration to start with
curInterval := 1 * time.Second

// Maximum duration to sleep for
maxInterval := 5 * time.Second

startTime := time.Now()
untilTime := startTime.Add(retry)

hasRequiredKeys := func(tags Tags, keys []string) bool {
for _, k := range keys {
if _, ok := tags[k]; !ok {
return false
}
}
return true
}

for {
res, err = getAwsTags(m.logger, timeout)
if err != nil {
return err
}

// Tags are not empty or we have reached time limit
if (len(res) > 0 && hasRequiredKeys(res, requiredKeys)) || time.Since(startTime) > retry {
break
}

// Avoid exceeding the time limit
interval := time.Until(untilTime)
if interval > curInterval {
interval = curInterval
}

time.Sleep(interval)

// Adjust sleep duration to take longer the next time
curInterval = time.Duration(float64(curInterval) * 2)
if curInterval > maxInterval {
curInterval = maxInterval
}
}

m.remote = result
m.remote = res
return nil
}

Expand Down