From 035c704f7b5646991e2f9fd0c006bf5b78b9e8ce Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 8 Feb 2017 11:49:50 +0100 Subject: [PATCH 001/166] Initial commit From b11478212a6ca8c2efa42378614252628db2e9f5 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 8 Feb 2017 11:50:18 +0100 Subject: [PATCH 002/166] chore: Add .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..9f11b755a --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea/ From aff2f7ac1de0e285b786b33b1b39905b6ecadd1f Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 8 Feb 2017 11:50:26 +0100 Subject: [PATCH 003/166] docs: Add initial WIP README --- README.md | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 000000000..f91722832 --- /dev/null +++ b/README.md @@ -0,0 +1,45 @@ +KonTemplate - A simple Kubernetes templater +=========================================== + +I made this tool out of frustration with the available ways to template Kubernetes resource files. All I want out of +such a tool is a way to specify lots of resources with placeholders that get filled in with specific values, based on +which context (i.e. k8s cluster) is specified. + +## Overview + +KonTemplate lets you describe resources as you normally would in a simple folder structure: + +``` +. +├── prod-cluster.json +└── some-api + ├── deployment.yaml + └── service.yaml +``` + +This example has all resources belonging to `some-api` (no file naming conventions enforced at all!) in the `some-api` +folder and the configuration for the cluster `prod-cluster` in the corresponding file. + +Lets take a short look at `prod-cluster.json`: + +```json +{ + "context": "k8s.prod.mydomain.com", + "include": [ + { + "name": "some-api", + "values": { + "importantFeature": true, + "apiPort": 4567 + } + } + ] +} +``` + + +Those values are then templated into the resource files of `some-api`. + +## Usage + +You must have `kubectl` installed to use KonTemplate. From bb45bfa737896c6f26ef4a816458bbfe508b8280 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 8 Feb 2017 11:50:39 +0100 Subject: [PATCH 004/166] feat context: Add types and loading functions --- context/context.go | 51 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 context/context.go diff --git a/context/context.go b/context/context.go new file mode 100644 index 000000000..e842feae1 --- /dev/null +++ b/context/context.go @@ -0,0 +1,51 @@ +package context + +import ( + "encoding/json" + "github.com/polydawn/meep" + "io/ioutil" + "path" +) + +type ResourceSet struct { + Name string `json:"name"` + Values map[string]interface{} `json:"values"` +} + +type Context struct { + Name string `json:"context"` + Global map[string]interface{} `json:"global"` + ResourceSets []ResourceSet `json:"include"` + BaseDir string +} + +type ContextLoadingError struct { + meep.AllTraits + Filename string +} + +// Attempt to load and deserialise a Context from the specified file. +func LoadContextFromFile(filename string) (*Context, error) { + file, err := ioutil.ReadFile(filename) + + if err != nil { + return nil, meep.New( + &ContextLoadingError{Filename: filename}, + meep.Cause(err), + ) + } + + var c Context + + err = json.Unmarshal(file, &c) + if err != nil { + return nil, meep.New( + &ContextLoadingError{Filename: filename}, + meep.Cause(err), + ) + } + + c.BaseDir = path.Dir(filename) + + return &c, nil +} From 9e3ee3f2bbce7c7cb2aaea3de2ffb23b17a030ca Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 8 Feb 2017 11:50:49 +0100 Subject: [PATCH 005/166] feat templater: Add initial templating support --- templater/templater.go | 97 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 templater/templater.go diff --git a/templater/templater.go b/templater/templater.go new file mode 100644 index 000000000..f4be1e6fa --- /dev/null +++ b/templater/templater.go @@ -0,0 +1,97 @@ +package templater + +import ( + "fmt" + "io/ioutil" + "strings" + "os" + "path" + "text/template" + "bytes" + + "github.com/tazjin/kontemplate/context" + "github.com/polydawn/meep" +) + +// Error that is caused by non-existent template files being specified +type TemplateNotFoundError struct { + meep.AllTraits + Name string +} + +// Error that is caused during templating, e.g. required value being absent or invalid template format +type TemplatingError struct { + meep.AllTraits +} + +func LoadAndPrepareTemplates(c *context.Context) ([]string, error) { + output := make([]string, 0) + + for _, rs := range c.ResourceSets { + fmt.Fprintf(os.Stderr,"Loading resources for %s\n", rs.Name) + + rp := path.Join(c.BaseDir, rs.Name) + files, err := ioutil.ReadDir(rp) + + if err != nil { + return nil, meep.New( + &TemplateNotFoundError{Name: rs.Name}, + meep.Cause(err), + ) + } + + + for _, file := range files { + if !file.IsDir() && isResourceFile(file) { + p := path.Join(rp, file.Name()) + o, err := templateFile(c, &rs, p) + + if err != nil { + return nil, err + } + + output = append(output, o) + } + } + } + + return output, nil +} + +func templateFile(c *context.Context, rs *context.ResourceSet, filename string) (string, error) { + tpl, err := template.ParseFiles(filename) + + if err != nil { + return "", meep.New( + &TemplateNotFoundError{Name: filename}, + meep.Cause(err), + ) + } + + var b bytes.Buffer + + // Merge global and resourceset-specific values (don't override from global) + for k, v := range c.Global { + if _, ok := rs.Values[k]; !ok { + rs.Values[k] = v + } + } + + err = tpl.Execute(&b, rs.Values) + + if err != nil { + return "", meep.New( + &TemplatingError{}, + meep.Cause(err), + ) + } + + return b.String(), nil +} + +// Checks whether a file is a resource file (i.e. is YAML or JSON) +func isResourceFile(f os.FileInfo) bool { + return strings.HasSuffix(f.Name(), "yaml") || + strings.HasSuffix(f.Name(), "yml") || + strings.HasSuffix(f.Name(), "json") +} From 8fb24f9f750c4d87f1f36f11ca80f86c78832be6 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 8 Feb 2017 11:51:08 +0100 Subject: [PATCH 006/166] feat main: Initial program implementation & example --- example/prod-cluster.json | 16 +++++++++++++ example/some-api/deployment.yaml | 0 example/some-api/service.yaml | 5 ++++ main.go | 39 ++++++++++++++++++++++++++++++++ 4 files changed, 60 insertions(+) create mode 100644 example/prod-cluster.json create mode 100644 example/some-api/deployment.yaml create mode 100644 example/some-api/service.yaml create mode 100644 main.go diff --git a/example/prod-cluster.json b/example/prod-cluster.json new file mode 100644 index 000000000..76246ca38 --- /dev/null +++ b/example/prod-cluster.json @@ -0,0 +1,16 @@ +{ + "context": "k8s.prod.mydomain.com", + "global": { + "globalTest": "lizards" + }, + "include": [ + { + "name": "some-api", + "values": { + "version": "1.0-SNAPSHOT-0e6884d", + "importantFeature": true, + "apiPort": 4567 + } + } + ] +} diff --git a/example/some-api/deployment.yaml b/example/some-api/deployment.yaml new file mode 100644 index 000000000..e69de29bb diff --git a/example/some-api/service.yaml b/example/some-api/service.yaml new file mode 100644 index 000000000..6aee87880 --- /dev/null +++ b/example/some-api/service.yaml @@ -0,0 +1,5 @@ +--- +name: foo +importantFeature: {{ .importantFeature }} +port: {{ .apiPort }} +globalTest: {{ .globalTest }} diff --git a/main.go b/main.go new file mode 100644 index 000000000..c33ec133d --- /dev/null +++ b/main.go @@ -0,0 +1,39 @@ +package main + +import ( + "os" + "fmt" + + "github.com/tazjin/kontemplate/context" + "github.com/tazjin/kontemplate/templater" +) + +func main() { + args := os.Args[1:] + if len(args) == 0 { + fmt.Fprintln(os.Stderr, "Usage: kontemplate ") + os.Exit(1) + } + + c, err := context.LoadContextFromFile(os.Args[1]) + + if err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err) + os.Exit(1) + } + + fmt.Fprintf(os.Stderr,"Applying cluster %s\n", c.Name) + + for _, rs := range c.ResourceSets { + fmt.Fprintf(os.Stderr,"Applying resource %s with values %v\n", rs.Name, rs.Values) + resources, err := templater.LoadAndPrepareTemplates(c) + + if err != nil { + fmt.Println(err) + } + + for _, r := range resources { + fmt.Print(r) + } + } +} From 8fac7c1a415d6ea5e41c28f4e7ab5c1ef0883997 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 8 Feb 2017 11:55:59 +0100 Subject: [PATCH 007/166] chore: Better example & gofmt --- example/prod-cluster.json | 2 +- example/some-api/deployment.yaml | 0 example/some-api/service.yaml | 5 ----- example/some-api/some-api.yaml | 34 ++++++++++++++++++++++++++++++++ main.go | 6 +++--- templater/templater.go | 9 ++++----- 6 files changed, 42 insertions(+), 14 deletions(-) delete mode 100644 example/some-api/deployment.yaml delete mode 100644 example/some-api/service.yaml create mode 100644 example/some-api/some-api.yaml diff --git a/example/prod-cluster.json b/example/prod-cluster.json index 76246ca38..70e2365f1 100644 --- a/example/prod-cluster.json +++ b/example/prod-cluster.json @@ -1,7 +1,7 @@ { "context": "k8s.prod.mydomain.com", "global": { - "globalTest": "lizards" + "globalVar": "lizards" }, "include": [ { diff --git a/example/some-api/deployment.yaml b/example/some-api/deployment.yaml deleted file mode 100644 index e69de29bb..000000000 diff --git a/example/some-api/service.yaml b/example/some-api/service.yaml deleted file mode 100644 index 6aee87880..000000000 --- a/example/some-api/service.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -name: foo -importantFeature: {{ .importantFeature }} -port: {{ .apiPort }} -globalTest: {{ .globalTest }} diff --git a/example/some-api/some-api.yaml b/example/some-api/some-api.yaml new file mode 100644 index 000000000..6f6a29a15 --- /dev/null +++ b/example/some-api/some-api.yaml @@ -0,0 +1,34 @@ +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: some-api +spec: + replicas: 1 + template: + metadata: + labels: + app: some-api + spec: + containers: + - image: my.container.repo/some-api:{{ .version }} + name: some-api + env: + - name: ENABLE_IMPORTANT_FEATURE + value: {{ .importantFeature }} + - name: SOME_GLOBAL_VAR + value: {{ .globalVar }} +--- +apiVersion: v1 +kind: Service +metadata: + name: some-api + labels: + app: some-api +spec: + selector: + app: some-api + ports: + - port: 80 + targetPort: {{ .apiPort }} + name: http diff --git a/main.go b/main.go index c33ec133d..158c9a566 100644 --- a/main.go +++ b/main.go @@ -1,8 +1,8 @@ package main import ( - "os" "fmt" + "os" "github.com/tazjin/kontemplate/context" "github.com/tazjin/kontemplate/templater" @@ -22,10 +22,10 @@ func main() { os.Exit(1) } - fmt.Fprintf(os.Stderr,"Applying cluster %s\n", c.Name) + fmt.Fprintf(os.Stderr, "Applying cluster %s\n", c.Name) for _, rs := range c.ResourceSets { - fmt.Fprintf(os.Stderr,"Applying resource %s with values %v\n", rs.Name, rs.Values) + fmt.Fprintf(os.Stderr, "Applying resource %s with values %v\n", rs.Name, rs.Values) resources, err := templater.LoadAndPrepareTemplates(c) if err != nil { diff --git a/templater/templater.go b/templater/templater.go index f4be1e6fa..27beff371 100644 --- a/templater/templater.go +++ b/templater/templater.go @@ -1,16 +1,16 @@ package templater import ( + "bytes" "fmt" "io/ioutil" - "strings" "os" "path" + "strings" "text/template" - "bytes" - "github.com/tazjin/kontemplate/context" "github.com/polydawn/meep" + "github.com/tazjin/kontemplate/context" ) // Error that is caused by non-existent template files being specified @@ -28,7 +28,7 @@ func LoadAndPrepareTemplates(c *context.Context) ([]string, error) { output := make([]string, 0) for _, rs := range c.ResourceSets { - fmt.Fprintf(os.Stderr,"Loading resources for %s\n", rs.Name) + fmt.Fprintf(os.Stderr, "Loading resources for %s\n", rs.Name) rp := path.Join(c.BaseDir, rs.Name) files, err := ioutil.ReadDir(rp) @@ -40,7 +40,6 @@ func LoadAndPrepareTemplates(c *context.Context) ([]string, error) { ) } - for _, file := range files { if !file.IsDir() && isResourceFile(file) { p := path.Join(rp, file.Name()) From ad82ff3e7560edf132d04b8a4e08447af3821efb Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 8 Feb 2017 11:58:26 +0100 Subject: [PATCH 008/166] chore: Add LICENSE Open code under MIT license. --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..b1b8e03c8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Vincent Ambo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From 7ac63613fb64c6cec3119afc51ba4bac91b81a94 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 8 Feb 2017 12:58:53 +0100 Subject: [PATCH 009/166] feat main: Add proper CLI support Adds a basic CLI structure with a single "run" command that takes a --file (-f) and --limit (-l) flag. --limit can be used to only output certain resource sets. Closes #4 --- context/context.go | 3 +- main.go | 72 +++++++++++++++++++++++++++---------- templater/templater.go | 80 +++++++++++++++++++++++++++++------------- 3 files changed, 110 insertions(+), 45 deletions(-) diff --git a/context/context.go b/context/context.go index e842feae1..612faa39a 100644 --- a/context/context.go +++ b/context/context.go @@ -2,9 +2,10 @@ package context import ( "encoding/json" - "github.com/polydawn/meep" "io/ioutil" "path" + + "github.com/polydawn/meep" ) type ResourceSet struct { diff --git a/main.go b/main.go index 158c9a566..de734e8ac 100644 --- a/main.go +++ b/main.go @@ -4,36 +4,70 @@ import ( "fmt" "os" + "github.com/polydawn/meep" "github.com/tazjin/kontemplate/context" "github.com/tazjin/kontemplate/templater" + "github.com/urfave/cli" ) func main() { - args := os.Args[1:] - if len(args) == 0 { - fmt.Fprintln(os.Stderr, "Usage: kontemplate ") - os.Exit(1) + app := cli.NewApp() + + app.Name = "kontemplate" + app.Usage = "simple Kubernetes resource templating" + app.Version = "0.0.1" + + app.Commands = []cli.Command{ + ApplyCommand(), } - c, err := context.LoadContextFromFile(os.Args[1]) + app.Run(os.Args) +} - if err != nil { - fmt.Fprintf(os.Stderr, "%v\n", err) - os.Exit(1) - } +func ApplyCommand() cli.Command { + return cli.Command{ + Name: "run", + Usage: "Interpolate and print templates", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "file, f", + Usage: "Cluster configuration file to use", + }, + cli.StringSliceFlag{ + Name: "limit, l", + Usage: "Limit templating to certain resource sets", + }, + }, + Action: func(c *cli.Context) error { + limit := c.StringSlice("limit") + f := c.String("file") - fmt.Fprintf(os.Stderr, "Applying cluster %s\n", c.Name) + if f == "" { + return meep.New( + &meep.ErrInvalidParam{ + Param: "file", + Reason: "Cluster config file must be specified", + }, + ) + } - for _, rs := range c.ResourceSets { - fmt.Fprintf(os.Stderr, "Applying resource %s with values %v\n", rs.Name, rs.Values) - resources, err := templater.LoadAndPrepareTemplates(c) + ctx, err := context.LoadContextFromFile(f) - if err != nil { - fmt.Println(err) - } + if err != nil { + return err + } - for _, r := range resources { - fmt.Print(r) - } + resources, err := templater.LoadAndPrepareTemplates(&limit, ctx) + + if err != nil { + return err + } + + for _, r := range resources { + fmt.Println(r) + } + + return nil + }, } } diff --git a/templater/templater.go b/templater/templater.go index 27beff371..a2c860c60 100644 --- a/templater/templater.go +++ b/templater/templater.go @@ -24,37 +24,67 @@ type TemplatingError struct { meep.AllTraits } -func LoadAndPrepareTemplates(c *context.Context) ([]string, error) { - output := make([]string, 0) - +func LoadAndPrepareTemplates(limit *[]string, c *context.Context) (output []string, err error) { for _, rs := range c.ResourceSets { - fmt.Fprintf(os.Stderr, "Loading resources for %s\n", rs.Name) + if resourceSetIncluded(limit, &rs.Name) { + err = processResourceSet(c, &rs, &output) - rp := path.Join(c.BaseDir, rs.Name) - files, err := ioutil.ReadDir(rp) - - if err != nil { - return nil, meep.New( - &TemplateNotFoundError{Name: rs.Name}, - meep.Cause(err), - ) - } - - for _, file := range files { - if !file.IsDir() && isResourceFile(file) { - p := path.Join(rp, file.Name()) - o, err := templateFile(c, &rs, p) - - if err != nil { - return nil, err - } - - output = append(output, o) + if err != nil { + return } } } - return output, nil + return +} + +func resourceSetIncluded(limit *[]string, resourceSetName *string) bool { + if len(*limit) == 0 { + return true + } + + for _, name := range *limit { + if name == *resourceSetName { + return true + } + } + + return false +} + +func processResourceSet(c *context.Context, rs *context.ResourceSet, output *[]string) error { + fmt.Fprintf(os.Stderr, "Loading resources for %s\n", rs.Name) + + rp := path.Join(c.BaseDir, rs.Name) + files, err := ioutil.ReadDir(rp) + + err = processFiles(c, rs, rp, files, output) + + if err != nil { + return meep.New( + &TemplateNotFoundError{Name: rs.Name}, + meep.Cause(err), + ) + } + + return nil +} + +func processFiles(c *context.Context, rs *context.ResourceSet, rp string, files []os.FileInfo, output *[]string) error { + for _, file := range files { + if !file.IsDir() && isResourceFile(file) { + p := path.Join(rp, file.Name()) + o, err := templateFile(c, rs, p) + + if err != nil { + return err + } + + *output = append(*output, o) + } + } + + return nil } func templateFile(c *context.Context, rs *context.ResourceSet, filename string) (string, error) { From 13bf8a8ba39ed6e42bf6965a60c7e44991a364c7 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 8 Feb 2017 13:00:34 +0100 Subject: [PATCH 010/166] feat build: Add Travis.CI support --- .travis.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..4f2ee4d97 --- /dev/null +++ b/.travis.yml @@ -0,0 +1 @@ +language: go From 1e80f19f7b3e5246b42e74862b32885fb621b03c Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 8 Feb 2017 13:03:06 +0100 Subject: [PATCH 011/166] docs: Add build status --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index f91722832..df74f2c33 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ KonTemplate - A simple Kubernetes templater =========================================== +[![Build Status](https://travis-ci.org/tazjin/kontemplate.svg?branch=master)](https://travis-ci.org/tazjin/kontemplate) + I made this tool out of frustration with the available ways to template Kubernetes resource files. All I want out of such a tool is a way to specify lots of resources with placeholders that get filled in with specific values, based on which context (i.e. k8s cluster) is specified. From d94a0ffc25f5e6205dd2fa770819c2095160800a Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 8 Feb 2017 13:11:10 +0100 Subject: [PATCH 012/166] feat context: Add YAML loading support Closes #5 --- context/context.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/context/context.go b/context/context.go index 612faa39a..140f11fce 100644 --- a/context/context.go +++ b/context/context.go @@ -2,9 +2,12 @@ package context import ( "encoding/json" + "fmt" "io/ioutil" "path" + "strings" + "github.com/ghodss/yaml" "github.com/polydawn/meep" ) @@ -38,7 +41,17 @@ func LoadContextFromFile(filename string) (*Context, error) { var c Context - err = json.Unmarshal(file, &c) + if strings.HasSuffix(filename, "json") { + err = json.Unmarshal(file, &c) + } else if strings.HasSuffix(filename, "yaml") || strings.HasSuffix(filename, "yml") { + err = yaml.Unmarshal(file, &c) + } else { + return nil, meep.New( + &ContextLoadingError{Filename: filename}, + meep.Cause(fmt.Errorf("File format not supported. Must be JSON or YAML.")), + ) + } + if err != nil { return nil, meep.New( &ContextLoadingError{Filename: filename}, From a1c23d701826aa4ab963416946ff7054aabc1a7a Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 8 Feb 2017 13:15:36 +0100 Subject: [PATCH 013/166] docs: Add YAML to docs --- README.md | 29 +++++++++++++---------------- example/prod-cluster.yaml | 10 ++++++++++ 2 files changed, 23 insertions(+), 16 deletions(-) create mode 100644 example/prod-cluster.yaml diff --git a/README.md b/README.md index df74f2c33..24bc9a37a 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ KonTemplate lets you describe resources as you normally would in a simple folder ``` . -├── prod-cluster.json +├── prod-cluster.yaml └── some-api ├── deployment.yaml └── service.yaml @@ -22,24 +22,21 @@ KonTemplate lets you describe resources as you normally would in a simple folder This example has all resources belonging to `some-api` (no file naming conventions enforced at all!) in the `some-api` folder and the configuration for the cluster `prod-cluster` in the corresponding file. -Lets take a short look at `prod-cluster.json`: +Lets take a short look at `prod-cluster.yaml`: -```json -{ - "context": "k8s.prod.mydomain.com", - "include": [ - { - "name": "some-api", - "values": { - "importantFeature": true, - "apiPort": 4567 - } - } - ] -} +```yaml +--- +context: k8s.prod.mydomain.com +global: + globalVar: lizards +include: + - name: some-api + values: + version: 1.0-0e6884d + importantFeature: true + apiPort: 4567 ``` - Those values are then templated into the resource files of `some-api`. ## Usage diff --git a/example/prod-cluster.yaml b/example/prod-cluster.yaml new file mode 100644 index 000000000..dd7804f71 --- /dev/null +++ b/example/prod-cluster.yaml @@ -0,0 +1,10 @@ +--- +context: k8s.prod.mydomain.com +global: + globalVar: lizards +include: + - name: some-api + values: + version: 1.0-0e6884d + importantFeature: true + apiPort: 4567 From 3ef0f35bfeb73adbe6b205b7bdd54ea6a49f1c97 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 8 Feb 2017 13:47:56 +0100 Subject: [PATCH 014/166] fix templater: Guard against empty values map --- templater/templater.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/templater/templater.go b/templater/templater.go index a2c860c60..5c1db77aa 100644 --- a/templater/templater.go +++ b/templater/templater.go @@ -99,6 +99,11 @@ func templateFile(c *context.Context, rs *context.ResourceSet, filename string) var b bytes.Buffer + // Guard against empty map before merging keys + if rs.Values == nil { + rs.Values = make(map[string]interface{}, 0) + } + // Merge global and resourceset-specific values (don't override from global) for k, v := range c.Global { if _, ok := rs.Values[k]; !ok { From 6147ea7b9b4c9071ef7abe777e56f77e8497b440 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 8 Feb 2017 13:53:46 +0100 Subject: [PATCH 015/166] docs: Add usage & installation sections --- README.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 24bc9a37a..86a0616dc 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,23 @@ include: Those values are then templated into the resource files of `some-api`. +## Installation + +Assuming you have Go configured correctly, you can simply `go get github.com/tazjin/kontemplate/...`. + ## Usage -You must have `kubectl` installed to use KonTemplate. +You must have `kubectl` installed to use KonTemplate effectively. + +At the moment KonTemplate will simply output the templated Kubernetes resource files, which can +then be piped into `kubectl`: + +``` +# Look at output and check to see if it's correct ... +kontemplate run -f example/prod-cluster.yaml -l some-api + +# ... if it is, go ahead and apply it +kontemplate run -f example/prod-cluster.yaml -l some-api | kubectl apply -f - + +# That's it! +``` \ No newline at end of file From efe49de57f6757c7ae6752927029932c556e881c Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 8 Feb 2017 14:07:56 +0100 Subject: [PATCH 016/166] docs: Add generated 'kontemplate run' help --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index 86a0616dc..c89dbeb1f 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,18 @@ Assuming you have Go configured correctly, you can simply `go get github.com/taz You must have `kubectl` installed to use KonTemplate effectively. +``` +NAME: + kontemplate run - Interpolate and print templates + +USAGE: + kontemplate run [command options] [arguments...] + +OPTIONS: + --file value, -f value Cluster configuration file to use + --limit value, -l value Limit templating to certain resource sets +``` + At the moment KonTemplate will simply output the templated Kubernetes resource files, which can then be piped into `kubectl`: From 25f2a1616caf2a05199370998223f4a64bb55f81 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 8 Feb 2017 14:37:36 +0100 Subject: [PATCH 017/166] feat template: Add additional template functions This adds the Go template functions from [sprig][] as well as a custom `json` function that can interpolate any data as a JSON object - very useful for adding arrays of data in JSON format into a variable: ``` certificateDomains: - oslo.pub - tazj.in annotations: acme/certificate: {{ .certificateDomains | json }} annotations: acme/certificate: ["oslo.pub", "tazj.in"] ``` [sprig]: https://godoc.org/github.com/Masterminds/sprig --- templater/templater.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/templater/templater.go b/templater/templater.go index 5c1db77aa..6e443c14b 100644 --- a/templater/templater.go +++ b/templater/templater.go @@ -2,6 +2,7 @@ package templater import ( "bytes" + "encoding/json" "fmt" "io/ioutil" "os" @@ -9,6 +10,7 @@ import ( "strings" "text/template" + "github.com/Masterminds/sprig" "github.com/polydawn/meep" "github.com/tazjin/kontemplate/context" ) @@ -88,7 +90,7 @@ func processFiles(c *context.Context, rs *context.ResourceSet, rp string, files } func templateFile(c *context.Context, rs *context.ResourceSet, filename string) (string, error) { - tpl, err := template.ParseFiles(filename) + tpl, err := template.New(path.Base(filename)).Funcs(templateFuncs()).ParseFiles(filename) if err != nil { return "", meep.New( @@ -123,6 +125,16 @@ func templateFile(c *context.Context, rs *context.ResourceSet, filename string) return b.String(), nil } +func templateFuncs() template.FuncMap { + m := sprig.TxtFuncMap() + m["json"] = func(data interface{}) string { + b, _ := json.Marshal(data) + return string(b) + } + + return m +} + // Checks whether a file is a resource file (i.e. is YAML or JSON) func isResourceFile(f os.FileInfo) bool { return strings.HasSuffix(f.Name(), "yaml") || From bc9fc9730dd30e2d302ce5f88cd93da9604eb6f0 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 8 Feb 2017 15:32:44 +0100 Subject: [PATCH 018/166] feat main: Add apply command This integrates support for actually calling out to `kubectl apply`. A dry-run flag is implemented, too. The `run` command has been renamed to `template`. --- main.go | 128 ++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 96 insertions(+), 32 deletions(-) diff --git a/main.go b/main.go index de734e8ac..c39972ff6 100644 --- a/main.go +++ b/main.go @@ -3,6 +3,7 @@ package main import ( "fmt" "os" + "os/exec" "github.com/polydawn/meep" "github.com/tazjin/kontemplate/context" @@ -10,6 +11,10 @@ import ( "github.com/urfave/cli" ) +type KubeCtlError struct { + meep.AllTraits +} + func main() { app := cli.NewApp() @@ -18,46 +23,20 @@ func main() { app.Version = "0.0.1" app.Commands = []cli.Command{ - ApplyCommand(), + templateCommand(), + applyCommand(), } app.Run(os.Args) } -func ApplyCommand() cli.Command { +func templateCommand() cli.Command { return cli.Command{ - Name: "run", + Name: "template", Usage: "Interpolate and print templates", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "file, f", - Usage: "Cluster configuration file to use", - }, - cli.StringSliceFlag{ - Name: "limit, l", - Usage: "Limit templating to certain resource sets", - }, - }, + Flags: commonFlags(), Action: func(c *cli.Context) error { - limit := c.StringSlice("limit") - f := c.String("file") - - if f == "" { - return meep.New( - &meep.ErrInvalidParam{ - Param: "file", - Reason: "Cluster config file must be specified", - }, - ) - } - - ctx, err := context.LoadContextFromFile(f) - - if err != nil { - return err - } - - resources, err := templater.LoadAndPrepareTemplates(&limit, ctx) + resources, err := templateResources(c) if err != nil { return err @@ -71,3 +50,88 @@ func ApplyCommand() cli.Command { }, } } + +func applyCommand() cli.Command { + dryRun := false + + return cli.Command{ + Name: "apply", + Usage: "Interpolate templates and run 'kubectl apply'", + Flags: append(commonFlags(), cli.BoolFlag{ + Name: "dry-run", + Usage: "Only print objects that would be sent, without sending them", + Destination: &dryRun, + }), + Action: func(c *cli.Context) error { + resources, err := templateResources(c) + + if err != nil { + return err + } + + var kubectl *exec.Cmd + if dryRun { + kubectl = exec.Command("kubectl", "apply", "-f", "-", "--dry-run") + } else { + kubectl = exec.Command("kubectl", "apply", "-f", "-") + } + + stdin, err := kubectl.StdinPipe() + if err != nil { + return meep.New(&KubeCtlError{}, meep.Cause(err)) + } + + kubectl.Stdout = os.Stdout + kubectl.Stderr = os.Stderr + + if err = kubectl.Start(); err != nil { + return meep.New(&KubeCtlError{}, meep.Cause(err)) + } + + for _, r := range resources { + fmt.Fprintln(stdin, r) + } + + stdin.Close() + + kubectl.Wait() + + return nil + }, + } +} + +func commonFlags() []cli.Flag { + return []cli.Flag{ + cli.StringFlag{ + Name: "file, f", + Usage: "Cluster configuration file to use", + }, + cli.StringSliceFlag{ + Name: "limit, l", + Usage: "Limit templating to certain resource sets", + }, + } +} + +func templateResources(c *cli.Context) ([]string, error) { + limit := c.StringSlice("limit") + f := c.String("file") + + if f == "" { + return nil, meep.New( + &meep.ErrInvalidParam{ + Param: "file", + Reason: "Cluster config file must be specified", + }, + ) + } + + ctx, err := context.LoadContextFromFile(f) + + if err != nil { + return nil, err + } + + return templater.LoadAndPrepareTemplates(&limit, ctx) +} From 250d01c0446a7bc69b466c576731177cf97331d9 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 8 Feb 2017 15:35:58 +0100 Subject: [PATCH 019/166] docs: Update README for 'apply' command --- README.md | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index c89dbeb1f..93761a855 100644 --- a/README.md +++ b/README.md @@ -49,25 +49,33 @@ You must have `kubectl` installed to use KonTemplate effectively. ``` NAME: - kontemplate run - Interpolate and print templates + kontemplate - simple Kubernetes resource templating USAGE: - kontemplate run [command options] [arguments...] + kontemplate [global options] command [command options] [arguments...] -OPTIONS: - --file value, -f value Cluster configuration file to use - --limit value, -l value Limit templating to certain resource sets +VERSION: + 0.0.1 + +COMMANDS: + template Interpolate and print templates + apply Interpolate templates and run 'kubectl apply' + help, h Shows a list of commands or help for one command + +GLOBAL OPTIONS: + --help, -h show help + --version, -v print the version ``` -At the moment KonTemplate will simply output the templated Kubernetes resource files, which can -then be piped into `kubectl`: +Examples: ``` -# Look at output and check to see if it's correct ... -kontemplate run -f example/prod-cluster.yaml -l some-api +# Look at output for a specific resource set and check to see if it's correct ... +kontemplate template -f example/prod-cluster.yaml -l some-api -# ... if it is, go ahead and apply it -kontemplate run -f example/prod-cluster.yaml -l some-api | kubectl apply -f - +# ... maybe do a dry-run to see what kubectl would do: +kontemplate apply -f example/prod-cluster.yaml --dry-run -# That's it! +# And actually apply it if you like what you see: +kontemplate apply -f example/prod-cluster.yaml ``` \ No newline at end of file From 11a5cf9e192b57eb594ce276ef7723693ff28014 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 8 Feb 2017 16:19:10 +0100 Subject: [PATCH 020/166] feat main: Add replace support & respect context setting * Adds support for calling `kubectl replace` (necessary for resource types that do not support `apply`). * Sets `kubectl` context to whatever is defined in the cluster configuration file --- main.go | 89 ++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 60 insertions(+), 29 deletions(-) diff --git a/main.go b/main.go index c39972ff6..1a775bf7c 100644 --- a/main.go +++ b/main.go @@ -25,6 +25,7 @@ func main() { app.Commands = []cli.Command{ templateCommand(), applyCommand(), + replaceCommand(), } app.Run(os.Args) @@ -36,7 +37,9 @@ func templateCommand() cli.Command { Usage: "Interpolate and print templates", Flags: commonFlags(), Action: func(c *cli.Context) error { - resources, err := templateResources(c) + limit := c.StringSlice("limit") + ctx, err := loadContext(c) + resources, err := templater.LoadAndPrepareTemplates(&limit, ctx) if err != nil { return err @@ -63,44 +66,73 @@ func applyCommand() cli.Command { Destination: &dryRun, }), Action: func(c *cli.Context) error { - resources, err := templateResources(c) + limit := c.StringSlice("limit") + ctx, err := loadContext(c) + resources, err := templater.LoadAndPrepareTemplates(&limit, ctx) if err != nil { return err } - var kubectl *exec.Cmd + var args []string if dryRun { - kubectl = exec.Command("kubectl", "apply", "-f", "-", "--dry-run") + args = []string{"apply", "-f", "-", "--dry-run"} } else { - kubectl = exec.Command("kubectl", "apply", "-f", "-") + args = []string{"apply", "-f", "-"} } - stdin, err := kubectl.StdinPipe() - if err != nil { - return meep.New(&KubeCtlError{}, meep.Cause(err)) - } - - kubectl.Stdout = os.Stdout - kubectl.Stderr = os.Stderr - - if err = kubectl.Start(); err != nil { - return meep.New(&KubeCtlError{}, meep.Cause(err)) - } - - for _, r := range resources { - fmt.Fprintln(stdin, r) - } - - stdin.Close() - - kubectl.Wait() - - return nil + return runKubectlWithResources(ctx, &args, &resources) }, } } +func replaceCommand() cli.Command { + return cli.Command{ + Name: "replace", + Usage: "Interpolate templates and run 'kubectl replace'", + Flags: commonFlags(), + Action: func(c *cli.Context) error { + limit := c.StringSlice("limit") + ctx, err := loadContext(c) + resources, err := templater.LoadAndPrepareTemplates(&limit, ctx) + + if err != nil { + return err + } + + args := []string{"replace", "--save-config=true", "-f", "-"} + return runKubectlWithResources(ctx, &args, &resources) + }, + } +} + +func runKubectlWithResources(c *context.Context, kubectlArgs *[]string, resources *[]string) error { + args := append(*kubectlArgs, fmt.Sprintf("--context=%s", c.Name)) + + kubectl := exec.Command("kubectl", args...) + + stdin, err := kubectl.StdinPipe() + if err != nil { + return meep.New(&KubeCtlError{}, meep.Cause(err)) + } + + kubectl.Stdout = os.Stdout + kubectl.Stderr = os.Stderr + + if err = kubectl.Start(); err != nil { + return meep.New(&KubeCtlError{}, meep.Cause(err)) + } + + for _, r := range *resources { + fmt.Fprintln(stdin, r) + } + stdin.Close() + + kubectl.Wait() + + return nil +} + func commonFlags() []cli.Flag { return []cli.Flag{ cli.StringFlag{ @@ -114,8 +146,7 @@ func commonFlags() []cli.Flag { } } -func templateResources(c *cli.Context) ([]string, error) { - limit := c.StringSlice("limit") +func loadContext(c *cli.Context) (*context.Context, error) { f := c.String("file") if f == "" { @@ -133,5 +164,5 @@ func templateResources(c *cli.Context) ([]string, error) { return nil, err } - return templater.LoadAndPrepareTemplates(&limit, ctx) + return ctx, nil } From 8e08a282eb4f29618c8de8090927b521ea0c9c2b Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 8 Feb 2017 16:44:55 +0100 Subject: [PATCH 021/166] feat templater: Add ability to exclude resource sets * renamed --limit to --include (-i) * added --exclude (-e) Kontemplate users can now explicitly include and exclude certain resource sets. Excludes always override includes. Closes #11 --- main.go | 23 +++++++++----- templater/templater.go | 68 +++++++++++++++++++++++++++++------------- 2 files changed, 62 insertions(+), 29 deletions(-) diff --git a/main.go b/main.go index 1a775bf7c..f6e492143 100644 --- a/main.go +++ b/main.go @@ -37,9 +37,10 @@ func templateCommand() cli.Command { Usage: "Interpolate and print templates", Flags: commonFlags(), Action: func(c *cli.Context) error { - limit := c.StringSlice("limit") + include := c.StringSlice("include") + exclude := c.StringSlice("exclude") ctx, err := loadContext(c) - resources, err := templater.LoadAndPrepareTemplates(&limit, ctx) + resources, err := templater.LoadAndPrepareTemplates(&include, &exclude, ctx) if err != nil { return err @@ -66,9 +67,10 @@ func applyCommand() cli.Command { Destination: &dryRun, }), Action: func(c *cli.Context) error { - limit := c.StringSlice("limit") + include := c.StringSlice("include") + exclude := c.StringSlice("exclude") ctx, err := loadContext(c) - resources, err := templater.LoadAndPrepareTemplates(&limit, ctx) + resources, err := templater.LoadAndPrepareTemplates(&include, &exclude, ctx) if err != nil { return err @@ -92,9 +94,10 @@ func replaceCommand() cli.Command { Usage: "Interpolate templates and run 'kubectl replace'", Flags: commonFlags(), Action: func(c *cli.Context) error { - limit := c.StringSlice("limit") + include := c.StringSlice("include") + exclude := c.StringSlice("exclude") ctx, err := loadContext(c) - resources, err := templater.LoadAndPrepareTemplates(&limit, ctx) + resources, err := templater.LoadAndPrepareTemplates(&include, &exclude, ctx) if err != nil { return err @@ -140,8 +143,12 @@ func commonFlags() []cli.Flag { Usage: "Cluster configuration file to use", }, cli.StringSliceFlag{ - Name: "limit, l", - Usage: "Limit templating to certain resource sets", + Name: "include, i", + Usage: "Limit templating to explicitly included resource sets", + }, + cli.StringSliceFlag{ + Name: "exclude, e", + Usage: "Exclude certain resource sets from templating", }, } } diff --git a/templater/templater.go b/templater/templater.go index 6e443c14b..bb65cd1df 100644 --- a/templater/templater.go +++ b/templater/templater.go @@ -26,34 +26,20 @@ type TemplatingError struct { meep.AllTraits } -func LoadAndPrepareTemplates(limit *[]string, c *context.Context) (output []string, err error) { - for _, rs := range c.ResourceSets { - if resourceSetIncluded(limit, &rs.Name) { - err = processResourceSet(c, &rs, &output) +func LoadAndPrepareTemplates(include *[]string, exclude *[]string, c *context.Context) (output []string, err error) { + limitedResourceSets := applyLimits(&c.ResourceSets, include, exclude) - if err != nil { - return - } + for _, rs := range *limitedResourceSets { + err = processResourceSet(c, &rs, &output) + + if err != nil { + return } } return } -func resourceSetIncluded(limit *[]string, resourceSetName *string) bool { - if len(*limit) == 0 { - return true - } - - for _, name := range *limit { - if name == *resourceSetName { - return true - } - } - - return false -} - func processResourceSet(c *context.Context, rs *context.ResourceSet, output *[]string) error { fmt.Fprintf(os.Stderr, "Loading resources for %s\n", rs.Name) @@ -125,6 +111,46 @@ func templateFile(c *context.Context, rs *context.ResourceSet, filename string) return b.String(), nil } +// Applies the limits of explicitly included or excluded resources and returns the updated resource set. +// Exclude takes priority over include +func applyLimits(rs *[]context.ResourceSet, include *[]string, exclude *[]string) *[]context.ResourceSet { + if len(*include) == 0 && len(*exclude) == 0 { + return rs + } + + // Exclude excluded resource sets + excluded := make([]context.ResourceSet, 0) + for _, r := range *rs { + if !contains(exclude, &r.Name) { + excluded = append(excluded, r) + } + } + + // Include included resource sets + if len(*include) == 0 { + return &excluded + } + included := make([]context.ResourceSet, 0) + for _, r := range excluded { + if contains(include, &r.Name) { + included = append(included, r) + } + } + + return &included +} + +// Check whether a certain string is contained in a string slice +func contains(s *[]string, v *string) bool { + for _, r := range *s { + if r == *v { + return true + } + } + + return false +} + func templateFuncs() template.FuncMap { m := sprig.TxtFuncMap() m["json"] = func(data interface{}) string { From d6b16793c150202f78e5b862b372481f08a00a6f Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 8 Feb 2017 16:47:33 +0100 Subject: [PATCH 022/166] docs: Update README with new options --- README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 93761a855..817f642d6 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,7 @@ VERSION: COMMANDS: template Interpolate and print templates apply Interpolate templates and run 'kubectl apply' + replace Interpolate templates and run 'kubectl replace' help, h Shows a list of commands or help for one command GLOBAL OPTIONS: @@ -67,11 +68,20 @@ GLOBAL OPTIONS: --version, -v print the version ``` +All options support the same set of extra flags: + +``` +OPTIONS: + --file value, -f value Cluster configuration file to use + --include value, -i value Limit templating to explicitly included resource sets + --exclude value, -e value Exclude certain resource sets from templating +``` + Examples: ``` # Look at output for a specific resource set and check to see if it's correct ... -kontemplate template -f example/prod-cluster.yaml -l some-api +kontemplate template -f example/prod-cluster.yaml -i some-api # ... maybe do a dry-run to see what kubectl would do: kontemplate apply -f example/prod-cluster.yaml --dry-run From 4e8223ef3496f390dd84a2a47fc8846afd6f7c76 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 8 Feb 2017 17:14:40 +0100 Subject: [PATCH 023/166] feat context: Add support for resource set collections A resource set collection is a resource set with an addition 'include' array configured. It is a short-hand for importing multiple resource sets from the same folder and for excluding/including them as a group. See https://github.com/tazjin/kontemplate/issues/9 for more information. Closes #9 --- context/context.go | 24 ++++++++++++++++++++++++ templater/templater.go | 10 +++++----- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/context/context.go b/context/context.go index 140f11fce..94838f668 100644 --- a/context/context.go +++ b/context/context.go @@ -14,6 +14,10 @@ import ( type ResourceSet struct { Name string `json:"name"` Values map[string]interface{} `json:"values"` + + // Fields for resource set collections + Include []ResourceSet `json:"include"` + Parent *string } type Context struct { @@ -63,3 +67,23 @@ func LoadContextFromFile(filename string) (*Context, error) { return &c, nil } + +// Flattens resource set collections, i.e. resource sets that themselves have an additional 'include' field set. +// Those will be regarded as a short-hand for including multiple resource sets from a subfolder. +// See https://github.com/tazjin/kontemplate/issues/9 for more information. +func flattenResourceSetCollections(rs *[]ResourceSet) *[]ResourceSet { + flattened := make([]ResourceSet, 0) + + for _, r := range *rs { + if len(r.Include) == 0 { + flattened = append(flattened, r) + } else { + for _, subResourceSet := range r.Include { + subResourceSet.Parent = &r.Name + flattened = append(flattened, subResourceSet) + } + } + } + + return &flattened +} diff --git a/templater/templater.go b/templater/templater.go index bb65cd1df..29079eb94 100644 --- a/templater/templater.go +++ b/templater/templater.go @@ -121,7 +121,7 @@ func applyLimits(rs *[]context.ResourceSet, include *[]string, exclude *[]string // Exclude excluded resource sets excluded := make([]context.ResourceSet, 0) for _, r := range *rs { - if !contains(exclude, &r.Name) { + if !matchesResourceSet(exclude, &r) { excluded = append(excluded, r) } } @@ -132,7 +132,7 @@ func applyLimits(rs *[]context.ResourceSet, include *[]string, exclude *[]string } included := make([]context.ResourceSet, 0) for _, r := range excluded { - if contains(include, &r.Name) { + if matchesResourceSet(include, &r) { included = append(included, r) } } @@ -140,10 +140,10 @@ func applyLimits(rs *[]context.ResourceSet, include *[]string, exclude *[]string return &included } -// Check whether a certain string is contained in a string slice -func contains(s *[]string, v *string) bool { +// Check whether an include/exclude string slice matches a resource set +func matchesResourceSet(s *[]string, rs *context.ResourceSet) bool { for _, r := range *s { - if r == *v { + if r == rs.Name || r == *rs.Parent { return true } } From bace4dd895ac0bcb29388c64f3711de114e36765 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 8 Feb 2017 17:21:55 +0100 Subject: [PATCH 024/166] fix context: Set sub resource names correctly --- context/context.go | 1 + 1 file changed, 1 insertion(+) diff --git a/context/context.go b/context/context.go index 94838f668..2de78541e 100644 --- a/context/context.go +++ b/context/context.go @@ -80,6 +80,7 @@ func flattenResourceSetCollections(rs *[]ResourceSet) *[]ResourceSet { } else { for _, subResourceSet := range r.Include { subResourceSet.Parent = &r.Name + subResourceSet.Name = path.Join(r.Name, subResourceSet.Name) flattened = append(flattened, subResourceSet) } } From 756a4c745d111e74c2c673009e14b14b1cd8141f Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 8 Feb 2017 17:23:34 +0100 Subject: [PATCH 025/166] fix templater: Guard against empty parent reference --- templater/templater.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templater/templater.go b/templater/templater.go index 29079eb94..89d66ff38 100644 --- a/templater/templater.go +++ b/templater/templater.go @@ -143,7 +143,7 @@ func applyLimits(rs *[]context.ResourceSet, include *[]string, exclude *[]string // Check whether an include/exclude string slice matches a resource set func matchesResourceSet(s *[]string, rs *context.ResourceSet) bool { for _, r := range *s { - if r == rs.Name || r == *rs.Parent { + if r == rs.Name || (rs.Parent != nil && r == *rs.Parent) { return true } } From dd2fdd63e5c1fdaed0eb5116e32724068495fae7 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 8 Feb 2017 17:32:19 +0100 Subject: [PATCH 026/166] fix templater & ctx: Correctly check resource set parent --- context/context.go | 5 +++-- templater/templater.go | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/context/context.go b/context/context.go index 2de78541e..108bda28a 100644 --- a/context/context.go +++ b/context/context.go @@ -17,7 +17,7 @@ type ResourceSet struct { // Fields for resource set collections Include []ResourceSet `json:"include"` - Parent *string + Parent string } type Context struct { @@ -63,6 +63,7 @@ func LoadContextFromFile(filename string) (*Context, error) { ) } + c.ResourceSets = *flattenResourceSetCollections(&c.ResourceSets) c.BaseDir = path.Dir(filename) return &c, nil @@ -79,7 +80,7 @@ func flattenResourceSetCollections(rs *[]ResourceSet) *[]ResourceSet { flattened = append(flattened, r) } else { for _, subResourceSet := range r.Include { - subResourceSet.Parent = &r.Name + subResourceSet.Parent = r.Name subResourceSet.Name = path.Join(r.Name, subResourceSet.Name) flattened = append(flattened, subResourceSet) } diff --git a/templater/templater.go b/templater/templater.go index 89d66ff38..6ca7e6770 100644 --- a/templater/templater.go +++ b/templater/templater.go @@ -143,7 +143,7 @@ func applyLimits(rs *[]context.ResourceSet, include *[]string, exclude *[]string // Check whether an include/exclude string slice matches a resource set func matchesResourceSet(s *[]string, rs *context.ResourceSet) bool { for _, r := range *s { - if r == rs.Name || (rs.Parent != nil && r == *rs.Parent) { + if r == rs.Name || r == rs.Parent { return true } } From c046e5acff451eb7beafe64fedf83fad51bb4aca Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 8 Feb 2017 18:06:47 +0100 Subject: [PATCH 027/166] feat main: Add 'delete' command --- main.go | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/main.go b/main.go index f6e492143..dc320686f 100644 --- a/main.go +++ b/main.go @@ -26,6 +26,7 @@ func main() { templateCommand(), applyCommand(), replaceCommand(), + deleteCommand(), } app.Run(os.Args) @@ -109,6 +110,27 @@ func replaceCommand() cli.Command { } } +func deleteCommand() cli.Command { + return cli.Command{ + Name: "delete", + Usage: "Interpolate templates and run 'kubectl delete'", + Flags: commonFlags(), + Action: func(c *cli.Context) error { + include := c.StringSlice("include") + exclude := c.StringSlice("exclude") + ctx, err := loadContext(c) + resources, err := templater.LoadAndPrepareTemplates(&include, &exclude, ctx) + + if err != nil { + return err + } + + args := []string{"delete", "-f", "-"} + return runKubectlWithResources(ctx, &args, &resources) + }, + } +} + func runKubectlWithResources(c *context.Context, kubectlArgs *[]string, resources *[]string) error { args := append(*kubectlArgs, fmt.Sprintf("--context=%s", c.Name)) From a6eb4210575c3d81ebd449b0b89092e33abf3394 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 8 Feb 2017 18:08:54 +0100 Subject: [PATCH 028/166] docs: Update README with 'delete' command --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 817f642d6..b9390a855 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ COMMANDS: template Interpolate and print templates apply Interpolate templates and run 'kubectl apply' replace Interpolate templates and run 'kubectl replace' + delete Interpolate templates and run 'kubectl delete' help, h Shows a list of commands or help for one command GLOBAL OPTIONS: From 75b6199c1b0b04bac32abc6f18a1dc2e68da0242 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 8 Feb 2017 21:44:51 +0100 Subject: [PATCH 029/166] feat context: Add deserialisation tests --- context/context_test.go | 83 ++++++++++++++++++++++++++ context/testdata/collections-test.yaml | 15 +++++ context/testdata/flat-test.yaml | 10 ++++ 3 files changed, 108 insertions(+) create mode 100644 context/context_test.go create mode 100644 context/testdata/collections-test.yaml create mode 100644 context/testdata/flat-test.yaml diff --git a/context/context_test.go b/context/context_test.go new file mode 100644 index 000000000..b34222ed4 --- /dev/null +++ b/context/context_test.go @@ -0,0 +1,83 @@ +package context + +import ( + "reflect" + "testing" +) + +func TestLoadFlatContextFromFile(t *testing.T) { + ctx, err := LoadContextFromFile("testdata/flat-test.yaml") + + if err != nil { + t.Error(err) + t.Fail() + } + + expected := Context{ + Name: "k8s.prod.mydomain.com", + Global: map[string]interface{}{ + "globalVar": "lizards", + }, + ResourceSets: []ResourceSet{ + { + Name: "some-api", + Values: map[string]interface{}{ + "apiPort": float64(4567), // yep! + "importantFeature": true, + "version": "1.0-0e6884d", + }, + Include: nil, + Parent: "", + }, + }, + BaseDir: "testdata", + } + + if !reflect.DeepEqual(*ctx, expected) { + t.Error("Loaded context and expected context did not match") + t.Fail() + } +} + +func TestLoadContextWithResourceSetCollections(t *testing.T) { + ctx, err := LoadContextFromFile("testdata/collections-test.yaml") + + if err != nil { + t.Error(err) + t.Fail() + } + + expected := Context{ + Name: "k8s.prod.mydomain.com", + Global: map[string]interface{}{ + "globalVar": "lizards", + }, + ResourceSets: []ResourceSet{ + { + Name: "some-api", + Values: map[string]interface{}{ + "apiPort": float64(4567), // yep! + "importantFeature": true, + "version": "1.0-0e6884d", + }, + Include: nil, + Parent: "", + }, + { + Name: "collection/nested", + Values: map[string]interface{}{ + "lizards": "good", + }, + Include: nil, + Parent: "collection", + }, + }, + BaseDir: "testdata", + } + + if !reflect.DeepEqual(*ctx, expected) { + t.Error("Loaded context and expected context did not match") + t.Fail() + } + +} diff --git a/context/testdata/collections-test.yaml b/context/testdata/collections-test.yaml new file mode 100644 index 000000000..a619c8cfd --- /dev/null +++ b/context/testdata/collections-test.yaml @@ -0,0 +1,15 @@ +--- +context: k8s.prod.mydomain.com +global: + globalVar: lizards +include: + - name: some-api + values: + version: 1.0-0e6884d + importantFeature: true + apiPort: 4567 + - name: collection + include: + - name: nested + values: + lizards: good diff --git a/context/testdata/flat-test.yaml b/context/testdata/flat-test.yaml new file mode 100644 index 000000000..dd7804f71 --- /dev/null +++ b/context/testdata/flat-test.yaml @@ -0,0 +1,10 @@ +--- +context: k8s.prod.mydomain.com +global: + globalVar: lizards +include: + - name: some-api + values: + version: 1.0-0e6884d + importantFeature: true + apiPort: 4567 From b58b1e33858e7694e8ff7e7d2c564133257abd21 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 8 Feb 2017 22:11:24 +0100 Subject: [PATCH 030/166] feat templater: Add applyLimits tests --- templater/templater_test.go | 138 ++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 templater/templater_test.go diff --git a/templater/templater_test.go b/templater/templater_test.go new file mode 100644 index 000000000..b262787ae --- /dev/null +++ b/templater/templater_test.go @@ -0,0 +1,138 @@ +package templater + +import ( + "github.com/tazjin/kontemplate/context" + "reflect" + "testing" +) + +func TestApplyNoLimits(t *testing.T) { + resources := []context.ResourceSet{ + { + Name: "testResourceSet1", + }, + { + Name: "testResourceSet2", + }, + } + + result := applyLimits(&resources, &[]string{}, &[]string{}) + + if !reflect.DeepEqual(resources, *result) { + t.Error("Resource set slice changed, but shouldn't have.") + t.Errorf("Expected: %v\nResult: %v\n", resources, *result) + t.Fail() + } +} + +func TestApplyIncludeLimits(t *testing.T) { + resources := []context.ResourceSet{ + { + Name: "testResourceSet1", + }, + { + Name: "testResourceSet2", + }, + { + Name: "testResourceSet3", + Parent: "included", + }, + } + + includes := []string{"testResourceSet1", "included"} + + result := applyLimits(&resources, &includes, &[]string{}) + + expected := []context.ResourceSet{ + { + Name: "testResourceSet1", + }, + { + Name: "testResourceSet3", + Parent: "included", + }, + } + + if !reflect.DeepEqual(expected, *result) { + t.Error("Result does not contain expected resource sets.") + t.Errorf("Expected: %v\nResult: %v\n", expected, *result) + t.Fail() + } +} + +func TestApplyExcludeLimits(t *testing.T) { + resources := []context.ResourceSet{ + { + Name: "testResourceSet1", + }, + { + Name: "testResourceSet2", + }, + { + Name: "testResourceSet3", + Parent: "included", + }, + } + + exclude := []string{"testResourceSet2"} + + result := applyLimits(&resources, &[]string{}, &exclude) + + expected := []context.ResourceSet{ + { + Name: "testResourceSet1", + }, + { + Name: "testResourceSet3", + Parent: "included", + }, + } + + if !reflect.DeepEqual(expected, *result) { + t.Error("Result does not contain expected resource sets.") + t.Errorf("Expected: %v\nResult: %v\n", expected, *result) + t.Fail() + } +} + +func TestApplyLimitsExcludeIncludePrecedence(t *testing.T) { + resources := []context.ResourceSet{ + { + Name: "collection/nested1", + Parent: "collection", + }, + { + Name: "collection/nested2", + Parent: "collection", + }, + { + Name: "collection/nested3", + Parent: "collection", + }, + { + Name: "something-else", + }, + } + + include := []string{"collection"} + exclude := []string{"collection/nested2"} + + result := applyLimits(&resources, &include, &exclude) + + expected := []context.ResourceSet{ + { + Name: "collection/nested1", + Parent: "collection", + }, + { + Name: "collection/nested3", + Parent: "collection", + }, + } + + if !reflect.DeepEqual(expected, *result) { + t.Error("Result does not contain expected resource sets.") + t.Errorf("Expected: %v\nResult: %v\n", expected, *result) + t.Fail() + } +} From 25c9ed4adc1234932566c4eb6509acb9171440e2 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 8 Feb 2017 22:15:07 +0100 Subject: [PATCH 031/166] feat main: Version bump to v1.0.0-beta1 --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index dc320686f..2f5f1b9b3 100644 --- a/main.go +++ b/main.go @@ -20,7 +20,7 @@ func main() { app.Name = "kontemplate" app.Usage = "simple Kubernetes resource templating" - app.Version = "0.0.1" + app.Version = "v1.0.0-beta1" app.Commands = []cli.Command{ templateCommand(), From 4713d565d344d123409dac389c327478b097766a Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Thu, 9 Feb 2017 15:32:14 +0100 Subject: [PATCH 032/166] fix templater: Don't fail with two identical stack traces --- templater/templater.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/templater/templater.go b/templater/templater.go index 6ca7e6770..fc7433ff1 100644 --- a/templater/templater.go +++ b/templater/templater.go @@ -23,7 +23,8 @@ type TemplateNotFoundError struct { // Error that is caused during templating, e.g. required value being absent or invalid template format type TemplatingError struct { - meep.AllTraits + meep.TraitAutodescribing + meep.TraitCausable } func LoadAndPrepareTemplates(include *[]string, exclude *[]string, c *context.Context) (output []string, err error) { From 2f6e0081214b4033132725065014c5022b997c92 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Thu, 9 Feb 2017 15:33:03 +0100 Subject: [PATCH 033/166] feat templater: Add 'pass' lookup function This introduces support for looking up secret values in the 'pass' command line tool (https://www.passwordstore.org/). Values like passwords can be interpolated from pass and even more complex structures like certificates for Kubernetes Secrets can be retrieved and base64- encoded as necessary. Fixes #2 --- example/some-api/some-api.yaml | 7 +++++++ templater/pass.go | 32 ++++++++++++++++++++++++++++++++ templater/templater.go | 1 + 3 files changed, 40 insertions(+) create mode 100644 templater/pass.go diff --git a/example/some-api/some-api.yaml b/example/some-api/some-api.yaml index 6f6a29a15..57ab7c652 100644 --- a/example/some-api/some-api.yaml +++ b/example/some-api/some-api.yaml @@ -1,4 +1,11 @@ --- +apiVersion: v1 +kind: Secret +metadata: + name: secret-certificate +data: + cert.pem: {{ passLookup "my/secret/certificate" | b64enc }} +--- apiVersion: extensions/v1beta1 kind: Deployment metadata: diff --git a/templater/pass.go b/templater/pass.go new file mode 100644 index 000000000..f1dc82986 --- /dev/null +++ b/templater/pass.go @@ -0,0 +1,32 @@ +// This file contains the implementation of a template function for retrieving variables from 'pass', the standard UNIX +// password manager. +package templater + +import ( + "fmt" + "os" + "os/exec" + + "github.com/polydawn/meep" +) + +type PassError struct { + meep.TraitAutodescribing + meep.TraitCausable + Output string +} + +func GetFromPass(key string) (string, error) { + fmt.Fprintf(os.Stderr, "Attempting to look up %s in pass\n", key) + pass := exec.Command("pass", "show", key) + + output, err := pass.CombinedOutput() + if err != nil { + return "", meep.New( + &PassError{Output: string(output)}, + meep.Cause(err), + ) + } + + return string(output), nil +} diff --git a/templater/templater.go b/templater/templater.go index fc7433ff1..5e38ddf89 100644 --- a/templater/templater.go +++ b/templater/templater.go @@ -158,6 +158,7 @@ func templateFuncs() template.FuncMap { b, _ := json.Marshal(data) return string(b) } + m["passLookup"] = GetFromPass return m } From c181decd9d6584ecd7b5d5596f25f7739442a328 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Thu, 9 Feb 2017 15:44:07 +0100 Subject: [PATCH 034/166] fix main: Add a forgotten error check --- main.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/main.go b/main.go index 2f5f1b9b3..00d06e7dc 100644 --- a/main.go +++ b/main.go @@ -40,9 +40,13 @@ func templateCommand() cli.Command { Action: func(c *cli.Context) error { include := c.StringSlice("include") exclude := c.StringSlice("exclude") - ctx, err := loadContext(c) - resources, err := templater.LoadAndPrepareTemplates(&include, &exclude, ctx) + ctx, err := loadContext(c) + if err != nil { + return err + } + + resources, err := templater.LoadAndPrepareTemplates(&include, &exclude, ctx) if err != nil { return err } From 7a930aad113703ae3a829bbc1253d218c89f1f20 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Fri, 10 Feb 2017 20:52:02 +0100 Subject: [PATCH 035/166] feat util: Add silly map-merge function that should be in the stdlib --- util/util.go | 25 ++++++++++++++++ util/util_test.go | 74 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 util/util.go create mode 100644 util/util_test.go diff --git a/util/util.go b/util/util.go new file mode 100644 index 000000000..3d05322ef --- /dev/null +++ b/util/util.go @@ -0,0 +1,25 @@ +package util + +// Merges two maps together. Values from the second map override values in the first map. +// The returned map is new if anything was changed. +func Merge(in1 *map[string]interface{}, in2 *map[string]interface{}) *map[string]interface{} { + if in1 == nil || len(*in1) == 0 { + return in2 + } + + if in2 == nil || len(*in2) == 0 { + return in1 + } + + + new := make(map[string]interface{}) + for k, v := range *in1 { + new[k] = v + } + + for k, v := range *in2 { + new[k] = v + } + + return &new +} diff --git a/util/util_test.go b/util/util_test.go new file mode 100644 index 000000000..c08055335 --- /dev/null +++ b/util/util_test.go @@ -0,0 +1,74 @@ +package util + +import ( + "testing" + "reflect" +) + +func TestMergeWithEmptyMap(t *testing.T) { + testMap := map[string]interface{}{ + "foo": "bar", + } + + empty := make(map[string]interface{}) + + res1 := Merge(&testMap, &empty) + res2 := Merge(&empty, &testMap) + + if res1 != &testMap || res2 != &testMap { + t.Error("A new map was returned incorrectly.") + t.Fail() + } +} + +func TestMergeWithNilMap(t *testing.T) { + testMap := map[string]interface{}{ + "foo": "bar", + } + + res1 := Merge(&testMap, nil) + res2 := Merge(nil, &testMap) + + if res1 != &testMap || res2 != &testMap { + t.Error("A new map was returned incorrectly.") + t.Fail() + } +} + +func TestMergeMaps(t *testing.T) { + map1 := map[string]interface{}{ + "foo": "bar", + } + + map2 := map[string]interface{}{ + "bar": "baz", + } + + result := Merge(&map1, &map2) + expected := map[string]interface{}{ + "foo": "bar", + "bar": "baz", + } + + if !reflect.DeepEqual(*result, expected) { + t.Error("Maps were merged incorrectly.") + t.Fail() + } +} + +func TestMergeMapsPrecedence(t *testing.T) { + map1 := map[string]interface{}{ + "foo": "incorrect", + } + + map2 := map[string]interface{}{ + "foo": "correct", + } + + result := Merge(&map1, &map2) + + if (*result)["foo"] != "correct" { + t.Error("Map merge precedence test failed.") + t.Fail() + } +} From f81fe551bcf1baa1167ed237c7120df69cf2ddab Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Fri, 10 Feb 2017 20:53:59 +0100 Subject: [PATCH 036/166] chore templater: Use new util.Merge func --- templater/templater.go | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/templater/templater.go b/templater/templater.go index 5e38ddf89..e6f8a92ee 100644 --- a/templater/templater.go +++ b/templater/templater.go @@ -13,6 +13,7 @@ import ( "github.com/Masterminds/sprig" "github.com/polydawn/meep" "github.com/tazjin/kontemplate/context" + "github.com/tazjin/kontemplate/util" ) // Error that is caused by non-existent template files being specified @@ -88,17 +89,7 @@ func templateFile(c *context.Context, rs *context.ResourceSet, filename string) var b bytes.Buffer - // Guard against empty map before merging keys - if rs.Values == nil { - rs.Values = make(map[string]interface{}, 0) - } - - // Merge global and resourceset-specific values (don't override from global) - for k, v := range c.Global { - if _, ok := rs.Values[k]; !ok { - rs.Values[k] = v - } - } + rs.Values = *util.Merge(&c.Global, &rs.Values) err = tpl.Execute(&b, rs.Values) From 0147c3e13e741f1d2bac7f082e509222fb86ab38 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Tue, 14 Feb 2017 19:00:06 +0100 Subject: [PATCH 037/166] feat ctx: Let sub-resource-sets inherit vars from parent Users of kontemplate may expect variables defined on the parent resource to be inherited by children. This implements that functionality. Values defined twice are overwritten by the child's definition. Fixes #20 --- context/context.go | 2 + context/context_test.go | 59 +++++++++++++++++++ .../testdata/parent-variable-override.yaml | 10 ++++ context/testdata/parent-variables.yaml | 10 ++++ 4 files changed, 81 insertions(+) create mode 100644 context/testdata/parent-variable-override.yaml create mode 100644 context/testdata/parent-variables.yaml diff --git a/context/context.go b/context/context.go index 108bda28a..520070d88 100644 --- a/context/context.go +++ b/context/context.go @@ -9,6 +9,7 @@ import ( "github.com/ghodss/yaml" "github.com/polydawn/meep" + "github.com/tazjin/kontemplate/util" ) type ResourceSet struct { @@ -82,6 +83,7 @@ func flattenResourceSetCollections(rs *[]ResourceSet) *[]ResourceSet { for _, subResourceSet := range r.Include { subResourceSet.Parent = r.Name subResourceSet.Name = path.Join(r.Name, subResourceSet.Name) + subResourceSet.Values = *util.Merge(&r.Values, &subResourceSet.Values) flattened = append(flattened, subResourceSet) } } diff --git a/context/context_test.go b/context/context_test.go index b34222ed4..66b857ffe 100644 --- a/context/context_test.go +++ b/context/context_test.go @@ -81,3 +81,62 @@ func TestLoadContextWithResourceSetCollections(t *testing.T) { } } + +func TestSubresourceVariableInheritance(t *testing.T) { + ctx, err := LoadContextFromFile("testdata/parent-variables.yaml") + + if err != nil { + t.Error(err) + t.Fail() + } + + expected := Context{ + Name: "k8s.prod.mydomain.com", + ResourceSets: []ResourceSet{ + { + Name: "parent/child", + Values: map[string]interface{}{ + "foo": "bar", + "bar": "baz", + }, + Include: nil, + Parent: "parent", + }, + }, + BaseDir: "testdata", + } + + if !reflect.DeepEqual(*ctx, expected) { + t.Error("Loaded and expected context did not match") + t.Fail() + } +} + +func TestSubresourceVariableInheritanceOverride(t *testing.T) { + ctx, err := LoadContextFromFile("testdata/parent-variable-override.yaml") + + if err != nil { + t.Error(err) + t.Fail() + } + + expected := Context{ + Name: "k8s.prod.mydomain.com", + ResourceSets: []ResourceSet{ + { + Name: "parent/child", + Values: map[string]interface{}{ + "foo": "newvalue", + }, + Include: nil, + Parent: "parent", + }, + }, + BaseDir: "testdata", + } + + if !reflect.DeepEqual(*ctx, expected) { + t.Error("Loaded and expected context did not match") + t.Fail() + } +} diff --git a/context/testdata/parent-variable-override.yaml b/context/testdata/parent-variable-override.yaml new file mode 100644 index 000000000..42676c302 --- /dev/null +++ b/context/testdata/parent-variable-override.yaml @@ -0,0 +1,10 @@ +--- +context: k8s.prod.mydomain.com +include: + - name: parent + values: + foo: bar + include: + - name: child + values: + foo: newvalue diff --git a/context/testdata/parent-variables.yaml b/context/testdata/parent-variables.yaml new file mode 100644 index 000000000..8459fd304 --- /dev/null +++ b/context/testdata/parent-variables.yaml @@ -0,0 +1,10 @@ +--- +context: k8s.prod.mydomain.com +include: + - name: parent + values: + foo: bar + include: + - name: child + values: + bar: baz From 0b992c6156a3beba093ff41030783a68f52377ef Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Mon, 20 Feb 2017 14:25:18 +0100 Subject: [PATCH 038/166] fix pass: Trim leading & trailing whitespace --- templater/pass.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/templater/pass.go b/templater/pass.go index f1dc82986..fbd2c0764 100644 --- a/templater/pass.go +++ b/templater/pass.go @@ -8,6 +8,7 @@ import ( "os/exec" "github.com/polydawn/meep" + "strings" ) type PassError struct { @@ -28,5 +29,7 @@ func GetFromPass(key string) (string, error) { ) } - return string(output), nil + trimmed := strings.TrimSpace(string(output)) + + return trimmed, nil } From 45aee8257fd5bb947cb127cd645d8ab571a15379 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Mon, 20 Feb 2017 14:25:39 +0100 Subject: [PATCH 039/166] style: Apply go fmt --- context/context_test.go | 4 ++-- util/util.go | 1 - util/util_test.go | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/context/context_test.go b/context/context_test.go index 66b857ffe..4c95f058c 100644 --- a/context/context_test.go +++ b/context/context_test.go @@ -100,7 +100,7 @@ func TestSubresourceVariableInheritance(t *testing.T) { "bar": "baz", }, Include: nil, - Parent: "parent", + Parent: "parent", }, }, BaseDir: "testdata", @@ -129,7 +129,7 @@ func TestSubresourceVariableInheritanceOverride(t *testing.T) { "foo": "newvalue", }, Include: nil, - Parent: "parent", + Parent: "parent", }, }, BaseDir: "testdata", diff --git a/util/util.go b/util/util.go index 3d05322ef..ea9f636ac 100644 --- a/util/util.go +++ b/util/util.go @@ -11,7 +11,6 @@ func Merge(in1 *map[string]interface{}, in2 *map[string]interface{}) *map[string return in1 } - new := make(map[string]interface{}) for k, v := range *in1 { new[k] = v diff --git a/util/util_test.go b/util/util_test.go index c08055335..a6adcd713 100644 --- a/util/util_test.go +++ b/util/util_test.go @@ -1,8 +1,8 @@ package util import ( - "testing" "reflect" + "testing" ) func TestMergeWithEmptyMap(t *testing.T) { From 3b0f41e71d8ff2763ee827d17034b5d929d0c7ff Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Tue, 4 Apr 2017 11:02:34 +0200 Subject: [PATCH 040/166] feat templater: Fail if values are missing Golang's template package now has an option for failing if template variables are missing: https://golang.org/pkg/text/template/#Template.Option This updates the templater code to make use of that option and return the errors encountered during templating. This fixes #1 --- templater/templater.go | 4 +++- templater/templater_test.go | 17 +++++++++++++++++ templater/testdata/test-template.txt | 1 + 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 templater/testdata/test-template.txt diff --git a/templater/templater.go b/templater/templater.go index e6f8a92ee..e9e8c11e9 100644 --- a/templater/templater.go +++ b/templater/templater.go @@ -16,6 +16,8 @@ import ( "github.com/tazjin/kontemplate/util" ) +const failOnMissingKeys string = "missingkey=error" + // Error that is caused by non-existent template files being specified type TemplateNotFoundError struct { meep.AllTraits @@ -78,7 +80,7 @@ func processFiles(c *context.Context, rs *context.ResourceSet, rp string, files } func templateFile(c *context.Context, rs *context.ResourceSet, filename string) (string, error) { - tpl, err := template.New(path.Base(filename)).Funcs(templateFuncs()).ParseFiles(filename) + tpl, err := template.New(path.Base(filename)).Funcs(templateFuncs()).Option(failOnMissingKeys).ParseFiles(filename) if err != nil { return "", meep.New( diff --git a/templater/templater_test.go b/templater/templater_test.go index b262787ae..4aeee254d 100644 --- a/templater/templater_test.go +++ b/templater/templater_test.go @@ -3,6 +3,7 @@ package templater import ( "github.com/tazjin/kontemplate/context" "reflect" + "strings" "testing" ) @@ -136,3 +137,19 @@ func TestApplyLimitsExcludeIncludePrecedence(t *testing.T) { t.Fail() } } + +func TestFailOnMissingKeys(t *testing.T) { + ctx := context.Context{} + resourceSet := context.ResourceSet{} + + _, err := templateFile(&ctx, &resourceSet, "testdata/test-template.txt") + + if err == nil { + t.Errorf("Template with missing keys should have failed.\n") + t.Fail() + } + + if !strings.Contains(err.Error(), "map has no entry for key \"testName\"") { + t.Errorf("Templating failed with unexpected error: %v\n", err) + } +} diff --git a/templater/testdata/test-template.txt b/templater/testdata/test-template.txt new file mode 100644 index 000000000..06f1cfc63 --- /dev/null +++ b/templater/testdata/test-template.txt @@ -0,0 +1 @@ +Template for test {{ .testName }} From 4eadb588412af8f2028ed5a71037c45f7caff269 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Tue, 4 Apr 2017 13:47:53 +0200 Subject: [PATCH 041/166] fix main: Don't panic if file is unspecified Instead of printing a spooky stacktrace when the user forgets to specify the `-f` argument, return an error a lot more gracefully. --- main.go | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/main.go b/main.go index 00d06e7dc..f5af42015 100644 --- a/main.go +++ b/main.go @@ -75,8 +75,11 @@ func applyCommand() cli.Command { include := c.StringSlice("include") exclude := c.StringSlice("exclude") ctx, err := loadContext(c) - resources, err := templater.LoadAndPrepareTemplates(&include, &exclude, ctx) + if err != nil { + return err + } + resources, err := templater.LoadAndPrepareTemplates(&include, &exclude, ctx) if err != nil { return err } @@ -102,8 +105,11 @@ func replaceCommand() cli.Command { include := c.StringSlice("include") exclude := c.StringSlice("exclude") ctx, err := loadContext(c) - resources, err := templater.LoadAndPrepareTemplates(&include, &exclude, ctx) + if err != nil { + return err + } + resources, err := templater.LoadAndPrepareTemplates(&include, &exclude, ctx) if err != nil { return err } @@ -122,9 +128,13 @@ func deleteCommand() cli.Command { Action: func(c *cli.Context) error { include := c.StringSlice("include") exclude := c.StringSlice("exclude") - ctx, err := loadContext(c) - resources, err := templater.LoadAndPrepareTemplates(&include, &exclude, ctx) + ctx, err := loadContext(c) + if err != nil { + return err + } + + resources, err := templater.LoadAndPrepareTemplates(&include, &exclude, ctx) if err != nil { return err } @@ -186,7 +196,7 @@ func loadContext(c *cli.Context) (*context.Context, error) { return nil, meep.New( &meep.ErrInvalidParam{ Param: "file", - Reason: "Cluster config file must be specified", + Reason: "Cluster config file must be specified (-f)", }, ) } From 11cfc80020010ff949de767698124cc5719361e0 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Tue, 4 Apr 2017 14:28:22 +0200 Subject: [PATCH 042/166] feat context: Support resource set default values This adds functionality to specify default values directly in resource sets. The idea is that users can create a file called `values.yaml` or `values.json` in a resource set's folder and have all variables specified in that file be automatically merged into the resource set variables with the lowest priority. This fixes #25 This fixes #30 (to a degree) --- context/context.go | 46 +++++++++++++++++++++++++-- context/context_test.go | 14 ++++++++ context/testdata/default-loading.yaml | 6 ++++ context/testdata/default/default.yaml | 2 ++ 4 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 context/testdata/default-loading.yaml create mode 100644 context/testdata/default/default.yaml diff --git a/context/context.go b/context/context.go index 520070d88..ad3d00693 100644 --- a/context/context.go +++ b/context/context.go @@ -64,8 +64,9 @@ func LoadContextFromFile(filename string) (*Context, error) { ) } - c.ResourceSets = *flattenResourceSetCollections(&c.ResourceSets) + c.ResourceSets = flattenResourceSetCollections(&c.ResourceSets) c.BaseDir = path.Dir(filename) + c.ResourceSets = loadAllDefaultValues(&c) return &c, nil } @@ -73,7 +74,7 @@ func LoadContextFromFile(filename string) (*Context, error) { // Flattens resource set collections, i.e. resource sets that themselves have an additional 'include' field set. // Those will be regarded as a short-hand for including multiple resource sets from a subfolder. // See https://github.com/tazjin/kontemplate/issues/9 for more information. -func flattenResourceSetCollections(rs *[]ResourceSet) *[]ResourceSet { +func flattenResourceSetCollections(rs *[]ResourceSet) []ResourceSet { flattened := make([]ResourceSet, 0) for _, r := range *rs { @@ -89,5 +90,44 @@ func flattenResourceSetCollections(rs *[]ResourceSet) *[]ResourceSet { } } - return &flattened + return flattened +} + +func loadAllDefaultValues(c *Context) []ResourceSet { + updated := make([]ResourceSet, len(c.ResourceSets)) + + for i, rs := range c.ResourceSets { + merged := loadDefaultValues(&rs, c) + rs.Values = *merged + updated[i] = rs + } + + return updated +} + +// Loads and merges default values for a resource set collection from path/to/set/default.{json|yaml}. +// YAML takes precedence over JSON. +// Default values in resource set collections have the lowest priority possible. +func loadDefaultValues(rs *ResourceSet, c *Context) *map[string]interface{} { + var defaultVars map[string]interface{} + + // Attempt to load YAML values + y, err := ioutil.ReadFile(path.Join(c.BaseDir, rs.Name, "default.yaml")) + if err == nil { + yaml.Unmarshal(y, &defaultVars) + return util.Merge(&defaultVars, &rs.Values) + } + + // Attempt to load JSON values + j, err := ioutil.ReadFile(path.Join(c.BaseDir, rs.Name, "default.json")) + if err == nil { + json.Unmarshal(j, &defaultVars) + return util.Merge(&defaultVars, &rs.Values) + } + + // The actual error is not inspected here. The reasoning for this is that in case of serious problems (e.g. + // permission issues with the folder / folder not existing) failure will occur a bit later anyways. + // Otherwise we'd have to differentiate between file-not-found-errors (no default values specified) and other + // errors here. + return &rs.Values } diff --git a/context/context_test.go b/context/context_test.go index 4c95f058c..8da30c9a8 100644 --- a/context/context_test.go +++ b/context/context_test.go @@ -140,3 +140,17 @@ func TestSubresourceVariableInheritanceOverride(t *testing.T) { t.Fail() } } + +func TestDefaultValuesLoading(t *testing.T) { + ctx, err := LoadContextFromFile("testdata/default-loading.yaml") + if err != nil { + t.Error(err) + t.Fail() + } + + rs := ctx.ResourceSets[0] + if rs.Values["defaultValues"] != "loaded" { + t.Errorf("Default values not loaded from YAML file") + t.Fail() + } +} diff --git a/context/testdata/default-loading.yaml b/context/testdata/default-loading.yaml new file mode 100644 index 000000000..d589c99b4 --- /dev/null +++ b/context/testdata/default-loading.yaml @@ -0,0 +1,6 @@ +--- +context: default-loading +include: + - name: default + values: + override: notAtAll \ No newline at end of file diff --git a/context/testdata/default/default.yaml b/context/testdata/default/default.yaml new file mode 100644 index 000000000..0ffa3cd81 --- /dev/null +++ b/context/testdata/default/default.yaml @@ -0,0 +1,2 @@ +defaultValues: loaded +override: noop \ No newline at end of file From 746e733cbbc8aef939eb2c3a695d5c41bbb4cb5a Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Tue, 4 Apr 2017 14:36:27 +0200 Subject: [PATCH 043/166] fix templater: Don't try to template default value files --- templater/templater.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/templater/templater.go b/templater/templater.go index e9e8c11e9..67e33a535 100644 --- a/templater/templater.go +++ b/templater/templater.go @@ -156,8 +156,12 @@ func templateFuncs() template.FuncMap { return m } -// Checks whether a file is a resource file (i.e. is YAML or JSON) +// Checks whether a file is a resource file (i.e. is YAML or JSON) and not a default values file. func isResourceFile(f os.FileInfo) bool { + if f.Name() == "default.json" || f.Name() == "default.yaml" { + return false + } + return strings.HasSuffix(f.Name(), "yaml") || strings.HasSuffix(f.Name(), "yml") || strings.HasSuffix(f.Name(), "json") From 7286751db75187533c0c83dbd46162c14ca935a4 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Tue, 4 Apr 2017 14:38:00 +0200 Subject: [PATCH 044/166] fix test: Assert variable override order --- context/context_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/context/context_test.go b/context/context_test.go index 8da30c9a8..c5b5e7f12 100644 --- a/context/context_test.go +++ b/context/context_test.go @@ -153,4 +153,9 @@ func TestDefaultValuesLoading(t *testing.T) { t.Errorf("Default values not loaded from YAML file") t.Fail() } + + if rs.Values["override"] != "notAtAll" { + t.Error("Default values should not override other values") + t.Fail() + } } From 32ca64c50a0080198a924c4d9f7c18637b4a76f4 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Thu, 4 May 2017 17:24:36 +0200 Subject: [PATCH 045/166] feat templater: Warn if no valid resource sets are included After filtering resource sets, check whether any resource sets "survived". Otherwise it can be assumed that the user specified invalid exclude/include combinations and should be warned about that. Fixes #35 --- templater/templater.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/templater/templater.go b/templater/templater.go index 67e33a535..9a00da3a7 100644 --- a/templater/templater.go +++ b/templater/templater.go @@ -33,6 +33,11 @@ type TemplatingError struct { func LoadAndPrepareTemplates(include *[]string, exclude *[]string, c *context.Context) (output []string, err error) { limitedResourceSets := applyLimits(&c.ResourceSets, include, exclude) + if len(*limitedResourceSets) == 0 { + fmt.Fprintln(os.Stderr, "No valid resource sets included!") + return + } + for _, rs := range *limitedResourceSets { err = processResourceSet(c, &rs, &output) From c8a63861aee60a156ed4a63f6b5211dc6abac225 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Thu, 4 May 2017 18:32:26 +0200 Subject: [PATCH 046/166] refactor main: Move to Kingpin CLI library Replace urfave/cli with the kingpin[1] library. It has slightly more sensible argument validation than the other Go libraries. Additionally I've opted for removing the '-f / --file' flag in favour of positional arguments to commands. A previous command like `kontemplate template -f somefile.yml` is now just `kontemplate template somefile.yml`. All other arguments remain the same. [1]: https://github.com/alecthomas/kingpin --- main.go | 222 ++++++++++++++++++++------------------------------------ 1 file changed, 77 insertions(+), 145 deletions(-) diff --git a/main.go b/main.go index f5af42015..d2aa8209b 100644 --- a/main.go +++ b/main.go @@ -8,141 +8,111 @@ import ( "github.com/polydawn/meep" "github.com/tazjin/kontemplate/context" "github.com/tazjin/kontemplate/templater" - "github.com/urfave/cli" + "gopkg.in/alecthomas/kingpin.v2" ) type KubeCtlError struct { meep.AllTraits } +var ( + app = kingpin.New("kontemplate", "simple Kubernetes resource templating") + + // Global flags + includes = app.Flag("include", "Resource sets to include explicitly").Short('i').Strings() + excludes = app.Flag("exclude", "Resource sets to exclude explicitly").Short('e').Strings() + + // Commands + template = app.Command("template", "Template resource sets and print them") + templateFile = template.Arg("file", "Cluster configuration file to use").Required().String() + + apply = app.Command("apply", "Template resources and pass to 'kubectl apply'") + applyFile = apply.Arg("file", "Cluster configuration file to use").Required().String() + applyDryRun = apply.Flag("dry-run", "Print remote operations without executing them").Default("false").Bool() + + replace = app.Command("replace", "Template resources and pass to 'kubectl replace'") + replaceFile = replace.Arg("file", "Cluster configuration file to use").Required().String() + + delete = app.Command("delete", "Template resources and pass to 'kubectl delete'") + deleteFile = delete.Arg("file", "Cluster configuration file to use").Required().String() + + create = app.Command("create", "Template resources and pass to 'kubectl create'") + createFile = create.Arg("file", "Cluster configuration file to use").Required().String() +) + func main() { - app := cli.NewApp() + app.HelpFlag.Short('h') - app.Name = "kontemplate" - app.Usage = "simple Kubernetes resource templating" - app.Version = "v1.0.0-beta1" + switch kingpin.MustParse(app.Parse(os.Args[1:])) { + case template.FullCommand(): + templateCommand() - app.Commands = []cli.Command{ - templateCommand(), - applyCommand(), - replaceCommand(), - deleteCommand(), - } + case apply.FullCommand(): + applyCommand() - app.Run(os.Args) -} + case replace.FullCommand(): + replaceCommand() -func templateCommand() cli.Command { - return cli.Command{ - Name: "template", - Usage: "Interpolate and print templates", - Flags: commonFlags(), - Action: func(c *cli.Context) error { - include := c.StringSlice("include") - exclude := c.StringSlice("exclude") + case delete.FullCommand(): + deleteCommand() - ctx, err := loadContext(c) - if err != nil { - return err - } - - resources, err := templater.LoadAndPrepareTemplates(&include, &exclude, ctx) - if err != nil { - return err - } - - for _, r := range resources { - fmt.Println(r) - } - - return nil - }, + case create.FullCommand(): + createCommand() } } -func applyCommand() cli.Command { - dryRun := false +func templateCommand() { + _, resources := loadContextAndResources(templateFile) - return cli.Command{ - Name: "apply", - Usage: "Interpolate templates and run 'kubectl apply'", - Flags: append(commonFlags(), cli.BoolFlag{ - Name: "dry-run", - Usage: "Only print objects that would be sent, without sending them", - Destination: &dryRun, - }), - Action: func(c *cli.Context) error { - include := c.StringSlice("include") - exclude := c.StringSlice("exclude") - ctx, err := loadContext(c) - if err != nil { - return err - } - - resources, err := templater.LoadAndPrepareTemplates(&include, &exclude, ctx) - if err != nil { - return err - } - - var args []string - if dryRun { - args = []string{"apply", "-f", "-", "--dry-run"} - } else { - args = []string{"apply", "-f", "-"} - } - - return runKubectlWithResources(ctx, &args, &resources) - }, + for _, r := range *resources { + fmt.Println(r) } } -func replaceCommand() cli.Command { - return cli.Command{ - Name: "replace", - Usage: "Interpolate templates and run 'kubectl replace'", - Flags: commonFlags(), - Action: func(c *cli.Context) error { - include := c.StringSlice("include") - exclude := c.StringSlice("exclude") - ctx, err := loadContext(c) - if err != nil { - return err - } +func applyCommand() { + ctx, resources := loadContextAndResources(applyFile) - resources, err := templater.LoadAndPrepareTemplates(&include, &exclude, ctx) - if err != nil { - return err - } + var kubectlArgs []string - args := []string{"replace", "--save-config=true", "-f", "-"} - return runKubectlWithResources(ctx, &args, &resources) - }, + if *applyDryRun { + kubectlArgs = []string{"apply", "-f", "-", "--dry-run"} + } else { + kubectlArgs = []string{"apply", "-f", "-"} } + + runKubectlWithResources(ctx, &kubectlArgs, resources) } -func deleteCommand() cli.Command { - return cli.Command{ - Name: "delete", - Usage: "Interpolate templates and run 'kubectl delete'", - Flags: commonFlags(), - Action: func(c *cli.Context) error { - include := c.StringSlice("include") - exclude := c.StringSlice("exclude") +func replaceCommand() { + ctx, resources := loadContextAndResources(replaceFile) + args := []string{"replace", "--save-config=true", "-f", "-"} + runKubectlWithResources(ctx, &args, resources) +} - ctx, err := loadContext(c) - if err != nil { - return err - } +func deleteCommand() { + ctx, resources := loadContextAndResources(deleteFile) + args := []string{"delete", "-f", "-"} + runKubectlWithResources(ctx, &args, resources) +} - resources, err := templater.LoadAndPrepareTemplates(&include, &exclude, ctx) - if err != nil { - return err - } +func createCommand() { + ctx, resources := loadContextAndResources(createFile) + args := []string{"create", "--save-config=true", "-f", "-"} + runKubectlWithResources(ctx, &args, resources) +} - args := []string{"delete", "-f", "-"} - return runKubectlWithResources(ctx, &args, &resources) - }, +func loadContextAndResources(file *string) (*context.Context, *[]string) { + ctx, err := context.LoadContextFromFile(*file) + if err != nil { + app.Fatalf("Error loading context: %v\n", err) } + + resources, err := templater.LoadAndPrepareTemplates(includes, excludes, ctx) + if err != nil { + app.Fatalf("Error templating resource sets: %v\n", err) + } + + return ctx, &resources } func runKubectlWithResources(c *context.Context, kubectlArgs *[]string, resources *[]string) error { @@ -171,41 +141,3 @@ func runKubectlWithResources(c *context.Context, kubectlArgs *[]string, resource return nil } - -func commonFlags() []cli.Flag { - return []cli.Flag{ - cli.StringFlag{ - Name: "file, f", - Usage: "Cluster configuration file to use", - }, - cli.StringSliceFlag{ - Name: "include, i", - Usage: "Limit templating to explicitly included resource sets", - }, - cli.StringSliceFlag{ - Name: "exclude, e", - Usage: "Exclude certain resource sets from templating", - }, - } -} - -func loadContext(c *cli.Context) (*context.Context, error) { - f := c.String("file") - - if f == "" { - return nil, meep.New( - &meep.ErrInvalidParam{ - Param: "file", - Reason: "Cluster config file must be specified (-f)", - }, - ) - } - - ctx, err := context.LoadContextFromFile(f) - - if err != nil { - return nil, err - } - - return ctx, nil -} From 211289765af73d4a429fd4eeb304bb64d78fbff1 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Thu, 4 May 2017 18:43:11 +0200 Subject: [PATCH 047/166] docs README: Update usage examples for kingpin CLI --- README.md | 56 +++++++++++++++++++++++++++---------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index b9390a855..fbbbcf606 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -KonTemplate - A simple Kubernetes templater +Kontemplate - A simple Kubernetes templater =========================================== [![Build Status](https://travis-ci.org/tazjin/kontemplate.svg?branch=master)](https://travis-ci.org/tazjin/kontemplate) @@ -9,7 +9,7 @@ which context (i.e. k8s cluster) is specified. ## Overview -KonTemplate lets you describe resources as you normally would in a simple folder structure: +Kontemplate lets you describe resources as you normally would in a simple folder structure: ``` . @@ -45,48 +45,48 @@ Assuming you have Go configured correctly, you can simply `go get github.com/taz ## Usage -You must have `kubectl` installed to use KonTemplate effectively. +You must have `kubectl` installed to use Kontemplate effectively. ``` -NAME: - kontemplate - simple Kubernetes resource templating +usage: kontemplate [] [ ...] -USAGE: - kontemplate [global options] command [command options] [arguments...] +simple Kubernetes resource templating -VERSION: - 0.0.1 +Flags: + -h, --help Show context-sensitive help (also try --help-long and --help-man). + -i, --include=INCLUDE ... Resource sets to include explicitly + -e, --exclude=EXCLUDE ... Resource sets to exclude explicitly -COMMANDS: - template Interpolate and print templates - apply Interpolate templates and run 'kubectl apply' - replace Interpolate templates and run 'kubectl replace' - delete Interpolate templates and run 'kubectl delete' - help, h Shows a list of commands or help for one command +Commands: + help [...] + Show help. -GLOBAL OPTIONS: - --help, -h show help - --version, -v print the version -``` + template + Template resource sets and print them -All options support the same set of extra flags: + apply [] + Template resources and pass to 'kubectl apply' + + replace + Template resources and pass to 'kubectl replace' + + delete + Template resources and pass to 'kubectl delete' + + create + Template resources and pass to 'kubectl create' -``` -OPTIONS: - --file value, -f value Cluster configuration file to use - --include value, -i value Limit templating to explicitly included resource sets - --exclude value, -e value Exclude certain resource sets from templating ``` Examples: ``` # Look at output for a specific resource set and check to see if it's correct ... -kontemplate template -f example/prod-cluster.yaml -i some-api +kontemplate template example/prod-cluster.yaml -i some-api # ... maybe do a dry-run to see what kubectl would do: -kontemplate apply -f example/prod-cluster.yaml --dry-run +kontemplate apply example/prod-cluster.yaml --dry-run # And actually apply it if you like what you see: -kontemplate apply -f example/prod-cluster.yaml +kontemplate apply example/prod-cluster.yaml ``` \ No newline at end of file From 7644a1f675dc225f8758b51c8929c715c33d2606 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Mon, 8 May 2017 10:13:10 +0200 Subject: [PATCH 048/166] docs: Document template format --- docs/templates.md | 91 +++++++++++++++++++++++++++++++++++++++++ docs/tips-and-tricks.md | 0 2 files changed, 91 insertions(+) create mode 100644 docs/templates.md create mode 100644 docs/tips-and-tricks.md diff --git a/docs/templates.md b/docs/templates.md new file mode 100644 index 000000000..bfd254630 --- /dev/null +++ b/docs/templates.md @@ -0,0 +1,91 @@ +Kontemplate templates +===================== + +The template file format is based on Go's [templating engine][] in combination +with a small extension library called [sprig][] that adds additional template +functions. + +## Basic variable interpolation + +The basic template format uses `{{ .variableName }}` as the interpolation format. + +### Example: + +Assuming that you include a resource set as such: + +``` +- name: api-gateway + values: + internalHost: http://my-internal-host/ +``` + +And the api-gateway resource set includes a ConfigMap (some fields left out for +the example): + +``` +# api-gateway/configmap.yaml: +--- +kind: ConfigMap +metadata: + name: api-gateway-config +data: + internalHost: {{ .internalHost }} +``` + +The resulting output will be: + +``` + +--- +kind: ConfigMap +metadata: + name: api-gateway-config +data: + internalHost: http://my-internal-host/ +``` + +## Template functions + +Go templates support template functions which you can think of as a sort of +shell-like pipeline where text flows through transformations from left to +right. + +Some template functions come from Go's standard library and are listed in the +[Go documentation][]. In addition the functions declared by [sprig][] are +available in kontemplate, as well as two custom functions: + +`json`: Encodes any supplied data structure as JSON. +`passLookup`: Looks up the supplied key in [pass][] + +## Examples: + +``` +# With the following values: +name: Donald +certKeyPath: my-website/cert-key + +# The following interpolations are possible: + +{{ .name | upper }} +-> DONALD + +{{ .name | upper | repeat 2 }} +-> DONALD DONALD + +{{ .certKeyPath | passLookup }} +-> Returns content of 'my-website/cert-key' from pass +``` + +## Caveats + +Kontemplate does not by itself parse any of the content of the templates, which +means that it does not validate whether the resources you supply are valid YAML +or JSON. + +You can perform some validation by using `kontemplate apply --dry-run` which +will make use of the Dry-Run functionality in `kubectl`. + +[templating engine]: https://golang.org/pkg/text/template/ +[sprig]: http://masterminds.github.io/sprig/ +[Go documentation]: https://golang.org/pkg/text/template/#hdr-Functions +[pass]: https://www.passwordstore.org/ diff --git a/docs/tips-and-tricks.md b/docs/tips-and-tricks.md new file mode 100644 index 000000000..e69de29bb From 5bc67f42712367008bbe719c57659d7672b9f23f Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Mon, 8 May 2017 10:24:56 +0200 Subject: [PATCH 049/166] docs: Add some tips and tricks --- docs/tips-and-tricks.md | 67 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/docs/tips-and-tricks.md b/docs/tips-and-tricks.md index e69de29bb..debf014cf 100644 --- a/docs/tips-and-tricks.md +++ b/docs/tips-and-tricks.md @@ -0,0 +1,67 @@ +Kontemplate tips & tricks +========================= + + +## Update Deployments when ConfigMaps change + +Kubernetes does [not currently][] have the ability to perform rolling updates +of Deployments and other resource types when `ConfigMap` or `Secret` objects +are updated. + +It is possible to make use of annotations and templating functions in +Kontemplate to force updates to these resources anyways (assuming that the +`ConfigMap` or `Secret` contains interpolated variables). + +For example: + +```yaml +# A ConfigMap that contains some data structure in JSON format +--- +kind: ConfigMap +metadata: + name: app-config +data: + configFile: {{ .appConfig | json }} +``` + +Now whenever the `appConfig` variable changes we would like to update the +`Deployment` making use of it, too. We can do this by adding a hash of the +configuration to the annotations of the created `Pod` objects: + +```yaml + +--- +kind: Deployment +metadata: + name: app +spec: + template: + metadata: + annotations: + configHash: {{ .appConfig | json | sha256sum }} + spec: + containers: + - name: app + # Some details omitted ... + volumeMounts: + - name: config + mountPath: /etc/app/ + volumes: + - name: config + configMap: + name: app-config +``` + +Now if the `ConfigMap` object appears first in the resource files, `kubectl` +will apply the resources sequentially and the updated annotation will cause +a rolling update of all relevant pods. + +## direnv & pass + +Users of `pass` may have multiple different password stores on their machines. +Assuming that `kontemplate` configuration exists somewhere on the filesystem +per project, it is easy to use [direnv][] to switch to the correct +`PASSWORD_STORE_DIR` variable when entering the folder. + +[not currently]: https://github.com/kubernetes/kubernetes/issues/22368 +[direnv]: https://direnv.net/ \ No newline at end of file From c2a8f8784f9500056a3abf966168262784c07a1f Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Mon, 8 May 2017 10:32:58 +0200 Subject: [PATCH 050/166] docs: Add more information to README --- README.md | 43 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index fbbbcf606..eb677ea4b 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,15 @@ Kontemplate - A simple Kubernetes templater [![Build Status](https://travis-ci.org/tazjin/kontemplate.svg?branch=master)](https://travis-ci.org/tazjin/kontemplate) -I made this tool out of frustration with the available ways to template Kubernetes resource files. All I want out of -such a tool is a way to specify lots of resources with placeholders that get filled in with specific values, based on -which context (i.e. k8s cluster) is specified. +Kontemplate is a simple CLI tool that can take sets of Kubernetes resource +files with placeholders and insert values per environment. + +This tool was made because in many cases all I want in terms of Kubernetes +configuration is simple value interpolation per environment (i.e. Kubernetes +cluster), but with the same deployment files. + +In my experience this is often enough and more complex solutions such as +[Helm][] are not required. ## Overview @@ -37,7 +43,32 @@ include: apiPort: 4567 ``` -Those values are then templated into the resource files of `some-api`. +Those values are then templated into the resource files of `some-api`. That's it! + +You can also set up more complicated folder structures for organisation, for example: + +``` +. +├── api +│   ├── image-api +│   │   └── deployment.yaml +│   └── music-api +│   └── deployment.yaml +│   │   └── default.json +├── frontend +│   ├── main-app +│   │   ├── deployment.yaml +│   │   └── service.yaml +│   └── user-page +│   ├── deployment.yaml +│   └── service.yaml +├── prod-cluster.yaml +└── test-cluster.yaml +``` + +And selectively template or apply resources with a command such as +`kontemplate apply test-cluster.yaml --include api --include frontend/user-page` +to only update the `api` resource sets and the `frontend/user-page` resource set. ## Installation @@ -89,4 +120,6 @@ kontemplate apply example/prod-cluster.yaml --dry-run # And actually apply it if you like what you see: kontemplate apply example/prod-cluster.yaml -``` \ No newline at end of file +``` + +[Helm]: https://helm.sh/ \ No newline at end of file From 9b2d102bbf8249a577f9c675b268507c61c3271e Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Mon, 8 May 2017 10:34:16 +0200 Subject: [PATCH 051/166] docs: Add note about binary releases to README --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index eb677ea4b..0227de8fb 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,8 @@ to only update the `api` resource sets and the `frontend/user-page` resource set Assuming you have Go configured correctly, you can simply `go get github.com/tazjin/kontemplate/...`. +There are signed binary releases available on the [releases page][] for Linux, OS X and Windows. + ## Usage You must have `kubectl` installed to use Kontemplate effectively. @@ -122,4 +124,5 @@ kontemplate apply example/prod-cluster.yaml --dry-run kontemplate apply example/prod-cluster.yaml ``` -[Helm]: https://helm.sh/ \ No newline at end of file +[Helm]: https://helm.sh/ +[releases page]: https://github.com/tazjin/kontemplate/releases From d93bc51e86081c7331554167fbba1318ac9a4927 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Mon, 8 May 2017 11:08:21 +0200 Subject: [PATCH 052/166] feat main: Add version command Adds a version command that can have the Kontemplate git hash added to it at build time by using the Go linker's -X flag. --- main.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/main.go b/main.go index d2aa8209b..23b4e6c2c 100644 --- a/main.go +++ b/main.go @@ -11,6 +11,10 @@ import ( "gopkg.in/alecthomas/kingpin.v2" ) +const version string = "1.0" +// This variable will be initialised by the Go linker during the builder +var gitHash string + type KubeCtlError struct { meep.AllTraits } @@ -38,6 +42,8 @@ var ( create = app.Command("create", "Template resources and pass to 'kubectl create'") createFile = create.Arg("file", "Cluster configuration file to use").Required().String() + + versionCmd = app.Command("version", "Show kontemplate version") ) func main() { @@ -58,6 +64,17 @@ func main() { case create.FullCommand(): createCommand() + + case versionCmd.FullCommand(): + versionCommand() + } +} + +func versionCommand() { + if gitHash == "" { + fmt.Printf("Kontemplate version %s (git commit unknown)\n", version) + } else { + fmt.Printf("Kontemplate version %s (git commit: %s)\n", version, gitHash) } } From 1e3ecad709691256d5dabcab448e946aa543bb85 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Mon, 8 May 2017 11:12:00 +0200 Subject: [PATCH 053/166] feat release: Add simple release script Adds a simple script that will build stripped binaries for various platforms and GPG-sign them. --- .gitignore | 1 + README.md | 3 ++- build-release.sh | 57 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 1 deletion(-) create mode 100755 build-release.sh diff --git a/.gitignore b/.gitignore index 9f11b755a..53a04aab3 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .idea/ +release/ diff --git a/README.md b/README.md index 0227de8fb..0fc78e817 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,8 @@ to only update the `api` resource sets and the `frontend/user-page` resource set Assuming you have Go configured correctly, you can simply `go get github.com/tazjin/kontemplate/...`. -There are signed binary releases available on the [releases page][] for Linux, OS X and Windows. +There are signed binary releases available on the [releases page][] for Linux, OS X, +FreeBSD and Windows. ## Usage diff --git a/build-release.sh b/build-release.sh new file mode 100755 index 000000000..d70bd3eea --- /dev/null +++ b/build-release.sh @@ -0,0 +1,57 @@ +#!/bin/bash +set -ueo pipefail + +readonly GIT_HASH="$(git rev-parse --short HEAD)" +readonly LDFLAGS="-X main.gitHash=${GIT_HASH} -w -s" +readonly VERSION="1.0-${GIT_HASH}" + +function build-for() { + local os="${1}" + local arch="${2}" + local target="release/${os}/${arch}" + + echo "Building kontemplate for ${os}-${arch} in ${target}" + + mkdir -p "${target}" + + env GOOS="${os}" GOARCH="${arch}" go build \ + -ldflags "${LDFLAGS}" \ + -o "${target}/kontemplate" \ + -tags netgo + + +} + +function sign-for() { + local os="${1}" + local arch="${2}" + local target="release/${os}/${arch}" + local bin="${target}/kontemplate" + local hash="$(sha256sum ${bin})" + local tar="release/kontemplate-${VERSION}-${os}-${arch}.tar.gz" + + echo "Signing kontemplate binary for ${os}-${arch} with SHA256 ${hash}" + gpg --sign "${bin}" + + echo "Packing release into ${tar}" + tar czvf "${tar}" -C "${target}" kontemplate kontemplate.gpg +} + +case "${1}" in + "build") + # Build releases for various operating systems: + build-for "linux" "amd64" + build-for "darwin" "amd64" + build-for "windows" "amd64" + build-for "freebsd" "amd64" + exit 0 + ;; + "sign") + # Sign releases: + sign-for "linux" "amd64" + sign-for "darwin" "amd64" + sign-for "windows" "amd64" + sign-for "freebsd" "amd64" + exit 0 + ;; +esac From d15dfefce2c2e29205b25f1317f706588d8062a3 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Mon, 8 May 2017 13:47:43 +0200 Subject: [PATCH 054/166] feat build: Add ArchLinux PKGBUILD --- PKGBUILD | 38 ++++++++++++++++++++++++++++++++++++++ main.go | 1 + 2 files changed, 39 insertions(+) create mode 100644 PKGBUILD diff --git a/PKGBUILD b/PKGBUILD new file mode 100644 index 000000000..e6b148b54 --- /dev/null +++ b/PKGBUILD @@ -0,0 +1,38 @@ +# Maintainer: Vincent Ambo +pkgname=kontemplate-git +pkgver=master_1e3ecad +pkgrel=1 +pkgdesc="Simple Kubernetes resource templating" +arch=('x86_64') +url="https://github.com/tazjin/kontemplate" +license=('MIT') +makedepends=('go') +optdepends=('pass: Template secrets into resources') +source=('kontemplate-git::git+https://github.com/tazjin/kontemplate.git') +md5sums=('SKIP') + +pkgver() { + cd "$srcdir/$pkgname" + echo -n "master_$(git rev-parse --short HEAD)" +} + +prepare() { + cd "$srcdir/$pkgname" + echo "Fetching Go dependencies..." + go get -v ./... +} + +build() { + cd "$srcdir/$pkgname" + local GIT_HASH="$(git rev-parse --short HEAD)" + + go build -tags netgo \ + -ldflags "-X main.gitHash=${GIT_HASH} -w -s" \ + -o 'kontemplate' +} + +package() { + cd "$srcdir/$pkgname" + + install -D -m 0755 'kontemplate' "${pkgdir}/usr/bin/kontemplate" +} diff --git a/main.go b/main.go index 23b4e6c2c..53f4defd8 100644 --- a/main.go +++ b/main.go @@ -12,6 +12,7 @@ import ( ) const version string = "1.0" + // This variable will be initialised by the Go linker during the builder var gitHash string From 20ccc33347c325075cc6f9dfab637ec22660347e Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Mon, 8 May 2017 13:53:24 +0200 Subject: [PATCH 055/166] docs: Mention AUR package in README --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 0fc78e817..50bfae9fe 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,8 @@ Assuming you have Go configured correctly, you can simply `go get github.com/taz There are signed binary releases available on the [releases page][] for Linux, OS X, FreeBSD and Windows. +An [AUR package][] is available for Arch Linux and other `pacman`-based distributions. + ## Usage You must have `kubectl` installed to use Kontemplate effectively. @@ -127,3 +129,4 @@ kontemplate apply example/prod-cluster.yaml [Helm]: https://helm.sh/ [releases page]: https://github.com/tazjin/kontemplate/releases +[AUR package]: https://aur.archlinux.org/packages/kontemplate-git/ From cb6413bff708551975eda7368595a60a144731af Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Mon, 8 May 2017 14:18:13 +0200 Subject: [PATCH 056/166] fix main: Exit with kubectl status code If kubectl fails during a kontemplate run, kontemplate should also exit with a non-zero status code. This fixes #43 --- build-release.sh | 2 +- main.go | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/build-release.sh b/build-release.sh index d70bd3eea..32eefaf39 100755 --- a/build-release.sh +++ b/build-release.sh @@ -3,7 +3,7 @@ set -ueo pipefail readonly GIT_HASH="$(git rev-parse --short HEAD)" readonly LDFLAGS="-X main.gitHash=${GIT_HASH} -w -s" -readonly VERSION="1.0-${GIT_HASH}" +readonly VERSION="1.0.1-${GIT_HASH}" function build-for() { local os="${1}" diff --git a/main.go b/main.go index 53f4defd8..0fcf93d66 100644 --- a/main.go +++ b/main.go @@ -11,7 +11,7 @@ import ( "gopkg.in/alecthomas/kingpin.v2" ) -const version string = "1.0" +const version string = "1.0.1" // This variable will be initialised by the Go linker during the builder var gitHash string @@ -155,7 +155,5 @@ func runKubectlWithResources(c *context.Context, kubectlArgs *[]string, resource } stdin.Close() - kubectl.Wait() - - return nil + return kubectl.Wait() } From c45e616258f4187dca19964508b97a94d2893430 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Mon, 8 May 2017 14:29:47 +0200 Subject: [PATCH 057/166] fix main: Print information about kubectl errors --- main.go | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/main.go b/main.go index 0fcf93d66..3a0febbb0 100644 --- a/main.go +++ b/main.go @@ -98,25 +98,36 @@ func applyCommand() { kubectlArgs = []string{"apply", "-f", "-"} } - runKubectlWithResources(ctx, &kubectlArgs, resources) + if err := runKubectlWithResources(ctx, &kubectlArgs, resources); err != nil { + failWithKubectlError(err) + } } func replaceCommand() { ctx, resources := loadContextAndResources(replaceFile) args := []string{"replace", "--save-config=true", "-f", "-"} - runKubectlWithResources(ctx, &args, resources) + + if err := runKubectlWithResources(ctx, &args, resources); err != nil { + failWithKubectlError(err) + } } func deleteCommand() { ctx, resources := loadContextAndResources(deleteFile) args := []string{"delete", "-f", "-"} - runKubectlWithResources(ctx, &args, resources) + + if err := runKubectlWithResources(ctx, &args, resources); err != nil { + failWithKubectlError(err) + } } func createCommand() { ctx, resources := loadContextAndResources(createFile) args := []string{"create", "--save-config=true", "-f", "-"} - runKubectlWithResources(ctx, &args, resources) + + if err := runKubectlWithResources(ctx, &args, resources); err != nil { + failWithKubectlError(err) + } } func loadContextAndResources(file *string) (*context.Context, *[]string) { @@ -157,3 +168,8 @@ func runKubectlWithResources(c *context.Context, kubectlArgs *[]string, resource return kubectl.Wait() } + +func failWithKubectlError(err error) { + fmt.Errorf("Kubectl error: %v\n", err) + os.Exit(1) +} From aadea2f698fedf5423184b854913d126a6219fb5 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Thu, 18 May 2017 19:35:44 +0200 Subject: [PATCH 058/166] feat build: Add Repeatr formula for repeatable builds Adds a formula for Repeatr (http://repeatr.io/) that can be used to build kontemplate in a repeatable way with pinned dependencies. Fixes #47 --- kontemplate.frm | 83 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 kontemplate.frm diff --git a/kontemplate.frm b/kontemplate.frm new file mode 100644 index 000000000..2e200ccb0 --- /dev/null +++ b/kontemplate.frm @@ -0,0 +1,83 @@ +inputs: + "/": # https://github.com/tklx/base + type: "tar" + hash: "9nkvYhmJHaeK_Agc3Lm5rg444dSLWDp0Pri-KilHiX3A9Pt4TaQ7RxOj5qMSs6XT" + silo: "https://github.com/tklx/base/releases/download/0.1.1/rootfs.tar.xz" + "/opt": + type: "tar" + hash: "gi0Kpb-VH3TK0UBX6YmpuKsrMAUlxicPrY2YvXPo9sBQm_NsD_hKrn7pmc95zrmM" + silo: "https://storage.googleapis.com/golang/go1.8.1.linux-amd64.tar.gz" + # Kontemplate dependencies! + "/go/src/github.com/polydawn/meep": + type: "git" + hash: "1487840a4bf30270decdc04123c41cdc7a8067c9" + silo: "https://github.com/polydawn/meep" + "/go/src/github.com/Masterminds/sprig": + type: "git" + hash: "f5b0ed4a680a0943228155eaf6a77a96ead1bc77" + silo: "https://github.com/Masterminds/sprig" + "/go/src/github.com/ghodss/yaml": + type: "git" + hash: "0ca9ea5df5451ffdf184b4428c902747c2c11cd7" + silo: "https://github.com/ghodss/yaml" + "/go/src/gopkg.in/yaml.v2": + type: "git" + hash: "cd8b52f8269e0feb286dfeef29f8fe4d5b397e0b" + silo: "https://gopkg.in/yaml.v2" + "/go/src/gopkg.in/alecthomas/kingpin.v2": + type: "git" + hash: "7f0871f2e17818990e4eed73f9b5c2f429501228" + silo: "https://gopkg.in/alecthomas/kingpin.v2" + "/go/src/github.com/alecthomas/template": + type: "git" + hash: "a0175ee3bccc567396460bf5acd36800cb10c49c" + silo: "https://github.com/alecthomas/template" + "/go/src/github.com/alecthomas/units": + type: "git" + hash: "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a" + silo: "https://github.com/alecthomas/units" + "/go/src/github.com/Masterminds/semver": + type: "git" + hash: "abff1900528dbdaf6f3f5aa92c398be1eaf2a9f7" + silo: "https://github.com/Masterminds/semver" + "/go/src/github.com/aokoli/goutils": + type: "git" + hash: "e57d01ace047c1a43e6a49ecf3ecc50ed2be81d1" + silo: "https://github.com/aokoli/goutils" + "/go/src/github.com/huandu/xstrings": + type: "git" + hash: "3959339b333561bf62a38b424fd41517c2c90f40" + silo: "https://github.com/huandu/xstrings" + "/go/src/github.com/imdario/mergo": + type: "git" + hash: "d806ba8c21777d504a2090a2ca4913c750dd3a33" + silo: "https://github.com/imdario/mergo" + "/go/src/github.com/satori/go.uuid": + type: "git" + hash: "5bf94b69c6b68ee1b541973bb8e1144db23a194b" + silo: "https://github.com/satori/go.uuid" + "/go/src/golang.org/x/crypto": + type: "git" + hash: "ab89591268e0c8b748cbe4047b00197516011af5" + silo: "https://go.googlesource.com/crypto" +action: + policy: governor + command: + - "/bin/sh" + - "-e" + - "-c" + - | + export PATH="/opt/go/bin:$PATH" + export GOROOT=/opt/go + export GOPATH=/go + echo 'nameserver 8.8.8.8' > /etc/resolv.conf + apt-get update && apt-get install -y git ca-certificates + mkdir -p /go/src/github.com/tazjin + git clone --single-branch --branch v1.0.1 https://github.com/tazjin/kontemplate /go/src/github.com/tazjin/kontemplate + cd /go/src/github.com/tazjin/kontemplate + ./build-release.sh build +outputs: + "release": + type: "dir" + mount: "/go/src/github.com/tazjin/kontemplate/release" + silo: "file://release" From f79b261079336cd36bc923e8cf7f35f7295820ce Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Thu, 18 May 2017 19:37:01 +0200 Subject: [PATCH 059/166] chore: Version bump to 1.0.2 --- build-release.sh | 4 +--- kontemplate.frm | 2 +- main.go | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/build-release.sh b/build-release.sh index 32eefaf39..7f82db223 100755 --- a/build-release.sh +++ b/build-release.sh @@ -3,7 +3,7 @@ set -ueo pipefail readonly GIT_HASH="$(git rev-parse --short HEAD)" readonly LDFLAGS="-X main.gitHash=${GIT_HASH} -w -s" -readonly VERSION="1.0.1-${GIT_HASH}" +readonly VERSION="1.0.2-${GIT_HASH}" function build-for() { local os="${1}" @@ -18,8 +18,6 @@ function build-for() { -ldflags "${LDFLAGS}" \ -o "${target}/kontemplate" \ -tags netgo - - } function sign-for() { diff --git a/kontemplate.frm b/kontemplate.frm index 2e200ccb0..e8e1ff390 100644 --- a/kontemplate.frm +++ b/kontemplate.frm @@ -73,7 +73,7 @@ action: echo 'nameserver 8.8.8.8' > /etc/resolv.conf apt-get update && apt-get install -y git ca-certificates mkdir -p /go/src/github.com/tazjin - git clone --single-branch --branch v1.0.1 https://github.com/tazjin/kontemplate /go/src/github.com/tazjin/kontemplate + git clone --single-branch --branch v1.0.2 https://github.com/tazjin/kontemplate /go/src/github.com/tazjin/kontemplate cd /go/src/github.com/tazjin/kontemplate ./build-release.sh build outputs: diff --git a/main.go b/main.go index 3a0febbb0..033f2b5ca 100644 --- a/main.go +++ b/main.go @@ -11,7 +11,7 @@ import ( "gopkg.in/alecthomas/kingpin.v2" ) -const version string = "1.0.1" +const version string = "1.0.2" // This variable will be initialised by the Go linker during the builder var gitHash string From de4171da318ca3b14f9aa14277c2685344a3d0a0 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Thu, 18 May 2017 20:33:11 +0200 Subject: [PATCH 060/166] feat build: Add Homebrew binary formula Adds a Homebrew formula that downloads and installs the 1.0.2 binary release. Users should be able to "tap" this formula from OS X, the README will be updated in a separate commit. This fixes #41 --- kontemplate.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 kontemplate.rb diff --git a/kontemplate.rb b/kontemplate.rb new file mode 100644 index 000000000..0f0ec6424 --- /dev/null +++ b/kontemplate.rb @@ -0,0 +1,13 @@ +# Homebrew binary formula for Kontemplate + +class Kontemplate < Formula + desc "Kontemplate - Extremely simple Kubernetes resource templates" + homepage "https://github.com/tazjin/kontemplate" + url "https://github.com/tazjin/kontemplate/releases/download/v1.0.2/kontemplate-1.0.2-f79b261-darwin-amd64.tar.gz" + sha256 "5a2db5467bc77e4379b5b98f35c9864010f7023ae01a25fb5cda1aede59e021c" + version "1.0.2-f79b261" + + def install + bin.install "kontemplate" + end +end From 9923b1e64d5e0a096353b06db6b132b4e4331e25 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Thu, 18 May 2017 20:54:31 +0200 Subject: [PATCH 061/166] docs README: Update installation instructions --- README.md | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 50bfae9fe..7b4d9ff6a 100644 --- a/README.md +++ b/README.md @@ -72,13 +72,33 @@ to only update the `api` resource sets and the `frontend/user-page` resource set ## Installation -Assuming you have Go configured correctly, you can simply `go get github.com/tazjin/kontemplate/...`. +It is recommended to install Kontemplate from the signed binary releases available on the +[releases page][]. Release binaries are available for Linux, OS X, FreeBSD and Windows. -There are signed binary releases available on the [releases page][] for Linux, OS X, -FreeBSD and Windows. +### Homebrew + +OS X users with Homebrew installed can "tap" Kontemplate like such: + +```sh +brew tap tazjin/kontemplate https://github.com/tazjin/kontemplate +brew install kontemplate +``` + +### Arch Linux An [AUR package][] is available for Arch Linux and other `pacman`-based distributions. +### Building repeatably from source + +Version pinning for Go dependencies is provided by a [Repeatr][] formula. After cloning +the repository the latest release can be built with `repeatr run kontemplate.frm`. + +This will place release binaries in the `release` folder. + +### Building from source + +Assuming you have Go configured correctly, you can simply `go get github.com/tazjin/kontemplate/...`. + ## Usage You must have `kubectl` installed to use Kontemplate effectively. @@ -130,3 +150,4 @@ kontemplate apply example/prod-cluster.yaml [Helm]: https://helm.sh/ [releases page]: https://github.com/tazjin/kontemplate/releases [AUR package]: https://aur.archlinux.org/packages/kontemplate-git/ +[Repeatr]: http://repeatr.io/ From d76ea59f4c9dcdaace382568d2a4037ccce1ccba Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 7 Jun 2017 10:02:48 +0200 Subject: [PATCH 062/166] feat image: Add Dockerfile for CI pipeline image Adds a simple Docker image that can be used in CI pipelines to deploy `kontemplate`-based environments. This image contains kontemplate and all of its dependencies (including pass as an optional dependency). --- image/Dockerfile | 14 ++++++++++++++ image/README.md | 12 ++++++++++++ image/hashes | 2 ++ 3 files changed, 28 insertions(+) create mode 100644 image/Dockerfile create mode 100644 image/README.md create mode 100644 image/hashes diff --git a/image/Dockerfile b/image/Dockerfile new file mode 100644 index 000000000..90a3385dd --- /dev/null +++ b/image/Dockerfile @@ -0,0 +1,14 @@ +FROM alpine:3.6 + +ADD hashes /root/hashes +ADD https://storage.googleapis.com/kubernetes-release/release/v1.6.4/bin/linux/amd64/kubectl /usr/bin/kubectl +ADD https://github.com/tazjin/kontemplate/releases/download/v1.0.2/kontemplate-1.0.2-f79b261-linux-amd64.tar.gz /tmp/kontemplate.tar.gz + +# Pass release version is 1.7.1 +ADD https://raw.githubusercontent.com/zx2c4/password-store/38ec1c72e29c872ec0cdde82f75490640d4019bf/src/password-store.sh /usr/bin/pass + +RUN sha256sum -c /root/hashes && \ + apk add -U bash tree gnupg && \ + chmod +x /usr/bin/kubectl /usr/bin/pass && \ + tar xzvf /tmp/kontemplate.tar.gz && \ + mv kontemplate /usr/bin/kontemplate diff --git a/image/README.md b/image/README.md new file mode 100644 index 000000000..fe0476540 --- /dev/null +++ b/image/README.md @@ -0,0 +1,12 @@ +Kontemplate Docker image +======================== + +This builds a simple Docker image available on the Docker Hub as `tazjin/kontemplate`. + +Builds are automated based on the Dockerfile contained here. + +It contains both `kontemplate` and `kubectl` and can be used as part of container-based +CI pipelines. + +`pass` and its dependencies are also installed to enable the use of the `passLookup` +template function if desired. diff --git a/image/hashes b/image/hashes new file mode 100644 index 000000000..cf08300ac --- /dev/null +++ b/image/hashes @@ -0,0 +1,2 @@ +156e5592ed3ae6c9aff12fbbed17141ed7b3ede26ed169001df5b1211435f033 /tmp/kontemplate.tar.gz +a91c6b028bb3f737898ff003f7f3a3f2d242ea52e89ade1f6ca3ab99170119e5 /usr/bin/kubectl From f3264329b92c93177e771e3786a91e0b9c68dec5 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sun, 11 Jun 2017 21:25:56 +0200 Subject: [PATCH 063/166] refactor templater: Add intermediate type to represent rendered RSes As a first step in resolving #51 this refactors the `templater` package to return rendered resource sets as a distinct type. This also fixes #56 --- templater/templater.go | 49 ++++++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/templater/templater.go b/templater/templater.go index 9a00da3a7..afb96423e 100644 --- a/templater/templater.go +++ b/templater/templater.go @@ -30,58 +30,79 @@ type TemplatingError struct { meep.TraitCausable } -func LoadAndPrepareTemplates(include *[]string, exclude *[]string, c *context.Context) (output []string, err error) { +type RenderedResource struct { + Filename string + Rendered string +} + +type RenderedResourceSet struct { + Name string + Resources []RenderedResource +} + +func LoadAndApplyTemplates(include *[]string, exclude *[]string, c *context.Context) ([]RenderedResourceSet, error) { limitedResourceSets := applyLimits(&c.ResourceSets, include, exclude) + renderedResourceSets := make([]RenderedResourceSet, len(c.ResourceSets)) if len(*limitedResourceSets) == 0 { - fmt.Fprintln(os.Stderr, "No valid resource sets included!") - return + return renderedResourceSets, fmt.Errorf("No valid resource sets included!") } for _, rs := range *limitedResourceSets { - err = processResourceSet(c, &rs, &output) + set, err := processResourceSet(c, &rs) if err != nil { - return + return nil, err } + + renderedResourceSets = append(renderedResourceSets, *set) } - return + return renderedResourceSets, nil } -func processResourceSet(c *context.Context, rs *context.ResourceSet, output *[]string) error { +func processResourceSet(c *context.Context, rs *context.ResourceSet) (*RenderedResourceSet, error) { fmt.Fprintf(os.Stderr, "Loading resources for %s\n", rs.Name) rp := path.Join(c.BaseDir, rs.Name) files, err := ioutil.ReadDir(rp) - err = processFiles(c, rs, rp, files, output) + resources, err := processFiles(c, rs, rp, files) if err != nil { - return meep.New( + return nil, meep.New( &TemplateNotFoundError{Name: rs.Name}, meep.Cause(err), ) } - return nil + return &RenderedResourceSet{ + Name: rs.Name, + Resources: resources, + }, nil } -func processFiles(c *context.Context, rs *context.ResourceSet, rp string, files []os.FileInfo, output *[]string) error { +func processFiles(c *context.Context, rs *context.ResourceSet, rp string, files []os.FileInfo) ([]RenderedResource, error) { + resources := make([]RenderedResource, len(c.ResourceSets)) + for _, file := range files { if !file.IsDir() && isResourceFile(file) { p := path.Join(rp, file.Name()) o, err := templateFile(c, rs, p) if err != nil { - return err + return resources, err } - *output = append(*output, o) + res := RenderedResource{ + Filename: file.Name(), + Rendered: o, + } + resources = append(resources, res) } } - return nil + return resources, nil } func templateFile(c *context.Context, rs *context.ResourceSet, filename string) (string, error) { From 162b962fad4e9d4f99b865aa4d22e7c3581756e8 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sun, 11 Jun 2017 21:34:22 +0200 Subject: [PATCH 064/166] refactor main: Call kubectl individually per resource set Instead of passing the rendered output of all resource sets to kubectl simultaneously, build upon the previous commit and pass resource sets individually to new instances of kubectl. This resolves #51 --- main.go | 47 +++++++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/main.go b/main.go index 033f2b5ca..57dd62068 100644 --- a/main.go +++ b/main.go @@ -130,13 +130,13 @@ func createCommand() { } } -func loadContextAndResources(file *string) (*context.Context, *[]string) { +func loadContextAndResources(file *string) (*context.Context, *[]templater.RenderedResourceSet) { ctx, err := context.LoadContextFromFile(*file) if err != nil { app.Fatalf("Error loading context: %v\n", err) } - resources, err := templater.LoadAndPrepareTemplates(includes, excludes, ctx) + resources, err := templater.LoadAndApplyTemplates(includes, excludes, ctx) if err != nil { app.Fatalf("Error templating resource sets: %v\n", err) } @@ -144,29 +144,36 @@ func loadContextAndResources(file *string) (*context.Context, *[]string) { return ctx, &resources } -func runKubectlWithResources(c *context.Context, kubectlArgs *[]string, resources *[]string) error { +func runKubectlWithResources(c *context.Context, kubectlArgs *[]string, resourceSets *[]templater.RenderedResourceSet) error { args := append(*kubectlArgs, fmt.Sprintf("--context=%s", c.Name)) - kubectl := exec.Command("kubectl", args...) + for _, resourceSet := range *resourceSets { + kubectl := exec.Command("kubectl", args...) - stdin, err := kubectl.StdinPipe() - if err != nil { - return meep.New(&KubeCtlError{}, meep.Cause(err)) + stdin, err := kubectl.StdinPipe() + if err != nil { + return meep.New(&KubeCtlError{}, meep.Cause(err)) + } + + kubectl.Stdout = os.Stdout + kubectl.Stderr = os.Stderr + + if err = kubectl.Start(); err != nil { + return meep.New(&KubeCtlError{}, meep.Cause(err)) + } + + for _, r := range resourceSet.Resources { + fmt.Printf("Passing file %s/%s to kubectl", resourceSet.Name, r.Filename) + fmt.Fprintln(stdin, r.Rendered) + } + stdin.Close() + + if err = kubectl.Wait(); err != nil { + return err + } } - kubectl.Stdout = os.Stdout - kubectl.Stderr = os.Stderr - - if err = kubectl.Start(); err != nil { - return meep.New(&KubeCtlError{}, meep.Cause(err)) - } - - for _, r := range *resources { - fmt.Fprintln(stdin, r) - } - stdin.Close() - - return kubectl.Wait() + return nil } func failWithKubectlError(err error) { From 3cba344fbec32d4570c6b1a8758426009dfb8173 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sun, 11 Jun 2017 21:43:02 +0200 Subject: [PATCH 065/166] fix main: Fix 'kontemplate template' output --- main.go | 11 +++++++---- templater/templater.go | 4 ++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/main.go b/main.go index 57dd62068..5ece1183d 100644 --- a/main.go +++ b/main.go @@ -80,10 +80,13 @@ func versionCommand() { } func templateCommand() { - _, resources := loadContextAndResources(templateFile) + _, resourceSets := loadContextAndResources(templateFile) - for _, r := range *resources { - fmt.Println(r) + for _, rs := range *resourceSets { + for _, r := range rs.Resources { + fmt.Fprintf(os.Stderr, "Rendered file %s/%s:\n", rs.Name, r.Filename) + fmt.Println(r.Rendered) + } } } @@ -163,7 +166,7 @@ func runKubectlWithResources(c *context.Context, kubectlArgs *[]string, resource } for _, r := range resourceSet.Resources { - fmt.Printf("Passing file %s/%s to kubectl", resourceSet.Name, r.Filename) + fmt.Printf("Passing file %s/%s to kubectl\n", resourceSet.Name, r.Filename) fmt.Fprintln(stdin, r.Rendered) } stdin.Close() diff --git a/templater/templater.go b/templater/templater.go index afb96423e..9054ebac4 100644 --- a/templater/templater.go +++ b/templater/templater.go @@ -42,7 +42,7 @@ type RenderedResourceSet struct { func LoadAndApplyTemplates(include *[]string, exclude *[]string, c *context.Context) ([]RenderedResourceSet, error) { limitedResourceSets := applyLimits(&c.ResourceSets, include, exclude) - renderedResourceSets := make([]RenderedResourceSet, len(c.ResourceSets)) + renderedResourceSets := make([]RenderedResourceSet, 0) if len(*limitedResourceSets) == 0 { return renderedResourceSets, fmt.Errorf("No valid resource sets included!") @@ -83,7 +83,7 @@ func processResourceSet(c *context.Context, rs *context.ResourceSet) (*RenderedR } func processFiles(c *context.Context, rs *context.ResourceSet, rp string, files []os.FileInfo) ([]RenderedResource, error) { - resources := make([]RenderedResource, len(c.ResourceSets)) + resources := make([]RenderedResource, 0) for _, file := range files { if !file.IsDir() && isResourceFile(file) { From a7781b169df929028f2d4e4fe2f6b297aede60e5 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sun, 11 Jun 2017 22:27:20 +0200 Subject: [PATCH 066/166] fix templater: Ignore slash-suffixes on includes/excludes To prevent situations where a shell auto-appends a slash to an include/exclude specification on the CLI, trailing slashes in those string lists are now trimmed. This fixes #54 --- templater/templater.go | 1 + 1 file changed, 1 insertion(+) diff --git a/templater/templater.go b/templater/templater.go index 9054ebac4..4d9a04eb2 100644 --- a/templater/templater.go +++ b/templater/templater.go @@ -163,6 +163,7 @@ func applyLimits(rs *[]context.ResourceSet, include *[]string, exclude *[]string // Check whether an include/exclude string slice matches a resource set func matchesResourceSet(s *[]string, rs *context.ResourceSet) bool { for _, r := range *s { + r = strings.TrimSuffix(r, "/") if r == rs.Name || r == rs.Parent { return true } From cf5e392baffb6db275c02a757e5ec92afff741c7 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sun, 11 Jun 2017 23:11:37 +0200 Subject: [PATCH 067/166] feat: Add shell script to check if dependencies are up-to-date --- diff-deps.fish | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100755 diff-deps.fish diff --git a/diff-deps.fish b/diff-deps.fish new file mode 100755 index 000000000..4a5bf7585 --- /dev/null +++ b/diff-deps.fish @@ -0,0 +1,29 @@ +#!/usr/bin/env fish + +function get_remote_master + git ls-remote "$argv[1]" | \ + grep 'refs/heads/master' | \ + awk '{print $1}' +end + +function list_deps + grep '"git"' -B2 kontemplate.frm | \ + grep -P -o '(?<=silo: ")https://.+(?=")' +end + +function diff_dep + set -l current (grep -B1 "$argv[1]" kontemplate.frm | grep -P -o '(?<=hash: ").+(?=")') + set -l remote (get_remote_master "$argv[1]") + + if [ $current != $remote ] + echo "$argv[1]" + echo -e "current:\t$current" + echo -e "remote:\t\t$remote\n" + else + echo -e "$argv[1] up to date\n" + end +end + +for dep in (list_deps) + diff_dep $dep +end From 3a2f00f29ff5f2c19ea2e16c99e03e82bd28602d Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sun, 11 Jun 2017 23:12:08 +0200 Subject: [PATCH 068/166] chore: Update dependencies --- kontemplate.frm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kontemplate.frm b/kontemplate.frm index e8e1ff390..55bcc75bd 100644 --- a/kontemplate.frm +++ b/kontemplate.frm @@ -10,11 +10,11 @@ inputs: # Kontemplate dependencies! "/go/src/github.com/polydawn/meep": type: "git" - hash: "1487840a4bf30270decdc04123c41cdc7a8067c9" + hash: "296620394aa9cbcce7cfe9ad903489b8057662bc" silo: "https://github.com/polydawn/meep" "/go/src/github.com/Masterminds/sprig": type: "git" - hash: "f5b0ed4a680a0943228155eaf6a77a96ead1bc77" + hash: "e039e20e500c2c025d9145be375e27cf42a94174" silo: "https://github.com/Masterminds/sprig" "/go/src/github.com/ghodss/yaml": type: "git" @@ -42,7 +42,7 @@ inputs: silo: "https://github.com/Masterminds/semver" "/go/src/github.com/aokoli/goutils": type: "git" - hash: "e57d01ace047c1a43e6a49ecf3ecc50ed2be81d1" + hash: "3391d3790d23d03408670993e957e8f408993c34" silo: "https://github.com/aokoli/goutils" "/go/src/github.com/huandu/xstrings": type: "git" From e2be6152f9e19a404a8597dd4857ea6061aec7e9 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sun, 11 Jun 2017 23:25:29 +0200 Subject: [PATCH 069/166] chore: Release version 1.1.0 This release features some cleanup and under-the-hood changes, as well as "ecosystem-features" that don't directly affect the way Kontemplate itself functions. * Resource-sets are now passed on to kubectl in individual invocations. This means that kubectl errors can be scoped to individual resource set files and issues such as #51 are less of a problem. * A Dockerfile is provided and published at `tazjin:kontemplate` on Docker Hub. This image contains `kontemplate`, `kubectl` and `pass` and can be used - for example - as an image for a step in a CI system. * Kontemplate is now available on Homebrew, check the README for installation instructions. * If different resource sets don't contain `---` separators in YAML, `kubectl` calls will no longer fail. (#51) * Autocompleted trailing slashes in shells are now filtered from include & exclude lists to enhance the CLI experience slightly. --- build-release.sh | 2 +- kontemplate.frm | 2 +- main.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build-release.sh b/build-release.sh index 7f82db223..5e5b2cbec 100755 --- a/build-release.sh +++ b/build-release.sh @@ -3,7 +3,7 @@ set -ueo pipefail readonly GIT_HASH="$(git rev-parse --short HEAD)" readonly LDFLAGS="-X main.gitHash=${GIT_HASH} -w -s" -readonly VERSION="1.0.2-${GIT_HASH}" +readonly VERSION="1.1.0-${GIT_HASH}" function build-for() { local os="${1}" diff --git a/kontemplate.frm b/kontemplate.frm index 55bcc75bd..26ca18140 100644 --- a/kontemplate.frm +++ b/kontemplate.frm @@ -73,7 +73,7 @@ action: echo 'nameserver 8.8.8.8' > /etc/resolv.conf apt-get update && apt-get install -y git ca-certificates mkdir -p /go/src/github.com/tazjin - git clone --single-branch --branch v1.0.2 https://github.com/tazjin/kontemplate /go/src/github.com/tazjin/kontemplate + git clone --single-branch --branch v1.1.0 https://github.com/tazjin/kontemplate /go/src/github.com/tazjin/kontemplate cd /go/src/github.com/tazjin/kontemplate ./build-release.sh build outputs: diff --git a/main.go b/main.go index 5ece1183d..099125767 100644 --- a/main.go +++ b/main.go @@ -11,7 +11,7 @@ import ( "gopkg.in/alecthomas/kingpin.v2" ) -const version string = "1.0.2" +const version string = "1.1.0" // This variable will be initialised by the Go linker during the builder var gitHash string From 5144842e97f67b21cb106dc45383f9691a6b69c5 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sun, 11 Jun 2017 23:44:27 +0200 Subject: [PATCH 070/166] chore: Update Brew formula & Dockerfile to 1.1.0 --- image/Dockerfile | 2 +- image/hashes | 2 +- kontemplate.rb | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/image/Dockerfile b/image/Dockerfile index 90a3385dd..feabeba30 100644 --- a/image/Dockerfile +++ b/image/Dockerfile @@ -2,7 +2,7 @@ FROM alpine:3.6 ADD hashes /root/hashes ADD https://storage.googleapis.com/kubernetes-release/release/v1.6.4/bin/linux/amd64/kubectl /usr/bin/kubectl -ADD https://github.com/tazjin/kontemplate/releases/download/v1.0.2/kontemplate-1.0.2-f79b261-linux-amd64.tar.gz /tmp/kontemplate.tar.gz +ADD https://github.com/tazjin/kontemplate/releases/download/v1.1.0/kontemplate-1.1.0-f7ce04e-linux-amd64.tar.gz /tmp/kontemplate.tar.gz # Pass release version is 1.7.1 ADD https://raw.githubusercontent.com/zx2c4/password-store/38ec1c72e29c872ec0cdde82f75490640d4019bf/src/password-store.sh /usr/bin/pass diff --git a/image/hashes b/image/hashes index cf08300ac..69febe853 100644 --- a/image/hashes +++ b/image/hashes @@ -1,2 +1,2 @@ -156e5592ed3ae6c9aff12fbbed17141ed7b3ede26ed169001df5b1211435f033 /tmp/kontemplate.tar.gz +f2bdd1d458b2fad935f1f64e0a40d59b28be6b0debbc20241e1d8ad4a77a7cc2 /tmp/kontemplate.tar.gz a91c6b028bb3f737898ff003f7f3a3f2d242ea52e89ade1f6ca3ab99170119e5 /usr/bin/kubectl diff --git a/kontemplate.rb b/kontemplate.rb index 0f0ec6424..5277f291b 100644 --- a/kontemplate.rb +++ b/kontemplate.rb @@ -3,9 +3,9 @@ class Kontemplate < Formula desc "Kontemplate - Extremely simple Kubernetes resource templates" homepage "https://github.com/tazjin/kontemplate" - url "https://github.com/tazjin/kontemplate/releases/download/v1.0.2/kontemplate-1.0.2-f79b261-darwin-amd64.tar.gz" - sha256 "5a2db5467bc77e4379b5b98f35c9864010f7023ae01a25fb5cda1aede59e021c" - version "1.0.2-f79b261" + url "https://github.com/tazjin/kontemplate/releases/download/v1.1.0/kontemplate-1.1.0-f7ce04e-darwin-amd64.tar.gz" + sha256 "1dccc80804589f6bd233dd79f52527f2ba8c4fa8d38857c80b2f47b504fe6c04" + version "1.1.0-f7ce04e" def install bin.install "kontemplate" From e2f7cf6258f469ae9b69d0b5cce57b397c08c36d Mon Sep 17 00:00:00 2001 From: "Dr. J. Kubernaught" Date: Thu, 22 Jun 2017 15:05:34 +0200 Subject: [PATCH 071/166] docs: Add official code of conduct As dictated by the goddess to the eternal polyfather of love. Signed-off-by: Reverend Dr. J. Kubernaughtt The Most Recent --- CODE_OF_CONDUCT.md | 20 ++++++++++++++++++++ README.md | 6 ++++++ 2 files changed, 26 insertions(+) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..c4013ac13 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,20 @@ +A SERMON ON ETHICS AND LOVE +=========================== + +One day Mal-2 asked the messenger spirit Saint Gulik to approach the Goddess and request Her presence for some desperate advice. Shortly afterwards the radio came on by itself, and an ethereal female Voice said **YES?** + +"O! Eris! Blessed Mother of Man! Queen of Chaos! Daughter of Discord! Concubine of Confusion! O! Exquisite Lady, I beseech You to lift a heavy burden from my heart!" + +**WHAT BOTHERS YOU, MAL? YOU DON'T SOUND WELL.** + +"I am filled with fear and tormented with terrible visions of pain. Everywhere people are hurting one another, the planet is rampant with injustices, whole societies plunder groups of their own people, mothers imprison sons, children perish while brothers war. O, woe." + +**WHAT IS THE MATTER WITH THAT, IF IT IS WHAT YOU WANT TO DO?** + +"But nobody Wants it! Everybody hates it." + +**OH. WELL, THEN *STOP*.** + +At which moment She turned herself into an aspirin commercial and left The Polyfather stranded alone with his species. + +SINISTER DEXTER HAS A BROKEN SPIROMETER. diff --git a/README.md b/README.md index 7b4d9ff6a..2095cc8fe 100644 --- a/README.md +++ b/README.md @@ -147,6 +147,12 @@ kontemplate apply example/prod-cluster.yaml --dry-run kontemplate apply example/prod-cluster.yaml ``` +## Contributing + +Feel free to contribute pull requests, file bugs and open issues with feature suggestions! + +Please follow the [code of conduct](CODE_OF_CONDUCT.md). + [Helm]: https://helm.sh/ [releases page]: https://github.com/tazjin/kontemplate/releases [AUR package]: https://aur.archlinux.org/packages/kontemplate-git/ From 3728d0ae2eb957c8ba4251ee0f562c4941163816 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Thu, 22 Jun 2017 15:52:43 +0200 Subject: [PATCH 072/166] refactor context: Extract loadJsonOrYaml to util package The logic to deserialise a structure from *either* JSON or YAML is reused several times and can be easily extracted, which this commit does. --- context/context.go | 32 +++----------------------------- util/util.go | 27 +++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/context/context.go b/context/context.go index ad3d00693..0dde59055 100644 --- a/context/context.go +++ b/context/context.go @@ -1,13 +1,8 @@ package context import ( - "encoding/json" - "fmt" - "io/ioutil" "path" - "strings" - "github.com/ghodss/yaml" "github.com/polydawn/meep" "github.com/tazjin/kontemplate/util" ) @@ -35,27 +30,8 @@ type ContextLoadingError struct { // Attempt to load and deserialise a Context from the specified file. func LoadContextFromFile(filename string) (*Context, error) { - file, err := ioutil.ReadFile(filename) - - if err != nil { - return nil, meep.New( - &ContextLoadingError{Filename: filename}, - meep.Cause(err), - ) - } - var c Context - - if strings.HasSuffix(filename, "json") { - err = json.Unmarshal(file, &c) - } else if strings.HasSuffix(filename, "yaml") || strings.HasSuffix(filename, "yml") { - err = yaml.Unmarshal(file, &c) - } else { - return nil, meep.New( - &ContextLoadingError{Filename: filename}, - meep.Cause(fmt.Errorf("File format not supported. Must be JSON or YAML.")), - ) - } + err := util.LoadJsonOrYaml(filename, &c) if err != nil { return nil, meep.New( @@ -112,16 +88,14 @@ func loadDefaultValues(rs *ResourceSet, c *Context) *map[string]interface{} { var defaultVars map[string]interface{} // Attempt to load YAML values - y, err := ioutil.ReadFile(path.Join(c.BaseDir, rs.Name, "default.yaml")) + err := util.LoadJsonOrYaml(path.Join(c.BaseDir, rs.Name, "default.yaml"), &defaultVars) if err == nil { - yaml.Unmarshal(y, &defaultVars) return util.Merge(&defaultVars, &rs.Values) } // Attempt to load JSON values - j, err := ioutil.ReadFile(path.Join(c.BaseDir, rs.Name, "default.json")) + err = util.LoadJsonOrYaml(path.Join(c.BaseDir, rs.Name, "default.json"), &defaultVars) if err == nil { - json.Unmarshal(j, &defaultVars) return util.Merge(&defaultVars, &rs.Values) } diff --git a/util/util.go b/util/util.go index ea9f636ac..0c5815a38 100644 --- a/util/util.go +++ b/util/util.go @@ -1,5 +1,14 @@ package util +import ( + "encoding/json" + "fmt" + "io/ioutil" + "strings" + + "github.com/ghodss/yaml" +) + // Merges two maps together. Values from the second map override values in the first map. // The returned map is new if anything was changed. func Merge(in1 *map[string]interface{}, in2 *map[string]interface{}) *map[string]interface{} { @@ -22,3 +31,21 @@ func Merge(in1 *map[string]interface{}, in2 *map[string]interface{}) *map[string return &new } + +// Loads either a YAML or JSON file from the specified path and deserialises it into the provided interface. +func LoadJsonOrYaml(filename string, addr interface{}) error { + file, err := ioutil.ReadFile(filename) + if err != nil { + return err + } + + if strings.HasSuffix(filename, "json") { + err = json.Unmarshal(file, addr) + } else if strings.HasSuffix(filename, "yaml") || strings.HasSuffix(filename, "yml") { + err = yaml.Unmarshal(file, addr) + } else { + return fmt.Errorf("File format not supported. Must be JSON or YAML.") + } + + return nil +} From 5bc6370af2073fec6a5a59cf60749e94a38c57dc Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Thu, 22 Jun 2017 15:54:14 +0200 Subject: [PATCH 073/166] fix main: Correctly print kubectl errors --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index 099125767..0d809071a 100644 --- a/main.go +++ b/main.go @@ -180,6 +180,6 @@ func runKubectlWithResources(c *context.Context, kubectlArgs *[]string, resource } func failWithKubectlError(err error) { - fmt.Errorf("Kubectl error: %v\n", err) + fmt.Fprintf(os.Stderr, "Kubectl error: %v\n", err) os.Exit(1) } From 8c7a3d6c30292a003c62792ce0ac03abff264a85 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Thu, 22 Jun 2017 15:55:38 +0200 Subject: [PATCH 074/166] feat build: Run go vet before building --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 4f2ee4d97..db8944c2c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1 +1,4 @@ +--- language: go +before_script: + - go vet ./... From 68e1e484594aa86a874c6eb2ad328274c5f48dd7 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Thu, 22 Jun 2017 16:00:53 +0200 Subject: [PATCH 075/166] feat build: Test if 'go fmt' has been applied --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index db8944c2c..382ca5061 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ --- language: go before_script: + - if [[ -n "$(go fmt ./...)" ]]; then echo 'Run go fmt!' && exit 1; fi - go vet ./... From 9d26c17f13240479ef3e12e9182aca3ac2e61901 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Thu, 22 Jun 2017 17:01:36 +0200 Subject: [PATCH 076/166] feat context: Add ability to import extra variables from files Kontemplate context specifications can now load extra variables from YAML or JSON files by specifying a list of files (relative to the context file) under the `import` key. --- context/context.go | 34 +++++++++++++++-- context/context_test.go | 44 ++++++++++++++++++++++ context/testdata/import-vars-override.yaml | 9 +++++ context/testdata/import-vars-simple.yaml | 5 +++ context/testdata/test-vars-override.yaml | 3 ++ context/testdata/test-vars.yaml | 5 +++ 6 files changed, 96 insertions(+), 4 deletions(-) create mode 100644 context/testdata/import-vars-override.yaml create mode 100644 context/testdata/import-vars-simple.yaml create mode 100644 context/testdata/test-vars-override.yaml create mode 100644 context/testdata/test-vars.yaml diff --git a/context/context.go b/context/context.go index 0dde59055..c812af8b2 100644 --- a/context/context.go +++ b/context/context.go @@ -17,10 +17,11 @@ type ResourceSet struct { } type Context struct { - Name string `json:"context"` - Global map[string]interface{} `json:"global"` - ResourceSets []ResourceSet `json:"include"` - BaseDir string + Name string `json:"context"` + Global map[string]interface{} `json:"global"` + ResourceSets []ResourceSet `json:"include"` + VariableImports []string `json:"import"` + BaseDir string } type ContextLoadingError struct { @@ -44,9 +45,34 @@ func LoadContextFromFile(filename string) (*Context, error) { c.BaseDir = path.Dir(filename) c.ResourceSets = loadAllDefaultValues(&c) + err = c.loadImportedVariables() + if err != nil { + return nil, meep.New( + &ContextLoadingError{Filename: filename}, + meep.Cause(err), + ) + } + return &c, nil } +// Kontemplate supports specifying additional variable files with the `import` keyword. This function loads those +// variable files and merges them together with the context's other global variables. +func (ctx *Context) loadImportedVariables() error { + for _, file := range ctx.VariableImports { + var importedVars map[string]interface{} + err := util.LoadJsonOrYaml(path.Join(ctx.BaseDir, file), &importedVars) + + if err != nil { + return err + } + + ctx.Global = *util.Merge(&ctx.Global, &importedVars) + } + + return nil +} + // Flattens resource set collections, i.e. resource sets that themselves have an additional 'include' field set. // Those will be regarded as a short-hand for including multiple resource sets from a subfolder. // See https://github.com/tazjin/kontemplate/issues/9 for more information. diff --git a/context/context_test.go b/context/context_test.go index c5b5e7f12..b6acf416e 100644 --- a/context/context_test.go +++ b/context/context_test.go @@ -159,3 +159,47 @@ func TestDefaultValuesLoading(t *testing.T) { t.Fail() } } + +func TestImportValuesLoading(t *testing.T) { + ctx, err := LoadContextFromFile("testdata/import-vars-simple.yaml") + if err != nil { + t.Error(err) + t.Fail() + } + + expected := map[string]interface{}{ + "override": "true", + "music": map[string]interface{}{ + "artist": "Pallida", + "track": "Tractor Beam", + }, + } + + if !reflect.DeepEqual(ctx.Global, expected) { + t.Error("Expected global values after loading imports did not match!") + t.Fail() + } +} + +func TestImportValuesOverride(t *testing.T) { + ctx, err := LoadContextFromFile("testdata/import-vars-override.yaml") + if err != nil { + t.Error(err) + t.Fail() + } + + expected := map[string]interface{}{ + "override": float64(3), + "music": map[string]interface{}{ + "artist": "Pallida", + "track": "Tractor Beam", + }, + "place": "Oslo", + "globalVar": "very global!", + } + + if !reflect.DeepEqual(ctx.Global, expected) { + t.Error("Expected global values after loading imports did not match!") + t.Fail() + } +} diff --git a/context/testdata/import-vars-override.yaml b/context/testdata/import-vars-override.yaml new file mode 100644 index 000000000..c3d47bcfc --- /dev/null +++ b/context/testdata/import-vars-override.yaml @@ -0,0 +1,9 @@ +--- +context: k8s.prod.mydomain.com +global: + globalVar: very global! + override: 1 +import: + - test-vars.yaml + - test-vars-override.yaml +include: [] diff --git a/context/testdata/import-vars-simple.yaml b/context/testdata/import-vars-simple.yaml new file mode 100644 index 000000000..12244e1ab --- /dev/null +++ b/context/testdata/import-vars-simple.yaml @@ -0,0 +1,5 @@ +--- +context: k8s.prod.mydomain.com +import: + - test-vars.yaml +include: [] diff --git a/context/testdata/test-vars-override.yaml b/context/testdata/test-vars-override.yaml new file mode 100644 index 000000000..5215c559c --- /dev/null +++ b/context/testdata/test-vars-override.yaml @@ -0,0 +1,3 @@ +--- +override: 3 +place: Oslo diff --git a/context/testdata/test-vars.yaml b/context/testdata/test-vars.yaml new file mode 100644 index 000000000..af27bdc45 --- /dev/null +++ b/context/testdata/test-vars.yaml @@ -0,0 +1,5 @@ +--- +override: 'true' +music: + artist: Pallida + track: Tractor Beam From 7607f6dc0fff076cc69ca2d6f50eb04b728e3a44 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Thu, 13 Jul 2017 15:57:18 +0200 Subject: [PATCH 077/166] feat context: Allow overriding resource set paths Instead of always inferring the path at which files in a resource set are located, let users override the path by specifying a `path` field. This makes it possible to add the same resource set multiple times with different values while still keeping distinct names for addressability (for example when using include/exclude). This fixes #70 --- context/context.go | 23 +++++-- context/context_test.go | 68 +++++++++++++++++++ context/testdata/explicit-path.yaml | 11 +++ .../testdata/explicit-subresource-path.yaml | 8 +++ 4 files changed, 105 insertions(+), 5 deletions(-) create mode 100644 context/testdata/explicit-path.yaml create mode 100644 context/testdata/explicit-subresource-path.yaml diff --git a/context/context.go b/context/context.go index c812af8b2..66a45390e 100644 --- a/context/context.go +++ b/context/context.go @@ -9,6 +9,7 @@ import ( type ResourceSet struct { Name string `json:"name"` + Path string `json:"path"` Values map[string]interface{} `json:"values"` // Fields for resource set collections @@ -41,7 +42,7 @@ func LoadContextFromFile(filename string) (*Context, error) { ) } - c.ResourceSets = flattenResourceSetCollections(&c.ResourceSets) + c.ResourceSets = flattenPrepareResourceSetPaths(&c.ResourceSets) c.BaseDir = path.Dir(filename) c.ResourceSets = loadAllDefaultValues(&c) @@ -73,19 +74,31 @@ func (ctx *Context) loadImportedVariables() error { return nil } -// Flattens resource set collections, i.e. resource sets that themselves have an additional 'include' field set. +// Correctly prepares the file paths for resource sets by inferring implicit paths and flattening resource set +// collections, i.e. resource sets that themselves have an additional 'include' field set. // Those will be regarded as a short-hand for including multiple resource sets from a subfolder. // See https://github.com/tazjin/kontemplate/issues/9 for more information. -func flattenResourceSetCollections(rs *[]ResourceSet) []ResourceSet { +func flattenPrepareResourceSetPaths(rs *[]ResourceSet) []ResourceSet { flattened := make([]ResourceSet, 0) for _, r := range *rs { + // If a path is not explicitly specified it should default to the resource set name. + // This is also the classic behaviour prior to kontemplate 1.2 + if r.Path == "" { + r.Path = r.Name + } + if len(r.Include) == 0 { flattened = append(flattened, r) } else { for _, subResourceSet := range r.Include { + if subResourceSet.Path == "" { + subResourceSet.Path = subResourceSet.Name + } + subResourceSet.Parent = r.Name subResourceSet.Name = path.Join(r.Name, subResourceSet.Name) + subResourceSet.Path = path.Join(r.Path, subResourceSet.Path) subResourceSet.Values = *util.Merge(&r.Values, &subResourceSet.Values) flattened = append(flattened, subResourceSet) } @@ -114,13 +127,13 @@ func loadDefaultValues(rs *ResourceSet, c *Context) *map[string]interface{} { var defaultVars map[string]interface{} // Attempt to load YAML values - err := util.LoadJsonOrYaml(path.Join(c.BaseDir, rs.Name, "default.yaml"), &defaultVars) + err := util.LoadJsonOrYaml(path.Join(c.BaseDir, rs.Path, "default.yaml"), &defaultVars) if err == nil { return util.Merge(&defaultVars, &rs.Values) } // Attempt to load JSON values - err = util.LoadJsonOrYaml(path.Join(c.BaseDir, rs.Name, "default.json"), &defaultVars) + err = util.LoadJsonOrYaml(path.Join(c.BaseDir, rs.Path, "default.json"), &defaultVars) if err == nil { return util.Merge(&defaultVars, &rs.Values) } diff --git a/context/context_test.go b/context/context_test.go index b6acf416e..350b2b66a 100644 --- a/context/context_test.go +++ b/context/context_test.go @@ -21,6 +21,7 @@ func TestLoadFlatContextFromFile(t *testing.T) { ResourceSets: []ResourceSet{ { Name: "some-api", + Path: "some-api", Values: map[string]interface{}{ "apiPort": float64(4567), // yep! "importantFeature": true, @@ -55,6 +56,7 @@ func TestLoadContextWithResourceSetCollections(t *testing.T) { ResourceSets: []ResourceSet{ { Name: "some-api", + Path: "some-api", Values: map[string]interface{}{ "apiPort": float64(4567), // yep! "importantFeature": true, @@ -65,6 +67,7 @@ func TestLoadContextWithResourceSetCollections(t *testing.T) { }, { Name: "collection/nested", + Path: "collection/nested", Values: map[string]interface{}{ "lizards": "good", }, @@ -95,6 +98,7 @@ func TestSubresourceVariableInheritance(t *testing.T) { ResourceSets: []ResourceSet{ { Name: "parent/child", + Path: "parent/child", Values: map[string]interface{}{ "foo": "bar", "bar": "baz", @@ -125,6 +129,7 @@ func TestSubresourceVariableInheritanceOverride(t *testing.T) { ResourceSets: []ResourceSet{ { Name: "parent/child", + Path: "parent/child", Values: map[string]interface{}{ "foo": "newvalue", }, @@ -203,3 +208,66 @@ func TestImportValuesOverride(t *testing.T) { t.Fail() } } + +func TestExplicitPathLoading(t *testing.T) { + ctx, err := LoadContextFromFile("testdata/explicit-path.yaml") + if err != nil { + t.Error(err) + t.Fail() + } + + expected := Context{ + Name: "k8s.prod.mydomain.com", + ResourceSets: []ResourceSet{ + { + Name: "some-api-europe", + Path: "some-api", + Values: map[string]interface{}{ + "location": "europe", + }, + Include: nil, + Parent: "", + }, + { + Name: "some-api-asia", + Path: "some-api", + Values: map[string]interface{}{ + "location": "asia", + }, + Include: nil, + Parent: "", + }, + }, + BaseDir: "testdata", + } + + if !reflect.DeepEqual(*ctx, expected) { + t.Error("Loaded context and expected context did not match") + t.Fail() + } +} + +func TestExplicitSubresourcePathLoading(t *testing.T) { + ctx, err := LoadContextFromFile("testdata/explicit-subresource-path.yaml") + if err != nil { + t.Error(err) + t.Fail() + } + + expected := Context{ + Name: "k8s.prod.mydomain.com", + ResourceSets: []ResourceSet{ + { + Name: "parent/child", + Path: "parent-path/child-path", + Parent: "parent", + }, + }, + BaseDir: "testdata", + } + + if !reflect.DeepEqual(*ctx, expected) { + t.Error("Loaded context and expected context did not match") + t.Fail() + } +} diff --git a/context/testdata/explicit-path.yaml b/context/testdata/explicit-path.yaml new file mode 100644 index 000000000..2c81f83c0 --- /dev/null +++ b/context/testdata/explicit-path.yaml @@ -0,0 +1,11 @@ +--- +context: k8s.prod.mydomain.com +include: + - name: some-api-europe + path: some-api + values: + location: europe + - name: some-api-asia + path: some-api + values: + location: asia diff --git a/context/testdata/explicit-subresource-path.yaml b/context/testdata/explicit-subresource-path.yaml new file mode 100644 index 000000000..6cf861832 --- /dev/null +++ b/context/testdata/explicit-subresource-path.yaml @@ -0,0 +1,8 @@ +--- +context: k8s.prod.mydomain.com +include: + - name: parent + path: parent-path + include: + - name: child + path: child-path From 5e7bb55e0018fafea01f6e99ff13c52ec33b24af Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Thu, 13 Jul 2017 16:00:21 +0200 Subject: [PATCH 078/166] refactor templater: Use resource set 'path' field when loading files --- templater/templater.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/templater/templater.go b/templater/templater.go index 4d9a04eb2..4a0c8e7d8 100644 --- a/templater/templater.go +++ b/templater/templater.go @@ -22,6 +22,7 @@ const failOnMissingKeys string = "missingkey=error" type TemplateNotFoundError struct { meep.AllTraits Name string + Path string } // Error that is caused during templating, e.g. required value being absent or invalid template format @@ -64,14 +65,14 @@ func LoadAndApplyTemplates(include *[]string, exclude *[]string, c *context.Cont func processResourceSet(c *context.Context, rs *context.ResourceSet) (*RenderedResourceSet, error) { fmt.Fprintf(os.Stderr, "Loading resources for %s\n", rs.Name) - rp := path.Join(c.BaseDir, rs.Name) + rp := path.Join(c.BaseDir, rs.Path) files, err := ioutil.ReadDir(rp) resources, err := processFiles(c, rs, rp, files) if err != nil { return nil, meep.New( - &TemplateNotFoundError{Name: rs.Name}, + &TemplateNotFoundError{Name: rs.Name, Path: rs.Path}, meep.Cause(err), ) } From 658c6a9b0caedab65fea82e29e087aefdc8bf2f6 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sat, 29 Jul 2017 20:29:43 +0200 Subject: [PATCH 079/166] docs README: Update feature list Adds a feature list with links to individual feature description documents. --- README.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2095cc8fe..4a31cd69d 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,16 @@ cluster), but with the same deployment files. In my experience this is often enough and more complex solutions such as [Helm][] are not required. -## Overview +Check out a Kontemplate setup example and the feature list below! + +## Features + +* [Simple, yet powerful templates](docs/templates.md) +* [Clean cluster configuration files](docs/cluster-config.md) +* [Integration with pass](docs/pass.md) +* [Integration with kubectl](docs/kubectl.md) + +## Example Kontemplate lets you describe resources as you normally would in a simple folder structure: @@ -147,6 +156,9 @@ kontemplate apply example/prod-cluster.yaml --dry-run kontemplate apply example/prod-cluster.yaml ``` +Check out the feature list and the individual feature documentation above and read the +[best practices](docs/best-practices.md). Then you should be good to go! + ## Contributing Feel free to contribute pull requests, file bugs and open issues with feature suggestions! From 14d2859720a1532148212e83f447d63bb722b1bf Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sat, 29 Jul 2017 20:30:09 +0200 Subject: [PATCH 080/166] docs templates: Document some template logic Adds documentation for `if` and `range` statements in Go templates and also more explicitly points people at the Go documentation for more information. --- docs/templates.md | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/docs/templates.md b/docs/templates.md index bfd254630..8dff28dd1 100644 --- a/docs/templates.md +++ b/docs/templates.md @@ -5,6 +5,13 @@ The template file format is based on Go's [templating engine][] in combination with a small extension library called [sprig][] that adds additional template functions. +Go templates can either simply display variables or build more complicated +*pipelines* in which variables are passed to functions for further processing, +or in which conditionals are evaluated for more complex template logic. + +It is recommended that you check out the Golang [documentation][] for the templating +engine in addition to the cherry-picked features listed here. + ## Basic variable interpolation The basic template format uses `{{ .variableName }}` as the interpolation format. @@ -66,7 +73,7 @@ certKeyPath: my-website/cert-key # The following interpolations are possible: -{{ .name | upper }} +{{ .name | upper }} -> DONALD {{ .name | upper | repeat 2 }} @@ -76,6 +83,39 @@ certKeyPath: my-website/cert-key -> Returns content of 'my-website/cert-key' from pass ``` +## Conditionals & ranges + +Some logic is supported in Golang templates and can be used in Kontemplate, too. + +With the following values: + +``` +useKube2IAM: true +servicePorts: + - 8080 + - 9090 +``` + +The following interpolations are possible: + +``` +# Conditionally insert something in the template: +metadata: + annotations: + foo: bar + {{ if .useKube2IAM -}} iam.amazonaws.com/role: my-api {{- end }} +``` + +``` +# Iterate over a list of values +ports: + {{ range .servicePorts }} + - port: {{ . }} + {{ end }} +``` + +Check out the Golang documentation (linked above) for more information about template logic. + ## Caveats Kontemplate does not by itself parse any of the content of the templates, which @@ -86,6 +126,7 @@ You can perform some validation by using `kontemplate apply --dry-run` which will make use of the Dry-Run functionality in `kubectl`. [templating engine]: https://golang.org/pkg/text/template/ +[documentation]: https://golang.org/pkg/text/template/ [sprig]: http://masterminds.github.io/sprig/ [Go documentation]: https://golang.org/pkg/text/template/#hdr-Functions [pass]: https://www.passwordstore.org/ From 367c5d2b60bed86395932b82cf67875e27f8176f Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sun, 30 Jul 2017 00:48:58 +0200 Subject: [PATCH 081/166] docs context: Document ResourceSet & Context struct fields --- context/context.go | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/context/context.go b/context/context.go index 66a45390e..ff17eccc3 100644 --- a/context/context.go +++ b/context/context.go @@ -8,20 +8,37 @@ import ( ) type ResourceSet struct { + // Name of the resource set. This can be used in include/exclude statements during kontemplate runs. Name string `json:"name"` + + // Path to the folder containing the files for this resource set. This defaults to the value of the 'name' field + // if unset. Path string `json:"path"` + + // Values to include when interpolating resources from this resource set. Values map[string]interface{} `json:"values"` - // Fields for resource set collections + // Nested resource sets to include Include []ResourceSet `json:"include"` + + // Parent resource set for flattened resource sets. Should not be manually specified. Parent string } type Context struct { + // The name of the kubectl context Name string `json:"context"` + + // Global variables that should be accessible by all resource sets Global map[string]interface{} `json:"global"` - ResourceSets []ResourceSet `json:"include"` + + // File names of YAML or JSON files including extra variables that should be globally accessible VariableImports []string `json:"import"` + + // The resource sets to include in this context + ResourceSets []ResourceSet `json:"include"` + + // This field represents the absolute path to the context base directory and should not be manually specified. BaseDir string } From 55499689589b605ee736bc8334f4391cdfd2a27c Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sun, 30 Jul 2017 00:49:31 +0200 Subject: [PATCH 082/166] docs cluster-config: Document cluster configuration files Adds documentation for the YAML files describing cluster configuration. --- docs/cluster-config.md | 93 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 docs/cluster-config.md diff --git a/docs/cluster-config.md b/docs/cluster-config.md new file mode 100644 index 000000000..1bf4944e9 --- /dev/null +++ b/docs/cluster-config.md @@ -0,0 +1,93 @@ +Cluster configuration +========================== + +Every cluster (or "environment") that requires individual configuration is specified in +a very simple YAML file in Kontemplate. + +An example file for a hypothetical test environment could look like this: + +```yaml +--- +context: k8s.test.mydomain.com +global: + clusterName: test-cluster + defaultReplicas: 2 +import: + - test-secrets.yaml +include: + - name: gateway + path: tools/nginx + values: + tlsDomains: + - test.oslo.pub + - test.tazj.in + - path: backend + values: + env: test + include: + - name: blog + values: + url: test.tazj.in + - name: pub-service +``` + +## Fields + +This is documentation for the individual fields in a cluster context file. + +### `context` + +The `context` field contains the name of the kubectl-context. You can list context names with +'kubectl config get-contexts'. + +This must be set here so that Kontemplate can use the correct context when calling kubectl. + +This field is **required**. + +### `global` + +The `global` field contains a key/value map of variables that should be available to all resource +sets in the cluster. + +This field is **optional**. + +### `import` + +The `import` field contains the file names of additional YAML or JSON files from which global +variables should be loaded. Using this field makes it possible to keep certain configuration that +is the same for some, but not all, clusters in a common place. + +This field is **optional**. + +### `include` + +The `include` field contains the actual resource sets to be included in the cluster. + +Information about the structure of resource sets can be found in the [resource set documentation][]. + +This field is **required**. + +## External variables + +As mentioned above, extra variables can be loaded from additional YAML or JSON files. Assuming you +have a file called `test-secrets.yaml` which contains variables that should be shared between a `test` +and `dev` cluster, you could include it in your context as such: + +``` +# test-secrets.yaml: +mySecretVar: foo-bar-12345 + +# test-cluster.yaml: +context: k8s.test.mydomain.com +include: + - test-secrets.yaml + +# dev-cluster.yaml: +context: k8s.dev.mydomain.com +include: + - test-secrets.yaml +``` + +The variable `mySecretVar` is then available as a global variable. + +[resource set documentation]: resource-sets.md From 4adc8f2c4c9c3eabc086b329e4af9477bb0eee92 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sun, 30 Jul 2017 01:12:00 +0200 Subject: [PATCH 083/166] docs resource-sets: Document resource set structure Documents the structure of resource sets and the fields necessary for including them in cluster configurations. Also adds some words about nested resource sets and the like. --- README.md | 1 + docs/cluster-config.md | 2 +- docs/resource-sets.md | 146 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 docs/resource-sets.md diff --git a/README.md b/README.md index 4a31cd69d..d287f6001 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ Check out a Kontemplate setup example and the feature list below! * [Simple, yet powerful templates](docs/templates.md) * [Clean cluster configuration files](docs/cluster-config.md) +* [Resources organised as simple resource sets](docs/resource-sets.md) * [Integration with pass](docs/pass.md) * [Integration with kubectl](docs/kubectl.md) diff --git a/docs/cluster-config.md b/docs/cluster-config.md index 1bf4944e9..3bc59a630 100644 --- a/docs/cluster-config.md +++ b/docs/cluster-config.md @@ -73,7 +73,7 @@ As mentioned above, extra variables can be loaded from additional YAML or JSON f have a file called `test-secrets.yaml` which contains variables that should be shared between a `test` and `dev` cluster, you could include it in your context as such: -``` +```yaml # test-secrets.yaml: mySecretVar: foo-bar-12345 diff --git a/docs/resource-sets.md b/docs/resource-sets.md new file mode 100644 index 000000000..1d5eeabc5 --- /dev/null +++ b/docs/resource-sets.md @@ -0,0 +1,146 @@ +Resource Sets +================ + +Resource sets are collections of Kubernetes resources that should be passed to `kubectl` together. + +Technically a resource set is simply a folder with a few YAML and/or JSON templates in it. + + +# Creating resource sets + +Simply create a folder in your Kontemplate repository and place a YAML or JSON file in it. These +files get interpreted as [templates][] during Kontemplate runs and variables (as well as template +logic or functions) will be interpolated. + +Refer to the template documentation for information on how to write templates. + +## Default variables + +Sometimes it is useful to specify default values for variables that should be interpolated during +a run if the [cluster configuration][] does not specify a variable explicitly. + +This can be done simply by placing a `default.yaml` or `default.json` file in the resource set +folder and filling it with key/value pairs of the intended default variables. + +Kontemplate will error during interpolation if any variables are left unspecified. + +# Including resource sets + +Under the cluster configuration `include` key resource sets are included and required variables +are specified. For example: + +```yaml +include: + - name: some-api + values: + version: 1.2-SNAPSHOT +``` + +This will include a resource set from a folder called `some-api` and set the specified `version` variable. + +## Fields + +The available fields when including a resource set are these: + +### `name` + +The `name` field contains the name of the resource set. This name can be used to refer to the resource set +when specifying explicit includes or excludes during a run. + +By default it is assumed that the `name` is the path to the resource set folder, but this can be overridden. + +This field is **required**. + +### `path` + +The `path` field specifies an explicit path to a resource set folder in the case that it should differ from +the resource set's `name`. + +This field is **optional**. + +### `values` + +The `values` field specifies key/values pairs of variables that should be available during templating. + +This field is **optional**. + +### `include` + +The `include` field specifies additional resource sets that should be included and that should inherit the +variables of this resource set. + +The fully qualified names of "nested" resource sets are set to `${PARENT_NAME}/${CHILD_NAME}` and paths are +merged in the same way. + +This makes it easy to organise different resource sets as "groups" to include / exclude them collectively +during runs. + +This field is **optional**. + +## Multiple includes + +Resource sets can be included multiple times with different configurations. In this case it is recommended +to set the `path` and `name` fields explicitly. For example: + +```yaml +include: + - name: forwarder-europe + path: tools/forwarder + values: + source: europe + - name: forwarder-asia + path: tools/forwarder + values: + source: asia +``` + +The two different configurations can be referred to by their set names, but will use the same resource +templates with different configurations. + +## Nesting resource sets + +As mentioned above for the `include` field, resource sets can be nested. This lets users group resource +sets in logical ways using simple folder structures. + +Assuming a folder structure like: + +``` +├── backend +│   ├── auth-api +│   ├── message-api +│   └── order-api +└── frontend + ├── app-page + └── login-page +``` + +With each of these folders being a resource set, they could be included in a cluster configuration like so: + +```yaml +include: + - name: backend + include: + - name: auth-api + - name: message-api + - name: order-api + - name: frontend: + include: + - name: app-page + - name: login-page +``` + +Kontemplate could then be run with, for example, `--include backend` to only include the resource sets nested +in the backend group. Specific resource sets can also be targeted, for example as `--include backend/order-api`. + +Variables specified in the parent resource set are inherited by the children. + +### Caveats + +Two caveats apply that users should be aware of: + +1. The parent resource set can not contain any resource templates itself. + +2. Only one level of nesting is supported. Specifying `include` again on a nested resource set will be ignored. + +[templates]: templates.md +[cluster configuration]: cluster-config.md From d7569abccacba40200b61c54571016edd9e5a457 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sun, 30 Jul 2017 01:16:45 +0200 Subject: [PATCH 084/166] docs: Add tables of content to all documentation files --- README.md | 16 ++++++++++++++++ docs/cluster-config.md | 13 +++++++++++++ docs/resource-sets.md | 17 +++++++++++++++++ docs/templates.md | 13 +++++++++++++ docs/tips-and-tricks.md | 10 +++++++++- 5 files changed, 68 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d287f6001..17e0a6d24 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,22 @@ In my experience this is often enough and more complex solutions such as Check out a Kontemplate setup example and the feature list below! + +**Table of Contents** + +- [Kontemplate - A simple Kubernetes templater](#kontemplate---a-simple-kubernetes-templater) + - [Features](#features) + - [Example](#example) + - [Installation](#installation) + - [Homebrew](#homebrew) + - [Arch Linux](#arch-linux) + - [Building repeatably from source](#building-repeatably-from-source) + - [Building from source](#building-from-source) + - [Usage](#usage) + - [Contributing](#contributing) + + + ## Features * [Simple, yet powerful templates](docs/templates.md) diff --git a/docs/cluster-config.md b/docs/cluster-config.md index 3bc59a630..a87c3fb2a 100644 --- a/docs/cluster-config.md +++ b/docs/cluster-config.md @@ -31,6 +31,19 @@ include: - name: pub-service ``` + +**Table of Contents** + +- [Cluster configuration](#cluster-configuration) + - [Fields](#fields) + - [`context`](#context) + - [`global`](#global) + - [`import`](#import) + - [`include`](#include) + - [External variables](#external-variables) + + + ## Fields This is documentation for the individual fields in a cluster context file. diff --git a/docs/resource-sets.md b/docs/resource-sets.md index 1d5eeabc5..94d402f9a 100644 --- a/docs/resource-sets.md +++ b/docs/resource-sets.md @@ -5,6 +5,23 @@ Resource sets are collections of Kubernetes resources that should be passed to ` Technically a resource set is simply a folder with a few YAML and/or JSON templates in it. + +**Table of Contents** + +- [Resource Sets](#resource-sets) +- [Creating resource sets](#creating-resource-sets) + - [Default variables](#default-variables) +- [Including resource sets](#including-resource-sets) + - [Fields](#fields) + - [`name`](#name) + - [`path`](#path) + - [`values`](#values) + - [`include`](#include) + - [Multiple includes](#multiple-includes) + - [Nesting resource sets](#nesting-resource-sets) + - [Caveats](#caveats) + + # Creating resource sets diff --git a/docs/templates.md b/docs/templates.md index 8dff28dd1..8bcc2c7f5 100644 --- a/docs/templates.md +++ b/docs/templates.md @@ -12,6 +12,19 @@ or in which conditionals are evaluated for more complex template logic. It is recommended that you check out the Golang [documentation][] for the templating engine in addition to the cherry-picked features listed here. + +**Table of Contents** + +- [Kontemplate templates](#kontemplate-templates) + - [Basic variable interpolation](#basic-variable-interpolation) + - [Example:](#example) + - [Template functions](#template-functions) + - [Examples:](#examples) + - [Conditionals & ranges](#conditionals--ranges) + - [Caveats](#caveats) + + + ## Basic variable interpolation The basic template format uses `{{ .variableName }}` as the interpolation format. diff --git a/docs/tips-and-tricks.md b/docs/tips-and-tricks.md index debf014cf..73328fa0a 100644 --- a/docs/tips-and-tricks.md +++ b/docs/tips-and-tricks.md @@ -1,6 +1,14 @@ Kontemplate tips & tricks ========================= + +**Table of Contents** + +- [Kontemplate tips & tricks](#kontemplate-tips--tricks) + - [Update Deployments when ConfigMaps change](#update-deployments-when-configmaps-change) + - [direnv & pass](#direnv--pass) + + ## Update Deployments when ConfigMaps change @@ -64,4 +72,4 @@ per project, it is easy to use [direnv][] to switch to the correct `PASSWORD_STORE_DIR` variable when entering the folder. [not currently]: https://github.com/kubernetes/kubernetes/issues/22368 -[direnv]: https://direnv.net/ \ No newline at end of file +[direnv]: https://direnv.net/ From a159b71a19ccf83da1699efa111b2ff3ecb24b38 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Fri, 4 Aug 2017 23:04:43 +0200 Subject: [PATCH 085/166] style: Apply go fmt --- context/context.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/context/context.go b/context/context.go index ff17eccc3..0e787b559 100644 --- a/context/context.go +++ b/context/context.go @@ -9,11 +9,11 @@ import ( type ResourceSet struct { // Name of the resource set. This can be used in include/exclude statements during kontemplate runs. - Name string `json:"name"` + Name string `json:"name"` // Path to the folder containing the files for this resource set. This defaults to the value of the 'name' field // if unset. - Path string `json:"path"` + Path string `json:"path"` // Values to include when interpolating resources from this resource set. Values map[string]interface{} `json:"values"` @@ -22,24 +22,24 @@ type ResourceSet struct { Include []ResourceSet `json:"include"` // Parent resource set for flattened resource sets. Should not be manually specified. - Parent string + Parent string } type Context struct { // The name of the kubectl context - Name string `json:"context"` + Name string `json:"context"` // Global variables that should be accessible by all resource sets - Global map[string]interface{} `json:"global"` + Global map[string]interface{} `json:"global"` // File names of YAML or JSON files including extra variables that should be globally accessible - VariableImports []string `json:"import"` + VariableImports []string `json:"import"` // The resource sets to include in this context - ResourceSets []ResourceSet `json:"include"` + ResourceSets []ResourceSet `json:"include"` // This field represents the absolute path to the context base directory and should not be manually specified. - BaseDir string + BaseDir string } type ContextLoadingError struct { From 93425b951ce5afc1fed1a78232493002f8472dea Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Fri, 4 Aug 2017 23:06:11 +0200 Subject: [PATCH 086/166] docs: Complete cycle by linking back to kontemplate.works --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 17e0a6d24..14967f1e0 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Kontemplate - A simple Kubernetes templater [![Build Status](https://travis-ci.org/tazjin/kontemplate.svg?branch=master)](https://travis-ci.org/tazjin/kontemplate) -Kontemplate is a simple CLI tool that can take sets of Kubernetes resource +[Kontemplate][] is a simple CLI tool that can take sets of Kubernetes resource files with placeholders and insert values per environment. This tool was made because in many cases all I want in terms of Kubernetes @@ -182,6 +182,7 @@ Feel free to contribute pull requests, file bugs and open issues with feature su Please follow the [code of conduct](CODE_OF_CONDUCT.md). +[Kontemplate]: http://kontemplate.works [Helm]: https://helm.sh/ [releases page]: https://github.com/tazjin/kontemplate/releases [AUR package]: https://aur.archlinux.org/packages/kontemplate-git/ From a9c450c5a33a3e410377e1fde21c45e2db6bf51c Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Tue, 22 Aug 2017 18:29:20 +0200 Subject: [PATCH 087/166] fix build: Build Windows executable with correct name Windows executable filenames must end in ".exe" because the operating system can't execute them otherwise. This fixes #73 --- build-release.sh | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/build-release.sh b/build-release.sh index 5e5b2cbec..0109cace7 100755 --- a/build-release.sh +++ b/build-release.sh @@ -5,10 +5,21 @@ readonly GIT_HASH="$(git rev-parse --short HEAD)" readonly LDFLAGS="-X main.gitHash=${GIT_HASH} -w -s" readonly VERSION="1.1.0-${GIT_HASH}" +function binary-name() { + local os="${1}" + local target="${2}" + if [ "${os}" = "windows" ]; then + echo -n "${target}/kontemplate.exe" + else + echo -n "${target}/kontemplate" + fi +} + function build-for() { local os="${1}" local arch="${2}" local target="release/${os}/${arch}" + local bin=$(binary-name "${os}" "${target}") echo "Building kontemplate for ${os}-${arch} in ${target}" @@ -16,7 +27,7 @@ function build-for() { env GOOS="${os}" GOARCH="${arch}" go build \ -ldflags "${LDFLAGS}" \ - -o "${target}/kontemplate" \ + -o "${bin}" \ -tags netgo } From e8cfa9c1199a493c739408da8666c1b71a79f2e4 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Tue, 22 Aug 2017 18:37:54 +0200 Subject: [PATCH 088/166] refactor build: Keep GPG-signatures outside of tarballs Instead of signing the binary and adding the signature in the release tarball, keep the GPG-signatures *outside* of the tarball. This makes it easier to use the built-in GPG-signature verification features of package managers such as pacman. --- build-release.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/build-release.sh b/build-release.sh index 0109cace7..9fd11d961 100755 --- a/build-release.sh +++ b/build-release.sh @@ -35,15 +35,15 @@ function sign-for() { local os="${1}" local arch="${2}" local target="release/${os}/${arch}" - local bin="${target}/kontemplate" - local hash="$(sha256sum ${bin})" + local bin=$(binary-name "${os}" "${target}") local tar="release/kontemplate-${VERSION}-${os}-${arch}.tar.gz" - echo "Signing kontemplate binary for ${os}-${arch} with SHA256 ${hash}" - gpg --sign "${bin}" - echo "Packing release into ${tar}" - tar czvf "${tar}" -C "${target}" kontemplate kontemplate.gpg + tar czvf "${tar}" -C "${target}" $(basename "${bin}") + + local hash=$(sha256sum "${tar}") + echo "Signing kontemplate release tarball for ${os}-${arch} with SHA256 ${hash}" + gpg --armor --detach-sig --sign "${tar}" } case "${1}" in @@ -56,7 +56,7 @@ case "${1}" in exit 0 ;; "sign") - # Sign releases: + # Bundle and sign releases: sign-for "linux" "amd64" sign-for "darwin" "amd64" sign-for "windows" "amd64" From 825506d2e9e88773c91942b8a39058c7efd61bd1 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Tue, 22 Aug 2017 18:48:54 +0200 Subject: [PATCH 089/166] chore build: Bump dependency versions Several bugfixes, nothing major. Skipped one "outdated" dependency because the only change was a new text file. --- kontemplate.frm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kontemplate.frm b/kontemplate.frm index 26ca18140..4209c162e 100644 --- a/kontemplate.frm +++ b/kontemplate.frm @@ -10,7 +10,7 @@ inputs: # Kontemplate dependencies! "/go/src/github.com/polydawn/meep": type: "git" - hash: "296620394aa9cbcce7cfe9ad903489b8057662bc" + hash: "eaf1db2168fe380b4da17a35f0adddb5ae15a651" silo: "https://github.com/polydawn/meep" "/go/src/github.com/Masterminds/sprig": type: "git" @@ -22,11 +22,11 @@ inputs: silo: "https://github.com/ghodss/yaml" "/go/src/gopkg.in/yaml.v2": type: "git" - hash: "cd8b52f8269e0feb286dfeef29f8fe4d5b397e0b" + hash: "eb3733d160e74a9c7e442f435eb3bea458e1d19f" silo: "https://gopkg.in/yaml.v2" "/go/src/gopkg.in/alecthomas/kingpin.v2": type: "git" - hash: "7f0871f2e17818990e4eed73f9b5c2f429501228" + hash: "1087e65c9441605df944fb12c33f0fe7072d18ca" silo: "https://gopkg.in/alecthomas/kingpin.v2" "/go/src/github.com/alecthomas/template": type: "git" @@ -38,7 +38,7 @@ inputs: silo: "https://github.com/alecthomas/units" "/go/src/github.com/Masterminds/semver": type: "git" - hash: "abff1900528dbdaf6f3f5aa92c398be1eaf2a9f7" + hash: "517734cc7d6470c0d07130e40fd40bdeb9bcd3fd" silo: "https://github.com/Masterminds/semver" "/go/src/github.com/aokoli/goutils": type: "git" From d22b3694fe5e0fb0ec9d45f6724a7e5dcdbaaad1 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Tue, 22 Aug 2017 19:04:32 +0200 Subject: [PATCH 090/166] feat main: Warn if resource set contains no templates If a resource set is specified by the user and does _not_ contain any templates, a warning will now be printed. This fixes #79 --- main.go | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/main.go b/main.go index 0d809071a..fe2311599 100644 --- a/main.go +++ b/main.go @@ -83,6 +83,11 @@ func templateCommand() { _, resourceSets := loadContextAndResources(templateFile) for _, rs := range *resourceSets { + if len(rs.Resources) == 0 { + fmt.Fprintf(os.Stderr, "Warning: Resource set '%s' contains no valid templates\n", rs.Name) + break + } + for _, r := range rs.Resources { fmt.Fprintf(os.Stderr, "Rendered file %s/%s:\n", rs.Name, r.Filename) fmt.Println(r.Rendered) @@ -150,7 +155,12 @@ func loadContextAndResources(file *string) (*context.Context, *[]templater.Rende func runKubectlWithResources(c *context.Context, kubectlArgs *[]string, resourceSets *[]templater.RenderedResourceSet) error { args := append(*kubectlArgs, fmt.Sprintf("--context=%s", c.Name)) - for _, resourceSet := range *resourceSets { + for _, rs := range *resourceSets { + if len(rs.Resources) == 0 { + fmt.Fprintf(os.Stderr, "Warning: Resource set '%s' contains no valid templates\n", rs.Name) + continue + } + kubectl := exec.Command("kubectl", args...) stdin, err := kubectl.StdinPipe() @@ -165,8 +175,8 @@ func runKubectlWithResources(c *context.Context, kubectlArgs *[]string, resource return meep.New(&KubeCtlError{}, meep.Cause(err)) } - for _, r := range resourceSet.Resources { - fmt.Printf("Passing file %s/%s to kubectl\n", resourceSet.Name, r.Filename) + for _, r := range rs.Resources { + fmt.Printf("Passing file %s/%s to kubectl\n", rs.Name, r.Filename) fmt.Fprintln(stdin, r.Rendered) } stdin.Close() From f8b6ad652dd538a91b6b26ccb19f96fc43eb9b31 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Tue, 22 Aug 2017 19:18:14 +0200 Subject: [PATCH 091/166] Version 1.2.0 This release comes with some new features, usability improvements and a better build & release process. Features: * Documentation has been improved significantly, check out the new [README][] and follow the links within! * Extra variables can now be loaded from files on disk. Simply specify a list of YAML/JSON files under the 'import' key in your cluster context file. Check out #66 for details! * Resource set paths can now be overridden by users. By default it is assumed that the path to a resource set is the same as its name, however this is now user-controllable. This means the same resource set can be included multiple times under different names, for easier including/excluding. See #71 for details! * Kontemplate is currently getting a website that is under construction at [kontemplate.works][] - feel free to check it out and [give feedback][]! Fixes: * Windows release binaries now have the correct filename * Several potential warning and error messages have been improved Release binaries are signed with GPG key `66F505681DB8F43B` which is verified on my Github profile. [README]: https://github.com/tazjin/kontemplate/blob/master/README.md [kontemplate.works]: http://kontemplate.works/ [give feedback]: https://github.com/tazjin/kontemplate-website/issues --- build-release.sh | 2 +- kontemplate.frm | 2 +- main.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build-release.sh b/build-release.sh index 9fd11d961..ce24e00fc 100755 --- a/build-release.sh +++ b/build-release.sh @@ -3,7 +3,7 @@ set -ueo pipefail readonly GIT_HASH="$(git rev-parse --short HEAD)" readonly LDFLAGS="-X main.gitHash=${GIT_HASH} -w -s" -readonly VERSION="1.1.0-${GIT_HASH}" +readonly VERSION="1.2.0-${GIT_HASH}" function binary-name() { local os="${1}" diff --git a/kontemplate.frm b/kontemplate.frm index 4209c162e..59b519169 100644 --- a/kontemplate.frm +++ b/kontemplate.frm @@ -73,7 +73,7 @@ action: echo 'nameserver 8.8.8.8' > /etc/resolv.conf apt-get update && apt-get install -y git ca-certificates mkdir -p /go/src/github.com/tazjin - git clone --single-branch --branch v1.1.0 https://github.com/tazjin/kontemplate /go/src/github.com/tazjin/kontemplate + git clone --single-branch --branch v1.2.0 https://github.com/tazjin/kontemplate /go/src/github.com/tazjin/kontemplate cd /go/src/github.com/tazjin/kontemplate ./build-release.sh build outputs: diff --git a/main.go b/main.go index fe2311599..0ff50a80f 100644 --- a/main.go +++ b/main.go @@ -11,7 +11,7 @@ import ( "gopkg.in/alecthomas/kingpin.v2" ) -const version string = "1.1.0" +const version string = "1.2.0" // This variable will be initialised by the Go linker during the builder var gitHash string From 4c7ff46c73cc1c3e75f1a5f25dfac3f0131c06ab Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Tue, 22 Aug 2017 19:37:14 +0200 Subject: [PATCH 092/166] chore image: Bump Docker image to build v1.2.0 --- image/Dockerfile | 4 ++-- image/hashes | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/image/Dockerfile b/image/Dockerfile index feabeba30..03c638aae 100644 --- a/image/Dockerfile +++ b/image/Dockerfile @@ -1,8 +1,8 @@ FROM alpine:3.6 ADD hashes /root/hashes -ADD https://storage.googleapis.com/kubernetes-release/release/v1.6.4/bin/linux/amd64/kubectl /usr/bin/kubectl -ADD https://github.com/tazjin/kontemplate/releases/download/v1.1.0/kontemplate-1.1.0-f7ce04e-linux-amd64.tar.gz /tmp/kontemplate.tar.gz +ADD https://storage.googleapis.com/kubernetes-release/release/v1.7.4/bin/linux/amd64/kubectl /usr/bin/kubectl +ADD https://github.com/tazjin/kontemplate/releases/download/v1.2.0/kontemplate-1.2.0-f8b6ad6-linux-amd64.tar.gz /tmp/kontemplate.tar.gz # Pass release version is 1.7.1 ADD https://raw.githubusercontent.com/zx2c4/password-store/38ec1c72e29c872ec0cdde82f75490640d4019bf/src/password-store.sh /usr/bin/pass diff --git a/image/hashes b/image/hashes index 69febe853..9f2154b05 100644 --- a/image/hashes +++ b/image/hashes @@ -1,2 +1,2 @@ -f2bdd1d458b2fad935f1f64e0a40d59b28be6b0debbc20241e1d8ad4a77a7cc2 /tmp/kontemplate.tar.gz -a91c6b028bb3f737898ff003f7f3a3f2d242ea52e89ade1f6ca3ab99170119e5 /usr/bin/kubectl +fd62f9732e53a18748c6afe5854b62ec5c8b56e140b4a78baf100089d3961d03 /tmp/kontemplate.tar.gz +fb5a961a6ba55093691228d71e64bffd57ada98226f195c409ce297d1cb5086a /usr/bin/kubectl From 8ccaab322a93af0ea81ae4d2c564f550b5c3964d Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Tue, 22 Aug 2017 19:38:36 +0200 Subject: [PATCH 093/166] chore brew: Update Homebrew formula to v1.2.0 --- kontemplate.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kontemplate.rb b/kontemplate.rb index 5277f291b..7fa061ba0 100644 --- a/kontemplate.rb +++ b/kontemplate.rb @@ -3,9 +3,9 @@ class Kontemplate < Formula desc "Kontemplate - Extremely simple Kubernetes resource templates" homepage "https://github.com/tazjin/kontemplate" - url "https://github.com/tazjin/kontemplate/releases/download/v1.1.0/kontemplate-1.1.0-f7ce04e-darwin-amd64.tar.gz" - sha256 "1dccc80804589f6bd233dd79f52527f2ba8c4fa8d38857c80b2f47b504fe6c04" - version "1.1.0-f7ce04e" + url "https://github.com/tazjin/kontemplate/releases/download/v1.2.0/kontemplate-1.2.0-f8b6ad6-darwin-amd64.tar.gz" + sha256 "0cda70956b4d4e4944d5760970aaf20d586ef9d62ee90e2d67b70f03ed85e075" + version "1.2.0-f8b6ad6" def install bin.install "kontemplate" From 063a3e9d3037c77aea765d7c58bff567b21d5946 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Fri, 25 Aug 2017 14:35:54 +0200 Subject: [PATCH 094/166] fix context: Support ".yml" extension on default files In other places in Kontemplate that deal with YAML files, both the ".yaml" and ".yml" extension are supported. This fixes context loading code to support that for resource set variables, too. This fixes #83 --- context/context.go | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/context/context.go b/context/context.go index 0e787b559..fe04a051e 100644 --- a/context/context.go +++ b/context/context.go @@ -143,16 +143,13 @@ func loadAllDefaultValues(c *Context) []ResourceSet { func loadDefaultValues(rs *ResourceSet, c *Context) *map[string]interface{} { var defaultVars map[string]interface{} - // Attempt to load YAML values - err := util.LoadJsonOrYaml(path.Join(c.BaseDir, rs.Path, "default.yaml"), &defaultVars) - if err == nil { - return util.Merge(&defaultVars, &rs.Values) - } + defaultFilenames := []string{"default.yml", "default.yaml", "default.json"} - // Attempt to load JSON values - err = util.LoadJsonOrYaml(path.Join(c.BaseDir, rs.Path, "default.json"), &defaultVars) - if err == nil { - return util.Merge(&defaultVars, &rs.Values) + for _, filename := range defaultFilenames { + err := util.LoadJsonOrYaml(path.Join(c.BaseDir, rs.Path, filename), &defaultVars) + if err == nil { + return util.Merge(&defaultVars, &rs.Values) + } } // The actual error is not inspected here. The reasoning for this is that in case of serious problems (e.g. From b20bc5f57a9e4e25760f03de752c2ed2811fa5fe Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Thu, 31 Aug 2017 18:37:57 +0200 Subject: [PATCH 095/166] fix templater: Don't template default.yml files After the change from #84 default variable files with the '.yml' extension got templated as resource set templates accidentally. This resolves the issue by moving the list reserved default file names to a common place and reusing it in both the templater and context pkg. This fixes #85 --- context/context.go | 4 +--- templater/templater.go | 6 ++++-- util/util.go | 3 +++ 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/context/context.go b/context/context.go index fe04a051e..0be42d6d2 100644 --- a/context/context.go +++ b/context/context.go @@ -143,9 +143,7 @@ func loadAllDefaultValues(c *Context) []ResourceSet { func loadDefaultValues(rs *ResourceSet, c *Context) *map[string]interface{} { var defaultVars map[string]interface{} - defaultFilenames := []string{"default.yml", "default.yaml", "default.json"} - - for _, filename := range defaultFilenames { + for _, filename := range util.DefaultFilenames { err := util.LoadJsonOrYaml(path.Join(c.BaseDir, rs.Path, filename), &defaultVars) if err == nil { return util.Merge(&defaultVars, &rs.Values) diff --git a/templater/templater.go b/templater/templater.go index 4a0c8e7d8..f6878d3df 100644 --- a/templater/templater.go +++ b/templater/templater.go @@ -186,8 +186,10 @@ func templateFuncs() template.FuncMap { // Checks whether a file is a resource file (i.e. is YAML or JSON) and not a default values file. func isResourceFile(f os.FileInfo) bool { - if f.Name() == "default.json" || f.Name() == "default.yaml" { - return false + for _, defaultFile := range util.DefaultFilenames { + if f.Name() == defaultFile { + return false + } } return strings.HasSuffix(f.Name(), "yaml") || diff --git a/util/util.go b/util/util.go index 0c5815a38..eb8806036 100644 --- a/util/util.go +++ b/util/util.go @@ -9,6 +9,9 @@ import ( "github.com/ghodss/yaml" ) +// Filenames excluded from templating for the purpose of containing default variable values inside a resource set. +var DefaultFilenames []string = []string{"default.yml", "default.yaml", "default.json"} + // Merges two maps together. Values from the second map override values in the first map. // The returned map is new if anything was changed. func Merge(in1 *map[string]interface{}, in2 *map[string]interface{}) *map[string]interface{} { From 68e2f99062952290b59699a83187e7a5058fac3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Magnus=20Landr=C3=B8?= Date: Tue, 17 Oct 2017 22:49:42 +0200 Subject: [PATCH 096/166] feat templater: Add IP lookup function This introduces support for looking up IP addresses using local DNS resolver. Function will return a list of all IP addresses associated with hostname. Further processing can be achieved using supported list template functions. --- templater/dns.go | 34 ++++++++++++++++++++++++++++++++++ templater/templater.go | 1 + 2 files changed, 35 insertions(+) create mode 100644 templater/dns.go diff --git a/templater/dns.go b/templater/dns.go new file mode 100644 index 000000000..087399202 --- /dev/null +++ b/templater/dns.go @@ -0,0 +1,34 @@ +// This file contains the implementation of a template function for retrieving IP addresses from DNS +package templater + +import ( + "fmt" + "github.com/polydawn/meep" + "net" + "os" +) + +type DNSError struct { + meep.TraitAutodescribing + meep.TraitCausable + Output string +} + +func GetIPsFromDNS(host string) ([]interface{}, error) { + fmt.Fprintf(os.Stderr, "Attempting to look up IP for %s in DNS\n", host) + ips, err := net.LookupIP(host) + + if err != nil { + return nil, meep.New( + &DNSError{Output: "IP address lookup failed"}, + meep.Cause(err), + ) + } + + var result []interface{} = make([]interface{}, len(ips)) + for i, ip := range ips { + result[i] = ip + } + + return result, nil +} diff --git a/templater/templater.go b/templater/templater.go index f6878d3df..c9260abc2 100644 --- a/templater/templater.go +++ b/templater/templater.go @@ -180,6 +180,7 @@ func templateFuncs() template.FuncMap { return string(b) } m["passLookup"] = GetFromPass + m["lookupIPAddr"] = GetIPsFromDNS return m } From 2574942338d5fda9dc3beb46dfc600707189de3d Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Fri, 27 Oct 2017 02:36:27 +0200 Subject: [PATCH 097/166] fix main: Do not stop templater if a single resource set is empty This fixes #91 --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index 0ff50a80f..574403cbf 100644 --- a/main.go +++ b/main.go @@ -85,7 +85,7 @@ func templateCommand() { for _, rs := range *resourceSets { if len(rs.Resources) == 0 { fmt.Fprintf(os.Stderr, "Warning: Resource set '%s' contains no valid templates\n", rs.Name) - break + continue } for _, r := range rs.Resources { From 9cffd3d1d40e90844394011898f6dd820f7805b7 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Fri, 27 Oct 2017 02:37:28 +0200 Subject: [PATCH 098/166] refactor templater: Add explicit note about empty-rs warnings Due to an interesting combination of an error not being handled and Go initialising everything with what it thinks should be the default, non-existent resource sets have been gracefully handled already. This makes this accidental fix explicit. Fixes #90 --- templater/templater.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/templater/templater.go b/templater/templater.go index c9260abc2..bb1f8c2ee 100644 --- a/templater/templater.go +++ b/templater/templater.go @@ -66,7 +66,10 @@ func processResourceSet(c *context.Context, rs *context.ResourceSet) (*RenderedR fmt.Fprintf(os.Stderr, "Loading resources for %s\n", rs.Name) rp := path.Join(c.BaseDir, rs.Path) - files, err := ioutil.ReadDir(rp) + + // Explicitly discard this error, which will give us an empty list of files instead. + // This will end up printing a warning to the user, but it won't stop the rest of the process. + files, _ := ioutil.ReadDir(rp) resources, err := processFiles(c, rs, rp, files) From bd03e639bfdaf5b8919b66d87c683fcaa6ec0aad Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Fri, 27 Oct 2017 02:39:16 +0200 Subject: [PATCH 099/166] refactor main: Reword empty/nonexistent resource set warning --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index 574403cbf..a4ab56b05 100644 --- a/main.go +++ b/main.go @@ -84,7 +84,7 @@ func templateCommand() { for _, rs := range *resourceSets { if len(rs.Resources) == 0 { - fmt.Fprintf(os.Stderr, "Warning: Resource set '%s' contains no valid templates\n", rs.Name) + fmt.Fprintf(os.Stderr, "Warning: Resource set '%s' does not exist or contains no valid templates\n", rs.Name) continue } From 7bbc3cc033b8082f7758d4c2713846c8a8aadf59 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sat, 4 Nov 2017 12:36:45 +0100 Subject: [PATCH 100/166] Version 1.3.0 This release comes with minor usability improvements and features. * A new 'lookupIPAddr' template function is available for resolving DNS A records in templates. Thanks to @landro for the pull request! * Handling of "non-standard" resource set structures has been improved to result in better error messages and behaviour in several places. Release binaries are signed with GPG key `66F505681DB8F43B` which is verified on my Github profile. -------------- Note: This is the last Kontemplate release that will be written in Go. Rob Pike's art project has proven its point but I believe it is ethically questionable and morally indefensible to continue on this path. You can track #72 for the Rust-rewrite of Kontemplate. --- build-release.sh | 2 +- kontemplate.frm | 2 +- main.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build-release.sh b/build-release.sh index ce24e00fc..a8df0ecb6 100755 --- a/build-release.sh +++ b/build-release.sh @@ -3,7 +3,7 @@ set -ueo pipefail readonly GIT_HASH="$(git rev-parse --short HEAD)" readonly LDFLAGS="-X main.gitHash=${GIT_HASH} -w -s" -readonly VERSION="1.2.0-${GIT_HASH}" +readonly VERSION="1.3.0-${GIT_HASH}" function binary-name() { local os="${1}" diff --git a/kontemplate.frm b/kontemplate.frm index 59b519169..d6f5b1a1b 100644 --- a/kontemplate.frm +++ b/kontemplate.frm @@ -73,7 +73,7 @@ action: echo 'nameserver 8.8.8.8' > /etc/resolv.conf apt-get update && apt-get install -y git ca-certificates mkdir -p /go/src/github.com/tazjin - git clone --single-branch --branch v1.2.0 https://github.com/tazjin/kontemplate /go/src/github.com/tazjin/kontemplate + git clone --single-branch --branch v1.3.0 https://github.com/tazjin/kontemplate /go/src/github.com/tazjin/kontemplate cd /go/src/github.com/tazjin/kontemplate ./build-release.sh build outputs: diff --git a/main.go b/main.go index a4ab56b05..aadc8eaa8 100644 --- a/main.go +++ b/main.go @@ -11,7 +11,7 @@ import ( "gopkg.in/alecthomas/kingpin.v2" ) -const version string = "1.2.0" +const version string = "1.3.0" // This variable will be initialised by the Go linker during the builder var gitHash string From eaec5d57da87c2aae3b150150dc6d71a0fa74e60 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sat, 4 Nov 2017 13:31:35 +0100 Subject: [PATCH 101/166] chore: Bump Docker image & Homebrew release to v1.3.0 --- image/Dockerfile | 2 +- image/hashes | 2 +- kontemplate.rb | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/image/Dockerfile b/image/Dockerfile index 03c638aae..049a780c0 100644 --- a/image/Dockerfile +++ b/image/Dockerfile @@ -2,7 +2,7 @@ FROM alpine:3.6 ADD hashes /root/hashes ADD https://storage.googleapis.com/kubernetes-release/release/v1.7.4/bin/linux/amd64/kubectl /usr/bin/kubectl -ADD https://github.com/tazjin/kontemplate/releases/download/v1.2.0/kontemplate-1.2.0-f8b6ad6-linux-amd64.tar.gz /tmp/kontemplate.tar.gz +ADD https://github.com/tazjin/kontemplate/releases/download/v1.3.0/kontemplate-1.3.0-98daa6b-linux-amd64.tar.gz /tmp/kontemplate.tar.gz # Pass release version is 1.7.1 ADD https://raw.githubusercontent.com/zx2c4/password-store/38ec1c72e29c872ec0cdde82f75490640d4019bf/src/password-store.sh /usr/bin/pass diff --git a/image/hashes b/image/hashes index 9f2154b05..5cf26e4dc 100644 --- a/image/hashes +++ b/image/hashes @@ -1,2 +1,2 @@ -fd62f9732e53a18748c6afe5854b62ec5c8b56e140b4a78baf100089d3961d03 /tmp/kontemplate.tar.gz +c0353f77c62047dfbfd6dafca91e26e91b18395949e4208b422c8cf3d196732d /tmp/kontemplate.tar.gz fb5a961a6ba55093691228d71e64bffd57ada98226f195c409ce297d1cb5086a /usr/bin/kubectl diff --git a/kontemplate.rb b/kontemplate.rb index 7fa061ba0..2b0bcbfa8 100644 --- a/kontemplate.rb +++ b/kontemplate.rb @@ -3,9 +3,9 @@ class Kontemplate < Formula desc "Kontemplate - Extremely simple Kubernetes resource templates" homepage "https://github.com/tazjin/kontemplate" - url "https://github.com/tazjin/kontemplate/releases/download/v1.2.0/kontemplate-1.2.0-f8b6ad6-darwin-amd64.tar.gz" - sha256 "0cda70956b4d4e4944d5760970aaf20d586ef9d62ee90e2d67b70f03ed85e075" - version "1.2.0-f8b6ad6" + url "https://github.com/tazjin/kontemplate/releases/download/v1.3.0/kontemplate-1.3.0-98daa6b-darwin-amd64.tar.gz" + sha256 "4372e2c0f1249aa43f67e664335c20748d0b394ada1fcceefa3ae786e839ee47" + version "1.3.0-98daa6b" def install bin.install "kontemplate" From a67f2d87eef1da2875a4608c722f2a8d01d2fb3f Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Tue, 7 Nov 2017 19:19:42 +0100 Subject: [PATCH 102/166] docs(README): Remove dead links Never got around to writing them and maybe they shouldn't be hanging around. --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 14967f1e0..141baf9e1 100644 --- a/README.md +++ b/README.md @@ -36,8 +36,8 @@ Check out a Kontemplate setup example and the feature list below! * [Simple, yet powerful templates](docs/templates.md) * [Clean cluster configuration files](docs/cluster-config.md) * [Resources organised as simple resource sets](docs/resource-sets.md) -* [Integration with pass](docs/pass.md) -* [Integration with kubectl](docs/kubectl.md) +* Integration with pass +* Integration with kubectl ## Example @@ -173,8 +173,7 @@ kontemplate apply example/prod-cluster.yaml --dry-run kontemplate apply example/prod-cluster.yaml ``` -Check out the feature list and the individual feature documentation above and read the -[best practices](docs/best-practices.md). Then you should be good to go! +Check out the feature list and the individual feature documentation above. Then you should be good to go! ## Contributing From 5ee1e9387d41a4a817c56cac2535a07cd81a77c7 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Thu, 16 Nov 2017 15:08:32 +0100 Subject: [PATCH 103/166] feat(image): Install git in kontemplate image --- image/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/image/Dockerfile b/image/Dockerfile index 049a780c0..b2e8d5fba 100644 --- a/image/Dockerfile +++ b/image/Dockerfile @@ -8,7 +8,7 @@ ADD https://github.com/tazjin/kontemplate/releases/download/v1.3.0/kontemplate-1 ADD https://raw.githubusercontent.com/zx2c4/password-store/38ec1c72e29c872ec0cdde82f75490640d4019bf/src/password-store.sh /usr/bin/pass RUN sha256sum -c /root/hashes && \ - apk add -U bash tree gnupg && \ + apk add -U bash tree gnupg git && \ chmod +x /usr/bin/kubectl /usr/bin/pass && \ tar xzvf /tmp/kontemplate.tar.gz && \ mv kontemplate /usr/bin/kontemplate From bfad4a3932f233e7cf315af9df083dc072779388 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Tue, 21 Nov 2017 11:24:04 +0100 Subject: [PATCH 104/166] feat(license): Relicense under GPLv3 All further kontemplate source code changes and releases will happen under the GPLv3. Previous releases are still available under the MIT license. --- LICENSE | 687 +++++++++++++++++++++++++++++++++++- PKGBUILD | 2 +- README.md | 3 + build-release.sh | 9 + context/context.go | 9 + context/context_test.go | 9 + diff-deps.fish | 9 + kontemplate.frm | 9 + main.go | 15 + templater/dns.go | 13 +- templater/pass.go | 14 +- templater/templater.go | 9 + templater/templater_test.go | 9 + util/util.go | 9 + util/util_test.go | 9 + 15 files changed, 794 insertions(+), 21 deletions(-) diff --git a/LICENSE b/LICENSE index b1b8e03c8..94a9ed024 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,674 @@ -MIT License + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 -Copyright (c) 2017 Vincent Ambo + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: + Preamble -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. + The GNU General Public License is a free, copyleft license for +software and other kinds of works. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/PKGBUILD b/PKGBUILD index e6b148b54..2c62fec4d 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -5,7 +5,7 @@ pkgrel=1 pkgdesc="Simple Kubernetes resource templating" arch=('x86_64') url="https://github.com/tazjin/kontemplate" -license=('MIT') +license=('GPLv3') makedepends=('go') optdepends=('pass: Template secrets into resources') source=('kontemplate-git::git+https://github.com/tazjin/kontemplate.git') diff --git a/README.md b/README.md index 141baf9e1..896574901 100644 --- a/README.md +++ b/README.md @@ -179,6 +179,9 @@ Check out the feature list and the individual feature documentation above. Then Feel free to contribute pull requests, file bugs and open issues with feature suggestions! +Kontemplate is licensed under the GPLv3, a copy of the license and its terms can be found +in the `LICENSE` file. + Please follow the [code of conduct](CODE_OF_CONDUCT.md). [Kontemplate]: http://kontemplate.works diff --git a/build-release.sh b/build-release.sh index a8df0ecb6..84ad4fb0b 100755 --- a/build-release.sh +++ b/build-release.sh @@ -1,3 +1,12 @@ +# Copyright (C) 2016-2017 Vincent Ambo +# +# This file is part of Kontemplate. +# +# Kontemplate is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + #!/bin/bash set -ueo pipefail diff --git a/context/context.go b/context/context.go index 0be42d6d2..a6d181e7b 100644 --- a/context/context.go +++ b/context/context.go @@ -1,3 +1,12 @@ +// Copyright (C) 2016-2017 Vincent Ambo +// +// This file is part of Kontemplate. +// +// Kontemplate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + package context import ( diff --git a/context/context_test.go b/context/context_test.go index 350b2b66a..38b6a76e7 100644 --- a/context/context_test.go +++ b/context/context_test.go @@ -1,3 +1,12 @@ +// Copyright (C) 2016-2017 Vincent Ambo +// +// This file is part of Kontemplate. +// +// Kontemplate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + package context import ( diff --git a/diff-deps.fish b/diff-deps.fish index 4a5bf7585..9ad788ece 100755 --- a/diff-deps.fish +++ b/diff-deps.fish @@ -1,3 +1,12 @@ +# Copyright (C) 2016-2017 Vincent Ambo +# +# This file is part of Kontemplate. +# +# Kontemplate is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + #!/usr/bin/env fish function get_remote_master diff --git a/kontemplate.frm b/kontemplate.frm index d6f5b1a1b..195a83afd 100644 --- a/kontemplate.frm +++ b/kontemplate.frm @@ -1,3 +1,12 @@ +# Copyright (C) 2016-2017 Vincent Ambo +# +# This file is part of Kontemplate. +# +# Kontemplate is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + inputs: "/": # https://github.com/tklx/base type: "tar" diff --git a/main.go b/main.go index aadc8eaa8..19dd74d0c 100644 --- a/main.go +++ b/main.go @@ -1,3 +1,18 @@ +// Copyright (C) 2016-2017 Vincent Ambo +// +// Kontemplate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + package main import ( diff --git a/templater/dns.go b/templater/dns.go index 087399202..096ceb1df 100644 --- a/templater/dns.go +++ b/templater/dns.go @@ -1,4 +1,15 @@ -// This file contains the implementation of a template function for retrieving IP addresses from DNS +// Copyright (C) 2016-2017 Vincent Ambo +// +// This file is part of Kontemplate. +// +// Kontemplate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This file contains the implementation of a template function for retrieving +// IP addresses from DNS + package templater import ( diff --git a/templater/pass.go b/templater/pass.go index fbd2c0764..ecddff740 100644 --- a/templater/pass.go +++ b/templater/pass.go @@ -1,5 +1,15 @@ -// This file contains the implementation of a template function for retrieving variables from 'pass', the standard UNIX -// password manager. +// Copyright (C) 2016-2017 Vincent Ambo +// +// This file is part of Kontemplate. +// +// Kontemplate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This file contains the implementation of a template function for retrieving +// variables from 'pass', the standard UNIX password manager. + package templater import ( diff --git a/templater/templater.go b/templater/templater.go index bb1f8c2ee..7fb85e8b2 100644 --- a/templater/templater.go +++ b/templater/templater.go @@ -1,3 +1,12 @@ +// Copyright (C) 2016-2017 Vincent Ambo +// +// This file is part of Kontemplate. +// +// Kontemplate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + package templater import ( diff --git a/templater/templater_test.go b/templater/templater_test.go index 4aeee254d..f6b12c758 100644 --- a/templater/templater_test.go +++ b/templater/templater_test.go @@ -1,3 +1,12 @@ +// Copyright (C) 2016-2017 Vincent Ambo +// +// This file is part of Kontemplate. +// +// Kontemplate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + package templater import ( diff --git a/util/util.go b/util/util.go index eb8806036..947ab6e1c 100644 --- a/util/util.go +++ b/util/util.go @@ -1,3 +1,12 @@ +// Copyright (C) 2016-2017 Vincent Ambo +// +// This file is part of Kontemplate. +// +// Kontemplate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + package util import ( diff --git a/util/util_test.go b/util/util_test.go index a6adcd713..1a17adfcd 100644 --- a/util/util_test.go +++ b/util/util_test.go @@ -1,3 +1,12 @@ +// Copyright (C) 2016-2017 Vincent Ambo +// +// This file is part of Kontemplate. +// +// Kontemplate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + package util import ( From 9286e70da371e2dda00ca0d280fcbbb2b2720e80 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Mon, 11 Dec 2017 20:55:50 +0100 Subject: [PATCH 105/166] docs(cluster-config): 'context' key optional for template command Thanks to @christopher376 on the Kubernetes Slack for pointing out this mistake. --- docs/cluster-config.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cluster-config.md b/docs/cluster-config.md index a87c3fb2a..fee1f5763 100644 --- a/docs/cluster-config.md +++ b/docs/cluster-config.md @@ -55,7 +55,7 @@ The `context` field contains the name of the kubectl-context. You can list conte This must be set here so that Kontemplate can use the correct context when calling kubectl. -This field is **required**. +This field is **required** for `kubectl`-wrapping commands. It can be left out if only the `template`-command is used. ### `global` From 4b1d44f71bf243a86b90d047ef65d5b418b4f5bd Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Fri, 9 Mar 2018 11:32:17 +0100 Subject: [PATCH 106/166] refactor(build): Add Nix derivation & configure Travis to build it Adds a Nix-derivation for building kontemplate with dependencies pinned in Nix. --- .travis.yml | 5 +-- default.nix | 33 +++++++++++++++ deps.nix | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 154 insertions(+), 4 deletions(-) create mode 100644 default.nix create mode 100644 deps.nix diff --git a/.travis.yml b/.travis.yml index 382ca5061..b49a7b383 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,2 @@ --- -language: go -before_script: - - if [[ -n "$(go fmt ./...)" ]]; then echo 'Run go fmt!' && exit 1; fi - - go vet ./... +language: nix diff --git a/default.nix b/default.nix new file mode 100644 index 000000000..f61d83153 --- /dev/null +++ b/default.nix @@ -0,0 +1,33 @@ +# Copyright (C) 2016-2018 Vincent Ambo +# +# This file is part of Kontemplate. +# +# Kontemplate is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This file is the Nix derivation used to install Kontemplate on +# Nix-based systems. + +{ pkgs ? import {} }: + +with pkgs; buildGoPackage rec { + name = "kontemplate-${version}"; + version = "master"; + src = ./.; + goPackagePath = "github.com/tazjin/kontemplate"; + goDeps = ./deps.nix; + + # Enable checks and configure check-phase to include vet: + doCheck = true; + preCheck = '' + getGoDirs "" | parallel -j $NIX_BUILD_CORES buildGoDir vet + ''; + + meta = with lib; { + description = "A resource templating helper for Kubernetes"; + homepage = "http://kontemplate.works/"; + license = licenses.gpl3; + }; +} diff --git a/deps.nix b/deps.nix new file mode 100644 index 000000000..811654f34 --- /dev/null +++ b/deps.nix @@ -0,0 +1,120 @@ +# This file was generated by https://github.com/kamilchm/go2nix v1.2.1 +[ + { + goPackagePath = "github.com/Masterminds/semver"; + fetch = { + type = "git"; + url = "https://github.com/Masterminds/semver"; + rev = "517734cc7d6470c0d07130e40fd40bdeb9bcd3fd"; + sha256 = "1625b5sxpmlz60jw67j1ljfcc09d4lhxg3z6gc4am8s2rrdgwij6"; + }; + } + { + goPackagePath = "github.com/Masterminds/sprig"; + fetch = { + type = "git"; + url = "https://github.com/Masterminds/sprig"; + rev = "e039e20e500c2c025d9145be375e27cf42a94174"; + sha256 = "1yhpyzq6ghwl0242phjpbc9358fcw63pxrcxsyv9n4dm0w15va3m"; + }; + } + { + goPackagePath = "github.com/alecthomas/template"; + fetch = { + type = "git"; + url = "https://github.com/alecthomas/template"; + rev = "a0175ee3bccc567396460bf5acd36800cb10c49c"; + sha256 = "0qjgvvh26vk1cyfq9fadyhfgdj36f1iapbmr5xp6zqipldz8ffxj"; + }; + } + { + goPackagePath = "github.com/alecthomas/units"; + fetch = { + type = "git"; + url = "https://github.com/alecthomas/units"; + rev = "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a"; + sha256 = "1j65b91qb9sbrml9cpabfrcf07wmgzzghrl7809hjjhrmbzri5bl"; + }; + } + { + goPackagePath = "github.com/aokoli/goutils"; + fetch = { + type = "git"; + url = "https://github.com/aokoli/goutils"; + rev = "3391d3790d23d03408670993e957e8f408993c34"; + sha256 = "1yj4yjfwylica31sgj69ygb04p9xxi22kgfxd0j5f58zr8vwww2n"; + }; + } + { + goPackagePath = "github.com/ghodss/yaml"; + fetch = { + type = "git"; + url = "https://github.com/ghodss/yaml"; + rev = "0ca9ea5df5451ffdf184b4428c902747c2c11cd7"; + sha256 = "0skwmimpy7hlh7pva2slpcplnm912rp3igs98xnqmn859kwa5v8g"; + }; + } + { + goPackagePath = "github.com/huandu/xstrings"; + fetch = { + type = "git"; + url = "https://github.com/huandu/xstrings"; + rev = "3959339b333561bf62a38b424fd41517c2c90f40"; + sha256 = "0f1jyd80grpr88gwhljx2x0xgsyzw07807n4z4axxxlybh5f0nh1"; + }; + } + { + goPackagePath = "github.com/imdario/mergo"; + fetch = { + type = "git"; + url = "https://github.com/imdario/mergo"; + rev = "d806ba8c21777d504a2090a2ca4913c750dd3a33"; + sha256 = "12n3lfbfxvnag916c6dpxl48j29s482zwsqjc6wk4vb68qbz2nl3"; + }; + } + { + goPackagePath = "github.com/polydawn/meep"; + fetch = { + type = "git"; + url = "https://github.com/polydawn/meep"; + rev = "eaf1db2168fe380b4da17a35f0adddb5ae15a651"; + sha256 = "12n134fb2imnj67xkbznzm0gqkg36hdxwr960y91qb5s2q2krxir"; + }; + } + { + goPackagePath = "github.com/satori/go.uuid"; + fetch = { + type = "git"; + url = "https://github.com/satori/go.uuid"; + rev = "5bf94b69c6b68ee1b541973bb8e1144db23a194b"; + sha256 = "0l782l4srv36pj8pfgn61996d0vjifld4a569rbjwq5h14pd0c07"; + }; + } + { + goPackagePath = "golang.org/x/crypto"; + fetch = { + type = "git"; + url = "https://go.googlesource.com/crypto"; + rev = "ab89591268e0c8b748cbe4047b00197516011af5"; + sha256 = "1cbg8wlv1hmdps9ksa4kym5zy0mb2yjykw4ns7yqv7nmz4s5xajr"; + }; + } + { + goPackagePath = "gopkg.in/alecthomas/kingpin.v2"; + fetch = { + type = "git"; + url = "https://gopkg.in/alecthomas/kingpin.v2"; + rev = "1087e65c9441605df944fb12c33f0fe7072d18ca"; + sha256 = "18llqzkdqf62qbqcv2fd3j0igl6cwwn4dissf5skkvxrcxjcmmj0"; + }; + } + { + goPackagePath = "gopkg.in/yaml.v2"; + fetch = { + type = "git"; + url = "https://gopkg.in/yaml.v2"; + rev = "eb3733d160e74a9c7e442f435eb3bea458e1d19f"; + sha256 = "1srhvcaa9db3a6xj29mkjr5kg33y71pclrlx4vcwz5m1lgb5c7q6"; + }; + } +] From d7f19934d94f978535df1e9ea361924e15ad8239 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Fri, 9 Mar 2018 12:50:49 +0100 Subject: [PATCH 107/166] feat(build): Add Nix derivation for release builds Adds a Nix derivation that produces statically linked output binaries for multiple operating systems and architectures. This requires a Nix-channel version that includes the Go 1.10 compiler. --- release.nix | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 release.nix diff --git a/release.nix b/release.nix new file mode 100644 index 000000000..fc047f26a --- /dev/null +++ b/release.nix @@ -0,0 +1,53 @@ +# Copyright (C) 2016-2018 Vincent Ambo +# +# This file is part of Kontemplate. +# +# Kontemplate is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This file is the Nix derivation used to build release binaries for +# several different architectures and operating systems. + +{ pkgs ? import {} }: + +with pkgs; let buildGo110Package = + callPackage { + go = go_1_10; +}; +in buildGo110Package rec { + name = "kontemplate-${version}"; + version = "master"; + src = ./.; + goPackagePath = "github.com/tazjin/kontemplate"; + goDeps = ./deps.nix; + + # This configuration enables the building of statically linked + # executables. For some reason, those will have multiple references + # to the Go compiler's installation path in them, which is the + # reason for setting the 'allowGoReference' flag. + dontStrip = true; # Linker configuration handles stripping + allowGoReference = true; + CGO_ENABLED="0"; + GOCACHE="off"; + + # Configure release builds via the "build-matrix" script: + buildInputs = [ git ]; + buildPhase = '' + cd go/src/${goPackagePath} + ./build-release.sh build + ''; + + outputs = [ "out" ]; + installPhase = '' + mkdir $out + cp -r release/ $out + ''; + + meta = with lib; { + description = "A resource templating helper for Kubernetes"; + homepage = "http://kontemplate.works/"; + license = licenses.gpl3; + }; +} From 4a6db37ef7581fc26f3eff50a25d4a44fab23d37 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Fri, 9 Mar 2018 12:52:24 +0100 Subject: [PATCH 108/166] chore: Remove legacy build system --- README.md | 7 ---- diff-deps.fish | 38 -------------------- kontemplate.frm | 92 ------------------------------------------------- 3 files changed, 137 deletions(-) delete mode 100755 diff-deps.fish delete mode 100644 kontemplate.frm diff --git a/README.md b/README.md index 896574901..dd5280a97 100644 --- a/README.md +++ b/README.md @@ -114,13 +114,6 @@ brew install kontemplate An [AUR package][] is available for Arch Linux and other `pacman`-based distributions. -### Building repeatably from source - -Version pinning for Go dependencies is provided by a [Repeatr][] formula. After cloning -the repository the latest release can be built with `repeatr run kontemplate.frm`. - -This will place release binaries in the `release` folder. - ### Building from source Assuming you have Go configured correctly, you can simply `go get github.com/tazjin/kontemplate/...`. diff --git a/diff-deps.fish b/diff-deps.fish deleted file mode 100755 index 9ad788ece..000000000 --- a/diff-deps.fish +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (C) 2016-2017 Vincent Ambo -# -# This file is part of Kontemplate. -# -# Kontemplate is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. - -#!/usr/bin/env fish - -function get_remote_master - git ls-remote "$argv[1]" | \ - grep 'refs/heads/master' | \ - awk '{print $1}' -end - -function list_deps - grep '"git"' -B2 kontemplate.frm | \ - grep -P -o '(?<=silo: ")https://.+(?=")' -end - -function diff_dep - set -l current (grep -B1 "$argv[1]" kontemplate.frm | grep -P -o '(?<=hash: ").+(?=")') - set -l remote (get_remote_master "$argv[1]") - - if [ $current != $remote ] - echo "$argv[1]" - echo -e "current:\t$current" - echo -e "remote:\t\t$remote\n" - else - echo -e "$argv[1] up to date\n" - end -end - -for dep in (list_deps) - diff_dep $dep -end diff --git a/kontemplate.frm b/kontemplate.frm deleted file mode 100644 index 195a83afd..000000000 --- a/kontemplate.frm +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (C) 2016-2017 Vincent Ambo -# -# This file is part of Kontemplate. -# -# Kontemplate is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. - -inputs: - "/": # https://github.com/tklx/base - type: "tar" - hash: "9nkvYhmJHaeK_Agc3Lm5rg444dSLWDp0Pri-KilHiX3A9Pt4TaQ7RxOj5qMSs6XT" - silo: "https://github.com/tklx/base/releases/download/0.1.1/rootfs.tar.xz" - "/opt": - type: "tar" - hash: "gi0Kpb-VH3TK0UBX6YmpuKsrMAUlxicPrY2YvXPo9sBQm_NsD_hKrn7pmc95zrmM" - silo: "https://storage.googleapis.com/golang/go1.8.1.linux-amd64.tar.gz" - # Kontemplate dependencies! - "/go/src/github.com/polydawn/meep": - type: "git" - hash: "eaf1db2168fe380b4da17a35f0adddb5ae15a651" - silo: "https://github.com/polydawn/meep" - "/go/src/github.com/Masterminds/sprig": - type: "git" - hash: "e039e20e500c2c025d9145be375e27cf42a94174" - silo: "https://github.com/Masterminds/sprig" - "/go/src/github.com/ghodss/yaml": - type: "git" - hash: "0ca9ea5df5451ffdf184b4428c902747c2c11cd7" - silo: "https://github.com/ghodss/yaml" - "/go/src/gopkg.in/yaml.v2": - type: "git" - hash: "eb3733d160e74a9c7e442f435eb3bea458e1d19f" - silo: "https://gopkg.in/yaml.v2" - "/go/src/gopkg.in/alecthomas/kingpin.v2": - type: "git" - hash: "1087e65c9441605df944fb12c33f0fe7072d18ca" - silo: "https://gopkg.in/alecthomas/kingpin.v2" - "/go/src/github.com/alecthomas/template": - type: "git" - hash: "a0175ee3bccc567396460bf5acd36800cb10c49c" - silo: "https://github.com/alecthomas/template" - "/go/src/github.com/alecthomas/units": - type: "git" - hash: "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a" - silo: "https://github.com/alecthomas/units" - "/go/src/github.com/Masterminds/semver": - type: "git" - hash: "517734cc7d6470c0d07130e40fd40bdeb9bcd3fd" - silo: "https://github.com/Masterminds/semver" - "/go/src/github.com/aokoli/goutils": - type: "git" - hash: "3391d3790d23d03408670993e957e8f408993c34" - silo: "https://github.com/aokoli/goutils" - "/go/src/github.com/huandu/xstrings": - type: "git" - hash: "3959339b333561bf62a38b424fd41517c2c90f40" - silo: "https://github.com/huandu/xstrings" - "/go/src/github.com/imdario/mergo": - type: "git" - hash: "d806ba8c21777d504a2090a2ca4913c750dd3a33" - silo: "https://github.com/imdario/mergo" - "/go/src/github.com/satori/go.uuid": - type: "git" - hash: "5bf94b69c6b68ee1b541973bb8e1144db23a194b" - silo: "https://github.com/satori/go.uuid" - "/go/src/golang.org/x/crypto": - type: "git" - hash: "ab89591268e0c8b748cbe4047b00197516011af5" - silo: "https://go.googlesource.com/crypto" -action: - policy: governor - command: - - "/bin/sh" - - "-e" - - "-c" - - | - export PATH="/opt/go/bin:$PATH" - export GOROOT=/opt/go - export GOPATH=/go - echo 'nameserver 8.8.8.8' > /etc/resolv.conf - apt-get update && apt-get install -y git ca-certificates - mkdir -p /go/src/github.com/tazjin - git clone --single-branch --branch v1.3.0 https://github.com/tazjin/kontemplate /go/src/github.com/tazjin/kontemplate - cd /go/src/github.com/tazjin/kontemplate - ./build-release.sh build -outputs: - "release": - type: "dir" - mount: "/go/src/github.com/tazjin/kontemplate/release" - silo: "file://release" From a1d9d8b199a3c9402c34ae1a12f85495d3c1cfd2 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Fri, 9 Mar 2018 12:57:07 +0100 Subject: [PATCH 109/166] feat(build): Build both derivations on Travis --- .travis.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.travis.yml b/.travis.yml index b49a7b383..a97db2493 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,2 +1,10 @@ --- language: nix +script: + # Build the derivation targeting NixOS. + # This derivation executes tests. + - nix-build default.nix + + # Build the release derivation with statically linked executables + # for multiple operating systems. + - nix-build release.nix From 850fdcf3e0270db885f661ae59225db92143c3f2 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Fri, 9 Mar 2018 14:14:17 +0100 Subject: [PATCH 110/166] feat(build): Pin nixpkgs used for release build Pin the nixpkgs-commit used for building the Kontemplate release to a specific commit. Kontemplate builds should now be fully repeatable (and most likely reproducible!) on any machine running Nix. --- release.nix | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/release.nix b/release.nix index fc047f26a..fb80a0e42 100644 --- a/release.nix +++ b/release.nix @@ -10,13 +10,13 @@ # This file is the Nix derivation used to build release binaries for # several different architectures and operating systems. -{ pkgs ? import {} }: - -with pkgs; let buildGo110Package = - callPackage { - go = go_1_10; -}; -in buildGo110Package rec { +let pkgs = import ((import {}).fetchFromGitHub { + owner = "NixOS"; + repo = "nixpkgs"; + rev = "1bc5bf4beb759e563ffc7a8a3067f10a00b45a7d"; + sha256 = "00gd96p7yz3rgpjjkizp397y2syfc272yvwxqixbjd1qdshbizmj"; +}) {}; +in with pkgs; buildGoPackage rec { name = "kontemplate-${version}"; version = "master"; src = ./.; From bafb792339b5898a0e6b6219ad54b5f501d727c2 Mon Sep 17 00:00:00 2001 From: Niklas Wik Date: Tue, 9 Jan 2018 15:12:50 +0200 Subject: [PATCH 111/166] feat(templater): Added support for file include Adds a 'fileContent' template function to insert the literal contents of a file specified in the template. Signed-off-by: Niklas Wik --- docs/templates.md | 5 +++-- example/some-api/some-api.yaml | 2 ++ templater/fromfile.go | 24 ++++++++++++++++++++++++ templater/templater.go | 1 + 4 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 templater/fromfile.go diff --git a/docs/templates.md b/docs/templates.md index 8bcc2c7f5..d9ff0fd12 100644 --- a/docs/templates.md +++ b/docs/templates.md @@ -72,10 +72,11 @@ right. Some template functions come from Go's standard library and are listed in the [Go documentation][]. In addition the functions declared by [sprig][] are -available in kontemplate, as well as two custom functions: +available in kontemplate, as well as three custom functions: `json`: Encodes any supplied data structure as JSON. -`passLookup`: Looks up the supplied key in [pass][] +`passLookup`: Looks up the supplied key in [pass][]. +`fromFile`: Insert the contents of the given file from the resource set folder. ## Examples: diff --git a/example/some-api/some-api.yaml b/example/some-api/some-api.yaml index 57ab7c652..44b615417 100644 --- a/example/some-api/some-api.yaml +++ b/example/some-api/some-api.yaml @@ -25,6 +25,8 @@ spec: value: {{ .importantFeature }} - name: SOME_GLOBAL_VAR value: {{ .globalVar }} + - name: FILE_VAR + value: {{ fileContent "some-api/filevar.txt" }} --- apiVersion: v1 kind: Service diff --git a/templater/fromfile.go b/templater/fromfile.go new file mode 100644 index 000000000..f4f1e79cb --- /dev/null +++ b/templater/fromfile.go @@ -0,0 +1,24 @@ +// Copyright (C) 2017 Niklas Wik +// +// This file is part of Kontemplate. +// +// Kontemplate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +package templater + +import ( + "io/ioutil" +) + +//GetFromFile returns file content as string +func GetFromFile(file string) (string, error) { + + data, err := ioutil.ReadFile(file) + if err != nil { + return "", err + } + return string(data), nil +} diff --git a/templater/templater.go b/templater/templater.go index 7fb85e8b2..5bdb2f2e3 100644 --- a/templater/templater.go +++ b/templater/templater.go @@ -193,6 +193,7 @@ func templateFuncs() template.FuncMap { } m["passLookup"] = GetFromPass m["lookupIPAddr"] = GetIPsFromDNS + m["fileContent"] = GetFromFile return m } From b8722ce83bce727d88a61cecff3343d3046e75f7 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Fri, 9 Mar 2018 14:49:33 +0100 Subject: [PATCH 112/166] refactor(templater): Pass resource set path to insertFile function This is actually several refactors in one: * rename "fileContent" function to "insertFile" * pass the resource set path to the "insetFile" function * update docs and example with a pipeline including indentation adjustments for the inserted file --- docs/templates.md | 7 ++++--- example/some-api/some-api.yaml | 15 ++++++++++++--- templater/fromfile.go | 24 ------------------------ templater/templater.go | 13 ++++++++++--- 4 files changed, 26 insertions(+), 33 deletions(-) delete mode 100644 templater/fromfile.go diff --git a/docs/templates.md b/docs/templates.md index d9ff0fd12..11488f573 100644 --- a/docs/templates.md +++ b/docs/templates.md @@ -74,9 +74,10 @@ Some template functions come from Go's standard library and are listed in the [Go documentation][]. In addition the functions declared by [sprig][] are available in kontemplate, as well as three custom functions: -`json`: Encodes any supplied data structure as JSON. -`passLookup`: Looks up the supplied key in [pass][]. -`fromFile`: Insert the contents of the given file from the resource set folder. +* `json`: Encodes any supplied data structure as JSON. +* `passLookup`: Looks up the supplied key in [pass][]. +* `insertFile`: Insert the contents of the given file in the resource + set folder as a string. ## Examples: diff --git a/example/some-api/some-api.yaml b/example/some-api/some-api.yaml index 44b615417..887eb69a9 100644 --- a/example/some-api/some-api.yaml +++ b/example/some-api/some-api.yaml @@ -4,7 +4,18 @@ kind: Secret metadata: name: secret-certificate data: - cert.pem: {{ passLookup "my/secret/certificate" | b64enc }} + cert.pem: { passLookup "my/secret/certificate" | b64enc }} +--- +apiVersion: extensions/v1beta1 +kind: ConfigMap +metadata: + name: some-config +data: + # The content of the example configuration file is templated in here + # by the 'insertFile' function and indented for YAML-compatibility + # with the 'indent' function: + some.cfg: | +{{ insertFile "some.cfg" | indent 4 }} --- apiVersion: extensions/v1beta1 kind: Deployment @@ -25,8 +36,6 @@ spec: value: {{ .importantFeature }} - name: SOME_GLOBAL_VAR value: {{ .globalVar }} - - name: FILE_VAR - value: {{ fileContent "some-api/filevar.txt" }} --- apiVersion: v1 kind: Service diff --git a/templater/fromfile.go b/templater/fromfile.go deleted file mode 100644 index f4f1e79cb..000000000 --- a/templater/fromfile.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (C) 2017 Niklas Wik -// -// This file is part of Kontemplate. -// -// Kontemplate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -package templater - -import ( - "io/ioutil" -) - -//GetFromFile returns file content as string -func GetFromFile(file string) (string, error) { - - data, err := ioutil.ReadFile(file) - if err != nil { - return "", err - } - return string(data), nil -} diff --git a/templater/templater.go b/templater/templater.go index 5bdb2f2e3..fd514a5ac 100644 --- a/templater/templater.go +++ b/templater/templater.go @@ -119,7 +119,7 @@ func processFiles(c *context.Context, rs *context.ResourceSet, rp string, files } func templateFile(c *context.Context, rs *context.ResourceSet, filename string) (string, error) { - tpl, err := template.New(path.Base(filename)).Funcs(templateFuncs()).Option(failOnMissingKeys).ParseFiles(filename) + tpl, err := template.New(path.Base(filename)).Funcs(templateFuncs(rs)).Option(failOnMissingKeys).ParseFiles(filename) if err != nil { return "", meep.New( @@ -185,7 +185,7 @@ func matchesResourceSet(s *[]string, rs *context.ResourceSet) bool { return false } -func templateFuncs() template.FuncMap { +func templateFuncs(rs *context.ResourceSet) template.FuncMap { m := sprig.TxtFuncMap() m["json"] = func(data interface{}) string { b, _ := json.Marshal(data) @@ -193,7 +193,14 @@ func templateFuncs() template.FuncMap { } m["passLookup"] = GetFromPass m["lookupIPAddr"] = GetIPsFromDNS - m["fileContent"] = GetFromFile + m["insertFile"] = func(file string) (string, error) { + data, err := ioutil.ReadFile(path.Join(rs.Path, file)) + if err != nil { + return "", err + } + + return string(data), nil + } return m } From 3aa2cb8d3efe04eda8cf1d0d2dcb5ad8ddf21147 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Fri, 9 Mar 2018 15:17:54 +0100 Subject: [PATCH 113/166] refactor: Remove old error handling library MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removes the old error handling library and switches to plain fmt.Errorf calls. There are several reasons for this: * There are no useful types or handling here anyways, so output format is the only priority. * Users don't care about getting stacktraces. * My emotional wellbeing. Fin de siècle. --- context/context.go | 17 +++++------------ deps.nix | 9 --------- example/some-api/some-api.yaml | 2 +- main.go | 9 ++------- templater/dns.go | 12 +----------- templater/pass.go | 13 +------------ templater/templater.go | 29 +++-------------------------- 7 files changed, 13 insertions(+), 78 deletions(-) diff --git a/context/context.go b/context/context.go index a6d181e7b..9996f31fa 100644 --- a/context/context.go +++ b/context/context.go @@ -10,9 +10,9 @@ package context import ( + "fmt" "path" - "github.com/polydawn/meep" "github.com/tazjin/kontemplate/util" ) @@ -51,9 +51,8 @@ type Context struct { BaseDir string } -type ContextLoadingError struct { - meep.AllTraits - Filename string +func contextLoadingError(filename string, cause error) error { + return fmt.Errorf("Context loading failed on file %s due to: \n%v", filename, cause) } // Attempt to load and deserialise a Context from the specified file. @@ -62,10 +61,7 @@ func LoadContextFromFile(filename string) (*Context, error) { err := util.LoadJsonOrYaml(filename, &c) if err != nil { - return nil, meep.New( - &ContextLoadingError{Filename: filename}, - meep.Cause(err), - ) + return nil, contextLoadingError(filename, err) } c.ResourceSets = flattenPrepareResourceSetPaths(&c.ResourceSets) @@ -74,10 +70,7 @@ func LoadContextFromFile(filename string) (*Context, error) { err = c.loadImportedVariables() if err != nil { - return nil, meep.New( - &ContextLoadingError{Filename: filename}, - meep.Cause(err), - ) + return nil, contextLoadingError(filename, err) } return &c, nil diff --git a/deps.nix b/deps.nix index 811654f34..db2692a79 100644 --- a/deps.nix +++ b/deps.nix @@ -72,15 +72,6 @@ sha256 = "12n3lfbfxvnag916c6dpxl48j29s482zwsqjc6wk4vb68qbz2nl3"; }; } - { - goPackagePath = "github.com/polydawn/meep"; - fetch = { - type = "git"; - url = "https://github.com/polydawn/meep"; - rev = "eaf1db2168fe380b4da17a35f0adddb5ae15a651"; - sha256 = "12n134fb2imnj67xkbznzm0gqkg36hdxwr960y91qb5s2q2krxir"; - }; - } { goPackagePath = "github.com/satori/go.uuid"; fetch = { diff --git a/example/some-api/some-api.yaml b/example/some-api/some-api.yaml index 887eb69a9..f0188f9db 100644 --- a/example/some-api/some-api.yaml +++ b/example/some-api/some-api.yaml @@ -4,7 +4,7 @@ kind: Secret metadata: name: secret-certificate data: - cert.pem: { passLookup "my/secret/certificate" | b64enc }} + cert.pem: {{ passLookup "my/secret/certificate" | b64enc }} --- apiVersion: extensions/v1beta1 kind: ConfigMap diff --git a/main.go b/main.go index 19dd74d0c..8d432ff01 100644 --- a/main.go +++ b/main.go @@ -20,7 +20,6 @@ import ( "os" "os/exec" - "github.com/polydawn/meep" "github.com/tazjin/kontemplate/context" "github.com/tazjin/kontemplate/templater" "gopkg.in/alecthomas/kingpin.v2" @@ -31,10 +30,6 @@ const version string = "1.3.0" // This variable will be initialised by the Go linker during the builder var gitHash string -type KubeCtlError struct { - meep.AllTraits -} - var ( app = kingpin.New("kontemplate", "simple Kubernetes resource templating") @@ -180,14 +175,14 @@ func runKubectlWithResources(c *context.Context, kubectlArgs *[]string, resource stdin, err := kubectl.StdinPipe() if err != nil { - return meep.New(&KubeCtlError{}, meep.Cause(err)) + return fmt.Errorf("kubectl error: %v", err) } kubectl.Stdout = os.Stdout kubectl.Stderr = os.Stderr if err = kubectl.Start(); err != nil { - return meep.New(&KubeCtlError{}, meep.Cause(err)) + return fmt.Errorf("kubectl error: %v", err) } for _, r := range rs.Resources { diff --git a/templater/dns.go b/templater/dns.go index 096ceb1df..e6b210680 100644 --- a/templater/dns.go +++ b/templater/dns.go @@ -14,26 +14,16 @@ package templater import ( "fmt" - "github.com/polydawn/meep" "net" "os" ) -type DNSError struct { - meep.TraitAutodescribing - meep.TraitCausable - Output string -} - func GetIPsFromDNS(host string) ([]interface{}, error) { fmt.Fprintf(os.Stderr, "Attempting to look up IP for %s in DNS\n", host) ips, err := net.LookupIP(host) if err != nil { - return nil, meep.New( - &DNSError{Output: "IP address lookup failed"}, - meep.Cause(err), - ) + return nil, fmt.Errorf("IP address lookup failed: %v", err) } var result []interface{} = make([]interface{}, len(ips)) diff --git a/templater/pass.go b/templater/pass.go index ecddff740..53d8f2f9b 100644 --- a/templater/pass.go +++ b/templater/pass.go @@ -16,27 +16,16 @@ import ( "fmt" "os" "os/exec" - - "github.com/polydawn/meep" "strings" ) -type PassError struct { - meep.TraitAutodescribing - meep.TraitCausable - Output string -} - func GetFromPass(key string) (string, error) { fmt.Fprintf(os.Stderr, "Attempting to look up %s in pass\n", key) pass := exec.Command("pass", "show", key) output, err := pass.CombinedOutput() if err != nil { - return "", meep.New( - &PassError{Output: string(output)}, - meep.Cause(err), - ) + return "", fmt.Errorf("Pass lookup failed: %s (%v)", output, err) } trimmed := strings.TrimSpace(string(output)) diff --git a/templater/templater.go b/templater/templater.go index fd514a5ac..bfd2af1f6 100644 --- a/templater/templater.go +++ b/templater/templater.go @@ -20,26 +20,12 @@ import ( "text/template" "github.com/Masterminds/sprig" - "github.com/polydawn/meep" "github.com/tazjin/kontemplate/context" "github.com/tazjin/kontemplate/util" ) const failOnMissingKeys string = "missingkey=error" -// Error that is caused by non-existent template files being specified -type TemplateNotFoundError struct { - meep.AllTraits - Name string - Path string -} - -// Error that is caused during templating, e.g. required value being absent or invalid template format -type TemplatingError struct { - meep.TraitAutodescribing - meep.TraitCausable -} - type RenderedResource struct { Filename string Rendered string @@ -83,10 +69,7 @@ func processResourceSet(c *context.Context, rs *context.ResourceSet) (*RenderedR resources, err := processFiles(c, rs, rp, files) if err != nil { - return nil, meep.New( - &TemplateNotFoundError{Name: rs.Name, Path: rs.Path}, - meep.Cause(err), - ) + return nil, err } return &RenderedResourceSet{ @@ -122,10 +105,7 @@ func templateFile(c *context.Context, rs *context.ResourceSet, filename string) tpl, err := template.New(path.Base(filename)).Funcs(templateFuncs(rs)).Option(failOnMissingKeys).ParseFiles(filename) if err != nil { - return "", meep.New( - &TemplateNotFoundError{Name: filename}, - meep.Cause(err), - ) + return "", fmt.Errorf("Template %s not found: %v", filename, err) } var b bytes.Buffer @@ -135,10 +115,7 @@ func templateFile(c *context.Context, rs *context.ResourceSet, filename string) err = tpl.Execute(&b, rs.Values) if err != nil { - return "", meep.New( - &TemplatingError{}, - meep.Cause(err), - ) + return "", fmt.Errorf("Error while templating %s: %v", filename, err) } return b.String(), nil From 03838ff31b10f53dd73ab4d966216f2c9212b271 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sat, 17 Mar 2018 21:00:38 +0100 Subject: [PATCH 114/166] fix(example): Add missing file to example folder --- example/some-api/some.cfg | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 example/some-api/some.cfg diff --git a/example/some-api/some.cfg b/example/some-api/some.cfg new file mode 100644 index 000000000..733d5e167 --- /dev/null +++ b/example/some-api/some.cfg @@ -0,0 +1,4 @@ +{ + "something": 1542, + "other-thing": "да" +} From 1f373caba04109841f0913465cf47b76e10b8584 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sat, 17 Mar 2018 21:48:21 +0100 Subject: [PATCH 115/166] chore: Bump version to 1.4.0 --- build-release.sh | 2 +- main.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build-release.sh b/build-release.sh index 84ad4fb0b..c2ca64d33 100755 --- a/build-release.sh +++ b/build-release.sh @@ -12,7 +12,7 @@ set -ueo pipefail readonly GIT_HASH="$(git rev-parse --short HEAD)" readonly LDFLAGS="-X main.gitHash=${GIT_HASH} -w -s" -readonly VERSION="1.3.0-${GIT_HASH}" +readonly VERSION="1.4.0-${GIT_HASH}" function binary-name() { local os="${1}" diff --git a/main.go b/main.go index 8d432ff01..624af0f2c 100644 --- a/main.go +++ b/main.go @@ -25,7 +25,7 @@ import ( "gopkg.in/alecthomas/kingpin.v2" ) -const version string = "1.3.0" +const version string = "1.4.0" // This variable will be initialised by the Go linker during the builder var gitHash string From e2effbb28919177f19cbf64eb361f9792fd7a832 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sat, 17 Mar 2018 23:13:36 +0100 Subject: [PATCH 116/166] chore(image): Bump image version to 1.4 * upgrade Alpine release * upgrade kubectl to 1.9.4 * upgrade Kontemplate to 1.4.0 --- image/Dockerfile | 6 +++--- image/hashes | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/image/Dockerfile b/image/Dockerfile index b2e8d5fba..96cd77c4c 100644 --- a/image/Dockerfile +++ b/image/Dockerfile @@ -1,8 +1,8 @@ -FROM alpine:3.6 +FROM alpine:3.7 ADD hashes /root/hashes -ADD https://storage.googleapis.com/kubernetes-release/release/v1.7.4/bin/linux/amd64/kubectl /usr/bin/kubectl -ADD https://github.com/tazjin/kontemplate/releases/download/v1.3.0/kontemplate-1.3.0-98daa6b-linux-amd64.tar.gz /tmp/kontemplate.tar.gz +ADD https://storage.googleapis.com/kubernetes-release/release/v1.9.4/bin/linux/amd64/kubectl /usr/bin/kubectl +ADD https://github.com/tazjin/kontemplate/releases/download/v1.4.0/kontemplate-1.4.0-1f373ca-linux-amd64.tar.gz /tmp/kontemplate.tar.gz # Pass release version is 1.7.1 ADD https://raw.githubusercontent.com/zx2c4/password-store/38ec1c72e29c872ec0cdde82f75490640d4019bf/src/password-store.sh /usr/bin/pass diff --git a/image/hashes b/image/hashes index 5cf26e4dc..073d2a787 100644 --- a/image/hashes +++ b/image/hashes @@ -1,2 +1,2 @@ -c0353f77c62047dfbfd6dafca91e26e91b18395949e4208b422c8cf3d196732d /tmp/kontemplate.tar.gz -fb5a961a6ba55093691228d71e64bffd57ada98226f195c409ce297d1cb5086a /usr/bin/kubectl +fbf7b8f60e8090f9b615f893b4cc18583c77cd5a0092b46d0ceb7a78f99bab74 /tmp/kontemplate.tar.gz +1649bcda95e1662da0616a5314261285fa4154d769dc4148cc339d859b564c39 /usr/bin/kubectl From 867f40307e9038406625bccf8047f627dd7cb148 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sat, 17 Mar 2018 23:16:45 +0100 Subject: [PATCH 117/166] chore(brew): Bump Homebrew formula to 1.4.0 --- kontemplate.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kontemplate.rb b/kontemplate.rb index 2b0bcbfa8..2900b3deb 100644 --- a/kontemplate.rb +++ b/kontemplate.rb @@ -3,9 +3,9 @@ class Kontemplate < Formula desc "Kontemplate - Extremely simple Kubernetes resource templates" homepage "https://github.com/tazjin/kontemplate" - url "https://github.com/tazjin/kontemplate/releases/download/v1.3.0/kontemplate-1.3.0-98daa6b-darwin-amd64.tar.gz" - sha256 "4372e2c0f1249aa43f67e664335c20748d0b394ada1fcceefa3ae786e839ee47" - version "1.3.0-98daa6b" + url "https://github.com/tazjin/kontemplate/releases/download/v1.4.0/kontemplate-1.4.0-1f373ca-darwin-amd64.tar.gz" + sha256 "b034cfec6019c973ea7dd30297e1c3434c595e29106e557698ca24f3a4659e5a" + version "1.4.0-1f373ca" def install bin.install "kontemplate" From e1c2c19330911d9e35b03b8b08681e0a7cd0cb70 Mon Sep 17 00:00:00 2001 From: noqcks Date: Thu, 29 Mar 2018 13:12:46 -0400 Subject: [PATCH 118/166] feat(templater) Add a template function to insert surrounding repo's Git hash A template function has been added that allows one to template the Git hash of the surrounding repo. This is useful to be able to inspect the deployment revision of an object in Kubernetes. --- docs/templates.md | 4 ++++ templater/templater.go | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/docs/templates.md b/docs/templates.md index 11488f573..6c44035d0 100644 --- a/docs/templates.md +++ b/docs/templates.md @@ -75,6 +75,7 @@ Some template functions come from Go's standard library and are listed in the available in kontemplate, as well as three custom functions: * `json`: Encodes any supplied data structure as JSON. +* `gitHEAD`: Retrieves the commit hash at Git `HEAD`. * `passLookup`: Looks up the supplied key in [pass][]. * `insertFile`: Insert the contents of the given file in the resource set folder as a string. @@ -96,6 +97,9 @@ certKeyPath: my-website/cert-key {{ .certKeyPath | passLookup }} -> Returns content of 'my-website/cert-key' from pass + +{{ gitHEAD }} +-> Returns the Git commit hash at HEAD. ``` ## Conditionals & ranges diff --git a/templater/templater.go b/templater/templater.go index bfd2af1f6..89cc1cd73 100644 --- a/templater/templater.go +++ b/templater/templater.go @@ -15,6 +15,7 @@ import ( "fmt" "io/ioutil" "os" + "os/exec" "path" "strings" "text/template" @@ -169,6 +170,14 @@ func templateFuncs(rs *context.ResourceSet) template.FuncMap { return string(b) } m["passLookup"] = GetFromPass + m["gitHEAD"] = func() (string, error) { + out, err := exec.Command("sh", "-c", "git rev-parse HEAD").Output() + if err != nil { + return "", err + } + output := strings.TrimSpace(string(out)) + return output, nil + } m["lookupIPAddr"] = GetIPsFromDNS m["insertFile"] = func(file string) (string, error) { data, err := ioutil.ReadFile(path.Join(rs.Path, file)) From ac445d5235a5a5d543d4926290e7eaef6a3b40dd Mon Sep 17 00:00:00 2001 From: noqcks Date: Thu, 29 Mar 2018 13:36:39 -0400 Subject: [PATCH 119/166] refactor(templater) Refactor templator to use exec.Command directly instead of executing in sh --- templater/templater.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templater/templater.go b/templater/templater.go index 89cc1cd73..3c8d2ccdb 100644 --- a/templater/templater.go +++ b/templater/templater.go @@ -171,7 +171,7 @@ func templateFuncs(rs *context.ResourceSet) template.FuncMap { } m["passLookup"] = GetFromPass m["gitHEAD"] = func() (string, error) { - out, err := exec.Command("sh", "-c", "git rev-parse HEAD").Output() + out, err := exec.Command("git", "rev-parse", "HEAD").Output() if err != nil { return "", err } From 3ea3bed7acda9b365c45d268342fb52e236f95ed Mon Sep 17 00:00:00 2001 From: noqcks Date: Thu, 3 May 2018 13:50:57 -0400 Subject: [PATCH 120/166] fix(templater): add baseDir to gitHead cmd so that directory is overwritten This makes it so that when gitHead is called in a template the git hash that is returned is the hash of the folder containing the template, not the hash of the folder where kontemplate is called. --- templater/templater.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/templater/templater.go b/templater/templater.go index 3c8d2ccdb..c0eff234f 100644 --- a/templater/templater.go +++ b/templater/templater.go @@ -103,7 +103,7 @@ func processFiles(c *context.Context, rs *context.ResourceSet, rp string, files } func templateFile(c *context.Context, rs *context.ResourceSet, filename string) (string, error) { - tpl, err := template.New(path.Base(filename)).Funcs(templateFuncs(rs)).Option(failOnMissingKeys).ParseFiles(filename) + tpl, err := template.New(path.Base(filename)).Funcs(templateFuncs(c, rs)).Option(failOnMissingKeys).ParseFiles(filename) if err != nil { return "", fmt.Errorf("Template %s not found: %v", filename, err) @@ -163,7 +163,7 @@ func matchesResourceSet(s *[]string, rs *context.ResourceSet) bool { return false } -func templateFuncs(rs *context.ResourceSet) template.FuncMap { +func templateFuncs(c *context.Context, rs *context.ResourceSet) template.FuncMap { m := sprig.TxtFuncMap() m["json"] = func(data interface{}) string { b, _ := json.Marshal(data) @@ -171,13 +171,13 @@ func templateFuncs(rs *context.ResourceSet) template.FuncMap { } m["passLookup"] = GetFromPass m["gitHEAD"] = func() (string, error) { - out, err := exec.Command("git", "rev-parse", "HEAD").Output() - if err != nil { - return "", err - } - output := strings.TrimSpace(string(out)) - return output, nil + out, err := exec.Command("git", "-C", c.BaseDir, "rev-parse", "HEAD").Output() + if err != nil { + return "", err } + output := strings.TrimSpace(string(out)) + return output, nil + } m["lookupIPAddr"] = GetIPsFromDNS m["insertFile"] = func(file string) (string, error) { data, err := ioutil.ReadFile(path.Join(rs.Path, file)) From 84dcc294bfe0c5037efa1cf1e98aaf0d2727fbb7 Mon Sep 17 00:00:00 2001 From: Niklas Wik Date: Mon, 23 Apr 2018 08:12:38 +0300 Subject: [PATCH 121/166] feat(main): Support output directories in template function. This introduces a new command line flag `--output` (or `-o` for short) which makes it possible to template all specified resource sets into a folder (instead of to stdout) when using `kontemplate template`. --- main.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/main.go b/main.go index 624af0f2c..7d0b33aa7 100644 --- a/main.go +++ b/main.go @@ -38,8 +38,9 @@ var ( excludes = app.Flag("exclude", "Resource sets to exclude explicitly").Short('e').Strings() // Commands - template = app.Command("template", "Template resource sets and print them") - templateFile = template.Arg("file", "Cluster configuration file to use").Required().String() + template = app.Command("template", "Template resource sets and print them") + templateFile = template.Arg("file", "Cluster configuration file to use").Required().String() + templateOutputDir = template.Flag("output", "Output directory in which to save templated files instead of printing them").Short('o').String() apply = app.Command("apply", "Template resources and pass to 'kubectl apply'") applyFile = apply.Arg("file", "Cluster configuration file to use").Required().String() @@ -100,7 +101,13 @@ func templateCommand() { for _, r := range rs.Resources { fmt.Fprintf(os.Stderr, "Rendered file %s/%s:\n", rs.Name, r.Filename) - fmt.Println(r.Rendered) + if *templateOutputDir != "" { + os.MkdirAll(*templateOutputDir, 0777) + f, _ := os.Create(*templateOutputDir + "/" + r.Filename) + fmt.Fprintf(f, r.Rendered) + } else { + fmt.Println(r.Rendered) + } } } } From b8c32640196618b697b3c6ca58d416097f87dd3d Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Tue, 8 May 2018 11:15:57 +0200 Subject: [PATCH 122/166] fix(main): Handle errors & logic when templating to directory This does several changes to the new "template to directory" feature introduced in the previous commit: 1. Errors are now "handled". In classic Go-style, it is of course all too easy to do absolutely nothing with errors, but we can't have that. I'm onto you, Renee French's husband! 2. Resource sets containing similarly named files are now templated correctly by prepending the resource set name to the filename. 3. In the same vein as the previous point, nested resource sets are now also output correctly by replacing slashes (`/`) with dashes (`-`) to guarantee that the output files are a flat list. Some minor cosmetic fixes have also been applied. --- main.go | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/main.go b/main.go index 7d0b33aa7..bd5d4cbaf 100644 --- a/main.go +++ b/main.go @@ -19,6 +19,7 @@ import ( "fmt" "os" "os/exec" + "strings" "github.com/tazjin/kontemplate/context" "github.com/tazjin/kontemplate/templater" @@ -99,19 +100,45 @@ func templateCommand() { continue } - for _, r := range rs.Resources { - fmt.Fprintf(os.Stderr, "Rendered file %s/%s:\n", rs.Name, r.Filename) - if *templateOutputDir != "" { - os.MkdirAll(*templateOutputDir, 0777) - f, _ := os.Create(*templateOutputDir + "/" + r.Filename) - fmt.Fprintf(f, r.Rendered) - } else { + if *templateOutputDir != "" { + templateIntoDirectory(templateOutputDir, rs) + } else { + for _, r := range rs.Resources { + fmt.Fprintf(os.Stderr, "Rendered file %s/%s:\n", rs.Name, r.Filename) fmt.Println(r.Rendered) } } } } +func templateIntoDirectory(outputDir *string, rs templater.RenderedResourceSet) { + // Attempt to create the output directory if it does not + // already exist: + if err := os.MkdirAll(*templateOutputDir, 0775); err != nil { + app.Fatalf("Could not create output directory: %v\n", err) + } + + // Nested resource sets may contain slashes in their names. + // These are replaced with dashes for the purpose of writing a + // flat list of output files: + setName := strings.Replace(rs.Name, "/", "-", -1) + + for _, r := range rs.Resources { + filename := fmt.Sprintf("%s/%s-%s", *templateOutputDir, setName, r.Filename) + fmt.Fprintf(os.Stderr, "Writing file %s\n", filename) + + file, err := os.Create(filename) + if err != nil { + app.Fatalf("Could not create file %s: %v\n", filename, err) + } + + _, err = fmt.Fprintf(file, r.Rendered) + if err != nil { + app.Fatalf("Error writing file %s: %v\n", filename, err) + } + } +} + func applyCommand() { ctx, resources := loadContextAndResources(applyFile) From ea297abe1d841967cc43599e07e7c032653264ee Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Tue, 8 May 2018 11:26:01 +0200 Subject: [PATCH 123/166] fix(main): Use 'app.Fatalf' for fatal kubectl errors --- main.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/main.go b/main.go index bd5d4cbaf..b0117876c 100644 --- a/main.go +++ b/main.go @@ -234,6 +234,5 @@ func runKubectlWithResources(c *context.Context, kubectlArgs *[]string, resource } func failWithKubectlError(err error) { - fmt.Fprintf(os.Stderr, "Kubectl error: %v\n", err) - os.Exit(1) + app.Fatalf("Kubectl error: %v\n", err) } From c68518d6c9cceb4e8585cb512b8818ce252ceac3 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Tue, 8 May 2018 11:27:35 +0200 Subject: [PATCH 124/166] chore: Bump version to 1.5.0 --- build-release.sh | 2 +- main.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build-release.sh b/build-release.sh index c2ca64d33..97fcf75d8 100755 --- a/build-release.sh +++ b/build-release.sh @@ -12,7 +12,7 @@ set -ueo pipefail readonly GIT_HASH="$(git rev-parse --short HEAD)" readonly LDFLAGS="-X main.gitHash=${GIT_HASH} -w -s" -readonly VERSION="1.4.0-${GIT_HASH}" +readonly VERSION="1.5.0-${GIT_HASH}" function binary-name() { local os="${1}" diff --git a/main.go b/main.go index b0117876c..13aabd248 100644 --- a/main.go +++ b/main.go @@ -26,7 +26,7 @@ import ( "gopkg.in/alecthomas/kingpin.v2" ) -const version string = "1.4.0" +const version string = "1.5.0" // This variable will be initialised by the Go linker during the builder var gitHash string From 75c92172afe461700a347551e6812e8cfd77ff4a Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Tue, 8 May 2018 11:44:10 +0200 Subject: [PATCH 125/166] chore(brew): Update Homebrew formula for v1.5.0 --- kontemplate.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kontemplate.rb b/kontemplate.rb index 2900b3deb..8d14cec41 100644 --- a/kontemplate.rb +++ b/kontemplate.rb @@ -3,9 +3,9 @@ class Kontemplate < Formula desc "Kontemplate - Extremely simple Kubernetes resource templates" homepage "https://github.com/tazjin/kontemplate" - url "https://github.com/tazjin/kontemplate/releases/download/v1.4.0/kontemplate-1.4.0-1f373ca-darwin-amd64.tar.gz" - sha256 "b034cfec6019c973ea7dd30297e1c3434c595e29106e557698ca24f3a4659e5a" - version "1.4.0-1f373ca" + url "https://github.com/tazjin/kontemplate/releases/download/v1.5.0/kontemplate-1.5.0-c68518d-darwin-amd64.tar.gz" + sha256 "61cd9ad3e28f52260458b707fd3120afa53e0610213ddd0ab03f489439f6b8a9" + version "1.5.0-c68518d" def install bin.install "kontemplate" From fa5c12a9a21dc4b0180468442556ab3fb1feb5a1 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Tue, 8 May 2018 11:49:29 +0200 Subject: [PATCH 126/166] chore(image): Bump version to 1.5.0 * Kontemplate 1.5.0 * kubectl 1.10.2 --- image/Dockerfile | 4 ++-- image/hashes | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/image/Dockerfile b/image/Dockerfile index 96cd77c4c..dc85c417a 100644 --- a/image/Dockerfile +++ b/image/Dockerfile @@ -1,8 +1,8 @@ FROM alpine:3.7 ADD hashes /root/hashes -ADD https://storage.googleapis.com/kubernetes-release/release/v1.9.4/bin/linux/amd64/kubectl /usr/bin/kubectl -ADD https://github.com/tazjin/kontemplate/releases/download/v1.4.0/kontemplate-1.4.0-1f373ca-linux-amd64.tar.gz /tmp/kontemplate.tar.gz +ADD https://storage.googleapis.com/kubernetes-release/release/v1.10.2/bin/linux/amd64/kubectl /usr/bin/kubectl +ADD https://github.com/tazjin/kontemplate/releases/download/v1.5.0/kontemplate-1.5.0-c68518d-linux-amd64.tar.gz /tmp/kontemplate.tar.gz # Pass release version is 1.7.1 ADD https://raw.githubusercontent.com/zx2c4/password-store/38ec1c72e29c872ec0cdde82f75490640d4019bf/src/password-store.sh /usr/bin/pass diff --git a/image/hashes b/image/hashes index 073d2a787..1a0602ed6 100644 --- a/image/hashes +++ b/image/hashes @@ -1,2 +1,2 @@ -fbf7b8f60e8090f9b615f893b4cc18583c77cd5a0092b46d0ceb7a78f99bab74 /tmp/kontemplate.tar.gz -1649bcda95e1662da0616a5314261285fa4154d769dc4148cc339d859b564c39 /usr/bin/kubectl +b37004daed21b3368271d01471aa9eda979358fa707b8e9f36614c876d051bb0 /tmp/kontemplate.tar.gz +524766fa2b88d64cff3276f4284107519f3cdd233692f08935aafa03b570f3ea /usr/bin/kubectl From 09869cf8fca09d3b1076e4ee558b8ca9fc91b733 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Tue, 15 May 2018 11:10:13 +0200 Subject: [PATCH 127/166] docs: Add contribution guidelines This document is intended to provide some information about how to contribute to the project, from basic sanity checks and code quality to information about how to structure git commits. These are the most common things that I encounter in pull requests sent to my projects. --- CONTRIBUTING.md | 114 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..e600aa22d --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,114 @@ +Contribution Guidelines +======================= + + +**Table of Contents** + +- [Contribution Guidelines](#contribution-guidelines) + - [Before making a change](#before-making-a-change) + - [Commit messages](#commit-messages) + - [Commit content](#commit-content) + - [Code quality](#code-quality) + - [Builds & tests](#builds--tests) + + + +This is a loose set of "guidelines" for contributing to my projects. +Please note that I will not accept any pull requests that don't follow +these guidelines. + +Also consider the [code of conduct](CODE_OF_CONDUCT.md). No really, +you should. + +## Before making a change + +Before making a change, consider your motivation for making the +change. Documentation updates, bug fixes and the like are *always* +welcome. + +When adding a feature you should consider whether it is only useful +for your particular use-case or whether it is generally applicable for +other users of the project. + +When in doubt - just ask me! + +## Commit messages + +All commit messages should follow the style-guide used by the [Angular +project][]. This means for the most part that your commit message +should be structured like this: + +``` +type(scope): Subject line with at most 68 a character length + +Body of the commit message with an empty line between subject and +body. This text should explain what the change does and why it has +been made, *especially* if it introduces a new feature. + +Relevant issues should be mentioned if they exist. +``` + +Where `type` can be one of: + +* `feat`: A new feature has been introduced +* `fix`: An issue of some kind has been fixed +* `docs`: Documentation or comments have been updated +* `style`: Formatting changes only +* `refactor`: Hopefully self-explanatory! +* `test`: Added missing tests / fixed tests +* `chore`: Maintenance work + +And `scope` should refer to some kind of logical grouping inside of +the project. + +Please take a look at the existing commit log for examples. + +## Commit content + +Multiple changes should be divided into multiple git commits whenever +possible. Common sense applies. + +The fix for a single-line whitespace issue is fine to include in a +different commit. Introducing a new feature and refactoring +(unrelated) code in the same commit is not fine. + +`git commit -a` is generally **taboo**. + +In my experience making "sane" commits becomes *significantly* easier +as developer tooling is improved. The interface to `git` that I +recommend is [magit][]. Even if you are not yet an Emacs user, it +makes sense to install Emacs just to be able to use magit - it is +really that good. + +For staging sane chunks on the command line with only git, consider +`git add -p`. + +## Code quality + +This one should go without saying - but please ensure that your code +quality does not fall below the rest of the project. This is of course +very subjective, but as an example if you place code that throws away +errors into a block in which errors are handled properly your change +will be rejected. + +In my experience there is a strong correlation between the visual +appearance of a code block and its quality. This is a simple way to +sanity-check your work while squinting and keeping some distance from +your screen ;-) + +## Builds & tests + +Most of my projects are built using [Nix][] to avoid "build pollution" +via the user's environment. If you have Nix installed and are +contributing to a project that has a `default.nix`, consider using +`nix-build` to verify that builds work correctly. + +If the project has tests, check that they still work before submitting +your change. + +Both of these will usually be covered by Travis CI. + + +[Angular project]: https://gist.github.com/stephenparish/9941e89d80e2bc58a153#format-of-the-commit-message +[magit]: https://magit.vc/ +[Nix]: https://nixos.org/nix/ From 5cf9d53e80accaeede1b4e38772d7d53c0190549 Mon Sep 17 00:00:00 2001 From: Phillip Johnsen Date: Wed, 30 May 2018 21:43:31 +0200 Subject: [PATCH 128/166] feat(context): allow explicit variables to be defined as argument These changes allows variables to be defined when executing `kontemplate` via one or more `--variable` arguments. With this in place one can either define new variables or override existing variables loaded from a file: ``` $ kontemplate apply --variable version=v1.0 example/fancy-app.yaml ``` This avoids the need to write variables into a temporary file that is only needed to provide "external variables" into resource sets. Closes https://github.com/tazjin/kontemplate/issues/122 --- context/context.go | 21 +++++++++++++++++++++ context/context_test.go | 22 ++++++++++++++++++++++ main.go | 6 ++++++ 3 files changed, 49 insertions(+) diff --git a/context/context.go b/context/context.go index 9996f31fa..1a2e5c88c 100644 --- a/context/context.go +++ b/context/context.go @@ -12,6 +12,7 @@ package context import ( "fmt" "path" + "strings" "github.com/tazjin/kontemplate/util" ) @@ -158,3 +159,23 @@ func loadDefaultValues(rs *ResourceSet, c *Context) *map[string]interface{} { // errors here. return &rs.Values } + +// New variables can be defined or default values overridden with command line arguments when executing kontemplate. +func (ctx *Context) SetVariablesFromArguments(vars *[]string) error { + // Resource set files might not have defined any global variables, if so we have to + // create that a map before potentially writing variables into it + if ctx.Global == nil { + ctx.Global = make(map[string]interface{}, len(*vars)) + } + + for _, v := range *vars { + varParts := strings.Split(v, "=") + if len(varParts) != 2 { + return fmt.Errorf(`invalid explicit variable provided (%s), name and value should be divided with "="`, v) + } + + ctx.Global[varParts[0]] = varParts[1] + } + + return nil +} diff --git a/context/context_test.go b/context/context_test.go index 38b6a76e7..6dc27466a 100644 --- a/context/context_test.go +++ b/context/context_test.go @@ -280,3 +280,25 @@ func TestExplicitSubresourcePathLoading(t *testing.T) { t.Fail() } } + +func TestSetVariablesFromArguments(t *testing.T) { + vars := []string{"version=some-service-version"} + ctx, _ := LoadContextFromFile("testdata/default-loading.yaml") + + if err := ctx.SetVariablesFromArguments(&vars); err != nil { + t.Error(err) + } + + if version := ctx.Global["version"]; version != "some-service-version" { + t.Errorf(`Expected variable "version" to have value "some-service-version" but was "%s"`, version) + } +} + +func TestSetInvalidVariablesFromArguments(t *testing.T) { + vars := []string{"version: some-service-version"} + ctx, _ := LoadContextFromFile("testdata/default-loading.yaml") + + if err := ctx.SetVariablesFromArguments(&vars); err == nil { + t.Error("Expected invalid variable to return an error") + } +} diff --git a/main.go b/main.go index 13aabd248..ee70bdae5 100644 --- a/main.go +++ b/main.go @@ -37,6 +37,7 @@ var ( // Global flags includes = app.Flag("include", "Resource sets to include explicitly").Short('i').Strings() excludes = app.Flag("exclude", "Resource sets to exclude explicitly").Short('e').Strings() + variables = app.Flag("var", "Provide variables to templates explicitly").Strings() // Commands template = app.Command("template", "Template resource sets and print them") @@ -188,6 +189,11 @@ func loadContextAndResources(file *string) (*context.Context, *[]templater.Rende app.Fatalf("Error loading context: %v\n", err) } + err = ctx.SetVariablesFromArguments(variables) + if err != nil { + app.Fatalf("Error setting explicit variables in context: %v\n", err) + } + resources, err := templater.LoadAndApplyTemplates(includes, excludes, ctx) if err != nil { app.Fatalf("Error templating resource sets: %v\n", err) From b33c353233aae4d73b1567aee41fd5d4cad5bf76 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sat, 9 Jun 2018 16:23:09 +0200 Subject: [PATCH 129/166] refactor(context): Implement more explicit merging of variables The hierarchy for loading variables was previously not expressed explicitly. This commit refactors the logic for merging variables to explicitly set the different layers of variables as values on the context object and merge them for each resource set in `mergeContextValues`. --- context/context.go | 86 ++++++++++++++-------- context/context_test.go | 60 +++++++++------ context/testdata/import-vars-override.yaml | 3 +- main.go | 7 +- templater/templater.go | 2 - 5 files changed, 96 insertions(+), 62 deletions(-) diff --git a/context/context.go b/context/context.go index 1a2e5c88c..42ad2e8c1 100644 --- a/context/context.go +++ b/context/context.go @@ -43,11 +43,17 @@ type Context struct { Global map[string]interface{} `json:"global"` // File names of YAML or JSON files including extra variables that should be globally accessible - VariableImports []string `json:"import"` + VariableImportFiles []string `json:"import"` // The resource sets to include in this context ResourceSets []ResourceSet `json:"include"` + // Variables imported from additional files + ImportedVars map[string]interface{} + + // Explicitly set variables (via `--var`) that should override all others + ExplicitVars map[string]interface{} + // This field represents the absolute path to the context base directory and should not be manually specified. BaseDir string } @@ -57,41 +63,59 @@ func contextLoadingError(filename string, cause error) error { } // Attempt to load and deserialise a Context from the specified file. -func LoadContextFromFile(filename string) (*Context, error) { - var c Context - err := util.LoadJsonOrYaml(filename, &c) +func LoadContext(filename string, explicitVars *[]string) (*Context, error) { + var ctx Context + err := util.LoadJsonOrYaml(filename, &ctx) if err != nil { return nil, contextLoadingError(filename, err) } - c.ResourceSets = flattenPrepareResourceSetPaths(&c.ResourceSets) - c.BaseDir = path.Dir(filename) - c.ResourceSets = loadAllDefaultValues(&c) + ctx.BaseDir = path.Dir(filename) - err = c.loadImportedVariables() + // Prepare the resource sets by resolving parents etc. + ctx.ResourceSets = flattenPrepareResourceSetPaths(&ctx.ResourceSets) + + // Add variables explicitly specified on the command line + ctx.ExplicitVars, err = loadExplicitVars(explicitVars) + if err != nil { + return nil, fmt.Errorf("Error setting explicit variables: %v\n", err) + } + + // Add variables loaded from import files + ctx.ImportedVars, err = ctx.loadImportedVariables() if err != nil { return nil, contextLoadingError(filename, err) } - return &c, nil + // Merge variables (explicit > import > include > global > default) + ctx.ResourceSets = ctx.mergeContextValues() + + if err != nil { + return nil, contextLoadingError(filename, err) + } + + return &ctx, nil } -// Kontemplate supports specifying additional variable files with the `import` keyword. This function loads those -// variable files and merges them together with the context's other global variables. -func (ctx *Context) loadImportedVariables() error { - for _, file := range ctx.VariableImports { +// Kontemplate supports specifying additional variable files with the +// `import` keyword. This function loads those variable files and +// merges them together with the context's other global variables. +func (ctx *Context) loadImportedVariables() (map[string]interface{}, error) { + allImportedVars := make(map[string]interface{}) + + for _, file := range ctx.VariableImportFiles { var importedVars map[string]interface{} err := util.LoadJsonOrYaml(path.Join(ctx.BaseDir, file), &importedVars) if err != nil { - return err + return nil, err } - ctx.Global = *util.Merge(&ctx.Global, &importedVars) + allImportedVars = *util.Merge(&allImportedVars, &importedVars) } - return nil + return allImportedVars, nil } // Correctly prepares the file paths for resource sets by inferring implicit paths and flattening resource set @@ -128,11 +152,16 @@ func flattenPrepareResourceSetPaths(rs *[]ResourceSet) []ResourceSet { return flattened } -func loadAllDefaultValues(c *Context) []ResourceSet { - updated := make([]ResourceSet, len(c.ResourceSets)) +// Merges the context and resource set variables according in the +// desired precedence order. +func (ctx *Context) mergeContextValues() []ResourceSet { + updated := make([]ResourceSet, len(ctx.ResourceSets)) - for i, rs := range c.ResourceSets { - merged := loadDefaultValues(&rs, c) + for i, rs := range ctx.ResourceSets { + merged := loadDefaultValues(&rs, ctx) + merged = util.Merge(&ctx.Global, merged) + merged = util.Merge(merged, &ctx.ImportedVars) + merged = util.Merge(merged, &ctx.ExplicitVars) rs.Values = *merged updated[i] = rs } @@ -160,22 +189,19 @@ func loadDefaultValues(rs *ResourceSet, c *Context) *map[string]interface{} { return &rs.Values } -// New variables can be defined or default values overridden with command line arguments when executing kontemplate. -func (ctx *Context) SetVariablesFromArguments(vars *[]string) error { - // Resource set files might not have defined any global variables, if so we have to - // create that a map before potentially writing variables into it - if ctx.Global == nil { - ctx.Global = make(map[string]interface{}, len(*vars)) - } +// Prepares the variables specified explicitly via `--var` when +// executing kontemplate for adding to the context. +func loadExplicitVars(vars *[]string) (map[string]interface{}, error) { + explicitVars := make(map[string]interface{}, len(*vars)) for _, v := range *vars { varParts := strings.Split(v, "=") if len(varParts) != 2 { - return fmt.Errorf(`invalid explicit variable provided (%s), name and value should be divided with "="`, v) + return nil, fmt.Errorf(`invalid explicit variable provided (%s), name and value should be separated with "="`, v) } - ctx.Global[varParts[0]] = varParts[1] + explicitVars[varParts[0]] = varParts[1] } - return nil + return explicitVars, nil } diff --git a/context/context_test.go b/context/context_test.go index 6dc27466a..6a4cec6c9 100644 --- a/context/context_test.go +++ b/context/context_test.go @@ -14,8 +14,10 @@ import ( "testing" ) +var noExplicitVars []string = make([]string, 0) + func TestLoadFlatContextFromFile(t *testing.T) { - ctx, err := LoadContextFromFile("testdata/flat-test.yaml") + ctx, err := LoadContext("testdata/flat-test.yaml", &noExplicitVars) if err != nil { t.Error(err) @@ -35,12 +37,15 @@ func TestLoadFlatContextFromFile(t *testing.T) { "apiPort": float64(4567), // yep! "importantFeature": true, "version": "1.0-0e6884d", + "globalVar": "lizards", }, Include: nil, Parent: "", }, }, BaseDir: "testdata", + ImportedVars: make(map[string]interface{}, 0), + ExplicitVars: make(map[string]interface{}, 0), } if !reflect.DeepEqual(*ctx, expected) { @@ -50,7 +55,7 @@ func TestLoadFlatContextFromFile(t *testing.T) { } func TestLoadContextWithResourceSetCollections(t *testing.T) { - ctx, err := LoadContextFromFile("testdata/collections-test.yaml") + ctx, err := LoadContext("testdata/collections-test.yaml", &noExplicitVars) if err != nil { t.Error(err) @@ -70,6 +75,7 @@ func TestLoadContextWithResourceSetCollections(t *testing.T) { "apiPort": float64(4567), // yep! "importantFeature": true, "version": "1.0-0e6884d", + "globalVar": "lizards", }, Include: nil, Parent: "", @@ -79,12 +85,15 @@ func TestLoadContextWithResourceSetCollections(t *testing.T) { Path: "collection/nested", Values: map[string]interface{}{ "lizards": "good", + "globalVar": "lizards", }, Include: nil, Parent: "collection", }, }, BaseDir: "testdata", + ImportedVars: make(map[string]interface{}, 0), + ExplicitVars: make(map[string]interface{}, 0), } if !reflect.DeepEqual(*ctx, expected) { @@ -95,7 +104,7 @@ func TestLoadContextWithResourceSetCollections(t *testing.T) { } func TestSubresourceVariableInheritance(t *testing.T) { - ctx, err := LoadContextFromFile("testdata/parent-variables.yaml") + ctx, err := LoadContext("testdata/parent-variables.yaml", &noExplicitVars) if err != nil { t.Error(err) @@ -117,6 +126,8 @@ func TestSubresourceVariableInheritance(t *testing.T) { }, }, BaseDir: "testdata", + ImportedVars: make(map[string]interface{}, 0), + ExplicitVars: make(map[string]interface{}, 0), } if !reflect.DeepEqual(*ctx, expected) { @@ -126,7 +137,7 @@ func TestSubresourceVariableInheritance(t *testing.T) { } func TestSubresourceVariableInheritanceOverride(t *testing.T) { - ctx, err := LoadContextFromFile("testdata/parent-variable-override.yaml") + ctx, err := LoadContext("testdata/parent-variable-override.yaml", &noExplicitVars) if err != nil { t.Error(err) @@ -147,6 +158,8 @@ func TestSubresourceVariableInheritanceOverride(t *testing.T) { }, }, BaseDir: "testdata", + ImportedVars: make(map[string]interface{}, 0), + ExplicitVars: make(map[string]interface{}, 0), } if !reflect.DeepEqual(*ctx, expected) { @@ -156,7 +169,7 @@ func TestSubresourceVariableInheritanceOverride(t *testing.T) { } func TestDefaultValuesLoading(t *testing.T) { - ctx, err := LoadContextFromFile("testdata/default-loading.yaml") + ctx, err := LoadContext("testdata/default-loading.yaml", &noExplicitVars) if err != nil { t.Error(err) t.Fail() @@ -175,7 +188,7 @@ func TestDefaultValuesLoading(t *testing.T) { } func TestImportValuesLoading(t *testing.T) { - ctx, err := LoadContextFromFile("testdata/import-vars-simple.yaml") + ctx, err := LoadContext("testdata/import-vars-simple.yaml", &noExplicitVars) if err != nil { t.Error(err) t.Fail() @@ -189,14 +202,14 @@ func TestImportValuesLoading(t *testing.T) { }, } - if !reflect.DeepEqual(ctx.Global, expected) { - t.Error("Expected global values after loading imports did not match!") + if !reflect.DeepEqual(ctx.ImportedVars, expected) { + t.Error("Expected imported values after loading imports did not match!") t.Fail() } } -func TestImportValuesOverride(t *testing.T) { - ctx, err := LoadContextFromFile("testdata/import-vars-override.yaml") +func TestValuesOverride(t *testing.T) { + ctx, err := LoadContext("testdata/import-vars-override.yaml", &noExplicitVars) if err != nil { t.Error(err) t.Fail() @@ -208,18 +221,18 @@ func TestImportValuesOverride(t *testing.T) { "artist": "Pallida", "track": "Tractor Beam", }, - "place": "Oslo", + "place": "Oslo", "globalVar": "very global!", } - if !reflect.DeepEqual(ctx.Global, expected) { - t.Error("Expected global values after loading imports did not match!") + if !reflect.DeepEqual(ctx.ResourceSets[0].Values, expected) { + t.Error("Expected overrides after loading imports did not match!") t.Fail() } } func TestExplicitPathLoading(t *testing.T) { - ctx, err := LoadContextFromFile("testdata/explicit-path.yaml") + ctx, err := LoadContext("testdata/explicit-path.yaml", &noExplicitVars) if err != nil { t.Error(err) t.Fail() @@ -248,6 +261,8 @@ func TestExplicitPathLoading(t *testing.T) { }, }, BaseDir: "testdata", + ImportedVars: make(map[string]interface{}, 0), + ExplicitVars: make(map[string]interface{}, 0), } if !reflect.DeepEqual(*ctx, expected) { @@ -257,7 +272,7 @@ func TestExplicitPathLoading(t *testing.T) { } func TestExplicitSubresourcePathLoading(t *testing.T) { - ctx, err := LoadContextFromFile("testdata/explicit-subresource-path.yaml") + ctx, err := LoadContext("testdata/explicit-subresource-path.yaml", &noExplicitVars) if err != nil { t.Error(err) t.Fail() @@ -270,9 +285,12 @@ func TestExplicitSubresourcePathLoading(t *testing.T) { Name: "parent/child", Path: "parent-path/child-path", Parent: "parent", + Values: make(map[string]interface{}, 0), }, }, BaseDir: "testdata", + ImportedVars: make(map[string]interface{}, 0), + ExplicitVars: make(map[string]interface{}, 0), } if !reflect.DeepEqual(*ctx, expected) { @@ -283,22 +301,18 @@ func TestExplicitSubresourcePathLoading(t *testing.T) { func TestSetVariablesFromArguments(t *testing.T) { vars := []string{"version=some-service-version"} - ctx, _ := LoadContextFromFile("testdata/default-loading.yaml") + ctx, _ := LoadContext("testdata/default-loading.yaml", &vars) - if err := ctx.SetVariablesFromArguments(&vars); err != nil { - t.Error(err) - } - - if version := ctx.Global["version"]; version != "some-service-version" { + if version := ctx.ExplicitVars["version"]; version != "some-service-version" { t.Errorf(`Expected variable "version" to have value "some-service-version" but was "%s"`, version) } } func TestSetInvalidVariablesFromArguments(t *testing.T) { vars := []string{"version: some-service-version"} - ctx, _ := LoadContextFromFile("testdata/default-loading.yaml") + _, err := LoadContext("testdata/default-loading.yaml", &vars) - if err := ctx.SetVariablesFromArguments(&vars); err == nil { + if err == nil { t.Error("Expected invalid variable to return an error") } } diff --git a/context/testdata/import-vars-override.yaml b/context/testdata/import-vars-override.yaml index c3d47bcfc..c749a2a66 100644 --- a/context/testdata/import-vars-override.yaml +++ b/context/testdata/import-vars-override.yaml @@ -6,4 +6,5 @@ global: import: - test-vars.yaml - test-vars-override.yaml -include: [] +include: + - name: test-set diff --git a/main.go b/main.go index ee70bdae5..b792a1a3b 100644 --- a/main.go +++ b/main.go @@ -184,16 +184,11 @@ func createCommand() { } func loadContextAndResources(file *string) (*context.Context, *[]templater.RenderedResourceSet) { - ctx, err := context.LoadContextFromFile(*file) + ctx, err := context.LoadContext(*file, variables) if err != nil { app.Fatalf("Error loading context: %v\n", err) } - err = ctx.SetVariablesFromArguments(variables) - if err != nil { - app.Fatalf("Error setting explicit variables in context: %v\n", err) - } - resources, err := templater.LoadAndApplyTemplates(includes, excludes, ctx) if err != nil { app.Fatalf("Error templating resource sets: %v\n", err) diff --git a/templater/templater.go b/templater/templater.go index c0eff234f..13ec9643e 100644 --- a/templater/templater.go +++ b/templater/templater.go @@ -111,8 +111,6 @@ func templateFile(c *context.Context, rs *context.ResourceSet, filename string) var b bytes.Buffer - rs.Values = *util.Merge(&c.Global, &rs.Values) - err = tpl.Execute(&b, rs.Values) if err != nil { From 141355f3505237b11480775fb83b5ba1bfcf7806 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sat, 9 Jun 2018 20:13:57 +0200 Subject: [PATCH 130/166] refactor(util): Use YAML parser for both JSON & YAML files JSON is a subset of YAML and the previous detection logic is unnecessary. --- main.go | 4 ++-- util/util.go | 17 ++++++----------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/main.go b/main.go index b792a1a3b..0ca8de428 100644 --- a/main.go +++ b/main.go @@ -35,8 +35,8 @@ var ( app = kingpin.New("kontemplate", "simple Kubernetes resource templating") // Global flags - includes = app.Flag("include", "Resource sets to include explicitly").Short('i').Strings() - excludes = app.Flag("exclude", "Resource sets to exclude explicitly").Short('e').Strings() + includes = app.Flag("include", "Resource sets to include explicitly").Short('i').Strings() + excludes = app.Flag("exclude", "Resource sets to exclude explicitly").Short('e').Strings() variables = app.Flag("var", "Provide variables to templates explicitly").Strings() // Commands diff --git a/util/util.go b/util/util.go index 947ab6e1c..ee972e2ee 100644 --- a/util/util.go +++ b/util/util.go @@ -10,10 +10,7 @@ package util import ( - "encoding/json" - "fmt" "io/ioutil" - "strings" "github.com/ghodss/yaml" ) @@ -44,19 +41,17 @@ func Merge(in1 *map[string]interface{}, in2 *map[string]interface{}) *map[string return &new } -// Loads either a YAML or JSON file from the specified path and deserialises it into the provided interface. -func LoadJsonOrYaml(filename string, addr interface{}) error { +// Loads either a YAML or JSON file from the specified path and +// deserialises it into the provided interface. +func LoadData(filename string, addr interface{}) error { file, err := ioutil.ReadFile(filename) if err != nil { return err } - if strings.HasSuffix(filename, "json") { - err = json.Unmarshal(file, addr) - } else if strings.HasSuffix(filename, "yaml") || strings.HasSuffix(filename, "yml") { - err = yaml.Unmarshal(file, addr) - } else { - return fmt.Errorf("File format not supported. Must be JSON or YAML.") + err = yaml.Unmarshal(file, addr) + if err != nil { + return err } return nil From ae6d960df941c3980c91e90e940b50ad59697eb7 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sat, 9 Jun 2018 20:14:38 +0200 Subject: [PATCH 131/166] feat(context): Support loading import variables from absolute paths This lets users specify the paths from which to import additional variables using absolute paths in addition to relative paths. This enables both loading of configuration files placed outside of the resource set folder (if desired), as well as special use-cases such as specifying `/dev/stdin` as an input path to read variables from standard input. This change supersedes #131 --- context/context.go | 15 ++++++++++++--- context/context_test.go | 16 ++++++++-------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/context/context.go b/context/context.go index 42ad2e8c1..237d51435 100644 --- a/context/context.go +++ b/context/context.go @@ -65,7 +65,7 @@ func contextLoadingError(filename string, cause error) error { // Attempt to load and deserialise a Context from the specified file. func LoadContext(filename string, explicitVars *[]string) (*Context, error) { var ctx Context - err := util.LoadJsonOrYaml(filename, &ctx) + err := util.LoadData(filename, &ctx) if err != nil { return nil, contextLoadingError(filename, err) @@ -105,8 +105,17 @@ func (ctx *Context) loadImportedVariables() (map[string]interface{}, error) { allImportedVars := make(map[string]interface{}) for _, file := range ctx.VariableImportFiles { + // Ensure that the filename is not merged with the baseDir if + // it is set to an absolute path. + var filePath string + if path.IsAbs(file) { + filePath = file + } else { + filePath = path.Join(ctx.BaseDir, file) + } + var importedVars map[string]interface{} - err := util.LoadJsonOrYaml(path.Join(ctx.BaseDir, file), &importedVars) + err := util.LoadData(filePath, &importedVars) if err != nil { return nil, err @@ -176,7 +185,7 @@ func loadDefaultValues(rs *ResourceSet, c *Context) *map[string]interface{} { var defaultVars map[string]interface{} for _, filename := range util.DefaultFilenames { - err := util.LoadJsonOrYaml(path.Join(c.BaseDir, rs.Path, filename), &defaultVars) + err := util.LoadData(path.Join(c.BaseDir, rs.Path, filename), &defaultVars) if err == nil { return util.Merge(&defaultVars, &rs.Values) } diff --git a/context/context_test.go b/context/context_test.go index 6a4cec6c9..0af9dfc1c 100644 --- a/context/context_test.go +++ b/context/context_test.go @@ -43,7 +43,7 @@ func TestLoadFlatContextFromFile(t *testing.T) { Parent: "", }, }, - BaseDir: "testdata", + BaseDir: "testdata", ImportedVars: make(map[string]interface{}, 0), ExplicitVars: make(map[string]interface{}, 0), } @@ -84,14 +84,14 @@ func TestLoadContextWithResourceSetCollections(t *testing.T) { Name: "collection/nested", Path: "collection/nested", Values: map[string]interface{}{ - "lizards": "good", + "lizards": "good", "globalVar": "lizards", }, Include: nil, Parent: "collection", }, }, - BaseDir: "testdata", + BaseDir: "testdata", ImportedVars: make(map[string]interface{}, 0), ExplicitVars: make(map[string]interface{}, 0), } @@ -125,7 +125,7 @@ func TestSubresourceVariableInheritance(t *testing.T) { Parent: "parent", }, }, - BaseDir: "testdata", + BaseDir: "testdata", ImportedVars: make(map[string]interface{}, 0), ExplicitVars: make(map[string]interface{}, 0), } @@ -157,7 +157,7 @@ func TestSubresourceVariableInheritanceOverride(t *testing.T) { Parent: "parent", }, }, - BaseDir: "testdata", + BaseDir: "testdata", ImportedVars: make(map[string]interface{}, 0), ExplicitVars: make(map[string]interface{}, 0), } @@ -221,7 +221,7 @@ func TestValuesOverride(t *testing.T) { "artist": "Pallida", "track": "Tractor Beam", }, - "place": "Oslo", + "place": "Oslo", "globalVar": "very global!", } @@ -260,7 +260,7 @@ func TestExplicitPathLoading(t *testing.T) { Parent: "", }, }, - BaseDir: "testdata", + BaseDir: "testdata", ImportedVars: make(map[string]interface{}, 0), ExplicitVars: make(map[string]interface{}, 0), } @@ -288,7 +288,7 @@ func TestExplicitSubresourcePathLoading(t *testing.T) { Values: make(map[string]interface{}, 0), }, }, - BaseDir: "testdata", + BaseDir: "testdata", ImportedVars: make(map[string]interface{}, 0), ExplicitVars: make(map[string]interface{}, 0), } From 77ca5b47cf5d40fa53204633924e8ba30a517b3f Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sat, 9 Jun 2018 20:45:42 +0200 Subject: [PATCH 132/166] fix(context): Global values have precedence over defaults --- context/context.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/context/context.go b/context/context.go index 237d51435..e6e3d4a12 100644 --- a/context/context.go +++ b/context/context.go @@ -88,7 +88,7 @@ func LoadContext(filename string, explicitVars *[]string) (*Context, error) { return nil, contextLoadingError(filename, err) } - // Merge variables (explicit > import > include > global > default) + // Merge variables (explicit > import > include > global > default) ctx.ResourceSets = ctx.mergeContextValues() if err != nil { @@ -168,7 +168,7 @@ func (ctx *Context) mergeContextValues() []ResourceSet { for i, rs := range ctx.ResourceSets { merged := loadDefaultValues(&rs, ctx) - merged = util.Merge(&ctx.Global, merged) + merged = util.Merge(merged, &ctx.Global) merged = util.Merge(merged, &ctx.ImportedVars) merged = util.Merge(merged, &ctx.ExplicitVars) rs.Values = *merged From c91cb21f70bbd4550bf216e64136816b42145392 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sat, 9 Jun 2018 21:15:18 +0200 Subject: [PATCH 133/166] feat(templater): Support single-template resource sets Supports resource sets in which the `path` is pointed at a single template file. The example has been updated with ... an example of this. This closes #81. --- example/other-config.yaml | 7 +++++ example/prod-cluster.yaml | 7 +++++ templater/templater.go | 64 +++++++++++++++++++++++++-------------- 3 files changed, 55 insertions(+), 23 deletions(-) create mode 100644 example/other-config.yaml diff --git a/example/other-config.yaml b/example/other-config.yaml new file mode 100644 index 000000000..87370569c --- /dev/null +++ b/example/other-config.yaml @@ -0,0 +1,7 @@ +--- +apiVersion: extensions/v1beta1 +kind: ConfigMap +metadata: + name: other-config +data: + globalData: {{ .globalVar }} diff --git a/example/prod-cluster.yaml b/example/prod-cluster.yaml index dd7804f71..9f300a492 100644 --- a/example/prod-cluster.yaml +++ b/example/prod-cluster.yaml @@ -3,8 +3,15 @@ context: k8s.prod.mydomain.com global: globalVar: lizards include: + # By default resource sets are included from a folder with the same + # name as the resource set's name - name: some-api values: version: 1.0-0e6884d importantFeature: true apiPort: 4567 + + # Paths can also be specified manually (and point at single template + # files!) + - name: other-config + path: other-config.yaml diff --git a/templater/templater.go b/templater/templater.go index 13ec9643e..192aac849 100644 --- a/templater/templater.go +++ b/templater/templater.go @@ -58,43 +58,57 @@ func LoadAndApplyTemplates(include *[]string, exclude *[]string, c *context.Cont return renderedResourceSets, nil } -func processResourceSet(c *context.Context, rs *context.ResourceSet) (*RenderedResourceSet, error) { +func processResourceSet(ctx *context.Context, rs *context.ResourceSet) (*RenderedResourceSet, error) { fmt.Fprintf(os.Stderr, "Loading resources for %s\n", rs.Name) - rp := path.Join(c.BaseDir, rs.Path) - - // Explicitly discard this error, which will give us an empty list of files instead. - // This will end up printing a warning to the user, but it won't stop the rest of the process. - files, _ := ioutil.ReadDir(rp) - - resources, err := processFiles(c, rs, rp, files) - + resourcePath := path.Join(ctx.BaseDir, rs.Path) + fileInfo, err := os.Stat(resourcePath) if err != nil { return nil, err } + var files []os.FileInfo + var resources []RenderedResource + + // Treat single-file resource paths separately from resource + // sets containing multiple templates + if fileInfo.IsDir() { + // Explicitly discard this error, which will give us an empty + // list of files instead. + // This will end up printing a warning to the user, but it + // won't stop the rest of the process. + files, _ = ioutil.ReadDir(resourcePath) + resources, err = processFiles(ctx, rs, files) + if err != nil { + return nil, err + } + } else { + resource, err := templateFile(ctx, rs, resourcePath) + if err != nil { + return nil, err + } + + resources = []RenderedResource{resource} + } + return &RenderedResourceSet{ Name: rs.Name, Resources: resources, }, nil } -func processFiles(c *context.Context, rs *context.ResourceSet, rp string, files []os.FileInfo) ([]RenderedResource, error) { +func processFiles(ctx *context.Context, rs *context.ResourceSet, files []os.FileInfo) ([]RenderedResource, error) { resources := make([]RenderedResource, 0) for _, file := range files { if !file.IsDir() && isResourceFile(file) { - p := path.Join(rp, file.Name()) - o, err := templateFile(c, rs, p) + path := path.Join(ctx.BaseDir, rs.Path, file.Name()) + res, err := templateFile(ctx, rs, path) if err != nil { return resources, err } - res := RenderedResource{ - Filename: file.Name(), - Rendered: o, - } resources = append(resources, res) } } @@ -102,22 +116,26 @@ func processFiles(c *context.Context, rs *context.ResourceSet, rp string, files return resources, nil } -func templateFile(c *context.Context, rs *context.ResourceSet, filename string) (string, error) { - tpl, err := template.New(path.Base(filename)).Funcs(templateFuncs(c, rs)).Option(failOnMissingKeys).ParseFiles(filename) +func templateFile(ctx *context.Context, rs *context.ResourceSet, filepath string) (RenderedResource, error) { + var resource RenderedResource + tpl, err := template.New(path.Base(filepath)).Funcs(templateFuncs(ctx, rs)).Option(failOnMissingKeys).ParseFiles(filepath) if err != nil { - return "", fmt.Errorf("Template %s not found: %v", filename, err) + return resource, fmt.Errorf("Could not load template %s: %v", filepath, err) } var b bytes.Buffer - err = tpl.Execute(&b, rs.Values) - if err != nil { - return "", fmt.Errorf("Error while templating %s: %v", filename, err) + return resource, fmt.Errorf("Error while templating %s: %v", filepath, err) } - return b.String(), nil + resource = RenderedResource{ + Filename: path.Base(filepath), + Rendered: b.String(), + } + + return resource, nil } // Applies the limits of explicitly included or excluded resources and returns the updated resource set. From 97bef90387261d5e93c3bf8668ec55667b7fa2e0 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sat, 9 Jun 2018 21:30:22 +0200 Subject: [PATCH 134/166] chore: Bump version to 1.6.0 --- build-release.sh | 2 +- main.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build-release.sh b/build-release.sh index 97fcf75d8..445ebec58 100755 --- a/build-release.sh +++ b/build-release.sh @@ -12,7 +12,7 @@ set -ueo pipefail readonly GIT_HASH="$(git rev-parse --short HEAD)" readonly LDFLAGS="-X main.gitHash=${GIT_HASH} -w -s" -readonly VERSION="1.5.0-${GIT_HASH}" +readonly VERSION="1.6.0-${GIT_HASH}" function binary-name() { local os="${1}" diff --git a/main.go b/main.go index 0ca8de428..a94397619 100644 --- a/main.go +++ b/main.go @@ -26,7 +26,7 @@ import ( "gopkg.in/alecthomas/kingpin.v2" ) -const version string = "1.5.0" +const version string = "1.6.0" // This variable will be initialised by the Go linker during the builder var gitHash string From 3a688191af802a04b2de4f57291ad7372c02d68e Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sat, 9 Jun 2018 21:57:32 +0200 Subject: [PATCH 135/166] fix(build): Fix shebang in build-release.sh --- build-release.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build-release.sh b/build-release.sh index 445ebec58..462e1b8e3 100755 --- a/build-release.sh +++ b/build-release.sh @@ -1,3 +1,6 @@ +#!/usr/bin/env bash +set -ueo pipefail + # Copyright (C) 2016-2017 Vincent Ambo # # This file is part of Kontemplate. @@ -7,9 +10,6 @@ # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. -#!/bin/bash -set -ueo pipefail - readonly GIT_HASH="$(git rev-parse --short HEAD)" readonly LDFLAGS="-X main.gitHash=${GIT_HASH} -w -s" readonly VERSION="1.6.0-${GIT_HASH}" From e9992aec0b690f32ed71aeef2dd73ea15669a2e9 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sat, 9 Jun 2018 21:58:42 +0200 Subject: [PATCH 136/166] chore(brew): Update Homebrew formula for 1.6.0 --- kontemplate.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kontemplate.rb b/kontemplate.rb index 8d14cec41..1b33be2ea 100644 --- a/kontemplate.rb +++ b/kontemplate.rb @@ -3,9 +3,9 @@ class Kontemplate < Formula desc "Kontemplate - Extremely simple Kubernetes resource templates" homepage "https://github.com/tazjin/kontemplate" - url "https://github.com/tazjin/kontemplate/releases/download/v1.5.0/kontemplate-1.5.0-c68518d-darwin-amd64.tar.gz" - sha256 "61cd9ad3e28f52260458b707fd3120afa53e0610213ddd0ab03f489439f6b8a9" - version "1.5.0-c68518d" + url "https://github.com/tazjin/kontemplate/releases/download/v1.6.0/kontemplate-1.6.0-97bef90-darwin-amd64.tar.gz" + sha256 "d21529153d369d2347477f981a855525695c0bc2912a50f05d3baf15c96f7c16" + version "1.6.0-97bef90" def install bin.install "kontemplate" From 01f771fa2fb19ae6ca365c9d08000a81a266d609 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sat, 9 Jun 2018 22:04:41 +0200 Subject: [PATCH 137/166] chore(image): Bump version to 1.6.0 * kontemplate 1.6.0 * kubectl 1.10.4 --- image/Dockerfile | 7 ++++--- image/hashes | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/image/Dockerfile b/image/Dockerfile index dc85c417a..12d24fffe 100644 --- a/image/Dockerfile +++ b/image/Dockerfile @@ -1,8 +1,8 @@ FROM alpine:3.7 ADD hashes /root/hashes -ADD https://storage.googleapis.com/kubernetes-release/release/v1.10.2/bin/linux/amd64/kubectl /usr/bin/kubectl -ADD https://github.com/tazjin/kontemplate/releases/download/v1.5.0/kontemplate-1.5.0-c68518d-linux-amd64.tar.gz /tmp/kontemplate.tar.gz +ADD https://storage.googleapis.com/kubernetes-release/release/v1.10.4/bin/linux/amd64/kubectl /usr/bin/kubectl +ADD https://github.com/tazjin/kontemplate/releases/download/v1.6.0/kontemplate-1.6.0-97bef90-linux-amd64.tar.gz /tmp/kontemplate.tar.gz # Pass release version is 1.7.1 ADD https://raw.githubusercontent.com/zx2c4/password-store/38ec1c72e29c872ec0cdde82f75490640d4019bf/src/password-store.sh /usr/bin/pass @@ -11,4 +11,5 @@ RUN sha256sum -c /root/hashes && \ apk add -U bash tree gnupg git && \ chmod +x /usr/bin/kubectl /usr/bin/pass && \ tar xzvf /tmp/kontemplate.tar.gz && \ - mv kontemplate /usr/bin/kontemplate + mv kontemplate /usr/bin/kontemplate && \ + /usr/bin/kontemplate version diff --git a/image/hashes b/image/hashes index 1a0602ed6..28212e483 100644 --- a/image/hashes +++ b/image/hashes @@ -1,2 +1,2 @@ -b37004daed21b3368271d01471aa9eda979358fa707b8e9f36614c876d051bb0 /tmp/kontemplate.tar.gz -524766fa2b88d64cff3276f4284107519f3cdd233692f08935aafa03b570f3ea /usr/bin/kubectl +dc97c5547766cfb9cdc40c164113ec43938c6928ccf787e899f700998faad2a4 /tmp/kontemplate.tar.gz +92ef1ab45e9564bf3225b7b08ada61cfb04d872f5d279cbb2d1781d8060fe83b /usr/bin/kubectl From 92c5c846e2ce19c4c46a70a36c533245538c2dd3 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Thu, 21 Jun 2018 13:55:59 +0200 Subject: [PATCH 138/166] fix(context): Use SplitN to split CLI variable specifications In some cases the value of a variable may contain an equals sign, which would not work in the previous version. This uses `SplitN` to split the variable specifier into a pre-determined number (2) of sub-slices. Further `=`-symbols will then be included in the second substring. --- context/context.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context/context.go b/context/context.go index e6e3d4a12..321d4fa42 100644 --- a/context/context.go +++ b/context/context.go @@ -204,7 +204,7 @@ func loadExplicitVars(vars *[]string) (map[string]interface{}, error) { explicitVars := make(map[string]interface{}, len(*vars)) for _, v := range *vars { - varParts := strings.Split(v, "=") + varParts := strings.SplitN(v, "=", 2) if len(varParts) != 2 { return nil, fmt.Errorf(`invalid explicit variable provided (%s), name and value should be separated with "="`, v) } From bd5980a5d7433fd0ec81bd270c9d58e506ede54b Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sat, 23 Jun 2018 18:07:56 +0200 Subject: [PATCH 139/166] feat(main): Add CLI flag for setting kubectl executable This lets users choose the executable (either by full path or via a $PATH-entry) to be used when executing `kubectl`. This is useful in, for example, OpenShift based setups. This fixes #143 --- main.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/main.go b/main.go index a94397619..ca20bb76b 100644 --- a/main.go +++ b/main.go @@ -35,9 +35,10 @@ var ( app = kingpin.New("kontemplate", "simple Kubernetes resource templating") // Global flags - includes = app.Flag("include", "Resource sets to include explicitly").Short('i').Strings() - excludes = app.Flag("exclude", "Resource sets to exclude explicitly").Short('e').Strings() - variables = app.Flag("var", "Provide variables to templates explicitly").Strings() + includes = app.Flag("include", "Resource sets to include explicitly").Short('i').Strings() + excludes = app.Flag("exclude", "Resource sets to exclude explicitly").Short('e').Strings() + variables = app.Flag("var", "Provide variables to templates explicitly").Strings() + kubectlBin = app.Flag("kubectl", "Path to the kubectl binary (default 'kubectl')").Default("kubectl").String() // Commands template = app.Command("template", "Template resource sets and print them") @@ -206,7 +207,7 @@ func runKubectlWithResources(c *context.Context, kubectlArgs *[]string, resource continue } - kubectl := exec.Command("kubectl", args...) + kubectl := exec.Command(*kubectlBin, args...) stdin, err := kubectl.StdinPipe() if err != nil { From 2db3bbcdbe0d4760cb16a847d8a56f2a4319316e Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Tue, 26 Jun 2018 12:23:03 +0200 Subject: [PATCH 140/166] refactor(context): Rewrite and explain value merging logic Changes the logic for merging context values to be unambiguous and easy to follow. * loadDefaultVars returns the default vars directly instead of performing merging in addition * all merging is performed in `mergeContextValues` using explicit explanations for every step of the merge. After this commit the order of merging goes from least to most "specific", please read the explanatory comments for more information. This relates to #142. --- context/context.go | 63 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 52 insertions(+), 11 deletions(-) diff --git a/context/context.go b/context/context.go index 321d4fa42..49de7f469 100644 --- a/context/context.go +++ b/context/context.go @@ -88,7 +88,9 @@ func LoadContext(filename string, explicitVars *[]string) (*Context, error) { return nil, contextLoadingError(filename, err) } - // Merge variables (explicit > import > include > global > default) + // Merge variables defined at different levels. The + // `mergeContextValues` function is documented with the merge + // hierarchy. ctx.ResourceSets = ctx.mergeContextValues() if err != nil { @@ -163,14 +165,50 @@ func flattenPrepareResourceSetPaths(rs *[]ResourceSet) []ResourceSet { // Merges the context and resource set variables according in the // desired precedence order. +// +// For now the reasoning behind the merge order is from least specific +// in relation to the cluster configuration, which means that the +// precedence is (in ascending order): +// +// 1. Default values in resource sets. +// 2. Values imported from files (via `import:`) +// 3. Global values in a cluster configuration +// 4. Values set in a resource set's `include`-section +// 5. Explicit values set on the CLI (`--var`) +// +// For a discussion on the reasoning behind this order, please consult +// https://github.com/tazjin/kontemplate/issues/142 func (ctx *Context) mergeContextValues() []ResourceSet { updated := make([]ResourceSet, len(ctx.ResourceSets)) + // Merging has to happen separately for every individual + // resource set to make use of the default values: for i, rs := range ctx.ResourceSets { - merged := loadDefaultValues(&rs, ctx) + // Begin by loading default values from the resource + // sets configuration. + // + // Resource sets are used across different cluster + // contexts and the default values in them have the + // lowest precedence. + defaultValues := loadDefaultValues(&rs, ctx) + + // Continue by merging default values with values + // imported from external files. Those values are also + // used across cluster contexts, but have higher + // precedence than defaults. + merged := util.Merge(defaultValues, &ctx.ImportedVars) + + // Merge global values defined in the cluster context: merged = util.Merge(merged, &ctx.Global) - merged = util.Merge(merged, &ctx.ImportedVars) + + // Merge values configured in the resource set's + // `include` section: + merged = util.Merge(merged, &rs.Values) + + // Merge values defined explicitly on the CLI: merged = util.Merge(merged, &ctx.ExplicitVars) + + // Continue with the newly merged resource set: rs.Values = *merged updated[i] = rs } @@ -178,23 +216,26 @@ func (ctx *Context) mergeContextValues() []ResourceSet { return updated } -// Loads and merges default values for a resource set collection from path/to/set/default.{json|yaml}. -// YAML takes precedence over JSON. -// Default values in resource set collections have the lowest priority possible. +// Loads default values for a resource set collection from +// path/to/set/default.{json|yaml}. func loadDefaultValues(rs *ResourceSet, c *Context) *map[string]interface{} { var defaultVars map[string]interface{} for _, filename := range util.DefaultFilenames { err := util.LoadData(path.Join(c.BaseDir, rs.Path, filename), &defaultVars) if err == nil { - return util.Merge(&defaultVars, &rs.Values) + return &defaultVars } } - // The actual error is not inspected here. The reasoning for this is that in case of serious problems (e.g. - // permission issues with the folder / folder not existing) failure will occur a bit later anyways. - // Otherwise we'd have to differentiate between file-not-found-errors (no default values specified) and other - // errors here. + // The actual error is not inspected here. The reasoning for + // this is that in case of serious problems (e.g. permission + // issues with the folder / folder not existing) failure will + // occur a bit later anyways. + // + // Otherwise we'd have to differentiate between + // file-not-found-errors (no default values specified) and + // other errors here. return &rs.Values } From 4a6f087fbfe84731db0a8e5084364ad0f6ad5212 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Tue, 26 Jun 2018 12:25:42 +0200 Subject: [PATCH 141/166] chore(context): Remove previous value override test --- context/context_test.go | 23 ---------------------- context/testdata/import-vars-override.yaml | 10 ---------- 2 files changed, 33 deletions(-) delete mode 100644 context/testdata/import-vars-override.yaml diff --git a/context/context_test.go b/context/context_test.go index 0af9dfc1c..e700cf419 100644 --- a/context/context_test.go +++ b/context/context_test.go @@ -208,29 +208,6 @@ func TestImportValuesLoading(t *testing.T) { } } -func TestValuesOverride(t *testing.T) { - ctx, err := LoadContext("testdata/import-vars-override.yaml", &noExplicitVars) - if err != nil { - t.Error(err) - t.Fail() - } - - expected := map[string]interface{}{ - "override": float64(3), - "music": map[string]interface{}{ - "artist": "Pallida", - "track": "Tractor Beam", - }, - "place": "Oslo", - "globalVar": "very global!", - } - - if !reflect.DeepEqual(ctx.ResourceSets[0].Values, expected) { - t.Error("Expected overrides after loading imports did not match!") - t.Fail() - } -} - func TestExplicitPathLoading(t *testing.T) { ctx, err := LoadContext("testdata/explicit-path.yaml", &noExplicitVars) if err != nil { diff --git a/context/testdata/import-vars-override.yaml b/context/testdata/import-vars-override.yaml deleted file mode 100644 index c749a2a66..000000000 --- a/context/testdata/import-vars-override.yaml +++ /dev/null @@ -1,10 +0,0 @@ ---- -context: k8s.prod.mydomain.com -global: - globalVar: very global! - override: 1 -import: - - test-vars.yaml - - test-vars-override.yaml -include: - - name: test-set From 6d8214bfc8eb9733f0adf58a629c77950c34e80d Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Tue, 26 Jun 2018 12:25:55 +0200 Subject: [PATCH 142/166] test(context): Introduce an explicit value merging test Introduces a test which will merge variables defined at every possible layer together and ensure that the loaded context configuration is as expected. The test data provides an actual resource set template that can be tested locally from a kontemplate source checkout: kontemplate template context/testdata/merging/context.yaml --var cliVar=cliVar --- context/context_test.go | 22 +++++++++++++++++++ context/testdata/merging/context.yaml | 15 +++++++++++++ context/testdata/merging/import-vars.yaml | 4 ++++ .../testdata/merging/resource/default.yaml | 5 +++++ context/testdata/merging/resource/output.yaml | 5 +++++ 5 files changed, 51 insertions(+) create mode 100644 context/testdata/merging/context.yaml create mode 100644 context/testdata/merging/import-vars.yaml create mode 100644 context/testdata/merging/resource/default.yaml create mode 100644 context/testdata/merging/resource/output.yaml diff --git a/context/context_test.go b/context/context_test.go index e700cf419..34c77cc9b 100644 --- a/context/context_test.go +++ b/context/context_test.go @@ -293,3 +293,25 @@ func TestSetInvalidVariablesFromArguments(t *testing.T) { t.Error("Expected invalid variable to return an error") } } + +// This test ensures that variables are merged in the correct order. +// Please consult the test data in `testdata/merging`. +func TestValueMergePrecedence(t *testing.T) { + cliVars:= []string{"cliVar=cliVar"} + ctx, _ := LoadContext("testdata/merging/context.yaml", &cliVars) + + expected := map[string]interface{}{ + "defaultVar": "defaultVar", + "importVar": "importVar", + "globalVar": "globalVar", + "includeVar": "includeVar", + "cliVar": "cliVar", + } + + result := ctx.ResourceSets[0].Values + + if !reflect.DeepEqual(expected, result) { + t.Errorf("Merged values did not match expected result: \n%v", result) + t.Fail() + } +} diff --git a/context/testdata/merging/context.yaml b/context/testdata/merging/context.yaml new file mode 100644 index 000000000..df30d3d8c --- /dev/null +++ b/context/testdata/merging/context.yaml @@ -0,0 +1,15 @@ +# This context file is intended to test the merge hierarchy of +# variables defined at different levels. +--- +context: merging.in.kontemplate.works +global: + globalVar: globalVar + includeVar: should be overridden (global) + cliVar: should be overridden (global) +import: + - import-vars.yaml +include: + - name: resource + values: + includeVar: includeVar + cliVar: should be overridden (include) diff --git a/context/testdata/merging/import-vars.yaml b/context/testdata/merging/import-vars.yaml new file mode 100644 index 000000000..2a5135257 --- /dev/null +++ b/context/testdata/merging/import-vars.yaml @@ -0,0 +1,4 @@ +importVar: importVar +globalVar: should be overridden (import) +includeVar: should be overridden (import) +cliVar: should be overridden (import) diff --git a/context/testdata/merging/resource/default.yaml b/context/testdata/merging/resource/default.yaml new file mode 100644 index 000000000..040a19aab --- /dev/null +++ b/context/testdata/merging/resource/default.yaml @@ -0,0 +1,5 @@ +defaultVar: defaultVar +importVar: should be overridden (default) +globalVar: should be overridden (default) +includeVar: should be overridden (default) +cliVar: should be overridden (default) diff --git a/context/testdata/merging/resource/output.yaml b/context/testdata/merging/resource/output.yaml new file mode 100644 index 000000000..5920b2720 --- /dev/null +++ b/context/testdata/merging/resource/output.yaml @@ -0,0 +1,5 @@ +defaultVar: {{ .defaultVar }} +importVar: {{ .importVar }} +globalVar: {{ .globalVar }} +includeVar: {{ .includeVar }} +cliVar: {{ .cliVar }} From 6b39254917a89a854060aac5a87ad88a6d4831fa Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Tue, 26 Jun 2018 12:30:41 +0200 Subject: [PATCH 143/166] chore: Bump version to 1.7.0 --- build-release.sh | 2 +- main.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build-release.sh b/build-release.sh index 462e1b8e3..1dfae4ddc 100755 --- a/build-release.sh +++ b/build-release.sh @@ -12,7 +12,7 @@ set -ueo pipefail readonly GIT_HASH="$(git rev-parse --short HEAD)" readonly LDFLAGS="-X main.gitHash=${GIT_HASH} -w -s" -readonly VERSION="1.6.0-${GIT_HASH}" +readonly VERSION="1.7.0-${GIT_HASH}" function binary-name() { local os="${1}" diff --git a/main.go b/main.go index ca20bb76b..6dacc5275 100644 --- a/main.go +++ b/main.go @@ -26,7 +26,7 @@ import ( "gopkg.in/alecthomas/kingpin.v2" ) -const version string = "1.6.0" +const version string = "1.7.0" // This variable will be initialised by the Go linker during the builder var gitHash string From ab059ad41c7195e5c4a3f456b7f590d881884dcf Mon Sep 17 00:00:00 2001 From: Phillip Johnsen Date: Wed, 27 Jun 2018 23:37:16 +0200 Subject: [PATCH 144/166] feat(templater): override sprig default function with guarded alternative These changes overrides the `default` function provided by sprig with an alternative to retrieve variable values from variables that might not have been declared at all. Referencing a variable in a template that is not declared, will lead to the underlying templating functionality raising an error, causing kontemplate to exit. The override alternative to `default` accepts a second string argument with the variable name. If the variable in question has not been declared the first argument's value would be returned, just as the original `default` function does. --- templater/templater.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/templater/templater.go b/templater/templater.go index 192aac849..ff4e0efde 100644 --- a/templater/templater.go +++ b/templater/templater.go @@ -203,7 +203,13 @@ func templateFuncs(c *context.Context, rs *context.ResourceSet) template.FuncMap return string(data), nil } + m["default"] = func(defaultVal interface{}, varName string) interface{} { + if val, ok := rs.Values[varName]; ok { + return val + } + return defaultVal + } return m } From df1a9a1625f01478b1ce6a5754363468f4342924 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sat, 14 Jul 2018 21:40:05 +0200 Subject: [PATCH 145/166] test(templater): Add a test for the `default` template function --- templater/templater_test.go | 17 +++++++++++++++++ templater/testdata/test-default.txt | 1 + 2 files changed, 18 insertions(+) create mode 100644 templater/testdata/test-default.txt diff --git a/templater/templater_test.go b/templater/templater_test.go index f6b12c758..2a9ddaa2b 100644 --- a/templater/templater_test.go +++ b/templater/templater_test.go @@ -162,3 +162,20 @@ func TestFailOnMissingKeys(t *testing.T) { t.Errorf("Templating failed with unexpected error: %v\n", err) } } + +func TestDefaultTemplateFunction(t *testing.T) { + ctx := context.Context{} + resourceSet := context.ResourceSet{} + + res, err := templateFile(&ctx, &resourceSet, "testdata/test-default.txt") + + if err != nil { + t.Errorf("Templating with default values should have succeeded.\n") + t.Fail() + } + + if res.Rendered != "defaultValue\n" { + t.Error("Result does not contain expected rendered default value.") + t.Fail() + } +} diff --git a/templater/testdata/test-default.txt b/templater/testdata/test-default.txt new file mode 100644 index 000000000..4f7997bd6 --- /dev/null +++ b/templater/testdata/test-default.txt @@ -0,0 +1 @@ +{{ default "defaultValue" "missingVar" }} From 511ae92224d3d1474c9303bb8b3bb7f8146a4356 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 15 Aug 2018 19:38:39 +0200 Subject: [PATCH 146/166] chore(build): Update dependencies to newest version Updates the following dependencies to latest: * Masterminds/semver * Masterminds/sprig * ghodss/yaml * satori/go.uuid -> google/uuid * huandu/xstrings * imdario/mergo * crypto * alecthomas/kingpin.v2 * yaml.v2 As usual Go libraries are YOLO-versioned, so who knows what changed here. I'll be going through `sprig` at least to add that to the changelog. This relates to #152. --- deps.nix | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/deps.nix b/deps.nix index db2692a79..f0a52f526 100644 --- a/deps.nix +++ b/deps.nix @@ -5,8 +5,8 @@ fetch = { type = "git"; url = "https://github.com/Masterminds/semver"; - rev = "517734cc7d6470c0d07130e40fd40bdeb9bcd3fd"; - sha256 = "1625b5sxpmlz60jw67j1ljfcc09d4lhxg3z6gc4am8s2rrdgwij6"; + rev = "c84ddcca87bf5a941b138dde832a7e20b0159ad8"; + sha256 = "1dcfdr018a0yszjpvr3wshvq9cc3kvy95l55si556p617wsn1wan"; }; } { @@ -14,8 +14,8 @@ fetch = { type = "git"; url = "https://github.com/Masterminds/sprig"; - rev = "e039e20e500c2c025d9145be375e27cf42a94174"; - sha256 = "1yhpyzq6ghwl0242phjpbc9358fcw63pxrcxsyv9n4dm0w15va3m"; + rev = "77bb58b7f5e10889a1195c21b9e7a96ee166f199"; + sha256 = "0q4g12f3nvda1skz33qzbbdd2vj3gjfwf361hyzlx20s71brk3bk"; }; } { @@ -50,8 +50,17 @@ fetch = { type = "git"; url = "https://github.com/ghodss/yaml"; - rev = "0ca9ea5df5451ffdf184b4428c902747c2c11cd7"; - sha256 = "0skwmimpy7hlh7pva2slpcplnm912rp3igs98xnqmn859kwa5v8g"; + rev = "e9ed3c6dfb39bb1a32197cb10d527906fe4da4b6"; + sha256 = "07cf0j3wbsl1gmn175mdgljcarfz4xbqd6pgc7b08a5lcn7zwhjz"; + }; + } + { + goPackagePath = "github.com/google/uuid"; + fetch = { + type = "git"; + url = "https://github.com/google/uuid"; + rev = "dec09d789f3dba190787f8b4454c7d3c936fed9e"; + sha256 = "1hc4w67p6zkh2qk7wm1yrl69jjrjjk615mly5ll4iidn1m4mzi4i"; }; } { @@ -59,8 +68,8 @@ fetch = { type = "git"; url = "https://github.com/huandu/xstrings"; - rev = "3959339b333561bf62a38b424fd41517c2c90f40"; - sha256 = "0f1jyd80grpr88gwhljx2x0xgsyzw07807n4z4axxxlybh5f0nh1"; + rev = "7bb0250b58e5c15670406e6f93ffda43281305b1"; + sha256 = "1fc8q65xvsxpa12p8hcjqap2pf72zqlwpm165js9kwbgm2sf977c"; }; } { @@ -68,17 +77,8 @@ fetch = { type = "git"; url = "https://github.com/imdario/mergo"; - rev = "d806ba8c21777d504a2090a2ca4913c750dd3a33"; - sha256 = "12n3lfbfxvnag916c6dpxl48j29s482zwsqjc6wk4vb68qbz2nl3"; - }; - } - { - goPackagePath = "github.com/satori/go.uuid"; - fetch = { - type = "git"; - url = "https://github.com/satori/go.uuid"; - rev = "5bf94b69c6b68ee1b541973bb8e1144db23a194b"; - sha256 = "0l782l4srv36pj8pfgn61996d0vjifld4a569rbjwq5h14pd0c07"; + rev = "9f23e2d6bd2a77f959b2bf6acdbefd708a83a4a4"; + sha256 = "1lbzy8p8wv439sqgf0n21q52flf2wbamp6qa1jkyv6an0nc952q7"; }; } { @@ -86,8 +86,8 @@ fetch = { type = "git"; url = "https://go.googlesource.com/crypto"; - rev = "ab89591268e0c8b748cbe4047b00197516011af5"; - sha256 = "1cbg8wlv1hmdps9ksa4kym5zy0mb2yjykw4ns7yqv7nmz4s5xajr"; + rev = "de0752318171da717af4ce24d0a2e8626afaeb11"; + sha256 = "1ps1dl2a5lwr3vbwcy8n4i1v73m567y024sk961fk281phrzp13i"; }; } { @@ -95,8 +95,8 @@ fetch = { type = "git"; url = "https://gopkg.in/alecthomas/kingpin.v2"; - rev = "1087e65c9441605df944fb12c33f0fe7072d18ca"; - sha256 = "18llqzkdqf62qbqcv2fd3j0igl6cwwn4dissf5skkvxrcxjcmmj0"; + rev = "947dcec5ba9c011838740e680966fd7087a71d0d"; + sha256 = "0mndnv3hdngr3bxp7yxfd47cas4prv98sqw534mx7vp38gd88n5r"; }; } { @@ -104,8 +104,8 @@ fetch = { type = "git"; url = "https://gopkg.in/yaml.v2"; - rev = "eb3733d160e74a9c7e442f435eb3bea458e1d19f"; - sha256 = "1srhvcaa9db3a6xj29mkjr5kg33y71pclrlx4vcwz5m1lgb5c7q6"; + rev = "5420a8b6744d3b0345ab293f6fcba19c978f1183"; + sha256 = "0dwjrs2lp2gdlscs7bsrmyc5yf6mm4fvgw71bzr9mv2qrd2q73s1"; }; } ] From f31a7d33b65b88d3193abe694bebefee3e86b484 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 15 Aug 2018 21:15:52 +0200 Subject: [PATCH 147/166] chore(brew): Update Homebrew formula for 1.7.0 --- kontemplate.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kontemplate.rb b/kontemplate.rb index 1b33be2ea..dd2b5a3ff 100644 --- a/kontemplate.rb +++ b/kontemplate.rb @@ -3,9 +3,9 @@ class Kontemplate < Formula desc "Kontemplate - Extremely simple Kubernetes resource templates" homepage "https://github.com/tazjin/kontemplate" - url "https://github.com/tazjin/kontemplate/releases/download/v1.6.0/kontemplate-1.6.0-97bef90-darwin-amd64.tar.gz" - sha256 "d21529153d369d2347477f981a855525695c0bc2912a50f05d3baf15c96f7c16" - version "1.6.0-97bef90" + url "https://github.com/tazjin/kontemplate/releases/download/v1.7.0/kontemplate-1.7.0-511ae92-darwin-amd64.tar.gz" + sha256 "44910488c0e0480e306cc7b1de564a4c0d39013130f2f6e89bcd9a7401ef6a9a" + version "kontemplate-1.7.0-511ae92" def install bin.install "kontemplate" From 20b9432f16c8d183bb787eddbe1c59b037c3f953 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 15 Aug 2018 21:21:09 +0200 Subject: [PATCH 148/166] chore(image): Update image for Kontemplate 1.7.0 Version changes: * Kontemplate 1.7.0 * Kubectl 1.11.0 * Alpine 3.8 --- image/Dockerfile | 6 +++--- image/hashes | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/image/Dockerfile b/image/Dockerfile index 12d24fffe..404c95aac 100644 --- a/image/Dockerfile +++ b/image/Dockerfile @@ -1,8 +1,8 @@ -FROM alpine:3.7 +FROM alpine:3.8 ADD hashes /root/hashes -ADD https://storage.googleapis.com/kubernetes-release/release/v1.10.4/bin/linux/amd64/kubectl /usr/bin/kubectl -ADD https://github.com/tazjin/kontemplate/releases/download/v1.6.0/kontemplate-1.6.0-97bef90-linux-amd64.tar.gz /tmp/kontemplate.tar.gz +ADD https://storage.googleapis.com/kubernetes-release/release/v1.11.0/bin/linux/amd64/kubectl /usr/bin/kubectl +ADD https://github.com/tazjin/kontemplate/releases/download/v1.7.0/kontemplate-1.7.0-511ae92-linux-amd64.tar.gz /tmp/kontemplate.tar.gz # Pass release version is 1.7.1 ADD https://raw.githubusercontent.com/zx2c4/password-store/38ec1c72e29c872ec0cdde82f75490640d4019bf/src/password-store.sh /usr/bin/pass diff --git a/image/hashes b/image/hashes index 28212e483..95adb9910 100644 --- a/image/hashes +++ b/image/hashes @@ -1,2 +1,2 @@ -dc97c5547766cfb9cdc40c164113ec43938c6928ccf787e899f700998faad2a4 /tmp/kontemplate.tar.gz -92ef1ab45e9564bf3225b7b08ada61cfb04d872f5d279cbb2d1781d8060fe83b /usr/bin/kubectl +d9a48cd9d84a30e6f1a3e965f73003486ca2afb0f81f74959dc9d96c7be4823c /tmp/kontemplate.tar.gz +7fc84102a20aba4c766245714ce9555e3bf5d4116aab38a15b11419070a0fa90 /usr/bin/kubectl From 5f433c46c16f4466ad17578eb1762399aa7746d1 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 15 Aug 2018 21:23:12 +0200 Subject: [PATCH 149/166] docs(README): Add note about installing on NixOS --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index dd5280a97..f95c9a1e0 100644 --- a/README.md +++ b/README.md @@ -110,6 +110,12 @@ brew tap tazjin/kontemplate https://github.com/tazjin/kontemplate brew install kontemplate ``` +### NixOS + +Kontemplate has been included in [NixOS](https://nixos.org/) since version 17.09. + +It is available as `kontemplate` from the default Nix package set. + ### Arch Linux An [AUR package][] is available for Arch Linux and other `pacman`-based distributions. From 54db9785d65d8a9dfc78d1cf230810b19f1abffb Mon Sep 17 00:00:00 2001 From: Martin Lehmann Date: Thu, 15 Nov 2018 13:49:23 +0100 Subject: [PATCH 150/166] feat(main): Support specifying kubectl args in ResourceSets --- context/context.go | 3 ++ context/context_test.go | 36 +++++++++++++++++++++++ context/testdata/flat-with-args-test.yaml | 9 ++++++ docs/resource-sets.md | 7 +++++ main.go | 6 ++-- templater/templater.go | 2 ++ 6 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 context/testdata/flat-with-args-test.yaml diff --git a/context/context.go b/context/context.go index 49de7f469..314fc3584 100644 --- a/context/context.go +++ b/context/context.go @@ -28,6 +28,9 @@ type ResourceSet struct { // Values to include when interpolating resources from this resource set. Values map[string]interface{} `json:"values"` + // Args to pass on to kubectl for this resource set. + Args []string `json:"args"` + // Nested resource sets to include Include []ResourceSet `json:"include"` diff --git a/context/context_test.go b/context/context_test.go index 34c77cc9b..d7fdc11e5 100644 --- a/context/context_test.go +++ b/context/context_test.go @@ -54,6 +54,42 @@ func TestLoadFlatContextFromFile(t *testing.T) { } } +func TestLoadContextWithArgs(t *testing.T) { + ctx, err := LoadContext("testdata/flat-with-args-test.yaml", &noExplicitVars) + + if err != nil { + t.Error(err) + t.Fail() + } + + expected := Context{ + Name: "k8s.prod.mydomain.com", + ResourceSets: []ResourceSet{ + { + Name: "some-api", + Path: "some-api", + Values: make(map[string]interface{}, 0), + Args: []string{ + "--as=some-user", + "--as-group=hello:world", + "--as-banana", + "true", + }, + Include: nil, + Parent: "", + }, + }, + BaseDir: "testdata", + ImportedVars: make(map[string]interface{}, 0), + ExplicitVars: make(map[string]interface{}, 0), + } + + if !reflect.DeepEqual(*ctx, expected) { + t.Error("Loaded context and expected context did not match") + t.Fail() + } +} + func TestLoadContextWithResourceSetCollections(t *testing.T) { ctx, err := LoadContext("testdata/collections-test.yaml", &noExplicitVars) diff --git a/context/testdata/flat-with-args-test.yaml b/context/testdata/flat-with-args-test.yaml new file mode 100644 index 000000000..29d3334fb --- /dev/null +++ b/context/testdata/flat-with-args-test.yaml @@ -0,0 +1,9 @@ +--- +context: k8s.prod.mydomain.com +include: + - name: some-api + args: + - --as=some-user + - --as-group=hello:world + - --as-banana + - "true" diff --git a/docs/resource-sets.md b/docs/resource-sets.md index 94d402f9a..1444dd491 100644 --- a/docs/resource-sets.md +++ b/docs/resource-sets.md @@ -16,6 +16,7 @@ Technically a resource set is simply a folder with a few YAML and/or JSON templa - [`name`](#name) - [`path`](#path) - [`values`](#values) + - [`args`](#args) - [`include`](#include) - [Multiple includes](#multiple-includes) - [Nesting resource sets](#nesting-resource-sets) @@ -81,6 +82,12 @@ The `values` field specifies key/values pairs of variables that should be availa This field is **optional**. +### `args` + +The `args` field specifies a list of arguments that should be passed to `kubectl`. + +This field is **optional**. + ### `include` The `include` field specifies additional resource sets that should be included and that should inherit the diff --git a/main.go b/main.go index 6dacc5275..3d20adde3 100644 --- a/main.go +++ b/main.go @@ -199,7 +199,7 @@ func loadContextAndResources(file *string) (*context.Context, *[]templater.Rende } func runKubectlWithResources(c *context.Context, kubectlArgs *[]string, resourceSets *[]templater.RenderedResourceSet) error { - args := append(*kubectlArgs, fmt.Sprintf("--context=%s", c.Name)) + argsWithContext := append(*kubectlArgs, fmt.Sprintf("--context=%s", c.Name)) for _, rs := range *resourceSets { if len(rs.Resources) == 0 { @@ -207,7 +207,9 @@ func runKubectlWithResources(c *context.Context, kubectlArgs *[]string, resource continue } - kubectl := exec.Command(*kubectlBin, args...) + argsWithResourceSetArgs := append(argsWithContext, rs.Args...) + + kubectl := exec.Command(*kubectlBin, argsWithResourceSetArgs...) stdin, err := kubectl.StdinPipe() if err != nil { diff --git a/templater/templater.go b/templater/templater.go index ff4e0efde..e842daeba 100644 --- a/templater/templater.go +++ b/templater/templater.go @@ -35,6 +35,7 @@ type RenderedResource struct { type RenderedResourceSet struct { Name string Resources []RenderedResource + Args []string } func LoadAndApplyTemplates(include *[]string, exclude *[]string, c *context.Context) ([]RenderedResourceSet, error) { @@ -94,6 +95,7 @@ func processResourceSet(ctx *context.Context, rs *context.ResourceSet) (*Rendere return &RenderedResourceSet{ Name: rs.Name, Resources: resources, + Args: rs.Args, }, nil } From a53e75741fecc3416c0b6b7e5d5468d8913da48f Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Mon, 21 Jan 2019 11:01:31 +0100 Subject: [PATCH 151/166] fix(build): Depend on GNU Parallel during build CI builds for `master` track unstable nixpkgs releases, in which parallel is no longer part of the standard build environment. However, this tool is required for executing some of the tests. --- default.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/default.nix b/default.nix index f61d83153..e2c1a5650 100644 --- a/default.nix +++ b/default.nix @@ -18,6 +18,7 @@ with pkgs; buildGoPackage rec { src = ./.; goPackagePath = "github.com/tazjin/kontemplate"; goDeps = ./deps.nix; + buildInputs = [ parallel ]; # Enable checks and configure check-phase to include vet: doCheck = true; From 2682d05ffcadc3f594aab883a8ca56667d479780 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Mon, 21 Jan 2019 11:10:36 +0100 Subject: [PATCH 152/166] chore(build): Move Nix commit for releases to latest NixOS stable Updates a few build dependencies, most notably Go to 1.11. The chosen commit is the current stable `nixos-18.09` tag in nixpkgs-channels. While doing this I also checked whether it is now possible to remove the exception for allowing references, but it's not. Go may in the future gain support for fully reproducible builds, see for example issue #16860 on their compiler. At that point maybe we can drop a few settings here. --- release.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/release.nix b/release.nix index fb80a0e42..9b3ff55c1 100644 --- a/release.nix +++ b/release.nix @@ -13,8 +13,8 @@ let pkgs = import ((import {}).fetchFromGitHub { owner = "NixOS"; repo = "nixpkgs"; - rev = "1bc5bf4beb759e563ffc7a8a3067f10a00b45a7d"; - sha256 = "00gd96p7yz3rgpjjkizp397y2syfc272yvwxqixbjd1qdshbizmj"; + rev = "50f41ea2fcf86def32799f75577a4fe5cfd1132e"; + sha256 = "1q0bxl5nxx1kabqvyzkdw91c5dnwpi2rwsgs5jdmnj7f0qqgdxh8"; }) {}; in with pkgs; buildGoPackage rec { name = "kontemplate-${version}"; From 245d3bd1548b9cd11e3d2bf9bd93550477da1541 Mon Sep 17 00:00:00 2001 From: Felix Wiedmann Date: Sat, 19 Jan 2019 23:10:28 +0100 Subject: [PATCH 153/166] chore(build): kubectl upgraded to the latest stable version (1.13.2) Updates the kubectl version used in the Docker build to the latest stable release. Please see the Kubernetes documentation for details. --- image/Dockerfile | 2 +- image/hashes | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/image/Dockerfile b/image/Dockerfile index 404c95aac..d87f010e4 100644 --- a/image/Dockerfile +++ b/image/Dockerfile @@ -1,7 +1,7 @@ FROM alpine:3.8 ADD hashes /root/hashes -ADD https://storage.googleapis.com/kubernetes-release/release/v1.11.0/bin/linux/amd64/kubectl /usr/bin/kubectl +ADD https://storage.googleapis.com/kubernetes-release/release/v1.13.2/bin/linux/amd64/kubectl /usr/bin/kubectl ADD https://github.com/tazjin/kontemplate/releases/download/v1.7.0/kontemplate-1.7.0-511ae92-linux-amd64.tar.gz /tmp/kontemplate.tar.gz # Pass release version is 1.7.1 diff --git a/image/hashes b/image/hashes index 95adb9910..d6b4c2692 100644 --- a/image/hashes +++ b/image/hashes @@ -1,2 +1,2 @@ d9a48cd9d84a30e6f1a3e965f73003486ca2afb0f81f74959dc9d96c7be4823c /tmp/kontemplate.tar.gz -7fc84102a20aba4c766245714ce9555e3bf5d4116aab38a15b11419070a0fa90 /usr/bin/kubectl +2c7ab398559c7f4f91102c4a65184e0a5a3a137060c3179e9361d9c20b467181 /usr/bin/kubectl From 8abe03de3c75dd1dc1f2f65ff71b1ec3888208c2 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 1 May 2019 16:18:10 +0000 Subject: [PATCH 154/166] fix(build): Make Nix build work with recent nixpkgs Changes in the Go build environment for Nix have caused the parallel vetting to run in a subshell which does not have the required function definitions. This commit fixes the error by not running vet in parallel (there's no point to that anyways as the project is tiny). --- default.nix | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/default.nix b/default.nix index e2c1a5650..b2f1c284f 100644 --- a/default.nix +++ b/default.nix @@ -23,7 +23,9 @@ with pkgs; buildGoPackage rec { # Enable checks and configure check-phase to include vet: doCheck = true; preCheck = '' - getGoDirs "" | parallel -j $NIX_BUILD_CORES buildGoDir vet + for pkg in $(getGoDirs ""); do + buildGoDir vet "$pkg" + done ''; meta = with lib; { From 6353ed9d142412ee2c9c656c47459c56b3cdf698 Mon Sep 17 00:00:00 2001 From: Jude Venn Date: Mon, 29 Apr 2019 23:13:15 +0100 Subject: [PATCH 155/166] feat(templater): Add insertTemplate function Similar to insertFile, but runs the templating against the file before inserting. This is useful for sharing common config between yaml files, e.g. volume mounts in a deployment.yaml and cronjob.yaml, and it's useful to be able to use the `configHash` annotation pattern with a templated configmap.yaml --- docs/templates.md | 4 +++- templater/templater.go | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/docs/templates.md b/docs/templates.md index 6c44035d0..32da20510 100644 --- a/docs/templates.md +++ b/docs/templates.md @@ -72,13 +72,15 @@ right. Some template functions come from Go's standard library and are listed in the [Go documentation][]. In addition the functions declared by [sprig][] are -available in kontemplate, as well as three custom functions: +available in kontemplate, as well as five custom functions: * `json`: Encodes any supplied data structure as JSON. * `gitHEAD`: Retrieves the commit hash at Git `HEAD`. * `passLookup`: Looks up the supplied key in [pass][]. * `insertFile`: Insert the contents of the given file in the resource set folder as a string. +* `insertTemplate`: Insert the contents of the given template in the resource + set folder as a string. ## Examples: diff --git a/templater/templater.go b/templater/templater.go index e842daeba..55b64766c 100644 --- a/templater/templater.go +++ b/templater/templater.go @@ -205,6 +205,14 @@ func templateFuncs(c *context.Context, rs *context.ResourceSet) template.FuncMap return string(data), nil } + m["insertTemplate"] = func(file string) (string, error) { + data, err := templateFile(c, rs, path.Join(rs.Path, file)) + if err != nil { + return "", err + } + + return data.Rendered, nil + } m["default"] = func(defaultVal interface{}, varName string) interface{} { if val, ok := rs.Values[varName]; ok { return val From c422686f8488df917d206b36354ca7903a0906d3 Mon Sep 17 00:00:00 2001 From: Jude Venn Date: Tue, 30 Apr 2019 08:44:06 +0100 Subject: [PATCH 156/166] test(templater): Add a test for the `insertTemplate` function --- templater/templater_test.go | 24 ++++++++++++++++++++++ templater/testdata/test-insertTemplate.txt | 1 + 2 files changed, 25 insertions(+) create mode 100644 templater/testdata/test-insertTemplate.txt diff --git a/templater/templater_test.go b/templater/templater_test.go index 2a9ddaa2b..4237681c1 100644 --- a/templater/templater_test.go +++ b/templater/templater_test.go @@ -179,3 +179,27 @@ func TestDefaultTemplateFunction(t *testing.T) { t.Fail() } } + +func TestInsertTemplateFunction(t *testing.T) { + ctx := context.Context{} + resourceSet := context.ResourceSet{ + Path: "testdata", + Values: map[string]interface{}{ + "testName": "TestInsertTemplateFunction", + }, + } + + res, err := templateFile(&ctx, &resourceSet, "testdata/test-insertTemplate.txt") + + if err != nil { + t.Error(err) + t.Errorf("Templating with an insertTemplate call should have succeeded.\n") + t.Fail() + } + + if res.Rendered != "Inserting \"Template for test TestInsertTemplateFunction\".\n" { + t.Error("Result does not contain expected rendered template value.") + t.Error(res.Rendered) + t.Fail() + } +} diff --git a/templater/testdata/test-insertTemplate.txt b/templater/testdata/test-insertTemplate.txt new file mode 100644 index 000000000..8155e174f --- /dev/null +++ b/templater/testdata/test-insertTemplate.txt @@ -0,0 +1 @@ +Inserting "{{ insertTemplate "test-template.txt" | trim }}". From 75a3cd2534bb9138b3f42f4591a65a954c2be9b4 Mon Sep 17 00:00:00 2001 From: Jude Venn Date: Tue, 30 Apr 2019 10:05:09 +0100 Subject: [PATCH 157/166] docs: Update `configHash` tip to use `insertTemplate` function An advantage of this method over the previous one is that any edits to the ConfigMap yaml file will also trigger a rolling update. It also keeps knowledge of what the ConfigMap contains inside its yaml file instead of the Deployment needing to know which variables to hash. --- docs/tips-and-tricks.md | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/docs/tips-and-tricks.md b/docs/tips-and-tricks.md index 73328fa0a..5401ac91e 100644 --- a/docs/tips-and-tricks.md +++ b/docs/tips-and-tricks.md @@ -17,24 +17,26 @@ of Deployments and other resource types when `ConfigMap` or `Secret` objects are updated. It is possible to make use of annotations and templating functions in -Kontemplate to force updates to these resources anyways (assuming that the -`ConfigMap` or `Secret` contains interpolated variables). +Kontemplate to force updates to these resources anyways. For example: ```yaml -# A ConfigMap that contains some data structure in JSON format +# A ConfigMap that contains some configuration for your app --- kind: ConfigMap metadata: name: app-config data: - configFile: {{ .appConfig | json }} + app.conf: | + name: {{ .appName }} + foo: bar ``` -Now whenever the `appConfig` variable changes we would like to update the -`Deployment` making use of it, too. We can do this by adding a hash of the -configuration to the annotations of the created `Pod` objects: +Now whenever the `appName` variable changes or we make an edit to the +`ConfigMap` we would like to update the `Deployment` making use of it, too. We +can do this by adding a hash of the parsed template to the annotations of the +created `Pod` objects: ```yaml @@ -46,7 +48,7 @@ spec: template: metadata: annotations: - configHash: {{ .appConfig | json | sha256sum }} + configHash: {{ insertTemplate "app-config.yaml" | sha256sum }} spec: containers: - name: app @@ -60,9 +62,9 @@ spec: name: app-config ``` -Now if the `ConfigMap` object appears first in the resource files, `kubectl` -will apply the resources sequentially and the updated annotation will cause -a rolling update of all relevant pods. +Now any change to the `ConfigMap` - either by directly editing the yaml file or +via a changed template variable - will cause the annotation to change, +triggering a rolling update of all relevant pods. ## direnv & pass From d0f52766b35a9ccccb131fbbb33f6398ea6042d2 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 4 Sep 2019 10:56:07 +0100 Subject: [PATCH 158/166] fix(context): Ensure resource set paths are made absolute Resolving of files (for `insertFile` and `insertTemplate`) should always be relative to the resource set location, the previous behaviour was considered a bug. This is fixed by ensuring that resource set paths are absolute at context loading time. --- context/context.go | 12 +++++++++--- context/context_test.go | 18 +++++++++--------- templater/templater.go | 9 ++++----- 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/context/context.go b/context/context.go index 314fc3584..262e8c948 100644 --- a/context/context.go +++ b/context/context.go @@ -77,7 +77,7 @@ func LoadContext(filename string, explicitVars *[]string) (*Context, error) { ctx.BaseDir = path.Dir(filename) // Prepare the resource sets by resolving parents etc. - ctx.ResourceSets = flattenPrepareResourceSetPaths(&ctx.ResourceSets) + ctx.ResourceSets = flattenPrepareResourceSetPaths(&ctx.BaseDir, &ctx.ResourceSets) // Add variables explicitly specified on the command line ctx.ExplicitVars, err = loadExplicitVars(explicitVars) @@ -136,7 +136,7 @@ func (ctx *Context) loadImportedVariables() (map[string]interface{}, error) { // collections, i.e. resource sets that themselves have an additional 'include' field set. // Those will be regarded as a short-hand for including multiple resource sets from a subfolder. // See https://github.com/tazjin/kontemplate/issues/9 for more information. -func flattenPrepareResourceSetPaths(rs *[]ResourceSet) []ResourceSet { +func flattenPrepareResourceSetPaths(baseDir *string, rs *[]ResourceSet) []ResourceSet { flattened := make([]ResourceSet, 0) for _, r := range *rs { @@ -146,6 +146,12 @@ func flattenPrepareResourceSetPaths(rs *[]ResourceSet) []ResourceSet { r.Path = r.Name } + // Paths are made absolute by resolving them relative to the context base, + // unless absolute paths were specified. + if !path.IsAbs(r.Path) { + r.Path = path.Join(*baseDir, r.Path) + } + if len(r.Include) == 0 { flattened = append(flattened, r) } else { @@ -225,7 +231,7 @@ func loadDefaultValues(rs *ResourceSet, c *Context) *map[string]interface{} { var defaultVars map[string]interface{} for _, filename := range util.DefaultFilenames { - err := util.LoadData(path.Join(c.BaseDir, rs.Path, filename), &defaultVars) + err := util.LoadData(path.Join(rs.Path, filename), &defaultVars) if err == nil { return &defaultVars } diff --git a/context/context_test.go b/context/context_test.go index d7fdc11e5..5d6164585 100644 --- a/context/context_test.go +++ b/context/context_test.go @@ -32,7 +32,7 @@ func TestLoadFlatContextFromFile(t *testing.T) { ResourceSets: []ResourceSet{ { Name: "some-api", - Path: "some-api", + Path: "testdata/some-api", Values: map[string]interface{}{ "apiPort": float64(4567), // yep! "importantFeature": true, @@ -67,7 +67,7 @@ func TestLoadContextWithArgs(t *testing.T) { ResourceSets: []ResourceSet{ { Name: "some-api", - Path: "some-api", + Path: "testdata/some-api", Values: make(map[string]interface{}, 0), Args: []string{ "--as=some-user", @@ -106,7 +106,7 @@ func TestLoadContextWithResourceSetCollections(t *testing.T) { ResourceSets: []ResourceSet{ { Name: "some-api", - Path: "some-api", + Path: "testdata/some-api", Values: map[string]interface{}{ "apiPort": float64(4567), // yep! "importantFeature": true, @@ -118,7 +118,7 @@ func TestLoadContextWithResourceSetCollections(t *testing.T) { }, { Name: "collection/nested", - Path: "collection/nested", + Path: "testdata/collection/nested", Values: map[string]interface{}{ "lizards": "good", "globalVar": "lizards", @@ -152,7 +152,7 @@ func TestSubresourceVariableInheritance(t *testing.T) { ResourceSets: []ResourceSet{ { Name: "parent/child", - Path: "parent/child", + Path: "testdata/parent/child", Values: map[string]interface{}{ "foo": "bar", "bar": "baz", @@ -185,7 +185,7 @@ func TestSubresourceVariableInheritanceOverride(t *testing.T) { ResourceSets: []ResourceSet{ { Name: "parent/child", - Path: "parent/child", + Path: "testdata/parent/child", Values: map[string]interface{}{ "foo": "newvalue", }, @@ -256,7 +256,7 @@ func TestExplicitPathLoading(t *testing.T) { ResourceSets: []ResourceSet{ { Name: "some-api-europe", - Path: "some-api", + Path: "testdata/some-api", Values: map[string]interface{}{ "location": "europe", }, @@ -265,7 +265,7 @@ func TestExplicitPathLoading(t *testing.T) { }, { Name: "some-api-asia", - Path: "some-api", + Path: "testdata/some-api", Values: map[string]interface{}{ "location": "asia", }, @@ -296,7 +296,7 @@ func TestExplicitSubresourcePathLoading(t *testing.T) { ResourceSets: []ResourceSet{ { Name: "parent/child", - Path: "parent-path/child-path", + Path: "testdata/parent-path/child-path", Parent: "parent", Values: make(map[string]interface{}, 0), }, diff --git a/templater/templater.go b/templater/templater.go index 55b64766c..6cf8a4c2e 100644 --- a/templater/templater.go +++ b/templater/templater.go @@ -62,8 +62,7 @@ func LoadAndApplyTemplates(include *[]string, exclude *[]string, c *context.Cont func processResourceSet(ctx *context.Context, rs *context.ResourceSet) (*RenderedResourceSet, error) { fmt.Fprintf(os.Stderr, "Loading resources for %s\n", rs.Name) - resourcePath := path.Join(ctx.BaseDir, rs.Path) - fileInfo, err := os.Stat(resourcePath) + fileInfo, err := os.Stat(rs.Path) if err != nil { return nil, err } @@ -78,13 +77,13 @@ func processResourceSet(ctx *context.Context, rs *context.ResourceSet) (*Rendere // list of files instead. // This will end up printing a warning to the user, but it // won't stop the rest of the process. - files, _ = ioutil.ReadDir(resourcePath) + files, _ = ioutil.ReadDir(rs.Path) resources, err = processFiles(ctx, rs, files) if err != nil { return nil, err } } else { - resource, err := templateFile(ctx, rs, resourcePath) + resource, err := templateFile(ctx, rs, rs.Path) if err != nil { return nil, err } @@ -104,7 +103,7 @@ func processFiles(ctx *context.Context, rs *context.ResourceSet, files []os.File for _, file := range files { if !file.IsDir() && isResourceFile(file) { - path := path.Join(ctx.BaseDir, rs.Path, file.Name()) + path := path.Join(rs.Path, file.Name()) res, err := templateFile(ctx, rs, path) if err != nil { From 5b82bc60fc8972d083f4f9146647cc161578a7c1 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 4 Sep 2019 11:07:49 +0100 Subject: [PATCH 159/166] chore: Update dependency versions --- deps.nix | 106 +++++++++++++++++++++++++++---------------------------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/deps.nix b/deps.nix index f0a52f526..7693968bd 100644 --- a/deps.nix +++ b/deps.nix @@ -1,111 +1,111 @@ -# This file was generated by https://github.com/kamilchm/go2nix v1.2.1 +# This file was generated by https://github.com/kamilchm/go2nix v1.3.0 [ + { + goPackagePath = "github.com/Masterminds/goutils"; + fetch = { + type = "git"; + url = "https://github.com/Masterminds/goutils"; + rev = "41ac8693c5c10a92ea1ff5ac3a7f95646f6123b0"; + sha256 = "180px47gj936qyk5bkv5mbbgiil9abdjq6kwkf7sq70vyi9mcfiq"; + }; + } { goPackagePath = "github.com/Masterminds/semver"; fetch = { - type = "git"; - url = "https://github.com/Masterminds/semver"; - rev = "c84ddcca87bf5a941b138dde832a7e20b0159ad8"; - sha256 = "1dcfdr018a0yszjpvr3wshvq9cc3kvy95l55si556p617wsn1wan"; + type = "git"; + url = "https://github.com/Masterminds/semver"; + rev = "5bc3b9184d48f1412b300b87a200cf020d9254cf"; + sha256 = "1vdfm653v50jf63cw0kg2hslx50cn4mk6lj3p51bi11jrg48kfng"; }; } { goPackagePath = "github.com/Masterminds/sprig"; fetch = { - type = "git"; - url = "https://github.com/Masterminds/sprig"; - rev = "77bb58b7f5e10889a1195c21b9e7a96ee166f199"; - sha256 = "0q4g12f3nvda1skz33qzbbdd2vj3gjfwf361hyzlx20s71brk3bk"; + type = "git"; + url = "https://github.com/Masterminds/sprig"; + rev = "6f509977777c33eae63b2136d97f7b976cb971cc"; + sha256 = "05h9k6fhjxnpwlihj3z02q9kvqvnq53jix0ab84sx0666bci3cdh"; }; } { goPackagePath = "github.com/alecthomas/template"; fetch = { - type = "git"; - url = "https://github.com/alecthomas/template"; - rev = "a0175ee3bccc567396460bf5acd36800cb10c49c"; - sha256 = "0qjgvvh26vk1cyfq9fadyhfgdj36f1iapbmr5xp6zqipldz8ffxj"; + type = "git"; + url = "https://github.com/alecthomas/template"; + rev = "fb15b899a75114aa79cc930e33c46b577cc664b1"; + sha256 = "1vlasv4dgycydh5wx6jdcvz40zdv90zz1h7836z7lhsi2ymvii26"; }; } { goPackagePath = "github.com/alecthomas/units"; fetch = { - type = "git"; - url = "https://github.com/alecthomas/units"; - rev = "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a"; - sha256 = "1j65b91qb9sbrml9cpabfrcf07wmgzzghrl7809hjjhrmbzri5bl"; - }; - } - { - goPackagePath = "github.com/aokoli/goutils"; - fetch = { - type = "git"; - url = "https://github.com/aokoli/goutils"; - rev = "3391d3790d23d03408670993e957e8f408993c34"; - sha256 = "1yj4yjfwylica31sgj69ygb04p9xxi22kgfxd0j5f58zr8vwww2n"; + type = "git"; + url = "https://github.com/alecthomas/units"; + rev = "c3de453c63f4bdb4dadffab9805ec00426c505f7"; + sha256 = "0js37zlgv37y61j4a2d46jh72xm5kxmpaiw0ya9v944bjpc386my"; }; } { goPackagePath = "github.com/ghodss/yaml"; fetch = { - type = "git"; - url = "https://github.com/ghodss/yaml"; - rev = "e9ed3c6dfb39bb1a32197cb10d527906fe4da4b6"; - sha256 = "07cf0j3wbsl1gmn175mdgljcarfz4xbqd6pgc7b08a5lcn7zwhjz"; + type = "git"; + url = "https://github.com/ghodss/yaml"; + rev = "25d852aebe32c875e9c044af3eef9c7dc6bc777f"; + sha256 = "1w9yq0bxzygc4qwkwwiy7k1k1yviaspcqqv18255k2xkjv5ipccz"; }; } { goPackagePath = "github.com/google/uuid"; fetch = { - type = "git"; - url = "https://github.com/google/uuid"; - rev = "dec09d789f3dba190787f8b4454c7d3c936fed9e"; - sha256 = "1hc4w67p6zkh2qk7wm1yrl69jjrjjk615mly5ll4iidn1m4mzi4i"; + type = "git"; + url = "https://github.com/google/uuid"; + rev = "c2e93f3ae59f2904160ceaab466009f965df46d6"; + sha256 = "0zw8fvl6jqg0fmv6kmvhss0g4gkrbvgyvl2zgy5wdbdlgp4fja0h"; }; } { goPackagePath = "github.com/huandu/xstrings"; fetch = { - type = "git"; - url = "https://github.com/huandu/xstrings"; - rev = "7bb0250b58e5c15670406e6f93ffda43281305b1"; - sha256 = "1fc8q65xvsxpa12p8hcjqap2pf72zqlwpm165js9kwbgm2sf977c"; + type = "git"; + url = "https://github.com/huandu/xstrings"; + rev = "8bbcf2f9ccb55755e748b7644164cd4bdce94c1d"; + sha256 = "1ivvc95514z63k7cpz71l0dwlanffmsh1pijhaqmp41kfiby8rsx"; }; } { goPackagePath = "github.com/imdario/mergo"; fetch = { - type = "git"; - url = "https://github.com/imdario/mergo"; - rev = "9f23e2d6bd2a77f959b2bf6acdbefd708a83a4a4"; - sha256 = "1lbzy8p8wv439sqgf0n21q52flf2wbamp6qa1jkyv6an0nc952q7"; + type = "git"; + url = "https://github.com/imdario/mergo"; + rev = "4c317f2286be3bd0c4f1a0e622edc6398ec4656d"; + sha256 = "0bihha1qsgfjk14yv1hwddv3d8dzxpbjlaxwwyys6lhgxz1cr9h9"; }; } { goPackagePath = "golang.org/x/crypto"; fetch = { - type = "git"; - url = "https://go.googlesource.com/crypto"; - rev = "de0752318171da717af4ce24d0a2e8626afaeb11"; - sha256 = "1ps1dl2a5lwr3vbwcy8n4i1v73m567y024sk961fk281phrzp13i"; + type = "git"; + url = "https://go.googlesource.com/crypto"; + rev = "9756ffdc24725223350eb3266ffb92590d28f278"; + sha256 = "0q7hxaaq6lp0v8qqzifvysl47z5rfdlrxkh3d29vsl3wyby3dxl8"; }; } { goPackagePath = "gopkg.in/alecthomas/kingpin.v2"; fetch = { - type = "git"; - url = "https://gopkg.in/alecthomas/kingpin.v2"; - rev = "947dcec5ba9c011838740e680966fd7087a71d0d"; + type = "git"; + url = "https://gopkg.in/alecthomas/kingpin.v2"; + rev = "947dcec5ba9c011838740e680966fd7087a71d0d"; sha256 = "0mndnv3hdngr3bxp7yxfd47cas4prv98sqw534mx7vp38gd88n5r"; }; } { goPackagePath = "gopkg.in/yaml.v2"; fetch = { - type = "git"; - url = "https://gopkg.in/yaml.v2"; - rev = "5420a8b6744d3b0345ab293f6fcba19c978f1183"; - sha256 = "0dwjrs2lp2gdlscs7bsrmyc5yf6mm4fvgw71bzr9mv2qrd2q73s1"; + type = "git"; + url = "https://gopkg.in/yaml.v2"; + rev = "51d6538a90f86fe93ac480b35f37b2be17fef232"; + sha256 = "01wj12jzsdqlnidpyjssmj0r4yavlqy7dwrg7adqd8dicjc4ncsa"; }; } ] From 7b9ae4831d1515916c0aed3d98c2a143bcf04c49 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 4 Sep 2019 11:15:40 +0100 Subject: [PATCH 160/166] chore: Update copyright headers to 2019 --- build-release.sh | 2 +- context/context.go | 2 +- context/context_test.go | 2 +- default.nix | 2 +- main.go | 2 +- release.nix | 2 +- templater/dns.go | 2 +- templater/pass.go | 2 +- templater/templater.go | 2 +- templater/templater_test.go | 2 +- util/util.go | 2 +- util/util_test.go | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/build-release.sh b/build-release.sh index 1dfae4ddc..a147e674e 100755 --- a/build-release.sh +++ b/build-release.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -ueo pipefail -# Copyright (C) 2016-2017 Vincent Ambo +# Copyright (C) 2016-2019 Vincent Ambo # # This file is part of Kontemplate. # diff --git a/context/context.go b/context/context.go index 262e8c948..2d0378a0e 100644 --- a/context/context.go +++ b/context/context.go @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2017 Vincent Ambo +// Copyright (C) 2016-2019 Vincent Ambo // // This file is part of Kontemplate. // diff --git a/context/context_test.go b/context/context_test.go index 5d6164585..7ecd9d587 100644 --- a/context/context_test.go +++ b/context/context_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2017 Vincent Ambo +// Copyright (C) 2016-2019 Vincent Ambo // // This file is part of Kontemplate. // diff --git a/default.nix b/default.nix index b2f1c284f..afec560fb 100644 --- a/default.nix +++ b/default.nix @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2018 Vincent Ambo +# Copyright (C) 2016-2019 Vincent Ambo # # This file is part of Kontemplate. # diff --git a/main.go b/main.go index 3d20adde3..ceb801b11 100644 --- a/main.go +++ b/main.go @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2017 Vincent Ambo +// Copyright (C) 2016-2019 Vincent Ambo // // Kontemplate is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by diff --git a/release.nix b/release.nix index 9b3ff55c1..54b32b0dd 100644 --- a/release.nix +++ b/release.nix @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2018 Vincent Ambo +# Copyright (C) 2016-2019 Vincent Ambo # # This file is part of Kontemplate. # diff --git a/templater/dns.go b/templater/dns.go index e6b210680..6cd974dd9 100644 --- a/templater/dns.go +++ b/templater/dns.go @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2017 Vincent Ambo +// Copyright (C) 2016-2019 Vincent Ambo // // This file is part of Kontemplate. // diff --git a/templater/pass.go b/templater/pass.go index 53d8f2f9b..f7fbcb433 100644 --- a/templater/pass.go +++ b/templater/pass.go @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2017 Vincent Ambo +// Copyright (C) 2016-2019 Vincent Ambo // // This file is part of Kontemplate. // diff --git a/templater/templater.go b/templater/templater.go index 6cf8a4c2e..a8f0c670a 100644 --- a/templater/templater.go +++ b/templater/templater.go @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2017 Vincent Ambo +// Copyright (C) 2016-2019 Vincent Ambo // // This file is part of Kontemplate. // diff --git a/templater/templater_test.go b/templater/templater_test.go index 4237681c1..c20858c20 100644 --- a/templater/templater_test.go +++ b/templater/templater_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2017 Vincent Ambo +// Copyright (C) 2016-2019 Vincent Ambo // // This file is part of Kontemplate. // diff --git a/util/util.go b/util/util.go index ee972e2ee..56fa1e3fc 100644 --- a/util/util.go +++ b/util/util.go @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2017 Vincent Ambo +// Copyright (C) 2016-2019 Vincent Ambo // // This file is part of Kontemplate. // diff --git a/util/util_test.go b/util/util_test.go index 1a17adfcd..53c560817 100644 --- a/util/util_test.go +++ b/util/util_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2017 Vincent Ambo +// Copyright (C) 2016-2019 Vincent Ambo // // This file is part of Kontemplate. // From fad8dfcef67c0ee2158dc67a90ec0566d76aa8f2 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 4 Sep 2019 11:22:26 +0100 Subject: [PATCH 161/166] chore(build): Bump version to 1.8.0 --- build-release.sh | 2 +- main.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build-release.sh b/build-release.sh index a147e674e..e4258c53d 100755 --- a/build-release.sh +++ b/build-release.sh @@ -12,7 +12,7 @@ set -ueo pipefail readonly GIT_HASH="$(git rev-parse --short HEAD)" readonly LDFLAGS="-X main.gitHash=${GIT_HASH} -w -s" -readonly VERSION="1.7.0-${GIT_HASH}" +readonly VERSION="1.8.0-${GIT_HASH}" function binary-name() { local os="${1}" diff --git a/main.go b/main.go index ceb801b11..e55d42465 100644 --- a/main.go +++ b/main.go @@ -26,7 +26,7 @@ import ( "gopkg.in/alecthomas/kingpin.v2" ) -const version string = "1.7.0" +const version string = "1.8.0" // This variable will be initialised by the Go linker during the builder var gitHash string From e7cf4668a2fd52a0e200b68ab3a81c75f514f4cd Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 4 Sep 2019 11:32:38 +0100 Subject: [PATCH 162/166] chore: Bump Nix channel used for release builds Bumps the channel to a recent NixOS 19.03 commit. --- release.nix | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/release.nix b/release.nix index 54b32b0dd..950bacc74 100644 --- a/release.nix +++ b/release.nix @@ -11,10 +11,10 @@ # several different architectures and operating systems. let pkgs = import ((import {}).fetchFromGitHub { - owner = "NixOS"; - repo = "nixpkgs"; - rev = "50f41ea2fcf86def32799f75577a4fe5cfd1132e"; - sha256 = "1q0bxl5nxx1kabqvyzkdw91c5dnwpi2rwsgs5jdmnj7f0qqgdxh8"; + owner = "NixOS"; + repo = "nixpkgs-channels"; + rev = "541d9cce8af7a490fb9085305939569567cb58e6"; + sha256 = "0jgz72hhzkd5vyq5v69vpljjlnf0lqaz7fh327bvb3cvmwbfxrja"; }) {}; in with pkgs; buildGoPackage rec { name = "kontemplate-${version}"; From 1cdfcbf879a6b199f0b20aaccf38f8e854d6f920 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 4 Sep 2019 11:38:32 +0100 Subject: [PATCH 163/166] chore(image): Bump alpine, kubectl and pass --- image/Dockerfile | 8 ++++---- image/hashes | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/image/Dockerfile b/image/Dockerfile index d87f010e4..aee1c9e64 100644 --- a/image/Dockerfile +++ b/image/Dockerfile @@ -1,11 +1,11 @@ -FROM alpine:3.8 +FROM alpine:3.10 ADD hashes /root/hashes -ADD https://storage.googleapis.com/kubernetes-release/release/v1.13.2/bin/linux/amd64/kubectl /usr/bin/kubectl +ADD https://storage.googleapis.com/kubernetes-release/release/v1.15.3/bin/linux/amd64/kubectl /usr/bin/kubectl ADD https://github.com/tazjin/kontemplate/releases/download/v1.7.0/kontemplate-1.7.0-511ae92-linux-amd64.tar.gz /tmp/kontemplate.tar.gz -# Pass release version is 1.7.1 -ADD https://raw.githubusercontent.com/zx2c4/password-store/38ec1c72e29c872ec0cdde82f75490640d4019bf/src/password-store.sh /usr/bin/pass +# Pass release version is 1.7.3 +ADD https://raw.githubusercontent.com/zx2c4/password-store/74fdfb5022f317ad48d449e29543710bdad1afda/src/password-store.sh /usr/bin/pass RUN sha256sum -c /root/hashes && \ apk add -U bash tree gnupg git && \ diff --git a/image/hashes b/image/hashes index d6b4c2692..f00b2f235 100644 --- a/image/hashes +++ b/image/hashes @@ -1,2 +1,2 @@ d9a48cd9d84a30e6f1a3e965f73003486ca2afb0f81f74959dc9d96c7be4823c /tmp/kontemplate.tar.gz -2c7ab398559c7f4f91102c4a65184e0a5a3a137060c3179e9361d9c20b467181 /usr/bin/kubectl +6e805054a1fb2280abb53f75b57a1b92bf9c66ffe0d2cdcd46e81b079d93c322 /usr/bin/kubectl From 6c3b29943ccf3e8c02310036f2c61c8d4142fe80 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 4 Sep 2019 11:46:57 +0100 Subject: [PATCH 164/166] fix(build): Explicitly patch interpreter shebangs in build --- release.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/release.nix b/release.nix index 950bacc74..4af08f50c 100644 --- a/release.nix +++ b/release.nix @@ -36,6 +36,7 @@ in with pkgs; buildGoPackage rec { buildInputs = [ git ]; buildPhase = '' cd go/src/${goPackagePath} + patchShebangs build-release.sh ./build-release.sh build ''; From 064f65dec295144dc8d440ebcf63f08eb154169d Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 4 Sep 2019 12:05:10 +0100 Subject: [PATCH 165/166] chore: Update image & Homebrew formula for 1.8.0 release --- image/Dockerfile | 2 +- image/hashes | 2 +- kontemplate.rb | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/image/Dockerfile b/image/Dockerfile index aee1c9e64..a40fa83b0 100644 --- a/image/Dockerfile +++ b/image/Dockerfile @@ -2,7 +2,7 @@ FROM alpine:3.10 ADD hashes /root/hashes ADD https://storage.googleapis.com/kubernetes-release/release/v1.15.3/bin/linux/amd64/kubectl /usr/bin/kubectl -ADD https://github.com/tazjin/kontemplate/releases/download/v1.7.0/kontemplate-1.7.0-511ae92-linux-amd64.tar.gz /tmp/kontemplate.tar.gz +ADD https://github.com/tazjin/kontemplate/releases/download/v1.8.0/kontemplate-1.8.0-6c3b299-linux-amd64.tar.gz /tmp/kontemplate.tar.gz # Pass release version is 1.7.3 ADD https://raw.githubusercontent.com/zx2c4/password-store/74fdfb5022f317ad48d449e29543710bdad1afda/src/password-store.sh /usr/bin/pass diff --git a/image/hashes b/image/hashes index f00b2f235..bfd87c020 100644 --- a/image/hashes +++ b/image/hashes @@ -1,2 +1,2 @@ -d9a48cd9d84a30e6f1a3e965f73003486ca2afb0f81f74959dc9d96c7be4823c /tmp/kontemplate.tar.gz +a39dfdd77e4655acaabe301285cf389cb5fc8145060f5677dc93db1cc20911a4 /tmp/kontemplate.tar.gz 6e805054a1fb2280abb53f75b57a1b92bf9c66ffe0d2cdcd46e81b079d93c322 /usr/bin/kubectl diff --git a/kontemplate.rb b/kontemplate.rb index dd2b5a3ff..45a31ab71 100644 --- a/kontemplate.rb +++ b/kontemplate.rb @@ -3,9 +3,9 @@ class Kontemplate < Formula desc "Kontemplate - Extremely simple Kubernetes resource templates" homepage "https://github.com/tazjin/kontemplate" - url "https://github.com/tazjin/kontemplate/releases/download/v1.7.0/kontemplate-1.7.0-511ae92-darwin-amd64.tar.gz" - sha256 "44910488c0e0480e306cc7b1de564a4c0d39013130f2f6e89bcd9a7401ef6a9a" - version "kontemplate-1.7.0-511ae92" + url "https://github.com/tazjin/kontemplate/releases/download/v1.8.0/kontemplate-1.8.0-6c3b299-darwin-amd64.tar.gz" + sha256 "c541f39ef14f4822ff2f5472a9c1e57f73f0277b6b85bec8e9514640aac311a8" + version "kontemplate-1.8.0-6c3b299" def install bin.install "kontemplate" From 795a97466527a5f02e79e47b7fb316c78ffde667 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Fri, 20 Dec 2019 22:13:07 +0000 Subject: [PATCH 166/166] chore(kontemplate): Prepare kontemplate for depot-merge This merge will not yet include moving over to buildGo.nix, as support for testing and such is not present in that library yet. --- .travis.yml | 10 -- CODE_OF_CONDUCT.md | 20 --- CONTRIBUTING.md | 114 ------------------ PKGBUILD | 38 ------ kontemplate.rb | 13 -- .gitignore => ops/kontemplate/.gitignore | 0 LICENSE => ops/kontemplate/LICENSE | 0 README.md => ops/kontemplate/README.md | 0 .../kontemplate/build-release.sh | 0 .../kontemplate/context}/context.go | 0 .../kontemplate/context}/context_test.go | 0 .../context}/testdata/collections-test.yaml | 0 .../context}/testdata/default-loading.yaml | 0 .../context}/testdata/default/default.yaml | 0 .../context}/testdata/explicit-path.yaml | 0 .../testdata/explicit-subresource-path.yaml | 0 .../context}/testdata/flat-test.yaml | 0 .../testdata/flat-with-args-test.yaml | 0 .../context}/testdata/import-vars-simple.yaml | 0 .../context}/testdata/merging/context.yaml | 0 .../testdata/merging/import-vars.yaml | 0 .../testdata/merging/resource/default.yaml | 0 .../testdata/merging/resource/output.yaml | 0 .../testdata/parent-variable-override.yaml | 0 .../context}/testdata/parent-variables.yaml | 0 .../context}/testdata/test-vars-override.yaml | 0 .../context}/testdata/test-vars.yaml | 0 default.nix => ops/kontemplate/default.nix | 0 deps.nix => ops/kontemplate/deps.nix | 0 .../kontemplate/docs}/cluster-config.md | 0 .../kontemplate/docs}/resource-sets.md | 0 {docs => ops/kontemplate/docs}/templates.md | 0 .../kontemplate/docs}/tips-and-tricks.md | 0 .../kontemplate/example}/other-config.yaml | 0 .../kontemplate/example}/prod-cluster.json | 0 .../kontemplate/example}/prod-cluster.yaml | 0 .../example}/some-api/some-api.yaml | 0 .../kontemplate/example}/some-api/some.cfg | 0 {image => ops/kontemplate/image}/Dockerfile | 0 {image => ops/kontemplate/image}/README.md | 0 {image => ops/kontemplate/image}/hashes | 0 main.go => ops/kontemplate/main.go | 0 release.nix => ops/kontemplate/release.nix | 0 .../kontemplate/templater}/dns.go | 0 .../kontemplate/templater}/pass.go | 0 .../kontemplate/templater}/templater.go | 0 .../kontemplate/templater}/templater_test.go | 0 .../templater}/testdata/test-default.txt | 0 .../testdata/test-insertTemplate.txt | 0 .../templater}/testdata/test-template.txt | 0 {util => ops/kontemplate/util}/util.go | 0 {util => ops/kontemplate/util}/util_test.go | 0 52 files changed, 195 deletions(-) delete mode 100644 .travis.yml delete mode 100644 CODE_OF_CONDUCT.md delete mode 100644 CONTRIBUTING.md delete mode 100644 PKGBUILD delete mode 100644 kontemplate.rb rename .gitignore => ops/kontemplate/.gitignore (100%) rename LICENSE => ops/kontemplate/LICENSE (100%) rename README.md => ops/kontemplate/README.md (100%) rename build-release.sh => ops/kontemplate/build-release.sh (100%) rename {context => ops/kontemplate/context}/context.go (100%) rename {context => ops/kontemplate/context}/context_test.go (100%) rename {context => ops/kontemplate/context}/testdata/collections-test.yaml (100%) rename {context => ops/kontemplate/context}/testdata/default-loading.yaml (100%) rename {context => ops/kontemplate/context}/testdata/default/default.yaml (100%) rename {context => ops/kontemplate/context}/testdata/explicit-path.yaml (100%) rename {context => ops/kontemplate/context}/testdata/explicit-subresource-path.yaml (100%) rename {context => ops/kontemplate/context}/testdata/flat-test.yaml (100%) rename {context => ops/kontemplate/context}/testdata/flat-with-args-test.yaml (100%) rename {context => ops/kontemplate/context}/testdata/import-vars-simple.yaml (100%) rename {context => ops/kontemplate/context}/testdata/merging/context.yaml (100%) rename {context => ops/kontemplate/context}/testdata/merging/import-vars.yaml (100%) rename {context => ops/kontemplate/context}/testdata/merging/resource/default.yaml (100%) rename {context => ops/kontemplate/context}/testdata/merging/resource/output.yaml (100%) rename {context => ops/kontemplate/context}/testdata/parent-variable-override.yaml (100%) rename {context => ops/kontemplate/context}/testdata/parent-variables.yaml (100%) rename {context => ops/kontemplate/context}/testdata/test-vars-override.yaml (100%) rename {context => ops/kontemplate/context}/testdata/test-vars.yaml (100%) rename default.nix => ops/kontemplate/default.nix (100%) rename deps.nix => ops/kontemplate/deps.nix (100%) rename {docs => ops/kontemplate/docs}/cluster-config.md (100%) rename {docs => ops/kontemplate/docs}/resource-sets.md (100%) rename {docs => ops/kontemplate/docs}/templates.md (100%) rename {docs => ops/kontemplate/docs}/tips-and-tricks.md (100%) rename {example => ops/kontemplate/example}/other-config.yaml (100%) rename {example => ops/kontemplate/example}/prod-cluster.json (100%) rename {example => ops/kontemplate/example}/prod-cluster.yaml (100%) rename {example => ops/kontemplate/example}/some-api/some-api.yaml (100%) rename {example => ops/kontemplate/example}/some-api/some.cfg (100%) rename {image => ops/kontemplate/image}/Dockerfile (100%) rename {image => ops/kontemplate/image}/README.md (100%) rename {image => ops/kontemplate/image}/hashes (100%) rename main.go => ops/kontemplate/main.go (100%) rename release.nix => ops/kontemplate/release.nix (100%) rename {templater => ops/kontemplate/templater}/dns.go (100%) rename {templater => ops/kontemplate/templater}/pass.go (100%) rename {templater => ops/kontemplate/templater}/templater.go (100%) rename {templater => ops/kontemplate/templater}/templater_test.go (100%) rename {templater => ops/kontemplate/templater}/testdata/test-default.txt (100%) rename {templater => ops/kontemplate/templater}/testdata/test-insertTemplate.txt (100%) rename {templater => ops/kontemplate/templater}/testdata/test-template.txt (100%) rename {util => ops/kontemplate/util}/util.go (100%) rename {util => ops/kontemplate/util}/util_test.go (100%) diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index a97db2493..000000000 --- a/.travis.yml +++ /dev/null @@ -1,10 +0,0 @@ ---- -language: nix -script: - # Build the derivation targeting NixOS. - # This derivation executes tests. - - nix-build default.nix - - # Build the release derivation with statically linked executables - # for multiple operating systems. - - nix-build release.nix diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index c4013ac13..000000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,20 +0,0 @@ -A SERMON ON ETHICS AND LOVE -=========================== - -One day Mal-2 asked the messenger spirit Saint Gulik to approach the Goddess and request Her presence for some desperate advice. Shortly afterwards the radio came on by itself, and an ethereal female Voice said **YES?** - -"O! Eris! Blessed Mother of Man! Queen of Chaos! Daughter of Discord! Concubine of Confusion! O! Exquisite Lady, I beseech You to lift a heavy burden from my heart!" - -**WHAT BOTHERS YOU, MAL? YOU DON'T SOUND WELL.** - -"I am filled with fear and tormented with terrible visions of pain. Everywhere people are hurting one another, the planet is rampant with injustices, whole societies plunder groups of their own people, mothers imprison sons, children perish while brothers war. O, woe." - -**WHAT IS THE MATTER WITH THAT, IF IT IS WHAT YOU WANT TO DO?** - -"But nobody Wants it! Everybody hates it." - -**OH. WELL, THEN *STOP*.** - -At which moment She turned herself into an aspirin commercial and left The Polyfather stranded alone with his species. - -SINISTER DEXTER HAS A BROKEN SPIROMETER. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index e600aa22d..000000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,114 +0,0 @@ -Contribution Guidelines -======================= - - -**Table of Contents** - -- [Contribution Guidelines](#contribution-guidelines) - - [Before making a change](#before-making-a-change) - - [Commit messages](#commit-messages) - - [Commit content](#commit-content) - - [Code quality](#code-quality) - - [Builds & tests](#builds--tests) - - - -This is a loose set of "guidelines" for contributing to my projects. -Please note that I will not accept any pull requests that don't follow -these guidelines. - -Also consider the [code of conduct](CODE_OF_CONDUCT.md). No really, -you should. - -## Before making a change - -Before making a change, consider your motivation for making the -change. Documentation updates, bug fixes and the like are *always* -welcome. - -When adding a feature you should consider whether it is only useful -for your particular use-case or whether it is generally applicable for -other users of the project. - -When in doubt - just ask me! - -## Commit messages - -All commit messages should follow the style-guide used by the [Angular -project][]. This means for the most part that your commit message -should be structured like this: - -``` -type(scope): Subject line with at most 68 a character length - -Body of the commit message with an empty line between subject and -body. This text should explain what the change does and why it has -been made, *especially* if it introduces a new feature. - -Relevant issues should be mentioned if they exist. -``` - -Where `type` can be one of: - -* `feat`: A new feature has been introduced -* `fix`: An issue of some kind has been fixed -* `docs`: Documentation or comments have been updated -* `style`: Formatting changes only -* `refactor`: Hopefully self-explanatory! -* `test`: Added missing tests / fixed tests -* `chore`: Maintenance work - -And `scope` should refer to some kind of logical grouping inside of -the project. - -Please take a look at the existing commit log for examples. - -## Commit content - -Multiple changes should be divided into multiple git commits whenever -possible. Common sense applies. - -The fix for a single-line whitespace issue is fine to include in a -different commit. Introducing a new feature and refactoring -(unrelated) code in the same commit is not fine. - -`git commit -a` is generally **taboo**. - -In my experience making "sane" commits becomes *significantly* easier -as developer tooling is improved. The interface to `git` that I -recommend is [magit][]. Even if you are not yet an Emacs user, it -makes sense to install Emacs just to be able to use magit - it is -really that good. - -For staging sane chunks on the command line with only git, consider -`git add -p`. - -## Code quality - -This one should go without saying - but please ensure that your code -quality does not fall below the rest of the project. This is of course -very subjective, but as an example if you place code that throws away -errors into a block in which errors are handled properly your change -will be rejected. - -In my experience there is a strong correlation between the visual -appearance of a code block and its quality. This is a simple way to -sanity-check your work while squinting and keeping some distance from -your screen ;-) - -## Builds & tests - -Most of my projects are built using [Nix][] to avoid "build pollution" -via the user's environment. If you have Nix installed and are -contributing to a project that has a `default.nix`, consider using -`nix-build` to verify that builds work correctly. - -If the project has tests, check that they still work before submitting -your change. - -Both of these will usually be covered by Travis CI. - - -[Angular project]: https://gist.github.com/stephenparish/9941e89d80e2bc58a153#format-of-the-commit-message -[magit]: https://magit.vc/ -[Nix]: https://nixos.org/nix/ diff --git a/PKGBUILD b/PKGBUILD deleted file mode 100644 index 2c62fec4d..000000000 --- a/PKGBUILD +++ /dev/null @@ -1,38 +0,0 @@ -# Maintainer: Vincent Ambo -pkgname=kontemplate-git -pkgver=master_1e3ecad -pkgrel=1 -pkgdesc="Simple Kubernetes resource templating" -arch=('x86_64') -url="https://github.com/tazjin/kontemplate" -license=('GPLv3') -makedepends=('go') -optdepends=('pass: Template secrets into resources') -source=('kontemplate-git::git+https://github.com/tazjin/kontemplate.git') -md5sums=('SKIP') - -pkgver() { - cd "$srcdir/$pkgname" - echo -n "master_$(git rev-parse --short HEAD)" -} - -prepare() { - cd "$srcdir/$pkgname" - echo "Fetching Go dependencies..." - go get -v ./... -} - -build() { - cd "$srcdir/$pkgname" - local GIT_HASH="$(git rev-parse --short HEAD)" - - go build -tags netgo \ - -ldflags "-X main.gitHash=${GIT_HASH} -w -s" \ - -o 'kontemplate' -} - -package() { - cd "$srcdir/$pkgname" - - install -D -m 0755 'kontemplate' "${pkgdir}/usr/bin/kontemplate" -} diff --git a/kontemplate.rb b/kontemplate.rb deleted file mode 100644 index 45a31ab71..000000000 --- a/kontemplate.rb +++ /dev/null @@ -1,13 +0,0 @@ -# Homebrew binary formula for Kontemplate - -class Kontemplate < Formula - desc "Kontemplate - Extremely simple Kubernetes resource templates" - homepage "https://github.com/tazjin/kontemplate" - url "https://github.com/tazjin/kontemplate/releases/download/v1.8.0/kontemplate-1.8.0-6c3b299-darwin-amd64.tar.gz" - sha256 "c541f39ef14f4822ff2f5472a9c1e57f73f0277b6b85bec8e9514640aac311a8" - version "kontemplate-1.8.0-6c3b299" - - def install - bin.install "kontemplate" - end -end diff --git a/.gitignore b/ops/kontemplate/.gitignore similarity index 100% rename from .gitignore rename to ops/kontemplate/.gitignore diff --git a/LICENSE b/ops/kontemplate/LICENSE similarity index 100% rename from LICENSE rename to ops/kontemplate/LICENSE diff --git a/README.md b/ops/kontemplate/README.md similarity index 100% rename from README.md rename to ops/kontemplate/README.md diff --git a/build-release.sh b/ops/kontemplate/build-release.sh similarity index 100% rename from build-release.sh rename to ops/kontemplate/build-release.sh diff --git a/context/context.go b/ops/kontemplate/context/context.go similarity index 100% rename from context/context.go rename to ops/kontemplate/context/context.go diff --git a/context/context_test.go b/ops/kontemplate/context/context_test.go similarity index 100% rename from context/context_test.go rename to ops/kontemplate/context/context_test.go diff --git a/context/testdata/collections-test.yaml b/ops/kontemplate/context/testdata/collections-test.yaml similarity index 100% rename from context/testdata/collections-test.yaml rename to ops/kontemplate/context/testdata/collections-test.yaml diff --git a/context/testdata/default-loading.yaml b/ops/kontemplate/context/testdata/default-loading.yaml similarity index 100% rename from context/testdata/default-loading.yaml rename to ops/kontemplate/context/testdata/default-loading.yaml diff --git a/context/testdata/default/default.yaml b/ops/kontemplate/context/testdata/default/default.yaml similarity index 100% rename from context/testdata/default/default.yaml rename to ops/kontemplate/context/testdata/default/default.yaml diff --git a/context/testdata/explicit-path.yaml b/ops/kontemplate/context/testdata/explicit-path.yaml similarity index 100% rename from context/testdata/explicit-path.yaml rename to ops/kontemplate/context/testdata/explicit-path.yaml diff --git a/context/testdata/explicit-subresource-path.yaml b/ops/kontemplate/context/testdata/explicit-subresource-path.yaml similarity index 100% rename from context/testdata/explicit-subresource-path.yaml rename to ops/kontemplate/context/testdata/explicit-subresource-path.yaml diff --git a/context/testdata/flat-test.yaml b/ops/kontemplate/context/testdata/flat-test.yaml similarity index 100% rename from context/testdata/flat-test.yaml rename to ops/kontemplate/context/testdata/flat-test.yaml diff --git a/context/testdata/flat-with-args-test.yaml b/ops/kontemplate/context/testdata/flat-with-args-test.yaml similarity index 100% rename from context/testdata/flat-with-args-test.yaml rename to ops/kontemplate/context/testdata/flat-with-args-test.yaml diff --git a/context/testdata/import-vars-simple.yaml b/ops/kontemplate/context/testdata/import-vars-simple.yaml similarity index 100% rename from context/testdata/import-vars-simple.yaml rename to ops/kontemplate/context/testdata/import-vars-simple.yaml diff --git a/context/testdata/merging/context.yaml b/ops/kontemplate/context/testdata/merging/context.yaml similarity index 100% rename from context/testdata/merging/context.yaml rename to ops/kontemplate/context/testdata/merging/context.yaml diff --git a/context/testdata/merging/import-vars.yaml b/ops/kontemplate/context/testdata/merging/import-vars.yaml similarity index 100% rename from context/testdata/merging/import-vars.yaml rename to ops/kontemplate/context/testdata/merging/import-vars.yaml diff --git a/context/testdata/merging/resource/default.yaml b/ops/kontemplate/context/testdata/merging/resource/default.yaml similarity index 100% rename from context/testdata/merging/resource/default.yaml rename to ops/kontemplate/context/testdata/merging/resource/default.yaml diff --git a/context/testdata/merging/resource/output.yaml b/ops/kontemplate/context/testdata/merging/resource/output.yaml similarity index 100% rename from context/testdata/merging/resource/output.yaml rename to ops/kontemplate/context/testdata/merging/resource/output.yaml diff --git a/context/testdata/parent-variable-override.yaml b/ops/kontemplate/context/testdata/parent-variable-override.yaml similarity index 100% rename from context/testdata/parent-variable-override.yaml rename to ops/kontemplate/context/testdata/parent-variable-override.yaml diff --git a/context/testdata/parent-variables.yaml b/ops/kontemplate/context/testdata/parent-variables.yaml similarity index 100% rename from context/testdata/parent-variables.yaml rename to ops/kontemplate/context/testdata/parent-variables.yaml diff --git a/context/testdata/test-vars-override.yaml b/ops/kontemplate/context/testdata/test-vars-override.yaml similarity index 100% rename from context/testdata/test-vars-override.yaml rename to ops/kontemplate/context/testdata/test-vars-override.yaml diff --git a/context/testdata/test-vars.yaml b/ops/kontemplate/context/testdata/test-vars.yaml similarity index 100% rename from context/testdata/test-vars.yaml rename to ops/kontemplate/context/testdata/test-vars.yaml diff --git a/default.nix b/ops/kontemplate/default.nix similarity index 100% rename from default.nix rename to ops/kontemplate/default.nix diff --git a/deps.nix b/ops/kontemplate/deps.nix similarity index 100% rename from deps.nix rename to ops/kontemplate/deps.nix diff --git a/docs/cluster-config.md b/ops/kontemplate/docs/cluster-config.md similarity index 100% rename from docs/cluster-config.md rename to ops/kontemplate/docs/cluster-config.md diff --git a/docs/resource-sets.md b/ops/kontemplate/docs/resource-sets.md similarity index 100% rename from docs/resource-sets.md rename to ops/kontemplate/docs/resource-sets.md diff --git a/docs/templates.md b/ops/kontemplate/docs/templates.md similarity index 100% rename from docs/templates.md rename to ops/kontemplate/docs/templates.md diff --git a/docs/tips-and-tricks.md b/ops/kontemplate/docs/tips-and-tricks.md similarity index 100% rename from docs/tips-and-tricks.md rename to ops/kontemplate/docs/tips-and-tricks.md diff --git a/example/other-config.yaml b/ops/kontemplate/example/other-config.yaml similarity index 100% rename from example/other-config.yaml rename to ops/kontemplate/example/other-config.yaml diff --git a/example/prod-cluster.json b/ops/kontemplate/example/prod-cluster.json similarity index 100% rename from example/prod-cluster.json rename to ops/kontemplate/example/prod-cluster.json diff --git a/example/prod-cluster.yaml b/ops/kontemplate/example/prod-cluster.yaml similarity index 100% rename from example/prod-cluster.yaml rename to ops/kontemplate/example/prod-cluster.yaml diff --git a/example/some-api/some-api.yaml b/ops/kontemplate/example/some-api/some-api.yaml similarity index 100% rename from example/some-api/some-api.yaml rename to ops/kontemplate/example/some-api/some-api.yaml diff --git a/example/some-api/some.cfg b/ops/kontemplate/example/some-api/some.cfg similarity index 100% rename from example/some-api/some.cfg rename to ops/kontemplate/example/some-api/some.cfg diff --git a/image/Dockerfile b/ops/kontemplate/image/Dockerfile similarity index 100% rename from image/Dockerfile rename to ops/kontemplate/image/Dockerfile diff --git a/image/README.md b/ops/kontemplate/image/README.md similarity index 100% rename from image/README.md rename to ops/kontemplate/image/README.md diff --git a/image/hashes b/ops/kontemplate/image/hashes similarity index 100% rename from image/hashes rename to ops/kontemplate/image/hashes diff --git a/main.go b/ops/kontemplate/main.go similarity index 100% rename from main.go rename to ops/kontemplate/main.go diff --git a/release.nix b/ops/kontemplate/release.nix similarity index 100% rename from release.nix rename to ops/kontemplate/release.nix diff --git a/templater/dns.go b/ops/kontemplate/templater/dns.go similarity index 100% rename from templater/dns.go rename to ops/kontemplate/templater/dns.go diff --git a/templater/pass.go b/ops/kontemplate/templater/pass.go similarity index 100% rename from templater/pass.go rename to ops/kontemplate/templater/pass.go diff --git a/templater/templater.go b/ops/kontemplate/templater/templater.go similarity index 100% rename from templater/templater.go rename to ops/kontemplate/templater/templater.go diff --git a/templater/templater_test.go b/ops/kontemplate/templater/templater_test.go similarity index 100% rename from templater/templater_test.go rename to ops/kontemplate/templater/templater_test.go diff --git a/templater/testdata/test-default.txt b/ops/kontemplate/templater/testdata/test-default.txt similarity index 100% rename from templater/testdata/test-default.txt rename to ops/kontemplate/templater/testdata/test-default.txt diff --git a/templater/testdata/test-insertTemplate.txt b/ops/kontemplate/templater/testdata/test-insertTemplate.txt similarity index 100% rename from templater/testdata/test-insertTemplate.txt rename to ops/kontemplate/templater/testdata/test-insertTemplate.txt diff --git a/templater/testdata/test-template.txt b/ops/kontemplate/templater/testdata/test-template.txt similarity index 100% rename from templater/testdata/test-template.txt rename to ops/kontemplate/templater/testdata/test-template.txt diff --git a/util/util.go b/ops/kontemplate/util/util.go similarity index 100% rename from util/util.go rename to ops/kontemplate/util/util.go diff --git a/util/util_test.go b/ops/kontemplate/util/util_test.go similarity index 100% rename from util/util_test.go rename to ops/kontemplate/util/util_test.go