Skip to content
This repository has been archived by the owner on Dec 5, 2024. It is now read-only.

Commit

Permalink
Merge pull request #14 from ingrammicro/feature/13-run-once-reconfigu…
Browse files Browse the repository at this point in the history
…ration

Have bootstrap command load config file changes during execution (issue #13)
  • Loading branch information
pbanos authored Sep 16, 2019
2 parents 0964946 + 3222e5d commit 20beb35
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 51 deletions.
93 changes: 58 additions & 35 deletions bootstrapping/bootstrapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ import (
"syscall"
"time"

log "github.com/sirupsen/logrus"
"github.com/allan-simon/go-singleinstance"
"github.com/codegangsta/cli"
"github.com/ingrammicro/cio/api/blueprint"
"github.com/ingrammicro/cio/api/types"
"github.com/ingrammicro/cio/cmd"
"github.com/ingrammicro/cio/utils"
"github.com/ingrammicro/cio/utils/format"
log "github.com/sirupsen/logrus"
)

const (
Expand Down Expand Up @@ -153,37 +153,7 @@ func start(c *cli.Context) error {
defer cancel()
go handleSysSignals(cancel)

config, err := utils.GetConcertoConfig()
if err != nil {
formatter.PrintFatal("Couldn't wire up config", err)
}

interval := config.BootstrapConfig.IntervalSeconds
if !(interval > 0) {
interval = defaultIntervalSeconds
}

splay := config.BootstrapConfig.SplaySeconds
if !(splay > 0) {
splay = defaultSplaySeconds
}

applyAfterIterations := config.BootstrapConfig.ApplyAfterIterations
if !(applyAfterIterations > 0) {
applyAfterIterations = defaultApplyAfterIterations
}

thresholdLines := c.Int("lines")
if !(thresholdLines > 0) {
thresholdLines = defaultThresholdLines
}
log.Debug("routine lines threshold: ", thresholdLines)
bootstrappingSvc, formatter := cmd.WireUpBootstrapping(c)

if config.BootstrapConfig.RunOnce {
return runBootstrapOnce(ctx, bootstrappingSvc, formatter, thresholdLines, interval, splay)
}
return runBootstrapPeriodically(ctx, bootstrappingSvc, formatter, applyAfterIterations, thresholdLines, interval, splay)
return runBootstrapPeriodically(ctx, c, formatter)
}

// Stop the bootstrapping process
Expand All @@ -199,18 +169,41 @@ func stop(c *cli.Context) error {
return nil
}

func runBootstrapPeriodically(ctx context.Context, bootstrappingSvc *blueprint.BootstrappingService, formatter format.Formatter, applyAfterIterations, thresholdLines, interval, splay int) error {
func runBootstrapPeriodically(ctx context.Context, c *cli.Context, formatter format.Formatter) error {
config, err := utils.GetConcertoConfig()
if err != nil {
formatter.PrintFatal("Couldn't wire up config", err)
}
if config.BootstrapConfig.RunOnce {
return runBootstrapOnce(ctx, c, config, formatter)
}
applyAfterIterations, thresholdLines, interval, splay := getBootstrappingConfigOrDefaults(c, config)

bootstrappingSvc, formatter := cmd.WireUpBootstrapping(c)
r := rand.New(rand.NewSource(time.Now().UnixNano()))
var blueprintConfig *types.BootstrappingConfiguration
var noPolicyfileApplicationIterations int
var lastPolicyfileApplicationErr, err error
var lastPolicyfileApplicationErr error
for {
var updated bool
blueprintConfig, updated, err = getBlueprintConfig(ctx, bootstrappingSvc, blueprintConfig, formatter)
if err == nil {
if updated || lastPolicyfileApplicationErr != nil || noPolicyfileApplicationIterations >= applyAfterIterations {
noPolicyfileApplicationIterations = -1
lastPolicyfileApplicationErr = applyPolicyfiles(ctx, bootstrappingSvc, blueprintConfig, formatter, thresholdLines)
var configUpdated bool
config, configUpdated, err = utils.ReloadConcertoConfig(c)
if err != nil && configUpdated {
if config.BootstrapConfig.RunOnce {
if lastPolicyfileApplicationErr != nil {
log.Info("Change to run-once mode detected after a failed policyfile application: starting run-once mode (with 3 retries)...")
return runBootstrapOnce(ctx, c, config, formatter)
}
log.Info("Change to run-once mode detected after a successful policyfile application: exiting...")
return nil
}
applyAfterIterations, thresholdLines, interval, splay = getBootstrappingConfigOrDefaults(c, config)
}
}
}
noPolicyfileApplicationIterations++
Expand All @@ -233,7 +226,11 @@ func runBootstrapPeriodically(ctx context.Context, bootstrappingSvc *blueprint.B
return nil
}

func runBootstrapOnce(ctx context.Context, bootstrappingSvc *blueprint.BootstrappingService, formatter format.Formatter, thresholdLines, interval, splay int) error {
func runBootstrapOnce(ctx context.Context, c *cli.Context, config *utils.Config, formatter format.Formatter) error {

_, thresholdLines, interval, splay := getBootstrappingConfigOrDefaults(c, config)

bootstrappingSvc, formatter := cmd.WireUpBootstrapping(c)
blueprintConfig, _, err := getBlueprintConfig(ctx, bootstrappingSvc, nil, formatter)
if err == nil {
err = applyPolicyfiles(ctx, bootstrappingSvc, blueprintConfig, formatter, thresholdLines)
Expand All @@ -259,6 +256,32 @@ func runBootstrapOnce(ctx context.Context, bootstrappingSvc *blueprint.Bootstrap
return err
}

func getBootstrappingConfigOrDefaults(c *cli.Context, config *utils.Config) (applyAfterIterations, thresholdLines, interval, splay int) {
if c == nil {
return defaultApplyAfterIterations, defaultThresholdLines, defaultIntervalSeconds, defaultSplaySeconds
}
interval = config.BootstrapConfig.IntervalSeconds
if !(interval > 0) {
interval = defaultIntervalSeconds
}

splay = config.BootstrapConfig.SplaySeconds
if !(splay > 0) {
splay = defaultSplaySeconds
}

applyAfterIterations = config.BootstrapConfig.ApplyAfterIterations
if !(applyAfterIterations > 0) {
applyAfterIterations = defaultApplyAfterIterations
}

thresholdLines = c.Int("lines")
if !(thresholdLines > 0) {
thresholdLines = defaultThresholdLines
}
return
}

func getBlueprintConfig(ctx context.Context, bootstrappingSvc *blueprint.BootstrappingService, previousBlueprintConfig *types.BootstrappingConfiguration, formatter format.Formatter) (*types.BootstrappingConfiguration, bool, error) {
log.Debug("getBlueprintConfig")
// Inquire about desired configuration changes to be applied by querying the `GET /blueprint/configuration` endpoint. This will provide a JSON response with the desired configuration changes
Expand Down
60 changes: 44 additions & 16 deletions utils/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ import (
"reflect"
"runtime"
"strings"
"time"

log "github.com/sirupsen/logrus"
"github.com/codegangsta/cli"
"github.com/mitchellh/go-homedir"
log "github.com/sirupsen/logrus"
)

const windowsServerConfigFile = "c:\\cio\\client.xml"
Expand All @@ -35,21 +36,22 @@ const nixServerKeyPath = "/etc/cio/client_ssl/private/key.pem"

// Config stores configuration file contents
type Config struct {
XMLName xml.Name `xml:"concerto"`
APIEndpoint string `xml:"server,attr"`
LogFile string `xml:"log_file,attr"`
LogLevel string `xml:"log_level,attr"`
Certificate Cert `xml:"ssl"`
BootstrapConfig BootstrapConfig `xml:"bootstrap"`
ConfLocation string
ConfFile string
IsHost bool
ConcertoURL string
BrownfieldToken string
CommandPollingToken string
ServerID string
CurrentUserName string
CurrentUserIsAdmin bool
XMLName xml.Name `xml:"concerto"`
APIEndpoint string `xml:"server,attr"`
LogFile string `xml:"log_file,attr"`
LogLevel string `xml:"log_level,attr"`
Certificate Cert `xml:"ssl"`
BootstrapConfig BootstrapConfig `xml:"bootstrap"`
ConfLocation string
ConfFile string
confFileLastLoadedAt time.Time
IsHost bool
ConcertoURL string
BrownfieldToken string
CommandPollingToken string
ServerID string
CurrentUserName string
CurrentUserIsAdmin bool
}

// Cert stores cert files location
Expand Down Expand Up @@ -124,6 +126,30 @@ func InitializeConcertoConfig(c *cli.Context) (*Config, error) {
return cachedConfig, nil
}

// ReloadConcertoConfig checks if the config file was modified and
// if so, attempts to reload it. It returns the resulting config
// (updated or not), whether an modification of the file happened,
// and any errors
func ReloadConcertoConfig(c *cli.Context) (*Config, bool, error) {
fi, err := os.Stat(cachedConfig.ConfFile)
if err != nil {
log.Warnf("Could not stat config file %q to see if it changed: %v", cachedConfig.ConfFile, err)
return cachedConfig, false, err
}
if fi.ModTime().After(cachedConfig.confFileLastLoadedAt) {
log.Infof("Config file %q changed since last reading, reloading configuration...", cachedConfig.ConfFile)
oldConfig := cachedConfig
cachedConfig = nil
_, err = InitializeConcertoConfig(c)
if err != nil {
log.Warnf("Could not load changes to config file %q: %v", cachedConfig.ConfFile, err)
cachedConfig = oldConfig
}
return cachedConfig, true, err
}
return cachedConfig, false, nil
}

func debugShowConfig() {
if log.GetLevel() < log.DebugLevel {
return
Expand Down Expand Up @@ -217,6 +243,7 @@ func (config *Config) readConcertoConfig(c *cli.Context) error {
return err
}
defer xmlFile.Close()
readingTime := time.Now()
b, err := ioutil.ReadAll(xmlFile)
if err != nil {
return fmt.Errorf("configuration File %s couldn't be read", config.ConfFile)
Expand All @@ -225,6 +252,7 @@ func (config *Config) readConcertoConfig(c *cli.Context) error {
if err = xml.Unmarshal(b, &config); err != nil {
return fmt.Errorf("configuration File %s does not have valid XML format", config.ConfFile)
}
config.confFileLastLoadedAt = readingTime

} else {
log.Debugf("Configuration File %s does not exist. Reading environment variables", config.ConfFile)
Expand Down

0 comments on commit 20beb35

Please sign in to comment.