Merge branch 'master' into transformer-no-create-arrays

This commit is contained in:
Tom Wieczorek
2019-05-28 11:27:04 +02:00
1136 changed files with 20658 additions and 457722 deletions

View File

@@ -1,31 +1,42 @@
language: go
os:
- linux
- osx
# TODO: Uncomment when tests running on Windows.
# - windows
go:
- 1.11.x
go_import_path: sigs.k8s.io/kustomize
# Maybe, maybe not.
# sudo: false
addons:
apt:
packages:
- tree
homebrew:
packages:
- tree
# Only clone the most recent commit.
git:
depth: 1
language: go
go:
- "1.12"
go_import_path: sigs.k8s.io/kustomize
env:
- GOLANGCI_RELEASE="v1.10.2"
- GOLANGCI_RELEASE="v1.12"
before_install:
- source ./bin/consider-early-travis-exit.sh
- sudo apt-get install tree
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $GOPATH/bin ${GOLANGCI_RELEASE}
- go get -u github.com/monopole/mdrip
# The following would install Helm if needed for some reason.
# - wget https://storage.googleapis.com/kubernetes-helm/helm-v2.13.1-linux-amd64.tar.gz
# - tar -xvzf helm-v2.13.1-linux-amd64.tar.gz
# - sudo mv linux-amd64/helm /usr/local/bin/helm
# Install must be set to prevent default `go get` to run.
# The dependencies have already been vendored by `dep` so
# we don't need to fetch them.
install:
-
# Skip the install process; let pre-commit.sh do it.
install: true
script:
- ./bin/pre-commit.sh

388
Gopkg.lock generated
View File

@@ -1,388 +0,0 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
digest = "1:d8ebbd207f3d3266d4423ce4860c9f3794956306ded6c7ba312ecc69cdfbf04c"
name = "github.com/PuerkitoBio/purell"
packages = ["."]
pruneopts = "NUT"
revision = "0bcb03f4b4d0a9428594752bd2a3b9aa0a9d4bd4"
version = "v1.1.0"
[[projects]]
branch = "master"
digest = "1:8098cd40cd09879efbf12e33bcd51ead4a66006ac802cd563a66c4f3373b9727"
name = "github.com/PuerkitoBio/urlesc"
packages = ["."]
pruneopts = "NUT"
revision = "de5bf2ad457846296e2031421a34e2568e304e35"
[[projects]]
digest = "1:a2c1d0e43bd3baaa071d1b9ed72c27d78169b2b269f71c105ac4ba34b1be4a39"
name = "github.com/davecgh/go-spew"
packages = ["spew"]
pruneopts = "NUT"
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
version = "v1.1.0"
[[projects]]
digest = "1:f8e6f07329067bc182633dcb19a3df53ce5d454b551e1b5a1cac2163748648d9"
name = "github.com/emicklei/go-restful"
packages = [
".",
"log",
]
pruneopts = "NUT"
revision = "3658237ded108b4134956c1b3050349d93e7b895"
version = "v2.7.1"
[[projects]]
digest = "1:ad32dc29f37281bacb5dcedff17c9461dc1739dc8a5f63a71ab491c6e92edf8d"
name = "github.com/evanphx/json-patch"
packages = ["."]
pruneopts = "NUT"
revision = "afac545df32f2287a079e2dfb7ba2745a643747e"
version = "v3.0.0"
[[projects]]
digest = "1:81466b4218bf6adddac2572a30ac733a9255919bc2f470b4827a317bd4ee1756"
name = "github.com/ghodss/yaml"
packages = ["."]
pruneopts = "NUT"
revision = "0ca9ea5df5451ffdf184b4428c902747c2c11cd7"
version = "v1.0.0"
[[projects]]
branch = "master"
digest = "1:260f7ebefc63024c8dfe2c9f1a2935a89fa4213637a1f522f592f80c001cc441"
name = "github.com/go-openapi/jsonpointer"
packages = ["."]
pruneopts = "NUT"
revision = "3a0015ad55fa9873f41605d3e8f28cd279c32ab2"
[[projects]]
branch = "master"
digest = "1:98abd61947ff5c7c6fcfec5473d02a4821ed3a2dd99a4fbfdb7925b0dd745546"
name = "github.com/go-openapi/jsonreference"
packages = ["."]
pruneopts = "NUT"
revision = "3fb327e6747da3043567ee86abd02bb6376b6be2"
[[projects]]
branch = "master"
digest = "1:e95b560c49fb849a61957a5fb3346ce23b3f67426e00e01179e5396cabc9a12c"
name = "github.com/go-openapi/spec"
packages = ["."]
pruneopts = "NUT"
revision = "bcff419492eeeb01f76e77d2ebc714dc97b607f5"
[[projects]]
branch = "master"
digest = "1:a610c604eb06f0be4b0fc667388b7a221155d77d7f9089f70ac142a4a9daf014"
name = "github.com/go-openapi/swag"
packages = ["."]
pruneopts = "NUT"
revision = "811b1089cde9dad18d4d0c2d09fbdbf28dbd27a5"
[[projects]]
digest = "1:1b3dd24f14a5280710fc7a3aa2480b6e4d20fdfc905841de9a3aa2aa2f1d4ee9"
name = "github.com/gogo/protobuf"
packages = [
"proto",
"sortkeys",
]
pruneopts = "NUT"
revision = "1adfc126b41513cc696b209667c8656ea7aac67c"
version = "v1.0.0"
[[projects]]
branch = "master"
digest = "1:e2b86e41f3d669fc36b50d31d32d22c8ac656c75aa5ea89717ce7177e134ff2a"
name = "github.com/golang/glog"
packages = ["."]
pruneopts = "NUT"
revision = "23def4e6c14b4da8ac2ed8007337bc5eb5007998"
[[projects]]
digest = "1:03e14cff610a8a58b774e36bd337fa979482be86aab01be81fb8bbd6d0f07fc8"
name = "github.com/golang/protobuf"
packages = [
"proto",
"ptypes",
"ptypes/any",
"ptypes/duration",
"ptypes/timestamp",
]
pruneopts = "NUT"
revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265"
version = "v1.1.0"
[[projects]]
branch = "master"
digest = "1:52c5834e2bebac9030c97cc0798ac11c3aa8a39f098aeb419f142533da6cd3cc"
name = "github.com/google/gofuzz"
packages = ["."]
pruneopts = "NUT"
revision = "24818f796faf91cd76ec7bddd72458fbced7a6c1"
[[projects]]
digest = "1:3d7c1446fc5c710351b246c0dc6700fae843ca27f5294d0bd9f68bab2a810c44"
name = "github.com/googleapis/gnostic"
packages = [
"OpenAPIv2",
"compiler",
"extensions",
]
pruneopts = "NUT"
revision = "ee43cbb60db7bd22502942cccbc39059117352ab"
version = "v0.1.0"
[[projects]]
digest = "1:406338ad39ab2e37b7f4452906442a3dbf0eb3379dd1f06aafb5c07e769a5fbb"
name = "github.com/inconshreveable/mousetrap"
packages = ["."]
pruneopts = "NUT"
revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
version = "v1.0"
[[projects]]
digest = "1:42c47ace7ccb114261ef7e0d418d274921514ab50a3bf6bdb9e51c3dde8ce13d"
name = "github.com/json-iterator/go"
packages = ["."]
pruneopts = "NUT"
revision = "ca39e5af3ece67bbcda3d0f4f56a8e24d9f2dad4"
version = "1.1.3"
[[projects]]
branch = "master"
digest = "1:ada518b8c338e10e0afa443d84671476d3bd1d926e13713938088e8ddbee1a3e"
name = "github.com/mailru/easyjson"
packages = [
"buffer",
"jlexer",
"jwriter",
]
pruneopts = "NUT"
revision = "3fdea8d05856a0c8df22ed4bc71b3219245e4485"
[[projects]]
digest = "1:2f42fa12d6911c7b7659738758631bec870b7e9b4c6be5444f963cdcfccc191f"
name = "github.com/modern-go/concurrent"
packages = ["."]
pruneopts = "NUT"
revision = "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94"
version = "1.0.3"
[[projects]]
digest = "1:314a5881fab303a80d6d2e35a77000f2224bb50f09ef63a9aa4c1f9eaef985d8"
name = "github.com/modern-go/reflect2"
packages = ["."]
pruneopts = "NUT"
revision = "1df9eeb2bb81f327b96228865c5687bc2194af3f"
version = "1.0.0"
[[projects]]
digest = "1:5cf3f025cbee5951a4ee961de067c8a89fc95a5adabead774f82822efabab121"
name = "github.com/pkg/errors"
packages = ["."]
pruneopts = "NUT"
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
version = "v0.8.0"
[[projects]]
digest = "1:0f156dbd01b40676bdcbc64e51535c09b50f83c9cca5faef3090f82f18bda3c2"
name = "github.com/spf13/cobra"
packages = ["."]
pruneopts = "NUT"
revision = "a1f051bc3eba734da4772d60e2d677f47cf93ef4"
version = "v0.0.2"
[[projects]]
digest = "1:15e5c398fbd9d2c439b635a08ac161b13d04f0c2aa587fe256b65dc0c3efe8b7"
name = "github.com/spf13/pflag"
packages = ["."]
pruneopts = "NUT"
revision = "583c0c0531f06d5278b7d917446061adc344b5cd"
version = "v1.0.1"
[[projects]]
branch = "master"
digest = "1:d1a6ebe75268a41b6fbb1d43947cf8688d8580423b7484fa5ae608beef6df24d"
name = "golang.org/x/net"
packages = [
"http2",
"http2/hpack",
"idna",
"lex/httplex",
]
pruneopts = "NUT"
revision = "1c05540f6879653db88113bc4a2b70aec4bd491f"
[[projects]]
digest = "1:e33513a825fcd765e97b5de639a2f7547542d1a8245df0cef18e1fd390b778a9"
name = "golang.org/x/text"
packages = [
"collate",
"collate/build",
"internal/colltab",
"internal/gen",
"internal/tag",
"internal/triegen",
"internal/ucd",
"language",
"secure/bidirule",
"transform",
"unicode/bidi",
"unicode/cldr",
"unicode/norm",
"unicode/rangetable",
"width",
]
pruneopts = "NUT"
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
version = "v0.3.0"
[[projects]]
digest = "1:2d1fbdc6777e5408cabeb02bf336305e724b925ff4546ded0fa8715a7267922a"
name = "gopkg.in/inf.v0"
packages = ["."]
pruneopts = "NUT"
revision = "d2d2541c53f18d2a059457998ce2876cc8e67cbf"
version = "v0.9.1"
[[projects]]
digest = "1:7c95b35057a0ff2e19f707173cc1a947fa43a6eb5c4d300d196ece0334046082"
name = "gopkg.in/yaml.v2"
packages = ["."]
pruneopts = "NUT"
revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
version = "v2.2.1"
[[projects]]
branch = "master"
digest = "1:d895c7c24a0dd1ed2ecd061fd88dfea9e1e84d6f280ed859942a2d1aabee10ec"
name = "k8s.io/api"
packages = [
"admissionregistration/v1alpha1",
"admissionregistration/v1beta1",
"apps/v1",
"apps/v1beta1",
"apps/v1beta2",
"authentication/v1",
"authentication/v1beta1",
"authorization/v1",
"authorization/v1beta1",
"autoscaling/v1",
"autoscaling/v2beta1",
"batch/v1",
"batch/v1beta1",
"batch/v2alpha1",
"certificates/v1beta1",
"core/v1",
"events/v1beta1",
"extensions/v1beta1",
"networking/v1",
"policy/v1beta1",
"rbac/v1",
"rbac/v1alpha1",
"rbac/v1beta1",
"scheduling/v1alpha1",
"settings/v1alpha1",
"storage/v1",
"storage/v1alpha1",
"storage/v1beta1",
]
pruneopts = "NUT"
revision = "53d615ae3f440f957cb9989d989d597f047262d9"
[[projects]]
branch = "master"
digest = "1:dff69dd9d9fc681ae077ce5a409aca3c24894d09102ab0395ca7972f6ec01811"
name = "k8s.io/apimachinery"
packages = [
"pkg/api/equality",
"pkg/api/meta",
"pkg/api/resource",
"pkg/api/validation",
"pkg/apis/meta/v1",
"pkg/apis/meta/v1/unstructured",
"pkg/apis/meta/v1/validation",
"pkg/apis/meta/v1beta1",
"pkg/conversion",
"pkg/conversion/queryparams",
"pkg/fields",
"pkg/labels",
"pkg/runtime",
"pkg/runtime/schema",
"pkg/runtime/serializer",
"pkg/runtime/serializer/json",
"pkg/runtime/serializer/protobuf",
"pkg/runtime/serializer/recognizer",
"pkg/runtime/serializer/versioning",
"pkg/selection",
"pkg/types",
"pkg/util/errors",
"pkg/util/framer",
"pkg/util/intstr",
"pkg/util/json",
"pkg/util/mergepatch",
"pkg/util/net",
"pkg/util/runtime",
"pkg/util/sets",
"pkg/util/strategicpatch",
"pkg/util/validation",
"pkg/util/validation/field",
"pkg/util/wait",
"pkg/util/yaml",
"pkg/watch",
"third_party/forked/golang/json",
"third_party/forked/golang/reflect",
]
pruneopts = "NUT"
revision = "13b73596e4b63e03203e86f6d9c7bcc1b937c62f"
[[projects]]
digest = "1:ae9ced9ef7b8eb2794a4f80bc3af9d2bc38ec7d60337367bad9a655c1d641458"
name = "k8s.io/client-go"
packages = ["kubernetes/scheme"]
pruneopts = "NUT"
revision = "23781f4d6632d88e869066eaebb743857aa1ef9b"
version = "v7.0.0"
[[projects]]
branch = "master"
digest = "1:f4fb3421360af5c51070bfe0c1c7467f8809fa70e278e129f068f5106b5c8a65"
name = "k8s.io/kube-openapi"
packages = [
"pkg/common",
"pkg/util/proto",
]
pruneopts = "NUT"
revision = "b3f03f55328800731ce03a164b80973014ecd455"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
input-imports = [
"github.com/evanphx/json-patch",
"github.com/ghodss/yaml",
"github.com/pkg/errors",
"github.com/spf13/cobra",
"gopkg.in/yaml.v2",
"k8s.io/api/core/v1",
"k8s.io/apimachinery/pkg/api/validation",
"k8s.io/apimachinery/pkg/apis/meta/v1",
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured",
"k8s.io/apimachinery/pkg/apis/meta/v1/validation",
"k8s.io/apimachinery/pkg/runtime",
"k8s.io/apimachinery/pkg/runtime/schema",
"k8s.io/apimachinery/pkg/util/mergepatch",
"k8s.io/apimachinery/pkg/util/strategicpatch",
"k8s.io/apimachinery/pkg/util/validation",
"k8s.io/apimachinery/pkg/util/validation/field",
"k8s.io/apimachinery/pkg/util/yaml",
"k8s.io/client-go/kubernetes/scheme",
"k8s.io/kube-openapi/pkg/common",
]
solver-name = "gps-cdcl"
solver-version = 1

View File

@@ -1,58 +0,0 @@
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
# prune out unused content from vendor
[prune]
go-tests = true
non-go = true
unused-packages = true
[[constraint]]
name = "github.com/evanphx/json-patch"
version = "3.0.0"
[[constraint]]
name = "github.com/ghodss/yaml"
version = "1.0.0"
[[constraint]]
name = "github.com/spf13/cobra"
version = "0.0.2"
[[constraint]]
branch = "master"
name = "k8s.io/api"
[[constraint]]
branch = "master"
name = "k8s.io/apimachinery"
[[constraint]]
name = "k8s.io/client-go"
version = "7.0.0"
[[override]]
branch = "master"
name = "k8s.io/utils"
[[override]]
branch = "master"
name = "github.com/go-openapi/spec"

View File

@@ -1,6 +1,5 @@
aliases:
kustomize-admins:
- grodrigues3
- monopole
- pwittrock
kustomize-maintainers:

View File

