From 9879f0f4960e411d811606cad561a6cfc9a31ab2 Mon Sep 17 00:00:00 2001 From: Nick Tan Date: Sun, 10 Jun 2018 01:33:36 -0700 Subject: [PATCH 1/2] add exec handler to run local command when receiving events --- cmd/config.go | 3 +- cmd/exec.go | 53 ++++++++++++++++ config/config.go | 6 ++ examples/conf/kubewatch.conf.exec.yaml | 14 +++++ pkg/client/run.go | 9 ++- pkg/handlers/exec/exec.go | 83 ++++++++++++++++++++++++++ pkg/handlers/handler.go | 16 ++--- 7 files changed, 173 insertions(+), 11 deletions(-) create mode 100644 cmd/exec.go create mode 100644 examples/conf/kubewatch.conf.exec.yaml create mode 100644 pkg/handlers/exec/exec.go diff --git a/cmd/config.go b/cmd/config.go index e08b86bc8..5e6ac1d0e 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -24,7 +24,7 @@ import ( var configCmd = &cobra.Command{ Use: "config SUBCOMMAND", Short: "config modifies kubewatch configuration", - Long: `config command allows admin setup his own configuration for running kubewatch`, + Long: `config command allows admin setup his own configuration for running kubewatch`, Run: func(cmd *cobra.Command, args []string) { cmd.Help() }, @@ -38,4 +38,5 @@ func init() { configCmd.AddCommand(resourceConfigCmd) configCmd.AddCommand(flockConfigCmd) configCmd.AddCommand(webhookConfigCmd) + configCmd.AddCommand(execConfigCmd) } diff --git a/cmd/exec.go b/cmd/exec.go new file mode 100644 index 000000000..2a8d9822f --- /dev/null +++ b/cmd/exec.go @@ -0,0 +1,53 @@ +/* +Copyright 2016 Skippbox, Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cmd + +import ( + "github.com/Sirupsen/logrus" + "github.com/bitnami-labs/kubewatch/config" + "github.com/spf13/cobra" +) + +// execConfigCmd represents the exec subcommand +var execConfigCmd = &cobra.Command{ + Use: "exec FLAG", + Short: "specific exec configuration", + Long: `specific exec configuration`, + Run: func(cmd *cobra.Command, args []string) { + conf, err := config.New() + if err != nil { + logrus.Fatal(err) + } + + execCmd, err := cmd.Flags().GetString("cmd") + if err == nil { + if len(execCmd) > 0 { + conf.Handler.Exec.Cmd = execCmd + } + } else { + logrus.Fatal(err) + } + + if err = conf.Write(); err != nil { + logrus.Fatal(err) + } + }, +} + +func init() { + execConfigCmd.Flags().StringP("cmd", "c", "", "Specify local command/shell to run") +} diff --git a/config/config.go b/config/config.go index 7b7cf6ca9..3d85799ea 100644 --- a/config/config.go +++ b/config/config.go @@ -33,6 +33,7 @@ type Handler struct { Mattermost Mattermost `json:"mattermost"` Flock Flock `json:"flock"` Webhook Webhook `json:"webhook"` + Exec Exec `json:"exec"` } // Resource contains resource configuration @@ -91,6 +92,11 @@ type Webhook struct { Url string `json:"url"` } +// Exec contains exec configuration +type Exec struct { + Cmd string `json:"cmd"` +} + // New creates new config object func New() (*Config, error) { c := &Config{} diff --git a/examples/conf/kubewatch.conf.exec.yaml b/examples/conf/kubewatch.conf.exec.yaml new file mode 100644 index 000000000..b57a8e662 --- /dev/null +++ b/examples/conf/kubewatch.conf.exec.yaml @@ -0,0 +1,14 @@ +handler: + exec: + cmd: "echo {reason} {name}" +resource: + deployment: false + replicationcontroller: false + replicaset: false + daemonset: false + services: false + pod: false + job: false + persistentvolume: false + configmap: true + ingress: false diff --git a/pkg/client/run.go b/pkg/client/run.go index 93931034b..1f393f7d2 100644 --- a/pkg/client/run.go +++ b/pkg/client/run.go @@ -20,12 +20,13 @@ import ( "log" "github.com/bitnami-labs/kubewatch/config" - "github.com/bitnami-labs/kubewatch/pkg/handlers" - "github.com/bitnami-labs/kubewatch/pkg/handlers/slack" "github.com/bitnami-labs/kubewatch/pkg/controller" + "github.com/bitnami-labs/kubewatch/pkg/handlers" + "github.com/bitnami-labs/kubewatch/pkg/handlers/exec" + "github.com/bitnami-labs/kubewatch/pkg/handlers/flock" "github.com/bitnami-labs/kubewatch/pkg/handlers/hipchat" "github.com/bitnami-labs/kubewatch/pkg/handlers/mattermost" - "github.com/bitnami-labs/kubewatch/pkg/handlers/flock" + "github.com/bitnami-labs/kubewatch/pkg/handlers/slack" "github.com/bitnami-labs/kubewatch/pkg/handlers/webhook" ) @@ -43,6 +44,8 @@ func Run(conf *config.Config) { eventHandler = new(flock.Flock) case len(conf.Handler.Webhook.Url) > 0: eventHandler = new(webhook.Webhook) + case len(conf.Handler.Exec.Cmd) > 0: + eventHandler = new(exec.Exec) default: eventHandler = new(handlers.Default) } diff --git a/pkg/handlers/exec/exec.go b/pkg/handlers/exec/exec.go new file mode 100644 index 000000000..9189b2445 --- /dev/null +++ b/pkg/handlers/exec/exec.go @@ -0,0 +1,83 @@ +/* +Copyright 2016 Skippbox, Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package exec + +import ( + "log" + "os" + "os/exec" + "strings" + + "github.com/bitnami-labs/kubewatch/config" + "github.com/bitnami-labs/kubewatch/pkg/event" +) + +// Exec handler implements handler.Handler interface, +// Notify event to run local command/shell +type Exec struct { + Cmd string +} + +// Init prepares Exec configuration +func (s *Exec) Init(c *config.Config) error { + cmd := c.Handler.Exec.Cmd + + if cmd == "" { + cmd = os.Getenv("KW_EXEC_CMD") + } + + s.Cmd = cmd + return nil +} + +func (s *Exec) ObjectCreated(obj interface{}) { + handleEvent(s, obj, "created") +} + +func (s *Exec) ObjectDeleted(obj interface{}) { + handleEvent(s, obj, "deleted") +} + +func (s *Exec) ObjectUpdated(oldObj, newObj interface{}) { + handleEvent(s, newObj, "updated") +} + +func handleEvent(s *Exec, obj interface{}, action string) { + e := event.New(obj, action) + + var cmdAndArgs []string + cmdCli := strings.Split(s.Cmd, " ") + for _, c := range cmdCli { + c = strings.Replace(c, "{reason}", e.Reason, -1) + c = strings.Replace(c, "{name}", e.Name, -1) + + cmdAndArgs = append(cmdAndArgs, c) + } + + if err := runCmd(cmdAndArgs); err != nil { + log.Printf("Exec local command %v failed %s", cmdAndArgs, err) + } else { + log.Printf("Exec local command successfully %s", cmdAndArgs) + } +} + +func runCmd(command []string) error { + cmd := exec.Command(command[0], command[1:]...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + return cmd.Run() +} diff --git a/pkg/handlers/handler.go b/pkg/handlers/handler.go index aab5fd4e4..79c40f6d9 100644 --- a/pkg/handlers/handler.go +++ b/pkg/handlers/handler.go @@ -18,10 +18,11 @@ package handlers import ( "github.com/bitnami-labs/kubewatch/config" - "github.com/bitnami-labs/kubewatch/pkg/handlers/slack" + "github.com/bitnami-labs/kubewatch/pkg/handlers/exec" + "github.com/bitnami-labs/kubewatch/pkg/handlers/flock" "github.com/bitnami-labs/kubewatch/pkg/handlers/hipchat" "github.com/bitnami-labs/kubewatch/pkg/handlers/mattermost" - "github.com/bitnami-labs/kubewatch/pkg/handlers/flock" + "github.com/bitnami-labs/kubewatch/pkg/handlers/slack" "github.com/bitnami-labs/kubewatch/pkg/handlers/webhook" ) @@ -36,12 +37,13 @@ type Handler interface { // Map maps each event handler function to a name for easily lookup var Map = map[string]interface{}{ - "default": &Default{}, - "slack": &slack.Slack{}, - "hipchat": &hipchat.Hipchat{}, + "default": &Default{}, + "slack": &slack.Slack{}, + "hipchat": &hipchat.Hipchat{}, "mattermost": &mattermost.Mattermost{}, - "flock": &flock.Flock{}, - "webhook": &webhook.Webhook{}, + "flock": &flock.Flock{}, + "webhook": &webhook.Webhook{}, + "exec": &exec.Exec{}, } // Default handler implements Handler interface, From 3fe9e5d48437c5f38a0726ffc542805f9598447c Mon Sep 17 00:00:00 2001 From: Nick Tan Date: Wed, 13 Jun 2018 07:43:51 -0700 Subject: [PATCH 2/2] add example in readme --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index fd9528c36..02bf4faa1 100644 --- a/README.md +++ b/README.md @@ -159,6 +159,21 @@ $ kubewatch config slack --channel --token $ kubewatch config flock --url ``` +### Configure exec + +Config the `exec` handler to run local command when receive the event + +```console +$ kubewatch config exec --cmd "" +``` +the local command can have arguments and here are the placeholders substituted by each event object: +* \{reason\} - the reason of the event, "created", "deleted", "updated" +* \{name\} - the name of the event source object, in format of "/", Ex., 'default/my-test-configmap' +Example of using placeholder: +```console +$ kubewatch config exec --cmd "echo {name} {reason}" +``` + ### Configure resources to be watched ```console