release cmd/config 0.0.6

Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.0
This commit is contained in:
Phillip Wittrock
2020-01-09 17:02:08 -08:00
169 changed files with 27274 additions and 2232 deletions

View File

@@ -78,7 +78,10 @@ func NewConfigCommand(name string) *cobra.Command {
root.AddCommand(commands.CountCommand(name))
root.AddCommand(commands.RunFnCommand(name))
root.AddCommand(commands.SetCommand(name))
root.AddCommand(commands.ListSettersCommand(name))
root.AddCommand(commands.CreateSetterCommand(name))
root.AddCommand(commands.SinkCommand(name))
root.AddCommand(commands.SourceCommand(name))
root.AddCommand(&cobra.Command{
Use: "docs-merge",
@@ -92,8 +95,13 @@ func NewConfigCommand(name string) *cobra.Command {
})
root.AddCommand(&cobra.Command{
Use: "docs-fn",
Short: "[Alpha] Documentation for writing containerized functions run by run.",
Long: api.ConfigFnLong,
Short: "[Alpha] Documentation for developing and invoking Configuration Functions.",
Long: api.FunctionsImplLong,
})
root.AddCommand(&cobra.Command{
Use: "docs-fn-spec",
Short: "[Alpha] Documentation for Configuration Functions Specification.",
Long: api.FunctionsSpecLong,
})
root.AddCommand(&cobra.Command{
Use: "docs-io-annotations",

View File

@@ -1,281 +0,0 @@
# Configuration Functions API Semantics
Configuration Functions are functions packaged as executables in containers which enable
**shift-left practices**. They configure applications and infrastructure through
Kubernetes style Resource Configuration, but run locally pre-commit.
Configuration functions enable shift-left practices (client-side) through:
- Pre-commit / delivery validation and linting of configuration
- e.g. Fail if any containers don't have PodSecurityPolicy or CPU / Memory limits
- Implementation of abstractions as client actuated APIs (e.g. templating)
- e.g. Create a client-side *"CRD"* for generating configuration checked into git
- Aspect Orient configuration / Injection of cross-cutting configuration
- e.g. T-Shirt size containers by annotating Resources with `small`, `medium`, `large`
and inject the cpu and memory resources into containers accordingly.
- e.g. Inject `init` and `side-car` containers into Resources based off of Resource
Type, annotations, etc.
Performing these on the client rather than the server enables:
- Configuration to be reviewed prior to being sent to the API server
- Configuration to be validated as part of the CD pipeline
- Configuration for Resources to validated holistically rather than individually
per-Resource -- e.g. ensure the `Service.selector` and `Deployment.spec.template` labels
match.
- MutatingWebHooks are scoped to a single Resource instance at a time.
- Low-level tweaks to the output of high-level abstractions -- e.g. add an `init container`
to a client *"CRD"* Resource after it was generated.
- Composition and layering of multiple functions together
- Compose generation, injection, validation together
Configuration Functions are implemented as executable programs published in containers which:
- Accept as input (stdin):
- A list of Resource Configuration
- A Function Configuration (to configure the function itself)
- Emit as output (stdout + exit):
- A list of Resource Configuration
- An exit code for success / failure
### Function Specification
- Functions **SHOULD** be published as container images containing a `CMD` invoking an executable.
- Functions **MUST** accept input on STDIN a `ResourceList` containing the Resources and
`functionConfig`.
- Functions **MUST** emit output on STDOUT a `ResourceList` containing the modified
Resources.
- Functions **MUST** exit non-0 on failure, and exit 0 on success.
- Functions **MAY** emit output on STDERR with error messaging.
- Functions performing validation **SHOULD** exit failure and emit error messaging
on a validation failure.
- Functions generating Resources **SHOULD** retain non-conflicting changes on the
generated Resources -- e.g. 1. the function generates a Deployment, but doesn't
specify `cpu`, 2. the user sets the `cpu` on the generated Resource, 3. the
function should keep the `cpu` when regenerating the Resource a second time.
- Functions **SHOULD** be usable outside `kustomize config run` -- e.g. though pipeline
mechanisms such as Tekton.
#### Input Format
Functions must accept on STDIN:
`ResourceList`:
- contains `items` field, same as `List.items`
- contains `functionConfig` field -- a single item with the configuration for the function itself
Example `ResourceList` Input:
apiVersion: config.kubernetes.io/v1alpha1
kind: ResourceList
functionConfig:
apiVersion: example.com/v1beta1
kind: Nginx
metadata:
name: my-instance
annotations:
config.kubernetes.io/local-config: "true"
spec:
replicas: 5
items:
- apiVersion: apps/v1
kind: Deployment
metadata:
name: my-instance
spec:
replicas: 3
...
- apiVersion: v1
kind: Service
metadata:
name: my-instance
spec:
...
#### Output Format
Functions must emit on STDOUT:
`ResourceList`:
- contains `items` field, same as `List.items`
Example `ResourceList` Output:
apiVersion: config.kubernetes.io/v1alpha1
kind: ResourceList
items:
- apiVersion: apps/v1
kind: Deployment
metadata:
name: my-instance
spec:
replicas: 5
...
- apiVersion: v1
kind: Service
metadata:
name: my-instance
spec:
...
#### Container Environment
When run by `kustomize config run`, functions are run in containers with the
following environment:
- Network: `none`
- User: `nobody`
- Security Options: `no-new-privileges`
- Volumes: the volume containing the `functionConfig` yaml is mounted under `/local` as `ro`
### Example Function Implementation
Following is an example for implementing an nginx abstraction using a config
function.
#### `nginx-template.sh`
`nginx-template.sh` is a simple bash script which uses a *heredoc* as a templating solution
for generating Resources from the functionConfig input fields.
The script wraps itself using `config run wrap -- $0` which will:
1. Parse the `ResourceList.functionConfig` (provided to the container stdin) into env vars
2. Merge the stdout into the original list of Resources
3. Defaults filenames for newly generated Resources (if they are not set as annotations)
to `config/NAME_KIND.yaml`
4. Format the output
#!/bin/bash
# script must run wrapped by `kustomize config run wrap`
# for parsing input the functionConfig into env vars
if [ -z ${WRAPPED} ]; then
export WRAPPED=true
config run wrap -- $0
exit $?
fi
cat <<End-of-message
apiVersion: v1
kind: Service
metadata:
name: ${NAME}
labels:
app: nginx
instance: ${NAME}
spec:
ports:
- port: 80
targetPort: 80
name: http
selector:
app: nginx
instance: ${NAME}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: ${NAME}
labels:
app: nginx
instance: ${NAME}
spec:
replicas: ${REPLICAS}
selector:
matchLabels:
app: nginx
instance: ${NAME}
template:
metadata:
labels:
app: nginx
instance: ${NAME}
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
End-of-message
#### `Dockerfile`
`Dockerfile` installs `kustomize config` and copies the script into the container image.
FROM golang:1.13-stretch
RUN go get sigs.k8s.io/kustomize/cmd/config
RUN mv /go/bin/config /usr/bin/config
COPY nginx-template.sh /usr/bin/nginx-template.sh
CMD ["nginx-template.sh]
### Example Function Usage
Following is an example of running the `kustomize config run` using the preceding API.
#### `nginx.yaml` (Input)
`dir/nginx.yaml` contains a reference to the Function. The contents of `nginx.yaml`
are passed to the Function through the `ResourceList.functionConfig` field.
apiVersion: example.com/v1beta1
kind: Nginx
metadata:
name: my-instance
annotations:
config.kubernetes.io/local-config: "true"
configFn:
container:
image: gcr.io/example-functions/nginx-template:v1.0.0
spec:
replicas: 5
- `configFn.container.image`: the image to use for this API
- `annotations[config.kubernetes.io/local-config]`: mark this as not a Resource that should
be applied
#### `kustomize config run dir/` (Output)
`dir/my-instance_deployment.yaml` contains the Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-instance
labels:
app: nginx
instance: my-instance
spec:
replicas: 5
selector:
matchLabels:
app: nginx
instance: my-instance
template:
metadata:
labels:
app: nginx
instance: my-instance
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
`dir/my-instance_service.yaml` contains the Service:
apiVersion: v1
kind: Service
metadata:
name: my-instance
labels:
app: nginx
instance: my-instance
spec:
ports:
- port: 80
targetPort: 80
name: http
selector:
app: nginx
instance: my-instance

View File

@@ -0,0 +1,181 @@
# Running Configuration Functions using kustomize CLI
Configuration functions can be implemented using any toolchain and invoked using any
container workflow orchestrator including Tekton, Cloud Build, or run directly using `docker run`.
Run `config help docs-fn-spec` to see the Configuration Functions Specification.
`kustomize config run` is an example orchestrator for invoking Configuration Functions. This
document describes how to implement and invoke an example function.
## Example Function Implementation
Following is an example for implementing an nginx abstraction using a configuration
function.
### `nginx-template.sh`
`nginx-template.sh` is a simple bash script which uses a _heredoc_ as a templating solution
for generating Resources from the functionConfig input fields.
The script wraps itself using `config run wrap -- $0` which will:
1. Parse the `ResourceList.functionConfig` (provided to the container stdin) into env vars
2. Merge the stdout into the original list of Resources
3. Defaults filenames for newly generated Resources (if they are not set as annotations)
to `config/NAME_KIND.yaml`
4. Format the output
```bash
#!/bin/bash
# script must run wrapped by "kustomize config run wrap"
# for parsing input the functionConfig into env vars
if [ -z ${WRAPPED} ]; then
export WRAPPED=true
config run wrap -- $0
exit $?
fi
cat <<End-of-message
apiVersion: v1
kind: Service
metadata:
name: ${NAME}
labels:
app: nginx
instance: ${NAME}
spec:
ports:
- port: 80
targetPort: 80
name: http
selector:
app: nginx
instance: ${NAME}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: ${NAME}
labels:
app: nginx
instance: ${NAME}
spec:
replicas: ${REPLICAS}
selector:
matchLabels:
app: nginx
instance: ${NAME}
template:
metadata:
labels:
app: nginx
instance: ${NAME}
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
End-of-message
```
### Dockerfile
`Dockerfile` installs `kustomize config` and copies the script into the container image.
```
FROM golang:1.13-stretch
RUN go get sigs.k8s.io/kustomize/cmd/config
RUN mv /go/bin/config /usr/bin/config
COPY nginx-template.sh /usr/bin/nginx-template.sh
CMD ["nginx-template.sh]
```
## Example Function Usage
Following is an example of running the `kustomize config run` using the preceding API.
When run by `kustomize config run`, functions are run in containers with the
following environment:
- Network: `none`
- User: `nobody`
- Security Options: `no-new-privileges`
- Volumes: the volume containing the `functionConfig` yaml is mounted under `/local` as `ro`
### Input
`dir/nginx.yaml` contains a reference to the Function. The contents of `nginx.yaml`
are passed to the Function through the `ResourceList.functionConfig` field.
```yaml
apiVersion: example.com/v1beta1
kind: Nginx
metadata:
name: my-instance
annotations:
config.kubernetes.io/local-config: "true"
config.k8s.io/function: |
container:
image: gcr.io/example-functions/nginx-template:v1.0.0
spec:
replicas: 5
```
- `annotations[config.k8s.io/function].container.image`: the image to use for this API
- `annotations[config.kubernetes.io/local-config]`: mark this as not a Resource that should
be applied
### Output
The function is invoked using by runing `kustomize config run dir/`.
`dir/my-instance_deployment.yaml` contains the Deployment:
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-instance
labels:
app: nginx
instance: my-instance
spec:
replicas: 5
selector:
matchLabels:
app: nginx
instance: my-instance
template:
metadata:
labels:
app: nginx
instance: my-instance
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
```
`dir/my-instance_service.yaml` contains the Service:
```yaml
apiVersion: v1
kind: Service
metadata:
name: my-instance
labels:
app: nginx
instance: my-instance
spec:
ports:
- port: 80
targetPort: 80
name: http
selector:
app: nginx
instance: my-instance
```

View File

@@ -0,0 +1,186 @@
# Configuration Functions Specification
This document specifies a standard for client-side functions that operate on
Kubernetes declarative configurations. This standard enables creating
small, interoperable, and language-independent executable programs packaged as
containers that can be chained together as part of a configuration management pipeline.
The end result of such a pipeline are fully rendered configurations that can then be
applied to a control plane (e.g. Using kubectl apply for Kubernetes control plane).
As such, although this document references Kubernetes Resource Model and API conventions,
it is completely decoupled from Kuberentes API machinery and does not depend on any
in-cluster components.
This document references terms described in [Kubernetes API Conventions][1].
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
interpreted as described in [RFC 2119][2].
## Use Cases
_Configuration functions_ enable shift-left practices (client-side) through:
- Pre-commit / delivery validation and linting of configuration
- e.g. Fail if any containers don't have PodSecurityPolicy or CPU / Memory limits
- Implementation of abstractions as client actuated APIs (e.g. templating)
- e.g. Create a client-side _"CRD"_ for generating configuration checked into git
- Aspect Orient configuration / Injection of cross-cutting configuration
- e.g. T-Shirt size containers by annotating Resources with `small`, `medium`, `large`
and inject the cpu and memory resources into containers accordingly.
- e.g. Inject `init` and `side-car` containers into Resources based off of Resource
Type, annotations, etc.
Performing these on the client rather than the server enables:
- Configuration to be reviewed prior to being sent to the API server
- Configuration to be validated as part of the CI?CD pipeline
- Configuration for Resources to validated holistically rather than individually
per-Resource
- e.g. ensure the `Service.selector` and `Deployment.spec.template` labels
match.
- e.g. MutatingWebHooks are scoped to a single Resource instance at a time.
- Low-level tweaks to the output of high-level abstractions
- e.g. add an `init container` to a client _"CRD"_ Resource after it was generated.
- Composition and layering of multiple functions together
- Compose generation, injection, validation together
## Spec
### Input Type
A function MUST accept as input a single [Kubernetes List type][3].
The `items` field in the input will contain a sequence of [Object types][3].
A function MAY not support [Simple types][3] and List types.
An example using `v1/ConfigMapList` as input:
```yaml
apiVersion: v1
kind: ConfigMapList
items:
- apiVersion: v1
kind: ConfigMap
metadata:
name: config1
data:
p1: v1
p2: v2
- apiVersion: v1
kind: ConfigMap
metadata:
name: config2
```
An example using `v1/List` as input:
```yaml
apiVersion: v1
kind: List
items:
spec:
- apiVersion: foo-corp.com/v1
kind: FulfillmentCenter
metadata:
name: staging
address: "100 Main St."
- apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: namespace-reader
rules:
- resources:
- namespaces
apiGroups:
- ""
verbs:
- get
- watch
- list
```
In addition, a function MUST accept as input a List of kind `ResourceList` where the
`functionConfig` field, if present, will contain the invocation-specific configuration passed to the function
by the orchestrator.
Functions MAY consider this field optional so that they can be triggered in an ad-hoc fashion.
An example using `config.kubernetes.io/v1beta1/ResourceList` as input:
```yaml
apiVersion: config.kubernetes.io/v1beta1
kind: ResourceList
functionConfig:
apiVersion: foo-corp.com/v1
kind: FulfillmentCenter
metadata:
name: staging
metadata:
annotations:
config.k8s.io/function: |
container:
image: gcr.io/example/foo:v1.0.0
spec:
address: "100 Main St."
items:
- apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: namespace-reader
rules:
- resources:
- namespaces
apiGroups:
- ""
verbs:
- get
- watch
- list
```
Here `FulfillmentCenter` kind with name `staging` is passed as the invocation-specific configuration
to the function.
### Output Type
A functions output MUST be the same as the input specification above
-- i.e. `ResourceList` or `List`.
This is necessary to enable chaining two or more functions together in a pipeline.
The serialization format of the output SHOULD match that of its input on each invocation
-- e.g. if the input was a `ResourceList`, the output should also be a `ResourceList`.
### Serialization Format
A function MUST support YAML as a serialization format for the input and output.
A function MUST use utf8 encoding (as YAML is a superset of JSON, JSON will also be supported
by any conforming function).
### Operations
A function MAY Create, Update, or Delete any number of items in the `items` field and output the
resultant list.
A function MAY modify annotations with prefix `config.kubernetes.io`, but must be careful about
doing so since theyre used for orchestration purposes and will likely impact subsequent functions
in the pipeline.
A function SHOULD preserve comments when input serialization format is YAML.
This allows for human authoring of configuration to coexist with changes made by functions.
### Containerization
A function MUST be implemented as a container.
A function container MUST be capable of running as a non-root user if it does not require
access to host filesystem or makes network calls.
### stdin/stdout/stderr and Exit Codes
A function MUST accept input from stdin and emit output to stdout.
Any error messages MUST be emitted to stderr.
An exit code of zero indicates function execution was successful.
A non-zero exit code indicates a failure.
[1]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md
[2]: https://tools.ietf.org/html/rfc2119
[3]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#types-kinds

View File

@@ -0,0 +1,23 @@
## set
[Alpha] List setters for Resources.
### Synopsis
List setters for Resources.
DIR
A directory containing Resource configuration.
NAME
Optional. The name of the setter to display.
### Examples
Show setters:
$ config set DIR/
NAME DESCRIPTION VALUE TYPE COUNT SETBY
name-prefix '' PREFIX string 2

View File

@@ -22,8 +22,8 @@ order they appear in the file).
#### Config Functions:
Config functions are specified as Kubernetes types containing a metadata.configFn.container.image
field. This field tells run how to invoke the container.
Config functions are specified as Kubernetes types containing a metadata.annotations.[config.kubernetes.io/function]
field specifying an image for the container to run. This image tells run how to invoke the container.
Example config function:
@@ -31,17 +31,17 @@ order they appear in the file).
apiVersion: fn.example.com/v1beta1
kind: ExampleFunctionKind
metadata:
configFn:
container:
# function is invoked as a container running this image
image: gcr.io/example/examplefunction:v1.0.1
annotations:
config.kubernetes.io/function: |
container:
# function is invoked as a container running this image
image: gcr.io/example/examplefunction:v1.0.1
config.kubernetes.io/local-config: "true" # tools should ignore this
spec:
configField: configValue
In the preceding example, 'kustomize config run example/' would identify the function by
the metadata.configFn field. It would then write all Resources in the directory to
the metadata.annotations.[config.kubernetes.io/function] field. It would then write all Resources in the directory to
a container stdin (running the gcr.io/example/examplefunction:v1.0.1 image). It
would then write the container stdout back to example/, replacing the directory
file contents.

View File

@@ -59,19 +59,19 @@ To create a custom setter for a field see: `kustomize help config create-setter`
List setters: Show the possible setters
$ config set DIR/
NAME DESCRIPTION VALUE TYPE COUNT OWNER
NAME DESCRIPTION VALUE TYPE COUNT SETBY
name-prefix '' PREFIX string 2
Perform substitution: set a new value, owner and description
Perform set: set a new value, owner and description
$ kustomize config set DIR/ name-prefix "test" --description "test environment" --set-by "dev"
performed 2 substitutions
set 2 values
Show substitutions: Show the new values
List setters: Show the new values
$ config set dir
NAME DESCRIPTION VALUE TYPE COUNT SUBSTITUTED OWNER
prefix 'test environment' test string 2 true dev
$ config set DIR/
NAME DESCRIPTION VALUE TYPE COUNT SETBY
name-prefix 'test environment' test string 2 dev
New Resource YAML:

View File

@@ -0,0 +1,18 @@
## sink
[Alpha] Implement a Sink by writing input to a local directory.
### Synopsis
[Alpha] Implement a Sink by writing input to a local directory.
kustomize config sink DIR
DIR:
Path to local directory.
`sink` writes its input to a directory
### Examples
kustomize config source DIR/ | your-function | kustomize config sink DIR/

View File

@@ -0,0 +1,21 @@
## source
[Alpha] Implement a Source by reading a local directory.
### Synopsis
[Alpha] Implement a Source by reading a local directory.
kustomize config source DIR
DIR:
Path to local directory.
`source` emits configuration to act as input to a function
### Examples
# emity configuration directory as input source to a function
kustomize config source DIR/
kustomize config source DIR/ | your-function | kustomize config sink DIR/

View File

@@ -9,7 +9,6 @@ require (
github.com/spf13/cobra v0.0.5
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.4.0
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
k8s.io/apimachinery v0.17.0
sigs.k8s.io/kustomize/kyaml v0.0.5
)

View File

@@ -101,6 +101,8 @@ github.com/posener/complete/v2 v2.0.1-alpha.12/go.mod h1://JlL91cS2JV7rOl6LVHrRq
github.com/posener/script v1.0.4 h1:nSuXW5ZdmFnQIueLB2s0qvs4oNsUloM1Zydzh75v42w=
github.com/posener/script v1.0.4/go.mod h1:Rg3ijooqulo05aGLyGsHoLmIOUzHUVK19WVgrYBPU/E=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=

View File

@@ -121,6 +121,13 @@ func (r *CatRunner) runE(c *cobra.Command, args []string) error {
out = o
}
// remove this annotation explicitly, the ByteWriter won't clear it by
// default because it doesn't set it
clear := []string{"config.kubernetes.io/path"}
if r.KeepAnnotations {
clear = nil
}
var outputs []kio.Writer
outputs = append(outputs, kio.ByteWriter{
Writer: out,
@@ -129,6 +136,7 @@ func (r *CatRunner) runE(c *cobra.Command, args []string) error {
WrappingAPIVersion: r.WrapApiVersion,
FunctionConfig: functionConfig,
Style: yaml.GetStyle(r.Styles...),
ClearAnnotations: clear,
})
return handleError(c, kio.Pipeline{Inputs: inputs, Filters: fltr, Outputs: outputs}.Execute())

View File

@@ -90,8 +90,6 @@ metadata:
name: foo
annotations:
app: nginx2
config.kubernetes.io/package: '.'
config.kubernetes.io/path: 'f1.yaml'
spec:
replicas: 1
---
@@ -100,8 +98,6 @@ metadata:
name: foo
annotations:
app: nginx
config.kubernetes.io/package: '.'
config.kubernetes.io/path: 'f1.yaml'
spec:
selector:
app: nginx
@@ -114,8 +110,6 @@ metadata:
app: nginx
annotations:
app: nginx
config.kubernetes.io/package: '.'
config.kubernetes.io/path: 'f2.yaml'
spec:
replicas: 3
`, b.String()) {
@@ -196,8 +190,6 @@ metadata:
name: foo
annotations:
app: nginx2
config.kubernetes.io/package: '.'
config.kubernetes.io/path: 'f1.yaml'
spec:
replicas: 1
---
@@ -206,8 +198,6 @@ metadata:
name: foo
annotations:
app: nginx
config.kubernetes.io/package: '.'
config.kubernetes.io/path: 'f1.yaml'
spec:
selector:
app: nginx
@@ -218,8 +208,6 @@ metadata:
name: foo
annotations:
config.kubernetes.io/local-config: "true"
config.kubernetes.io/package: '.'
config.kubernetes.io/path: 'f2.yaml'
configFn:
container:
image: gcr.io/example/image:version
@@ -233,8 +221,6 @@ metadata:
name: bar
annotations:
app: nginx
config.kubernetes.io/package: '.'
config.kubernetes.io/path: 'f2.yaml'
spec:
replicas: 3
`, b.String()) {
@@ -314,8 +300,6 @@ metadata:
name: foo
annotations:
config.kubernetes.io/local-config: "true"
config.kubernetes.io/package: '.'
config.kubernetes.io/path: 'f2.yaml'
configFn:
container:
image: gcr.io/example/reconciler:v1
@@ -414,8 +398,6 @@ metadata:
name: foo
annotations:
app: nginx2
config.kubernetes.io/package: '.'
config.kubernetes.io/path: 'f1.yaml'
spec:
replicas: 1
---
@@ -424,8 +406,6 @@ metadata:
name: foo
annotations:
app: nginx
config.kubernetes.io/package: '.'
config.kubernetes.io/path: 'f1.yaml'
spec:
selector:
app: nginx
@@ -438,8 +418,6 @@ metadata:
app: nginx
annotations:
app: nginx
config.kubernetes.io/package: '.'
config.kubernetes.io/path: 'f2.yaml'
spec:
replicas: 3
`, string(actual)) {
@@ -536,8 +514,6 @@ metadata:
name: foo
annotations:
app: nginx2
config.kubernetes.io/package: '.'
config.kubernetes.io/path: 'f1.yaml'
spec:
replicas: 1
---
@@ -546,8 +522,6 @@ metadata:
name: foo
annotations:
app: nginx
config.kubernetes.io/package: '.'
config.kubernetes.io/path: 'f1.yaml'
spec:
selector:
app: nginx
@@ -560,8 +534,6 @@ metadata:
app: nginx
annotations:
app: nginx
config.kubernetes.io/package: '.'
config.kubernetes.io/path: 'f2.yaml'
spec:
replicas: 3
`, string(actual)) {

View File

@@ -0,0 +1,47 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package commands
import (
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
"sigs.k8s.io/kustomize/kyaml/setters"
)
// NewListSettersRunner returns a command runner.
func NewListSettersRunner(parent string) *ListSettersRunner {
r := &ListSettersRunner{}
c := &cobra.Command{
Use: "list-setters DIR [NAME]",
Args: cobra.RangeArgs(1, 2),
Short: commands.ListSettersShort,
Long: commands.ListSettersLong,
Example: commands.ListSettersExamples,
PreRunE: r.preRunE,
RunE: r.runE,
}
fixDocs(parent, c)
r.Command = c
return r
}
func ListSettersCommand(parent string) *cobra.Command {
return NewListSettersRunner(parent).Command
}
type ListSettersRunner struct {
Command *cobra.Command
Lookup setters.LookupSetters
}
func (r *ListSettersRunner) preRunE(c *cobra.Command, args []string) error {
if len(args) > 1 {
r.Lookup.Name = args[1]
}
return nil
}
func (r *ListSettersRunner) runE(c *cobra.Command, args []string) error {
return handleError(c, lookup(r.Lookup, c, args))
}

View File

@@ -5,6 +5,7 @@ package commands
import (
"fmt"
"os"
"github.com/olekukonko/tablewriter"
"github.com/spf13/cobra"
@@ -63,14 +64,14 @@ func (r *SetRunner) runE(c *cobra.Command, args []string) error {
return handleError(c, r.perform(c, args))
}
return handleError(c, r.lookup(c, args))
return handleError(c, lookup(r.Lookup, c, args))
}
func (r *SetRunner) lookup(c *cobra.Command, args []string) error {
func lookup(l setters.LookupSetters, c *cobra.Command, args []string) error {
// lookup the setters
err := kio.Pipeline{
Inputs: []kio.Reader{&kio.LocalPackageReader{PackagePath: args[0]}},
Filters: []kio.Filter{&r.Lookup},
Filters: []kio.Filter{&l},
}.Execute()
if err != nil {
return err
@@ -86,8 +87,8 @@ func (r *SetRunner) lookup(c *cobra.Command, args []string) error {
table.SetHeader([]string{
"NAME", "DESCRIPTION", "VALUE", "TYPE", "COUNT", "SETBY",
})
for i := range r.Lookup.SetterCounts {
s := r.Lookup.SetterCounts[i]
for i := range l.SetterCounts {
s := l.SetterCounts[i]
v := s.Value
if s.Value == "" {
v = s.Value
@@ -102,6 +103,11 @@ func (r *SetRunner) lookup(c *cobra.Command, args []string) error {
})
}
table.Render()
if len(l.SetterCounts) == 0 {
// exit non-0 if no matching setters are found
os.Exit(1)
}
return nil
}

View File

@@ -76,7 +76,6 @@ metadata:
annotations:
app: nginx2
config.kubernetes.io/index: '0'
config.kubernetes.io/package: '.'
config.kubernetes.io/path: 'f1.yaml'
spec:
replicas: 1
@@ -87,7 +86,6 @@ metadata:
annotations:
app: nginx
config.kubernetes.io/index: '1'
config.kubernetes.io/package: '.'
config.kubernetes.io/path: 'f1.yaml'
spec:
selector:

View File

@@ -0,0 +1,48 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package commands
import (
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
"sigs.k8s.io/kustomize/kyaml/kio"
)
// GetSinkRunner returns a command for Sink.
func GetSinkRunner(name string) *SinkRunner {
r := &SinkRunner{}
c := &cobra.Command{
Use: "sink DIR",
Short: commands.SinkShort,
Long: commands.SinkLong,
Example: commands.SinkExamples,
RunE: r.runE,
Args: cobra.ExactArgs(1),
}
fixDocs(name, c)
r.Command = c
return r
}
func SinkCommand(name string) *cobra.Command {
return GetSinkRunner(name).Command
}
// SinkRunner contains the run function
type SinkRunner struct {
Command *cobra.Command
}
func (r *SinkRunner) runE(c *cobra.Command, args []string) error {
err := kio.Pipeline{
Inputs: []kio.Reader{&kio.ByteReader{Reader: c.InOrStdin()}},
Outputs: []kio.Writer{
&kio.LocalPackageWriter{
PackagePath: args[0],
ClearAnnotations: []string{"config.kubernetes.io/path"},
},
},
}.Execute()
return handleError(c, err)
}

View File

@@ -0,0 +1,140 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package commands_test
import (
"bytes"
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
)
func TestSinkCommand(t *testing.T) {
d, err := ioutil.TempDir("", "kustomize-source-test")
if !assert.NoError(t, err) {
t.FailNow()
}
defer os.RemoveAll(d)
// fmt the files
b := &bytes.Buffer{}
r := commands.GetSinkRunner("")
r.Command.SetIn(bytes.NewBufferString(`apiVersion: config.kubernetes.io/v1alpha1
kind: ResourceList
items:
- kind: Deployment
metadata:
labels:
app: nginx2
name: foo
annotations:
app: nginx2
config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'f1.yaml'
spec:
replicas: 1
- kind: Service
metadata:
name: foo
annotations:
app: nginx
config.kubernetes.io/index: '1'
config.kubernetes.io/path: 'f1.yaml'
spec:
selector:
app: nginx
- apiVersion: v1
kind: Abstraction
metadata:
name: foo
annotations:
config.kubernetes.io/function: |
container:
image: gcr.io/example/reconciler:v1
config.kubernetes.io/local-config: "true"
config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'f2.yaml'
spec:
replicas: 3
- apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: bar
annotations:
app: nginx
config.kubernetes.io/index: '1'
config.kubernetes.io/path: 'f2.yaml'
spec:
replicas: 3
`))
r.Command.SetArgs([]string{d})
r.Command.SetOut(b)
if !assert.NoError(t, r.Command.Execute()) {
t.FailNow()
}
actual, err := ioutil.ReadFile(filepath.Join(d, "f1.yaml"))
if !assert.NoError(t, err) {
t.FailNow()
}
expected := `kind: Deployment
metadata:
labels:
app: nginx2
name: foo
annotations:
app: nginx2
spec:
replicas: 1
---
kind: Service
metadata:
name: foo
annotations:
app: nginx
spec:
selector:
app: nginx
`
if !assert.Equal(t, expected, string(actual)) {
t.FailNow()
}
actual, err = ioutil.ReadFile(filepath.Join(d, "f2.yaml"))
if !assert.NoError(t, err) {
t.FailNow()
}
expected = `apiVersion: v1
kind: Abstraction
metadata:
name: foo
annotations:
config.kubernetes.io/function: |
container:
image: gcr.io/example/reconciler:v1
config.kubernetes.io/local-config: "true"
spec:
replicas: 3
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: bar
annotations:
app: nginx
spec:
replicas: 3
`
if !assert.Equal(t, expected, string(actual)) {
t.FailNow()
}
}

View File

@@ -0,0 +1,77 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package commands
import (
"fmt"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
// GetSourceRunner returns a command for Source.
func GetSourceRunner(name string) *SourceRunner {
r := &SourceRunner{}
c := &cobra.Command{
Use: "source DIR",
Short: commands.SourceShort,
Long: commands.SourceLong,
Example: commands.SourceExamples,
RunE: r.runE,
Args: cobra.ExactArgs(1),
}
fixDocs(name, c)
c.Flags().StringVar(&r.WrapKind, "wrap-kind", kio.ResourceListKind,
"output using this format.")
c.Flags().StringVar(&r.WrapApiVersion, "wrap-version", kio.ResourceListAPIVersion,
"output using this format.")
c.Flags().StringVar(&r.FunctionConfig, "function-config", "",
"path to function config.")
r.Command = c
_ = c.MarkFlagFilename("function-config", "yaml", "json", "yml")
return r
}
func SourceCommand(name string) *cobra.Command {
return GetSourceRunner(name).Command
}
// SourceRunner contains the run function
type SourceRunner struct {
WrapKind string
WrapApiVersion string
FunctionConfig string
Command *cobra.Command
}
func (r *SourceRunner) runE(c *cobra.Command, args []string) error {
// if there is a function-config specified, emit it
var functionConfig *yaml.RNode
if r.FunctionConfig != "" {
configs, err := kio.LocalPackageReader{PackagePath: r.FunctionConfig}.Read()
if err != nil {
return err
}
if len(configs) != 1 {
return fmt.Errorf("expected exactly 1 functionConfig, found %d", len(configs))
}
functionConfig = configs[0]
}
var outputs []kio.Writer
outputs = append(outputs, kio.ByteWriter{
Writer: c.OutOrStdout(),
KeepReaderAnnotations: true,
WrappingKind: r.WrapKind,
WrappingAPIVersion: r.WrapApiVersion,
FunctionConfig: functionConfig,
})
err := kio.Pipeline{
Inputs: []kio.Reader{kio.LocalPackageReader{PackagePath: args[0]}},
Outputs: outputs}.Execute()
return handleError(c, err)
}

View File

@@ -0,0 +1,136 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package commands_test
import (
"bytes"
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
)
func TestSourceCommand(t *testing.T) {
d, err := ioutil.TempDir("", "kustomize-source-test")
if !assert.NoError(t, err) {
return
}
defer os.RemoveAll(d)
err = ioutil.WriteFile(filepath.Join(d, "f1.yaml"), []byte(`
kind: Deployment
metadata:
labels:
app: nginx2
name: foo
annotations:
app: nginx2
spec:
replicas: 1
---
kind: Service
metadata:
name: foo
annotations:
app: nginx
spec:
selector:
app: nginx
`), 0600)
if !assert.NoError(t, err) {
return
}
err = ioutil.WriteFile(filepath.Join(d, "f2.yaml"), []byte(`
apiVersion: v1
kind: Abstraction
metadata:
name: foo
annotations:
config.kubernetes.io/function: |
container:
image: gcr.io/example/reconciler:v1
config.kubernetes.io/local-config: "true"
spec:
replicas: 3
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: bar
annotations:
app: nginx
spec:
replicas: 3
`), 0600)
if !assert.NoError(t, err) {
return
}
// fmt the files
b := &bytes.Buffer{}
r := commands.GetSourceRunner("")
r.Command.SetArgs([]string{d})
r.Command.SetOut(b)
if !assert.NoError(t, r.Command.Execute()) {
return
}
if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1alpha1
kind: ResourceList
items:
- kind: Deployment
metadata:
labels:
app: nginx2
name: foo
annotations:
app: nginx2
config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'f1.yaml'
spec:
replicas: 1
- kind: Service
metadata:
name: foo
annotations:
app: nginx
config.kubernetes.io/index: '1'
config.kubernetes.io/path: 'f1.yaml'
spec:
selector:
app: nginx
- apiVersion: v1
kind: Abstraction
metadata:
name: foo
annotations:
config.kubernetes.io/function: |
container:
image: gcr.io/example/reconciler:v1
config.kubernetes.io/local-config: "true"
config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'f2.yaml'
spec:
replicas: 3
- apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: bar
annotations:
app: nginx
config.kubernetes.io/index: '1'
config.kubernetes.io/path: 'f2.yaml'
spec:
replicas: 3
`, b.String()) {
return
}
}

View File

@@ -104,7 +104,6 @@ metadata:
namespace: default
annotations:
app: nginx2
config.kubernetes.io/package: .
config.kubernetes.io/path: f1.yaml
spec:
replicas: 1
@@ -118,7 +117,6 @@ metadata:
namespace: default
annotations:
app: nginx2
config.kubernetes.io/package: .
config.kubernetes.io/path: f1.yaml
spec:
replicas: 1
@@ -132,7 +130,6 @@ metadata:
namespace: default
annotations:
app: nginx2
config.kubernetes.io/package: .
config.kubernetes.io/path: f1.yaml
spec:
replicas: 1
@@ -146,7 +143,6 @@ metadata:
namespace: default2
annotations:
app: nginx2
config.kubernetes.io/package: .
config.kubernetes.io/path: f1.yaml
spec:
replicas: 1
@@ -160,7 +156,6 @@ metadata:
namespace: default
annotations:
app: nginx3
config.kubernetes.io/package: .
config.kubernetes.io/path: f1.yaml
spec:
replicas: 1
@@ -171,8 +166,7 @@ metadata:
app: nginx
annotations:
app: nginx
config.kubernetes.io/package: bar-package
config.kubernetes.io/path: f2.yaml
config.kubernetes.io/path: bar-package/f2.yaml
name: bar
spec:
replicas: 3
@@ -183,7 +177,6 @@ metadata:
namespace: default
annotations:
app: nginx
config.kubernetes.io/package: .
config.kubernetes.io/path: f1.yaml
spec:
selector:

View File

@@ -4,288 +4,6 @@
// Code generated by "mdtogo"; DO NOT EDIT.
package api
var ConfigFnLong = `# Configuration Functions API Semantics
Configuration Functions are functions packaged as executables in containers which enable
**shift-left practices**. They configure applications and infrastructure through
Kubernetes style Resource Configuration, but run locally pre-commit.
Configuration functions enable shift-left practices (client-side) through:
- Pre-commit / delivery validation and linting of configuration
- e.g. Fail if any containers don't have PodSecurityPolicy or CPU / Memory limits
- Implementation of abstractions as client actuated APIs (e.g. templating)
- e.g. Create a client-side *"CRD"* for generating configuration checked into git
- Aspect Orient configuration / Injection of cross-cutting configuration
- e.g. T-Shirt size containers by annotating Resources with ` + "`" + `small` + "`" + `, ` + "`" + `medium` + "`" + `, ` + "`" + `large` + "`" + `
and inject the cpu and memory resources into containers accordingly.
- e.g. Inject ` + "`" + `init` + "`" + ` and ` + "`" + `side-car` + "`" + ` containers into Resources based off of Resource
Type, annotations, etc.
Performing these on the client rather than the server enables:
- Configuration to be reviewed prior to being sent to the API server
- Configuration to be validated as part of the CD pipeline
- Configuration for Resources to validated holistically rather than individually
per-Resource -- e.g. ensure the ` + "`" + `Service.selector` + "`" + ` and ` + "`" + `Deployment.spec.template` + "`" + ` labels
match.
- MutatingWebHooks are scoped to a single Resource instance at a time.
- Low-level tweaks to the output of high-level abstractions -- e.g. add an ` + "`" + `init container` + "`" + `
to a client *"CRD"* Resource after it was generated.
- Composition and layering of multiple functions together
- Compose generation, injection, validation together
Configuration Functions are implemented as executable programs published in containers which:
- Accept as input (stdin):
- A list of Resource Configuration
- A Function Configuration (to configure the function itself)
- Emit as output (stdout + exit):
- A list of Resource Configuration
- An exit code for success / failure
### Function Specification
- Functions **SHOULD** be published as container images containing a ` + "`" + `CMD` + "`" + ` invoking an executable.
- Functions **MUST** accept input on STDIN a ` + "`" + `ResourceList` + "`" + ` containing the Resources and
` + "`" + `functionConfig` + "`" + `.
- Functions **MUST** emit output on STDOUT a ` + "`" + `ResourceList` + "`" + ` containing the modified
Resources.
- Functions **MUST** exit non-0 on failure, and exit 0 on success.
- Functions **MAY** emit output on STDERR with error messaging.
- Functions performing validation **SHOULD** exit failure and emit error messaging
on a validation failure.
- Functions generating Resources **SHOULD** retain non-conflicting changes on the
generated Resources -- e.g. 1. the function generates a Deployment, but doesn't
specify ` + "`" + `cpu` + "`" + `, 2. the user sets the ` + "`" + `cpu` + "`" + ` on the generated Resource, 3. the
function should keep the ` + "`" + `cpu` + "`" + ` when regenerating the Resource a second time.
- Functions **SHOULD** be usable outside ` + "`" + `kustomize config run` + "`" + ` -- e.g. though pipeline
mechanisms such as Tekton.
#### Input Format
Functions must accept on STDIN:
` + "`" + `ResourceList` + "`" + `:
- contains ` + "`" + `items` + "`" + ` field, same as ` + "`" + `List.items` + "`" + `
- contains ` + "`" + `functionConfig` + "`" + ` field -- a single item with the configuration for the function itself
Example ` + "`" + `ResourceList` + "`" + ` Input:
apiVersion: config.kubernetes.io/v1alpha1
kind: ResourceList
functionConfig:
apiVersion: example.com/v1beta1
kind: Nginx
metadata:
name: my-instance
annotations:
config.kubernetes.io/local-config: "true"
spec:
replicas: 5
items:
- apiVersion: apps/v1
kind: Deployment
metadata:
name: my-instance
spec:
replicas: 3
...
- apiVersion: v1
kind: Service
metadata:
name: my-instance
spec:
...
#### Output Format
Functions must emit on STDOUT:
` + "`" + `ResourceList` + "`" + `:
- contains ` + "`" + `items` + "`" + ` field, same as ` + "`" + `List.items` + "`" + `
Example ` + "`" + `ResourceList` + "`" + ` Output:
apiVersion: config.kubernetes.io/v1alpha1
kind: ResourceList
items:
- apiVersion: apps/v1
kind: Deployment
metadata:
name: my-instance
spec:
replicas: 5
...
- apiVersion: v1
kind: Service
metadata:
name: my-instance
spec:
...
#### Container Environment
When run by ` + "`" + `kustomize config run` + "`" + `, functions are run in containers with the
following environment:
- Network: ` + "`" + `none` + "`" + `
- User: ` + "`" + `nobody` + "`" + `
- Security Options: ` + "`" + `no-new-privileges` + "`" + `
- Volumes: the volume containing the ` + "`" + `functionConfig` + "`" + ` yaml is mounted under ` + "`" + `/local` + "`" + ` as ` + "`" + `ro` + "`" + `
### Example Function Implementation
Following is an example for implementing an nginx abstraction using a config
function.
#### ` + "`" + `nginx-template.sh` + "`" + `
` + "`" + `nginx-template.sh` + "`" + ` is a simple bash script which uses a *heredoc* as a templating solution
for generating Resources from the functionConfig input fields.
The script wraps itself using ` + "`" + `config run wrap -- $0` + "`" + ` which will:
1. Parse the ` + "`" + `ResourceList.functionConfig` + "`" + ` (provided to the container stdin) into env vars
2. Merge the stdout into the original list of Resources
3. Defaults filenames for newly generated Resources (if they are not set as annotations)
to ` + "`" + `config/NAME_KIND.yaml` + "`" + `
4. Format the output
#!/bin/bash
# script must run wrapped by ` + "`" + `kustomize config run wrap` + "`" + `
# for parsing input the functionConfig into env vars
if [ -z ${WRAPPED} ]; then
export WRAPPED=true
config run wrap -- $0
exit $?
fi
cat <<End-of-message
apiVersion: v1
kind: Service
metadata:
name: ${NAME}
labels:
app: nginx
instance: ${NAME}
spec:
ports:
- port: 80
targetPort: 80
name: http
selector:
app: nginx
instance: ${NAME}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: ${NAME}
labels:
app: nginx
instance: ${NAME}
spec:
replicas: ${REPLICAS}
selector:
matchLabels:
app: nginx
instance: ${NAME}
template:
metadata:
labels:
app: nginx
instance: ${NAME}
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
End-of-message
#### ` + "`" + `Dockerfile` + "`" + `
` + "`" + `Dockerfile` + "`" + ` installs ` + "`" + `kustomize config` + "`" + ` and copies the script into the container image.
FROM golang:1.13-stretch
RUN go get sigs.k8s.io/kustomize/cmd/config
RUN mv /go/bin/config /usr/bin/config
COPY nginx-template.sh /usr/bin/nginx-template.sh
CMD ["nginx-template.sh]
### Example Function Usage
Following is an example of running the ` + "`" + `kustomize config run` + "`" + ` using the preceding API.
#### ` + "`" + `nginx.yaml` + "`" + ` (Input)
` + "`" + `dir/nginx.yaml` + "`" + ` contains a reference to the Function. The contents of ` + "`" + `nginx.yaml` + "`" + `
are passed to the Function through the ` + "`" + `ResourceList.functionConfig` + "`" + ` field.
apiVersion: example.com/v1beta1
kind: Nginx
metadata:
name: my-instance
annotations:
config.kubernetes.io/local-config: "true"
configFn:
container:
image: gcr.io/example-functions/nginx-template:v1.0.0
spec:
replicas: 5
- ` + "`" + `configFn.container.image` + "`" + `: the image to use for this API
- ` + "`" + `annotations[config.kubernetes.io/local-config]` + "`" + `: mark this as not a Resource that should
be applied
#### ` + "`" + `kustomize config run dir/` + "`" + ` (Output)
` + "`" + `dir/my-instance_deployment.yaml` + "`" + ` contains the Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-instance
labels:
app: nginx
instance: my-instance
spec:
replicas: 5
selector:
matchLabels:
app: nginx
instance: my-instance
template:
metadata:
labels:
app: nginx
instance: my-instance
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
` + "`" + `dir/my-instance_service.yaml` + "`" + ` contains the Service:
apiVersion: v1
kind: Service
metadata:
name: my-instance
labels:
app: nginx
instance: my-instance
spec:
ports:
- port: 80
targetPort: 80
name: http
selector:
app: nginx
instance: my-instance`
var ConfigIoLong = `# Configuration IO API Semantics
Resource Configuration may be read / written from / to sources such as directories,
@@ -346,6 +64,355 @@ Example:
annotations:
config.kubernetes.io/local-config: "true"`
var FunctionsImplShort = `Following is an example for implementing an nginx abstraction using a configuration`
var FunctionsImplLong = `# Running Configuration Functions using kustomize CLI
Configuration functions can be implemented using any toolchain and invoked using any
container workflow orchestrator including Tekton, Cloud Build, or run directly using ` + "`" + `docker run` + "`" + `.
Run ` + "`" + `config help docs-fn-spec` + "`" + ` to see the Configuration Functions Specification.
` + "`" + `kustomize config run` + "`" + ` is an example orchestrator for invoking Configuration Functions. This
document describes how to implement and invoke an example function.
function.
### ` + "`" + `nginx-template.sh` + "`" + `
` + "`" + `nginx-template.sh` + "`" + ` is a simple bash script which uses a _heredoc_ as a templating solution
for generating Resources from the functionConfig input fields.
The script wraps itself using ` + "`" + `config run wrap -- $0` + "`" + ` which will:
1. Parse the ` + "`" + `ResourceList.functionConfig` + "`" + ` (provided to the container stdin) into env vars
2. Merge the stdout into the original list of Resources
3. Defaults filenames for newly generated Resources (if they are not set as annotations)
to ` + "`" + `config/NAME_KIND.yaml` + "`" + `
4. Format the output
#!/bin/bash
# script must run wrapped by "kustomize config run wrap"
# for parsing input the functionConfig into env vars
if [ -z ${WRAPPED} ]; then
export WRAPPED=true
config run wrap -- $0
exit $?
fi
cat <<End-of-message
apiVersion: v1
kind: Service
metadata:
name: ${NAME}
labels:
app: nginx
instance: ${NAME}
spec:
ports:
- port: 80
targetPort: 80
name: http
selector:
app: nginx
instance: ${NAME}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: ${NAME}
labels:
app: nginx
instance: ${NAME}
spec:
replicas: ${REPLICAS}
selector:
matchLabels:
app: nginx
instance: ${NAME}
template:
metadata:
labels:
app: nginx
instance: ${NAME}
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
End-of-message
### Dockerfile
` + "`" + `Dockerfile` + "`" + ` installs ` + "`" + `kustomize config` + "`" + ` and copies the script into the container image.
FROM golang:1.13-stretch
RUN go get sigs.k8s.io/kustomize/cmd/config
RUN mv /go/bin/config /usr/bin/config
COPY nginx-template.sh /usr/bin/nginx-template.sh
CMD ["nginx-template.sh]
## Example Function Usage
Following is an example of running the ` + "`" + `kustomize config run` + "`" + ` using the preceding API.
When run by ` + "`" + `kustomize config run` + "`" + `, functions are run in containers with the
following environment:
- Network: ` + "`" + `none` + "`" + `
- User: ` + "`" + `nobody` + "`" + `
- Security Options: ` + "`" + `no-new-privileges` + "`" + `
- Volumes: the volume containing the ` + "`" + `functionConfig` + "`" + ` yaml is mounted under ` + "`" + `/local` + "`" + ` as ` + "`" + `ro` + "`" + `
### Input
` + "`" + `dir/nginx.yaml` + "`" + ` contains a reference to the Function. The contents of ` + "`" + `nginx.yaml` + "`" + `
are passed to the Function through the ` + "`" + `ResourceList.functionConfig` + "`" + ` field.
apiVersion: example.com/v1beta1
kind: Nginx
metadata:
name: my-instance
annotations:
config.kubernetes.io/local-config: "true"
config.k8s.io/function: |
container:
image: gcr.io/example-functions/nginx-template:v1.0.0
spec:
replicas: 5
- ` + "`" + `annotations[config.k8s.io/function].container.image` + "`" + `: the image to use for this API
- ` + "`" + `annotations[config.kubernetes.io/local-config]` + "`" + `: mark this as not a Resource that should
be applied
### Output
The function is invoked using by runing ` + "`" + `kustomize config run dir/` + "`" + `.
` + "`" + `dir/my-instance_deployment.yaml` + "`" + ` contains the Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-instance
labels:
app: nginx
instance: my-instance
spec:
replicas: 5
selector:
matchLabels:
app: nginx
instance: my-instance
template:
metadata:
labels:
app: nginx
instance: my-instance
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
` + "`" + `dir/my-instance_service.yaml` + "`" + ` contains the Service:
apiVersion: v1
kind: Service
metadata:
name: my-instance
labels:
app: nginx
instance: my-instance
spec:
ports:
- port: 80
targetPort: 80
name: http
selector:
app: nginx
instance: my-instance`
var FunctionsSpecShort = `_Configuration functions_ enable shift-left practices (client-side) through:`
var FunctionsSpecLong = `# Configuration Functions Specification
This document specifies a standard for client-side functions that operate on
Kubernetes declarative configurations. This standard enables creating
small, interoperable, and language-independent executable programs packaged as
containers that can be chained together as part of a configuration management pipeline.
The end result of such a pipeline are fully rendered configurations that can then be
applied to a control plane (e.g. Using kubectl apply for Kubernetes control plane).
As such, although this document references Kubernetes Resource Model and API conventions,
it is completely decoupled from Kuberentes API machinery and does not depend on any
in-cluster components.
This document references terms described in [Kubernetes API Conventions][1].
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
interpreted as described in [RFC 2119][2].
- Pre-commit / delivery validation and linting of configuration
- e.g. Fail if any containers don't have PodSecurityPolicy or CPU / Memory limits
- Implementation of abstractions as client actuated APIs (e.g. templating)
- e.g. Create a client-side _"CRD"_ for generating configuration checked into git
- Aspect Orient configuration / Injection of cross-cutting configuration
- e.g. T-Shirt size containers by annotating Resources with ` + "`" + `small` + "`" + `, ` + "`" + `medium` + "`" + `, ` + "`" + `large` + "`" + `
and inject the cpu and memory resources into containers accordingly.
- e.g. Inject ` + "`" + `init` + "`" + ` and ` + "`" + `side-car` + "`" + ` containers into Resources based off of Resource
Type, annotations, etc.
Performing these on the client rather than the server enables:
- Configuration to be reviewed prior to being sent to the API server
- Configuration to be validated as part of the CI?CD pipeline
- Configuration for Resources to validated holistically rather than individually
per-Resource
- e.g. ensure the ` + "`" + `Service.selector` + "`" + ` and ` + "`" + `Deployment.spec.template` + "`" + ` labels
match.
- e.g. MutatingWebHooks are scoped to a single Resource instance at a time.
- Low-level tweaks to the output of high-level abstractions
- e.g. add an ` + "`" + `init container` + "`" + ` to a client _"CRD"_ Resource after it was generated.
- Composition and layering of multiple functions together
- Compose generation, injection, validation together
## Spec
### Input Type
A function MUST accept as input a single [Kubernetes List type][3].
The ` + "`" + `items` + "`" + ` field in the input will contain a sequence of [Object types][3].
A function MAY not support [Simple types][3] and List types.
An example using ` + "`" + `v1/ConfigMapList` + "`" + ` as input:
apiVersion: v1
kind: ConfigMapList
items:
- apiVersion: v1
kind: ConfigMap
metadata:
name: config1
data:
p1: v1
p2: v2
- apiVersion: v1
kind: ConfigMap
metadata:
name: config2
An example using ` + "`" + `v1/List` + "`" + ` as input:
apiVersion: v1
kind: List
items:
spec:
- apiVersion: foo-corp.com/v1
kind: FulfillmentCenter
metadata:
name: staging
address: "100 Main St."
- apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: namespace-reader
rules:
- resources:
- namespaces
apiGroups:
- ""
verbs:
- get
- watch
- list
In addition, a function MUST accept as input a List of kind ` + "`" + `ResourceList` + "`" + ` where the
` + "`" + `functionConfig` + "`" + ` field, if present, will contain the invocation-specific configuration passed to the function
by the orchestrator.
Functions MAY consider this field optional so that they can be triggered in an ad-hoc fashion.
An example using ` + "`" + `config.kubernetes.io/v1beta1/ResourceList` + "`" + ` as input:
apiVersion: config.kubernetes.io/v1beta1
kind: ResourceList
functionConfig:
apiVersion: foo-corp.com/v1
kind: FulfillmentCenter
metadata:
name: staging
metadata:
annotations:
config.k8s.io/function: |
container:
image: gcr.io/example/foo:v1.0.0
spec:
address: "100 Main St."
items:
- apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: namespace-reader
rules:
- resources:
- namespaces
apiGroups:
- ""
verbs:
- get
- watch
- list
Here ` + "`" + `FulfillmentCenter` + "`" + ` kind with name ` + "`" + `staging` + "`" + ` is passed as the invocation-specific configuration
to the function.
### Output Type
A functions output MUST be the same as the input specification above
-- i.e. ` + "`" + `ResourceList` + "`" + ` or ` + "`" + `List` + "`" + `.
This is necessary to enable chaining two or more functions together in a pipeline.
The serialization format of the output SHOULD match that of its input on each invocation
-- e.g. if the input was a ` + "`" + `ResourceList` + "`" + `, the output should also be a ` + "`" + `ResourceList` + "`" + `.
### Serialization Format
A function MUST support YAML as a serialization format for the input and output.
A function MUST use utf8 encoding (as YAML is a superset of JSON, JSON will also be supported
by any conforming function).
### Operations
A function MAY Create, Update, or Delete any number of items in the ` + "`" + `items` + "`" + ` field and output the
resultant list.
A function MAY modify annotations with prefix ` + "`" + `config.kubernetes.io` + "`" + `, but must be careful about
doing so since theyre used for orchestration purposes and will likely impact subsequent functions
in the pipeline.
A function SHOULD preserve comments when input serialization format is YAML.
This allows for human authoring of configuration to coexist with changes made by functions.
### Containerization
A function MUST be implemented as a container.
A function container MUST be capable of running as a non-root user if it does not require
access to host filesystem or makes network calls.
### stdin/stdout/stderr and Exit Codes
A function MUST accept input from stdin and emit output to stdout.
Any error messages MUST be emitted to stderr.
An exit code of zero indicates function execution was successful.
A non-zero exit code indicates a failure.
[1]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md
[2]: https://tools.ietf.org/html/rfc2119
[3]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#types-kinds`
var Merge2Long = `# Merge (2-way)
2-way merges fields from a source to a destination, overriding the destination fields

View File

@@ -142,6 +142,25 @@ var GrepExamples = `
# look for Resources matching a specific container image
kustomize config grep "spec.template.spec.containers[name=nginx].image=nginx:1\.7\.9" my-dir/ | kustomize config tree`
var ListSettersShort = `[Alpha] List setters for Resources.`
var ListSettersLong = `
List setters for Resources.
DIR
A directory containing Resource configuration.
NAME
Optional. The name of the setter to display.
`
var ListSettersExamples = `
Show setters:
$ config set DIR/
NAME DESCRIPTION VALUE TYPE COUNT SETBY
name-prefix '' PREFIX string 2`
var MergeShort = `[Alpha] Merge Resource configuration files`
var MergeLong = `
[Alpha] Merge Resource configuration files
@@ -201,8 +220,8 @@ order they appear in the file).
#### Config Functions:
Config functions are specified as Kubernetes types containing a metadata.configFn.container.image
field. This field tells run how to invoke the container.
Config functions are specified as Kubernetes types containing a metadata.annotations.[config.kubernetes.io/function]
field specifying an image for the container to run. This image tells run how to invoke the container.
Example config function:
@@ -210,17 +229,17 @@ order they appear in the file).
apiVersion: fn.example.com/v1beta1
kind: ExampleFunctionKind
metadata:
configFn:
container:
# function is invoked as a container running this image
image: gcr.io/example/examplefunction:v1.0.1
annotations:
config.kubernetes.io/function: |
container:
# function is invoked as a container running this image
image: gcr.io/example/examplefunction:v1.0.1
config.kubernetes.io/local-config: "true" # tools should ignore this
spec:
configField: configValue
In the preceding example, 'kustomize config run example/' would identify the function by
the metadata.configFn field. It would then write all Resources in the directory to
the metadata.annotations.[config.kubernetes.io/function] field. It would then write all Resources in the directory to
a container stdin (running the gcr.io/example/examplefunction:v1.0.1 image). It
would then write the container stdout back to example/, replacing the directory
file contents.
@@ -286,19 +305,19 @@ var SetExamples = `
List setters: Show the possible setters
$ config set DIR/
NAME DESCRIPTION VALUE TYPE COUNT OWNER
NAME DESCRIPTION VALUE TYPE COUNT SETBY
name-prefix '' PREFIX string 2
Perform substitution: set a new value, owner and description
Perform set: set a new value, owner and description
$ kustomize config set DIR/ name-prefix "test" --description "test environment" --set-by "dev"
performed 2 substitutions
set 2 values
Show substitutions: Show the new values
List setters: Show the new values
$ config set dir
NAME DESCRIPTION VALUE TYPE COUNT SUBSTITUTED OWNER
prefix 'test environment' test string 2 true dev
$ config set DIR/
NAME DESCRIPTION VALUE TYPE COUNT SETBY
name-prefix 'test environment' test string 2 dev
New Resource YAML:
@@ -313,6 +332,37 @@ var SetExamples = `
name: test-app2 # {"description":"test environment","type":"string","x-kustomize":{"setBy":"dev","partialFieldSetters":[{"name":"name-prefix","value":"test"}]}}
...`
var SinkShort = `[Alpha] Implement a Sink by writing input to a local directory.`
var SinkLong = `
[Alpha] Implement a Sink by writing input to a local directory.
kustomize config sink DIR
DIR:
Path to local directory.
` + "`" + `sink` + "`" + ` writes its input to a directory
`
var SinkExamples = `
kustomize config source DIR/ | your-function | kustomize config sink DIR/`
var SourceShort = `[Alpha] Implement a Source by reading a local directory.`
var SourceLong = `
[Alpha] Implement a Source by reading a local directory.
kustomize config source DIR
DIR:
Path to local directory.
` + "`" + `source` + "`" + ` emits configuration to act as input to a function
`
var SourceExamples = `
# emity configuration directory as input source to a function
kustomize config source DIR/
kustomize config source DIR/ | your-function | kustomize config sink DIR/`
var TreeShort = `[Alpha] Display Resource structure from a directory or stdin.`
var TreeLong = `
[Alpha] Display Resource structure from a directory or stdin.

View File

@@ -9,6 +9,7 @@ package main
import (
"os"
"sigs.k8s.io/kustomize/cmd/config/complete"
"sigs.k8s.io/kustomize/cmd/config/configcobra"
"sigs.k8s.io/kustomize/kyaml/commandutil"
)
@@ -16,7 +17,10 @@ import (
func main() {
// enable the config commands
os.Setenv(commandutil.EnableAlphaCommmandsEnvName, "true")
if err := configcobra.NewConfigCommand("").Execute(); err != nil {
cmd := configcobra.NewConfigCommand("")
complete.Complete(cmd).Complete("config")
if err := cmd.Execute(); err != nil {
os.Exit(1)
}
}