Skip to content

Commit

Permalink
added a resource reload command
Browse files Browse the repository at this point in the history
  • Loading branch information
HeavyHorst committed Aug 13, 2018
1 parent 9eda77c commit ec5c624
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 25 deletions.
9 changes: 5 additions & 4 deletions cmd/remco/config.go
Expand Up @@ -69,10 +69,11 @@ type DefaultBackends struct {

// Resource is the representation of an resource configuration
type Resource struct {
Exec template.ExecConfig
StartCmd string `toml:"start_cmd" json:"reload_cmd"`
Template []*template.Renderer
Backends BackendConfigs `toml:"backend"`
Exec template.ExecConfig
StartCmd string `toml:"start_cmd" json:"start_cmd"`
ReloadCmd string `toml:"reload_cmd" json:"reload_cmd"`
Template []*template.Renderer
Backends BackendConfigs `toml:"backend"`

// defaults to the filename of the resource
Name string
Expand Down
1 change: 1 addition & 0 deletions cmd/remco/supervisor.go
Expand Up @@ -188,6 +188,7 @@ func (ru *Supervisor) runResource(r []Resource, stop, stopped chan struct{}) {
Template: r.Template,
Name: r.Name,
StartCmd: r.StartCmd,
ReloadCmd: r.ReloadCmd,
Connectors: backendConfigs,
}
res, err := template.NewResourceFromResourceConfig(ctx, ru.reapLock, rsc)
Expand Down
16 changes: 10 additions & 6 deletions pkg/template/renderer.go
Expand Up @@ -108,7 +108,7 @@ func (s *Renderer) createStageFile(funcMap map[string]interface{}) error {
// overwriting the target config file. Finally, syncFile will run a reload command
// if set to have the application or service pick up the changes.
// It returns a boolean indicating if the file has changed and an error if any.
func (s *Renderer) syncFiles() (bool, error) {
func (s *Renderer) syncFiles(runCommands bool) (bool, error) {
var changed bool
staged := s.stageFile.Name()
defer os.Remove(staged)
Expand All @@ -128,8 +128,10 @@ func (s *Renderer) syncFiles() (bool, error) {
"config": s.Dst,
}).Info("target config out of sync")

if err := s.check(staged); err != nil {
return changed, errors.Wrap(err, "config check failed")
if runCommands {
if err := s.check(staged); err != nil {
return changed, errors.Wrap(err, "config check failed")
}
}

s.logger.WithFields(logrus.Fields{
Expand All @@ -148,8 +150,10 @@ func (s *Renderer) syncFiles() (bool, error) {
os.Chown(s.Dst, s.UID, s.GID)
changed = true

if err := s.reload(); err != nil {
return changed, errors.Wrap(err, "reload command failed")
if runCommands {
if err := s.reload(); err != nil {
return changed, errors.Wrap(err, "reload command failed")
}
}

s.logger.WithFields(logrus.Fields{
Expand Down Expand Up @@ -236,7 +240,7 @@ func (s *Renderer) reload() error {
}

func execCommand(cmd string, logger *logrus.Entry, rl *sync.RWMutex) ([]byte, error) {
logger.Debug("Running " + cmd)
logger.Debugf("Running %q", cmd)
c := exec.Command("/bin/sh", "-c", cmd)

if rl != nil {
Expand Down
34 changes: 22 additions & 12 deletions pkg/template/resource.go
Expand Up @@ -47,8 +47,9 @@ type Resource struct {
sources []*Renderer
logger *logrus.Entry

exec Executor
startCmd string
exec Executor
startCmd string
reloadCmd string
// SignalChan is a channel to send os.Signal's to all child processes.
SignalChan chan os.Signal

Expand All @@ -60,8 +61,9 @@ type Resource struct {

// ResourceConfig is a configuration struct to create a new resource.
type ResourceConfig struct {
Exec ExecConfig
StartCmd string
Exec ExecConfig
StartCmd string
ReloadCmd string

// Template is the configuration for all template options.
// You can configure as much template-destination pairs as you like.
Expand Down Expand Up @@ -92,7 +94,7 @@ func NewResourceFromResourceConfig(ctx context.Context, reapLock *sync.RWMutex,

logger := log.WithFields(logrus.Fields{"resource": r.Name})
exec := NewExecutor(r.Exec.Command, r.Exec.ReloadSignal, r.Exec.KillSignal, r.Exec.KillTimeout, r.Exec.Splay, logger)
res, err := NewResource(backendList, r.Template, r.Name, exec, r.StartCmd)
res, err := NewResource(backendList, r.Template, r.Name, exec, r.StartCmd, r.ReloadCmd)
if err != nil {
for _, v := range backendList {
v.Close()
Expand All @@ -102,7 +104,7 @@ func NewResourceFromResourceConfig(ctx context.Context, reapLock *sync.RWMutex,
}

// NewResource creates a Resource.
func NewResource(backends []Backend, sources []*Renderer, name string, exec Executor, startCmd string) (*Resource, error) {
func NewResource(backends []Backend, sources []*Renderer, name string, exec Executor, startCmd, reloadCmd string) (*Resource, error) {
if len(backends) == 0 {
return nil, fmt.Errorf("a valid StoreClient is required")
}
Expand All @@ -125,6 +127,7 @@ func NewResource(backends []Backend, sources []*Renderer, name string, exec Exec
SignalChan: make(chan os.Signal, 1),
exec: exec,
startCmd: startCmd,
reloadCmd: reloadCmd,
}

// initialize the inidividual backend memkv Stores
Expand Down Expand Up @@ -192,14 +195,14 @@ func (t *Resource) setVars(storeClient Backend) error {
return nil
}

func (t *Resource) createStageFileAndSync() (bool, error) {
func (t *Resource) createStageFileAndSync(runCommands bool) (bool, error) {
var changed bool
for _, s := range t.sources {
err := s.createStageFile(t.funcMap)
if err != nil {
return changed, errors.Wrap(err, "create stage file failed")
}
c, err := s.syncFiles()
c, err := s.syncFiles(runCommands)
changed = changed || c
if err != nil {
return changed, errors.Wrap(err, "sync files failed")
Expand All @@ -213,7 +216,7 @@ func (t *Resource) createStageFileAndSync() (bool, error) {
// from the store, then we stage a candidate configuration file, and finally sync
// things up.
// It returns an error if any.
func (t *Resource) process(storeClients []Backend) (bool, error) {
func (t *Resource) process(storeClients []Backend, runCommands bool) (bool, error) {
var changed bool
var err error
for _, storeClient := range storeClients {
Expand All @@ -224,7 +227,7 @@ func (t *Resource) process(storeClients []Backend) (bool, error) {
}
}
}
if changed, err = t.createStageFileAndSync(); err != nil {
if changed, err = t.createStageFileAndSync(runCommands); err != nil {
return changed, errors.Wrap(err, "createStageFileAndSync failed")
}
return changed, nil
Expand Down Expand Up @@ -255,7 +258,7 @@ retryloop:
case <-ctx.Done():
return
case <-retryChan:
if _, err := t.process(t.backends); err != nil {
if _, err := t.process(t.backends, t.startCmd == ""); err != nil {
switch err.(type) {
case berr.BackendError:
err := err.(berr.BackendError)
Expand Down Expand Up @@ -344,7 +347,7 @@ retryloop:
for {
select {
case storeClient := <-processChan:
changed, err := t.process([]Backend{storeClient})
changed, err := t.process([]Backend{storeClient}, true)
if err != nil {
switch err.(type) {
case berr.BackendError:
Expand All @@ -356,6 +359,13 @@ retryloop:
if err := t.exec.Reload(); err != nil {
t.logger.Error(err)
}

if t.reloadCmd != "" {
output, err := execCommand(t.reloadCmd, t.logger, nil)
if err != nil {
t.logger.Error(fmt.Sprintf("failed to execute the resource reload cmd - %q", string(output)))
}
}
}
case s := <-t.SignalChan:
t.exec.SignalChild(s)
Expand Down
6 changes: 3 additions & 3 deletions pkg/template/resource_test.go
Expand Up @@ -68,7 +68,7 @@ func (s *ResourceSuite) SetUpSuite(t *C) {
}

exec := NewExecutor("", "", "", 0, 0, nil)
res, err := NewResource([]Backend{s.backend}, []*Renderer{s.renderer}, "test", exec, "")
res, err := NewResource([]Backend{s.backend}, []*Renderer{s.renderer}, "test", exec, "", "")
t.Assert(err, IsNil)
s.resource = res
}
Expand Down Expand Up @@ -103,12 +103,12 @@ func (s *ResourceSuite) TestSetVars(t *C) {
}

func (s *ResourceSuite) TestCreateStageFileAndSync(t *C) {
_, err := s.resource.createStageFileAndSync()
_, err := s.resource.createStageFileAndSync(true)
t.Check(err, IsNil)
}

func (s *ResourceSuite) TestProcess(t *C) {
_, err := s.resource.process(s.resource.backends)
_, err := s.resource.process(s.resource.backends, true)
t.Check(err, IsNil)

data, err := ioutil.ReadFile("/tmp/remco-basic-test.conf")
Expand Down

0 comments on commit ec5c624

Please sign in to comment.