cmd/config: Add examples and tutorials for config functions

- Add examples under `functions`
- Add built-in tutorial for functions
This commit is contained in:
Phillip Wittrock
2019-11-26 20:08:23 -08:00
parent 5876a8cce0
commit dc66de6bf3
34 changed files with 1524 additions and 2 deletions

View File

@@ -14,6 +14,7 @@ func GetRunFnRunner(name string) *RunFnRunner {
r := &RunFnRunner{}
c := &cobra.Command{
Use: "run-fns DIR",
Aliases: []string{"run"},
Short: commands.RunFnsShort,
Long: commands.RunFnsLong,
Example: commands.RunFnsExamples,

View File

@@ -278,3 +278,186 @@ var ConfigurationBasicsLong = `
│   ├── name: web
│   └── image: <YOUR-CONTAINER>
...`
var FunctionBasicsShort = `### Synopsis`
var FunctionBasicsLong = `
` + "`" + `kustomize config` + "`" + ` enables encapsulating function for manipulating Resource
configuration inside containers, which are run using ` + "`" + `run-fns` + "`" + `.
First fetch the kustomize repository, which contains a collection of example
functions
git clone https://github.com/kubernetes-sigs/kustomize
cd kustomize/functions/examples/
### Templating -- CockroachDB
This section demonstrates how to leverage templating based solutions from
` + "`" + `kustomize config` + "`" + `. The templating function is implemented as a ` + "`" + `bash` + "`" + ` script
using a ` + "`" + `heredoc` + "`" + `.
#### 1: Generate the Resources
` + "`" + `cd` + "`" + ` into the ` + "`" + `kustomize/functions/examples/template-heredoc-cockroachdb/` + "`" + `
directory, and invoke ` + "`" + `run-fns` + "`" + ` on the ` + "`" + `local-resource/` + "`" + ` directory.
cd template-heredoc-cockroachdb/
# view the Resources
kustomize config tree local-resource/ --name --image --replicas
# run the function
kustomize config run-fns local-resource/
# view the generated Resources
kustomize config tree local-resource/ --name --image --replicas
` + "`" + `run-fns` + "`" + ` generated the directory ` + "`" + ` local-resource/config` + "`" + ` containing the generated
Resources.
#### 2. Modify the Generated Resources
- modify the generated Resources by adding an annotation, sidecar container, etc.
- modify the ` + "`" + `local-resources/example-use.yaml` + "`" + ` by changing the replicas
re-run ` + "`" + `run-fns` + "`" + `. this will apply the updated replicas to the generated Resources,
but keep the fields that you manually added to the generated Resource configuration.
# run the function
kustomize config run-fns local-resource/
` + "`" + `run-fns` + "`" + ` facilitates a non-destructive *smart templating* approach that allows templating
to be composed with manual modifications directly to the template output, as well as
composition with other functions which may appy validation or injection of values.
#### 3. Function Implementation
the function implementation is located under the ` + "`" + `image/` + "`" + ` directory as a ` + "`" + `Dockerfile` + "`" + `
and a ` + "`" + `bash` + "`" + ` script.
### Templating -- Nginx
The steps in this section are identical to the CockroachDB templating example,
but the function implementation is very different, and implemented as a ` + "`" + `go` + "`" + `
program rather than a ` + "`" + `bash` + "`" + ` script.
#### 1: Generate the Resources
` + "`" + `cd` + "`" + ` into the ` + "`" + `kustomize/functions/examples/template-go-nginx/` + "`" + `
directory, and invoke ` + "`" + `run-fns` + "`" + ` on the ` + "`" + `local-resource/` + "`" + ` directory.
cd template-go-nginx/
# view the Resources
kustomize config tree local-resource/ --name --image --replicas
# run the function
kustomize config run-fns local-resource/
# view the generated Resources
kustomize config tree local-resource/ --name --image --replicas
` + "`" + `run-fns` + "`" + ` generated the directory ` + "`" + ` local-resource/config` + "`" + ` containing the generated
Resources. this time it put the configuration in a single file rather than multiple
files. The mapping of Resources to files is controlled by the function itself through
annotations on the generated Resources.
#### 2. Modify the Generated Resources
- modify the generated Resources by adding an annotation, sidecar container, etc.
- modify the ` + "`" + `local-resources/example-use.yaml` + "`" + ` by changing the replicas
re-run ` + "`" + `run-fns` + "`" + `. this will apply the updated replicas to the generated Resources,
but keep the fields that you manually added to the generated Resource configuration.
# run the function
kustomize config run-fns local-resource/
Just like in the preceding section, the function is implemented using a non-destructive
approach which merges the generated Resources into previously generated instances.
#### 3. Function Implementation
the function implementation is located under the ` + "`" + `image/` + "`" + ` directory as a ` + "`" + `Dockerfile` + "`" + `
and a ` + "`" + `go` + "`" + ` program.
### Validation -- resource reservations
This section uses ` + "`" + `run-fns` + "`" + ` to perform validation rather than generate Resources.
#### 1: Run the Validator
` + "`" + `cd` + "`" + ` into the ` + "`" + `kustomize/functions/examples/validator-resource-requests` + "`" + `
directory, and invoke ` + "`" + `run-fns` + "`" + ` on the ` + "`" + `local-resource/` + "`" + ` directory.
# run the function
kustomize config run-fns local-resource/
cpu-requests missing for a container in Deployment nginx (example-use.yaml [1])
Error: exit status 1
Usage:
...
#### 2: Fix the validation issue
The command will fail complaining that the nginx Deployment is missing ` + "`" + `cpu-requests` + "`" + `,
and print the name of the file + Resource index. Edit the file and uncomment the resources,
then re-run the functions.
kustomize config run-fns local-resource/
The validation now passes.
### Injection -- resource reservations
This section uses ` + "`" + `run-fns` + "`" + ` to perform injection of field values based off annotations
on the Resource.
#### 1: Run the Injector
` + "`" + `cd` + "`" + ` into the ` + "`" + `kustomize/functions/examples/inject-tshirt-sizes` + "`" + `
directory, and invoke ` + "`" + `run-fns` + "`" + ` on the ` + "`" + `local-resource/` + "`" + ` directory.
# print the resources
kustomize config tree local-resource --resources --name
local-resource
├── [example-use.yaml] Validator
└── [example-use.yaml] Deployment nginx
└── spec.template.spec.containers
└── 0
└── name: nginx
# run the functions
kustomize config run-fns local-resource/
# print the new resources
kustomize config tree local-resource --resources --name
├── [example-use.yaml] Validator
└── [example-use.yaml] Deployment nginx
└── spec.template.spec.containers
└── 0
├── name: nginx
└── resources: {requests: {cpu: 4, memory: 1GiB}}
#### 2: Change the tshirt-size
Change the ` + "`" + `tshirt-size` + "`" + ` annotation from ` + "`" + `medium` + "`" + ` to ` + "`" + `small` + "`" + ` and re-run the functions.
kustomize config run-fns local-resource/
kustomize config tree local-resource/
local-resource
├── [example-use.yaml] Validator
└── [example-use.yaml] Deployment nginx
└── spec.template.spec.containers
└── 0
├── name: nginx
└── resources: {requests: {cpu: 200m, memory: 50MiB}}
The function has applied the reservations for the new tshirt-size
### Function Composition
Functions may be composed together. Try putting the Injection (tshirt-size) and
Validation functions together in the same .yaml file (separated by ` + "`" + `---` + "`" + `). Run
` + "`" + `run-fns` + "`" + ` and observe that the first function in the file is applied to the Resources,
and then the second function in the file is applied.`

View File

@@ -77,7 +77,7 @@ func NewConfigCommand(name string) *cobra.Command {
})
root.AddCommand(&cobra.Command{
Use: "docs-fn",
Short: "[Alpha] Documentation for writing containerized functions run by run-fns.",
Short: "[Alpha] Documentation for writing containerized functions run by run.",
Long: api.ConfigFnLong,
})
root.AddCommand(&cobra.Command{
@@ -87,10 +87,16 @@ func NewConfigCommand(name string) *cobra.Command {
})
root.AddCommand(&cobra.Command{
Use: "tutorials-basics",
Use: "tutorials-command-basics",
Short: "[Alpha] Tutorials for using basic config commands.",
Long: tutorials.ConfigurationBasicsLong,
})
root.AddCommand(&cobra.Command{
Use: "tutorials-function-basics",
Short: "[Alpha] Tutorials for using functions.",
Long: tutorials.FunctionBasicsLong,
})
return root
}

View File

@@ -0,0 +1,184 @@
## Function Basics
### Synopsis
`kustomize config` enables encapsulating function for manipulating Resource
configuration inside containers, which are run using `run-fns`.
First fetch the kustomize repository, which contains a collection of example
functions
git clone https://github.com/kubernetes-sigs/kustomize
cd kustomize/functions/examples/
### Templating -- CockroachDB
This section demonstrates how to leverage templating based solutions from
`kustomize config`. The templating function is implemented as a `bash` script
using a `heredoc`.
#### 1: Generate the Resources
`cd` into the `kustomize/functions/examples/template-heredoc-cockroachdb/`
directory, and invoke `run-fns` on the `local-resource/` directory.
cd template-heredoc-cockroachdb/
# view the Resources
kustomize config tree local-resource/ --name --image --replicas
# run the function
kustomize config run-fns local-resource/
# view the generated Resources
kustomize config tree local-resource/ --name --image --replicas
`run-fns` generated the directory ` local-resource/config` containing the generated
Resources.
#### 2. Modify the Generated Resources
- modify the generated Resources by adding an annotation, sidecar container, etc.
- modify the `local-resources/example-use.yaml` by changing the replicas
re-run `run-fns`. this will apply the updated replicas to the generated Resources,
but keep the fields that you manually added to the generated Resource configuration.
# run the function
kustomize config run-fns local-resource/
`run-fns` facilitates a non-destructive *smart templating* approach that allows templating
to be composed with manual modifications directly to the template output, as well as
composition with other functions which may appy validation or injection of values.
#### 3. Function Implementation
the function implementation is located under the `image/` directory as a `Dockerfile`
and a `bash` script.
### Templating -- Nginx
The steps in this section are identical to the CockroachDB templating example,
but the function implementation is very different, and implemented as a `go`
program rather than a `bash` script.
#### 1: Generate the Resources
`cd` into the `kustomize/functions/examples/template-go-nginx/`
directory, and invoke `run-fns` on the `local-resource/` directory.
cd template-go-nginx/
# view the Resources
kustomize config tree local-resource/ --name --image --replicas
# run the function
kustomize config run-fns local-resource/
# view the generated Resources
kustomize config tree local-resource/ --name --image --replicas
`run-fns` generated the directory ` local-resource/config` containing the generated
Resources. this time it put the configuration in a single file rather than multiple
files. The mapping of Resources to files is controlled by the function itself through
annotations on the generated Resources.
#### 2. Modify the Generated Resources
- modify the generated Resources by adding an annotation, sidecar container, etc.
- modify the `local-resources/example-use.yaml` by changing the replicas
re-run `run-fns`. this will apply the updated replicas to the generated Resources,
but keep the fields that you manually added to the generated Resource configuration.
# run the function
kustomize config run-fns local-resource/
Just like in the preceding section, the function is implemented using a non-destructive
approach which merges the generated Resources into previously generated instances.
#### 3. Function Implementation
the function implementation is located under the `image/` directory as a `Dockerfile`
and a `go` program.
### Validation -- resource reservations
This section uses `run-fns` to perform validation rather than generate Resources.
#### 1: Run the Validator
`cd` into the `kustomize/functions/examples/validator-resource-requests`
directory, and invoke `run-fns` on the `local-resource/` directory.
# run the function
kustomize config run-fns local-resource/
cpu-requests missing for a container in Deployment nginx (example-use.yaml [1])
Error: exit status 1
Usage:
...
#### 2: Fix the validation issue
The command will fail complaining that the nginx Deployment is missing `cpu-requests`,
and print the name of the file + Resource index. Edit the file and uncomment the resources,
then re-run the functions.
kustomize config run-fns local-resource/
The validation now passes.
### Injection -- resource reservations
This section uses `run-fns` to perform injection of field values based off annotations
on the Resource.
#### 1: Run the Injector
`cd` into the `kustomize/functions/examples/inject-tshirt-sizes`
directory, and invoke `run-fns` on the `local-resource/` directory.
# print the resources
kustomize config tree local-resource --resources --name
local-resource
├── [example-use.yaml] Validator
└── [example-use.yaml] Deployment nginx
└── spec.template.spec.containers
└── 0
└── name: nginx
# run the functions
kustomize config run-fns local-resource/
# print the new resources
kustomize config tree local-resource --resources --name
├── [example-use.yaml] Validator
└── [example-use.yaml] Deployment nginx
└── spec.template.spec.containers
└── 0
├── name: nginx
└── resources: {requests: {cpu: 4, memory: 1GiB}}
#### 2: Change the tshirt-size
Change the `tshirt-size` annotation from `medium` to `small` and re-run the functions.
kustomize config run-fns local-resource/
kustomize config tree local-resource/
local-resource
├── [example-use.yaml] Validator
└── [example-use.yaml] Deployment nginx
└── spec.template.spec.containers
└── 0
├── name: nginx
└── resources: {requests: {cpu: 200m, memory: 50MiB}}
The function has applied the reservations for the new tshirt-size
### Function Composition
Functions may be composed together. Try putting the Injection (tshirt-size) and
Validation functions together in the same .yaml file (separated by `---`). Run
`run-fns` and observe that the first function in the file is applied to the Resources,
and then the second function in the file is applied.

View File

@@ -0,0 +1,2 @@
Copyright {{.Year}} {{.Holder}}
SPDX-License-Identifier: Apache-2.0

View File

@@ -0,0 +1,42 @@
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
.PHONY: generate license fix vet fmt test build tidy image
GOBIN := $(shell go env GOPATH)/bin
build:
(cd image && go build -v -o $(GOBIN)/config-function .)
all: generate license build fix vet fmt test lint tidy
fix:
(cd image && go fix ./...)
fmt:
(cd image && go fmt ./...)
generate:
(which $(GOBIN)/mdtogo || go get sigs.k8s.io/kustomize/cmd/mdtogo)
(cd image && GOBIN=$(GOBIN) go generate ./...)
license:
(which $(GOPATH)/bin/addlicense || go get github.com/google/addlicense)
$(GOPATH)/bin/addlicense -y 2019 -c "The Kubernetes Authors." -f LICENSE_TEMPLATE .
tidy:
(cd image && go mod tidy)
lint:
(which $(GOBIN)/golangci-lint || go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.19.1)
(cd image && $(GOBIN)/golangci-lint run ./...)
test:
(cd image && go test -cover ./...)
vet:
(cd image && go vet ./...)
image:
docker build image -t gcr.io/kustomize-functions/example-tshirt:v0.1.0
docker push gcr.io/kustomize-functions/example-tshirt:v0.1.0

View File

@@ -0,0 +1,35 @@
# Injection
This is an example of implementing an injection function.
This example is written in `go` and uses the `kyaml` libraries for parsing the
input and writing the output. Writing in `go` is not a requirement.
## Function implementation
The function is implemented as an [image](image), and built using `make image`.
The template is implemented as a go program, which reads a collection of input
Resource configuration, and looks for invalid configuration.
## Function invocation
The function is invoked by authoring a [local Resource](local-resource)
with `metadata.configFn` and running:
kustomize config run-fns local-resources/
This exists non-zero if there is an error.
## Running the Example
Run the validator with:
kustomize config run-fns local-resource/
This will add resource reservations to the Deployment. Change the `tshirt-size`
annotation from `medium` to `small` and rerun:
kustomize config run-fns local-resource/
Observe that the reservations have changed.

View File

@@ -0,0 +1,15 @@
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
FROM golang:1.13-stretch
ENV CGO_ENABLED=0
WORKDIR /go/src/
COPY go.mod .
COPY go.sum .
RUN go mod download
COPY main.go .
RUN go build -v -o /usr/local/bin/config-function ./
FROM alpine:latest
COPY --from=0 /usr/local/bin/config-function /usr/local/bin/config-function
CMD ["config-function"]

View File

@@ -0,0 +1,5 @@
module sigs.k8s.io/kustomize/functions/examples/injection-tshirt-sizes
go 1.13
require sigs.k8s.io/kustomize/kyaml v0.0.0-20191126155111-73fb32c85ad4

View File

@@ -0,0 +1,27 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI=
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20191026110619-0b21df46bc1d h1:LCPbGQ34PMrwad11aMZ+dbz5SAsq/0ySjRwQ8I9Qwd8=
gopkg.in/yaml.v3 v3.0.0-20191026110619-0b21df46bc1d/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
sigs.k8s.io/kustomize/kyaml v0.0.0-20191126155111-73fb32c85ad4 h1:QfQuib3Xi0uyHUi/R5MgWmDScb5iy/+LjoaGIdpmMpE=
sigs.k8s.io/kustomize/kyaml v0.0.0-20191126155111-73fb32c85ad4/go.mod h1:rywm/rcR5LmCBghz9956tE45OdUPChFoXVVs+WmhMTI=

View File

@@ -0,0 +1,116 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// Package main implements an injection function for resource reservations and
// is run with `kustomize config run-fns -- DIR/`.
package main
import (
"fmt"
"os"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
func main() {
rw := &kio.ByteReadWriter{Reader: os.Stdin, Writer: os.Stdout, KeepReaderAnnotations: true}
p := kio.Pipeline{
Inputs: []kio.Reader{rw}, // read the inputs into a slice
Filters: []kio.Filter{filter{}}, // run the inject into the inputs
Outputs: []kio.Writer{rw}} // copy the inputs to the output
if err := p.Execute(); err != nil {
os.Exit(1)
}
}
// filter implements kio.Filter
type filter struct{}
// Filter injects cpu and memory resource reservations into containers for
// Resources containing the `tshirt-size` annotation.
func (filter) Filter(in []*yaml.RNode) ([]*yaml.RNode, error) {
// inject the resource reservations into each Resource
for _, r := range in {
if err := inject(r); err != nil {
return nil, err
}
}
return in, nil
}
// cpuSizes is the mapping from tshirt-size to cpu reservation quantity
var cpuSizes = map[string]string{
"small": "200m",
"medium": "4",
"large": "16",
}
// memorySizes is the mapping from tshirt-size to memory reservation quantity
var memorySizes = map[string]string{
"small": "50MiB",
"medium": "1GiB",
"large": "32GiB",
}
// inject sets the cpu and memory reservations on all containers for Resources annotated
// with `tshirt-size: small|medium|large`
func inject(r *yaml.RNode) error {
// lookup the containers field
containers, err := r.Pipe(yaml.Lookup("spec", "template", "spec", "containers"))
if err != nil {
s, _ := r.String()
return fmt.Errorf("%v: %s", err, s)
}
if containers == nil {
// doesn't have containers, skip the Resource
return nil
}
// check for the tshirt-size annotations
meta, err := r.GetMeta()
if err != nil {
return err
}
var memorySize, cpuSize string
if size, found := meta.Annotations["tshirt-size"]; !found {
// not a tshirt-sized Resource, ignore it
return nil
} else {
// lookup the memory and cpu quantities based on the tshirt size
memorySize = memorySizes[size]
cpuSize = cpuSizes[size]
if memorySize == "" || cpuSize == "" {
return fmt.Errorf("unsupported tshirt-size: " + size)
}
}
// visit each container and apply the cpu and memory reservations
return containers.VisitElements(func(node *yaml.RNode) error {
// set cpu
err := node.PipeE(
// lookup resources.requests.cpu, creating the field as a
// ScalarNode if it doesn't exist
yaml.LookupCreate(yaml.ScalarNode, "resources", "requests", "cpu"),
// set the field value to the cpuSize
yaml.Set(yaml.NewScalarRNode(cpuSize)))
if err != nil {
s, _ := r.String()
return fmt.Errorf("%v: %s", err, s)
}
// set memory
err = node.PipeE(
// lookup resources.requests.memory, creating the field as a
// ScalarNode if it doesn't exist
yaml.LookupCreate(yaml.ScalarNode, "resources", "requests", "memory"),
// set the field value to the memorySize
yaml.Set(yaml.NewScalarRNode(memorySize)))
if err != nil {
s, _ := r.String()
return fmt.Errorf("%v: %s", err, s)
}
return nil
})
}

View File

@@ -0,0 +1,30 @@
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: examples.config.kubernetes.io/v1beta1
kind: Validator
metadata:
configFn:
container:
image: gcr.io/kustomize-functions/example-tshirt:v0.1.0
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
labels:
app: nginx
annotations:
tshirt-size: small # this injects the resource reservations
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx

View File

@@ -0,0 +1,2 @@
Copyright {{.Year}} {{.Holder}}
SPDX-License-Identifier: Apache-2.0

View File

@@ -0,0 +1,42 @@
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
.PHONY: generate license fix vet fmt test build tidy image
GOBIN := $(shell go env GOPATH)/bin
build:
(cd image && go build -v -o $(GOBIN)/config-function .)
all: generate license build fix vet fmt test lint tidy
fix:
(cd image && go fix ./...)
fmt:
(cd image && go fmt ./...)
generate:
(which $(GOBIN)/mdtogo || go get sigs.k8s.io/kustomize/cmd/mdtogo)
(cd image && GOBIN=$(GOBIN) go generate ./...)
license:
(which $(GOPATH)/bin/addlicense || go get github.com/google/addlicense)
$(GOPATH)/bin/addlicense -y 2019 -c "The Kubernetes Authors." -f LICENSE_TEMPLATE .
tidy:
(cd image && go mod tidy)
lint:
(which $(GOBIN)/golangci-lint || go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.19.1)
(cd image && $(GOBIN)/golangci-lint run ./...)
test:
(cd image && go test -cover ./...)
vet:
(cd image && go vet ./...)
image:
docker build image -t gcr.io/kustomize-functions/example-nginx:v0.1.0
docker push gcr.io/kustomize-functions/example-nginx:v0.1.0

View File

@@ -0,0 +1,55 @@
# Template GoTemplate
This is an example of implementing a template function using a `go` template.
This example uses a more sophisticated approach for building abstractions.
## Function implementation
The function is implemented as an [image](image), and built using `make image`.
The template is implemented as a go program with a `go` template. It parses the
functionConfig into a `go` struct and uses the `kyaml` module for parsing the
function input, and writing the function output.
1. read the inputs (stdin)
2. apply filters
- (custom filter) generate the nginx Deployment and Service from go templates using the
functionConfig as the template input.
- merge the generated Deployment and Service with the input Deployment and Service if
present
- set filenames on the Resources (`NAME.yaml`)
- format the Resources
3. write the outputs (stdout)
## Function invocation
The function is invoked by authoring a [local Resource](local-resource)
with `metadata.configFn` and running:
kustomize config run-fns local-resources/
This generates the `local-resources/config` directory containing the template output.
- the template output may be modified by adding fields -- such as initContainers,
sidecarConatiners, cpu resource limits, etc -- and these fields will be retained
when re-running `run-fns`
- the function input `example-use.yaml` may be changed and rerunning `run-fns` will update
only the parts changed in the template output.
## Running the Example
Run the config with:
kustomize config run-fns local-resource/
This will create the directory
local-resource/config
Add an annotation to the StatefulSet Resource and change the replica count of the
`kind: Nginx` Resource in `example-use.yaml`. Rerun the template:
kustomize config run-fns local-resource/
The replica count should be updated, but your annotation should remain.

View File

@@ -0,0 +1,15 @@
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
FROM golang:1.13-stretch
ENV CGO_ENABLED=0
WORKDIR /go/src/
COPY go.mod .
COPY go.sum .
RUN go mod download
COPY main.go .
RUN go build -v -o /usr/local/bin/config-function ./
FROM alpine:latest
COPY --from=0 /usr/local/bin/config-function /usr/local/bin/config-function
CMD ["config-function"]

View File

@@ -0,0 +1,5 @@
module sigs.k8s.io/kustomize/functions/examples/template-go-nginx
go 1.12
require sigs.k8s.io/kustomize/kyaml v0.0.0-20191126203124-d1b33e746881

View File

@@ -0,0 +1,27 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI=
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20191026110619-0b21df46bc1d h1:LCPbGQ34PMrwad11aMZ+dbz5SAsq/0ySjRwQ8I9Qwd8=
gopkg.in/yaml.v3 v3.0.0-20191026110619-0b21df46bc1d/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
sigs.k8s.io/kustomize/kyaml v0.0.0-20191126203124-d1b33e746881 h1:dmMlmv+TXMSAtCf6Za1GKNAjCssKtgnVkAYRiKdnjIY=
sigs.k8s.io/kustomize/kyaml v0.0.0-20191126203124-d1b33e746881/go.mod h1:rywm/rcR5LmCBghz9956tE45OdUPChFoXVVs+WmhMTI=

View File

@@ -0,0 +1,169 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package main
import (
"bytes"
"fmt"
"os"
"path/filepath"
"strconv"
"text/template"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/kio/filters"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
func main() {
rw := &kio.ByteReadWriter{
Reader: os.Stdin,
Writer: os.Stdout,
KeepReaderAnnotations: true,
}
err := kio.Pipeline{
Inputs: []kio.Reader{rw},
Filters: []kio.Filter{
&filter{rw: rw}, // generate the Resources from the template
filters.MergeFilter{}, // merge the generated template
// set Resource filenames
&filters.FileSetter{FilenamePattern: filepath.Join("config", "%n.yaml")},
filters.FormatFilter{}, // format the output
},
Outputs: []kio.Writer{rw},
}.Execute()
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
}
// define the input API schema as a struct
type API struct {
Metadata struct {
// Name is the Deployment Resource and Container name
Name string `yaml:"name"`
} `yaml:"metadata"`
Spec struct {
// Replicas is the number of Deployment replicas
// Defaults to the REPLICAS env var, or 1
Replicas *int `yaml:"replicas"`
} `yaml:"spec"`
}
// filter implements kio.Filter
type filter struct {
rw *kio.ByteReadWriter
}
// Filter checks each input and ensures that all containers have cpu and memory
// reservations set, otherwise it returns an error.
func (f *filter) Filter(in []*yaml.RNode) ([]*yaml.RNode, error) {
api := f.parseAPI()
// execute the service template
buff := &bytes.Buffer{}
t := template.Must(template.New("nginx-service").Parse(serviceTemplate))
if err := t.Execute(buff, api); err != nil {
return nil, err
}
s, err := yaml.Parse(buff.String())
if err != nil {
return nil, err
}
// execute the deployment template
buff = &bytes.Buffer{}
t = template.Must(template.New("nginx-deployment").Parse(deploymentTemplate))
if err := t.Execute(buff, api); err != nil {
return nil, err
}
d, err := yaml.Parse(buff.String())
if err != nil {
return nil, err
}
// add the template generated Resources to the output -- these will get merged by the next
// filter
in = append(in, s, d)
return in, nil
}
// parseAPI parses the functionConfig into an API struct, and validates the input
func (f *filter) parseAPI() API {
// parse the input function config -- TODO: simplify this
var api API
if err := yaml.Unmarshal([]byte(f.rw.FunctionConfig.MustString()), &api); err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
// Default functionConfig values from environment variables if they are not set
// in the functionConfig
r := os.Getenv("REPLICAS")
if r != "" && api.Spec.Replicas == nil {
replicas, err := strconv.Atoi(r)
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
api.Spec.Replicas = &replicas
}
if api.Spec.Replicas == nil {
r := 1
api.Spec.Replicas = &r
}
if api.Metadata.Name == "" {
fmt.Fprintf(os.Stderr, "must specify metadata.name\n")
os.Exit(1)
}
return api
}
var serviceTemplate = `
apiVersion: v1
kind: Service
metadata:
name: {{ .Metadata.Name }}
labels:
app: nginx
instance: {{ .Metadata.Name }}
spec:
ports:
- port: 80
targetPort: 80
name: http
selector:
app: nginx
instance: {{ .Metadata.Name }}
`
var deploymentTemplate = `apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Metadata.Name }}
labels:
app: nginx
instance: {{ .Metadata.Name }}
spec:
replicas: {{ .Spec.Replicas }}
selector:
matchLabels:
app: nginx
instance: {{ .Metadata.Name }}
template:
metadata:
labels:
app: nginx
instance: {{ .Metadata.Name }}
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
`

View File

@@ -0,0 +1,12 @@
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: examples.config.kubernetes.io/v1beta1 # call `kustomize config run-fns` on a directory containing this file
kind: Nginx
metadata:
name: demo
configFn:
container:
image: gcr.io/kustomize-functions/example-nginx:v0.1.0
spec:
replicas: 4

View File

@@ -0,0 +1,2 @@
Copyright {{.Year}} {{.Holder}}
SPDX-License-Identifier: Apache-2.0

View File

@@ -0,0 +1,12 @@
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
.PHONY: license image
license:
(which $(GOPATH)/bin/addlicense || go get github.com/google/addlicense)
$(GOPATH)/bin/addlicense -y 2019 -c "The Kubernetes Authors." -f LICENSE_TEMPLATE .
image:
docker build image -t gcr.io/kustomize-functions/example-cockroachdb:v0.1.0
docker push gcr.io/kustomize-functions/example-cockroachdb:v0.1.0

View File

@@ -0,0 +1,50 @@
# Template HereDoc
This is an example of implementing a template function using a heredoc.
This example uses the simplest approach for building abstractions.
## Function implementation
The function is implemented as an [image](image), and built using `make image`.
The template is implemented as a heredoc, which substitutes environment variables
into a static string.
This simple implementation uses `kustomize config run-fns wrap --` to perform the
heavy lifting of implementing the function interface.
- parse functionConfig from stdin into environment variables
- merge script output with items from stdin
## Function invocation
The function is invoked by authoring a [local Resource](local-resource)
with `metadata.configFn` and running:
kustomize config run-fns local-resources/
This generates the `local-resources/config` directory containing the template output.
- the template output may be modified by adding fields -- such as initContainers,
sidecarConatiners, cpu resource limits, etc -- and these fields will be retained
when re-running `run-fns`
- the function input `example-use.yaml` may be changed and rerunning `run-fns` will update
only the parts changed in the template output.
## Running the Example
Run the config with:
kustomize config run-fns local-resource/
This will create the directory
local-resource/config
Add an annotation to the StatefulSet Resource and change the replica count of the
`kind: CockroachDB` Resource in `example-use.yaml`. Rerun the template:
kustomize config run-fns local-resource/
The replica count should be updated, but your annotation should remain.

View File

@@ -0,0 +1,12 @@
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
FROM golang:1.13-stretch
ENV CGO_ENABLED=0
RUN go get -v sigs.k8s.io/kustomize/kustomize
FROM alpine:latest
RUN apk add --no-cache bash
COPY --from=0 /go/bin/kustomize /usr/local/bin
COPY cockroachdb-template.sh /usr/local/bin/config-function
CMD ["config-function"]

View File

@@ -0,0 +1,204 @@
#!/bin/bash
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
# use `kustomize config run-fns wrap` to parse the container stdin into
# environment variables, and to merge the template output into the
# input Resources.
if [ -z ${WRAPPED} ]; then
export WRAPPED=true
kustomize config run-fns wrap -- $0
exit $?
fi
# this is the template for a cockroachdb instance
# environment variables are parsed from the input functionConfig by
# `kustomize config run-fns wrap`
cat <<End-of-message
apiVersion: v1
kind: Service
metadata:
# This service is meant to be used by clients of the database. It exposes a ClusterIP that will
# automatically load balance connections to the different database pods.
name: ${NAME}-public
labels:
app: cockroachdb
name: ${NAME}
spec:
ports:
# The main port, served by gRPC, serves Postgres-flavor SQL, internode
# traffic and the cli.
- port: 26257
targetPort: 26257
name: grpc
# The secondary port serves the UI as well as health and debug endpoints.
- port: 8080
targetPort: 8080
name: http
selector:
app: cockroachdb
name: ${NAME}
---
apiVersion: v1
kind: Service
metadata:
# This service only exists to create DNS entries for each pod in the stateful
# set such that they can resolve each other's IP addresses. It does not
# create a load-balanced ClusterIP and should not be used directly by clients
# in most circumstances.
name: ${NAME}
labels:
app: cockroachdb
name: ${NAME}
annotations:
# This is needed to make the peer-finder work properly and to help avoid
# edge cases where instance 0 comes up after losing its data and needs to
# decide whether it should create a new cluster or try to join an existing
# one. If it creates a new cluster when it should have joined an existing
# one, we'd end up with two separate clusters listening at the same service
# endpoint, which would be very bad.
service.alpha.kubernetes.io/tolerate-unready-endpoints: "true"
# Enable automatic monitoring of all instances when Prometheus is running in the cluster.
prometheus.io/scrape: "true"
prometheus.io/path: "_status/vars"
prometheus.io/port: "8080"
spec:
ports:
- port: 26257
targetPort: 26257
name: grpc
- port: 8080
targetPort: 8080
name: http
clusterIP: None
selector:
app: cockroachdb
name: ${NAME}
---
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: ${NAME}-budget
labels:
app: cockroachdb
name: ${NAME}
spec:
selector:
matchLabels:
app: cockroachdb
name: ${NAME}
minAvailable: 67%
---
apiVersion: apps/v1 # for k8s versions before 1.9.0 use apps/v1beta2 and before 1.8.0 use extensions/v1beta1
kind: StatefulSet
metadata:
name: ${NAME}
labels:
app: cockroachdb
name: ${NAME}
spec:
serviceName: ${NAME}
replicas: ${REPLICAS}
selector:
matchLabels:
app: cockroachdb
name: ${NAME}
template:
metadata:
labels:
app: cockroachdb
name: ${NAME}
spec:
# Init containers are run only once in the lifetime of a pod, before
# it's started up for the first time. It has to exit successfully
# before the pod's main containers are allowed to start.
# This particular init container does a DNS lookup for other pods in
# the set to help determine whether or not a cluster already exists.
# If any other pods exist, it creates a file in the cockroach-data
# directory to pass that information along to the primary container that
# has to decide what command-line flags to use when starting CockroachDB.
# This only matters when a pod's persistent volume is empty - if it has
# data from a previous execution, that data will always be used.
#
# If your Kubernetes cluster uses a custom DNS domain, you will have
# to add an additional arg to this pod: "-domain=<your-custom-domain>"
initContainers:
- name: bootstrap
image: cockroachdb/cockroach-k8s-init:0.1
imagePullPolicy: IfNotPresent
args:
- "-on-start=/on-start.sh"
- "-service=cockroachdb"
env:
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
volumeMounts:
- name: datadir
mountPath: "/cockroach/cockroach-data"
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- cockroachdb
topologyKey: kubernetes.io/hostname
containers:
- name: ${NAME}
image: cockroachdb/cockroach:v1.1.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 26257
name: grpc
- containerPort: 8080
name: http
volumeMounts:
- name: datadir
mountPath: /cockroach/cockroach-data
command:
- "/bin/bash"
- "-ecx"
- |
# The use of qualified \`hostname -f\` is crucial:
# Other nodes aren't able to look up the unqualified hostname.
CRARGS=("start" "--logtostderr" "--insecure" "--host" "\$(hostname -f)" "--http-host" "0.0.0.0")
# We only want to initialize a new cluster (by omitting the join flag)
# if we're sure that we're the first node (i.e. index 0) and that
# there aren't any other nodes running as part of the cluster that
# this is supposed to be a part of (which indicates that a cluster
# already exists and we should make sure not to create a new one).
# It's fine to run without --join on a restart if there aren't any
# other nodes.
if [ ! "\$(hostname)" == "cockroachdb-0" ] || \
[ -e "/cockroach/cockroach-data/cluster_exists_marker" ]
then
# We don't join cockroachdb in order to avoid a node attempting
# to join itself, which currently doesn't work
# (https://github.com/cockroachdb/cockroach/issues/9625).
CRARGS+=("--join" "cockroachdb-public")
fi
exec /cockroach/cockroach \${CRARGS[*]}
# No pre-stop hook is required, a SIGTERM plus some time is all that's
# needed for graceful shutdown of a node.
terminationGracePeriodSeconds: 60
volumes:
- name: datadir
persistentVolumeClaim:
claimName: datadir
volumeClaimTemplates:
- metadata:
name: datadir
spec:
accessModes:
- "ReadWriteOnce"
resources:
requests:
storage: 1Gi
End-of-message

View File

@@ -0,0 +1,13 @@
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
# call `kustomize config run-fns` on a directory containing this file
apiVersion: examples.config.kubernetes.io/v1beta1
kind: CockroachDB
metadata:
name: demo
configFn:
container:
image: gcr.io/kustomize-functions/example-cockroachdb:v0.1.0
spec:
replicas: 3

View File

@@ -0,0 +1,2 @@
Copyright {{.Year}} {{.Holder}}
SPDX-License-Identifier: Apache-2.0

View File

@@ -0,0 +1,42 @@
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
.PHONY: generate license fix vet fmt test build tidy image
GOBIN := $(shell go env GOPATH)/bin
build:
(cd image && go build -v -o $(GOBIN)/config-function .)
all: generate license build fix vet fmt test lint tidy
fix:
(cd image && go fix ./...)
fmt:
(cd image && go fmt ./...)
generate:
(which $(GOBIN)/mdtogo || go get sigs.k8s.io/kustomize/cmd/mdtogo)
(cd image && GOBIN=$(GOBIN) go generate ./...)
license:
(which $(GOPATH)/bin/addlicense || go get github.com/google/addlicense)
$(GOPATH)/bin/addlicense -y 2019 -c "The Kubernetes Authors." -f LICENSE_TEMPLATE .
tidy:
(cd image && go mod tidy)
lint:
(which $(GOBIN)/golangci-lint || go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.19.1)
(cd image && $(GOBIN)/golangci-lint run ./...)
test:
(cd image && go test -cover ./...)
vet:
(cd image && go vet ./...)
image:
docker build image -t gcr.io/kustomize-functions/example-validator:v0.1.0
docker push gcr.io/kustomize-functions/example-validator:v0.1.0

View File

@@ -0,0 +1,38 @@
# Validation
This is an example of implementing a validation function.
This example is written in `go` and uses the `kyaml` libraries for parsing the
input and writing the output. Writing in `go` is not a requirement.
## Function implementation
The function is implemented as an [image](image), and built using `make image`.
The template is implemented as a go program, which reads a collection of input
Resource configuration, and looks for invalid configuration.
## Function invocation
The function is invoked by authoring a [local Resource](local-resource)
with `metadata.configFn` and running:
kustomize config run-fns local-resources/
This exists non-zero if there is an error.
## Running the Example
Run the validator with:
kustomize config run-fns local-resource/
This will return an error:
cpu-requests missing for container nginx
Now uncomment the resource reservations and run again:
kustomize config run-fns local-resource/
This will return success

View File

@@ -0,0 +1,15 @@
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
FROM golang:1.13-stretch
ENV CGO_ENABLED=0
WORKDIR /go/src/
COPY go.mod .
COPY go.sum .
RUN go mod download
COPY main.go .
RUN go build -v -o /usr/local/bin/config-function ./
FROM alpine:latest
COPY --from=0 /usr/local/bin/config-function /usr/local/bin/config-function
CMD ["config-function"]

View File

@@ -0,0 +1,5 @@
module sigs.k8s.io/kustomize/functions/examples/validator-resource-requests
go 1.13
require sigs.k8s.io/kustomize/kyaml v0.0.0-20191126155111-73fb32c85ad4

View File

@@ -0,0 +1,27 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI=
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20191026110619-0b21df46bc1d h1:LCPbGQ34PMrwad11aMZ+dbz5SAsq/0ySjRwQ8I9Qwd8=
gopkg.in/yaml.v3 v3.0.0-20191026110619-0b21df46bc1d/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
sigs.k8s.io/kustomize/kyaml v0.0.0-20191126155111-73fb32c85ad4 h1:QfQuib3Xi0uyHUi/R5MgWmDScb5iy/+LjoaGIdpmMpE=
sigs.k8s.io/kustomize/kyaml v0.0.0-20191126155111-73fb32c85ad4/go.mod h1:rywm/rcR5LmCBghz9956tE45OdUPChFoXVVs+WmhMTI=

View File

@@ -0,0 +1,94 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// Package main implements a validator function run by `kustomize config run-fns`
package main
import (
"fmt"
"os"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
func main() {
rw := &kio.ByteReadWriter{Reader: os.Stdin, Writer: os.Stdout, KeepReaderAnnotations: true}
p := kio.Pipeline{
Inputs: []kio.Reader{rw}, // read the inputs into a slice
Filters: []kio.Filter{filter{}}, // run the filter against the inputs
Outputs: []kio.Writer{rw}} // copy the inputs to the output
if err := p.Execute(); err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
}
// filter implements kio.Filter
type filter struct{}
// Filter checks each input and ensures that all containers have cpu and memory
// reservations set, otherwise it returns an error.
func (filter) Filter(in []*yaml.RNode) ([]*yaml.RNode, error) {
// validate each Resource
for _, r := range in {
if err := validate(r); err != nil {
return nil, err
}
}
return in, nil
}
func validate(r *yaml.RNode) error {
meta, err := r.GetMeta()
if err != nil {
return err
}
// lookup the containers field in the Resource
containers, err := r.Pipe(yaml.Lookup("spec", "template", "spec", "containers"))
if err != nil {
s, _ := r.String()
return fmt.Errorf("%v: %s", err, s)
}
if containers == nil {
// doesn't have containers, ignore it
return nil
}
// visit each container in the list and validate
return containers.VisitElements(func(node *yaml.RNode) error {
// check cpu is non-nil
f, err := node.Pipe(yaml.Lookup("resources", "requests", "cpu"))
if err != nil {
s, _ := r.String()
return fmt.Errorf("%v: %s", err, s)
}
if f == nil {
return fmt.Errorf(
"cpu-requests missing for a container in %s %s (%s [%s])",
meta.Kind, meta.Name,
meta.Annotations[kioutil.PathAnnotation],
meta.Annotations[kioutil.IndexAnnotation])
}
// check memory is non-nil
f, err = node.Pipe(yaml.Lookup("resources", "requests", "memory"))
if err != nil {
s, _ := r.String()
return fmt.Errorf("%v: %s", err, s)
}
if f == nil {
return fmt.Errorf(
"memory-requests missing for a container in %s in %s (%s [%s])",
meta.Kind, meta.Name,
meta.Annotations[kioutil.PathAnnotation],
meta.Annotations[kioutil.IndexAnnotation])
}
// container is valid
return nil
})
}

View File

@@ -0,0 +1,33 @@
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: examples.config.kubernetes.io/v1beta1
kind: Validator
metadata:
configFn:
container:
image: gcr.io/kustomize-functions/example-validator:v0.1.0
---
apiVersion: apps/v1 # this should fail validation
kind: Deployment
metadata:
name: nginx
labels:
app: nginx
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
# uncomment these to pass validation
# resources:
# requests:
# cpu: 100m
# memory: 300MiB