From ddb5be1b9df2be0a86468f8e76afbda755785afb Mon Sep 17 00:00:00 2001 From: sgandon Date: Wed, 19 Jun 2019 01:46:32 +0200 Subject: [PATCH] feat: optionally show logs for hooks (#699) Resolves #689 This adds a new yaml entry for the hook definition to allow the users to specifcy if they want to show the `command` logs or not. here is an example. ``` releases: - name: myapp chart: mychart # *snip* hooks: - events: ["cleanup"] showlogs: true command: "kubectl" args: ["get", "ingress"] ``` this will display the following output: ``` hook[cleanup] logs | NAME HOSTS ADDRESS PORTS AGE hook[cleanup] logs | catalog-gateway tdc.foo 80 2d6h hook[cleanup] logs | dataset foobar.barr.foo.xxxxxxx.com 80 2d6h hook[cleanup] logs | rating fooba.barr.foo.xxxxxxx.com 80 2d6h hook[cleanup] logs | sharing foobar.barr.foo.xxxxxxx.com 80 2d6h hook[cleanup] logs | tpsvc-iam-dev foo.barr.foo.xxxxxxx.com 80 2d6h hook[cleanup] logs | tpsvc-iam-front bar.barr.foo.xxxxxxx.com 80 2d6h ``` --- README.md | 4 +++- pkg/event/bus.go | 17 ++++++++++++----- pkg/event/bus_test.go | 29 ++++++++++++++++++++++------- 3 files changed, 37 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index e533d23c..08c4f986 100644 --- a/README.md +++ b/README.md @@ -829,9 +829,10 @@ A Helmfile hook is a per-release extension point that is composed of: - `events` - `command` - `args` +- `showlogs` Helmfile triggers various `events` while it is running. -Once `events` are triggered, associated `hooks` are executed, by running the `command` with `args`. +Once `events` are triggered, associated `hooks` are executed, by running the `command` with `args`. The standard output of the `command` will be displayed if `showlogs` is set and it's value is `true`. Currently supported `events` are: @@ -857,6 +858,7 @@ releases: # *snip* hooks: - events: ["prepare", "cleanup"] + showlogs: true command: "echo" args: ["{{`{{.Environment.Name}}`}}", "{{`{{.Release.Name}}`}}", "{{`{{.HelmfileCommand}}`}}\ "] diff --git a/pkg/event/bus.go b/pkg/event/bus.go index aa6fe02e..398d3d18 100644 --- a/pkg/event/bus.go +++ b/pkg/event/bus.go @@ -2,18 +2,21 @@ package event import ( "fmt" + "os" + "strings" + "github.com/roboll/helmfile/pkg/environment" "github.com/roboll/helmfile/pkg/helmexec" "github.com/roboll/helmfile/pkg/tmpl" "go.uber.org/zap" - "os" ) type Hook struct { - Name string `yaml:"name"` - Events []string `yaml:"events"` - Command string `yaml:"command"` - Args []string `yaml:"args"` + Name string `yaml:"name"` + Events []string `yaml:"events"` + Command string `yaml:"command"` + Args []string `yaml:"args"` + ShowLogs bool `yaml:"showlogs"` } type event struct { @@ -90,6 +93,10 @@ func (bus *Bus) Trigger(evt string, context map[string]interface{}) (bool, error bytes, err := bus.Runner.Execute(command, args, map[string]string{}) bus.Logger.Debugf("hook[%s]: %s\n", name, string(bytes)) + if hook.ShowLogs { + prefix := fmt.Sprintf("\nhook[%s] logs | ", evt) + bus.Logger.Infow(prefix + strings.ReplaceAll(string(bytes), "\n", prefix)) + } if err != nil { return false, fmt.Errorf("hook[%s]: command `%s` failed: %v", name, command, err) diff --git a/pkg/event/bus_test.go b/pkg/event/bus_test.go index a852d51c..6dfaf2d0 100644 --- a/pkg/event/bus_test.go +++ b/pkg/event/bus_test.go @@ -2,10 +2,13 @@ package event import ( "fmt" - "github.com/roboll/helmfile/pkg/environment" - "github.com/roboll/helmfile/pkg/helmexec" "os" "testing" + + "github.com/roboll/helmfile/pkg/environment" + "github.com/roboll/helmfile/pkg/helmexec" + "go.uber.org/zap" + "go.uber.org/zap/zaptest/observer" ) var logger = helmexec.NewLogger(os.Stdout, "warn") @@ -35,14 +38,21 @@ func TestTrigger(t *testing.T) { }{ { "okhook1", - &Hook{"okhook1", []string{"foo"}, "ok", []string{}}, + &Hook{"okhook1", []string{"foo"}, "ok", []string{}, true}, + "foo", + true, + "", + }, + { + "okhooké", + &Hook{"okhook2", []string{"foo"}, "ok", []string{}, false}, "foo", true, "", }, { "missinghook1", - &Hook{"okhook1", []string{"foo"}, "ok", []string{}}, + &Hook{"okhook1", []string{"foo"}, "ok", []string{}, false}, "bar", false, "", @@ -56,14 +66,14 @@ func TestTrigger(t *testing.T) { }, { "nghook1", - &Hook{"nghook1", []string{"foo"}, "ng", []string{}}, + &Hook{"nghook1", []string{"foo"}, "ng", []string{}, false}, "foo", false, "hook[nghook1]: command `ng` failed: cmd failed due to invalid cmd: ng", }, { "nghook2", - &Hook{"nghook2", []string{"foo"}, "ok", []string{"ng"}}, + &Hook{"nghook2", []string{"foo"}, "ok", []string{"ng"}, false}, "foo", false, "hook[nghook2]: command `ok` failed: cmd failed due to invalid arg: ng", @@ -77,13 +87,15 @@ func TestTrigger(t *testing.T) { if c.hook != nil { hooks = append(hooks, *c.hook) } + observer, observedLogs := observer.New(zap.InfoLevel) + zeLogger := zap.New(observer).Sugar() bus := &Bus{ Hooks: hooks, StateFilePath: "path/to/helmfile.yaml", BasePath: "path/to", Namespace: "myns", Env: environment.Environment{Name: "prod"}, - Logger: logger, + Logger: zeLogger, ReadFile: readFile, } @@ -109,5 +121,8 @@ func TestTrigger(t *testing.T) { t.Errorf("unexpected error for case \"%s\": %v", c.name, err) } } + if observedLogs.Len() != 0 && !hooks[0].ShowLogs { + t.Errorf("unexpected error for case \"%s\": Logs should not be created : %v", c.name, observedLogs.All()) + } } }