From d3e881198ec4a29d57f12ee0a98c63a71b457c8e Mon Sep 17 00:00:00 2001 From: arehmandev Date: Sun, 29 Apr 2018 02:41:28 +0100 Subject: [PATCH 1/6] Added dryrun, examples and retry for checking deployment status --- README.md | 41 +++++++++++--------- examples/config.yaml | 3 ++ examples/example.sh | 13 +++++++ examples/file.yaml | 9 +++++ examples/nginx-deployment.yaml | 26 +++++++++++++ examples/split.yaml | 11 ++++++ main.go | 71 ++++++++++++++++++++++++---------- render.go | 1 + render_test.go | 16 ++++---- types.go | 1 + 10 files changed, 145 insertions(+), 47 deletions(-) create mode 100644 examples/config.yaml create mode 100755 examples/example.sh create mode 100644 examples/file.yaml create mode 100644 examples/nginx-deployment.yaml create mode 100644 examples/split.yaml diff --git a/README.md b/README.md index 45927c2..ef72eb1 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,7 @@ You can fail an ongoing deployment if there's been a new deployment by adding `- ## Templating You can add the flag --debug-templates to render templates at run time. +Check the examples folder for more info. [Sprig](https://masterminds.github.io/sprig/) is used to add templating functions. @@ -102,29 +103,32 @@ can also be used to iterate over returned list. # split.yaml --- apiVersion: v1 +kind: ConfigMap +metadata: + name: list data: foo: {{ range split .LIST "," }} - {{.}} {{ end }} -kind: ConfigMap -metadata: - name: list + ``` ``` $ export LIST="one,two,three" -$ ./kd -f split.yaml -- --dry-run -o yaml +$ ./kd -f split.yaml --dry-run --debug-templates [INFO] 2017/10/18 15:08:09 main.go:241: deploying configmap/list [INFO] 2017/10/18 15:08:09 main.go:248: apiVersion: v1 -data: - foo: - - one - - two - - three +--- +apiVersion: v1 kind: ConfigMap metadata: name: list +data: + foo: + {{ range split .LIST "," }} + - {{.}} + {{ end }} ``` ### file @@ -135,12 +139,12 @@ metadata: # file.yaml --- apiVersion: v1 -data: - foo: -{{ file .BAR }} kind: ConfigMap metadata: name: list +data: + foo: +{{ file .BAR | indent 4}} ``` ``` @@ -150,17 +154,18 @@ $ cat < config.yaml - three EOF $ export BAR=${PWD}/config.yaml -$ ./kd -f file.yaml -- --dry-run -o yaml +$ ./kd -f file.yaml --dry-run --debug-templates [INFO] 2017/10/18 15:08:09 main.go:241: deploying configmap/list [INFO] 2017/10/18 15:08:09 main.go:248: apiVersion: v1 -data: - foo: - - one - - two - - three +apiVersion: v1 kind: ConfigMap metadata: name: list +data: + foo: + - one + - two + - three ``` ## Configuration diff --git a/examples/config.yaml b/examples/config.yaml new file mode 100644 index 0000000..86f34c6 --- /dev/null +++ b/examples/config.yaml @@ -0,0 +1,3 @@ +- one +- two +- three diff --git a/examples/example.sh b/examples/example.sh new file mode 100755 index 0000000..4815e7f --- /dev/null +++ b/examples/example.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +export NGINX_IMAGE_TAG="1.11-alpine" +kd -c mykube -namespace testing -f nginx-deployment.yaml --debug-templates --dryrun + +export LIST="ENTRY1,ENTRY2,ENTRY3,ENTRY4" +kd -c mykube -n testing -f split.yaml --debug-templates --dryrun + +export BAR="${PWD}/config.yaml" +kd -c mykube -n testing -f file.yaml --debug-templates --dryrun + +echo "Dry run example complete" + diff --git a/examples/file.yaml b/examples/file.yaml new file mode 100644 index 0000000..e7c9539 --- /dev/null +++ b/examples/file.yaml @@ -0,0 +1,9 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: list +data: + foo: +{{ file .BAR | indent 4 }} + diff --git a/examples/nginx-deployment.yaml b/examples/nginx-deployment.yaml new file mode 100644 index 0000000..5f44fcc --- /dev/null +++ b/examples/nginx-deployment.yaml @@ -0,0 +1,26 @@ +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: nginx +spec: + replicas: 5 + template: + metadata: + labels: + name: nginx + spec: + containers: + - name: nginx + image: nginx:{{.NGINX_IMAGE_TAG}} + ports: + - containerPort: 80 + resources: + limits: + cpu: "0.1" + livenessProbe: + httpGet: + path: / + port: 80 + initialDelaySeconds: 10 + timeoutSeconds: 1 diff --git a/examples/split.yaml b/examples/split.yaml new file mode 100644 index 0000000..347ca37 --- /dev/null +++ b/examples/split.yaml @@ -0,0 +1,11 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: list +data: + foo: + {{ range split .LIST "," }} + - {{.}} + {{ end }} + diff --git a/main.go b/main.go index bbca4eb..b912e4a 100644 --- a/main.go +++ b/main.go @@ -18,7 +18,12 @@ import ( yaml "gopkg.in/yaml.v2" ) -const DeployDelaySeconds = 3 +const ( + // DeployDelaySeconds - delay between deployments + DeployDelaySeconds = 3 + // MaxHealthcheckRetries - Amount of times to retry checking of resource after deployment + MaxHealthcheckRetries = 3 +) var ( // Version is set at compile time, passing -ldflags "-X main.Version=" @@ -27,6 +32,9 @@ var ( logInfo *log.Logger logError *log.Logger logDebug *log.Logger + + // dryRun Defaults to false + dryRun bool ) func init() { @@ -53,6 +61,12 @@ func main() { Usage: "debug template output", EnvVar: "DEBUG_TEMPLATES,PLUGIN_DEBUG_TEMPLATES", }, + cli.BoolFlag{ + Name: "dryrun", + Usage: "if true, kd will exit prior to deployment", + EnvVar: "DRY_RUN", + Destination: &dryRun, + }, cli.BoolFlag{ Name: "insecure-skip-tls-verify", Usage: "if true, the server's certificate will not be checked for validity", @@ -196,8 +210,12 @@ func run(c *cli.Context) error { if err := yaml.Unmarshal(r.Template, &r); err != nil { return err } - if err := deploy(c, r); err != nil { - return err + + // Only perform deploy if dry-run is not set to true + if !dryRun { + if err := deploy(c, r); err != nil { + return err + } } } return nil @@ -269,10 +287,10 @@ func isWatchableResouce(r *ObjectResource) bool { included := false watchable := []string{"Deployment", "StatefulSet", "DaemonSet", "Job"} for _, item := range watchable { - if item == r.Kind { - included = true - break - } + if item == r.Kind { + included = true + break + } } return included } @@ -301,8 +319,8 @@ func watchResource(c *cli.Context, r *ObjectResource) error { og := r.DeploymentStatus.ObservedGeneration ready := false - var availableResourceCount int32 = 0 - var unavailableResourceCount int32 = 0 + var availableResourceCount int32 + var unavailableResourceCount int32 for { select { @@ -310,10 +328,21 @@ func watchResource(c *cli.Context, r *ObjectResource) error { return fmt.Errorf("%s rolling update %q timed out after %s", r.Kind, r.Name, c.Duration("timeout").String()) case <-ticker.C: r.DeploymentStatus = DeploymentStatus{} - // @TODO should a one-off error (perhaps network issue) cause us to completly fail? - if err := updateResourceStatus(c, r); err != nil { - return err + + // Retry on error until max retries is met + for attempt := 0; attempt < MaxHealthcheckRetries; attempt++ { + if err := updateResourceStatus(c, r); err != nil { + + // Return error on final try + if attempt == (MaxHealthcheckRetries - 1) { + return err + } + + } else { + break + } } + if c.Bool("debug") { logDebug.Printf("fetching %s %q status: %+v", r.Kind, r.Name, r.DeploymentStatus) } @@ -322,33 +351,33 @@ func watchResource(c *cli.Context, r *ObjectResource) error { switch r.Kind { case "Deployment": - if (r.DeploymentStatus.UnavailableReplicas == 0 && r.DeploymentStatus.AvailableReplicas == r.DeploymentStatus.Replicas) && - r.DeploymentStatus.Replicas == r.DeploymentStatus.UpdatedReplicas { + if (r.DeploymentStatus.UnavailableReplicas == 0 && r.DeploymentStatus.AvailableReplicas == r.DeploymentStatus.Replicas) && + r.DeploymentStatus.Replicas == r.DeploymentStatus.UpdatedReplicas { ready = true } availableResourceCount = r.DeploymentStatus.AvailableReplicas unavailableResourceCount = r.DeploymentStatus.UnavailableReplicas case "StatefulSet": - if (r.DeploymentStatus.ReadyReplicas == r.ObjectSpec.Replicas) && - r.DeploymentStatus.CurrentRevision == r.DeploymentStatus.UpdateRevision { + if (r.DeploymentStatus.ReadyReplicas == r.ObjectSpec.Replicas) && + r.DeploymentStatus.CurrentRevision == r.DeploymentStatus.UpdateRevision { ready = true } availableResourceCount = r.DeploymentStatus.ReadyReplicas - unavailableResourceCount = r.ObjectSpec.Replicas-r.DeploymentStatus.ReadyReplicas + unavailableResourceCount = r.ObjectSpec.Replicas - r.DeploymentStatus.ReadyReplicas case "DaemonSet": - if (r.DeploymentStatus.DesiredNumberScheduled == r.DeploymentStatus.NumberAvailable) && - (r.DeploymentStatus.UpdatedNumberScheduled == r.DeploymentStatus.DesiredNumberScheduled) { + if (r.DeploymentStatus.DesiredNumberScheduled == r.DeploymentStatus.NumberAvailable) && + (r.DeploymentStatus.UpdatedNumberScheduled == r.DeploymentStatus.DesiredNumberScheduled) { ready = true } availableResourceCount = r.DeploymentStatus.NumberAvailable - unavailableResourceCount = r.DeploymentStatus.DesiredNumberScheduled-r.DeploymentStatus.UpdatedNumberScheduled + unavailableResourceCount = r.DeploymentStatus.DesiredNumberScheduled - r.DeploymentStatus.UpdatedNumberScheduled case "Job": if r.DeploymentStatus.Succeeded == 1 { availableResourceCount = 1 - ready = true + ready = true } unavailableResourceCount = 1 } diff --git a/render.go b/render.go index b679a1e..061aef8 100644 --- a/render.go +++ b/render.go @@ -9,6 +9,7 @@ import ( "github.com/Masterminds/sprig" ) +// Render - the function used for rendering templates (with Sprig support) func Render(tmpl string, vars map[string]string) (string, error) { fm := sprig.TxtFuncMap() // Preserve old KD functionality (strings param order vs sprig) diff --git a/render_test.go b/render_test.go index df06d87..5a0411b 100644 --- a/render_test.go +++ b/render_test.go @@ -20,9 +20,9 @@ func readfile(filepath string) string { } func TestRender(t *testing.T) { - test_data := make(map[string]string) - test_data["MY_LIST"] = "one,two,three" - test_data["FILE_PATH"] = "test/complex-file.pem" + testData := make(map[string]string) + testData["MY_LIST"] = "one,two,three" + testData["FILE_PATH"] = "test/complex-file.pem" cases := []struct { name string @@ -39,31 +39,31 @@ func TestRender(t *testing.T) { { name: "Check list variables are rendered", inputdata: readfile("test/list-prerendered.yaml"), - inputvars: test_data, + inputvars: testData, want: readfile("test/list-rendered.yaml"), }, { name: "Check file function is rendered", inputdata: readfile("test/file-prerendered.yaml"), - inputvars: test_data, + inputvars: testData, want: readfile("test/file-rendered.yaml"), }, { name: "Check contains function works as expected", inputdata: readfile("test/contains-prerendered.yaml"), - inputvars: test_data, + inputvars: testData, want: readfile("test/contains-rendered.yaml"), }, { name: "Check hasPrefix function works as expected", inputdata: readfile("test/hasPrefix-prerendered.yaml"), - inputvars: test_data, + inputvars: testData, want: readfile("test/hasPrefix-rendered.yaml"), }, { name: "Check hasSuffix function works as expected", inputdata: readfile("test/hasSuffix-prerendered.yaml"), - inputvars: test_data, + inputvars: testData, want: readfile("test/hasSuffix-rendered.yaml"), }, } diff --git a/types.go b/types.go index b6855ec..0edae98 100644 --- a/types.go +++ b/types.go @@ -78,6 +78,7 @@ type DeploymentStatus struct { Succeeded int32 `yaml:"succeeded,omitempty"` } +// ObjectSpec - fields used for setting StatefulSet update behaviour type ObjectSpec struct { // UpdateStrategy indicates the StatefulSetUpdateStrategy that will be employed to update Pods in the StatefulSet when a revision is made to Template. UpdateStrategy `yaml:"updateStrategy,omitempty"` From 6ba28be8572c2ebbbd4c7c8c9350bb8271fa52fe Mon Sep 17 00:00:00 2001 From: arehmandev Date: Sun, 29 Apr 2018 02:43:11 +0100 Subject: [PATCH 2/6] Added readme --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ef72eb1..90f7f65 100644 --- a/README.md +++ b/README.md @@ -126,9 +126,9 @@ metadata: name: list data: foo: - {{ range split .LIST "," }} - - {{.}} - {{ end }} + - one + - two + - three ``` ### file From de7740746ddca8300698eabfcf02f1b337b6f290 Mon Sep 17 00:00:00 2001 From: arehmandev Date: Sun, 29 Apr 2018 02:51:10 +0100 Subject: [PATCH 3/6] Added to readme and examples --- README.md | 23 +++++++++++------------ examples/split.yaml | 6 +++--- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 90f7f65..37b30a2 100644 --- a/README.md +++ b/README.md @@ -108,15 +108,14 @@ metadata: name: list data: foo: - {{ range split .LIST "," }} - - {{.}} - {{ end }} - +{{ range split .LIST "," }} + - {{ . }} + {{- end -}} ``` ``` $ export LIST="one,two,three" -$ ./kd -f split.yaml --dry-run --debug-templates +$ ./kd -f split.yaml --dryrun --debug-templates [INFO] 2017/10/18 15:08:09 main.go:241: deploying configmap/list [INFO] 2017/10/18 15:08:09 main.go:248: apiVersion: v1 --- @@ -126,9 +125,9 @@ metadata: name: list data: foo: - - one - - two - - three + - one + - two + - three ``` ### file @@ -149,12 +148,12 @@ data: ``` $ cat < config.yaml - - one - - two - - three +- one +- two +- three EOF $ export BAR=${PWD}/config.yaml -$ ./kd -f file.yaml --dry-run --debug-templates +$ ./kd -f file.yaml --dryrun --debug-templates [INFO] 2017/10/18 15:08:09 main.go:241: deploying configmap/list [INFO] 2017/10/18 15:08:09 main.go:248: apiVersion: v1 apiVersion: v1 diff --git a/examples/split.yaml b/examples/split.yaml index 347ca37..73dd183 100644 --- a/examples/split.yaml +++ b/examples/split.yaml @@ -5,7 +5,7 @@ metadata: name: list data: foo: - {{ range split .LIST "," }} - - {{.}} - {{ end }} +{{ range split .LIST "," }} + - {{ . }} + {{- end -}} From 2fd3ae5774e9682c625a8db3831086f502215092 Mon Sep 17 00:00:00 2001 From: arehmandev Date: Sun, 29 Apr 2018 03:11:13 +0100 Subject: [PATCH 4/6] Test ensures kd lists by numbers -> alphabet --- examples/example.sh | 9 +++++---- examples/{config.yaml => vars/config} | 0 main_test.go | 2 +- test/TestListDirectory/1-resource.yaml | 2 ++ test/TestListDirectory/2-resource.yaml | 2 ++ 5 files changed, 10 insertions(+), 5 deletions(-) rename examples/{config.yaml => vars/config} (100%) create mode 100644 test/TestListDirectory/1-resource.yaml create mode 100644 test/TestListDirectory/2-resource.yaml diff --git a/examples/example.sh b/examples/example.sh index 4815e7f..078d324 100755 --- a/examples/example.sh +++ b/examples/example.sh @@ -1,13 +1,14 @@ #!/bin/bash export NGINX_IMAGE_TAG="1.11-alpine" -kd -c mykube -namespace testing -f nginx-deployment.yaml --debug-templates --dryrun +#kd -c mykube -namespace testing -f nginx-deployment.yaml --debug-templates --dryrun export LIST="ENTRY1,ENTRY2,ENTRY3,ENTRY4" -kd -c mykube -n testing -f split.yaml --debug-templates --dryrun +#kd -c mykube -n testing -f split.yaml --debug-templates --dryrun -export BAR="${PWD}/config.yaml" -kd -c mykube -n testing -f file.yaml --debug-templates --dryrun +export BAR="${PWD}/vars/config" +#kd -c mykube -n testing -f file.yaml --debug-templates --dryrun echo "Dry run example complete" +kd -f . --debug-templates --dryrun diff --git a/examples/config.yaml b/examples/vars/config similarity index 100% rename from examples/config.yaml rename to examples/vars/config diff --git a/main_test.go b/main_test.go index 95a36fe..2d58c15 100644 --- a/main_test.go +++ b/main_test.go @@ -55,7 +55,7 @@ func TestListDirectory(t *testing.T) { { name: "Check yaml files exist", input: "test/TestListDirectory/", - want: []string{"test/TestListDirectory/a.yaml", "test/TestListDirectory/b.yaml", "test/TestListDirectory/empty.yaml"}, + want: []string{"test/TestListDirectory/1-resource.yaml", "test/TestListDirectory/2-resource.yaml", "test/TestListDirectory/a.yaml", "test/TestListDirectory/b.yaml", "test/TestListDirectory/empty.yaml"}, }, } diff --git a/test/TestListDirectory/1-resource.yaml b/test/TestListDirectory/1-resource.yaml new file mode 100644 index 0000000..0adbb7c --- /dev/null +++ b/test/TestListDirectory/1-resource.yaml @@ -0,0 +1,2 @@ +--- +noop: nil diff --git a/test/TestListDirectory/2-resource.yaml b/test/TestListDirectory/2-resource.yaml new file mode 100644 index 0000000..0adbb7c --- /dev/null +++ b/test/TestListDirectory/2-resource.yaml @@ -0,0 +1,2 @@ +--- +noop: nil From e78361cd391e2b42c46fa3bec465a5eca9f45c77 Mon Sep 17 00:00:00 2001 From: Abdul Rehman Date: Sun, 29 Apr 2018 03:26:09 +0100 Subject: [PATCH 5/6] Update example.sh --- examples/example.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/example.sh b/examples/example.sh index 078d324..a228c4d 100755 --- a/examples/example.sh +++ b/examples/example.sh @@ -9,6 +9,6 @@ export LIST="ENTRY1,ENTRY2,ENTRY3,ENTRY4" export BAR="${PWD}/vars/config" #kd -c mykube -n testing -f file.yaml --debug-templates --dryrun -echo "Dry run example complete" - kd -f . --debug-templates --dryrun + +echo "Dry run example complete" From 89482670f403cc910f6debc70dfa1e0ce9c89a9b Mon Sep 17 00:00:00 2001 From: arehmandev Date: Thu, 17 May 2018 15:05:12 +0100 Subject: [PATCH 6/6] Added healthcheck sleep between retries --- main.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/main.go b/main.go index b912e4a..4cdb7af 100644 --- a/main.go +++ b/main.go @@ -23,6 +23,8 @@ const ( DeployDelaySeconds = 3 // MaxHealthcheckRetries - Amount of times to retry checking of resource after deployment MaxHealthcheckRetries = 3 + // HealthCheckSleepDuration - the amount of time to sleep (seconds) between healthcehck retries + HealthCheckSleepDuration = time.Duration(int64(2)) * time.Second ) var ( @@ -338,6 +340,9 @@ func watchResource(c *cli.Context, r *ObjectResource) error { return err } + // Sleep between retries + time.Sleep(HealthCheckSleepDuration) + } else { break }