@@ -9,14 +9,21 @@ patch [kubernetes style] API objects. It's like
[`make`], in that what it does is declared in a file,
and it's like [`sed`], in that it emits editted text.
This tool is sponsored by [sig-cli] ([KEP]).
This tool is sponsored by [sig-cli] ([KEP]), and
inspired by [DAM].
[![Build Status](https://travis-ci.org/kubernetes-sigs/kustomize.svg?branch=master)](https://travis-ci.org/kubernetes-sigs/kustomize)
[![Go Report Card](https://goreportcard.com/badge/github.com/kubernetes-sigs/kustomize)](https://goreportcard.com/report/github.com/kubernetes-sigs/kustomize)
**Installation**: Download a binary from the [release
page], or see these [install] notes. Then try one of
the tested [examples].
Download a binary from the [release page], or see
these [instructions](docs/INSTALL.md).
Browse the [docs](docs) or jump right into the
tested [examples](examples).
kustomize [v2.0.3] is available in [kubectl v1.14][kubectl].
## Usage
@@ -115,29 +122,44 @@ The YAML can be directly [applied] to a cluster:
> kustomize build ~/someApp/overlays/production | kubectl apply -f -
> ```
## Community, discussion, contribution, and support
## Community
Learn how to engage with the Kubernetes community on the [community page].
To file bugs please read [this](docs/bugs.md).
You can reach the maintainers of this project at:
Before working on an implementation, please
* Read the [eschewed feature list].
* File an issue describing
how the new feature would behave
and label it [kind/feature].
### Other communication channels
- [Slack]
- [Mailing List]
- General kubernetes [community page]
### Code of conduct
Participation in the Kubernetes community is governed by the [Kubernetes Code of Conduct].
Participation in the Kubernetes community
is governed by the [Kubernetes Code of Conduct].
[KEP]: https://github.com/kubernetes/community/blob/master/keps/sig-cli/0008-kustomize.md
[`make`]: https://www.gnu.org/software/make
[`sed`]: https://www.gnu.org/software/sed
[DAM]: docs/glossary.md#declarative-application-management
[KEP]: https://github.com/kubernetes/enhancements/blob/master/keps/sig-cli/0008-kustomize.md
[Kubernetes Code of Conduct]: code-of-conduct.md
[Mailing List]: https://groups.google.com/forum/#!forum/kubernetes-sig-cli
[Slack]: https://kubernetes.slack.com/messages/sig-cli
[applied]: docs/glossary.md#apply
[base]: docs/glossary.md#base
[community page]: http://kubernetes.io/community/
[declarative configuration]: docs/glossary.md#declarative-application-management
[examples]: examples/README.md
[eschewed feature list]: docs/eschewedFeatures.md
[imageBase]: docs/base.jpg
[imageOverlay]: docs/overlay.jpg
[install]: docs/INSTALL.md
[kind/feature]: https://github.com/kubernetes-sigs/kustomize/labels/kind%2Ffeature
[kubectl]: https://kubernetes.io/blog/2019/03/25/kubernetes-1-14-release-announcement
[kubernetes style]: docs/glossary.md#kubernetes-style-object
[kustomization]: docs/glossary.md#kustomization
[overlay]: docs/glossary.md#overlay
@@ -148,8 +170,5 @@ Participation in the Kubernetes community is governed by the [Kubernetes Code of
[sig-cli]: https://github.com/kubernetes/community/blob/master/sig-cli/README.md
[variant]: docs/glossary.md#variant
[variants]: docs/glossary.md#variant
[v2.0.3]: https://github.com/kubernetes-sigs/kustomize/releases/tag/v2.0.3
[workflows]: docs/workflows.md
[community page]: http://kubernetes.io/community/
[Kubernetes Code of Conduct]: code-of-conduct.md
[Slack]: https://kubernetes.slack.com/messages/sig-cli
[Mailing List]: https://groups.google.com/forum/#!forum/kubernetes-sig-cli

View File

@@ -11,7 +11,7 @@ cd "$base_dir" || {
rc=0
function runTest {
function runFunc {
local name=$1
local result="SUCCESS"
printf "============== begin %s\n" "$name"
@@ -32,13 +32,79 @@ function testGoTest {
go test -v ./...
}
# These tests require the helm program, and at the moment
# we're not asking travis to install helm.
function testNoTravisGoTest {
go test -v sigs.k8s.io/kustomize/pkg/target \
-run TestChartInflatorPlugin -tags=notravis
go test -v sigs.k8s.io/kustomize/plugin/someteam.example.com/v1/chartinflator/... \
-run TestChartInflator -tags=notravis
mdrip --mode test --label helmtest README.md ./examples/chart.md
}
function testExamples {
mdrip --mode test --label test README.md ./examples
}
runTest testGoLangCILint
runTest testGoTest
runTest testExamples
function generateCode {
./plugin/generateBuiltins.sh $oldGoPath
}
# Use of GOPATH is optional if go modules are
# used. This script tries to work for people who
# don't have GOPATH set, and work for travis.
#
# Upon entry, travis has GOPATH set, and used it
# to install mdrip and the like.
#
# Use GOPATH to define XDG_CONFIG_HOME, then unset
# GOPATH so that go.mod is unambiguously honored.
echo "GOPATH=$GOPATH"
if [ -z ${GOPATH+x} ]; then
echo GOPATH is unset
tmp=$HOME/gopath
if [ -d "$tmp" ]; then
oldGoPath=$tmp
else
tmp=$HOME/go
if [ -d "$tmp" ]; then
oldGoPath=$tmp
fi
fi
else
oldGoPath=$GOPATH
unset GOPATH
fi
echo "oldGoPath=$oldGoPath"
export XDG_CONFIG_HOME=$oldGoPath/src/sigs.k8s.io
echo "XDG_CONFIG_HOME=$XDG_CONFIG_HOME"
if [ ! -d "$XDG_CONFIG_HOME" ]; then
echo "$XDG_CONFIG_HOME is not a directory."
exit 1
fi
# Until go v1.13, set this explicitly.
export GO111MODULE=on
echo "HOME=$HOME"
echo "GOPATH=$GOPATH"
echo "GO111MODULE=$GO111MODULE"
echo pwd=`pwd`
echo " "
echo "Working..."
runFunc generateCode
runFunc testGoLangCILint
runFunc testGoTest
if [ -z ${TRAVIS+x} ]; then
echo Not on travis, so running the notravis tests
runFunc testNoTravisGoTest
fi
PATH=$HOME/go/bin:$PATH
runFunc testExamples
if [ $rc -eq 0 ]; then
echo "SUCCESS!"

39
docs/FAQ.md Normal file
View File

@@ -0,0 +1,39 @@
# FAQ
## security: file 'foo' is not in or below 'bar'
v2.0 added a security check that prevents
kustomizations from reading files outside their own
directory root.
This was meant to help protect the person inclined to
download kustomization directories from the web and use
them without inspection to control their production
cluster
(see [#693](https://github.com/kubernetes-sigs/kustomize/issues/693),
[#700](https://github.com/kubernetes-sigs/kustomize/pull/700),
[#995](https://github.com/kubernetes-sigs/kustomize/pull/995) and
[#998](https://github.com/kubernetes-sigs/kustomize/pull/998))
Resources (including configmap and secret generators)
can _still be shared_ via the recommended best practice
of placing them in a directory with their own
kustomization file, and refering to this directory as a
[`base`](glossary.md#base) from any kustomization that
wants to use it. This encourages modularity and
relocatability.
At the moment (in v2.0.3), however, there's no
(released) analogous way to share patch files and other
transformer configuration data between kustomizations.
As a stop-gap until we add base-like behavior for
transformers, we've added a flag to disable the check:
```
kustomize build --load_restrictor none $target
```
This flag is not in v2.0.3, but is available from head
(`go install sigs.k8s.io/kustomize`).

View File

@@ -8,9 +8,20 @@ manager:
brew install kustomize
On windows, you can install kustomize with Chocolatey package
manager.
choco install kustomize
For support on the chocolatey package and prior releases, please reference the following links:
- [Choco Package](https://chocolatey.org/packages/kustomize)
- [Package Source](https://github.com/kenmaglio/choco-kustomize)
For all operating systems, download a binary from the
[release page].
Or try this to grab the latest official release
using the command line:

View File

@@ -1,23 +1,51 @@
# Kustomize docs
* [installation instructions](INSTALL.md)
* [kustomization.yaml](kustomization.yaml) - Example of a
[kustomization](glossary.md#kustomization)
English | [简体中文](zh/README.md)
# Documentation
* [Installation](INSTALL.md)
* [Examples](../examples) - detailed walkthroughs of various
workflows and concepts.
* [Glossary](glossary.md) - the word of the day is [_root_](glossary.md#kustomization-root).
* [kustomization.yaml](kustomization.yaml) - a
[kustomization](glossary.md#kustomization) file
with explanations of each field.
* [workflow](workflows.md) - Some steps one might take in using
bespoke and off-the-shelf configurations.
* [glossary](glossary.md) - An attempt to disambiguiate terminology.
* [eschewed features](eschewedFeatures.md) - Why certain features are (currently)
not supported in Kustomize.
* [Plugins](plugins.md) - extending kustomize with
custom generators and transformers.
* [contributing guidelines](../CONTRIBUTING.md) - Please read before sending a PR.
* [code of conduct](../code-of-conduct.md)
* [Workflows](workflows.md) - steps one might take in
using bespoke and off-the-shelf configurations.
* [FAQ](FAQ.md)
## Release notes
* [2.1](v_2.1.0.md) - Date TBD, target late May 2019
* [2.0](v_2.0.0.md) - Mar 2019.
kustomize [v2.0.3] is available in [kubectl v1.14][kubectl].
* [1.0](v_1.0.1.md) - May 2018. Initial release after development
in the [kubectl repository].
## Policies
* [Versioning](versioningPolicy.md) - how the code and
the kustomization file evolve in time.
* [Eschewed features](eschewedFeatures.md) - why certain features
are (currently) not supported in kustomize.
* [Contributing guidelines](../CONTRIBUTING.md) - please read
before sending a PR.
* [Code of conduct](../code-of-conduct.md)
[v2.0.3]: https://github.com/kubernetes-sigs/kustomize/releases/tag/v2.0.3
[kubectl]: https://kubernetes.io/blog/2019/03/25/kubernetes-1-14-release-announcement
[kubectl repository]: https://github.com/kubernetes/kubectl

46
docs/bugs.md Normal file
View File

@@ -0,0 +1,46 @@
# Filing bugs
[target package]: https://github.com/kubernetes-sigs/kustomize/tree/master/pkg/target
[example of a target test]: https://github.com/kubernetes-sigs/kustomize/blob/master/pkg/target/baseandoverlaysmall_test.go
File issues as desired, but
if you've found a problem with how
`kustomize build` works, consider the
following to improve response time.
## A good report specifies
* the output of `kustomize version`,
* the input (the content of `kustomization.yaml`
and any files it refers to),
* the expected YAML output.
## A great report is a bug reproduction test
kustomize has a simple test harness in the
[target package] for specifying a kustomization's
input and the expected output.
See this [example of a target test].
The pattern is
* call `NewKustTestHarness`
* specify kustomization input data (resources,
patches, etc.) as inline strings,
* call `makeKustTarget().MakeCustomizedResMap()`
* compare the actual output to expected output
In a bug reproduction test, the expected output
string initially contains the _wrong_ (unexpected)
output, thus unambiguously reproducing the bug.
Nearby comments should explain what the output
should be, and have a TODO pointing to the related
issue.
The person who fixes the bug then has a clear bug
reproduction and a test to modify when the bug is
fixed.
The bug reporter can then see the bug was fixed,
and has permanent regression coverage to prevent
its reintroduction.

View File

@@ -1,5 +1,10 @@
# Eschewed Features
The maintainers established this list to
place bounds on the kustomize feature
set. The bounds can be changed with
a consensus on the risks.
For a bigger picture about why kustomize
does some things and not others, see the
glossary entry for [DAM].
@@ -10,8 +15,8 @@ glossary entry for [DAM].
_compositions_ or _mixins_ - concepts that are widely accepted as
a best practice in various programming languages.
To this end, `kustomize` offers various _addition_ directives. One
can add labels, annotations, patches, resources and bases.
To this end, `kustomize` offers various _addition_ directives.
One may add labels, annotations, patches, resources, bases, etc.
Corresponding _removal_ directives are not offered.
Removal semantics would introduce many possibilities for
@@ -31,6 +36,48 @@ what you don't want and commit it to your private fork, then use
kustomize on your fork. As often as desired, use _git rebase_ to
capture improvements from the upstream base.
## Unstructured edits
_Structured edits_ are changes controlled by
knowledge of the k8s API, and YAML or JSON syntax.
Most edits performed by kustomize can be expressed as
[JSON patches] or [SMP patches]. Common edits, like
adding labels or adding a name prefix, get dedicated
shorthand commands. Another class of edits take
data from one specific object's field and use it in
another (e.g. a service object's name found and
copied into a container's command line).
These edits are designed to create valid output
given valid input, and can provide syntactically
and semantically informed error messages if inputs
are invalid.
_Unstructured edits_, e.g. a templating approach,
or a command to replace any target string in the
character stream with some other string, aren't
limited by any syntax or object structure.
Such powerful techniques are eschewed because
- There would be no way to say that a kustomization
was correct without running it and checking
the output.
- Errors in the output would be
disconnected from the edit that caused it.
- They are toil to maintain by a rotating
staff of operators.
Kustomizations are meant to be sharable and stackable.
Imagine tracing down a problem rooted in a
clever set of stacked regexp replacements
performed by various overlays on some remote base.
Other tools (sed, jinja, erb, envsubst, helm, ksonnet,
etc.) provide varying degrees of unstructured editting
and/or embedded languages, and can be used instead
of, or in a pipe with, kustomize.
## Build-time side effects from CLI args or env variables
`kustomize` supports the best practice of storing one's
@@ -39,7 +86,7 @@ entire configuration in a version control system.
Changing `kustomize build` configuration output as a result
of additional arguments or flags to `build`, or by
consulting shell environment variable values in `build`
code, would violate that goal.
code, would frustrate that goal.
`kustomize` insteads offers [kustomization] file `edit`
commands. Like any shell command, they can accept
@@ -49,7 +96,7 @@ For example, to set the tag used on an image to match an
environment variable, run
```
kustomize edit set imagetag nginx:$MY_NGINX_VERSION
kustomize edit set image nginx:$MY_NGINX_VERSION
```
as part of some encapsulating work flow executed before
@@ -74,12 +121,10 @@ commands that accept globbed arguments, expand them at _edit
time_ relative to the local file system, and store the resulting
explicit names into the kustomization file.
In this way the resources, patches and bases used at _build time_
remain explicitly declared in version control.
[DAM]: glossary.md#declarative-application-management
[base]: glossary.md#base
[DAM]: glossary.md#declarative-application-management
[java import]: https://www.codebyamir.com/blog/pitfalls-java-import-wildcards
[JSON patches]: glossary.md#patchjson6902
[kustomization]: glossary.md#kustomization
[OTS workflow]: workflows.md#off-the-shelf-configuration
[java import]: https://www.codebyamir.com/blog/pitfalls-java-import-wildcards
[SMP patches]: glossary.md#patchstrategicmerge

View File

@@ -5,6 +5,7 @@
[Declarative Application Management]: https://github.com/kubernetes/community/blob/master/contributors/design-proposals/architecture/declarative-application-management.md
[JSON]: https://www.json.org/
[JSONPatch]: https://tools.ietf.org/html/rfc6902
[JSONMergePatch]: https://tools.ietf.org/html/rfc7386
[Resource]: #resource
[YAML]: http://www.yaml.org/start.html
[application]: #application
@@ -30,8 +31,9 @@
[rebase]: https://git-scm.com/docs/git-rebase
[resource]: #resource
[resources]: #resource
[root]: #kustomization-root
[rpm]: https://en.wikipedia.org/wiki/Rpm_(software)
[strategic-merge]: https://github.com/kubernetes/community/blob/master/contributors/devel/strategic-merge-patch.md
[strategic-merge]: https://git.k8s.io/community/contributors/devel/sig-api-machinery/strategic-merge-patch.md
[target]: #target
[variant]: #variant
[variants]: #variant
@@ -73,10 +75,10 @@ management in k8s.
## base
A _base_ is a [target] that some [overlay] modifies.
A _base_ is a [kustomization] that some [overlay] modifies.
Any target, including an [overlay], can be a base to
another target.
Any kustomization, including an [overlay], can be a base to
another kustomization.
A base has no knowledge of the overlays that refer to it.
@@ -141,31 +143,105 @@ test or deploy) when that truth changes.
## kustomization
A _kustomization_ is a file called `kustomization.yaml` that
describes a configuration consumable by [kustomize].
The term _kustomization_ refers to a
`kustomization.yaml` file, or more generally to a
directory (the [root]) containing the
`kustomization.yaml` file and all the relative file
paths that it immediately references (all the local
data that doesn't require a URL specification).
I.e. if someone gives you a _kustomization_ for use
with [kustomize], it could be in the form of
* one file called `kustomization.yaml`,
* a tarball (containing that YAML file plus what it references),
* a git archive (ditto),
* a URL to a git repo (ditto), etc.
Here's an [example](kustomization.yaml) `kustomization.yaml`.
A kustomization file contains fields falling into four
categories:
* _resources_ - what existing [resources] are to be customized.
Example fields: _resources_, _crds_.
* _generators_ - what _new_ resources should be created.
Example fields: _configMapGenerator_ (legacy),
_secretGenerator_ (legacy), _generators_ (v2.1).
* _transformers_ - what to _do_ to the aforementioned resources.
Example fields: _namePrefix_, _nameSuffix_, _images_,
_commonLabels_, _patchesJson6902_, etc. and the more
general _transformers_ (v2.1) field.
* _meta_ - fields which may influence all or some of
the above. Example fields: _vars_, _namespace_,
_apiVersion_, _kind_, etc.
Here's an [example](kustomization.yaml).
## kustomization root
A kustomization contains fields falling into these categories:
The directory that immediately contains a
`kustomization.yaml` file.
* _Customization operators_ for modifying operands, e.g.
_namePrefix_, _nameSuffix_, _commonLabels_, _patches_, etc.
When a kustomization file is processed, it may or may
not be able to access files outside its root.
* _Customization operands_:
* [resources] - completely specified k8s API objects,
e.g. `deployment.yaml`, `configmap.yaml`, etc.
* [bases] - paths or github URLs specifying directories
containing a [kustomization]. These bases may
be subjected to more customization, or merely
included in the output.
* [CRD]s - custom resource definition files, to allow use
of _custom_ resources in the _resources_ list.
Not an actual operand - but allows the use of new operands.
Data files like resource YAML files, or text files
containing _name=value_ pairs intended for a ConfigMap
or Secret, or files representing a patch to be used in
a patch transformation, must live _within or below_ the
root, and as such are specified via _relative
paths_ exclusively.
* Generators, for creating more resources
(configmaps and secrets) which can then be
customized.
A special flag (in v2.1), `--load_restrictions none`,
is provided to relax this security feature, to, say,
allow a patch file to be shared by more than one
kustomization.
Other kustomizations (other directories containing a
`kustomization.yaml` file) may be referred to by URL, by
absolute path, or by relative path.
If kustomization __A__ depends on kustomization __B__, then
* __B__ may not _contain_ __A__.
* __B__ may not _depend on_ __A__, even transitively.
__A__ may contain __B__, but in this case it might be
simplest to have __A__ directly depend on __B__'s
resources and eliminate __B__'s kustomization.yaml file
(i.e. absorb __B__ into __A__).
Conventionally, __B__ is in a directory that's sibling
to __A__, or __B__ is off in a completely independent
git repository, referencable from any kustomization.
A common layout is
> ```
> ├── base
> │   ├── deployment.yaml
> │   ├── kustomization.yaml
> │   └── service.yaml
> └── overlays
> ├── dev
> │   ├── kustomization.yaml
> │   └── patch.yaml
> ├── prod
> │   ├── kustomization.yaml
> │   └── patch.yaml
> └── staging
> ├── kustomization.yaml
> └── patch.yaml
> ```
The three roots `dev`, `prod` and `staging`
(presumably) all refer to the `base` root. One would
have to inspect the `kustomization.yaml` files to be
sure.
## kubernetes
@@ -188,14 +264,14 @@ more than one version).
## kustomize
_kustomize_ is a command line tool supporting template-free
customization of declarative configuration targetted to
k8s-style objects.
_kustomize_ is a command line tool supporting
template-free, structured customization of declarative
configuration targetted to k8s-style objects.
_Targetted to k8s means_ that kustomize may need some
limited understanding of API resources, k8s concepts
like names, labels, namespaces, etc. and the semantics
of resource patching.
_Targetted to k8s means_ that kustomize has some
understanding of API resources, k8s concepts like
names, labels, namespaces, etc. and the semantics of
resource patching.
kustomize is an implementation of [DAM].
@@ -225,8 +301,8 @@ own [overlays] to do further customization.
## overlay
An _overlay_ is a [target] that modifies (and thus
depends on) another target.
An _overlay_ is a kustomization that modifies (and thus
depends on) another kustomization.
The [kustomization] in an overlay refers to (via file
path, URI or other method) some other kustomization,
@@ -244,7 +320,7 @@ _production_ environment variants.
These variants use the same overall resources, and vary
in relatively simple ways, e.g. the number of replicas
in a deployment, the CPU to a particular pod, the data
source used in a configmap, etc.
source used in a ConfigMap, etc.
One configures a cluster like this:
@@ -259,6 +335,7 @@ One configures a cluster like this:
Usage of the base is implicit - the overlay's
kustomization points to the base.
See also [root].
## package
@@ -289,15 +366,18 @@ value, e.g. an image tag.
By default, an SMP _replaces_ values. This
usually desired when the target value is a simple
string, but may not be desired when the target
string, but may not be desired when the target
value is a list.
To change this
default behavior, add a _directive_. Recognized
To change this
default behavior, add a _directive_. Recognized
directives include _replace_ (the default), _merge_
(avoid replacing a list), _delete_ and a few more
(see [these notes][strategic-merge]).
Note that for custom resources, SMPs are treated as
[json merge patches][JSONMergePatch].
Fun fact - any resource file can be used as
an SMP, overwriting matching fields in another
resource with the same group/version/kind/name,
@@ -314,6 +394,14 @@ A _patchJson6902_ can do almost everything a
_patchStrategicMerge_ can do, but with a briefer
syntax. See this [example][patchExampleJson6902].
## plugin
A chunk of code used by kustomize, but not necessarily
compiled into kustomize, to generate and/or transform a
kubernetes resource as part of a kustomization.
Details [here](plugins.md).
## resource
A _resource_ in the context of a REST-ful API is the
@@ -321,15 +409,19 @@ target object of an HTTP operation like _GET_, _PUT_ or
_POST_. k8s offers a REST-ful API surface to interact
with clients.
A _resource_, in the context of kustomization file,
is a path to a [YAML] or [JSON] file describing
a k8s API object, like a Deployment or a
ConfigmMap.
A _resource_, in the context of a kustomization, is a
[root] relative path to a [YAML] or [JSON] file
describing a k8s API object, like a Deployment or a
ConfigMap, or it's a path to a kustomization, or a URL
that resolves to a kustomization.
More generally, a resource can be any correct YAML file
that [defines an object](https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/#required-fields)
with a _kind_ and a _metadata/name_ field.
## root
See [kustomization root][root].
## sub-target / sub-application / sub-package
@@ -344,14 +436,13 @@ The _target_ is the argument to `kustomize build`, e.g.:
> kustomize build $target
> ```
`$target` must be a path or a url to a directory that
immediately contains a [kustomization].
`$target` must be a path or a url to a [kustomization].
The target contains, or refers to, all the information
needed to create customized resources to send to the
[apply] operation.
A target is a [base] or an [overlay].
A target can be a [base] or an [overlay].
## variant

187
docs/inventory_object.md Normal file
View File

@@ -0,0 +1,187 @@
# inventory directive in kustomization.yaml
New in v2.1.0, a kustomization file may have an `inventory` field:
```yaml
inventory:
type: ConfigMap
configMap:
name: prune-cm-name
namespace: some-namespace
```
### Motivation
If present, `Kustomize build` will make an _inventory_ object,
which could be a ConfigMap, or an App(to be added),
which can be consumed by a client such as those under development in
[cli-experimental](https://github.com/kubernetes-sigs/cli-experimental).
The client can recognize this object by name and use it to do a better job
with actions like `apply`, `prune` and `delete`.
### Implementation
The _inventory_ ConfigMap contains two special annotations:
- kustomize.config.k8s.io/Inventory
The value of this annotation is the JSON blob
for an Inventory object. The Inventory is a
struct that contains following information
- all objects within this kusotmization target
- all objects that reference within this kustomization target
Here is an example of an Inventory object
```json
{
"current":
{
"apps_v1_Deployment|default|mysql":null,
"~G_v1_Secret|default|pass-dfg7h97cf6":
[
{
"group":"apps",
"version":"v1",
"kind":"Deployment",
"name":"mysql",
"namespace":"default"
}
],
"~G_v1_Service|default|mysql":null
}
}
```
- kustomize.config.k8s.io/InventoryHash
The value of this annotation is a hash that is
computed from the list of items in the Inventory
Basically, this inventory object acts a record of objects that are applied as a group.
This object can be consumed by a client such as
[cli-experimental](https://github.com/kubernetes-sigs/cli-experimental).
The client can recognize the inventory annotations and take proper actions
when running apply, prune and delete.
### Example
Take following `kustomization.yaml` as an example
```yaml
resources:
- deployment.yaml
- service.yaml
secretGenerator:
- name: pass
literals:
- password=secret
inventory:
type: ConfigMap
configMap:
name: root-cm
namespace: default
namespace: default
```
where the `deployment.yaml` is
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
labels:
app: mysql
spec:
revisionHistoryLimit: 2
selector:
matchLabels:
app: mysql
strategy:
type: Recreate
template:
metadata:
labels:
app: mysql
spec:
containers:
- image: mysql:5.6
name: mysql
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: pass
key: password
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: mysql-persistent-storage
mountPath: /var/lib/mysql
volumes:
- name: mysql-persistent-storage
emptyDir: {}
```
and the `service.yaml` is
```yaml
apiVersion: v1
kind: Service
metadata:
name: mysql
labels:
app: mysql
spec:
ports:
- port: 3306
selector:
app: mysql
```
Running `kustomize build` gives 4 objects.
Besides the Deployment `mysql`, the Service `mysql`,
and the Secret `pass`, the output also contains a
ConfigMap object as
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
annotations:
kustomize.config.k8s.io/Inventory: '{"current":{"apps_v1_Deployment|default|mysql":null,"~G_v1_Secret|default|pass-dfg7h97cf6":[{"group":"apps","version":"v1","kind":"Deployment","name":"mysql","namespace":"default"}],"~G_v1_Service|default|mysql":null}}'
kustomize.config.k8s.io/InventoryHash: 7mgt867b75
name: haha
namespace: default
```
It is clear that this ConfigMap contains an `Inventory` annotation.
### Hash
Note that in the ConfigMap generated from `inventory` field, there is a hash
`b965tb9c7d`. It is the value for annotation `kustomize.config.k8s.io/InventoryHash`.
This hash is computed by hashing all the keys in data field, which is the following list
in this example.
```yaml
apps_v1_Deployment|default|mysql
~G_v1_Secret|default|pass-dfg7h97cf6
~G_v1_Service|default|mysql
```
When any object is added or removed from the kustomzation target, the hash changes. Thus by simply comparing the hash in the inventory objects, one can determine if the list of objects has changed.
### How prune works
In [cli-experimental](https://github.com/kubernetes-sigs/cli-experimental), there are different subcommands, `apply` and `prune`. Both are able to recognize an _inventory_ object and looking for its existing object on the cluster.
the `apply` command
recognizes the _inventory_ object by the annotation `kustomize.config.k8s.io/InventoryHash`. It then compares the current hash with the hash for the same object in the cluster. Since the hash reflects if there is any object added or removed, `apply` takes different actions correspondingly.
- When there is no existing _inventory_ object in the cluster, apply creates the inventory object.
- When the current hash is the same as the one in cluster, apply doesn't change the existing object in the cluster.
- when the current hash is different, apply merges the inventory annotation of the existing object in the cluster and the incoming object. The hash is updated to the latest hash.
The `prune` command parses the value of `kustomize.config.k8s.io/Inventory` of the existing _inventory_ object and computes two sets of objects based on the parsed data.
To be simple,
- The items in `Inventory.Current` will be kept
- The items in `Inventory.Previous` will be pruned when they
are not needed.

View File

@@ -28,9 +28,12 @@
# don't exist.
#
# In practice, fields with no value should simply be
# omitted from kustomize.yaml to reduce the content
# omitted from kustomization.yaml to reduce the content
# visible in configuration reviews.
# ----------------------------------------------------
# apiVersion and kind of Kustomization
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
# Adds namespace to all resources.
namespace: my-namespace
@@ -66,13 +69,16 @@ commonAnnotations:
# markers ("---").
resources:
- some-service.yaml
- ../some-dir/some-deployment.yaml
- sub-dir/some-deployment.yaml
# Each entry in this list results in the creation of
# one ConfigMap resource (it's a generator of n maps).
# The example below creates two ConfigMaps. One with the
# names and contents of the given files, the other with
# key/value as data.
# Each configMapGenerator item accepts a parameter of
# behavior: [create|replace|merge]. This allows an overlay to modify or
# replace an existing configMap from the parent.
configMapGenerator:
- name: myJavaServerProps
files:
@@ -85,35 +91,23 @@ configMapGenerator:
# Each entry in this list results in the creation of
# one Secret resource (it's a generator of n secrets).
# A command can do anything to get a secret,
# e.g. prompt the user directly, start a webserver to
# initate an oauth dance, etc.
secretGenerator:
- name: app-tls
commands:
tls.crt: "cat secret/tls.cert"
tls.key: "cat secret/tls.key"
files:
- secret/tls.cert
- secret/tls.key
type: "kubernetes.io/tls"
- name: app-tls-namespaced
# you can define a namespace to generate secret in, defaults to: "default"
namspace: apps
commands:
tls.crt: "cat secret/tls.cert"
tls.key: "cat secret/tls.key"
namespace: apps
files:
- tls.crt=catsecret/tls.cert
- tls.key=secret/tls.key
type: "kubernetes.io/tls"
- name: downloaded_secret
# timeoutSeconds specifies the number of seconds to
# wait for the commands below. It defaults to 5 seconds.
timeoutSeconds: 30
commands:
username: "curl -s https://path/to/secrets/username.yaml"
password: "curl -s https://path/to/secrets/password.yaml"
type: Opaque
- name: env_file_secret
# envCommand is similar to command but outputs lines of key=val pairs
# i.e. a Docker .env file or a .ini file.
# you can only specify one envCommand per secret.
envCommand: printf \"DB_USERNAME=admin\nDB_PASSWORD=somepw\"
# paths to files with k=v pairs, one pair per line.
envs:
- env.txt
type: Opaque
# generatorOptions modify behavior of all ConfigMap and Secret generators
@@ -124,11 +118,6 @@ generatorOptions:
# annotations to add to all generated resources
annotations:
kustomize.generated.resource: somevalue
# timeoutSeconds specifies the timeout for commands
timeoutSeconds: 30
# shell and arguments to use as a context for commands used in resource
# generation. Default at time of writing: ["sh", "-c"]
shell: ["sh", "-c"]
# disableNameSuffixHash is true disables the default behavior of adding a
# suffix to the names of generated resources that is a hash of
# the resource contents.
@@ -153,9 +142,9 @@ generatorOptions:
# etc. that differ from the common base).
bases:
- ../../base
- github.com/kubernetes-sigs/kustomize//examples/multibases?ref=v1.0.6
- github.com/kubernetes-sigs/kustomize/examples/multibases?ref=v1.0.6
- github.com/Liujingfang1/mysql
- github.com/Liujingfang1/kustomize//examples/helloWorld?ref=test-branch
- github.com/Liujingfang1/kustomize/examples/helloWorld?ref=test-branch
# Each entry in this list should resolve to
# a partial or complete resource definition file.
@@ -170,7 +159,7 @@ bases:
# a memory request/limit, change an env var in a
# ConfigMap, etc. Small patches are easy to review and
# easy to mix together in overlays.
patches:
patchesStrategicMerge:
- service_port_8888.yaml
- deployment_increase_replicas.yaml
- deployment_increase_memory.yaml
@@ -212,7 +201,7 @@ patchesJson6902:
path: add_service_annotation.yaml
# Each entry in this list should be a relative path to
# a file for custom resource definition(CRD).
# a file for custom resource definition(CRD) in openAPI definition.
#
# The presence of this field is to allow kustomize be
# aware of CRDs and apply proper
@@ -222,15 +211,25 @@ patchesJson6902:
# In kustomization, the ConfigMap object name may change by adding namePrefix, nameSuffix, or hashing
# The name reference for this ConfigMap object in CRD object need to be
# updated with namePrefix, nameSuffix, or hashing in the same way.
#
# The annotations can be put into openAPI definitions are:
# "x-kubernetes-annotation": ""
# "x-kubernetes-label-selector": ""
# "x-kubernetes-identity": ""
# "x-kubernetes-object-ref-api-version": "v1",
# "x-kubernetes-object-ref-kind": "Secret",
# "x-kubernetes-object-ref-name-key": "name",
crds:
- crds/typeA.yaml
- crds/typeB.yaml
- crds/typeA.json
- crds/typeB.json
# Vars are used to insert values from resources that cannot be referenced
# otherwise. For example if you need to pass a Service's name to the arguments
# or environment variables of a program but without hard coding the actual name
# of the Service you'd insert `$(MY_SERVICE_NAME)` into the value field of the
# env var or into the command or args of the container as shown here:
# Vars are used to capture text from one resource's field
# and insert that text elsewhere.
#
# For example, suppose someone specifies the name of a k8s Service
# object in a container's command line, and the name of a
# k8s Secret object in a container's environment variable,
# so that the following would work:
# ```
# containers:
# - image: myimage
@@ -240,19 +239,8 @@ crds:
# value: $(SOME_SECRET_NAME)
# ```
#
# Then you'll add an entry to `vars:` like shown below with the same name
# and a reference to the resource from which to pull the field's value.
# The actual field's path is optional and by default it will use
# `metadata.name`. Currently only string type fields are supported, no integers
# or booleans, etc. Also array access is currently not possible. For example getting
# the image field of container number 2 inside of a pod can currently not be done.
# To do so, add an entry to `vars:` as follows:
#
# Not every location of a variable is supported. To see a complete list of locations
# see the file [varreference.go](https://github.com/kubernetes-sigs/kustomize/blob/master/pkg/transformers/config/defaultconfig/varreference.go#L20).
#
# An example of a situation where you'd not use vars is when you'd like to set a
# pod's `serviceAccountName`. In that case you would just reference the ServiceAccount
# by name and Kustomize will resolve it to the eventual name while building the manifests.
vars:
- name: SOME_SECRET_NAME
objref:
@@ -273,23 +261,64 @@ vars:
apiVersion: apps/v1
fieldref:
fieldpath: spec.template.spec.restartPolicy
#
# A var is a tuple of variable name, object reference and field
# reference within that object. That's where the text is found.
#
# The field reference is optional; it defaults to `metadata.name`,
# a normal default, since kustomize is used to generate or
# modify the names of resources.
#
# At time of writing, only string type fields are supported.
# No ints, bools, arrays etc. It's not possible to, say,
# extract the name of the image in container number 2 of
# some pod template.
#
# A variable reference, i.e. the string '$(FOO)', can only
# be placed in particular fields of particular objects as
# specified by kustomize's configuration data.
#
# The default config data for vars is at
# https://github.com/kubernetes-sigs/kustomize/blob/master/pkg/transformers/config/defaultconfig/varreference.go
# Long story short, the default targets are all
# container command args and env value fields.
#
# Vars should _not_ be used for inserting names in places
# where kustomize is already handling that job. E.g.,
# a Deployment may reference a ConfigMap by name, and
# if kustomize changes the name of a ConfigMap, it knows
# to change the name reference in the Deployment.
# ImageTags modify the tags for images without creating patches.
# E.g. Given this fragment of a Deployment:
# Images modify the name, tags and/or digest for images without creating patches.
# E.g. Given this kubernetes Deployment fragment:
# ```
# containers:
# - name: myapp
# image: mycontainerregistry/myimage:v0
# - name: mypostgresdb
# image: postgres:8
# - name: nginxapp
# image: nginx:1.7.9
# - name: myapp
# image: my-demo-app:latest
# - name: alpine-app
# image: alpine:3.7
#```
# one can change the tag of myimage to v1 and the tag of nginx to 1.8.0 with the following:
# one can change the `image` in the following ways:
#
# - `postgres:8` to `my-registry/my-postgres:v1`,
# - nginx tag `1.7.9` to `1.8.0`,
# - image name `my-demo-app` to `my-app`,
# - alpine's tag `3.7` to a digest value
#
# It also supports digests. If digest is present newTag is ignored.
imageTags:
- name: mycontainerregistry/myimage
# all with the following *kustomization*:
images:
- name: postgres
newName: my-registry/my-postgres
newTag: v1
- name: nginx
newTag: 1.8.0
- name: my-demo-app
newName: my-app
- name: alpine
digest: sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3

348
docs/plugins.md Normal file
View File

@@ -0,0 +1,348 @@
# kustomize plugins
Kustomize offers a plugin framework allowing
people to write their own resource _generators_
and _transformers_.
[generator options]: ../examples/generatorOptions.md
[transformer configs]: ../examples/transformerconfigs
Write a plugin when changing [generator options]
or [transformer configs] doesn't meet your needs.
[12-factor]: https://12factor.net
* A _generator_ plugin could be a helm chart
inflator, or a plugin that emits all the
components (deployment, service, scaler,
ingress, etc.) needed by someone's [12-factor]
application, based on a smaller number of free
variables.
* A _transformer_ plugin might perform special
container command line edits, or any other
transformation that exceeds the power of the
builtin transformations (`namePrefix`,
`commonLabels`, etc.).
## Specification in `kustomization.yaml`
Start by adding a `generators` and/or `transformers`
field to your kustomization.
Each field accepts a string list:
> ```
> generators:
> - relative/path/to/some/file.yaml
> - relative/path/to/some/kustomization
> - /absolute/path/to/some/kustomization
> - https://github.com/org/repo/some/kustomization
>
> transformers:
> - {as above}
> ```
This is exactly like the syntax of the `resources`
field.
The value of each entry in a `resources`,
`generators` or `transformers` list must be a
relative path to a YAML file, or a path or URL
to a [kustomization].
[kustomization]: glossary.md#kustomization
In the former case the YAML is read from disk directly,
and in the latter case a kustomization is performed,
and its YAML output is merged with the YAML read
directly from files. The net result in all three cases
is a set of YAML objects.
Each object resulting from a `generators` or
`transformers` field is now further interpreted by
kustomize as a _plugin configuration_ object.
## Configuration
A kustomization file could have the following lines:
```
generators:
- chartInflator.yaml
```
Given this, the kustomization process would expect to
find a file called `chartInflator.yaml` in the
kustomization [root](glossary.md#kustomization-root).
This is the _plugin's configuration file_.
The file `chartInflator.yaml` could contain:
```
apiVersion: someteam.example.com/v1
kind: ChartInflator
metadata:
name: notImportantHere
chartName: minecraft
```
__The `apiVersion` and `kind` fields are
used to locate the plugin.__
[k8s object]: glossary.md#kubernetes-style-object
> Thus, these fields are required. They are also
> required because a kustomize plugin
> configuration object is also a [k8s object].
To get the plugin ready to generator or transform,
it is given the entire contents of the
configuration file.
[NameTransformer]: ../plugin/builtin/nametransformer/NameTransformer_test.go
[ChartInflator]: ../plugin/someteam.example.com/v1/chartinflator/ChartInflator_test.go
[plugins]: ../plugin/builtin
For more examples of plugin configuration YAML,
browse the unit tests below the [plugins] root,
e.g. the tests for [ChartInflator] or
[NameTransformer].
## Placement
Each plugin gets its own dedicated directory named
```
$XDG_CONFIG_HOME/kustomize/plugin
/${apiVersion}/LOWERCASE(${kind})
```
The default value of `XDG_CONFIG_HOME` is
`$HOME/.config`.
The one-plugin-per-directory requirement eases
creation of a plugin tarball (source, test, plugin
data files, etc.) for sharing.
In the case of a [Go plugin](#go-plugins), it also
allows one to provide a `go.mod` file for the
single plugin, easing resolution of package
version dependency skew.
When loading, kustomize will first look for an
_executable_ file called
```
$XDG_CONFIG_HOME/kustomize/plugin
/${apiVersion}/LOWERCASE(${kind})/${kind}
```
If this file is not found or is not executable,
kustomize will look for a file called `${kind}.so`
in the same directory and attempt to load it as a
[Go plugin](#go-plugins).
If both checks fail, the plugin load fails the overall
`kustomize build`.
## Execution
Plugins are only used during a run of the
`kustomize build` command.
Generator plugins are run after processing the
`resources` field (which itself is in some sense a
generator in that it emits resources for further
processing).
The full set of resources is then passed into the
transformation pipeline, wherein builtin
transformations like `namePrefix` and
`commonLabel` are applied (if they were specified
in the kustomization file), followed by the
user-specified transformers in the `transformers`
field.
The specified order of transformers in the
`transformers` field should be respected, as
transformers cannot be expected to be commutative.
A `kustomize build` that tries to use plugins but
omits the flag
> `--enable_alpha_plugins`
will fail with a warning about plugin use.
Flag use is an opt-in acknowledging the absence of
plugin provenance. It's meant to give pause to
someone who blindly downloads a kustomization from
the internet and attempts to run it, without
realizing that it might attempt to run 3rd party
code in plugin form. The plugin would have to be
installed already, but nevertheless the flag is a
reminder.
## Writing plugins
### Exec plugins
A _exec plugin_ is any executable that accepts a
single argument on its command line - the name of
a YAML file containing its configuration (the file name
provided in the kustomization file).
> TODO: more restrictions on plugin to allow the same exec
> plugin to be specified in a config under both the
> `generators` and `transformers` fields.
> - first arg could be the fixed string
> `generate` or `transform`,
> (the name of the configuration file moves to
> the 2nd arg), or
> - by default an exec plugin behaves as a tranformer
> unless a flag `-g` is provided, switching the
> exec plugin to behave as a generator.
[helm chart inflator]: ../plugin/someteam.example.com/v1/chartinflator
[bashed config map]: ../plugin/someteam.example.com/v1/bashedconfigmap
[sed transformer]: ../plugin/someteam.example.com/v1/sedtransformer
#### Examples
* [helm chart inflator] - A generator that inflates a helm chart.
* [bashed config map] - Super simple configMap generation from bash.
* [sed transformer] - Define your unstructured edits using a
plugin like this one.
A generator plugin accepts nothing on `stdin`, but emits
generated resources to `stdout`.
A transformer plugin accepts resource YAML on `stdin`,
and emits those resources, presumably transformed, to
`stdout`.
kustomize uses an exec plugin adapter to provide
marshalled resources on `stdin` and capture
`stdout` for further processing.
### Go plugins
[Go plugin]: https://golang.org/pkg/plugin/
A [Go plugin] for kustomize looks like this:
> ```
> package main
>
> import (
> "sigs.k8s.io/kustomize/pkg/ifc"
> "sigs.k8s.io/kustomize/pkg/resmap"
> ...
> )
>
> type plugin struct {...}
>
> var KustomizePlugin plugin
>
> func (p *plugin) Config(
> ldr ifc.Loader,
> rf *resmap.Factory,
> c []byte) error {...}
>
> func (p *plugin) Generate() (resmap.ResMap, error) {...}
>
> func (p *plugin) Transform(m resmap.ResMap) error {...}
> ```
Use of the identifiers `plugin`, `KustomizePlugin`
and implementation of the method signature
`Config` is required.
Implementing the `Generator` or `Transformer`
method allows (respectively) the plugin's config
file to be added to the `generators` or
`transformers` field in the kustomization file.
Do one or the other or both as desired.
[secret generator]: ../plugin/someteam.example.com/v1/secretsfromdatabase
[service generator]: ../plugin/someteam.example.com/v1/someservicegenerator
[string prefixer]: ../plugin/someteam.example.com/v1/stringprefixer
[date prefixer]: ../plugin/someteam.example.com/v1/dateprefixer
#### Examples
* [secret generator] - Generate secrets from a database.
* [service generator] - Generate a service from a name and port argument.
* [string prefixer] - uses the value in `metadata/name` as the prefix.
This particular example exists to show how a plugin can
transform the behavior of a plugin. See the
`TestTransformedTransformers` test in the `target` package.
* [date prefixer] - prefix the current date to resource names, a simple
example used to modify the string prefixer plugin just mentioned.
* All the builtin plugins [here](../plugin/builtin).
User authored plugins are
on the same footing as builtin operations.
A plugin can be both a generator and a
transformer. The `Generate` method will run along
with all the other generators before the
`Transform` method runs.
Here's a build command that sensibly assumes the
plugin source code sits in the directory where
kustomize expects to find `.so` files:
```
d=$XDG_CONFIG_HOME/kustomize/plugin\
/${apiVersion}/LOWERCASE(${kind})
go build -buildmode plugin \
-o $d/${kind}.so $d/${kind}.go
```
#### Caveats
Go plugins allow kustomize extensions that run
without the cost marshalling/unmarshalling all
resource data to/from a subprocess for each plugin
run.
[ELF]: https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
Go plugins work as [defined][Go plugin], but fall
short of common notions associated with the word
_plugin_. Go plugin compilation creates an [ELF]
formatted `.so` file, which by definition has no
information about the provenance of the object
code. Skew between the compilation conditions
(versions of package dependencies, `GOOS`,
`GOARCH`) of the main program ELF and the plugin
ELF will cause plugin load failure.
Exec plugins also lack provenance, but won't
complain about compilation skew.
In either case, a sensible way to share a plugin
is as a tar file of source code, tests and
associated data, unpackable under
`$XDG_CONFIG_HOME/kustomize/plugin` (exactly where
one would develop a plugin).
[Go modules]: https://github.com/golang/go/wiki/Modules
In the case of a Go plugin, an end user accepting
a shared plugin must compile both kustomize and
the plugin. Tooling could be built to make Go
_plugin sharing_ easier, but this requires some
critical mass of _plugin authoring_, which in turn
is hampered by confusion around sharing.
[Go modules], once they are more widely adopted,
will solve one of the biggest plugin sharing
difficulties - ambiguous plugin vs host
dependencies.

18
docs/v_1.0.1.md Normal file
View File

@@ -0,0 +1,18 @@
# kustomize 1.0.1
Initial release after move from
[github.com/kubernetes/kubectl]
to [github.com/kubernetes-sigs/kustomize].
History
* May 2018: v1.0 after move to [github.com/kubernetes-sigs/kubectl]
from [github.com/kubernetes/kubectl].
Has kustomization file, bases, overlays, basic transforms.
* Apr 2018: s/kinflate/kustomize/, s/manifest/kustomization/
* Oct 2017: s/kexpand/kinflate/
* Sep 2017: kexpand [starts](https://github.com/kubernetes/kubectl/pull/65)
in [github.com/kubernetes/kubectl]
* Aug 2018: [DAM] authored by Brian Grant
[DAM]: https://docs.google.com/document/d/1cLPGweVEYrVqQvBLJg6sxV-TrE5Rm2MNOBA_cxZP2WU

131
docs/v_2.0.0.md Normal file
View File

@@ -0,0 +1,131 @@
# kustomize 2.0.0
[security concern]: https://docs.google.com/document/d/1FYgLVdq-siB_Cef9yuQBmit0PbrE8lsyTBdGI2eA2y8/edit
After security review, a field used in secret
generation (see below) was removed from the
definition of a kustomization file with no
mechanism to convert it to a new form. Also, the
set of files accessible from a kustomization file
has been further constrained.
Per the [versioning policy](versioningPolicy.md),
backward incompatible changes trigger an increment
of the major version number, hence we go
from 1.0.11 to 2.0.0. We're taking this major
version increment opportunity to remove some
already deprecated fields, and the code paths
associated with them.
## Backward Incompatible Changes
### Kustomization Path Constraints
A kustomization file can specify paths to other
files, including resources, patches, configmap
generation data, secret generation data and
bases. In the case of a base, the path can be a
git URL instead.
In 1.x, these paths had to be relative to the
current kustomization directory (the location of
the kustomization file used in the `build`
command).
In 2.0, bases can continue to specify, via
relative paths, kustomizations outside the current
kustomization directory. But non-base paths are
constrained to terminate in or below the current
kustomization directory. Further, bases specified
via a git URL may not reference files outside of
the directory used to clone the repository.
### Kustomization Field Removals
#### patches
`patches` was deprecated and replaced by
`patchesStrategicMerge` when `patchesJson6902` was
introduced. In Kustomize 2.0.0, `patches` is
removed. Please use `patchesStrategicMerge`
instead.
#### imageTags
`imageTags` is replaced by `images` since `images`
can provide more features to change image names,
registries, tags and digests.
#### secretGenerator/commands
`commands` is removed from SecretGenerator due to
a [security concern]. One can use `files` or
`literals`, similar to ConfigMapGenerator, to
generate a secret.
```
secretGenerator:
- name: app-tls
files:
- secret/tls.cert
- secret/tls.key
type: "kubernetes.io/tls"
```
## Compatible Changes (New Features)
As this release is triggered by a security change,
there are no major new features to announce. A few
things that are worth mentioning in this release
are:
* More than _40_ issues closed since 1.0.11
release (including many extensions to
transformation rules).
* Users can run `kustomize edit fix` to migrate a
kustomization file working with previous
versions to one working with 2.0.0. For example,
a kustomization.yaml with following content
```
patches:
- deployment-patch.yaml
imageTags:
- name: postgres
newTag: v1
```
will be converted to
```
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
patchesStrategicMerge:
- deployment-patch.yaml
images:
- name: postgres
newTag: v1
```
* Kustomization filename
In previous versions, the name of a
kustomization file had to be
`kustomization.yaml`.
Kustomize allows `kustomization.yaml`,
`kustomization.yml` and
`Kustomization`. In a directory, only one of
those filenames is allowed. If there are more
than one found, Kustomize will exit with an
error. Please select the best filename for your
use cases.
* Cancelled plans to deprecate applying prefix/suffix to namespace.
The deprecation warning
```
Adding nameprefix and namesuffix to Namespace resource will be deprecated in next release.
```
was removed.

25
docs/v_2.1.0.md Normal file
View File

@@ -0,0 +1,25 @@
# kustomize 2.1.0
_WIP, release target date May 20 2019_
_TODO: provide details; this is just a high level list_
* The `inventory` field. See [inventory object](inventory_object.md).
* The `generators` and `transformers` fields.
See [plugins](plugins.md).
* The `--load_restrictions none` flag.
* Field changes / deprecations (use `kustomize fix`)
* The `resources` field generalized,
`bases` field deprecated.
* New `envs` sub-field introduced to both
`configMapGenerators` and `secretGenerators`,
replacing the deprecated `env` field
(allowing mixins of k=v pairs from different files).
* GO modules
* Last release with completely unsupported access to `pkg`.

219
docs/versioningPolicy.md Normal file
View File

@@ -0,0 +1,219 @@
# Versioning
Running `kustomize` means one is running a
particular version of a program, reading a
particular version of a [kustomization] file.
## Program Versioning
The command `kustomize version` prints a three
field version tag (e.g. `1.0.11`) that aspires to
[semantic versioning].
When enough changes have accumulated to
warrant a new release, a [release process]
is followed, and the fields in the version
number are bumped per semver.
## Kustomization File Versioning
At the time of writing (circa release of v2.0.0):
- A [kustomization] file is just a YAML file that
can be successfully parsed into a particular Go
struct defined in the `kustomize` binary.
- This struct does not have a version number,
which is the same as saying that its version
number matches the program's version number,
since it's compiled in.
### Field Change Policy
- A field's meaning cannot be changed.
- A field may be deprecated, then removed.
- Deprecation means triggering a _minor_ (semver)
version bump in the program, and
defining a migration path in a non-fatal
error message.
- Removal means triggering a _major_ (semver)
version bump, and fatal error if field encountered
(as with any unknown field).
### The `edit fix` Command
This `kustomize` command reads a Kustomization
file, converts deprecated fields to new
fields, and writes it out again in the latest
format.
This is a type version upgrade mechanism that
works within _major_ program revisions. There is
no downgrade capability, as there's no use case
for it (see discussion below).
### Examples
At the time of writing, in v1.0.x, there were 12
minor releases, with backward compatible
deprecations fixable via `edit fix`.
With the 2.0.0 release, there were three field
removals:
- `imageTag` was deprecated when `images` was
introduced, because the latter offers more
general features for image data manipulation.
`imageTag` was removed in v2.0.0.
- `patches` was deprecated and replaced by
`patchesStrategicMerge` when `patchesJson6902`
was introduced, to make a clearer
distinction between patch specification formats.
`patches` was removed in v2.0.0.
- `secretGenerator/commands` was removed
due to security concerns in v2.0.0
with no deprecation period.
The `edit fix` command in a v2.0.x binary
will no longer recognize these fields.
## Relationship to the k8s API
### Review of k8s API versioning
The k8s API has specific [conventions] and a
process for making [changes].
The presence of an `apiVersion` field in a k8s
native type signals:
- its reliability level (alpha vs beta vs
generally available),
- the existence of code to provide default values
to fields not present in a serialization,
- the existence of code to provide both forward
and backward conversion between different
versions of types.
The k8s API promises a lossless _conversion_
between versions over a specific range. This
means that a recent client can write an object
bearing the newest possible value for its version,
the server will accept it and store it in
"versionless" JSON form in storage, and can
convert it to a range of older versions should
an older client request data.
For native k8s types, this all requires writing Go
code in the kubernetes core repo, to provide
defaulting and conversions.
For CRDs, there's a [proposal] on how to manage
versioning (e.g. a remote service can offer type
defaulting and conversions).
### Kustomization file versioning
The critical difference between k8s API versioning
and kustomization file versioning is
- A k8s API server is able to go _forward_ and
_backward_ in versioning, to work with older
clients, over [some range].
- The `kustomize edit fix` command only moves
_forward_ within a _major_ program
version.
At the time of writing, the YAML in a
kustomization file does not represent a [k8s API]
object, and the kustomize command and associated
library is neither a server of, nor a client to,
the k8s API.
### Additional Kustomization file rules
In addition to the [field change policy] described
above, kustomization files conform to
the following rules.
#### Eschew classic k8s fields
Field names with dedicated meaning in k8s
(`metadata`, `spec`, `status`, etc.) aren't used.
This is enforced via code review.
#### Optional use of k8s `kind` and `apiVersion`
At the time of writing two [special] k8s
resource fields are allowed, but not required, in
a kustomization file: [`kind`] and [`apiVersion`].
If either field is present, they both must be, and
they must have the following values:
``` yaml
kind: Kustomization
apiVersion: kustomize.config.k8s.io/v1beta1
```
They are allowed to exist and have specific values
in a kustomization file only as a sort of
domain-squatting behavior for some future API. A
kustomize user gains nothing from adding these
fields to a kustomization file.
### Why not require `kind` and `apiVersion`
#### Ease of use and setting proper expectations
Use cases for a kustomization file don't include a
server storing muliple k8s kinds and offering
version downgrades.
The kustomization file is more akin to a
`Makefile`. A kustomize command can either read a
kustomization file, or it cannot, and in the later
case will complain as specifically as possible
about why (e.g. `unknown field Foo`).
So requiring a `kind` and `apiVersion` would just
be boilerplate in a user's files, and in all the
examples and tests.
Nevertheless, _a user still benefits from a
versioning policy_ and has a `fix` command to
upgrade files as needed.
#### We can change our minds
When/if the kustomization struct graduates to some
kind of API status, with an expectation of
"versionless" storage and downgrade capability,
whatever it looks like at that moment can be
locked into `/v1beta1` or `/v1` and the `kind`
and `apiVersion` fields can be required from that
moment forward.
[field change policy]: #field-change-policy
[some range]: https://kubernetes.io/docs/reference/using-api/deprecation-policy
[proposal]: https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/customresources-versioning.md
[beta-level rules]: https://github.com/kubernetes/community/blob/master/contributors/devel/api_changes.md#alpha-beta-and-stable-versions
[changes]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api_changes.md
[adapt]: https://github.com/kubernetes-sigs/kustomize/blob/master/pkg/types/kustomization.go#L166
[special]: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#resources
[k8s API]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md
[conventions]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md
[release process]: ../build/README.md
[kustomization]: glossary.md#kustomization
[`kind`]: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#types-kinds
[`apiVersion`]: https://kubernetes.io/docs/concepts/overview/kubernetes-api/#api-versioning
[semantic versioning]: https://semver.org

View File

@@ -13,6 +13,7 @@
[resources]: glossary.md#resource
[workflowBespoke]: workflowBespoke.jpg
[workflowOts]: workflowOts.jpg
[kubectl-v1.14.0]:https://kubernetes.io/blog/2019/03/25/kubernetes-1-14-release-announcement/
# workflows
@@ -71,6 +72,11 @@ Run kustomize, and pipe the output to [apply].
> kustomize build ~/ldap/overlays/production | kubectl apply -f -
> ```
You can also use [kubectl-v1.14.0] to apply your [variants].
> ```
> kubectl apply -k ~/ldap/overlays/staging
> kubectl apply -k ~/ldap/overlays/production
> ```
## Off-the-shelf configuration
@@ -120,6 +126,12 @@ distinct repository.
> kustomize build ~/ldap/overlays/production | kubectl apply -f -
> ```
You can also use [kubectl-v1.14.0] to apply your [variants].
> ```
> kubectl apply -k ~/ldap/overlays/staging
> kubectl apply -k ~/ldap/overlays/production
> ```
#### 5) (optionally) capture changes from upstream
The user can periodically [rebase] their [base] to

39
docs/zh/INSTALL.md Normal file
View File

@@ -0,0 +1,39 @@
[release 页面]: https://github.com/kubernetes-sigs/kustomize/releases
[Go]: https://golang.org
[golang.org]: https://golang.org
## 安装
在 macOS ,您可以使用软件包管理器 Homebrew 来安装 kustomize 。
brew install kustomize
在 windows ,您可以使用软件包管理器 Chocolatey 来安装 kustomize 。
choco install kustomize
有关软件包管理器 chocolatey 的使用以及对之前版本的支持,请参考以下链接:
- [Choco Package](https://chocolatey.org/packages/kustomize)
- [Package Source](https://github.com/kenmaglio/choco-kustomize)
对于其他系统,请在 [release 页面] 下载相应系统的二进制文件。
或者使用命令行获取最新的官方版本:
```
opsys=linux # or darwin, or windows
curl -s https://api.github.com/repos/kubernetes-sigs/kustomize/releases/latest |\
grep browser_download |\
grep $opsys |\
cut -d '"' -f 4 |\
xargs curl -O -L
mv kustomize_*_${opsys}_amd64 kustomize
chmod u+x kustomize
```
使用 [Go] v1.10.1 或更高版本安装(如果可以访问 [golang.org]
<!-- @installkustomize @test -->
```
go get sigs.k8s.io/kustomize
```

46
docs/zh/README.md Normal file
View File

@@ -0,0 +1,46 @@
[English](../README.md) | 简体中文
# 文档
* [安装说明](INSTALL.md)
* [示例](../../examples) - 各种使用流程和概念的详细演示。
* [术语表](../glossary.md) - 用于消除术语歧义。
* [kustomization.yaml](kustomization.yaml) - 包含
[kustomization](../glossary.md#kustomization) 所有字段的示例文件。
* [插件](../plugins.md) - 使用自定义的资源生成器和资源转换器来拓展 kustomize 功能。
* [工作流](workflows.md) - 使用定制及使用现成配置使用的一些步骤。
* [FAQ](../FAQ.md)
## 发行说明
* [2.1](../v_2.1.0.md) - 日期待定预计2019年5月下旬。
* [2.0](../v_2.0.0.md) - 2019年3月
可以在 [kubectl v1.14][kubectl] 中使用 kustomize [v2.0.3] 。
* [1.0](../v_1.0.1.md) - 2018年5月
于 [kubectl repository] 开发后的首发版本。
## 政策
* [版本控制](../versioningPolicy.md) - kustomize 代码及 kustomization 文件的版本控制策略。
* [规避功能](../eschewedFeatures.md) - 目前 Kustomize 不支持某些功能的原因。
* [贡献指南](../../CONTRIBUTING.md) - 请在提交 PR 之前阅读。
* [行为准则](../../code-of-conduct.md)
>声明:部分文档可能稍微滞后于英文版本,同步工作持续进行中
[v2.0.3]: https://github.com/kubernetes-sigs/kustomize/releases/tag/v2.0.3
[kubectl]: https://kubernetes.io/blog/2019/03/25/kubernetes-1-14-release-announcement
[kubectl repository]: https://github.com/kubernetes/kubectl

288
docs/zh/kustomization.yaml Normal file
View File

@@ -0,0 +1,288 @@
# Copyright 2018 The Kubernetes Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# ----------------------------------------------------
# Example kustomization.yaml content.
#
# This file declares the customization provided by
# the kustomize program.
#
# Since customization is, by definition, _custom_,
# there are no sensible default values for the fields
# in this file.
#
# The field values used below are merely examples, not
# to be copied literally. The values won't work if
# they happen to be references to external files that
# don't exist.
#
# In practice, fields with no value should simply be
# omitted from kustomization.yaml to reduce the content
# visible in configuration reviews.
# ----------------------------------------------------
# Kustomization 的 apiVersion 和 kind
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
# 为所有 resources 添加 namespace
namespace: my-namespace
# 该字段的值将添加在所有资源的名称之前
# 例如 将资源名称 “wordpress” 变为 “alices-wordpress”
namePrefix: alices-
# 该字段的值将添加在所有资源的名称后面
# 例如 将资源名称 “wordpress” 变为 “wordpress-v2”
# 如果资源类型为 ConfigMap 或 Secret ,则在哈希值之前附加后缀
nameSuffix: -v2
# 为所有资源和 selectors 增加 Labels
commonLabels:
someName: someValue
owner: alice
app: bingo
# 和 Labels 一样, 增加 Annotations
# 为 key:value 键值对
commonAnnotations:
oncallPager: 800-555-1212
# 此列表中的每条记录都必须是一个存在的 YAML 资源描述文件
# 一个 YAML 资源描述文件可以含有多个由(“---”)分隔的资源。
# kustomize 将读取这些YAML文件中的资源对其进行修改并
# 发布在 kustomize 的输出中。
resources:
- some-service.yaml
- sub-dir/some-deployment.yaml
# 列表中的每个条目都将创建一个 ConfigMap 它是n个 ConfigMap 的生成器)
# 下面的示例创建了两个 ConfigMaps
# 一个具有给定文件的名称和内容
# 另一个包含 key/value 键值对数据
# 每个 configMapGenerator 项都可以使用 [create | replace | merge] 参数
# 允许 overlay 从父级修改或替换现有的 configMap
configMapGenerator:
- name: myJavaServerProps
files:
- application.properties
- more.properties
- name: myJavaServerEnvVars
literals:
- JAVA_HOME=/opt/java/jdk
- JAVA_TOOL_OPTIONS=-agentlib:hprof
# 此列表中的每个条目都会导致创建一个Secret资源n个 secrets 的生成器)
secretGenerator:
- name: app-tls
files:
- secret/tls.cert
- secret/tls.key
type: "kubernetes.io/tls"
- name: app-tls-namespaced
# 你可以给生成的 secret 定义一个 namespace ,默认为 ”default“
namespace: apps
files:
- tls.crt=catsecret/tls.cert
- tls.key=secret/tls.key
type: "kubernetes.io/tls"
- name: env_file_secret
# 文件路径以 k=v 键值对的形式,每行一个键值对
envs:
- env.txt
type: Opaque
# generatorOptions 修改所有 ConfigMapGenerator 和 SecretGenerator 的行为
generatorOptions:
# 为所有生成的资源添加 labels
labels:
kustomize.generated.resources: somevalue
# 为所有生成的资源添加 annotations
annotations:
kustomize.generated.resource: somevalue
# disableNameSuffixHash 为 true 时将禁止默认的在名称后添加哈希值后缀的行为
disableNameSuffixHash: true
# 此列表中的每个条目都应解析为包含 kustomization 文件的目录,否则定制将失败
#
# 该条目可以是指向本地目录的相对路径
# 也可以是指向远程仓库中的目录的 URL
# URL 应该遵循 hashicorp/go-getter 中的 URL 格式
# https://github.com/hashicorp/go-getter#url-format
#
# 此字段的存在意味着此文件(您正在阅读的文件)是 _overlay_
# 它将进一步定制这些来自 _bases_ 文件中的配置
#
# 典型用例:开发,演示和生产环境
# 这些环境大部分相同但有些关键方式存在差异(镜像标签,一些服务器参数等,与公共 base 不同的配置)
bases:
- ../../base
- github.com/kubernetes-sigs/kustomize/examples/multibases?ref=v1.0.6
- github.com/Liujingfang1/mysql
- github.com/Liujingfang1/kustomize/examples/helloWorld?ref=test-branch
# 此列表中的每个条目都应可以解析为部分或完整的资源定义文件
#
# 这些(也可能是部分的)资源文件中的 name 必须与已经通过 `resources` 加载的 name 字段匹配
# 或者通过 `bases` 中的 name 字段匹配
# 这些条目将用于 _patch_修改已知资源
#
# 推荐使用小的 patches
# 例如:修改内存的 request/limit更改 ConfigMap 中的 env 变量等
# 小的 patches 易于维护和查看,并且易于在 overlays 中混合使用
patchesStrategicMerge:
- service_port_8888.yaml
- deployment_increase_replicas.yaml
- deployment_increase_memory.yaml
# patchesJson6902 列表中的每个条目都应可以解析为 kubernetes 对象和将应用于该对象的 JSON patch
# JSON patch 的文档地址https://tools.ietf.org/html/rfc6902
#
# 目标字段指向的 kubernetes 对象的 group、 version、 kind、 name 和 namespace 在同一 kustomization 内
# path 字段内容是 JSON patch 文件的相对路径
# patch 文件中的内容可以如下这种 JSON 格式:
#
# [
# {"op": "add", "path": "/some/new/path", "value": "value"},
# {"op": "replace", "path": "/some/existing/path", "value": "new value"}
# ]
#
# 也可以使用 YAML 格式表示:
#
# - op: add
# path: /some/new/path
# value: value
# - op:replace
# path: /some/existing/path
# value: new value
#
patchesJson6902:
- target:
version: v1
kind: Deployment
name: my-deployment
path: add_init_container.yaml
- target:
version: v1
kind: Service
name: my-service
path: add_service_annotation.yaml
# 此列表中的每个条目都应该是 openAPI 定义中自定义资源定义CRD文件的相对路径
#
# 该字段的存在是为了让 kustomize 知道用户自定义的 CRD
# 并对这些类型中的对象应用适当的转换
#
# 典型用例CRD 引用 ConfigMap 对象
# 在 kustomization 中ConfigMap 对象名称可能会通过 namePrefix 、nameSuffix 或 hashing 来更改 CRD 对象中此 ConfigMap 对象的名称
# 引用时需要以相同的方式使用 namePrefix 、 nameSuffix 或 hashing 来进行更新
#
# Annotations 可以放入 openAPI 的定义中:
# "x-kubernetes-annotation": ""
# "x-kubernetes-label-selector": ""
# "x-kubernetes-identity": ""
# "x-kubernetes-object-ref-api-version": "v1",
# "x-kubernetes-object-ref-kind": "Secret",
# "x-kubernetes-object-ref-name-key": "name",
crds:
- crds/typeA.json
- crds/typeB.json
# Vars 用于从一个 resource 字段中获取文本
# 并将该文本插入指定位置
#
# 例如,假设需要在容器的 command 中指定了 Service 对象的名称
# 并在容器的 env 中指定了 Secret 对象的名称
# 来确保以下内容可以正常工作:
# ```
# containers:
# - image: myimage
# command: ["start", "--host", "$(MY_SERVICE_NAME)"]
# env:
# - name: SECRET_TOKEN
# value: $(SOME_SECRET_NAME)
# ```
#
# 则可以在 `vars` 中添加如下内容:
#
vars:
- name: SOME_SECRET_NAME
objref:
kind: Secret
name: my-secret
apiVersion: v1
- name: MY_SERVICE_NAME
objref:
kind: Service
name: my-service
apiVersion: v1
fieldref:
fieldpath: metadata.name
- name: ANOTHER_DEPLOYMENTS_POD_RESTART_POLICY
objref:
kind: Deployment
name: my-deployment
apiVersion: apps/v1
fieldref:
fieldpath: spec.template.spec.restartPolicy
#
# var 是包含该对象的变量名、对象引用和字段引用的元组
#
# 字段引用是可选的,默认为 `metadata.name`
# 这是正常的默认值,因为 kustomize 用于生成或修改 resources 的名称
#
# 在撰写本文档时,仅支持字符串类型字段
# 不支持 intsboolsarrays 等
#
# 变量引用,即字符串 '$(FOO)' ,只能放在 kustomize 配置指定的特定对象的特定字段中
#
# 关于 vars 的默认配置数据可以查看:
# https://github.com/kubernetes-sigs/kustomize/blob/master/pkg/transformers/config/defaultconfig/varreference.go
# 默认目标是所有容器 command args 和 env 字段
#
# Vars _不应该_ 被用于 kustomize 已经处理过的配置中插入 names
# 例如, Deployment 可以通过 name 引用 ConfigMap
# 如果 kustomize 更改 ConfigMap 的名称,则知道更改 Deployment 中的引用的 name
# 修改镜像的名称、tag 或 image digest ,而无需使用 patches
# 例如,对于这种 kubernetes Deployment 片段:
# ```
# containers:
# - name: mypostgresdb
# image: postgres:8
# - name: nginxapp
# image: nginx:1.7.9
# - name: myapp
# image: my-demo-app:latest
# - name: alpine-app
# image: alpine:3.7
#```
# 想对 `image` 完成以下修改:
#
# - 将 `postgres:8` 修改为 `my-registry/my-postgres:v1`,
# - 将 nginx 的 tag 从 `1.7.9` 修改为 `1.8.0`,
# - 将 镜像名称从 `my-demo-app` 修改为 `my-app`,
# - 将 alpine 的 tag 从 `3.7` 修改为 digest 值
#
# 可以在 *kustomization* 中添加以下内容:
images:
- name: postgres
newName: my-registry/my-postgres
newTag: v1
- name: nginx
newTag: 1.8.0
- name: my-demo-app
newName: my-app
- name: alpine
digest: sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3

127
docs/zh/workflows.md Normal file
View File

@@ -0,0 +1,127 @@
[OTS]: ../glossary.md#off-the-shelf-configuration
[apply]: ../glossary.md#apply
[applying]: ../glossary.md#apply
[base]: ../glossary.md#base
[fork]: https://guides.github.com/activities/forking/
[variants]: ../glossary.md#variant
[kustomization]: ../glossary.md#kustomization
[off-the-shelf]: ../glossary.md#off-the-shelf-configuration
[overlays]: ../glossary.md#overlay
[patch]: ../glossary.md#patch
[patches]: ../glossary.md#patch
[rebase]: https://git-scm.com/docs/git-rebase
[resources]: ../glossary.md#resource
[workflowBespoke]: ../workflowBespoke.jpg
[workflowOts]: ../workflowOts.jpg
[kubectl-v1.14.0]:https://kubernetes.io/blog/2019/03/25/kubernetes-1-14-release-announcement/
# 工作流
工作流是 kustomize 运行和维护配置的步骤。
## 配置定制Bespoke configuration
在这个工作流方式中,所有的配置文件( YAML 资源)都为用户所有,存储在用户的私有 repo 中。其他用户是无法使用的。
![bespoke config workflow image][workflowBespoke]
#### 1) 创建一个目录用于版本控制
我们希望将一个名为 _ldap_ 的 Kubernetes 集群应用的配置保存在自己的 repo 中。
这里使用 git 进行版本控制。
> ```
> git init ~/ldap
> ```
#### 2) 创建一个 [base]
> ```
> mkdir -p ~/ldap/base
> ```
在这个目录中创建并提交 [kustomization] 文件及一组资源配置。
#### 3) 创建 [overlays]
> ```
> mkdir -p ~/ldap/overlays/staging
> mkdir -p ~/ldap/overlays/production
> ```
每个目录都包含需要一个 [kustomization] 文件以及一或多个 [patches]。
_staging_ 目录可能会有一个用于在 configmap 中打开一个实验标记的补丁。
_production_ 目录可能会有一个在 deployment 中增加副本数的补丁。
#### 4) 生成 [variants]
运行 kustomize将生成的配置用于 kubernetes 应用发布。
> ```
> kustomize build ~/ldap/overlays/staging | kubectl apply -f -
> kustomize build ~/ldap/overlays/production | kubectl apply -f -
> ```
也可以在 [kubectl-v1.14.0] 版,使用 ```kubectl``` 命令发布你的 [variants] 。
> ```
> kubectl apply -k ~/ldap/overlays/staging
> kubectl apply -k ~/ldap/overlays/production
> ```
## 使用现成的配置Off-the-shelf configuration
在这个工作流方式中,可从别人的 repo 中 fork kustomize 配置,并根据自己的需求来配置。
![off-the-shelf config workflow image][workflowOts]
#### 1) 寻找并且 [fork] 一个 [OTS] 配置
#### 2) 将其克隆为你自己的 [base]
这个 [base] 目录维护在上游为 [OTS] 配置的 repo ,在这个例子使用 `ladp` 的 repo 。
> ```
> mkdir ~/ldap
> git clone https://github.com/$USER/ldap ~/ldap/base
> cd ~/ldap/base
> git remote add upstream git@github.com:$USER/ldap
> ```
#### 3) 创建 [overlays]
如配置定制方法一样,创建并完善 _overlays_ 目录中的内容。
所有的 [overlays] 都依赖于 [base] 。
> ```
> mkdir -p ~/ldap/overlays/staging
> mkdir -p ~/ldap/overlays/production
> ```
用户可以将 `overlays` 维护在不同的 repo 中。
#### 4) 生成 [variants]
> ```
> kustomize build ~/ldap/overlays/staging | kubectl apply -f -
> kustomize build ~/ldap/overlays/production | kubectl apply -f -
> ```
也可以在 [kubectl-v1.14.0] 版,使用 ```kubectl``` 命令发布你的 [variants] 。
> ```
> kubectl apply -k ~/ldap/overlays/staging
> kubectl apply -k ~/ldap/overlays/production
> ```
#### 5) (可选)从上游更新
用户可以定期从上游 repo 中 [rebase] 他们的 [base] 以保证及时更新。
> ```
> cd ~/ldap/base
> git fetch upstream
> git rebase upstream/master
> ```

View File

@@ -1,3 +1,5 @@
English | [简体中文](zh/README-CN.md)
# Examples
These examples assume that `kustomize` is on your `$PATH`.
@@ -10,40 +12,57 @@ tests, and should work with HEAD
go get sigs.k8s.io/kustomize
```
* [hello world](helloWorld/README.md) - Deploy multiple
(differently configured) variants of a simple Hello
World server.
Basic Usage
* [LDAP](ldap/README.md) - Deploy multiple
(differently configured) variants of a LDAP server.
* [mySql](mySql/README.md) - Create a MySQL production
configuration from scratch.
* [springboot](springboot/README.md) - Create a Spring Boot
application production configuration from scratch.
* [combineConfigs](combineConfigs.md) -
* [configGenerations](configGeneration.md) -
Rolling update when ConfigMapGenerator changes.
* [combineConfigs](combineConfigs.md) -
Mixing configuration data from different owners
(e.g. devops/SRE and developers).
* [generatorOptions](generatorOptions.md) -
Modifying behavior of all ConfigMap and Secret generators.
* [vars](wordpress/README.md) - Injecting k8s runtime data into
container arguments (e.g. to point wordpress to a SQL service) by vars.
* [image names and tags](image.md) - Updating image names and tags without applying a patch.
* [remote target](remoteBuild.md) - Building a kustomization from a github URL
* [json patch](jsonpatch.md) - Apply a json patch in a kustomization
Advanced Usage
- generator plugins:
* [last mile helm](chart.md) - Make last mile modifications to
a helm chart.
* [secret generation](secretGeneratorPlugin.md) - Generating secrets from a plugin.
- customize builtin transformer configurations
* [transformer configs](transformerconfigs/README.md) - Customize transformer configurations
* [configGenerations](configGeneration.md) -
Rolling update when ConfigMapGenerator changes
* [generatorOptions](generatorOptions.md) - Modifying behavior of all ConfigMap and Secret generators.
* [breakfast](breakfast.md) - Customize breakfast for
Alice and Bob.
* [vars](wordpress/README.md) - Injecting k8s runtime data into container arguments (e.g. to point wordpress to a SQL service) by vars.
* [image tags](imageTags.md) - Updating image tags without applying a patch.
Multi Variant Examples
* [multibases](multibases/README.md) - Composing three variants (dev, staging, production) with a common base.
* [hello world](helloWorld/README.md) - Deploy multiple
(differently configured) variants of a simple Hello
World server.
* [LDAP](ldap/README.md) - Deploy multiple
(differently configured) variants of a LDAP server.
* [springboot](springboot/README.md) - Create a Spring Boot
application production configuration from scratch.
* [remote target](remoteBuild.md) - Building a kustomization from a github URL
* [json patch](jsonpatch.md) - Apply a json patch in a kustomization
* [transformer configs](transformerconfigs/README.md) - Customize transformer configurations
* [mySql](mySql/README.md) - Create a MySQL production
configuration from scratch.
* [breakfast](breakfast.md) - Customize breakfast for
Alice and Bob.
* [multibases](multibases/README.md) - Composing three variants (dev, staging, production) with a common base.

255
examples/chart.md Normal file
View File

@@ -0,0 +1,255 @@
# kustomization of a helm chart
[last mile]: https://testingclouds.wordpress.com/2018/07/20/844/
[stable chart]: https://github.com/helm/charts/tree/master/stable
[Helm charts]: https://github.com/helm/charts
[_minecraft_]: https://github.com/helm/charts/tree/master/stable/minecraft
[plugin]: ../docs/plugins.md
[Helm charts] aren't natively read by kustomize, but
kustomize has a [plugin] system that allows one to
access helm charts.
One pattern combining kustomize and helm is
the [last mile] modification, where
one uses an inflated chart as a base, then
modifies it on the way to the cluster using
kustomize.
The plugin used in the example below is coded to work
only for charts found in the [stable chart] repo. The
example arbitrarily uses [_minecraft_], but should work
for any chart.
The following example assumes you have `helm`
on your `$PATH`.
Make a place to work:
<!-- @makeWorkplace @helmtest -->
```
DEMO_HOME=$(mktemp -d)
mkdir -p $DEMO_HOME/base
mkdir -p $DEMO_HOME/dev
mkdir -p $DEMO_HOME/prod
```
## Use a remote chart
Define a kustomization representing your _development_
variant (aka environment).
This could involve any number of kustomizations (see
other examples), but in this case just add the name
prefix `dev-` to all resources:
<!-- @writeKustDev @helmtest -->
```
cat <<'EOF' >$DEMO_HOME/dev/kustomization.yaml
namePrefix: dev-
resources:
- ../base
EOF
```
Likewise define a _production_ variant, with a name
prefix `prod-`:
<!-- @writeKustProd @helmtest -->
```
cat <<'EOF' >$DEMO_HOME/prod/kustomization.yaml
namePrefix: prod-
resources:
- ../base
EOF
```
These two variants refer to a common base.
Define this base:
<!-- @writeKustDev @helmtest -->
```
cat <<'EOF' >$DEMO_HOME/base/kustomization.yaml
generators:
- chartInflator.yaml
EOF
```
The base refers to a generator configuration file
called `chartInflator.yaml`.
This file lets one specify the name of a [stable chart],
and other things like a path to a values file, defaulting
to the `values.yaml` that comes with the chart.
Create the config file `chartInflator.yaml`, specifying
the arbitrarily chosen chart name _minecraft_:
<!-- @writeGeneratorConfig @helmtest -->
```
cat <<'EOF' >$DEMO_HOME/base/chartInflator.yaml
apiVersion: someteam.example.com/v1
kind: ChartInflator
metadata:
name: notImportantHere
chartName: minecraft
EOF
```
Because this particular YAML file is listed in the
`generators:` stanza of a kustomization file, it is
treated as the binding between a generator plugin -
identified by the _apiVersion_ and _kind_ fields - and
other fields that configure the plugin.
Download the plugin to your `DEMO_HOME` and make it
executable:
<!-- @installPlugin @helmtest -->
```
plugin=plugin/someteam.example.com/v1/chartinflator/ChartInflator
curl -s --create-dirs -o \
"$DEMO_HOME/kustomize/$plugin" \
"https://raw.githubusercontent.com/\
kubernetes-sigs/kustomize/master/$plugin"
chmod a+x $DEMO_HOME/kustomize/$plugin
```
Check the directory layout:
<!-- @tree -->
```
tree $DEMO_HOME
```
Expect something like:
> ```
> /tmp/whatever
> ├── base
> │   ├── chartInflator.yaml
> │   └── kustomization.yaml
> ├── dev
> │   └── kustomization.yaml
> ├── kustomize
> │   └── plugin
> │   └── someteam.example.com
> │   └── v1
> │   └── chartinflator
> │   └── ChartInflator
> └── prod
> └── kustomization.yaml
> ```
Define a helper function to run kustomize with the
correct environment and flags for plugins:
<!-- @defineKustomizeIt @helmtest -->
```
function kustomizeIt {
XDG_CONFIG_HOME=$DEMO_HOME \
kustomize build --enable_alpha_plugins \
$DEMO_HOME/$1
}
```
Finally, build the `prod` variant. Notice that all
resource names now have the `prod-` prefix:
<!-- @doProd @helmtest -->
```
clear
kustomizeIt prod
```
Compare `dev` to `prod`:
<!-- @doCompare -->
```
diff <(kustomizeIt dev) <(kustomizeIt prod) | more
```
To see the unmodified but inflated chart, run kustomize
on the base. Every invocation here is re-downloading
and re-inflating the chart.
<!-- @showBase @helmtest -->
```
kustomizeIt base
```
## Use a local chart
The example above fetches a new copy of the chart
to a temporary directory with each kustomize
build, because a local chart home isn't specified
in the configuration.
To suppress fetching, specify a _chart home_
explicitly, and just make sure the chart is already
there.
To demo this so that it won't interfere with your
existing helm environment, do this:
<!-- @helmInit @helmtest -->
```
helmHome=$DEMO_HOME/dothelm
chartHome=$DEMO_HOME/base/charts
function doHelm {
helm --home $helmHome $@
}
# Create helm config files in a new location.
# The init command is extremely chatty
doHelm init --client-only >& /dev/null
```
Now download a chart; again use _minecraft_
(but you could use anything):
<!-- @fetchChart @helmtest -->
```
doHelm fetch --untar \
--untardir $chartHome \
stable/minecraft
```
The tree has more stuff now; helm config data
and a complete copy of the chart:
<!-- @tree -->
```
tree $DEMO_HOME
```
Add a `chartHome` field to the generator config file so
that it knows where to find the local chart:
<!-- @modifyGenConfig @helmtest -->
```
echo "chartHome: $chartHome" >>$DEMO_HOME/base/chartInflator.yaml
```
Change the values file, to show that the results
generated below are from the _locally_ stored chart:
<!-- @valueChange @helmtest -->
```
sed -i 's/CHANGEME!/SOMETHINGELSE/' $chartHome/minecraft/values.yaml
sed -i 's/LoadBalancer/NodePort/' $chartHome/minecraft/values.yaml
```
Finally, built it
<!-- @finalProd @helmtest -->
```
kustomizeIt prod
```
and observe the change from `LoadBalancer` to `NodePort`, and
the change in the encoded password.

View File

@@ -92,9 +92,9 @@ secret holding them (not covering that here).
<!--
secretGenerator:
- name: app-tls
commands:
tls.crt: "cat tls.cert"
tls.key: "cat tls.key"
files:
tls.crt=tls.cert
tls.key=tls.key
type: "kubernetes.io/tls"
EOF
-->

View File

@@ -5,12 +5,10 @@ Kustomize provides options to modify the behavior of ConfigMap and Secret genera
- disable appending a content hash suffix to the names of generated resources
- adding labels to generated resources
- adding annotations to generated resources
- changing shell and arguments for getting data from commands
- changing timeout for executing commands
This demo shows how to use these options. First create a workspace.
```
DEMO_HOME=$(mkdir -d)
DEMO_HOME=$(mktemp -d)
```
Create a kustomization and add a ConfigMap generator to it.

View File

@@ -1,4 +1,4 @@
# Demo: change image tags
# Demo: change image names and tags
Define a place to work:
@@ -42,21 +42,22 @@ EOF
```
The `myapp-pod` resource declares an initContainer and a container, both use the image `busybox:1.29.0`.
The tag `1.29.0` can be changed by adding `imageTags` in `kustomization.yaml`.
The image `busybox` and tag `1.29.0` can be changed by adding `images` in `kustomization.yaml`.
Add `imageTags`:
<!-- @addImageTags @test -->
Add `images`:
<!-- @addImages @test -->
```
cd $DEMO_HOME
kustomize edit set imagetag busybox:1.29.1
kustomize edit set image busybox=alpine:3.6
```
The `kustomization.yaml` will be added following `imageTags`.
The following `images` will be added to `kustomization.yaml`:
> ```
> imageTags:
> images:
> - name: busybox
> newTag: 1.29.1
> newName: alpine
> newTag: 3.6
> ```
Now build this `kustomization`
@@ -65,11 +66,11 @@ Now build this `kustomization`
kustomize build $DEMO_HOME
```
Confirm that this replaces _both_ busybox tags:
Confirm that this replaces _both_ busybox images and tags for `alpine:3.6`:
<!-- @confirmTags @test -->
<!-- @confirmImages @test -->
```
test 2 == \
$(kustomize build $DEMO_HOME | grep busybox:1.29.1 | wc -l); \
test 2 = \
$(kustomize build $DEMO_HOME | grep alpine:3.6 | wc -l); \
echo $?
```

View File

@@ -1,33 +1,38 @@
# remote targets
`kustomize build` can be run against a url. The effect is the same as cloing the repo, checking out the specified ref,
then running `kustomize build` against the desired directory in the local copy.
`kustomize build` can be run on a URL.
Take `github.com/kubernetes-sigs/kustomize//examples/multibases?ref=v1.0.6` as an example.
According to [multibases](multibases/README.md) demo, this kustomization contains three Pod objects with names as
`cluster-a-dev-myapp-pod`, `cluster-a-stag-myapp-pod`, `cluster-a-prod-myapp-pod`.
Running `kustomize build` against the url gives the same output.
The effect is the same as cloning the repo, checking out a particular
_ref_ (commit hash, branch name, release tag, etc.),
then running `kustomize build` against the desired
directory in the local copy.
To try this immediately, run a build against the kustomization
in the [multibases](multibases/README.md) example. There's
one pod in the output:
<!-- @remoteOverlayBuild @test -->
```
target="github.com/kubernetes-sigs/kustomize//examples/multibases/dev/?ref=v1.0.6"
test 1 == \
$(kustomize build $target | grep dev-myapp-pod | wc -l); \
echo $?
```
Run against the overlay in that example to get three pods
(the overlay combines the dev, staging and prod bases for
someone who wants to send them all at the same time):
<!-- @remoteBuild @test -->
```
target=github.com/kubernetes-sigs/kustomize//examples/multibases?ref=v1.0.6
target="https://github.com/kubernetes-sigs/kustomize//examples/multibases?ref=v1.0.6"
test 3 == \
$(kustomize build $target | grep cluster-a-.*-myapp-pod | wc -l); \
echo $?
```
Overlays can be remote as well:
<!-- @remoteOverlayBuild @test -->
```
target=github.com/kubernetes-sigs/kustomize//examples/multibases/dev/?ref=v1.0.6
test 1 == \
$(kustomize build $target | grep cluster-a-dev-myapp-pod | wc -l); \
echo $?
```
A base can also be specified as a URL:
A base can be a URL:
<!-- @createOverlay @test -->
```
@@ -39,7 +44,10 @@ bases:
namePrefix: remote-
EOF
```
Running `kustomize build $DEMO_HOME` and confirm the output contains three Pods and all have `remote-` prefix.
Build this to confirm that all three pods from the base
have the `remote-` prefix.
<!-- @remoteBases @test -->
```
test 3 == \
@@ -48,6 +56,7 @@ test 3 == \
```
## URL format
The url should follow
[hashicorp/go-getter URL format](https://github.com/hashicorp/go-getter#url-format).
Here are some example urls pointing to Github repos following this convention.

View File

@@ -0,0 +1,223 @@
[ConfigMaps]: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.13/#configmap-v1-core
[ELF]: https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
[Go plugin]: https://golang.org/pkg/plugin
[Secrets]: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.13/#secret-v1-core
[base64]: https://tools.ietf.org/html/rfc4648#section-4
[configuration directory]: https://wiki.archlinux.org/index.php/XDG_Base_Directory#Specification
[grpc]: https://grpc.io
[tag]: https://github.com/kubernetes-sigs/kustomize/releases
[v2.0.3]: https://github.com/kubernetes-sigs/kustomize/releases/tag/v2.0.3
[`exec.Command`]: https://golang.org/pkg/os/exec/#Command
# Generating Secrets
## What's a Secret?
Kubernetes [ConfigMaps] and [Secrets] are both
key:value maps, but the latter is intended to
signal that its values have a sensitive nature -
e.g. pass phrases or ssh keys.
Kubernetes developers work in various ways to hide
the information in a Secret more carefully than
the information held by ConfigMaps, Deployments,
etc.
## Make a place to work
<!-- @establishBase @test -->
```
DEMO_HOME=$(mktemp -d)
```
## Secret values from local files
kustomize has three different (builtin) ways to
generate a secret from local files:
* get them from so-called _env_ files (`NAME=VALUE`, one per line),
* consume the entire contents of a file to make one secret value,
* get literal values from the kustomization file itself.
Here's an example combining all three methods:
Make an env file with some short secrets:
<!-- @makeEnvFile @test -->
```
cat <<'EOF' >$DEMO_HOME/foo.env
ROUTER_PASSWORD=admin
DB_PASSWORD=iloveyou
EOF
```
Make a text file with a long secret:
<!-- @makeLongSecretFile @test -->
```
cat <<'EOF' >$DEMO_HOME/longsecret.txt
Lorem ipsum dolor sit amet,
consectetur adipiscing elit,
sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua.
EOF
```
And make a kustomization file referring to the
above and _additionally_ defining some literal KV
pairs in line:
<!-- @makeKustomization1 @test -->
```
cat <<'EOF' >$DEMO_HOME/kustomization.yaml
secretGenerator:
- name: mysecrets
envs:
- foo.env
files:
- longsecret.txt
literals:
- FRUIT=apple
- VEGETABLE=carrot
EOF
```
Now generate the Secret:
<!-- @build1 @test -->
```
result=$(kustomize build $DEMO_HOME)
echo "$result"
# Spot check the result:
test 1 == $(echo "$result" | grep -c "FRUIT: YXBwbGU=")
```
This emits something like
> ```
> apiVersion: v1
> kind: Secret
> metadata:
> name: mysecrets-hfb5df789h
> type: Opaque
> data:
> FRUIT: YXBwbGU=
> VEGETABLE: Y2Fycm90
> ROUTER_PASSWORD: YWRtaW4=
> DB_PASSWORD: aWxvdmV5b3U=
> longsecret.txt: TG9yZW0gaXBzdW0gZG9sb3Igc2l0I... (elided)
> ```
The name of the resource is the prefix `mysecrets`
(as specfied in the kustomization file), followed
by a hash of its contents.
Use your favorite base64 decoder to confirm the raw
versions of any of these values.
The problem that these three approaches share is
that the purported secrets must live on disk.
This adds additional security questions - who can
see the files, who installs them, who deletes
them, etc.
## Secret values from anywhere
> New _alpha_ behavior at HEAD, for v2.1+
A general alternative is to enshrine secret
value generation in a [plugin](../docs/plugins.md).
The values can then come in via, say, an
authenticated and authorized RPC to a password
vault service.
[sgp]: ../plugin/someteam.example.com/v1/secretsfromdatabase
Here's a [secret generator plugin][sgp]
that pretends to pull the values of a map
from a database.
Download it
<!-- @copyPlugin @test -->
```
repo=https://raw.githubusercontent.com/kubernetes-sigs/kustomize
pPath=plugin/someteam.example.com/v1/secretsfromdatabase
dir=$DEMO_HOME/kustomize/$pPath
mkdir -p $dir
curl -s -o $dir/SecretsFromDatabase.go \
${repo}/master/$pPath/SecretsFromDatabase.go
```
Compile it
<!-- @compilePlugin @xtest -->
```
go build -buildmode plugin \
-o $dir/SecretsFromDatabase.so \
$dir/SecretsFromDatabase.go
```
Create a configuration file for it:
<!-- @makeConfiguration @test -->
```
cat <<'EOF' >$DEMO_HOME/secretFromDb.yaml
apiVersion: someteam.example.com/v1
kind: SecretsFromDatabase
metadata:
name: mySecretGenerator
name: forbiddenValues
namespace: production
keys:
- ROCKET
- VEGETABLE
EOF
```
Create a new kustomization file
referencing this plugin:
<!-- @makeKustomization2 @test -->
```
cat <<'EOF' >$DEMO_HOME/kustomization.yaml
generators:
- secretFromDb.yaml
EOF
```
Finally, generate the secret, setting
`XDG_CONFIG_HOME` so that the plugin
can be found under `$DEMO_HOME`:
<!-- @build2 @xtest -->
```
result=$( \
XDG_CONFIG_HOME=$DEMO_HOME \
kustomize build --enable_alpha_plugins $DEMO_HOME )
echo "$result"
# Spot check the result:
test 1 == $(echo "$result" | grep -c "FRUIT: YXBwbGU=")
```
This should emit something like:
> ```
> apiVersion: v1
> kind: Secret
> metadata:
> name: mysecrets-bdt27dbkd6
> type: Opaque
> data:
> FRUIT: YXBwbGU=
> VEGETABLE: Y2Fycm90
> ```
i.e. a subset of the same values as above.

View File

@@ -1,37 +1,85 @@
# Transformer Configurations
Kustomize computes the resources by applying a series of transformers:
- namespace transformer
- prefix/suffix transformer
- label transformer
- annotation transformer
- name reference transformer
- variable reference transformer
Kustomize creates new resources by applying a series of transformations to an original
set of resources. Kustomize provides the following default transformers:
Each transformer takes a list of resources and modifies certain fields. The modification is based on the transformer's rule.
The fields to update is the transformer's configuration, which is a list of filedspec that can be represented in YAML format.
- annotations
- images
- labels
- name reference
- namespace
- prefix/suffix
- variable reference
## fieldSpec
FieldSpec is a type to represent a path to a field in one kind of resources. It has following format
```
A `fieldSpec` list, in a transformer's configuration, determines which resource types and which fields
within those types the transformer can modify.
## FieldSpec
FieldSpec is a type that represents a path to a field in one kind of resource.
```yaml
group: some-group
version: some-version
kind: some-kind
path: path/to/the/field
create: false
```
If `create` is set to true, it indicates the transformer to create the path if it is not found in the resources. This is most useful for label and annotation transformers, where the path for labels or annotations may not be set before the transformation.
## prefix/suffix transformer
Name prefix suffix transformer adds prefix and suffix to the `metadata/name` field for all resources with following configuration:
If `create` is set to `true`, the transformer creates the path to the field in the resource if the path is not already found. This is most useful for label and annotation transformers, where the path for labels or annotations may not be set before the transformation.
## Images transformer
The default images transformer updates the specified image key values found in paths that include
`containers` and `initcontainers` sub-paths.
If found, the `image` key value is customized by the values set in the `newName`, `newTag`, and `digest` fields.
The `name` field should match the `image` key value in a resource.
Example kustomization.yaml:
```yaml
images:
- name: postgres
newName: my-registry/my-postgres
newTag: v1
- name: nginx
newTag: 1.8.0
- name: my-demo-app
newName: my-app
- name: alpine
digest: sha256:25a0d4
```
Image transformer configurations can be customized by creating a list of `images` containing the `path` and `kind` fields.
The images transformation tutorial shows how to specify the default images transformer and customize the [images transformer configuration](images/README.md).
## Prefix/suffix transformer
The prefix/suffix transformer adds a prefix/suffix to the `metadata/name` field for all resources. Here is the default prefix transformer configuration:
```yaml
namePrefix:
- path: metadata/name
```
## label transformer
Label transformer adds labels to `metadata/labels` field for all resources. It also adds labels to `spec/selector` field in all Service and to `spec/selector/matchLabels` field in all Deployment.
Example kustomization.yaml:
```yaml
namePrefix:
alices-
nameSuffix:
-v2
```
## Labels transformer
The labels transformer adds labels to the `metadata/labels` field for all resources. It also adds labels to the `spec/selector` field in all Service resources as well as the `spec/selector/matchLabels` field in all Deployment resources.
Example:
```yaml
commonLabels:
- path: metadata/labels
create: true
@@ -44,15 +92,39 @@ commonLabels:
- path: spec/selector/matchLabels
create: true
kind: Deployment
(etc.)
```
## name reference transformer
Name reference transformer's configuration is different from all other transformers. It contains a list of namebackreferences, which represented all the possible fields that a type could be used as a reference in other types of resources. A namebackreference contains a type such as ConfigMap as well as a list of FieldSpecs where ConfigMap is referenced. Here is an example.
Example kustomization.yaml:
```yaml
commonLabels:
someName: someValue
owner: alice
app: bingo
```
## Annotations transformer
The annotations transformer adds annotations to the `metadata/annotations` field for all resources.
Annotations are also added to `spec/template/metadata/annotations` for Deployment,
ReplicaSet, DaemonSet, StatefulSet, Job, and CronJob resources, and `spec/jobTemplate/spec/template/metadata/annotations`
for CronJob resources.
Example kustomization.yaml
```yaml
commonAnnotations:
oncallPager: 800-555-1212
```
## Name reference transformer
Name reference transformer's configuration is different from all other transformers. It contains a list of `nameReferences`, which represent all of the possible fields that a type could be used as a reference in other types of resources. A `nameReference` contains a type such as ConfigMap as well as a list of `fieldSpecs` where ConfigMap is referenced in other resources. Here is an example:
```yaml
kind: ConfigMap
version: v1
FieldSpecs:
fieldSpecs:
- kind: Pod
version: v1
path: spec/volumes/configMap/name
@@ -60,10 +132,11 @@ FieldSpecs:
path: spec/template/spec/volumes/configMap/name
- kind: Job
path: spec/template/spec/volumes/configMap/name
(etc.)
```
Name reference transformer configuration contains a list of such namebackreferences for ConfigMap, Secret, Service, Role, ServiceAccount and so on.
```
Name reference transformer's configuration contains a list of `nameReferences` for resources such as ConfigMap, Secret, Service, Role, and ServiceAccount. Here is an example configuration:
```yaml
nameReference:
- kind: ConfigMap
version: v1
@@ -74,7 +147,7 @@ nameReference:
- path: spec/containers/env/valueFrom/configMapKeyRef/name
version: v1
kind: Pod
(etc.)
# ...
- kind: Secret
version: v1
fieldSpecs:
@@ -84,13 +157,22 @@ nameReference:
- path: spec/containers/env/valueFrom/secretKeyRef/name
version: v1
kind: Pod
(etc.)
```
## cusotmizing transformer configurations
## Customizing transformer configurations
In addition to the default transformers, you can create custom transformer configurations. Save the default transformer configurations to a local directory by calling `kustomize config save -d`, and modify and use these configurations. This tutorial shows how to create custom transformer configurations:
Kustomize has a default set of configurations. They can be saved to local directory through `kustomize config save -d`. Kustomize allows modifying those configuration files and using them in kustomization.yaml file. This tutorial shows how to customize those configurations to
- [support a CRD type](crd/README.md)
- disabling adding commonLabels to fields in some kind of resources
- add extra fields for variable substitution
- add extra fields for name reference
## Supporting escape characters in CRD path
```yaml
metadata:
annotations:
foo.k8s.io/bar: baz
```
Kustomize supports escaping special characters in path, e.g `matadata/annotations/foo.k8s.io\/bar`

View File

@@ -8,38 +8,6 @@ Create a workspace by
DEMO_HOME=$(mktemp -d)
```
### Get the native config as a starting point
Get the default transformer configurations using this command:
<!-- @saveConfig @test -->
```
kustomize config save -d $DEMO_HOME/kustomizeconfig
```
The default configurations are saved
in the directory `$DEMO_HOME/kusotmizeconfig` as several files
> ```
> commonannotations.yaml
> commonlabels.yaml
> nameprefix.yaml
> namereference.yaml
> namespace.yaml
> varreference.yaml
> ```
These files contain the field specifications for native resources
that transformation directives like `namePrefix`, `commonLabels`, etc.
need to do their work.
These default configurations already include some common
field specifictions for all types:
- nameprefix is added to `.metadata.name`
- namespace is added to `.metadata.namespace`
- labels is added to `.metadata.labels`
- annotations is added to `.metadata.annotations`
### Adding a custom resource
Consider a CRD of kind `MyKind` with fields
@@ -51,6 +19,7 @@ Consider a CRD of kind `MyKind` with fields
Add the following file to configure the transformers for the above fields
<!-- @addConfig @test -->
```
mkdir $DEMO_HOME/kustomizeconfig
cat > $DEMO_HOME/kustomizeconfig/mykind.yaml << EOF
commonLabels:
@@ -148,12 +117,6 @@ in the kustomization file:
cat >> $DEMO_HOME/kustomization.yaml << EOF
configurations:
- kustomizeconfig/mykind.yaml
- kustomizeconfig/commonannotations.yaml
- kustomizeconfig/commonlabels.yaml
- kustomizeconfig/nameprefix.yaml
- kustomizeconfig/namereference.yaml
- kustomizeconfig/namespace.yaml
- kustomizeconfig/varreference.yaml
EOF
```

View File

@@ -0,0 +1,128 @@
## Images transformations
This tutorial shows how to modify images in resources, and create a custom images transformer configuration.
Create a workspace by
<!-- @createws @test -->
```
DEMO_HOME=$(mktemp -d)
```
### Adding a custom resource
Consider a Custom Resource Definition(CRD) of kind `MyKind` with field
- `.spec.runLatest.container.image` referencing an image
Add the following file to configure the images transformer for the CRD:
<!-- @addConfig @test -->
```
mkdir $DEMO_HOME/kustomizeconfig
cat > $DEMO_HOME/kustomizeconfig/mykind.yaml << EOF
images:
- path: spec/runLatest/container/image
kind: MyKind
EOF
```
### Apply config
Create a file with some resources that includes an instance of `MyKind`:
<!-- @createResource @test -->
```
cat > $DEMO_HOME/resources.yaml << EOF
apiVersion: config/v1
kind: MyKind
metadata:
name: testSvc
spec:
runLatest:
container:
image: crd-image
containers:
- image: docker
name: ecosystem
- image: my-mysql
name: testing-1
---
group: apps
apiVersion: v1
kind: Deployment
metadata:
name: deploy1
spec:
template:
spec:
initContainers:
- name: nginx2
image: my-app
- name: init-alpine
image: alpine:1.8.0
EOF
```
Create a kustomization.yaml referring to it:
<!-- @createKustomization @test -->
```
cat > $DEMO_HOME/kustomization.yaml << EOF
resources:
- resources.yaml
images:
- name: crd-image
newName: new-crd-image
newTag: new-v1-tag
- name: my-app
newName: new-app-1
newTag: MYNEWTAG-1
- name: my-mysql
newName: prod-mysql
newTag: v3
- name: docker
newName: my-docker2
digest: sha256:25a0d4
EOF
```
Use the customized transformer configurations by specifying them
in the kustomization file:
<!-- @addTransformerConfigs @test -->
```
cat >> $DEMO_HOME/kustomization.yaml << EOF
configurations:
- kustomizeconfig/mykind.yaml
EOF
```
Run `kustomize build` and verify that the images have been updated.
<!-- @build @test -->
```
test 1 == \
$(kustomize build $DEMO_HOME | grep -A 2 ".*image" | grep "new-crd-image:new-v1-tag" | wc -l); \
echo $?
```
<!-- @build @test -->
```
test 1 == \
$(kustomize build $DEMO_HOME | grep -A 2 ".*image" | grep "new-app-1:MYNEWTAG-1" | wc -l); \
echo $?
```
<!-- @build @test -->
```
test 1 == \
$(kustomize build $DEMO_HOME | grep -A 2 ".*image" | grep "my-docker2@sha" | wc -l); \
echo $?
```
<!-- @build @test -->
```
test 1 == \
$(kustomize build $DEMO_HOME | grep -A 2 ".*image" | grep "prod-mysql:v3" | wc -l); \
echo $?
```

View File

@@ -0,0 +1,19 @@
resources:
- resources.yaml
configurations:
- kustomizeconfig/mykind.yaml
images:
- name: crd-image
newName: new-crd-image
newTag: new-v1-tag
- name: my-app
newName: new-app-1
newTag: MYNEWTAG-1
- name: my-mysql
newName: prod-mysql
newTag: v3
- name: docker
newName: my-docker2
digest: sha256:25a0d4

View File

@@ -0,0 +1,3 @@
images:
- path: spec/runLatest/container/image
kind: MyKind

View File

@@ -0,0 +1,27 @@
apiVersion: config/v1
kind: MyKind
metadata:
name: testSvc
spec:
runLatest:
container:
image: crd-image
containers:
- image: docker
name: ecosystem
- image: my-mysql
name: testing-1
---
group: apps
apiVersion: v1
kind: Deployment
metadata:
name: deploy1
spec:
template:
spec:
initContainers:
- name: nginx2
image: my-app
- name: init-alpine
image: alpine:1.8.0

44
examples/zh/README-CN.md Normal file
View File

@@ -0,0 +1,44 @@
[English](../README.md) | 简体中文
# 示例
这些示例默认 `kustomize` 在您的 `$PATH` 中。
这些示例通过了 [pre-commit](../../bin/pre-commit.sh) 测试,并且应该与 HEAD 一起使用。
```
go get sigs.k8s.io/kustomize
```
* [hello world](../helloWorld/README.md) - 部署多个不同配置的 Hello World 服务。
* [last mile helm](../chart.md) - 对 helm chart 进行 last mile 修改。
* [LDAP](../ldap/README.md) - 部署多个配置不同的 LDAP 服务。
* [mySql](../mySql/README.md) - 从头开始创建一个 MySQL 的生产配置。
* [springboot](../springboot/README.md) - 从头开始创建一个 Spring Boot 项目的生产配置。
* [combineConfigs](../combineConfigs.md) -
融合来自不同用户的配置数据(例如来自 devops/SRE 和 developers
* [configGenerations](../configGeneration.md) - 当 ConfigMapGenerator 修改时进行滚动更新。
* [secret generation](../secretGeneratorPlugin.md) - 生成 Secret。
* [generatorOptions](../generatorOptions.md) -修改所有 ConfigMapGenerator 和 SecretGenerator 的行为。
* [breakfast](../breakfast.md) - 给 Alice 和 Bob 定制一顿早餐 :)
* [vars](../wordpress/README.md) - 通过 vars 将一个资源的数据注入另一个资源的容器参数 (例如,为 wordpress 指定 SQL 服务)。
* [image names and tags](../image.md) - 在不使用 patch 的情况下更新镜像名称和标签。
* [multibases](../multibases/README.md) - 使用相同的 base 生成三个 variantsdevstagingproduction
* [remote target](../remoteBuild.md) - 通过 github URL 来构建 kustomization 。
* [json patch](../jsonpatch.md) -在 kustomization 中应用 json patch 。
* [transformer configs](../transformerconfigs/README.md) - 自定义 transformer 配置。

39
go.mod Normal file
View File

@@ -0,0 +1,39 @@
module sigs.k8s.io/kustomize
go 1.12
require (
github.com/PuerkitoBio/purell v1.1.0 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/emicklei/go-restful v2.9.3+incompatible // indirect
github.com/evanphx/json-patch v3.0.0+incompatible
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-openapi/jsonpointer v0.0.0-20180322222829-3a0015ad55fa // indirect
github.com/go-openapi/jsonreference v0.0.0-20180322222742-3fb327e6747d // indirect
github.com/go-openapi/spec v0.0.0-20180415031709-bcff419492ee
github.com/go-openapi/swag v0.0.0-20180405201759-811b1089cde9 // indirect
github.com/gogo/protobuf v1.0.0 // indirect
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf // indirect
github.com/googleapis/gnostic v0.1.0 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/json-iterator/go v0.0.0-20180315132816-ca39e5af3ece // indirect
github.com/mailru/easyjson v0.0.0-20180606163543-3fdea8d05856 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v0.0.0-20180228065516-1df9eeb2bb81 // indirect
github.com/onsi/ginkgo v1.8.0 // indirect
github.com/onsi/gomega v1.5.0 // indirect
github.com/pkg/errors v0.8.1
github.com/spf13/cobra v0.0.2
github.com/spf13/pflag v1.0.1
github.com/stretchr/testify v1.3.0 // indirect
golang.org/x/net v0.0.0-20190225153610-fe579d43d832 // indirect
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.2.1
k8s.io/api v0.0.0-20180510062335-53d615ae3f44
k8s.io/apimachinery v0.0.0-20180510061931-13b73596e4b6
k8s.io/client-go v7.0.0+incompatible
k8s.io/kube-openapi v0.0.0-20180510204742-b3f03f553288
sigs.k8s.io/yaml v1.1.0
)

90
go.sum Normal file
View File

@@ -0,0 +1,90 @@
github.com/PuerkitoBio/purell v1.1.0 h1:rmGxhojJlM0tuKtfdvliR84CFHljx9ag64t2xmVkjK4=
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emicklei/go-restful v2.9.3+incompatible h1:2OwhVdhtzYUp5P5wuGsVDPagKSRd9JK72sJCHVCXh5g=
github.com/emicklei/go-restful v2.9.3+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/evanphx/json-patch v3.0.0+incompatible h1:l91aby7TzBXBdmF8heZqjskeH9f3g7ZOL8/sSe+vTlU=
github.com/evanphx/json-patch v3.0.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-openapi/jsonpointer v0.0.0-20180322222829-3a0015ad55fa h1:hr8WVDjg4JKtQptZpzyb196TmruCs7PIsdJz8KAOZp8=
github.com/go-openapi/jsonpointer v0.0.0-20180322222829-3a0015ad55fa/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
github.com/go-openapi/jsonreference v0.0.0-20180322222742-3fb327e6747d h1:k3UQ7Z8yFYq0BNkYykKIheY0HlZBl1Hku+pO9HE9FNU=
github.com/go-openapi/jsonreference v0.0.0-20180322222742-3fb327e6747d/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
github.com/go-openapi/spec v0.0.0-20180415031709-bcff419492ee h1:eo0HQoNFtbiEc7+1gRF9pgW6azx8a1cO2fXcqq1MuD0=
github.com/go-openapi/spec v0.0.0-20180415031709-bcff419492ee/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
github.com/go-openapi/swag v0.0.0-20180405201759-811b1089cde9 h1:+vsw187FKvA2QUGAcE+vQSfyxqLbUXixPYRRMAzwu04=
github.com/go-openapi/swag v0.0.0-20180405201759-811b1089cde9/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
github.com/gogo/protobuf v1.0.0 h1:2jyBKDKU/8v3v2xVR2PtiWQviFUyiaGk2rpfyFT8rTM=
github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck=
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
github.com/googleapis/gnostic v0.1.0 h1:rVsPeBmXbYv4If/cumu1AzZPwV58q433hvONV1UEZoI=
github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/json-iterator/go v0.0.0-20180315132816-ca39e5af3ece h1:3HJXp/18JmMk5sjBP3LDUBtWjczCvynxaeAF6b6kWp8=
github.com/json-iterator/go v0.0.0-20180315132816-ca39e5af3ece/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/mailru/easyjson v0.0.0-20180606163543-3fdea8d05856 h1:hOnidOuIWNsFRPcxxStGeN3NNm4n4+w6KJ9cVJIh70o=
github.com/mailru/easyjson v0.0.0-20180606163543-3fdea8d05856/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180228065516-1df9eeb2bb81 h1:ImOHKpmdLPXWX5KSYquUWXKaopEPuY7TPPUo18u9aOI=
github.com/modern-go/reflect2 v0.0.0-20180228065516-1df9eeb2bb81/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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/spf13/cobra v0.0.2 h1:NfkwRbgViGoyjBKsLI0QMDcuMnhM+SBg3T0cGfpvKDE=
github.com/spf13/cobra v0.0.2/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190225153610-fe579d43d832 h1:2IdId8zoI92l1bUzjAOygcAOkmCe13HY1j0rqPPPzB8=
golang.org/x/net v0.0.0-20190225153610-fe579d43d832/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
k8s.io/api v0.0.0-20180510062335-53d615ae3f44 h1:zQ8YhMpuc1QJoor+Vm1moP9iEOyaQgOjSj3bo/zUEXE=
k8s.io/api v0.0.0-20180510062335-53d615ae3f44/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
k8s.io/apimachinery v0.0.0-20180510061931-13b73596e4b6 h1:pJrzRmry9HLPxkVGMk57cfeGRy/WG0oYXuji9t4zD1M=
k8s.io/apimachinery v0.0.0-20180510061931-13b73596e4b6/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0=
k8s.io/client-go v7.0.0+incompatible h1:kiH+Y6hn+pc78QS/mtBfMJAMIIaWevHi++JvOGEEQp4=
k8s.io/client-go v7.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
k8s.io/kube-openapi v0.0.0-20180510204742-b3f03f553288 h1:AhFqcaw5JbAAaZHxTe1fT+Jtek0pZmIwwt6FbsMA9to=
k8s.io/kube-openapi v0.0.0-20180510204742-b3f03f553288/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=

View File

@@ -15,7 +15,7 @@ limitations under the License.
*/
// Package error has contextual error types.
package error
package kusterr
import "fmt"

View File

@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package error
package kusterr
import (
"strings"

View File

@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package error
package kusterr
import (
"fmt"

View File

@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package error
package kusterr
import (
"strings"

View File

@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package error
package kusterr
import "fmt"

View File

@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package error
package kusterr
import (
"strings"

View File

@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package error
package kusterr
import "fmt"

View File

@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package error
package kusterr
import (
"strings"

View File

@@ -15,7 +15,7 @@ limitations under the License.
*/
// Package error has contextual error types.
package error
package kusterr
import (
"fmt"

View File

@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package error
package kusterr
import (
"fmt"

View File

@@ -1,18 +1,5 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// Package loadertest holds a fake for the Loader interface.
package loadertest
@@ -22,6 +9,8 @@ import (
"sigs.k8s.io/kustomize/pkg/fs"
"sigs.k8s.io/kustomize/pkg/ifc"
"sigs.k8s.io/kustomize/pkg/loader"
"sigs.k8s.io/kustomize/pkg/types"
"sigs.k8s.io/kustomize/pkg/validators"
)
// FakeLoader encapsulates the delegate Loader and the fake file system.
@@ -31,12 +20,23 @@ type FakeLoader struct {
}
// NewFakeLoader returns a Loader that uses a fake filesystem.
// The argument should be an absolute file path.
// The loader will be restricted to root only.
// The initialDir argument should be an absolute file path.
func NewFakeLoader(initialDir string) FakeLoader {
return NewFakeLoaderWithRestrictor(
loader.RestrictionRootOnly, initialDir)
}
// NewFakeLoaderWithRestrictor returns a Loader that
// uses a fake filesystem.
// The initialDir argument should be an absolute file path.
func NewFakeLoaderWithRestrictor(
lr loader.LoadRestrictorFunc, initialDir string) FakeLoader {
// Create fake filesystem and inject it into initial Loader.
fSys := fs.MakeFakeFS()
fSys.Mkdir(initialDir)
ldr, err := loader.NewLoader(initialDir, fSys)
ldr, err := loader.NewLoader(
lr, validators.MakeFakeValidator(), initialDir, fSys)
if err != nil {
log.Fatalf("Unable to make loader: %v", err)
}
@@ -53,7 +53,7 @@ func (f FakeLoader) AddDirectory(fullDirPath string) error {
return f.fs.Mkdir(fullDirPath)
}
// Root returns root.
// Root delegates.
func (f FakeLoader) Root() string {
return f.delegate.Root()
}
@@ -67,12 +67,22 @@ func (f FakeLoader) New(newRoot string) (ifc.Loader, error) {
return FakeLoader{fs: f.fs, delegate: l}, nil
}
// Load performs load from a given location.
// Load delegates.
func (f FakeLoader) Load(location string) ([]byte, error) {
return f.delegate.Load(location)
}
// Cleanup does nothing
// Cleanup delegates.
func (f FakeLoader) Cleanup() error {
return nil
return f.delegate.Cleanup()
}
// Validator delegates.
func (f FakeLoader) Validator() ifc.Validator {
return f.delegate.Validator()
}
// LoadKvPairs delegates.
func (f FakeLoader) LoadKvPairs(args types.GeneratorArgs) ([]types.Pair, error) {
return f.delegate.LoadKvPairs(args)
}

View File

@@ -1,51 +1,20 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// Package configmapandsecret generates configmaps and secrets per generator rules.
package configmapandsecret
import (
"fmt"
"path"
"strings"
"unicode/utf8"
"github.com/pkg/errors"
"k8s.io/api/core/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/validation"
"sigs.k8s.io/kustomize/pkg/fs"
"sigs.k8s.io/kustomize/pkg/ifc"
"sigs.k8s.io/kustomize/pkg/types"
)
// ConfigMapFactory makes ConfigMaps.
type ConfigMapFactory struct {
fSys fs.FileSystem
ldr ifc.Loader
}
// NewConfigMapFactory returns a new ConfigMapFactory.
func NewConfigMapFactory(
fSys fs.FileSystem, l ifc.Loader) *ConfigMapFactory {
return &ConfigMapFactory{fSys: fSys, ldr: l}
}
func (f *ConfigMapFactory) makeFreshConfigMap(
args *types.ConfigMapArgs) *corev1.ConfigMap {
cm := &corev1.ConfigMap{}
func makeFreshConfigMap(
args *types.ConfigMapArgs) *v1.ConfigMap {
cm := &v1.ConfigMap{}
cm.APIVersion = "v1"
cm.Kind = "ConfigMap"
cm.Name = args.Name
@@ -55,137 +24,48 @@ func (f *ConfigMapFactory) makeFreshConfigMap(
}
// MakeConfigMap returns a new ConfigMap, or nil and an error.
func (f *ConfigMapFactory) MakeConfigMap(
args *types.ConfigMapArgs, options *types.GeneratorOptions) (*corev1.ConfigMap, error) {
var all []kvPair
var err error
cm := f.makeFreshConfigMap(args)
pairs, err := keyValuesFromEnvFile(f.ldr, args.EnvSource)
func (f *Factory) MakeConfigMap(
args *types.ConfigMapArgs) (*v1.ConfigMap, error) {
all, err := f.ldr.LoadKvPairs(args.GeneratorArgs)
if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf(
"env source file: %s",
args.EnvSource))
return nil, err
}
all = append(all, pairs...)
pairs, err = keyValuesFromLiteralSources(args.LiteralSources)
if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf(
"literal sources %v", args.LiteralSources))
}
all = append(all, pairs...)
pairs, err = keyValuesFromFileSources(f.ldr, args.FileSources)
if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf(
"file sources: %v", args.FileSources))
}
all = append(all, pairs...)
for _, kv := range all {
err = addKvToConfigMap(cm, kv.key, kv.value)
cm := makeFreshConfigMap(args)
for _, p := range all {
err = f.addKvToConfigMap(cm, p)
if err != nil {
return nil, err
}
}
if options != nil {
cm.SetLabels(options.Labels)
cm.SetAnnotations(options.Annotations)
if f.options != nil {
cm.SetLabels(f.options.Labels)
cm.SetAnnotations(f.options.Annotations)
}
return cm, nil
}
func keyValuesFromLiteralSources(sources []string) ([]kvPair, error) {
var kvs []kvPair
for _, s := range sources {
k, v, err := parseLiteralSource(s)
if err != nil {
return nil, err
}
kvs = append(kvs, kvPair{key: k, value: v})
}
return kvs, nil
}
func keyValuesFromFileSources(ldr ifc.Loader, sources []string) ([]kvPair, error) {
var kvs []kvPair
for _, s := range sources {
k, fPath, err := parseFileSource(s)
if err != nil {
return nil, err
}
content, err := ldr.Load(fPath)
if err != nil {
return nil, err
}
kvs = append(kvs, kvPair{key: k, value: string(content)})
}
return kvs, nil
}
func keyValuesFromEnvFile(l ifc.Loader, path string) ([]kvPair, error) {
if path == "" {
return nil, nil
}
content, err := l.Load(path)
if err != nil {
return nil, err
}
return keyValuesFromLines(content)
}
// addKvToConfigMap adds the given key and data to the given config map.
// Error if key invalid, or already exists.
func addKvToConfigMap(configMap *v1.ConfigMap, keyName, data string) error {
// Note, the rules for ConfigMap keys are the exact same as the ones for SecretKeys.
if errs := validation.IsConfigMapKey(keyName); len(errs) != 0 {
return fmt.Errorf("%q is not a valid key name for a ConfigMap: %s", keyName, strings.Join(errs, ";"))
func (f *Factory) addKvToConfigMap(configMap *v1.ConfigMap, p types.Pair) error {
if err := f.ldr.Validator().ErrIfInvalidKey(p.Key); err != nil {
return err
}
if _, entryExists := configMap.Data[keyName]; entryExists {
return fmt.Errorf("cannot add key %s, another key by that name already exists: %v", keyName, configMap.Data)
// If the configmap data contains byte sequences that are all in the UTF-8
// range, we will write it to .Data
if utf8.Valid([]byte(p.Value)) {
if _, entryExists := configMap.Data[p.Key]; entryExists {
return fmt.Errorf(keyExistsErrorMsg, p.Key, configMap.Data)
}
configMap.Data[p.Key] = p.Value
return nil
}
configMap.Data[keyName] = data
// otherwise, it's BinaryData
if configMap.BinaryData == nil {
configMap.BinaryData = map[string][]byte{}
}
if _, entryExists := configMap.BinaryData[p.Key]; entryExists {
return fmt.Errorf(keyExistsErrorMsg, p.Key, configMap.BinaryData)
}
configMap.BinaryData[p.Key] = []byte(p.Value)
return nil
}
// parseFileSource parses the source given.
//
// Acceptable formats include:
// 1. source-path: the basename will become the key name
// 2. source-name=source-path: the source-name will become the key name and
// source-path is the path to the key file.
//
// Key names cannot include '='.
func parseFileSource(source string) (keyName, filePath string, err error) {
numSeparators := strings.Count(source, "=")
switch {
case numSeparators == 0:
return path.Base(source), source, nil
case numSeparators == 1 && strings.HasPrefix(source, "="):
return "", "", fmt.Errorf("key name for file path %v missing", strings.TrimPrefix(source, "="))
case numSeparators == 1 && strings.HasSuffix(source, "="):
return "", "", fmt.Errorf("file path for key name %v missing", strings.TrimSuffix(source, "="))
case numSeparators > 1:
return "", "", errors.New("key names or file paths cannot contain '='")
default:
components := strings.Split(source, "=")
return components[0], components[1], nil
}
}
// parseLiteralSource parses the source key=val pair into its component pieces.
// This functionality is distinguished from strings.SplitN(source, "=", 2) since
// it returns an error in the case of empty keys, values, or a missing equals sign.
func parseLiteralSource(source string) (keyName, value string, err error) {
// leading equal is invalid
if strings.Index(source, "=") == 0 {
return "", "", fmt.Errorf("invalid literal source %v, expected key=value", source)
}
// split after the first equal (so values can have the = character)
items := strings.SplitN(source, "=", 2)
if len(items) != 2 {
return "", "", fmt.Errorf("invalid literal source %v, expected key=value", source)
}
return items[0], strings.Trim(items[1], "\"'"), nil
}

View File

@@ -1,18 +1,5 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package configmapandsecret
@@ -25,6 +12,7 @@ import (
"sigs.k8s.io/kustomize/pkg/fs"
"sigs.k8s.io/kustomize/pkg/loader"
"sigs.k8s.io/kustomize/pkg/types"
"sigs.k8s.io/kustomize/pkg/validators"
)
func makeEnvConfigMap(name string) *corev1.ConfigMap {
@@ -57,6 +45,9 @@ func makeFileConfigMap(name string) *corev1.ConfigMap {
BAR=baz
`,
},
BinaryData: map[string][]byte{
"app.bin": {0xff, 0xfd},
},
}
}
@@ -92,9 +83,11 @@ func TestConstructConfigMap(t *testing.T) {
{
description: "construct config map from env",
input: types.ConfigMapArgs{
GeneratorArgs: types.GeneratorArgs{Name: "envConfigMap"},
DataSources: types.DataSources{
EnvSource: "configmap/app.env",
GeneratorArgs: types.GeneratorArgs{
Name: "envConfigMap",
DataSources: types.DataSources{
EnvSources: []string{"configmap/app.env"},
},
},
},
options: nil,
@@ -103,9 +96,11 @@ func TestConstructConfigMap(t *testing.T) {
{
description: "construct config map from file",
input: types.ConfigMapArgs{
GeneratorArgs: types.GeneratorArgs{Name: "fileConfigMap"},
DataSources: types.DataSources{
FileSources: []string{"configmap/app-init.ini"},
GeneratorArgs: types.GeneratorArgs{
Name: "fileConfigMap",
DataSources: types.DataSources{
FileSources: []string{"configmap/app-init.ini", "configmap/app.bin"},
},
},
},
options: nil,
@@ -114,9 +109,11 @@ func TestConstructConfigMap(t *testing.T) {
{
description: "construct config map from literal",
input: types.ConfigMapArgs{
GeneratorArgs: types.GeneratorArgs{Name: "literalConfigMap"},
DataSources: types.DataSources{
LiteralSources: []string{"a=x", "b=y", "c=\"Hello World\"", "d='true'"},
GeneratorArgs: types.GeneratorArgs{
Name: "literalConfigMap",
DataSources: types.DataSources{
LiteralSources: []string{"a=x", "b=y", "c=\"Hello World\"", "d='true'"},
},
},
},
options: &types.GeneratorOptions{
@@ -131,9 +128,11 @@ func TestConstructConfigMap(t *testing.T) {
fSys := fs.MakeFakeFS()
fSys.WriteFile("/configmap/app.env", []byte("DB_USERNAME=admin\nDB_PASSWORD=somepw\n"))
fSys.WriteFile("/configmap/app-init.ini", []byte("FOO=bar\nBAR=baz\n"))
f := NewConfigMapFactory(fSys, loader.NewFileLoaderAtRoot(fSys))
fSys.WriteFile("/configmap/app.bin", []byte{0xff, 0xfd})
ldr := loader.NewFileLoaderAtRoot(validators.MakeFakeValidator(), fSys)
for _, tc := range testCases {
cm, err := f.MakeConfigMap(&tc.input, tc.options)
f := NewFactory(ldr, tc.options)
cm, err := f.MakeConfigMap(&tc.input)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

View File

@@ -0,0 +1,23 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package configmapandsecret
import (
"sigs.k8s.io/kustomize/pkg/ifc"
"sigs.k8s.io/kustomize/pkg/types"
)
// Factory makes ConfigMaps and Secrets.
type Factory struct {
ldr ifc.Loader
options *types.GeneratorOptions
}
// NewFactory returns a new factory that makes ConfigMaps and Secrets.
func NewFactory(
ldr ifc.Loader, o *types.GeneratorOptions) *Factory {
return &Factory{ldr: ldr, options: o}
}
const keyExistsErrorMsg = "cannot add key %s, another key by that name already exists: %v"

View File

@@ -1,102 +0,0 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package configmapandsecret
import (
"bufio"
"bytes"
"fmt"
"os"
"strings"
"unicode"
"unicode/utf8"
"k8s.io/apimachinery/pkg/util/validation"
)
// kvPair represents a key value pair.
type kvPair struct {
key string
value string
}
var utf8bom = []byte{0xEF, 0xBB, 0xBF}
// keyValuesFromLines parses given content in to a list of key-value pairs.
func keyValuesFromLines(content []byte) ([]kvPair, error) {
var kvs []kvPair
scanner := bufio.NewScanner(bytes.NewReader(content))
currentLine := 0
for scanner.Scan() {
// Process the current line, retrieving a key/value pair if
// possible.
scannedBytes := scanner.Bytes()
kv, err := kvFromLine(scannedBytes, currentLine)
if err != nil {
return nil, err
}
currentLine++
if len(kv.key) == 0 {
// no key means line was empty or a comment
continue
}
kvs = append(kvs, kv)
}
return kvs, nil
}
// kvFromLine returns a kv with blank key if the line is empty or a comment.
// The value will be retrieved from the environment if necessary.
func kvFromLine(line []byte, currentLine int) (kvPair, error) {
kv := kvPair{}
if !utf8.Valid(line) {
return kv, fmt.Errorf("line %d has invalid utf8 bytes : %v", line, string(line))
}
// We trim UTF8 BOM from the first line of the file but no others
if currentLine == 0 {
line = bytes.TrimPrefix(line, utf8bom)
}
// trim the line from all leading whitespace first
line = bytes.TrimLeftFunc(line, unicode.IsSpace)
// If the line is empty or a comment, we return a blank key/value pair.
if len(line) == 0 || line[0] == '#' {
return kv, nil
}
data := strings.SplitN(string(line), "=", 2)
key := data[0]
if errs := validation.IsEnvVarName(key); len(errs) != 0 {
return kv, fmt.Errorf("%q is not a valid key name: %s", key, strings.Join(errs, ";"))
}
if len(data) == 2 {
kv.value = data[1]
} else {
// No value (no `=` in the line) is a signal to obtain the value
// from the environment.
kv.value = os.Getenv(key)
}
kv.key = key
return kv, nil
}

View File

@@ -1,68 +0,0 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package configmapandsecret
import (
"reflect"
"testing"
)
func TestKeyValuesFromLines(t *testing.T) {
tests := []struct {
desc string
content string
expectedPairs []kvPair
expectedErr bool
}{
{
desc: "valid kv content parse",
content: `
k1=v1
k2=v2
`,
expectedPairs: []kvPair{
{key: "k1", value: "v1"},
{key: "k2", value: "v2"},
},
expectedErr: false,
},
{
desc: "content with comments",
content: `
k1=v1
#k2=v2
`,
expectedPairs: []kvPair{
{key: "k1", value: "v1"},
},
expectedErr: false,
},
// TODO: add negative testcases
}
for _, test := range tests {
pairs, err := keyValuesFromLines([]byte(test.content))
if test.expectedErr && err == nil {
t.Fatalf("%s should not return error", test.desc)
}
if !reflect.DeepEqual(pairs, test.expectedPairs) {
t.Errorf("%s should succeed, got:%v exptected:%v", test.desc, pairs, test.expectedPairs)
}
}
}

View File

@@ -1,53 +1,17 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package configmapandsecret
import (
"context"
"fmt"
"log"
"os/exec"
"path/filepath"
"strings"
"time"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/validation"
"sigs.k8s.io/kustomize/pkg/fs"
"sigs.k8s.io/kustomize/pkg/types"
)
const (
defaultCommandTimeout = 5 * time.Second
)
// SecretFactory makes Secrets.
type SecretFactory struct {
fSys fs.FileSystem
wd string
}
// NewSecretFactory returns a new SecretFactory.
func NewSecretFactory(fSys fs.FileSystem, wd string) *SecretFactory {
return &SecretFactory{fSys: fSys, wd: wd}
}
func (f *SecretFactory) makeFreshSecret(args *types.SecretArgs) *corev1.Secret {
func makeFreshSecret(
args *types.SecretArgs) *corev1.Secret {
s := &corev1.Secret{}
s.APIVersion = "v1"
s.Kind = "Secret"
@@ -62,102 +26,33 @@ func (f *SecretFactory) makeFreshSecret(args *types.SecretArgs) *corev1.Secret {
}
// MakeSecret returns a new secret.
func (f *SecretFactory) MakeSecret(args *types.SecretArgs, options *types.GeneratorOptions) (*corev1.Secret, error) {
var all []kvPair
var err error
s := f.makeFreshSecret(args)
timeout := defaultCommandTimeout
if args.TimeoutSeconds != nil {
log.Println("SecretArgs.TimeoutSeconds will be deprected in next release. Please use GeneratorOptions.TimeoutSeconds instread.")
timeout = time.Duration(*args.TimeoutSeconds) * time.Second
}
pairs, err := f.keyValuesFromEnvFileCommand(args.EnvCommand, timeout, options)
func (f *Factory) MakeSecret(
args *types.SecretArgs) (*corev1.Secret, error) {
all, err := f.ldr.LoadKvPairs(args.GeneratorArgs)
if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf(
"env source file: %s",
args.EnvCommand))
return nil, err
}
all = append(all, pairs...)
pairs, err = f.keyValuesFromCommands(args.Commands, timeout, options)
if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf(
"commands %v", args.Commands))
}
all = append(all, pairs...)
for _, kv := range all {
err = addKvToSecret(s, kv.key, kv.value)
s := makeFreshSecret(args)
for _, p := range all {
err = f.addKvToSecret(s, p.Key, p.Value)
if err != nil {
return nil, err
}
}
if options != nil {
s.SetLabels(options.Labels)
s.SetAnnotations(options.Annotations)
if f.options != nil {
s.SetLabels(f.options.Labels)
s.SetAnnotations(f.options.Annotations)
}
return s, nil
}
func addKvToSecret(secret *corev1.Secret, keyName, data string) error {
// Note, the rules for SecretKeys keys are the exact same as the ones for ConfigMap.
if errs := validation.IsConfigMapKey(keyName); len(errs) != 0 {
return fmt.Errorf("%q is not a valid key name for a Secret: %s", keyName, strings.Join(errs, ";"))
func (f *Factory) addKvToSecret(secret *corev1.Secret, keyName, data string) error {
if err := f.ldr.Validator().ErrIfInvalidKey(keyName); err != nil {
return err
}
if _, entryExists := secret.Data[keyName]; entryExists {
return fmt.Errorf("cannot add key %s, another key by that name already exists", keyName)
return fmt.Errorf(keyExistsErrorMsg, keyName, secret.Data)
}
secret.Data[keyName] = []byte(data)
return nil
}
func (f *SecretFactory) keyValuesFromEnvFileCommand(cmd string, timeout time.Duration, options *types.GeneratorOptions) ([]kvPair, error) {
content, err := f.createSecretKey(cmd, timeout, options)
if err != nil {
return nil, err
}
return keyValuesFromLines(content)
}
func (f *SecretFactory) keyValuesFromCommands(sources map[string]string, timeout time.Duration, options *types.GeneratorOptions) ([]kvPair, error) {
var kvs []kvPair
for k, cmd := range sources {
content, err := f.createSecretKey(cmd, timeout, options)
if err != nil {
return nil, err
}
kvs = append(kvs, kvPair{key: k, value: string(content)})
}
return kvs, nil
}
// Run a command, return its output as the secret.
func (f *SecretFactory) createSecretKey(command string, timeout time.Duration, options *types.GeneratorOptions) ([]byte, error) {
if !f.fSys.IsDir(f.wd) {
f.wd = filepath.Dir(f.wd)
if !f.fSys.IsDir(f.wd) {
return nil, errors.New("not a directory: " + f.wd)
}
}
if options != nil && options.TimeoutSeconds != nil {
t := time.Duration(*options.TimeoutSeconds) * time.Second
if t > timeout {
timeout = t
}
}
var commands []string
if options == nil || len(options.Shell) == 0 {
commands = []string{"sh", "-c", command}
} else {
commands = append(options.Shell, command)
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
cmd := exec.CommandContext(ctx, commands[0], commands[1:]...)
cmd.Dir = f.wd
return cmd.Output()
}

View File

@@ -0,0 +1,140 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package configmapandsecret
import (
"reflect"
"testing"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/kustomize/pkg/fs"
"sigs.k8s.io/kustomize/pkg/loader"
"sigs.k8s.io/kustomize/pkg/types"
"sigs.k8s.io/kustomize/pkg/validators"
)
func makeEnvSecret(name string) *corev1.Secret {
return &corev1.Secret{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "Secret",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
Data: map[string][]byte{
"DB_PASSWORD": []byte("somepw"),
"DB_USERNAME": []byte("admin"),
},
Type: "Opaque",
}
}
func makeFileSecret(name string) *corev1.Secret {
return &corev1.Secret{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "Secret",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
Data: map[string][]byte{
"app-init.ini": []byte(`FOO=bar
BAR=baz
`),
},
Type: "Opaque",
}
}
func makeLiteralSecret(name string) *corev1.Secret {
s := &corev1.Secret{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "Secret",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
Data: map[string][]byte{
"a": []byte("x"),
"b": []byte("y"),
},
Type: "Opaque",
}
s.SetLabels(map[string]string{"foo": "bar"})
return s
}
func TestConstructSecret(t *testing.T) {
type testCase struct {
description string
input types.SecretArgs
options *types.GeneratorOptions
expected *corev1.Secret
}
testCases := []testCase{
{
description: "construct secret from env",
input: types.SecretArgs{
GeneratorArgs: types.GeneratorArgs{
Name: "envSecret",
DataSources: types.DataSources{
EnvSources: []string{"secret/app.env"},
},
},
},
options: nil,
expected: makeEnvSecret("envSecret"),
},
{
description: "construct secret from file",
input: types.SecretArgs{
GeneratorArgs: types.GeneratorArgs{
Name: "fileSecret",
DataSources: types.DataSources{
FileSources: []string{"secret/app-init.ini"},
},
},
},
options: nil,
expected: makeFileSecret("fileSecret"),
},
{
description: "construct secret from literal",
input: types.SecretArgs{
GeneratorArgs: types.GeneratorArgs{
Name: "literalSecret",
DataSources: types.DataSources{
LiteralSources: []string{"a=x", "b=y"},
},
},
},
options: &types.GeneratorOptions{
Labels: map[string]string{
"foo": "bar",
},
},
expected: makeLiteralSecret("literalSecret"),
},
}
fSys := fs.MakeFakeFS()
fSys.WriteFile("/secret/app.env", []byte("DB_USERNAME=admin\nDB_PASSWORD=somepw\n"))
fSys.WriteFile("/secret/app-init.ini", []byte("FOO=bar\nBAR=baz\n"))
ldr := loader.NewFileLoaderAtRoot(validators.MakeFakeValidator(), fSys)
for _, tc := range testCases {
f := NewFactory(ldr, tc.options)
cm, err := f.MakeSecret(&tc.input)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !reflect.DeepEqual(*cm, *tc.expected) {
t.Fatalf("in testcase: %q updated:\n%#v\ndoesn't match expected:\n%#v\n", tc.description, *cm, tc.expected)
}
}
}

View File

@@ -1,34 +0,0 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package k8sdeps provides kustomize factory with k8s dependencies
package k8sdeps
import (
"sigs.k8s.io/kustomize/k8sdeps/kunstruct"
"sigs.k8s.io/kustomize/k8sdeps/transformer"
"sigs.k8s.io/kustomize/k8sdeps/validator"
"sigs.k8s.io/kustomize/pkg/factory"
)
// NewFactory creates an instance of KustFactory using k8sdeps factories
func NewFactory() *factory.KustFactory {
return factory.NewKustFactory(
kunstruct.NewKunstructuredFactoryImpl(),
validator.NewKustValidator(),
transformer.NewFactoryImpl(),
)
}

View File

@@ -1,18 +1,5 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package kunstruct
@@ -25,15 +12,12 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/util/yaml"
"sigs.k8s.io/kustomize/k8sdeps/configmapandsecret"
"sigs.k8s.io/kustomize/pkg/fs"
"sigs.k8s.io/kustomize/pkg/ifc"
"sigs.k8s.io/kustomize/pkg/types"
)
// KunstructuredFactoryImpl hides construction using apimachinery types.
type KunstructuredFactoryImpl struct {
cmFactory *configmapandsecret.ConfigMapFactory
secretFactory *configmapandsecret.SecretFactory
}
var _ ifc.KunstructuredFactory = &KunstructuredFactoryImpl{}
@@ -53,6 +37,9 @@ func (kf *KunstructuredFactoryImpl) SliceFromBytes(
var out unstructured.Unstructured
err = decoder.Decode(&out)
if err == nil {
if len(out.Object) == 0 {
continue
}
err = kf.validate(out)
if err != nil {
return nil, err
@@ -77,36 +64,42 @@ func (kf *KunstructuredFactoryImpl) FromMap(
}
// MakeConfigMap returns an instance of Kunstructured for ConfigMap
func (kf *KunstructuredFactoryImpl) MakeConfigMap(args *types.ConfigMapArgs, options *types.GeneratorOptions) (ifc.Kunstructured, error) {
cm, err := kf.cmFactory.MakeConfigMap(args, options)
func (kf *KunstructuredFactoryImpl) MakeConfigMap(
ldr ifc.Loader,
options *types.GeneratorOptions,
args *types.ConfigMapArgs) (ifc.Kunstructured, error) {
o, err := configmapandsecret.NewFactory(
ldr, options).MakeConfigMap(args)
if err != nil {
return nil, err
}
return NewKunstructuredFromObject(cm)
return NewKunstructuredFromObject(o)
}
// MakeSecret returns an instance of Kunstructured for Secret
func (kf *KunstructuredFactoryImpl) MakeSecret(args *types.SecretArgs, options *types.GeneratorOptions) (ifc.Kunstructured, error) {
sec, err := kf.secretFactory.MakeSecret(args, options)
func (kf *KunstructuredFactoryImpl) MakeSecret(
ldr ifc.Loader,
options *types.GeneratorOptions,
args *types.SecretArgs) (ifc.Kunstructured, error) {
o, err := configmapandsecret.NewFactory(
ldr, options).MakeSecret(args)
if err != nil {
return nil, err
}
return NewKunstructuredFromObject(sec)
}
// Set sets loader, filesystem and workdirectory
func (kf *KunstructuredFactoryImpl) Set(fs fs.FileSystem, ldr ifc.Loader) {
kf.cmFactory = configmapandsecret.NewConfigMapFactory(fs, ldr)
kf.secretFactory = configmapandsecret.NewSecretFactory(fs, ldr.Root())
return NewKunstructuredFromObject(o)
}
// validate validates that u has kind and name
// except for kind `List`, which doesn't require a name
func (kf *KunstructuredFactoryImpl) validate(u unstructured.Unstructured) error {
kind := u.GetKind()
if kind == "" {
return fmt.Errorf("missing kind in object %v", u)
} else if strings.HasSuffix(kind, "List") {
return nil
}
if u.GetName() == "" {
return fmt.Errorf("missing metadata.name in object %v", u)
}
if u.GetKind() == "" {
return fmt.Errorf("missing kind in object %v", u)
}
return nil
}

View File

@@ -33,6 +33,24 @@ func TestSliceFromBytes(t *testing.T) {
"name": "winnie",
},
})
testList := factory.FromMap(
map[string]interface{}{
"apiVersion": "v1",
"kind": "List",
"items": []interface{}{
testConfigMap.Map(),
testConfigMap.Map(),
},
})
testConfigMapList := factory.FromMap(
map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMapList",
"items": []interface{}{
testConfigMap.Map(),
testConfigMap.Map(),
},
})
tests := []struct {
name string
@@ -100,6 +118,18 @@ WOOOOOOOOOOOOOOOOOOOOOOOOT: woot
expectedOut: []ifc.Kunstructured{},
expectedErr: true,
},
{
name: "emptyObjects",
input: []byte(`
---
#a comment
---
`),
expectedOut: []ifc.Kunstructured{},
expectedErr: false,
},
{
name: "Missing .metadata.name in object",
input: []byte(`
@@ -112,6 +142,42 @@ metadata:
expectedOut: nil,
expectedErr: true,
},
{
name: "List",
input: []byte(`
apiVersion: v1
kind: List
items:
- apiVersion: v1
kind: ConfigMap
metadata:
name: winnie
- apiVersion: v1
kind: ConfigMap
metadata:
name: winnie
`),
expectedOut: []ifc.Kunstructured{testList},
expectedErr: false,
},
{
name: "ConfigMapList",
input: []byte(`
apiVersion: v1
kind: ConfigMapList
items:
- apiVersion: v1
kind: ConfigMap
metadata:
name: winnie
- apiVersion: v1
kind: ConfigMap
metadata:
name: winnie
`),
expectedOut: []ifc.Kunstructured{testConfigMapList},
expectedErr: false,
},
}
for _, test := range tests {

View File

@@ -19,7 +19,7 @@ package kunstruct
import (
"encoding/json"
"fmt"
"sigs.k8s.io/kustomize/pkg/types"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -88,5 +88,19 @@ func (fs *UnstructAdapter) GetFieldValue(path string) (string, error) {
if found || err != nil {
return s, err
}
return "", fmt.Errorf("no field named '%s'", path)
return "", types.NoFieldError{Field: path}
}
// GetStringSlice returns value at the given fieldpath.
func (fs *UnstructAdapter) GetStringSlice(path string) ([]string, error) {
fields, err := parseFields(path)
if err != nil {
return []string{}, err
}
s, found, err := unstructured.NestedStringSlice(
fs.UnstructuredContent(), fields...)
if found || err != nil {
return s, err
}
return []string{}, types.NoFieldError{Field: path}
}

View File

@@ -1,27 +1,17 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// Package transformer provides transformer factory
package transformer
import (
"sigs.k8s.io/kustomize/k8sdeps/transformer/hash"
"sigs.k8s.io/kustomize/k8sdeps/transformer/inventory"
"sigs.k8s.io/kustomize/k8sdeps/transformer/patch"
"sigs.k8s.io/kustomize/pkg/ifc"
"sigs.k8s.io/kustomize/pkg/resource"
"sigs.k8s.io/kustomize/pkg/transformers"
"sigs.k8s.io/kustomize/pkg/types"
)
// FactoryImpl makes patch transformer and name hash transformer
@@ -33,7 +23,9 @@ func NewFactoryImpl() *FactoryImpl {
}
// MakePatchTransformer makes a new patch transformer
func (p *FactoryImpl) MakePatchTransformer(slice []*resource.Resource, rf *resource.Factory) (transformers.Transformer, error) {
func (p *FactoryImpl) MakePatchTransformer(
slice []*resource.Resource,
rf *resource.Factory) (transformers.Transformer, error) {
return patch.NewPatchTransformer(slice, rf)
}
@@ -41,3 +33,11 @@ func (p *FactoryImpl) MakePatchTransformer(slice []*resource.Resource, rf *resou
func (p *FactoryImpl) MakeHashTransformer() transformers.Transformer {
return hash.NewNameHashTransformer()
}
func (p *FactoryImpl) MakeInventoryTransformer(
arg *types.Inventory,
ldr ifc.Loader,
namespace string,
gp types.GarbagePolicy) transformers.Transformer {
return inventory.NewInventoryTransformer(arg, ldr, namespace, gp)
}

View File

@@ -20,6 +20,7 @@ import (
"crypto/sha256"
"encoding/json"
"fmt"
"sort"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -86,6 +87,21 @@ func SecretHash(sec *v1.Secret) (string, error) {
return h, nil
}
// SortArrayAndComputeHash sorts a string array and
// returns a hash for it
func SortArrayAndComputeHash(s []string) (string, error) {
sort.Strings(s)
data, err := json.Marshal(s)
if err != nil {
return "", err
}
h, err := encodeHash(hash(string(data)))
if err != nil {
return "", err
}
return h, nil
}
// encodeConfigMap encodes a ConfigMap.
// Data, Kind, and Name are taken into account.
func encodeConfigMap(cm *v1.ConfigMap) (string, error) {

View File

@@ -90,6 +90,28 @@ func TestSecretHash(t *testing.T) {
}
}
func TestArrayHash(t *testing.T) {
array1 := []string{"a", "b", "c"}
array2 := []string{"c", "b", "a"}
h1, err := SortArrayAndComputeHash(array1)
if err != nil {
t.Errorf("unexpected error %v", err)
}
if h1 == "" {
t.Errorf("failed to hash %v", array1)
}
h2, err := SortArrayAndComputeHash(array2)
if err != nil {
t.Errorf("unexpected error %v", err)
}
if h2 == "" {
t.Errorf("failed to hash %v", array2)
}
if h1 != h2 {
t.Errorf("hash is not consistent with reordered list: %s %s", h1, h2)
}
}
func TestEncodeConfigMap(t *testing.T) {
cases := []struct {
desc string

View File

@@ -35,7 +35,7 @@ func NewNameHashTransformer() transformers.Transformer {
// Transform appends hash to generated resources.
func (o *nameHashTransformer) Transform(m resmap.ResMap) error {
for _, res := range m {
if res.IsGenerated() {
if res.NeedHashSuffix() {
h, err := NewKustHash().Hash(res.Map())
if err != nil {
return err

View File

@@ -21,10 +21,10 @@ import (
"testing"
"sigs.k8s.io/kustomize/k8sdeps/kunstruct"
"sigs.k8s.io/kustomize/pkg/ifc"
"sigs.k8s.io/kustomize/pkg/resid"
"sigs.k8s.io/kustomize/pkg/resmap"
"sigs.k8s.io/kustomize/pkg/resource"
"sigs.k8s.io/kustomize/pkg/types"
)
func TestNameHashTransformer(t *testing.T) {
@@ -81,14 +81,14 @@ func TestNameHashTransformer(t *testing.T) {
},
},
}),
resid.NewResId(secret, "secret1"): rf.FromMap(
resid.NewResId(secret, "secret1"): rf.FromMapAndOption(
map[string]interface{}{
"apiVersion": "v1",
"kind": "Secret",
"metadata": map[string]interface{}{
"name": "secret1",
},
}).SetBehavior(ifc.BehaviorCreate),
}, &types.GeneratorArgs{Behavior: "create"}, &types.GeneratorOptions{DisableNameSuffixHash: false}),
}
expected := resmap.ResMap{
@@ -142,14 +142,14 @@ func TestNameHashTransformer(t *testing.T) {
},
},
}),
resid.NewResId(secret, "secret1"): rf.FromMap(
resid.NewResId(secret, "secret1"): rf.FromMapAndOption(
map[string]interface{}{
"apiVersion": "v1",
"kind": "Secret",
"metadata": map[string]interface{}{
"name": "secret1-7kc45hd5f7",
},
}).SetBehavior(ifc.BehaviorCreate),
}, &types.GeneratorArgs{Behavior: "create"}, &types.GeneratorOptions{DisableNameSuffixHash: false}),
}
tran := NewNameHashTransformer()

View File

@@ -0,0 +1,112 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package inventory
import (
"fmt"
"sigs.k8s.io/kustomize/k8sdeps/kunstruct"
"sigs.k8s.io/kustomize/k8sdeps/transformer/hash"
"sigs.k8s.io/kustomize/pkg/gvk"
"sigs.k8s.io/kustomize/pkg/ifc"
"sigs.k8s.io/kustomize/pkg/inventory"
"sigs.k8s.io/kustomize/pkg/resid"
"sigs.k8s.io/kustomize/pkg/resmap"
"sigs.k8s.io/kustomize/pkg/resource"
"sigs.k8s.io/kustomize/pkg/transformers"
"sigs.k8s.io/kustomize/pkg/types"
)
// inventoryTransformer compute the inventory object used in prune
type inventoryTransformer struct {
garbagePolicy types.GarbagePolicy
ldr ifc.Loader
cmName string
cmNamespace string
}
var _ transformers.Transformer = &inventoryTransformer{}
// NewInventoryTransformer makes a inventoryTransformer.
func NewInventoryTransformer(
p *types.Inventory,
ldr ifc.Loader,
namespace string,
gp types.GarbagePolicy) transformers.Transformer {
if p == nil || p.Type != "ConfigMap" || p.ConfigMap.Namespace != namespace {
return transformers.NewNoOpTransformer()
}
return &inventoryTransformer{
garbagePolicy: gp,
ldr: ldr,
cmName: p.ConfigMap.Name,
cmNamespace: p.ConfigMap.Namespace,
}
}
// Transform generates an inventory object based on the input ResMap.
// this transformer doesn't change existing resources -
// it just visits resources and accumulates information to make a new ConfigMap.
// The prune ConfigMap is used to support the pruning command in the client side tool,
// which is proposed in https://github.com/kubernetes/enhancements/pull/810
// The inventory data is written to annotation since
// 1. The key in data field is constrained and couldn't include arbitrary letters
// 2. The annotation can be put into any kind of objects
func (o *inventoryTransformer) Transform(m resmap.ResMap) error {
invty := inventory.NewInventory()
var keys []string
for _, r := range m {
ns, _ := r.GetFieldValue("metadata.namespace")
item := resid.NewItemId(r.GetGvk(), ns, r.GetName())
var refs []resid.ItemId
for _, refid := range r.GetRefBy() {
ref := m[refid]
ns, _ := ref.GetFieldValue("metadata.namespace")
refs = append(refs, resid.NewItemId(ref.GetGvk(), ns, ref.GetName()))
}
invty.Current[item] = refs
keys = append(keys, item.String())
}
h, err := hash.SortArrayAndComputeHash(keys)
if err != nil {
return err
}
args := &types.ConfigMapArgs{}
args.Name = o.cmName
args.Namespace = o.cmNamespace
opts := &types.GeneratorOptions{
Annotations: make(map[string]string),
}
opts.Annotations[inventory.HashAnnotation] = h
err = invty.UpdateAnnotations(opts.Annotations)
if err != nil {
return err
}
kf := kunstruct.NewKunstructuredFactoryImpl()
k, err := kf.MakeConfigMap(o.ldr, opts, args)
if err != nil {
return err
}
if o.garbagePolicy == types.GarbageCollect {
for k := range m {
delete(m, k)
}
}
id := resid.NewResIdWithPrefixNamespace(
gvk.Gvk{
Version: "v1",
Kind: "ConfigMap",
},
o.cmName,
"", o.cmNamespace)
if _, ok := m[id]; ok {
return fmt.Errorf("id %v is already used, please use a different name in the prune field", id)
}
m[id] = resource.NewFactory(kf).FromKunstructured(k)
return nil
}

View File

@@ -0,0 +1,161 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package inventory
import (
"reflect"
"testing"
"sigs.k8s.io/kustomize/k8sdeps/kunstruct"
"sigs.k8s.io/kustomize/pkg/fs"
"sigs.k8s.io/kustomize/pkg/gvk"
"sigs.k8s.io/kustomize/pkg/loader"
"sigs.k8s.io/kustomize/pkg/resid"
"sigs.k8s.io/kustomize/pkg/resmap"
"sigs.k8s.io/kustomize/pkg/resource"
"sigs.k8s.io/kustomize/pkg/types"
"sigs.k8s.io/kustomize/pkg/validators"
)
var secret = gvk.Gvk{Version: "v1", Kind: "Secret"}
var cmap = gvk.Gvk{Version: "v1", Kind: "ConfigMap"}
var deploy = gvk.Gvk{Group: "apps", Version: "v1", Kind: "Deployment"}
func makeResMap() resmap.ResMap {
rf := resource.NewFactory(
kunstruct.NewKunstructuredFactoryImpl())
objs := resmap.ResMap{
resid.NewResId(cmap, "cm1"): rf.FromMap(
map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": "cm1",
},
}),
resid.NewResId(secret, "secret1"): rf.FromMap(
map[string]interface{}{
"apiVersion": "v1",
"kind": "Secret",
"metadata": map[string]interface{}{
"name": "secret1",
},
}),
resid.NewResId(deploy, "deploy1"): rf.FromMap(
map[string]interface{}{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": map[string]interface{}{
"name": "deploy1",
},
"spec": map[string]interface{}{
"template": map[string]interface{}{
"spec": map[string]interface{}{
"containers": []interface{}{
map[string]interface{}{
"name": "nginx",
"image": "nginx:1.7.9",
"env": []interface{}{
map[string]interface{}{
"name": "CM_FOO",
"valueFrom": map[string]interface{}{
"configMapKeyRef": map[string]interface{}{
"name": "cm1",
"key": "somekey",
},
},
},
},
"envFrom": []interface{}{
map[string]interface{}{
"configMapRef": map[string]interface{}{
"name": "cm1",
"key": "somekey",
},
},
map[string]interface{}{
"secretRef": map[string]interface{}{
"name": "secret1",
"key": "somekey",
},
},
},
},
},
},
},
},
}),
}
objs[resid.NewResId(cmap, "cm1")].AppendRefBy(resid.NewResId(deploy, "deploy1"))
objs[resid.NewResId(secret, "secret1")].AppendRefBy(resid.NewResId(deploy, "deploy1"))
return objs
}
func TestInventoryTransformer(t *testing.T) {
rf := resource.NewFactory(
kunstruct.NewKunstructuredFactoryImpl())
ldr := loader.NewFileLoaderAtCwd(validators.MakeFakeValidator(), fs.MakeFakeFS())
// hash is derived based on all keys in the Inventory
// It is added to annotations as
// kustomize.config.k8s.io/InventoryHash: hash
// When seeing the same annotation, prune binary assumes no
// clean up is needed
hash := "h44788gt7g"
// inventory is the derived json string for an Inventory object
// It is added to annotations as
// kustomize.config.k8s.io/Inventory: inventory
inventory := "{\"current\":{\"apps_v1_Deployment|~X|deploy1\":null,\"~G_v1_ConfigMap|~X|cm1\":[{\"group\":\"apps\",\"version\":\"v1\",\"kind\":\"Deployment\",\"name\":\"deploy1\"}],\"~G_v1_Secret|~X|secret1\":[{\"group\":\"apps\",\"version\":\"v1\",\"kind\":\"Deployment\",\"name\":\"deploy1\"}]}}" // nolint
// This is the root or inventory object which tracks all
// the applied resources - this is the thing we expect the transformer to create.
pruneMap := rf.FromMap(
map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": "pruneCM",
"namespace": "default",
"annotations": map[string]interface{}{
"kustomize.config.k8s.io/Inventory": inventory,
"kustomize.config.k8s.io/InventoryHash": hash,
},
},
})
expected := resmap.ResMap{
resid.NewResIdWithPrefixNamespace(cmap, "pruneCM", "", "default"): pruneMap,
}
p := &types.Inventory{
Type: "ConfigMap",
ConfigMap: types.NameArgs{
Name: "pruneCM",
Namespace: "default",
},
}
objs := makeResMap()
// include the original resmap; only return the ConfigMap for pruning
tran := NewInventoryTransformer(p, ldr, "default", types.GarbageCollect)
tran.Transform(objs)
if !reflect.DeepEqual(objs, expected) {
err := expected.ErrorIfNotEqual(objs)
t.Fatalf("actual doesn't match expected: %v", err)
}
objs = makeResMap()
expected = objs.DeepCopy(rf)
expected[resid.NewResIdWithPrefixNamespace(cmap, "pruneCM", "", "default")] = pruneMap
// append the ConfigMap for pruning to the original resmap
tran = NewInventoryTransformer(p, ldr, "default", types.GarbageIgnore)
tran.Transform(objs)
if !reflect.DeepEqual(objs, expected) {
err := expected.ErrorIfNotEqual(objs)
t.Fatalf("actual doesn't match expected: %v", err)
}
}

View File

@@ -60,7 +60,7 @@ func (pt *patchTransformer) Transform(baseResourceMap resmap.ResMap) error {
for _, patch := range patches {
// Merge patches with base resource.
id := patch.Id()
matchedIds := baseResourceMap.FindByGVKN(id)
matchedIds := baseResourceMap.GetMatchingIds(id.GvknEquals)
if len(matchedIds) == 0 {
return fmt.Errorf("failed to find an object with %s to apply the patch", id.GvknString())
}

View File

@@ -1,24 +1,15 @@
/*
Copyright 2018 The Kubernetes Authors.
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package validator provides functions to validate labels, annotations, namespace using apimachinery
// Package validator provides functions to validate labels, annotations,
// namespaces and configmap/secret keys using apimachinery functions.
package validator
import (
"errors"
"fmt"
"strings"
apivalidation "k8s.io/apimachinery/pkg/api/validation"
v1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
"k8s.io/apimachinery/pkg/util/validation"
@@ -33,6 +24,24 @@ func NewKustValidator() *KustValidator {
return &KustValidator{}
}
func (v *KustValidator) ErrIfInvalidKey(k string) error {
if errs := validation.IsConfigMapKey(k); len(errs) != 0 {
return fmt.Errorf(
"%q is not a valid key name: %s",
k, strings.Join(errs, ";"))
}
return nil
}
func (v *KustValidator) IsEnvVarName(k string) error {
if errs := validation.IsEnvVarName(k); len(errs) != 0 {
return fmt.Errorf(
"%q is not a valid key name: %s",
k, strings.Join(errs, ";"))
}
return nil
}
// MakeAnnotationValidator returns a MapValidatorFunc using apimachinery.
func (v *KustValidator) MakeAnnotationValidator() func(map[string]string) error {
return func(x map[string]string) error {

View File

@@ -19,12 +19,11 @@ package main
import (
"os"
"sigs.k8s.io/kustomize/k8sdeps"
"sigs.k8s.io/kustomize/pkg/commands"
)
func main() {
if err := commands.NewDefaultCommand(k8sdeps.NewFactory()).Execute(); err != nil {
if err := commands.NewDefaultCommand().Execute(); err != nil {
os.Exit(1)
}
os.Exit(0)

View File

@@ -0,0 +1,161 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package accumulator
import (
"fmt"
"log"
"strings"
"sigs.k8s.io/kustomize/pkg/resid"
"sigs.k8s.io/kustomize/pkg/resmap"
"sigs.k8s.io/kustomize/pkg/transformers"
"sigs.k8s.io/kustomize/pkg/transformers/config"
"sigs.k8s.io/kustomize/pkg/types"
)
// ResAccumulator accumulates resources and the rules
// used to customize those resources.
type ResAccumulator struct {
resMap resmap.ResMap
tConfig *config.TransformerConfig
varSet types.VarSet
}
func MakeEmptyAccumulator() *ResAccumulator {
ra := &ResAccumulator{}
ra.resMap = make(resmap.ResMap)
ra.tConfig = &config.TransformerConfig{}
ra.varSet = types.VarSet{}
return ra
}
// ResMap returns a copy of the internal resMap.
func (ra *ResAccumulator) ResMap() resmap.ResMap {
result := make(resmap.ResMap)
for k, v := range ra.resMap {
result[k] = v
}
return result
}
// Vars returns a copy of underlying vars.
func (ra *ResAccumulator) Vars() []types.Var {
return ra.varSet.Set()
}
func (ra *ResAccumulator) MergeResourcesWithErrorOnIdCollision(
resources resmap.ResMap) (err error) {
ra.resMap, err = resmap.MergeWithErrorOnIdCollision(
resources, ra.resMap)
return err
}
func (ra *ResAccumulator) MergeResourcesWithOverride(
resources resmap.ResMap) (err error) {
ra.resMap, err = resmap.MergeWithOverride(
ra.resMap, resources)
return err
}
func (ra *ResAccumulator) MergeConfig(
tConfig *config.TransformerConfig) (err error) {
ra.tConfig, err = ra.tConfig.Merge(tConfig)
return err
}
func (ra *ResAccumulator) GetTransformerConfig() *config.TransformerConfig {
return ra.tConfig
}
func (ra *ResAccumulator) MergeVars(incoming []types.Var) error {
return ra.varSet.MergeSlice(incoming)
}
func (ra *ResAccumulator) MergeAccumulator(other *ResAccumulator) (err error) {
err = ra.MergeResourcesWithErrorOnIdCollision(other.resMap)
if err != nil {
return err
}
err = ra.MergeConfig(other.tConfig)
if err != nil {
return err
}
return ra.varSet.MergeSet(&other.varSet)
}
// makeVarReplacementMap returns a map of Var names to
// their final values. The values are strings intended
// for substitution wherever the $(var.Name) occurs.
func (ra *ResAccumulator) makeVarReplacementMap() (map[string]string, error) {
result := map[string]string{}
for _, v := range ra.Vars() {
matched := ra.resMap.GetMatchingIds(
resid.NewResId(v.ObjRef.GVK(), v.ObjRef.Name).GvknEquals)
if len(matched) > 1 {
return nil, fmt.Errorf(
"found %d resId matches for var %s "+
"(unable to disambiguate)",
len(matched), v)
}
if len(matched) == 1 {
s, err := ra.resMap[matched[0]].GetFieldValue(v.FieldRef.FieldPath)
if err != nil {
return nil, fmt.Errorf(
"field specified in var '%v' "+
"not found in corresponding resource", v)
}
result[v.Name] = s
} else {
return nil, fmt.Errorf(
"var '%v' cannot be mapped to a field "+
"in the set of known resources", v)
}
}
return result, nil
}
func (ra *ResAccumulator) Transform(t transformers.Transformer) error {
return t.Transform(ra.resMap)
}
func (ra *ResAccumulator) ResolveVars() error {
replacementMap, err := ra.makeVarReplacementMap()
if err != nil {
return err
}
if len(replacementMap) == 0 {
return nil
}
t := transformers.NewRefVarTransformer(
replacementMap, ra.tConfig.VarReference)
err = ra.Transform(t)
if len(t.UnusedVars()) > 0 {
log.Printf(
"well-defined vars that were never replaced: %s\n",
strings.Join(t.UnusedVars(), ","))
}
return err
}
func (ra *ResAccumulator) FixBackReferences() (err error) {
if ra.tConfig.NameReference == nil {
return nil
}
return ra.Transform(transformers.NewNameReferenceTransformer(
ra.tConfig.NameReference))
}

View File

@@ -0,0 +1,284 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package accumulator_test
import (
"bytes"
"log"
"os"
"strings"
"testing"
"sigs.k8s.io/kustomize/k8sdeps/kunstruct"
. "sigs.k8s.io/kustomize/pkg/accumulator"
"sigs.k8s.io/kustomize/pkg/gvk"
"sigs.k8s.io/kustomize/pkg/resid"
"sigs.k8s.io/kustomize/pkg/resmap"
"sigs.k8s.io/kustomize/pkg/resource"
"sigs.k8s.io/kustomize/pkg/transformers/config"
"sigs.k8s.io/kustomize/pkg/types"
)
func makeResAccumulator() (*ResAccumulator, *resource.Factory, error) {
ra := MakeEmptyAccumulator()
err := ra.MergeConfig(config.MakeDefaultConfig())
if err != nil {
return nil, nil, err
}
rf := resource.NewFactory(
kunstruct.NewKunstructuredFactoryImpl())
ra.MergeResourcesWithErrorOnIdCollision(resmap.ResMap{
resid.NewResId(
gvk.Gvk{Group: "apps", Version: "v1", Kind: "Deployment"},
"deploy1"): rf.FromMap(
map[string]interface{}{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": map[string]interface{}{
"name": "deploy1",
},
"spec": map[string]interface{}{
"template": map[string]interface{}{
"spec": map[string]interface{}{
"containers": []interface{}{
map[string]interface{}{
"command": []interface{}{
"myserver",
"--somebackendService $(SERVICE_ONE)",
"--yetAnother $(SERVICE_TWO)",
},
},
},
},
},
},
}),
resid.NewResId(
gvk.Gvk{Version: "v1", Kind: "Service"},
"backendOne"): rf.FromMap(
map[string]interface{}{
"apiVersion": "v1",
"kind": "Service",
"metadata": map[string]interface{}{
"name": "backendOne",
},
}),
resid.NewResId(
gvk.Gvk{Version: "v1", Kind: "Service"},
"backendTwo"): rf.FromMap(
map[string]interface{}{
"apiVersion": "v1",
"kind": "Service",
"metadata": map[string]interface{}{
"name": "backendTwo",
},
}),
})
return ra, rf, nil
}
func TestResolveVarsHappy(t *testing.T) {
ra, _, err := makeResAccumulator()
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
err = ra.MergeVars([]types.Var{
{
Name: "SERVICE_ONE",
ObjRef: types.Target{
Gvk: gvk.Gvk{Version: "v1", Kind: "Service"},
Name: "backendOne"},
},
{
Name: "SERVICE_TWO",
ObjRef: types.Target{
Gvk: gvk.Gvk{Version: "v1", Kind: "Service"},
Name: "backendTwo"},
},
})
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
err = ra.ResolveVars()
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
c := getCommand(find("deploy1", ra.ResMap()))
if c != "myserver --somebackendService backendOne --yetAnother backendTwo" {
t.Fatalf("unexpected command: %s", c)
}
}
func TestResolveVarsOneUnused(t *testing.T) {
ra, _, err := makeResAccumulator()
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
err = ra.MergeVars([]types.Var{
{
Name: "SERVICE_ONE",
ObjRef: types.Target{
Gvk: gvk.Gvk{Version: "v1", Kind: "Service"},
Name: "backendOne"},
},
{
Name: "SERVICE_UNUSED",
ObjRef: types.Target{
Gvk: gvk.Gvk{Version: "v1", Kind: "Service"},
Name: "backendTwo"},
},
})
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
var buf bytes.Buffer
log.SetOutput(&buf)
defer func() {
log.SetOutput(os.Stderr)
}()
err = ra.ResolveVars()
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
expectLog(t, buf, "well-defined vars that were never replaced: SERVICE_UNUSED")
c := getCommand(find("deploy1", ra.ResMap()))
if c != "myserver --somebackendService backendOne --yetAnother $(SERVICE_TWO)" {
t.Fatalf("unexpected command: %s", c)
}
}
func expectLog(t *testing.T, log bytes.Buffer, expect string) {
if !strings.Contains(log.String(), expect) {
t.Fatalf("expected log containing '%s', got '%s'", expect, log.String())
}
}
func TestResolveVarsVarNeedsDisambiguation(t *testing.T) {
ra, rf, err := makeResAccumulator()
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
ra.MergeResourcesWithErrorOnIdCollision(resmap.ResMap{
resid.NewResIdWithPrefixNamespace(
gvk.Gvk{Version: "v1", Kind: "Service"},
"backendOne", "", "fooNamespace"): rf.FromMap(
map[string]interface{}{
"apiVersion": "v1",
"kind": "Service",
"metadata": map[string]interface{}{
"name": "backendOne",
},
}),
})
err = ra.MergeVars([]types.Var{
{
Name: "SERVICE_ONE",
ObjRef: types.Target{
Gvk: gvk.Gvk{Version: "v1", Kind: "Service"},
Name: "backendOne",
},
},
})
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
err = ra.ResolveVars()
if err == nil {
t.Fatalf("expected error")
}
if !strings.Contains(
err.Error(), "unable to disambiguate") {
t.Fatalf("unexpected err: %v", err)
}
}
func TestResolveVarsGoodResIdBadField(t *testing.T) {
ra, _, err := makeResAccumulator()
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
err = ra.MergeVars([]types.Var{
{
Name: "SERVICE_ONE",
ObjRef: types.Target{
Gvk: gvk.Gvk{Version: "v1", Kind: "Service"},
Name: "backendOne"},
FieldRef: types.FieldSelector{FieldPath: "nope_nope_nope"},
},
})
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
err = ra.ResolveVars()
if err == nil {
t.Fatalf("expected error")
}
if !strings.Contains(
err.Error(),
"not found in corresponding resource") {
t.Fatalf("unexpected err: %v", err)
}
}
func TestResolveVarsUnmappableVar(t *testing.T) {
ra, _, err := makeResAccumulator()
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
err = ra.MergeVars([]types.Var{
{
Name: "SERVICE_THREE",
ObjRef: types.Target{
Gvk: gvk.Gvk{Version: "v1", Kind: "Service"},
Name: "doesNotExist"},
},
})
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
err = ra.ResolveVars()
if err == nil {
t.Fatalf("expected error")
}
if !strings.Contains(
err.Error(),
"cannot be mapped to a field in the set of known resources") {
t.Fatalf("unexpected err: %v", err)
}
}
func find(name string, resMap resmap.ResMap) *resource.Resource {
for k, v := range resMap {
if k.Name() == name {
return v
}
}
return nil
}
// Assumes arg is a deployment, returns the command of first container.
func getCommand(r *resource.Resource) string {
var m map[string]interface{}
var c []interface{}
m, _ = r.Map()["spec"].(map[string]interface{})
m, _ = m["template"].(map[string]interface{})
m, _ = m["spec"].(map[string]interface{})
c, _ = m["containers"].([]interface{})
m, _ = c[0].(map[string]interface{})
return strings.Join(m["command"].([]string), " ")
}

View File

@@ -1,37 +1,42 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package build
import (
"fmt"
"io"
"path/filepath"
"strings"
"sigs.k8s.io/kustomize/pkg/ifc"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/pkg/constants"
"sigs.k8s.io/kustomize/pkg/fs"
"sigs.k8s.io/kustomize/pkg/ifc/transformer"
"sigs.k8s.io/kustomize/pkg/loader"
"sigs.k8s.io/kustomize/pkg/pgmconfig"
"sigs.k8s.io/kustomize/pkg/plugins"
"sigs.k8s.io/kustomize/pkg/resmap"
"sigs.k8s.io/kustomize/pkg/target"
"sigs.k8s.io/yaml"
)
type buildOptions struct {
// Options contain the options for running a build
type Options struct {
kustomizationPath string
outputPath string
loadRestrictor loader.LoadRestrictorFunc
}
// NewOptions creates a Options object
func NewOptions(p, o string) *Options {
return &Options{
kustomizationPath: p,
outputPath: o,
loadRestrictor: loader.RestrictionRootOnly,
}
}
var examples = `
@@ -51,14 +56,17 @@ url examples:
// NewCmdBuild creates a new build command.
func NewCmdBuild(
out io.Writer, fs fs.FileSystem,
rf *resmap.Factory,
out io.Writer, fSys fs.FileSystem,
v ifc.Validator, rf *resmap.Factory,
ptf transformer.Factory) *cobra.Command {
var o buildOptions
var o Options
pluginConfig := plugins.DefaultPluginConfig()
pl := plugins.NewLoader(pluginConfig, rf)
cmd := &cobra.Command{
Use: "build [path]",
Short: "Print current configuration per contents of " + constants.KustomizationFileName,
Short: "Print current configuration per contents of " + pgmconfig.KustomizationFileNames[0],
Example: examples,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
@@ -66,49 +74,86 @@ func NewCmdBuild(
if err != nil {
return err
}
return o.RunBuild(out, fs, rf, ptf)
return o.RunBuild(out, v, fSys, rf, ptf, pl)
},
}
cmd.Flags().StringVarP(
&o.outputPath,
"output", "o", "",
"If specified, write the build output to this path.")
loader.AddLoadRestrictionsFlag(cmd.Flags())
plugins.AddEnablePluginsFlag(
cmd.Flags(), &pluginConfig.Enabled)
cmd.AddCommand(NewCmdBuildPrune(out, v, fSys, rf, ptf, pl))
return cmd
}
// Validate validates build command.
func (o *buildOptions) Validate(args []string) error {
func (o *Options) Validate(args []string) (err error) {
if len(args) > 1 {
return errors.New("specify one path to " + constants.KustomizationFileName)
return errors.New(
"specify one path to " + pgmconfig.KustomizationFileNames[0])
}
if len(args) == 0 {
o.kustomizationPath = "./"
o.kustomizationPath = loader.CWD
} else {
o.kustomizationPath = args[0]
}
return nil
o.loadRestrictor, err = loader.ValidateLoadRestrictorFlag()
return
}
// RunBuild runs build command.
func (o *buildOptions) RunBuild(
out io.Writer, fSys fs.FileSystem,
rf *resmap.Factory, ptf transformer.Factory) error {
ldr, err := loader.NewLoader(o.kustomizationPath, fSys)
func (o *Options) RunBuild(
out io.Writer, v ifc.Validator, fSys fs.FileSystem,
rf *resmap.Factory, ptf transformer.Factory,
pl *plugins.Loader) error {
ldr, err := loader.NewLoader(
o.loadRestrictor, v, o.kustomizationPath, fSys)
if err != nil {
return err
}
defer ldr.Cleanup()
kt, err := target.NewKustTarget(ldr, fSys, rf, ptf)
kt, err := target.NewKustTarget(ldr, rf, ptf, pl)
if err != nil {
return err
}
allResources, err := kt.MakeCustomizedResMap()
m, err := kt.MakeCustomizedResMap()
if err != nil {
return err
}
// Output the objects.
res, err := allResources.EncodeAsYaml()
return o.emitResources(out, fSys, m)
}
func (o *Options) RunBuildPrune(
out io.Writer, v ifc.Validator, fSys fs.FileSystem,
rf *resmap.Factory, ptf transformer.Factory,
pl *plugins.Loader) error {
ldr, err := loader.NewLoader(
o.loadRestrictor, v, o.kustomizationPath, fSys)
if err != nil {
return err
}
defer ldr.Cleanup()
kt, err := target.NewKustTarget(ldr, rf, ptf, pl)
if err != nil {
return err
}
m, err := kt.MakePruneConfigMap()
if err != nil {
return err
}
return o.emitResources(out, fSys, m)
}
func (o *Options) emitResources(
out io.Writer, fSys fs.FileSystem, m resmap.ResMap) error {
if o.outputPath != "" && fSys.IsDir(o.outputPath) {
return writeIndividualFiles(fSys, o.outputPath, m)
}
res, err := m.EncodeAsYaml()
if err != nil {
return err
}
@@ -118,3 +163,48 @@ func (o *buildOptions) RunBuild(
_, err = out.Write(res)
return err
}
func NewCmdBuildPrune(
out io.Writer, v ifc.Validator, fSys fs.FileSystem,
rf *resmap.Factory, ptf transformer.Factory,
pl *plugins.Loader) *cobra.Command {
var o Options
cmd := &cobra.Command{
Use: "alpha-inventory [path]",
Short: "Print the inventory object which contains a list of all other objects",
Example: examples,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
err := o.Validate(args)
if err != nil {
return err
}
return o.RunBuildPrune(out, v, fSys, rf, ptf, pl)
},
}
return cmd
}
func writeIndividualFiles(
fSys fs.FileSystem, folderPath string, m resmap.ResMap) error {
for _, res := range m {
filename := filepath.Join(
folderPath,
fmt.Sprintf(
"%s_%s.yaml",
strings.ToLower(res.GetGvk().String()),
strings.ToLower(res.GetName()),
),
)
out, err := yaml.Marshal(res.Map())
if err != nil {
return err
}
err = fSys.WriteFile(filename, out)
if err != nil {
return err
}
}
return nil
}

View File

@@ -17,28 +17,15 @@ limitations under the License.
package build
import (
"bytes"
"io/ioutil"
"os"
"path/filepath"
"reflect"
"strings"
"testing"
"github.com/ghodss/yaml"
"sigs.k8s.io/kustomize/k8sdeps"
"sigs.k8s.io/kustomize/pkg/commands/kustfile"
"sigs.k8s.io/kustomize/pkg/constants"
"sigs.k8s.io/kustomize/pkg/fs"
"sigs.k8s.io/kustomize/pkg/pgmconfig"
)
type buildTestCase struct {
Description string `yaml:"description"`
Args []string `yaml:"args"`
Filename string `yaml:"filename"`
// path to the file that contains the expected output
ExpectedStdout string `yaml:"expectedStdout"`
ExpectedError string `yaml:"expectedError"`
func TestNewOptionsToSilenceCodeInspectionError(t *testing.T) {
if NewOptions("foo", "bar") == nil {
t.Fatal("could not make new options")
}
}
func TestBuildValidate(t *testing.T) {
@@ -48,14 +35,14 @@ func TestBuildValidate(t *testing.T) {
path string
erMsg string
}{
{"noargs", []string{}, "./", ""},
{"noargs", []string{}, ".", ""},
{"file", []string{"beans"}, "beans", ""},
{"path", []string{"a/b/c"}, "a/b/c", ""},
{"path", []string{"too", "many"},
"", "specify one path to " + constants.KustomizationFileName},
"", "specify one path to " + pgmconfig.KustomizationFileNames[0]},
}
for _, mycase := range cases {
opts := buildOptions{}
opts := Options{}
e := opts.Validate(mycase.args)
if len(mycase.erMsg) > 0 {
if e == nil {
@@ -67,7 +54,7 @@ func TestBuildValidate(t *testing.T) {
continue
}
if e != nil {
t.Errorf("%s: unknown error %v", mycase.name, e)
t.Errorf("%s: unknown error: %v", mycase.name, e)
continue
}
if opts.kustomizationPath != mycase.path {
@@ -75,87 +62,3 @@ func TestBuildValidate(t *testing.T) {
}
}
}
func TestBuild(t *testing.T) {
const updateEnvVar = "UPDATE_KUSTOMIZE_EXPECTED_DATA"
updateKustomizeExpected := os.Getenv(updateEnvVar) == "true"
fSys := fs.MakeRealFS()
var testcases []string
filepath.Walk("testdata", func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if path == "testdata" {
return nil
}
name := filepath.Base(path)
if info.IsDir() {
if strings.HasPrefix(name, "testcase-") {
testcases = append(testcases, strings.TrimPrefix(name, "testcase-"))
}
return filepath.SkipDir
}
return nil
})
// sanity check that we found the right folder
if !kustfile.StringInSlice("simple", testcases) {
t.Fatalf("Error locating testcases")
}
for _, testcaseName := range testcases {
t.Run(testcaseName,
func(t *testing.T) {
runBuildTestCase(t, testcaseName, updateKustomizeExpected, fSys)
})
}
}
func runBuildTestCase(t *testing.T, testcaseName string, updateKustomizeExpected bool, fSys fs.FileSystem) {
name := testcaseName
testcase := buildTestCase{}
testcaseDir := filepath.Join("testdata", "testcase-"+name)
testcaseData, err := ioutil.ReadFile(filepath.Join(testcaseDir, "test.yaml"))
if err != nil {
t.Fatalf("%s: %v", name, err)
}
if err := yaml.Unmarshal(testcaseData, &testcase); err != nil {
t.Fatalf("%s: %v", name, err)
}
ops := &buildOptions{
kustomizationPath: testcase.Filename,
}
buf := bytes.NewBuffer([]byte{})
f := k8sdeps.NewFactory()
err = ops.RunBuild(
buf, fSys,
f.ResmapF,
f.TransformerF)
switch {
case err != nil && len(testcase.ExpectedError) == 0:
t.Errorf("unexpected error: %v", err)
case err != nil && len(testcase.ExpectedError) != 0:
if !strings.Contains(err.Error(), testcase.ExpectedError) {
t.Errorf("expected error to contain %q but got: %v", testcase.ExpectedError, err)
}
return
case err == nil && len(testcase.ExpectedError) != 0:
t.Errorf("unexpected no error")
}
actualBytes := buf.Bytes()
if !updateKustomizeExpected {
expectedBytes, err := ioutil.ReadFile(testcase.ExpectedStdout)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if !reflect.DeepEqual(actualBytes, expectedBytes) {
t.Errorf("\n**** Actual:\n\n%s\n\n**** doesn't equal expected:\n\n%s\n\n", actualBytes, expectedBytes)
}
} else {
ioutil.WriteFile(testcase.ExpectedStdout, actualBytes, 0644)
}
}

View File

@@ -1,85 +0,0 @@
diff -u -N /tmp/noop/apps_v1beta2_Deployment_nginx.yaml /tmp/transformed/apps_v1beta2_Deployment_nginx.yaml
--- /tmp/noop/apps_v1beta2_Deployment_nginx.yaml YYYY-MM-DD HH:MM:SS
+++ /tmp/transformed/apps_v1beta2_Deployment_nginx.yaml YYYY-MM-DD HH:MM:SS
@@ -1,14 +1,27 @@
apiVersion: apps/v1beta2
kind: Deployment
metadata:
+ annotations:
+ note: This is a test annotation
labels:
- app: nginx
- name: nginx
+ app: mynginx
+ org: example.com
+ team: foo
+ name: team-foo-nginx
spec:
+ selector:
+ matchLabels:
+ app: mynginx
+ org: example.com
+ team: foo
template:
metadata:
+ annotations:
+ note: This is a test annotation
labels:
- app: nginx
+ app: mynginx
+ org: example.com
+ team: foo
spec:
containers:
- image: nginx
diff -u -N /tmp/noop/networking.k8s.io_v1_NetworkPolicy_nginx.yaml /tmp/transformed/networking.k8s.io_v1_NetworkPolicy_nginx.yaml
--- /tmp/noop/networking.k8s.io_v1_NetworkPolicy_nginx.yaml YYYY-MM-DD HH:MM:SS
+++ /tmp/transformed/networking.k8s.io_v1_NetworkPolicy_nginx.yaml YYYY-MM-DD HH:MM:SS
@@ -1,13 +1,21 @@
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
- name: nginx
+ annotations:
+ note: This is a test annotation
+ labels:
+ app: mynginx
+ org: example.com
+ team: foo
+ name: team-foo-nginx
spec:
ingress:
- from:
- podSelector:
matchLabels:
- app: nginx
+ app: mynginx
+ org: example.com
+ team: foo
podSelector:
matchExpressions:
- key: app
diff -u -N /tmp/noop/v1_Service_nginx.yaml /tmp/transformed/v1_Service_nginx.yaml
--- /tmp/noop/v1_Service_nginx.yaml YYYY-MM-DD HH:MM:SS
+++ /tmp/transformed/v1_Service_nginx.yaml YYYY-MM-DD HH:MM:SS
@@ -1,11 +1,17 @@
apiVersion: v1
kind: Service
metadata:
+ annotations:
+ note: This is a test annotation
labels:
- app: nginx
- name: nginx
+ app: mynginx
+ org: example.com
+ team: foo
+ name: team-foo-nginx
spec:
ports:
- port: 80
selector:
- app: nginx
+ app: mynginx
+ org: example.com
+ team: foo

View File

@@ -1,71 +0,0 @@
apiVersion: v1
kind: Service
metadata:
annotations:
note: This is a test annotation
labels:
app: mynginx
org: example.com
team: foo
name: team-foo-nginx
spec:
ports:
- port: 80
selector:
app: mynginx
org: example.com
team: foo
---
apiVersion: apps/v1beta2
kind: Deployment
metadata:
annotations:
note: This is a test annotation
labels:
app: mynginx
org: example.com
team: foo
name: team-foo-nginx
spec:
selector:
matchLabels:
app: mynginx
org: example.com
team: foo
template:
metadata:
annotations:
note: This is a test annotation
labels:
app: mynginx
org: example.com
team: foo
spec:
containers:
- image: nginx
name: nginx
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
annotations:
note: This is a test annotation
labels:
app: mynginx
org: example.com
team: foo
name: team-foo-nginx
spec:
ingress:
- from:
- podSelector:
matchLabels:
app: mynginx
org: example.com
team: foo
podSelector:
matchExpressions:
- key: app
operator: In
values:
- test

View File

@@ -1,11 +0,0 @@
namePrefix: team-foo-
commonLabels:
app: mynginx
org: example.com
team: foo
commonAnnotations:
note: This is a test annotation
resources:
- resources/deployment.yaml
- resources/networkpolicy.yaml
- resources/service.yaml

View File

@@ -1,15 +0,0 @@
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: nginx
labels:
app: nginx
spec:
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx

View File

@@ -1,13 +0,0 @@
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: nginx
spec:
podSelector:
matchExpressions:
- {key: app, operator: In, values: [test]}
ingress:
- from:
- podSelector:
matchLabels:
app: nginx

View File

@@ -1,11 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
selector:
app: nginx

View File

@@ -1,5 +0,0 @@
description: base only
args: []
filename: testdata/testcase-base-only/in
expectedStdout: testdata/testcase-base-only/expected.yaml
expectedDiff: testdata/testcase-base-only/expected.diff

View File

@@ -1,6 +0,0 @@
namePrefix: p1-
configMapGenerator:
- name: com1
behavior: create
literals:
- from=base

View File

@@ -1,6 +0,0 @@
namePrefix: p2-
configMapGenerator:
- name: com2
behavior: create
literals:
- from=base

View File

@@ -1,16 +0,0 @@
diff -u -N /tmp/noop/v1_ConfigMap_com1.yaml /tmp/transformed/v1_ConfigMap_com1.yaml
--- /tmp/noop/v1_ConfigMap_com1.yaml YYYY-MM-DD HH:MM:SS
+++ /tmp/transformed/v1_ConfigMap_com1.yaml YYYY-MM-DD HH:MM:SS
@@ -1,9 +1,11 @@
apiVersion: v1
data:
+ baz: qux
+ foo: bar
from: overlay
kind: ConfigMap
metadata:
annotations: {}
creationTimestamp: null
labels: {}
- name: p1-com1-cmdb776d5b
+ name: p1-com1-dhbbm922gd

View File

@@ -1,19 +0,0 @@
apiVersion: v1
data:
baz: qux
foo: bar
from: overlay
kind: ConfigMap
metadata:
annotations: {}
labels: {}
name: p1-com1-dhbbm922gd
---
apiVersion: v1
data:
from: overlay
kind: ConfigMap
metadata:
annotations: {}
labels: {}
name: p2-com2-c4b8md75k9

View File

@@ -1,9 +0,0 @@
bases:
- myapp/mycomponent
- myapp/mycomponent2
configMapGenerator:
- name: com1
behavior: merge
literals:
- foo=bar
- baz=qux

View File

@@ -1,7 +0,0 @@
bases:
- ../../../../base/myapp/mycomponent
configMapGenerator:
- name: com1
behavior: merge
literals:
- from=overlay

View File

@@ -1,7 +0,0 @@
bases:
- ../../../../base/myapp/mycomponent2
configMapGenerator:
- name: com2
behavior: merge
literals:
- from=overlay

View File

@@ -1,5 +0,0 @@
description: configmap generator overlay
args: []
filename: testdata/testcase-configmaps/overlay/dev
expectedStdout: testdata/testcase-configmaps/expected.yaml
expectedDiff: testdata/testcase-configmaps/expected.diff

View File

@@ -1,6 +0,0 @@
apiVersion: v1beta1
kind: Bee
metadata:
name: bee
spec:
action: fly

View File

@@ -1,9 +0,0 @@
crds:
- mycrd.json
resources:
- secret.yaml
- mykind.yaml
- bee.yaml
namePrefix: test-

View File

@@ -1,9 +0,0 @@
apiVersion: jingfang.example.com/v1beta1
kind: MyKind
metadata:
name: mykind
spec:
secretRef:
name: crdsecret
beeRef:
name: bee

View File

@@ -1,6 +0,0 @@
apiVersion: v1
kind: Secret
metadata:
name: crdsecret
data:
PATH: YmJiYmJiYmIK

View File

@@ -1,36 +0,0 @@
diff -u -N /tmp/noop/jingfang.example.com_v1beta1_MyKind_mykind.yaml /tmp/transformed/jingfang.example.com_v1beta1_MyKind_mykind.yaml
--- /tmp/noop/jingfang.example.com_v1beta1_MyKind_mykind.yaml YYYY-MM-DD HH:MM:SS
+++ /tmp/transformed/jingfang.example.com_v1beta1_MyKind_mykind.yaml YYYY-MM-DD HH:MM:SS
@@ -1,9 +1,9 @@
apiVersion: jingfang.example.com/v1beta1
kind: MyKind
metadata:
- name: mykind
+ name: test-mykind
spec:
beeRef:
- name: bee
+ name: test-bee
secretRef:
- name: crdsecret
+ name: test-crdsecret
diff -u -N /tmp/noop/v1beta1_Bee_bee.yaml /tmp/transformed/v1beta1_Bee_bee.yaml
--- /tmp/noop/v1beta1_Bee_bee.yaml YYYY-MM-DD HH:MM:SS
+++ /tmp/transformed/v1beta1_Bee_bee.yaml YYYY-MM-DD HH:MM:SS
@@ -1,6 +1,6 @@
apiVersion: v1beta1
kind: Bee
metadata:
- name: bee
+ name: test-bee
spec:
action: fly
diff -u -N /tmp/noop/v1_Secret_crdsecret.yaml /tmp/transformed/v1_Secret_crdsecret.yaml
--- /tmp/noop/v1_Secret_crdsecret.yaml YYYY-MM-DD HH:MM:SS
+++ /tmp/transformed/v1_Secret_crdsecret.yaml YYYY-MM-DD HH:MM:SS
@@ -3,4 +3,4 @@
PATH: YmJiYmJiYmIK
kind: Secret
metadata:
- name: crdsecret
+ name: test-crdsecret

View File

@@ -1,23 +0,0 @@
apiVersion: v1
data:
PATH: YmJiYmJiYmIK
kind: Secret
metadata:
name: test-crdsecret
---
apiVersion: jingfang.example.com/v1beta1
kind: MyKind
metadata:
name: test-mykind
spec:
beeRef:
name: test-bee
secretRef:
name: test-crdsecret
---
apiVersion: v1beta1
kind: Bee
metadata:
name: test-bee
spec:
action: fly

View File

@@ -1,5 +0,0 @@
description: name reference in CRDs
args: []
filename: testdata/testcase-crds/crd
expectedStdout: testdata/testcase-crds/expected.yaml
expectedDiff: testdata/testcase-crds/expected.diff

View File

@@ -1,33 +0,0 @@
apiVersion: v1
data:
altGreeting: Good Morning from default namespace!
enableRisky: "false"
kind: ConfigMap
metadata:
name: the-map-4959m5tm6c
---
apiVersion: v1
data:
altGreeting: Good Morning from non-default namespace!
enableRisky: "false"
kind: ConfigMap
metadata:
name: the-non-default-namespace-map-b6h49k7mt8
namespace: non-default
---
apiVersion: v1
data:
password.txt: dmVyeSRlY3JldA==
kind: Secret
metadata:
name: the-secret-cfbmct72tb
type: Opaque
---
apiVersion: v1
data:
password.txt: dmVyeSRlY3JldA==
kind: Secret
metadata:
name: the-non-default-namespace-secret-255294gd9d
namespace: non-default
type: Opaque

View File

@@ -1,19 +0,0 @@
configMapGenerator:
- name: the-non-default-namespace-map
namespace: non-default
literals:
- altGreeting=Good Morning from non-default namespace!
- enableRisky="false"
- name: the-map
literals:
- altGreeting=Good Morning from default namespace!
- enableRisky="false"
secretGenerator:
- name: the-non-default-namespace-secret
namespace: non-default
commands:
password.txt: "cat password.txt"
- name: the-secret
commands:
password.txt: "cat password.txt"

Some files were not shown because too many files have changed in this diff Show More