Compare commits
219 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4569a09d54 | ||
|
|
25d3ad7522 | ||
|
|
77e18724db | ||
|
|
12d1771bb3 | ||
|
|
a78aa22399 | ||
|
|
05a91893bf | ||
|
|
8d420ec3f7 | ||
|
|
838a766d12 | ||
|
|
50d79e4d3e | ||
|
|
4d2d450f6e | ||
|
|
fdc46fb0b1 | ||
|
|
92ac9b5a0e | ||
|
|
857a9df70f | ||
|
|
969f4f28fa | ||
|
|
58aa45c50a | ||
|
|
5715f4bab4 | ||
|
|
c8502c78f5 | ||
|
|
909de5c94a | ||
|
|
2eaeb83ec3 | ||
|
|
03b9c2a3a3 | ||
|
|
59b98727ec | ||
|
|
5851f96524 | ||
|
|
08be3f061e | ||
|
|
5906aaba19 | ||
|
|
4b6f180d0c | ||
|
|
7f22f187f8 | ||
|
|
fa3a64e352 | ||
|
|
82f2cf9124 | ||
|
|
276693cf0e | ||
|
|
0197c019cc | ||
|
|
9576a81787 | ||
|
|
ff4a1c0b4f | ||
|
|
7dd28b1fd9 | ||
|
|
b754557418 | ||
|
|
f305c0d791 | ||
|
|
3fdaa2e903 | ||
|
|
964c74fb46 | ||
|
|
f14988ff80 | ||
|
|
f1adbfdbff | ||
|
|
072bf992b0 | ||
|
|
2d0d09e178 | ||
|
|
564b0d6827 | ||
|
|
5edae84a9e | ||
|
|
9432671887 | ||
|
|
8fda0f87ab | ||
|
|
08bc8637c8 | ||
|
|
9645f397ef | ||
|
|
ed9f716361 | ||
|
|
9986b65326 | ||
|
|
94dab9ddc4 | ||
|
|
81f246ed60 | ||
|
|
30ed50eb27 | ||
|
|
4325401fe7 | ||
|
|
65af5c13f1 | ||
|
|
9674fd12b2 | ||
|
|
2377902a0b | ||
|
|
1dbde0b085 | ||
|
|
5920563bbd | ||
|
|
23201c27f0 | ||
|
|
d4c7131f8f | ||
|
|
d2b189874b | ||
|
|
98a38eb290 | ||
|
|
aa729229e2 | ||
|
|
afbc1b0401 | ||
|
|
3305be9589 | ||
|
|
36772aac89 | ||
|
|
7755d6cac2 | ||
|
|
6f82073d4b | ||
|
|
2a3f09a2f0 | ||
|
|
6392e6629f | ||
|
|
c25ed7f7bc | ||
|
|
918247d2cc | ||
|
|
0c260ef804 | ||
|
|
2a06a174e8 | ||
|
|
54e8a014bc | ||
|
|
5b67b580f2 | ||
|
|
6a67183ed7 | ||
|
|
a38befdaa1 | ||
|
|
0312cdf677 | ||
|
|
991ffbbdfc | ||
|
|
bbd29d9dc1 | ||
|
|
28953e03a0 | ||
|
|
37489ec2e9 | ||
|
|
636ab874eb | ||
|
|
90d16c2377 | ||
|
|
5d24dda28a | ||
|
|
dec5109e31 | ||
|
|
cc8690381c | ||
|
|
f5f95e3692 | ||
|
|
809d5b1fe2 | ||
|
|
38b4365ab3 | ||
|
|
d865300fdb | ||
|
|
e2677cdc8a | ||
|
|
ea00134776 | ||
|
|
ad3cd47c25 | ||
|
|
a1dcf3386b | ||
|
|
e7ecceb0c2 | ||
|
|
50c40eb80c | ||
|
|
398ceb0a92 | ||
|
|
b7be630924 | ||
|
|
f557841e54 | ||
|
|
9fc24634a2 | ||
|
|
0617a283a0 | ||
|
|
f616e30a38 | ||
|
|
50b197f329 | ||
|
|
6fd0330b80 | ||
|
|
8127b09d12 | ||
|
|
09ab2bb5c0 | ||
|
|
54ac4e73e7 | ||
|
|
d4ad7f80e0 | ||
|
|
623e21d1c0 | ||
|
|
0c88c43c67 | ||
|
|
c6d8bcb01b | ||
|
|
5285e6101f | ||
|
|
2fb69db685 | ||
|
|
730597b77e | ||
|
|
d488d9804d | ||
|
|
f98bc42cbb | ||
|
|
d7b9f64c5a | ||
|
|
785291af62 | ||
|
|
4f05482e00 | ||
|
|
3c3f85e623 | ||
|
|
40bb81142b | ||
|
|
5563f1529b | ||
|
|
a95b26182a | ||
|
|
934e37d781 | ||
|
|
40bad7783f | ||
|
|
44696d5fb9 | ||
|
|
9d268de5a0 | ||
|
|
46e8fd7065 | ||
|
|
ab0c9b4118 | ||
|
|
4e7610a44d | ||
|
|
37720765fc | ||
|
|
09e64e5991 | ||
|
|
e017d04a16 | ||
|
|
b6efde13eb | ||
|
|
f774172927 | ||
|
|
9459665c96 | ||
|
|
7fb4eaec60 | ||
|
|
211cda054e | ||
|
|
5f75564ff5 | ||
|
|
6b5569d5ed | ||
|
|
e20edaf41e | ||
|
|
d934a28caf | ||
|
|
e002b69ffa | ||
|
|
bdad67c3ef | ||
|
|
92864ba0f7 | ||
|
|
ba45b1366a | ||
|
|
d06620c74d | ||
|
|
4f3f76addd | ||
|
|
71b7e07b02 | ||
|
|
27f652ae8e | ||
|
|
5c8d82aed0 | ||
|
|
c994130005 | ||
|
|
ccd255f323 | ||
|
|
2b05d39067 | ||
|
|
de65503177 | ||
|
|
5a3c6553fc | ||
|
|
7c4ca21578 | ||
|
|
efcc167c35 | ||
|
|
1f8e56a210 | ||
|
|
fa8d6f08e2 | ||
|
|
900ac005dd | ||
|
|
a42c72b2e0 | ||
|
|
a82bd23f8f | ||
|
|
ea24b3795c | ||
|
|
95f568b857 | ||
|
|
708cd7ef17 | ||
|
|
ed7261ca01 | ||
|
|
8a27162d98 | ||
|
|
2e0e43cd76 | ||
|
|
ba2866645e | ||
|
|
fd2ee6034b | ||
|
|
86241dfd98 | ||
|
|
1a28f3a391 | ||
|
|
35344c163a | ||
|
|
526ba2df0c | ||
|
|
db15bc6372 | ||
|
|
c83b7019f5 | ||
|
|
08e2a1047b | ||
|
|
69d816d5ab | ||
|
|
ea2d2c9db1 | ||
|
|
c7ef8a7e4d | ||
|
|
9bda5cf092 | ||
|
|
815db033cb | ||
|
|
501e1a406d | ||
|
|
6e54814b6d | ||
|
|
df2407ad3f | ||
|
|
d0c6a824c8 | ||
|
|
57f3743be8 | ||
|
|
ef71cb478f | ||
|
|
9a5c0f5a25 | ||
|
|
0e2c71cd6f | ||
|
|
0b3ab31248 | ||
|
|
49f586af39 | ||
|
|
487a6ebee4 | ||
|
|
bee9490129 | ||
|
|
8afba0b56c | ||
|
|
16fa3403a4 | ||
|
|
bcb89ee908 | ||
|
|
37f03b4d01 | ||
|
|
bc144275b4 | ||
|
|
7086e4f974 | ||
|
|
4d111436aa | ||
|
|
1583486546 | ||
|
|
10b4c5db43 | ||
|
|
5a54c96203 | ||
|
|
4a5b82333b | ||
|
|
e3934ee69c | ||
|
|
24daa9e3dc | ||
|
|
d9b422cc54 | ||
|
|
d62fb53472 | ||
|
|
00e52095d6 | ||
|
|
9c6d31d70e | ||
|
|
a734b96560 | ||
|
|
8f3fe7d4bd | ||
|
|
5f950813c4 | ||
|
|
920f53853b | ||
|
|
aa88a0563b |
@@ -12,6 +12,8 @@ before_install:
|
|||||||
- go get -u golang.org/x/tools/cmd/goimports
|
- go get -u golang.org/x/tools/cmd/goimports
|
||||||
- go get -u github.com/onsi/ginkgo/ginkgo
|
- go get -u github.com/onsi/ginkgo/ginkgo
|
||||||
- go get -u github.com/monopole/mdrip
|
- go get -u github.com/monopole/mdrip
|
||||||
|
- go get -u github.com/fzipp/gocyclo
|
||||||
|
- go get -u gopkg.in/alecthomas/gometalinter.v2 && gometalinter.v2 --install
|
||||||
|
|
||||||
# Install must be set to prevent default `go get` to run.
|
# Install must be set to prevent default `go get` to run.
|
||||||
# The dependencies have already been vendored by `dep` so
|
# The dependencies have already been vendored by `dep` so
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
1. Fork the repo, develop and test your code.
|
1. Fork the repo, develop and test your code.
|
||||||
See the [github workflow guide].
|
See the [github workflow guide].
|
||||||
1. For _new features_, provide a markdown-based demo following
|
1. For _new features_, provide a markdown-based demo following
|
||||||
the pattern established in the [demos](demos) directory.
|
the pattern established in the [examples](examples) directory.
|
||||||
Run `bin/pre-commit.sh` to test your demo.
|
Run `bin/pre-commit.sh` to test your demo.
|
||||||
1. Submit a pull request.
|
1. Submit a pull request.
|
||||||
|
|
||||||
|
|||||||
184
Gopkg.lock
generated
@@ -1,12 +1,33 @@
|
|||||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||||
|
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/PuerkitoBio/purell"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "0bcb03f4b4d0a9428594752bd2a3b9aa0a9d4bd4"
|
||||||
|
version = "v1.1.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/PuerkitoBio/urlesc"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "de5bf2ad457846296e2031421a34e2568e304e35"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/davecgh/go-spew"
|
name = "github.com/davecgh/go-spew"
|
||||||
packages = ["spew"]
|
packages = ["spew"]
|
||||||
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
|
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
|
||||||
version = "v1.1.0"
|
version = "v1.1.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/emicklei/go-restful"
|
||||||
|
packages = [
|
||||||
|
".",
|
||||||
|
"log"
|
||||||
|
]
|
||||||
|
revision = "3658237ded108b4134956c1b3050349d93e7b895"
|
||||||
|
version = "v2.7.1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/evanphx/json-patch"
|
name = "github.com/evanphx/json-patch"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
@@ -19,9 +40,36 @@
|
|||||||
revision = "0ca9ea5df5451ffdf184b4428c902747c2c11cd7"
|
revision = "0ca9ea5df5451ffdf184b4428c902747c2c11cd7"
|
||||||
version = "v1.0.0"
|
version = "v1.0.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/go-openapi/jsonpointer"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "3a0015ad55fa9873f41605d3e8f28cd279c32ab2"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/go-openapi/jsonreference"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "3fb327e6747da3043567ee86abd02bb6376b6be2"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/go-openapi/spec"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "bcff419492eeeb01f76e77d2ebc714dc97b607f5"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/go-openapi/swag"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "811b1089cde9dad18d4d0c2d09fbdbf28dbd27a5"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/gogo/protobuf"
|
name = "github.com/gogo/protobuf"
|
||||||
packages = ["proto","sortkeys"]
|
packages = [
|
||||||
|
"proto",
|
||||||
|
"sortkeys"
|
||||||
|
]
|
||||||
revision = "1adfc126b41513cc696b209667c8656ea7aac67c"
|
revision = "1adfc126b41513cc696b209667c8656ea7aac67c"
|
||||||
version = "v1.0.0"
|
version = "v1.0.0"
|
||||||
|
|
||||||
@@ -33,7 +81,13 @@
|
|||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/golang/protobuf"
|
name = "github.com/golang/protobuf"
|
||||||
packages = ["proto","ptypes","ptypes/any","ptypes/duration","ptypes/timestamp"]
|
packages = [
|
||||||
|
"proto",
|
||||||
|
"ptypes",
|
||||||
|
"ptypes/any",
|
||||||
|
"ptypes/duration",
|
||||||
|
"ptypes/timestamp"
|
||||||
|
]
|
||||||
revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265"
|
revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265"
|
||||||
version = "v1.1.0"
|
version = "v1.1.0"
|
||||||
|
|
||||||
@@ -45,7 +99,11 @@
|
|||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/googleapis/gnostic"
|
name = "github.com/googleapis/gnostic"
|
||||||
packages = ["OpenAPIv2","compiler","extensions"]
|
packages = [
|
||||||
|
"OpenAPIv2",
|
||||||
|
"compiler",
|
||||||
|
"extensions"
|
||||||
|
]
|
||||||
revision = "ee43cbb60db7bd22502942cccbc39059117352ab"
|
revision = "ee43cbb60db7bd22502942cccbc39059117352ab"
|
||||||
version = "v0.1.0"
|
version = "v0.1.0"
|
||||||
|
|
||||||
@@ -61,6 +119,16 @@
|
|||||||
revision = "ca39e5af3ece67bbcda3d0f4f56a8e24d9f2dad4"
|
revision = "ca39e5af3ece67bbcda3d0f4f56a8e24d9f2dad4"
|
||||||
version = "1.1.3"
|
version = "1.1.3"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/mailru/easyjson"
|
||||||
|
packages = [
|
||||||
|
"buffer",
|
||||||
|
"jlexer",
|
||||||
|
"jwriter"
|
||||||
|
]
|
||||||
|
revision = "3fdea8d05856a0c8df22ed4bc71b3219245e4485"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/modern-go/concurrent"
|
name = "github.com/modern-go/concurrent"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
@@ -73,6 +141,12 @@
|
|||||||
revision = "1df9eeb2bb81f327b96228865c5687bc2194af3f"
|
revision = "1df9eeb2bb81f327b96228865c5687bc2194af3f"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/pkg/errors"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
|
||||||
|
version = "v0.8.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/spf13/cobra"
|
name = "github.com/spf13/cobra"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
@@ -88,12 +162,33 @@
|
|||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "golang.org/x/net"
|
name = "golang.org/x/net"
|
||||||
packages = ["http/httpguts","http2","http2/hpack","idna"]
|
packages = [
|
||||||
|
"http/httpguts",
|
||||||
|
"http2",
|
||||||
|
"http2/hpack",
|
||||||
|
"idna"
|
||||||
|
]
|
||||||
revision = "2491c5de3490fced2f6cff376127c667efeed857"
|
revision = "2491c5de3490fced2f6cff376127c667efeed857"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "golang.org/x/text"
|
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"]
|
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"
|
||||||
|
]
|
||||||
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
|
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
|
||||||
version = "v0.3.0"
|
version = "v0.3.0"
|
||||||
|
|
||||||
@@ -112,13 +207,75 @@
|
|||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "k8s.io/api"
|
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"]
|
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"
|
||||||
|
]
|
||||||
revision = "53d615ae3f440f957cb9989d989d597f047262d9"
|
revision = "53d615ae3f440f957cb9989d989d597f047262d9"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "k8s.io/apimachinery"
|
name = "k8s.io/apimachinery"
|
||||||
packages = ["pkg/api/resource","pkg/apis/meta/v1","pkg/apis/meta/v1/unstructured","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"]
|
packages = [
|
||||||
|
"pkg/api/resource",
|
||||||
|
"pkg/apis/meta/v1",
|
||||||
|
"pkg/apis/meta/v1/unstructured",
|
||||||
|
"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"
|
||||||
|
]
|
||||||
revision = "13b73596e4b63e03203e86f6d9c7bcc1b937c62f"
|
revision = "13b73596e4b63e03203e86f6d9c7bcc1b937c62f"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
@@ -130,18 +287,15 @@
|
|||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "k8s.io/kube-openapi"
|
name = "k8s.io/kube-openapi"
|
||||||
packages = ["pkg/util/proto"]
|
packages = [
|
||||||
|
"pkg/common",
|
||||||
|
"pkg/util/proto"
|
||||||
|
]
|
||||||
revision = "b3f03f55328800731ce03a164b80973014ecd455"
|
revision = "b3f03f55328800731ce03a164b80973014ecd455"
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
name = "k8s.io/utils"
|
|
||||||
packages = ["exec"]
|
|
||||||
revision = "258e2a2fa64568210fbd6267cf1d8fd87c3cb86e"
|
|
||||||
|
|
||||||
[solve-meta]
|
[solve-meta]
|
||||||
analyzer-name = "dep"
|
analyzer-name = "dep"
|
||||||
analyzer-version = 1
|
analyzer-version = 1
|
||||||
inputs-digest = "6e36daa1798b0dae7f45160f9275ca6bbe6c5667de7cd808d0057cbaf19fc55e"
|
inputs-digest = "74d444cd05ac6f803960180ec8ccfd5a4358077f7c79a5218a243554cb599274"
|
||||||
solver-name = "gps-cdcl"
|
solver-name = "gps-cdcl"
|
||||||
solver-version = 1
|
solver-version = 1
|
||||||
|
|||||||
@@ -52,3 +52,7 @@
|
|||||||
[[constraint]]
|
[[constraint]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "k8s.io/utils"
|
name = "k8s.io/utils"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/go-openapi/spec"
|
||||||
|
|||||||
27
INSTALL.md
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
[release page]: https://github.com/kubernetes-sigs/kustomize/releases
|
||||||
|
[Go]: https://golang.org
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Download a binary from the [release page].
|
||||||
|
|
||||||
|
Or try this to grab the latest official release
|
||||||
|
using the command line:
|
||||||
|
|
||||||
|
```
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
To install from head with [Go] v1.10.1 or higher:
|
||||||
|
|
||||||
|
<!-- @installkustomize @test -->
|
||||||
|
```
|
||||||
|
go get github.com/kubernetes-sigs/kustomize
|
||||||
|
```
|
||||||
166
README.md
@@ -1,71 +1,141 @@
|
|||||||
# kustomize
|
# kustomize
|
||||||
|
|
||||||
[applied]: docs/glossary.md#apply
|
`kustomize` lets you customize raw, template-free YAML
|
||||||
[base]: docs/glossary.md#base
|
files for multiple purposes, leaving the original YAML
|
||||||
[declarative configuration]: docs/glossary.md#declarative-application-management
|
untouched and usable as is.
|
||||||
[demo]: demos/README.md
|
|
||||||
[demos]: demos/README.md
|
|
||||||
[imageBase]: docs/base.jpg
|
|
||||||
[imageOverlay]: docs/overlay.jpg
|
|
||||||
[kubernetes style]: docs/glossary.md#kubernetes-style-object
|
|
||||||
[KEP]: https://github.com/kubernetes/community/blob/master/keps/sig-cli/0008-kustomize.md
|
|
||||||
[kustomization]: docs/glossary.md#kustomization
|
|
||||||
[overlay]: docs/glossary.md#overlay
|
|
||||||
[resources]: docs/glossary.md#resource
|
|
||||||
[sig-cli]: https://github.com/kubernetes/community/blob/master/sig-cli/README.md
|
|
||||||
[workflows]: docs/workflows.md
|
|
||||||
|
|
||||||
|
`kustomize` targets kubernetes; it understands and can
|
||||||
`kustomize` is a command line tool supporting
|
patch [kubernetes style] API objects. It's like
|
||||||
template-free customization of YAML (or JSON) objects
|
[`make`], in that what it does is declared in a file,
|
||||||
that conform to the [kubernetes style]. If your
|
and it's like [`sed`], in that it emits editted text.
|
||||||
objects have a `kind` and a `metadata` field,
|
|
||||||
`kustomize` can patch them to support configuration
|
|
||||||
sharing and re-use.
|
|
||||||
|
|
||||||
For more details, try a [demo].
|
|
||||||
|
|
||||||
[](https://travis-ci.org/kubernetes-sigs/kustomize)
|
[](https://travis-ci.org/kubernetes-sigs/kustomize)
|
||||||
[](https://goreportcard.com/report/github.com/kubernetes-sigs/kustomize)
|
[](https://goreportcard.com/report/github.com/kubernetes-sigs/kustomize)
|
||||||
|
|
||||||
|
**Installation**: Download a binary from the [release
|
||||||
## Installation
|
page], or see these [install] notes. Then try one of
|
||||||
|
the tested [examples].
|
||||||
This assumes [Go](https://golang.org/) (v1.10.1 or higher)
|
|
||||||
is installed and your `PATH` contains `$GOPATH/bin`:
|
|
||||||
|
|
||||||
<!-- @installkustomize @test -->
|
|
||||||
```
|
|
||||||
go get github.com/kubernetes-sigs/kustomize
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
#### 1) Make a base
|
|
||||||
|
|
||||||
A [base] configuration is a [kustomization] file listing a set of
|
### 1) Make a [kustomization] file
|
||||||
k8s [resources] - deployments, services, configmaps,
|
|
||||||
secrets that serve some common purpose.
|
In some directory containing your YAML [resource]
|
||||||
|
files (deployments, services, configmaps, etc.), create a
|
||||||
|
[kustomization] file.
|
||||||
|
|
||||||
|
This file should declare those resources, and any
|
||||||
|
customization to apply to them, e.g. _add a common
|
||||||
|
label_.
|
||||||
|
|
||||||
![base image][imageBase]
|
![base image][imageBase]
|
||||||
|
|
||||||
#### 2) Customize it with overlays
|
File structure:
|
||||||
|
|
||||||
An [overlay] customizes your base along different dimensions
|
> ```
|
||||||
for different purposes or different teams, e.g. for
|
> ~/someApp
|
||||||
_development, staging and production_.
|
> ├── deployment.yaml
|
||||||
|
> ├── kustomization.yaml
|
||||||
|
> └── service.yaml
|
||||||
|
> ```
|
||||||
|
|
||||||
|
The resources in this directory could be a fork of
|
||||||
|
someone else's configuration. If so, you can easily
|
||||||
|
rebase from the source material to capture
|
||||||
|
improvements, because you don't modify the resources
|
||||||
|
directly.
|
||||||
|
|
||||||
|
Generate customized YAML with:
|
||||||
|
|
||||||
|
```
|
||||||
|
kustomize build ~/someApp
|
||||||
|
```
|
||||||
|
|
||||||
|
The YAML can be directly [applied] to a cluster:
|
||||||
|
|
||||||
|
> ```
|
||||||
|
> kustomize build ~/someApp | kubectl apply -f -
|
||||||
|
> ```
|
||||||
|
|
||||||
|
|
||||||
|
### 2) Create [variants] using [overlays]
|
||||||
|
|
||||||
|
Manage traditional [variants] of a configuration - like
|
||||||
|
_development_, _staging_ and _production_ - using
|
||||||
|
[overlays] that modify a common [base].
|
||||||
|
|
||||||
![overlay image][imageOverlay]
|
![overlay image][imageOverlay]
|
||||||
|
|
||||||
#### 3) Run kustomize
|
File structure:
|
||||||
|
> ```
|
||||||
|
> ~/someApp
|
||||||
|
> ├── base
|
||||||
|
> │ ├── deployment.yaml
|
||||||
|
> │ ├── kustomization.yaml
|
||||||
|
> │ └── service.yaml
|
||||||
|
> └── overlays
|
||||||
|
> ├── development
|
||||||
|
> │ ├── cpu_count.yaml
|
||||||
|
> │ ├── kustomization.yaml
|
||||||
|
> │ └── replica_count.yaml
|
||||||
|
> └── production
|
||||||
|
> ├── cpu_count.yaml
|
||||||
|
> ├── kustomization.yaml
|
||||||
|
> └── replica_count.yaml
|
||||||
|
> ```
|
||||||
|
|
||||||
Run `kustomize` on your overlay. The result
|
Take the work from step (1) above, move it into a
|
||||||
is printed to `stdout` as a set of complete
|
`someApp` subdirectory called `base`, then
|
||||||
resources, ready to be [applied] to a cluster.
|
place overlays in a sibling directory.
|
||||||
See the [demos].
|
|
||||||
|
|
||||||
|
An overlay is just another kustomization, refering to
|
||||||
|
the base, and referring to patches to apply to that
|
||||||
|
base.
|
||||||
|
|
||||||
|
This arrangement makes it easy to manage your
|
||||||
|
configuration with `git`. The base could have files
|
||||||
|
from an upstream repository managed by someone else.
|
||||||
|
The overlays could be in a repository you own.
|
||||||
|
Arranging the repo clones as siblings on disk avoids
|
||||||
|
the need for git submodules (though that works fine, if
|
||||||
|
you are a submodule fan).
|
||||||
|
|
||||||
|
Generate YAML with
|
||||||
|
|
||||||
|
```sh
|
||||||
|
kustomize build ~/someApp/overlays/production
|
||||||
|
```
|
||||||
|
|
||||||
|
The YAML can be directly [applied] to a cluster:
|
||||||
|
|
||||||
|
> ```sh
|
||||||
|
> kustomize build ~/someApp/overlays/production | kubectl apply -f -
|
||||||
|
> ```
|
||||||
|
|
||||||
## About
|
## About
|
||||||
|
|
||||||
This project sponsored by [sig-cli] ([KEP]).
|
This tool is sponsored by [sig-cli] ([KEP]).
|
||||||
|
|
||||||
|
|
||||||
|
[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
|
||||||
|
[applied]: docs/glossary.md#apply
|
||||||
|
[base]: docs/glossary.md#base
|
||||||
|
[declarative configuration]: docs/glossary.md#declarative-application-management
|
||||||
|
[examples]: examples/README.md
|
||||||
|
[imageBase]: docs/base.jpg
|
||||||
|
[imageOverlay]: docs/overlay.jpg
|
||||||
|
[install]: INSTALL.md
|
||||||
|
[kubernetes style]: docs/glossary.md#kubernetes-style-object
|
||||||
|
[kustomization]: docs/glossary.md#kustomization
|
||||||
|
[overlay]: docs/glossary.md#overlay
|
||||||
|
[overlays]: docs/glossary.md#overlay
|
||||||
|
[release page]: https://github.com/kubernetes-sigs/kustomize/releases
|
||||||
|
[resource]: docs/glossary.md#resource
|
||||||
|
[resources]: docs/glossary.md#resource
|
||||||
|
[sig-cli]: https://github.com/kubernetes/community/blob/master/sig-cli/README.md
|
||||||
|
[variant]: docs/glossary.md#variant
|
||||||
|
[variants]: docs/glossary.md#variant
|
||||||
|
[workflows]: docs/workflows.md
|
||||||
|
|||||||
15
SECURITY_CONTACTS
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Defined below are the security contacts for this repo.
|
||||||
|
#
|
||||||
|
# They are the contact point for the Product Security Team to reach out
|
||||||
|
# to for triaging and handling of incoming issues.
|
||||||
|
#
|
||||||
|
# The below names agree to abide by the
|
||||||
|
# [Embargo Policy](https://github.com/kubernetes/sig-release/blob/master/security-release-process-documentation/security-release-process.md#embargo-policy)
|
||||||
|
# and will be removed and replaced if they violate that agreement.
|
||||||
|
#
|
||||||
|
# DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE
|
||||||
|
# INSTRUCTIONS AT https://kubernetes.io/security/
|
||||||
|
|
||||||
|
monopole
|
||||||
|
Liujingfang1
|
||||||
|
pwittrock
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
# Make sure, we run in the root of the repo and
|
# Make sure, we run in the root of the repo and
|
||||||
# therefore run the tests on all packages
|
# therefore run the tests on all packages
|
||||||
@@ -31,10 +32,35 @@ function testGoFmt {
|
|||||||
diff <(echo -n) <(go_dirs | xargs -0 gofmt -s -d -l)
|
diff <(echo -n) <(go_dirs | xargs -0 gofmt -s -d -l)
|
||||||
}
|
}
|
||||||
|
|
||||||
function testGoImports {
|
|
||||||
diff -u <(echo -n) <(go_dirs | xargs -0 goimports -l)
|
function testGoCyclo {
|
||||||
|
diff <(echo -n) <(go_dirs | xargs -0 gocyclo -over 15)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function testGoLint {
|
||||||
|
diff -u <(echo -n) <(go_dirs | xargs -0 golint --min_confidence 0.85 )
|
||||||
|
}
|
||||||
|
|
||||||
|
function testGoMetalinter {
|
||||||
|
diff -u <(echo -n) <(go_dirs | xargs -0 gometalinter.v2 --disable-all --deadline 5m \
|
||||||
|
--enable=misspell \
|
||||||
|
--enable=structcheck \
|
||||||
|
--enable=deadcode \
|
||||||
|
# Disabling 'goimports' because it reports hyphens in imported package \
|
||||||
|
# names as errors, and we have to vendor them in regardless. \
|
||||||
|
# --enable=goimports \
|
||||||
|
--enable=varcheck \
|
||||||
|
--enable=goconst \
|
||||||
|
--enable=unparam \
|
||||||
|
--enable=ineffassign \
|
||||||
|
--enable=nakedret \
|
||||||
|
--enable=interfacer \
|
||||||
|
--enable=misspell \
|
||||||
|
--line-length=170 --enable=lll \
|
||||||
|
--dupl-threshold=400 --enable=dupl)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function testGoVet {
|
function testGoVet {
|
||||||
go vet -all ./...
|
go vet -all ./...
|
||||||
}
|
}
|
||||||
@@ -43,14 +69,22 @@ function testGoTest {
|
|||||||
go test -v ./...
|
go test -v ./...
|
||||||
}
|
}
|
||||||
|
|
||||||
function testDemos {
|
function testExamples {
|
||||||
mdrip --mode test --label test README.md ./demos
|
mdrip --mode test --label test README.md ./examples
|
||||||
}
|
}
|
||||||
|
|
||||||
runTest testGoFmt
|
runTest testGoFmt
|
||||||
runTest testGoImports
|
runTest testGoMetalinter
|
||||||
|
runTest testGoLint
|
||||||
runTest testGoVet
|
runTest testGoVet
|
||||||
|
runTest testGoCyclo
|
||||||
runTest testGoTest
|
runTest testGoTest
|
||||||
runTest testDemos
|
runTest testExamples
|
||||||
|
|
||||||
|
if [ $rc -eq 0 ]; then
|
||||||
|
echo "SUCCESS!"
|
||||||
|
else
|
||||||
|
echo "FAILURE; exit code $rc"
|
||||||
|
fi
|
||||||
|
|
||||||
exit $rc
|
exit $rc
|
||||||
|
|||||||
@@ -1,13 +1,45 @@
|
|||||||
## Overview
|
[releases page]: https://github.com/kubernetes-sigs/kustomize/releases
|
||||||
|
[`container-builder-local`]: https://github.com/GoogleCloudPlatform/container-builder-local
|
||||||
|
[Google Container Builder]: https://cloud.google.com/container-builder
|
||||||
|
|
||||||
This directory contains scripts and configuration files for publishing a
|
Scripts and configuration files for publishing a
|
||||||
`kustomize` release on [release page](https://github.com/kubernetes-sigs/kustomize/releases)
|
`kustomize` release on the [releases page].
|
||||||
|
|
||||||
## Steps to run build a release locally
|
### Build a release locally
|
||||||
Install container-builder-local from [github](https://github.com/GoogleCloudPlatform/container-builder-local).
|
|
||||||
|
|
||||||
```sh
|
Install [`container-builder-local`], then run
|
||||||
container-builder-local --config=build/cloudbuild_local.yaml --dryrun=false --write-workspace=/tmp/w .
|
|
||||||
|
```
|
||||||
|
container-builder-local \
|
||||||
|
--config=build/cloudbuild_local.yaml \
|
||||||
|
--dryrun=false --write-workspace=/tmp/w .
|
||||||
```
|
```
|
||||||
|
|
||||||
You will find the build artifacts under `/tmp/w/dist` directory
|
to build artifacts under `/tmp/w/dist`.
|
||||||
|
|
||||||
|
### Publish a Release
|
||||||
|
|
||||||
|
Get on an up-to-date master branch:
|
||||||
|
```
|
||||||
|
git checkout master
|
||||||
|
git fetch upstream
|
||||||
|
git rebase upstream/master
|
||||||
|
```
|
||||||
|
|
||||||
|
Define the version (see [semver principles](https://semver.org)), e.g.:
|
||||||
|
```
|
||||||
|
version=v1.0.3
|
||||||
|
```
|
||||||
|
|
||||||
|
Tag the repo:
|
||||||
|
```
|
||||||
|
git tag -a $version -m "$version release"
|
||||||
|
```
|
||||||
|
|
||||||
|
Push the tag upstream:
|
||||||
|
```
|
||||||
|
git push upstream $version
|
||||||
|
```
|
||||||
|
|
||||||
|
The new tag will trigger a job in [Google Container
|
||||||
|
Builder] to put a new release on the [releases page].
|
||||||
|
|||||||
@@ -56,4 +56,4 @@ case $key in
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
/goreleaser release --config=build/goreleaser.yml --rm-dist --skip-validate ${SNAPSHOT}
|
/goreleaser release --config=build/goreleaser.yaml --rm-dist --skip-validate ${SNAPSHOT}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ project_name: kustomize
|
|||||||
builds:
|
builds:
|
||||||
- main: ./kustomize.go
|
- main: ./kustomize.go
|
||||||
binary: kustomize
|
binary: kustomize
|
||||||
ldflags: -s -X github.com/kubernetes-sigs/kustomize/version.kustomizeVersion={{.Version}} -X github.com/kubernetes-sigs/kustomize/version.gitCommit={{.Commit}} -X github.com/kubernetes-sigs/kustomize/version.buildDate={{.Date}}
|
ldflags: -s -X github.com/kubernetes-sigs/kustomize/pkg/commands.kustomizeVersion={{.Version}} -X github.com/kubernetes-sigs/kustomize/pkg/commands.gitCommit={{.Commit}} -X github.com/kubernetes-sigs/kustomize/pkg/commands.buildDate={{.Date}}
|
||||||
goos:
|
goos:
|
||||||
- darwin
|
- darwin
|
||||||
- linux
|
- linux
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
# This is an example goreleaser.yaml file with some sane defaults.
|
|
||||||
# Make sure to check the documentation at http://goreleaser.com
|
|
||||||
project_name: kustomize
|
|
||||||
builds:
|
|
||||||
- main: ./kustomize.go
|
|
||||||
binary: kustomize
|
|
||||||
ldflags: -s -X github.com/kubernetes-sigs/kustomize/version.kustomizeVersion={{.Version}} -X github.com/kubernetes-sigs/kustomize/version.gitCommit={{.Commit}} -X github.com/kubernetes-sigs/kustomize/version.buildDate={{.Date}}
|
|
||||||
goos:
|
|
||||||
- darwin
|
|
||||||
- linux
|
|
||||||
- windows
|
|
||||||
goarch:
|
|
||||||
- amd64
|
|
||||||
env:
|
|
||||||
- CGO_ENABLED=0
|
|
||||||
checksum:
|
|
||||||
name_template: 'checksums.txt'
|
|
||||||
archive:
|
|
||||||
format: binary
|
|
||||||
snapshot:
|
|
||||||
name_template: "master"
|
|
||||||
changelog:
|
|
||||||
sort: asc
|
|
||||||
filters:
|
|
||||||
exclude:
|
|
||||||
- '^docs:'
|
|
||||||
- '^test:'
|
|
||||||
- Merge pull request
|
|
||||||
- Merge branch
|
|
||||||
release:
|
|
||||||
github:
|
|
||||||
owner: kubernetes-sigs
|
|
||||||
name: kustomize
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
# Demos
|
|
||||||
|
|
||||||
These demos assume that `kustomize` is on your `$PATH`.
|
|
||||||
They are covered by pre-submit tests.
|
|
||||||
|
|
||||||
* [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.
|
|
||||||
|
|
||||||
* [mySql](mySql/README.md) - Create a MySQL production
|
|
||||||
configuration from scratch.
|
|
||||||
|
|
||||||
* [springboot](springboot/README.md) - Create a Spring Boot
|
|
||||||
application production configuration from scratch.
|
|
||||||
|
|
||||||
* [configGeneration](configGeneration.md) -
|
|
||||||
Mixing configuration data from different owners
|
|
||||||
(e.g. devops/SRE and developers).
|
|
||||||
|
|
||||||
* [breakfast](breakfast.md) - Customize breakfast for
|
|
||||||
Alice and Bob.
|
|
||||||
BIN
docs/base.jpg
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 40 KiB |
@@ -68,25 +68,17 @@ management in k8s.
|
|||||||
|
|
||||||
A _base_ is a [target] that some [overlay] modifies.
|
A _base_ is a [target] that some [overlay] modifies.
|
||||||
|
|
||||||
Any target, including an overlay, can be a base to
|
Any target, including an [overlay], can be a base to
|
||||||
another target.
|
another target.
|
||||||
|
|
||||||
A base has no knowledge of the overlays that refer to it.
|
A base has no knowledge of the overlays that refer to it.
|
||||||
|
|
||||||
A base is usable in isolation, i.e. one should
|
|
||||||
be able to [apply] a base to a cluster directly.
|
|
||||||
|
|
||||||
For simple [gitops] management, a base configuration
|
For simple [gitops] management, a base configuration
|
||||||
could be the _sole content of a git repository
|
could be the _sole content of a git repository
|
||||||
dedicated to that purpose_. Same with [overlays].
|
dedicated to that purpose_. Same with [overlays].
|
||||||
Changes in a repo could generate a build, test and
|
Changes in a repo could generate a build, test and
|
||||||
deploy cycle.
|
deploy cycle.
|
||||||
|
|
||||||
Some of the demos for [kustomize] will break from this
|
|
||||||
idiom and store all demo config files in directories
|
|
||||||
_next_ to the `kustomize` code so that the code and
|
|
||||||
demos can be more easily maintained by the same group
|
|
||||||
of people.
|
|
||||||
|
|
||||||
## bespoke configuration
|
## bespoke configuration
|
||||||
|
|
||||||
@@ -162,10 +154,10 @@ It's often abbreviated as _k8s_.
|
|||||||
|
|
||||||
An object, expressed in a YAML or JSON file, with the
|
An object, expressed in a YAML or JSON file, with the
|
||||||
[fields required] by kubernetes. Basically just a
|
[fields required] by kubernetes. Basically just a
|
||||||
`kind` field to identify the type, a `metadata/name`
|
_kind_ field to identify the type, a _metadata/name_
|
||||||
field to identify the variant, and an `apiVersion`
|
field to identify the particular instance, and an
|
||||||
field to identify the version (if there's more than one
|
_apiVersion_ field to identify the version (if there's
|
||||||
version).
|
more than one version).
|
||||||
|
|
||||||
## kustomize
|
## kustomize
|
||||||
|
|
||||||
@@ -210,19 +202,24 @@ An _overlay_ is a [target] that modifies (and thus
|
|||||||
depends on) another target.
|
depends on) another target.
|
||||||
|
|
||||||
The [kustomization] in an overlay refers to (via file
|
The [kustomization] in an overlay refers to (via file
|
||||||
path, URI or other method) _some other kustomization_,
|
path, URI or other method) some other kustomization,
|
||||||
known as its [base].
|
known as its [base].
|
||||||
|
|
||||||
An overlay is unusable without its base.
|
An overlay is unusable without its base.
|
||||||
|
|
||||||
An overlay supports the typical notion of a
|
An overlay may act as a base to another overlay.
|
||||||
_development_, _QA_, _staging_ and _production_
|
|
||||||
environment variants.
|
|
||||||
|
|
||||||
The configuration of these environments is specified in
|
Overlays make the most sense when there is _more than
|
||||||
individual overlays (one per environment) that all
|
one_, because they create different [variants] of a
|
||||||
refer to a common base that holds common configuration.
|
common base - e.g. _development_, _QA_, _staging_ and
|
||||||
One configures the cluster like this:
|
_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.
|
||||||
|
|
||||||
|
One configures a cluster like this:
|
||||||
|
|
||||||
> ```
|
> ```
|
||||||
> kustomize build someapp/overlays/staging |\
|
> kustomize build someapp/overlays/staging |\
|
||||||
@@ -232,10 +229,9 @@ One configures the cluster like this:
|
|||||||
> kubectl apply -f -
|
> kubectl apply -f -
|
||||||
> ```
|
> ```
|
||||||
|
|
||||||
Usage of the base is implicit (the overlay's kustomization
|
Usage of the base is implicit - the overlay's
|
||||||
points to the base).
|
kustomization points to the base.
|
||||||
|
|
||||||
An overlay may act as a base to another overlay.
|
|
||||||
|
|
||||||
## package
|
## package
|
||||||
|
|
||||||
@@ -267,7 +263,7 @@ configmap.
|
|||||||
|
|
||||||
More generally, a resource can be any correct YAML file
|
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)
|
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.
|
with a _kind_ and a _metadata/name_ field.
|
||||||
|
|
||||||
|
|
||||||
A _resource_ in the content of a REST-ful API is the
|
A _resource_ in the content of a REST-ful API is the
|
||||||
@@ -290,8 +286,7 @@ The _target_ is the argument to `kustomize build`, e.g.:
|
|||||||
> ```
|
> ```
|
||||||
|
|
||||||
`$target` must be a path to a directory that
|
`$target` must be a path to a directory that
|
||||||
immediately contains a file called
|
immediately contains a [kustomization].
|
||||||
`kustomization.yaml` (i.e. a [kustomization]).
|
|
||||||
|
|
||||||
The target contains, or refers to, all the information
|
The target contains, or refers to, all the information
|
||||||
needed to create customized resources to send to the
|
needed to create customized resources to send to the
|
||||||
@@ -304,15 +299,15 @@ A target is a [base] or an [overlay].
|
|||||||
A _variant_ is the outcome, in a cluster, of applying
|
A _variant_ is the outcome, in a cluster, of applying
|
||||||
an [overlay] to a [base].
|
an [overlay] to a [base].
|
||||||
|
|
||||||
> E.g., a _staging_ and _production_ overlay both modify some
|
E.g., a _staging_ and _production_ overlay both modify
|
||||||
> common base to create distinct variants.
|
some common base to create distinct variants.
|
||||||
>
|
|
||||||
> The _staging_ variant is the set of resources
|
The _staging_ variant is the set of resources exposed
|
||||||
> exposed to quality assurance testing, or to some
|
to quality assurance testing, or to some external users
|
||||||
> external users who'd like to see what the next
|
who'd like to see what the next version of production
|
||||||
> version of production will look like.
|
will look like.
|
||||||
>
|
|
||||||
> The _production_ variant is the set of resources
|
The _production_ variant is the set of resources
|
||||||
> exposed to production traffic, and thus may employ
|
exposed to production traffic, and thus may employ
|
||||||
> deployments with a large number of replicas and higher
|
deployments with a large number of replicas and higher
|
||||||
> cpu and memory requests.
|
cpu and memory requests.
|
||||||
|
|||||||
@@ -32,6 +32,8 @@
|
|||||||
# visible in configuration reviews.
|
# visible in configuration reviews.
|
||||||
# ----------------------------------------------------
|
# ----------------------------------------------------
|
||||||
|
|
||||||
|
# Adds namespace to all resources.
|
||||||
|
namespace: my-namespace
|
||||||
|
|
||||||
# Value of this field is prepended to the
|
# Value of this field is prepended to the
|
||||||
# names of all resources, e.g. a deployment named
|
# names of all resources, e.g. a deployment named
|
||||||
@@ -80,6 +82,11 @@ secretGenerator:
|
|||||||
tls.crt: "cat secret/tls.cert"
|
tls.crt: "cat secret/tls.cert"
|
||||||
tls.key: "cat secret/tls.key"
|
tls.key: "cat secret/tls.key"
|
||||||
type: "kubernetes.io/tls"
|
type: "kubernetes.io/tls"
|
||||||
|
- name: downloaded_secret
|
||||||
|
commands:
|
||||||
|
username: "curl -s https://path/to/secrets/username.yaml"
|
||||||
|
password: "curl -s https://path/to/secrets/password.yaml"
|
||||||
|
type: Opaque
|
||||||
|
|
||||||
# Each entry in this list should resolve to a directory
|
# Each entry in this list should resolve to a directory
|
||||||
# containing a kustomization file, else the
|
# containing a kustomization file, else the
|
||||||
@@ -113,3 +120,83 @@ patches:
|
|||||||
- service_port_8888.yaml
|
- service_port_8888.yaml
|
||||||
- deployment_increase_replicas.yaml
|
- deployment_increase_replicas.yaml
|
||||||
- deployment_increase_memory.yaml
|
- deployment_increase_memory.yaml
|
||||||
|
|
||||||
|
|
||||||
|
# Each entry in this list should be a relative path to
|
||||||
|
# a file for custom resource definition(CRD).
|
||||||
|
#
|
||||||
|
# The presence of this field is to allow kustomize be
|
||||||
|
# aware of CRDs and apply proper
|
||||||
|
# transformation for any objects in those types.
|
||||||
|
#
|
||||||
|
# Typical use case: A CRD object refers to a ConfigMap object.
|
||||||
|
# In kustomization, the ConfigMap object name may change by adding namePrefix or hashing
|
||||||
|
# The name reference for this ConfigMap object in CRD object need to be
|
||||||
|
# updated with namePrefix or hashing in the same way.
|
||||||
|
crds:
|
||||||
|
- crds/typeA.yaml
|
||||||
|
- crds/typeB.yaml
|
||||||
|
|
||||||
|
# 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:
|
||||||
|
# ```
|
||||||
|
# containers:
|
||||||
|
# - image: myimage
|
||||||
|
# command: ["start", "--host", "$(MY_SERVICE_NAME)"]
|
||||||
|
# env:
|
||||||
|
# - name: SECRET_TOKEN
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
# Not every location of a variable is supported. To see a complete list of locations
|
||||||
|
# see the file [refvars.go](https://github.com/kubernetes-sigs/kustomize/blob/master/pkg/transformers/refvars.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:
|
||||||
|
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
|
||||||
|
|
||||||
|
# ImageTags modify the tags for images without creating patches.
|
||||||
|
# E.g. Given this fragment of a Deployment:
|
||||||
|
# ```
|
||||||
|
# containers:
|
||||||
|
# - name: myapp
|
||||||
|
# image: mycontainerregistry/myimage:v0
|
||||||
|
# - name: nginxapp
|
||||||
|
# image: nginx:1.7.9
|
||||||
|
#```
|
||||||
|
# one can change the tag of myimage to v1 and the tag of nginx to 1.8.0 with the following:
|
||||||
|
imageTags:
|
||||||
|
- name: mycontainerregistry/myimage
|
||||||
|
newTag: v1
|
||||||
|
- name: nginx
|
||||||
|
newTag: 1.8.0
|
||||||
|
|||||||
BIN
docs/overlay.jpg
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
@@ -10,7 +10,7 @@
|
|||||||
[patch]: glossary.md#patch
|
[patch]: glossary.md#patch
|
||||||
[patches]: glossary.md#patch
|
[patches]: glossary.md#patch
|
||||||
[rebase]: https://git-scm.com/docs/git-rebase
|
[rebase]: https://git-scm.com/docs/git-rebase
|
||||||
[resources]: glossary.md#resources
|
[resources]: glossary.md#resource
|
||||||
[workflowBespoke]: workflowBespoke.jpg
|
[workflowBespoke]: workflowBespoke.jpg
|
||||||
[workflowOts]: workflowOts.jpg
|
[workflowOts]: workflowOts.jpg
|
||||||
|
|
||||||
@@ -64,8 +64,8 @@ specified in the base.
|
|||||||
Run kustomize, and pipe the output to [apply].
|
Run kustomize, and pipe the output to [apply].
|
||||||
|
|
||||||
> ```
|
> ```
|
||||||
> kustomize ~/ldap/overlays/staging | kubectl apply -f -
|
> kustomize build ~/ldap/overlays/staging | kubectl apply -f -
|
||||||
> kustomize ~/ldap/overlays/production | kubectl apply -f -
|
> kustomize build ~/ldap/overlays/production | kubectl apply -f -
|
||||||
> ```
|
> ```
|
||||||
|
|
||||||
|
|
||||||
@@ -85,13 +85,13 @@ is periodically consulted for updates.
|
|||||||
|
|
||||||
The [base] directory is maintained in a repo whose
|
The [base] directory is maintained in a repo whose
|
||||||
upstream is an [OTS] configuration, in this case
|
upstream is an [OTS] configuration, in this case
|
||||||
https://github.com/kinflate/ldap.
|
some user's `ldap` repo:
|
||||||
|
|
||||||
> ```
|
> ```
|
||||||
> mkdir ~/ldap
|
> mkdir ~/ldap
|
||||||
> git clone https://github.com/$USER/ldap ~/ldap/base
|
> git clone https://github.com/$USER/ldap ~/ldap/base
|
||||||
> cd ~/ldap/base
|
> cd ~/ldap/base
|
||||||
> git remote add upstream git@github.com:kustomize/ldap
|
> git remote add upstream git@github.com:$USER/ldap
|
||||||
> ```
|
> ```
|
||||||
|
|
||||||
#### 3) create [overlays]
|
#### 3) create [overlays]
|
||||||
@@ -107,17 +107,19 @@ The [overlays] are siblings to each other and to the
|
|||||||
> mkdir -p ~/ldap/overlays/production
|
> mkdir -p ~/ldap/overlays/production
|
||||||
> ```
|
> ```
|
||||||
|
|
||||||
|
The user can maintain the `overlays` directory in a
|
||||||
|
distinct repository.
|
||||||
|
|
||||||
#### 4) bring up [variants]
|
#### 4) bring up [variants]
|
||||||
|
|
||||||
> ```
|
> ```
|
||||||
> kustomize ~/ldap/overlays/staging | kubectl apply -f -
|
> kustomize build ~/ldap/overlays/staging | kubectl apply -f -
|
||||||
> kustomize ~/ldap/overlays/production | kubectl apply -f -
|
> kustomize build ~/ldap/overlays/production | kubectl apply -f -
|
||||||
> ```
|
> ```
|
||||||
|
|
||||||
#### 5) (optionally) capture changes from upstream
|
#### 5) (optionally) capture changes from upstream
|
||||||
|
|
||||||
The user can optionally [rebase] their [base] to
|
The user can periodically [rebase] their [base] to
|
||||||
capture changes made in the upstream repository.
|
capture changes made in the upstream repository.
|
||||||
|
|
||||||
> ```
|
> ```
|
||||||
|
|||||||
38
examples/README.md
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# Examples
|
||||||
|
|
||||||
|
These examples assume that `kustomize` is on your `$PATH`.
|
||||||
|
|
||||||
|
They are covered by [pre-commit](../bin/pre-commit.sh)
|
||||||
|
tests, and should work with HEAD
|
||||||
|
|
||||||
|
<!-- @installkustomize @test -->
|
||||||
|
```
|
||||||
|
go get github.com/kubernetes-sigs/kustomize
|
||||||
|
```
|
||||||
|
|
||||||
|
* [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.
|
||||||
|
|
||||||
|
* [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) -
|
||||||
|
Mixing configuration data from different owners
|
||||||
|
(e.g. devops/SRE and developers).
|
||||||
|
|
||||||
|
* [configGenerations](configGeneration.md) -
|
||||||
|
Rolling update when ConfigMapGenerator changes
|
||||||
|
|
||||||
|
* [breakfast](breakfast.md) - Customize breakfast for
|
||||||
|
Alice and Bob.
|
||||||
|
|
||||||
|
* [container args](wordpress/README.md) - Injecting k8s runtime data into container arguments (e.g. to point wordpress to a SQL service).
|
||||||
|
|
||||||
|
* [image tags](imageTags.md) - Updating image tags without applying a patch.
|
||||||
@@ -113,7 +113,7 @@ A cluster environment is created by
|
|||||||
running `kustomize build` on a [target] that happens to
|
running `kustomize build` on a [target] that happens to
|
||||||
be an [overlay].
|
be an [overlay].
|
||||||
|
|
||||||
[helloworld]: helloworld.md
|
[helloworld]: helloWorld/README.md
|
||||||
|
|
||||||
The following example will do that, but will focus on
|
The following example will do that, but will focus on
|
||||||
configMap construction, and not worry about how to
|
configMap construction, and not worry about how to
|
||||||
208
examples/configGeneration.md
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
[patch]: ../../docs/glossary.md#patch
|
||||||
|
[resource]: ../../docs/glossary.md#resource
|
||||||
|
[variant]: ../../docs/glossary.md#variant
|
||||||
|
|
||||||
|
## ConfigMap generation and rolling updates
|
||||||
|
|
||||||
|
Kustomize provides two ways of adding ConfigMap in one `kustomization`, either by declaring ConfigMap as a [resource] or declaring ConfigMap from a ConfigMapGenerator. The formats inside `kustomization.yaml` are
|
||||||
|
|
||||||
|
> ```
|
||||||
|
> # declare ConfigMap as a resource
|
||||||
|
> resources:
|
||||||
|
> - configmap.yaml
|
||||||
|
>
|
||||||
|
> # declare ConfigMap from a ConfigMapGenerator
|
||||||
|
> configMapGenerator:
|
||||||
|
> - name: a-configmap
|
||||||
|
> files:
|
||||||
|
> - configs/configfile
|
||||||
|
> - configs/another_configfile
|
||||||
|
> ```
|
||||||
|
|
||||||
|
The ConfigMaps declared as [resource] are treated the same way as other resources. Kustomize doesn't append any hash to the ConfigMap name. The ConfigMap declared from a ConfigMapGenerator is treated differently. A hash is appended to the name and any change in the ConfigMap will trigger a rolling update.
|
||||||
|
|
||||||
|
In this demo, the same [hello_world](helloWorld/README.md) is used while the ConfigMap declared as [resources] is replaced by a ConfigMap declared from a ConfigmapGenerator. The change in this ConfigMap will result in a hash change and a rolling update.
|
||||||
|
|
||||||
|
### Establish base and staging
|
||||||
|
|
||||||
|
Establish the base with a configMapGenerator
|
||||||
|
<!-- @establishBase @test -->
|
||||||
|
```
|
||||||
|
DEMO_HOME=$(mktemp -d)
|
||||||
|
|
||||||
|
BASE=$DEMO_HOME/base
|
||||||
|
mkdir -p $BASE
|
||||||
|
|
||||||
|
curl -s -o "$BASE/#1.yaml" "https://raw.githubusercontent.com\
|
||||||
|
/kubernetes-sigs/kustomize\
|
||||||
|
/master/examples/helloWorld\
|
||||||
|
/{deployment,service}.yaml"
|
||||||
|
|
||||||
|
cat <<'EOF' >$BASE/kustomization.yaml
|
||||||
|
commonLabels:
|
||||||
|
app: hello
|
||||||
|
resources:
|
||||||
|
- deployment.yaml
|
||||||
|
- service.yaml
|
||||||
|
configMapGenerator:
|
||||||
|
- name: the-map
|
||||||
|
literals:
|
||||||
|
- altGreeting=Good Morning!
|
||||||
|
- enableRisky="false"
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
Establish the staging with a patch applied to the ConfigMap
|
||||||
|
<!-- @establishStaging @test -->
|
||||||
|
```
|
||||||
|
OVERLAYS=$DEMO_HOME/overlays
|
||||||
|
mkdir -p $OVERLAYS/staging
|
||||||
|
|
||||||
|
cat <<'EOF' >$OVERLAYS/staging/kustomization.yaml
|
||||||
|
namePrefix: staging-
|
||||||
|
commonLabels:
|
||||||
|
variant: staging
|
||||||
|
org: acmeCorporation
|
||||||
|
commonAnnotations:
|
||||||
|
note: Hello, I am staging!
|
||||||
|
bases:
|
||||||
|
- ../../base
|
||||||
|
patches:
|
||||||
|
- map.yaml
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat <<EOF >$OVERLAYS/staging/map.yaml
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: the-map
|
||||||
|
data:
|
||||||
|
altGreeting: "Have a pineapple!"
|
||||||
|
enableRisky: "true"
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
### Review
|
||||||
|
|
||||||
|
The _hello-world_ deployment running in this cluster is
|
||||||
|
configured with data from a configMap.
|
||||||
|
|
||||||
|
The deployment refers to this map by name:
|
||||||
|
|
||||||
|
|
||||||
|
<!-- @showDeployment @test -->
|
||||||
|
```
|
||||||
|
grep -C 2 configMapKeyRef $BASE/deployment.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
Changing the data held by a live configMap in a cluster
|
||||||
|
is considered bad practice. Deployments have no means
|
||||||
|
to know that the configMaps they refer to have
|
||||||
|
changed, so such updates have no effect.
|
||||||
|
|
||||||
|
The recommended way to change a deployment's
|
||||||
|
configuration is to
|
||||||
|
|
||||||
|
1. create a new configMap with a new name,
|
||||||
|
1. patch the _deployment_, modifying the name value of
|
||||||
|
the appropriate `configMapKeyRef` field.
|
||||||
|
|
||||||
|
This latter change initiates rolling update to the pods
|
||||||
|
in the deployment. The older configMap, when no longer
|
||||||
|
referenced by any other resource, is eventually garbage
|
||||||
|
collected.
|
||||||
|
|
||||||
|
### How this works with kustomize
|
||||||
|
|
||||||
|
The _staging_ [variant] here has a configMap [patch]:
|
||||||
|
|
||||||
|
<!-- @showMapPatch @test -->
|
||||||
|
```
|
||||||
|
cat $OVERLAYS/staging/map.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
This patch is by definition a named but not necessarily
|
||||||
|
complete resource spec intended to modify a complete
|
||||||
|
resource spec.
|
||||||
|
|
||||||
|
The ConfigMap it modifies is declared from a configMapGenerator.
|
||||||
|
|
||||||
|
<!-- @showMapBase @test -->
|
||||||
|
```
|
||||||
|
grep -C 4 configMapGenerator $BASE/kustomization.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
For a patch to work, the names in the `metadata/name`
|
||||||
|
fields must match.
|
||||||
|
|
||||||
|
However, the name values specified in the file are
|
||||||
|
_not_ what gets used in the cluster. By design,
|
||||||
|
kustomize modifies names of ConfigMaps declared from ConfigMapGenerator. To see the names
|
||||||
|
ultimately used in the cluster, just run kustomize:
|
||||||
|
|
||||||
|
<!-- @grepStagingName @test -->
|
||||||
|
```
|
||||||
|
kustomize build $OVERLAYS/staging |\
|
||||||
|
grep -B 8 -A 1 staging-the-map
|
||||||
|
```
|
||||||
|
|
||||||
|
The configMap name is prefixed by _staging-_, per the
|
||||||
|
`namePrefix` field in
|
||||||
|
`$OVERLAYS/staging/kustomization.yaml`.
|
||||||
|
|
||||||
|
The suffix to the configMap name is generated from a
|
||||||
|
hash of the maps content - in this case the name suffix
|
||||||
|
is _hhhhkfmgmk_:
|
||||||
|
|
||||||
|
<!-- @grepStagingHash @test -->
|
||||||
|
```
|
||||||
|
kustomize build $OVERLAYS/staging | grep hhhhkfmgmk
|
||||||
|
```
|
||||||
|
|
||||||
|
Now modify the map patch, to change the greeting
|
||||||
|
the server will use:
|
||||||
|
|
||||||
|
<!-- @changeMap @test -->
|
||||||
|
```
|
||||||
|
sed -i 's/pineapple/kiwi/' $OVERLAYS/staging/map.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
See the new greeting:
|
||||||
|
|
||||||
|
```
|
||||||
|
kustomize build $OVERLAYS/staging |\
|
||||||
|
grep -B 2 -A 3 kiwi
|
||||||
|
```
|
||||||
|
|
||||||
|
Run kustomize again to see the new configMap names:
|
||||||
|
|
||||||
|
<!-- @grepStagingName @test -->
|
||||||
|
```
|
||||||
|
kustomize build $OVERLAYS/staging |\
|
||||||
|
grep -B 8 -A 1 staging-the-map
|
||||||
|
```
|
||||||
|
|
||||||
|
Confirm that the change in configMap content resulted
|
||||||
|
in three new names ending in _khk45ktkd9_ - one in the
|
||||||
|
configMap name itself, and two in the deployment that
|
||||||
|
uses the map:
|
||||||
|
|
||||||
|
<!-- @countHashes @test -->
|
||||||
|
```
|
||||||
|
test 3 == \
|
||||||
|
$(kustomize build $OVERLAYS/staging | grep khk45ktkd9 | wc -l); \
|
||||||
|
echo $?
|
||||||
|
```
|
||||||
|
|
||||||
|
Applying these resources to the cluster will result in
|
||||||
|
a rolling update of the deployments pods, retargetting
|
||||||
|
them from the _hhhhkfmgmk_ maps to the _khk45ktkd9_
|
||||||
|
maps. The system will later garbage collect the
|
||||||
|
unused maps.
|
||||||
|
|
||||||
|
## Rollback
|
||||||
|
|
||||||
|
To rollback, one would undo whatever edits were made to
|
||||||
|
the configuation in source control, then rerun kustomize
|
||||||
|
on the reverted configuration and apply it to the
|
||||||
|
cluster.
|
||||||
@@ -51,7 +51,7 @@ mkdir -p $BASE
|
|||||||
|
|
||||||
curl -s -o "$BASE/#1.yaml" "https://raw.githubusercontent.com\
|
curl -s -o "$BASE/#1.yaml" "https://raw.githubusercontent.com\
|
||||||
/kubernetes-sigs/kustomize\
|
/kubernetes-sigs/kustomize\
|
||||||
/master/demos/helloWorld\
|
/master/examples/helloWorld\
|
||||||
/{configMap,deployment,kustomization,service}.yaml"
|
/{configMap,deployment,kustomization,service}.yaml"
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -112,6 +112,12 @@ sed -i 's/app: hello/app: my-hello/' \
|
|||||||
$BASE/kustomization.yaml
|
$BASE/kustomization.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
|
On a Mac, use:
|
||||||
|
```
|
||||||
|
sed -i '' $pattern $file
|
||||||
|
```
|
||||||
|
to get in-place editing.
|
||||||
|
|
||||||
See the effect:
|
See the effect:
|
||||||
<!-- @checkLabel @test -->
|
<!-- @checkLabel @test -->
|
||||||
```
|
```
|
||||||
@@ -309,122 +315,3 @@ To deploy, pipe the above commands to kubectl apply:
|
|||||||
> kustomize build $OVERLAYS/production |\
|
> kustomize build $OVERLAYS/production |\
|
||||||
> kubectl apply -f -
|
> kubectl apply -f -
|
||||||
> ```
|
> ```
|
||||||
|
|
||||||
## Rolling updates
|
|
||||||
|
|
||||||
### Review
|
|
||||||
|
|
||||||
The _hello-world_ deployment running in this cluster is
|
|
||||||
configured with data from a configMap.
|
|
||||||
|
|
||||||
The deployment refers to this map by name:
|
|
||||||
|
|
||||||
|
|
||||||
<!-- @showDeployment @test -->
|
|
||||||
```
|
|
||||||
grep -C 2 configMapKeyRef $DEMO_HOME/base/deployment.yaml
|
|
||||||
```
|
|
||||||
|
|
||||||
Changing the data held by a live configMap in a cluster
|
|
||||||
is considered bad practice. Deployments have no means
|
|
||||||
to know that the configMaps they refer to have
|
|
||||||
changed, so such updates have no effect.
|
|
||||||
|
|
||||||
The recommended way to change a deployment's
|
|
||||||
configuration is to
|
|
||||||
|
|
||||||
1. create a new configMap with a new name,
|
|
||||||
1. patch the _deployment_, modifying the name value of
|
|
||||||
the appropriate `configMapKeyRef` field.
|
|
||||||
|
|
||||||
This latter change initiates rolling update to the pods
|
|
||||||
in the deployment. The older configMap, when no longer
|
|
||||||
referenced by any other resource, is eventually garbage
|
|
||||||
collected.
|
|
||||||
|
|
||||||
### How this works with kustomize
|
|
||||||
|
|
||||||
The _staging_ [variant] here has a configMap [patch]:
|
|
||||||
|
|
||||||
<!-- @showMapPatch @test -->
|
|
||||||
```
|
|
||||||
cat $OVERLAYS/staging/map.yaml
|
|
||||||
```
|
|
||||||
|
|
||||||
This patch is by definition a named but not necessarily
|
|
||||||
complete resource spec intended to modify a complete
|
|
||||||
resource spec.
|
|
||||||
|
|
||||||
The resource it modifies is here:
|
|
||||||
|
|
||||||
<!-- @showMapBase @test -->
|
|
||||||
```
|
|
||||||
cat $DEMO_HOME/base/configMap.yaml
|
|
||||||
```
|
|
||||||
|
|
||||||
For a patch to work, the names in the `metadata/name`
|
|
||||||
fields must match.
|
|
||||||
|
|
||||||
However, the name values specified in the file are
|
|
||||||
_not_ what gets used in the cluster. By design,
|
|
||||||
kustomize modifies these names. To see the names
|
|
||||||
ultimately used in the cluster, just run kustomize:
|
|
||||||
|
|
||||||
<!-- @grepStagingName @test -->
|
|
||||||
```
|
|
||||||
kustomize build $OVERLAYS/staging |\
|
|
||||||
grep -B 8 -A 1 staging-the-map
|
|
||||||
```
|
|
||||||
|
|
||||||
The configMap name is prefixed by _staging-_, per the
|
|
||||||
`namePrefix` field in
|
|
||||||
`$OVERLAYS/staging/kustomization.yaml`.
|
|
||||||
|
|
||||||
The suffix to the configMap name is generated from a
|
|
||||||
hash of the maps content - in this case the name suffix
|
|
||||||
is _hhhhkfmgmk_:
|
|
||||||
|
|
||||||
<!-- @grepStagingHash @test -->
|
|
||||||
```
|
|
||||||
kustomize build $OVERLAYS/staging | grep hhhhkfmgmk
|
|
||||||
```
|
|
||||||
|
|
||||||
Now modify the map patch, to change the greeting
|
|
||||||
the server will use:
|
|
||||||
|
|
||||||
<!-- @changeMap @test -->
|
|
||||||
```
|
|
||||||
sed -i 's/pineapple/kiwi/' $OVERLAYS/staging/map.yaml
|
|
||||||
```
|
|
||||||
|
|
||||||
Run kustomize again to see the new names:
|
|
||||||
|
|
||||||
<!-- @grepStagingName @test -->
|
|
||||||
```
|
|
||||||
kustomize build $OVERLAYS/staging |\
|
|
||||||
grep -B 8 -A 1 staging-the-map
|
|
||||||
```
|
|
||||||
|
|
||||||
Confirm that the change in configMap content resulted
|
|
||||||
in three new names ending in _khk45ktkd9_ - one in the
|
|
||||||
configMap name itself, and two in the deployment that
|
|
||||||
uses the map:
|
|
||||||
|
|
||||||
<!-- @countHashes @test -->
|
|
||||||
```
|
|
||||||
test 3 == \
|
|
||||||
$(kustomize build $OVERLAYS/staging | grep khk45ktkd9 | wc -l)
|
|
||||||
```
|
|
||||||
|
|
||||||
Applying these resources to the cluster will result in
|
|
||||||
a rolling update of the deployments pods, retargetting
|
|
||||||
them from the _hhhhkfmgmk_ maps to the _khk45ktkd9_
|
|
||||||
maps. The system will later garbage collect the
|
|
||||||
unused maps.
|
|
||||||
|
|
||||||
## Rollback
|
|
||||||
|
|
||||||
To rollback, one would undo whatever edits were made to
|
|
||||||
the configuation in source control, then rerun kustomize
|
|
||||||
on the reverted configuration and apply it to the
|
|
||||||
cluster.
|
|
||||||
@@ -5,5 +5,5 @@ commonLabels:
|
|||||||
|
|
||||||
resources:
|
resources:
|
||||||
- deployment.yaml
|
- deployment.yaml
|
||||||
- configMap.yaml
|
|
||||||
- service.yaml
|
- service.yaml
|
||||||
|
- configMap.yaml
|
||||||
75
examples/imageTags.md
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
# Demo: change image tags
|
||||||
|
|
||||||
|
|
||||||
|
Define a place to work:
|
||||||
|
|
||||||
|
<!-- @makeWorkplace @test -->
|
||||||
|
```
|
||||||
|
DEMO_HOME=$(mktemp -d)
|
||||||
|
```
|
||||||
|
|
||||||
|
Make a `kustomization` containing a pod resource
|
||||||
|
|
||||||
|
<!-- @createKustomization @test -->
|
||||||
|
```
|
||||||
|
cat <<EOF >$DEMO_HOME/kustomization.yaml
|
||||||
|
resources:
|
||||||
|
- pod.yaml
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
Declare the pod resource
|
||||||
|
|
||||||
|
<!-- @createDeployment @test -->
|
||||||
|
```
|
||||||
|
cat <<EOF >$DEMO_HOME/pod.yaml
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: myapp-pod
|
||||||
|
labels:
|
||||||
|
app: myapp
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: myapp-container
|
||||||
|
image: busybox:1.29.0
|
||||||
|
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
|
||||||
|
initContainers:
|
||||||
|
- name: init-mydb
|
||||||
|
image: busybox:1.29.0
|
||||||
|
command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']
|
||||||
|
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`.
|
||||||
|
|
||||||
|
|
||||||
|
Add `imageTags`:
|
||||||
|
<!-- @addImageTags @test -->
|
||||||
|
```
|
||||||
|
cd $DEMO_HOME
|
||||||
|
kustomize edit set imagetag busybox:1.29.1
|
||||||
|
```
|
||||||
|
|
||||||
|
The `kustomization.yaml` will be added following `imageTags`.
|
||||||
|
> ```
|
||||||
|
> imageTags:
|
||||||
|
> - name: busybox
|
||||||
|
> newTag: 1.29.1
|
||||||
|
> ```
|
||||||
|
|
||||||
|
Now build this `kustomization`
|
||||||
|
<!-- @kustomizeBuild @test -->
|
||||||
|
```
|
||||||
|
kustomize build $DEMO_HOME
|
||||||
|
```
|
||||||
|
|
||||||
|
Confirm that this replaces _both_ busybox tags:
|
||||||
|
|
||||||
|
<!-- @confirmTags @test -->
|
||||||
|
```
|
||||||
|
test 2 == \
|
||||||
|
$(kustomize build $DEMO_HOME | grep busybox:1.29.1 | wc -l); \
|
||||||
|
echo $?
|
||||||
|
```
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
#
|
#
|
||||||
# This script assumes that the process running it has
|
# This script assumes that the process running it has
|
||||||
# checked out the kubernetes-sigs/kustomize repo, and
|
# checked out the kubernetes-sigs/kustomize repo, and
|
||||||
# has cd'ed into it (i.e. the directory above "demos")
|
# has cd'ed into it (i.e. the directory above "examples")
|
||||||
# before running it.
|
# before running it.
|
||||||
#
|
#
|
||||||
# At time of writing, its 'call point' was in
|
# At time of writing, its 'call point' was in
|
||||||
@@ -79,6 +79,6 @@ function runTest {
|
|||||||
|
|
||||||
setUpEnv
|
setUpEnv
|
||||||
|
|
||||||
pushd demos
|
pushd examples
|
||||||
runTest ldap/integration_test.sh ldap/base
|
runTest ldap/integration_test.sh ldap/base
|
||||||
popd
|
popd
|
||||||
@@ -45,7 +45,7 @@ mkdir -p $BASE
|
|||||||
|
|
||||||
CONTENT="https://raw.githubusercontent.com\
|
CONTENT="https://raw.githubusercontent.com\
|
||||||
/kubernetes-sigs/kustomize\
|
/kubernetes-sigs/kustomize\
|
||||||
/master/demos/ldap"
|
/master/examples/ldap"
|
||||||
|
|
||||||
curl -s -o "$BASE/#1" "$CONTENT/base\
|
curl -s -o "$BASE/#1" "$CONTENT/base\
|
||||||
/{deployment.yaml,kustomization.yaml,service.yaml,env.startup.txt}"
|
/{deployment.yaml,kustomization.yaml,service.yaml,env.startup.txt}"
|
||||||
@@ -29,7 +29,7 @@ Download them:
|
|||||||
```
|
```
|
||||||
curl -s -o "$DEMO_HOME/#1.yaml" "https://raw.githubusercontent.com\
|
curl -s -o "$DEMO_HOME/#1.yaml" "https://raw.githubusercontent.com\
|
||||||
/kubernetes-sigs/kustomize\
|
/kubernetes-sigs/kustomize\
|
||||||
/master/demos/mySql\
|
/master/examples/mySql\
|
||||||
/{deployment,secret,service}.yaml"
|
/{deployment,secret,service}.yaml"
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ Download them:
|
|||||||
```
|
```
|
||||||
CONTENT="https://raw.githubusercontent.com\
|
CONTENT="https://raw.githubusercontent.com\
|
||||||
/kubernetes-sigs/kustomize\
|
/kubernetes-sigs/kustomize\
|
||||||
/master/demos/springboot"
|
/master/examples/springboot"
|
||||||
|
|
||||||
curl -s -o "$DEMO_HOME/#1.yaml" \
|
curl -s -o "$DEMO_HOME/#1.yaml" \
|
||||||
"$CONTENT/base/{deployment,service}.yaml"
|
"$CONTENT/base/{deployment,service}.yaml"
|
||||||
@@ -4,7 +4,7 @@ metadata:
|
|||||||
name: demo-configmap
|
name: demo-configmap
|
||||||
data:
|
data:
|
||||||
application.properties: |
|
application.properties: |
|
||||||
app.name=Staging Kinflate Demo
|
app.name=Production Kinflate Demo
|
||||||
spring.jpa.hibernate.ddl-auto=update
|
spring.jpa.hibernate.ddl-auto=update
|
||||||
spring.datasource.url=jdbc:mysql://<production_db_ip>:3306/db_example
|
spring.datasource.url=jdbc:mysql://<production_db_ip>:3306/db_example
|
||||||
spring.datasource.username=root
|
spring.datasource.username=root
|
||||||
144
examples/wordpress/README.md
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
# Demo: Injecting k8s runtime data into containers
|
||||||
|
|
||||||
|
In this tutorial, you will learn how to use `kustomize` to declare a variable reference and substitute it in container's command.
|
||||||
|
|
||||||
|
To run WordPress, it's necessary to
|
||||||
|
|
||||||
|
- connect WordPress with a MySQL database
|
||||||
|
- access the service name of MySQL database from WordPress container
|
||||||
|
|
||||||
|
First make a place to work:
|
||||||
|
<!-- @makeDemoHome @test -->
|
||||||
|
```
|
||||||
|
DEMO_HOME=$(mktemp -d)
|
||||||
|
MYSQL_HOME=$DEMO_HOME/mysql
|
||||||
|
mkdir -p $MYSQL_HOME
|
||||||
|
WORDPRESS_HOME=$DEMO_HOME/wordpress
|
||||||
|
mkdir -p $WORDPRESS_HOME
|
||||||
|
```
|
||||||
|
|
||||||
|
### Download resources
|
||||||
|
|
||||||
|
Download the resources and `kustomization.yaml` for WordPress.
|
||||||
|
|
||||||
|
<!-- @downloadResources @test -->
|
||||||
|
```
|
||||||
|
CONTENT="https://raw.githubusercontent.com\
|
||||||
|
/kubernetes-sigs/kustomize\
|
||||||
|
/master/examples/wordpress/wordpress"
|
||||||
|
|
||||||
|
curl -s -o "$WORDPRESS_HOME/#1.yaml" \
|
||||||
|
"$CONTENT/{deployment,service,kustomization}.yaml"
|
||||||
|
```
|
||||||
|
|
||||||
|
Download the resources and `kustomization.yaml` for MySQL.
|
||||||
|
|
||||||
|
<!-- @downloadResources @test -->
|
||||||
|
```
|
||||||
|
CONTENT="https://raw.githubusercontent.com\
|
||||||
|
/kubernetes-sigs/kustomize\
|
||||||
|
/master/examples/wordpress/mysql"
|
||||||
|
|
||||||
|
curl -s -o "$MYSQL_HOME/#1.yaml" \
|
||||||
|
"$CONTENT/{deployment,service,secret,kustomization}.yaml"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create kustomization.yaml
|
||||||
|
Create a new kustomization with two bases:
|
||||||
|
|
||||||
|
<!-- @createKustomization @test -->
|
||||||
|
```
|
||||||
|
cat <<EOF >$DEMO_HOME/kustomization.yaml
|
||||||
|
bases:
|
||||||
|
- wordpress
|
||||||
|
- mysql
|
||||||
|
namePrefix: demo-
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
### Download patch for WordPress
|
||||||
|
In the new kustomization, apply a patch for wordpress deployment. The patch does two things
|
||||||
|
- Add an initial container to show the mysql service name
|
||||||
|
- Add environment variable that allow wordpress to find the mysql database
|
||||||
|
|
||||||
|
<!-- @downloadPatch @test -->
|
||||||
|
```
|
||||||
|
CONTENT="https://raw.githubusercontent.com\
|
||||||
|
/kubernetes-sigs/kustomize\
|
||||||
|
/master/examples/patch.yaml"
|
||||||
|
|
||||||
|
curl -s -o "$DEMO_HOME/#1.yaml" \
|
||||||
|
"$CONTENT/{patch}.yaml"
|
||||||
|
```
|
||||||
|
The patch has following content
|
||||||
|
> ```
|
||||||
|
> apiVersion: apps/v1beta2
|
||||||
|
> kind: Deployment
|
||||||
|
> metadata:
|
||||||
|
> name: wordpress
|
||||||
|
> spec:
|
||||||
|
> template:
|
||||||
|
> spec:
|
||||||
|
> initContainers:
|
||||||
|
> - name: init-command
|
||||||
|
> image: debian
|
||||||
|
> command:
|
||||||
|
> - "echo $(WORDPRESS_SERVICE)"
|
||||||
|
> - "echo $(MYSQL_SERVICE)"
|
||||||
|
> containers:
|
||||||
|
> - name: wordpress
|
||||||
|
> env:
|
||||||
|
> - name: WORDPRESS_DB_HOST
|
||||||
|
> value: $(MYSQL_SERVICE)
|
||||||
|
> - name: WORDPRESS_DB_PASSWORD
|
||||||
|
> valueFrom:
|
||||||
|
> secretKeyRef:
|
||||||
|
> name: mysql-pass
|
||||||
|
> key: password
|
||||||
|
> ```
|
||||||
|
The init container's command requires information that depends on k8s resource object fields, represented by the placeholder variables
|
||||||
|
$(WORDPRESS_SERVICE) and $(MYSQL_SERVICE).
|
||||||
|
|
||||||
|
### Bind the Variables to k8s Object Fields
|
||||||
|
|
||||||
|
<!-- @addVarRef @test -->
|
||||||
|
```
|
||||||
|
cat <<EOF >>$DEMO_HOME/kustomization.yaml
|
||||||
|
vars:
|
||||||
|
- name: WORDPRESS_SERVICE
|
||||||
|
objref:
|
||||||
|
kind: Service
|
||||||
|
name: wordpress
|
||||||
|
apiVersion: v1
|
||||||
|
fieldref:
|
||||||
|
fieldpath: metadata.name
|
||||||
|
- name: MYSQL_SERVICE
|
||||||
|
objref:
|
||||||
|
kind: Service
|
||||||
|
name: mysql
|
||||||
|
apiVersion: v1
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
`WORDPRESS_SERVICE` is from the field `metadata.name` of Service `wordpress`. If we don't specify `fieldref`, the default is `metadata.name`. So `MYSQL_SERVICE` is from the field `metadata.name` of Service `mysql`.
|
||||||
|
|
||||||
|
### Substitution
|
||||||
|
Confirm the variable substitution:
|
||||||
|
|
||||||
|
<!-- @kustomizeBuild @test -->
|
||||||
|
```
|
||||||
|
kustomize build $DEMO_HOME
|
||||||
|
```
|
||||||
|
|
||||||
|
Expect this in the output:
|
||||||
|
|
||||||
|
> ```
|
||||||
|
> (truncated)
|
||||||
|
> ...
|
||||||
|
> initContainers:
|
||||||
|
> - command:
|
||||||
|
> - echo demo-wordpress
|
||||||
|
> - echo demo-mysql
|
||||||
|
> image: debian
|
||||||
|
> name: init-command
|
||||||
|
>
|
||||||
|
> ```
|
||||||
19
examples/wordpress/kustomization.yaml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
bases:
|
||||||
|
- wordpress
|
||||||
|
- mysql
|
||||||
|
patches:
|
||||||
|
- patch.yaml
|
||||||
|
namePrefix: demo-
|
||||||
|
|
||||||
|
vars:
|
||||||
|
- name: WORDPRESS_SERVICE
|
||||||
|
objref:
|
||||||
|
kind: Service
|
||||||
|
name: wordpress
|
||||||
|
apiVersion: v1
|
||||||
|
- name: MYSQL_SERVICE
|
||||||
|
objref:
|
||||||
|
kind: Service
|
||||||
|
name: mysql
|
||||||
|
apiVersion: v1
|
||||||
|
|
||||||
35
examples/wordpress/mysql/deployment.yaml
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
apiVersion: apps/v1beta2 # for versions before 1.9.0 use apps/v1beta2
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: mysql
|
||||||
|
labels:
|
||||||
|
app: mysql
|
||||||
|
spec:
|
||||||
|
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: mysql-pass
|
||||||
|
key: password
|
||||||
|
ports:
|
||||||
|
- containerPort: 3306
|
||||||
|
name: mysql
|
||||||
|
volumeMounts:
|
||||||
|
- name: mysql-persistent-storage
|
||||||
|
mountPath: /var/lib/mysql
|
||||||
|
volumes:
|
||||||
|
- name: mysql-persistent-storage
|
||||||
|
emptyDir: {}
|
||||||
4
examples/wordpress/mysql/kustomization.yaml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
resources:
|
||||||
|
- deployment.yaml
|
||||||
|
- service.yaml
|
||||||
|
- secret.yaml
|
||||||
8
examples/wordpress/mysql/secret.yaml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: mysql-pass
|
||||||
|
type: Opaque
|
||||||
|
data:
|
||||||
|
# Default password is "admin".
|
||||||
|
password: YWRtaW4=
|
||||||
11
examples/wordpress/mysql/service.yaml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: mysql
|
||||||
|
labels:
|
||||||
|
app: mysql
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 3306
|
||||||
|
selector:
|
||||||
|
app: mysql
|
||||||
23
examples/wordpress/patch.yaml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
apiVersion: apps/v1beta2
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: wordpress
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
initContainers:
|
||||||
|
- name: init-command
|
||||||
|
image: debian
|
||||||
|
command:
|
||||||
|
- "echo $(WORDPRESS_SERVICE)"
|
||||||
|
- "echo $(MYSQL_SERVICE)"
|
||||||
|
containers:
|
||||||
|
- name: wordpress
|
||||||
|
env:
|
||||||
|
- name: WORDPRESS_DB_HOST
|
||||||
|
value: $(MYSQL_SERVICE)
|
||||||
|
- name: WORDPRESS_DB_PASSWORD
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: mysql-pass
|
||||||
|
key: password
|
||||||
29
examples/wordpress/wordpress/deployment.yaml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
apiVersion: apps/v1beta2 # for versions before 1.9.0 use apps/v1beta2
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: wordpress
|
||||||
|
labels:
|
||||||
|
app: wordpress
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: wordpress
|
||||||
|
strategy:
|
||||||
|
type: Recreate
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: wordpress
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: wordpress:4.8-apache
|
||||||
|
name: wordpress
|
||||||
|
ports:
|
||||||
|
- containerPort: 80
|
||||||
|
name: wordpress
|
||||||
|
volumeMounts:
|
||||||
|
- name: wordpress-persistent-storage
|
||||||
|
mountPath: /var/www/html
|
||||||
|
volumes:
|
||||||
|
- name: wordpress-persistent-storage
|
||||||
|
emptyDir: {}
|
||||||
3
examples/wordpress/wordpress/kustomization.yaml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
resources:
|
||||||
|
- deployment.yaml
|
||||||
|
- service.yaml
|
||||||
12
examples/wordpress/wordpress/service.yaml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: wordpress
|
||||||
|
labels:
|
||||||
|
app: wordpress
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
selector:
|
||||||
|
app: wordpress
|
||||||
|
type: LoadBalancer
|
||||||
@@ -14,45 +14,44 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Package app implements state for the set of all resources being customized.
|
||||||
|
// Should rename this - there's nothing "app"y about it.
|
||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/ghodss/yaml"
|
"github.com/ghodss/yaml"
|
||||||
|
"github.com/golang/glog"
|
||||||
|
|
||||||
|
"github.com/kubernetes-sigs/kustomize/pkg/configmapandsecret"
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||||
|
"github.com/kubernetes-sigs/kustomize/pkg/crds"
|
||||||
|
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||||
interror "github.com/kubernetes-sigs/kustomize/pkg/internal/error"
|
interror "github.com/kubernetes-sigs/kustomize/pkg/internal/error"
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/loader"
|
"github.com/kubernetes-sigs/kustomize/pkg/loader"
|
||||||
|
"github.com/kubernetes-sigs/kustomize/pkg/resmap"
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/resource"
|
"github.com/kubernetes-sigs/kustomize/pkg/resource"
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/transformers"
|
"github.com/kubernetes-sigs/kustomize/pkg/transformers"
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Application interface {
|
// Application implements the guts of the kustomize 'build' command.
|
||||||
// Resources computes and returns the resources for the app.
|
// TODO: Change name, as "application" is overloaded and somewhat
|
||||||
Resources() (resource.ResourceCollection, error)
|
// misleading (one can customize an RBAC policy). Perhaps "Target"
|
||||||
// SemiResources computes and returns the resources without name hash and name reference for the app
|
// https://github.com/kubernetes-sigs/kustomize/blob/master/docs/glossary.md#target
|
||||||
SemiResources() (resource.ResourceCollection, error)
|
type Application struct {
|
||||||
// RawResources computes and returns the raw resources from the kustomization file.
|
|
||||||
// It contains resources from
|
|
||||||
// 1) untransformed resources from current kustomization file
|
|
||||||
// 2) transformed resources from sub packages
|
|
||||||
RawResources() (resource.ResourceCollection, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ Application = &applicationImpl{}
|
|
||||||
|
|
||||||
// Private implementation of the Application interface
|
|
||||||
type applicationImpl struct {
|
|
||||||
kustomization *types.Kustomization
|
kustomization *types.Kustomization
|
||||||
loader loader.Loader
|
ldr loader.Loader
|
||||||
|
fSys fs.FileSystem
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewApp parses the kustomization file at the path using the loader.
|
// NewApplication returns a new instance of Application primed with a Loader.
|
||||||
func New(loader loader.Loader) (Application, error) {
|
func NewApplication(ldr loader.Loader, fSys fs.FileSystem) (*Application, error) {
|
||||||
content, err := loader.Load(constants.KustomizationFileName)
|
content, err := ldr.Load(constants.KustomizationFileName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -62,186 +61,8 @@ func New(loader loader.Loader) (Application, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &applicationImpl{kustomization: &m, loader: loader}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resources computes and returns the resources from the kustomization file.
|
return &Application{kustomization: &m, ldr: ldr, fSys: fSys}, nil
|
||||||
// The namehashing for configmap/secrets and resolving name reference is only done
|
|
||||||
// in the most top overlay once at the end of getting resources.
|
|
||||||
func (a *applicationImpl) Resources() (resource.ResourceCollection, error) {
|
|
||||||
res, err := a.SemiResources()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
t, err := a.getHashAndReferenceTransformer()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = t.Transform(res)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SemiResources computes and returns the resources without name hash and name reference for the app
|
|
||||||
func (a *applicationImpl) SemiResources() (resource.ResourceCollection, error) {
|
|
||||||
errs := &interror.KustomizationErrors{}
|
|
||||||
raw, err := a.rawResources()
|
|
||||||
if err != nil {
|
|
||||||
errs.Append(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cms, err := resource.NewFromConfigMaps(a.loader, a.kustomization.ConfigMapGenerator)
|
|
||||||
if err != nil {
|
|
||||||
errs.Append(err)
|
|
||||||
}
|
|
||||||
secrets, err := resource.NewFromSecretGenerators(a.loader.Root(), a.kustomization.SecretGenerator)
|
|
||||||
if err != nil {
|
|
||||||
errs.Append(err)
|
|
||||||
}
|
|
||||||
res, err := resource.Merge(cms, secrets)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
allRes, err := resource.MergeWithOverride(raw, res)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
patches, err := resource.NewFromPatches(a.loader, a.kustomization.Patches)
|
|
||||||
if err != nil {
|
|
||||||
errs.Append(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(errs.Get()) > 0 {
|
|
||||||
return nil, errs
|
|
||||||
}
|
|
||||||
|
|
||||||
t, err := a.getTransformer(patches)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = t.Transform(allRes)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return allRes, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RawResources computes and returns the raw resources from the kustomization file.
|
|
||||||
// The namehashing for configmap/secrets and resolving name reference is only done
|
|
||||||
// in the most top overlay once at the end of getting resources.
|
|
||||||
func (a *applicationImpl) RawResources() (resource.ResourceCollection, error) {
|
|
||||||
res, err := a.rawResources()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
t, err := a.getHashAndReferenceTransformer()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = t.Transform(res)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *applicationImpl) rawResources() (resource.ResourceCollection, error) {
|
|
||||||
subAppResources, errs := a.subAppResources()
|
|
||||||
resources, err := resource.NewFromResources(a.loader, a.kustomization.Resources)
|
|
||||||
if err != nil {
|
|
||||||
errs.Append(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(errs.Get()) > 0 {
|
|
||||||
return nil, errs
|
|
||||||
}
|
|
||||||
|
|
||||||
return resource.Merge(resources, subAppResources)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *applicationImpl) subAppResources() (resource.ResourceCollection, *interror.KustomizationErrors) {
|
|
||||||
sliceOfSubAppResources := []resource.ResourceCollection{}
|
|
||||||
errs := &interror.KustomizationErrors{}
|
|
||||||
for _, pkgPath := range a.kustomization.Bases {
|
|
||||||
subloader, err := a.loader.New(pkgPath)
|
|
||||||
if err != nil {
|
|
||||||
errs.Append(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
subapp, err := New(subloader)
|
|
||||||
if err != nil {
|
|
||||||
errs.Append(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Gather all transformed resources from subpackages.
|
|
||||||
subAppResources, err := subapp.SemiResources()
|
|
||||||
if err != nil {
|
|
||||||
errs.Append(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
sliceOfSubAppResources = append(sliceOfSubAppResources, subAppResources)
|
|
||||||
}
|
|
||||||
allResources, err := resource.Merge(sliceOfSubAppResources...)
|
|
||||||
if err != nil {
|
|
||||||
errs.Append(err)
|
|
||||||
}
|
|
||||||
return allResources, errs
|
|
||||||
}
|
|
||||||
|
|
||||||
// getTransformer generates the following transformers:
|
|
||||||
// 1) apply overlay
|
|
||||||
// 2) name prefix
|
|
||||||
// 3) apply labels
|
|
||||||
// 4) apply annotations
|
|
||||||
func (a *applicationImpl) getTransformer(patches []*resource.Resource) (transformers.Transformer, error) {
|
|
||||||
ts := []transformers.Transformer{}
|
|
||||||
|
|
||||||
ot, err := transformers.NewOverlayTransformer(patches)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ts = append(ts, ot)
|
|
||||||
|
|
||||||
npt, err := transformers.NewDefaultingNamePrefixTransformer(string(a.kustomization.NamePrefix))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ts = append(ts, npt)
|
|
||||||
|
|
||||||
lt, err := transformers.NewDefaultingLabelsMapTransformer(a.kustomization.CommonLabels)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ts = append(ts, lt)
|
|
||||||
|
|
||||||
at, err := transformers.NewDefaultingAnnotationsMapTransformer(a.kustomization.CommonAnnotations)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ts = append(ts, at)
|
|
||||||
|
|
||||||
return transformers.NewMultiTransformer(ts), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// getHashAndReferenceTransformer generates the following transformers:
|
|
||||||
// 1) name hash for configmap and secrests
|
|
||||||
// 2) apply name reference
|
|
||||||
func (a *applicationImpl) getHashAndReferenceTransformer() (transformers.Transformer, error) {
|
|
||||||
ts := []transformers.Transformer{}
|
|
||||||
nht := transformers.NewNameHashTransformer()
|
|
||||||
ts = append(ts, nht)
|
|
||||||
|
|
||||||
nrt, err := transformers.NewDefaultingNameReferenceTransformer()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ts = append(ts, nrt)
|
|
||||||
return transformers.NewMultiTransformer(ts), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func unmarshal(y []byte, o interface{}) error {
|
func unmarshal(y []byte, o interface{}) error {
|
||||||
@@ -254,3 +75,261 @@ func unmarshal(y []byte, o interface{}) error {
|
|||||||
dec.DisallowUnknownFields()
|
dec.DisallowUnknownFields()
|
||||||
return dec.Decode(o)
|
return dec.Decode(o)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MakeCustomizedResMap creates a ResMap per kustomization instructions.
|
||||||
|
// The Resources in the returned ResMap are fully customized.
|
||||||
|
func (a *Application) MakeCustomizedResMap() (resmap.ResMap, error) {
|
||||||
|
m, err := a.loadCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return a.resolveRefsToGeneratedResources(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeUncustomizedResMap purports to create a ResMap without customization.
|
||||||
|
// The Resources in the returned ResMap include all resources mentioned
|
||||||
|
// in the kustomization file and transitively reachable via its Bases,
|
||||||
|
// and all generated secrets and configMaps.
|
||||||
|
// Meant for use in generating a diff against customized resources.
|
||||||
|
// TODO: See https://github.com/kubernetes-sigs/kustomize/issues/85
|
||||||
|
func (a *Application) MakeUncustomizedResMap() (resmap.ResMap, error) {
|
||||||
|
m, err := a.loadResMapFromBasesAndResources()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return a.resolveRefsToGeneratedResources(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolveRefsToGeneratedResources fixes all name references.
|
||||||
|
func (a *Application) resolveRefsToGeneratedResources(m resmap.ResMap) (resmap.ResMap, error) {
|
||||||
|
err := transformers.NewNameHashTransformer().Transform(m)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var r []transformers.Transformer
|
||||||
|
t, err := transformers.NewDefaultingNameReferenceTransformer()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
r = append(r, t)
|
||||||
|
|
||||||
|
refVars, err := a.resolveRefVars(m)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
t, err = transformers.NewRefVarTransformer(refVars)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
r = append(r, t)
|
||||||
|
|
||||||
|
err = transformers.NewMultiTransformer(r).Transform(m)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadCustomizedResMap loads and customizes resources to build a ResMap.
|
||||||
|
func (a *Application) loadCustomizedResMap() (resmap.ResMap, error) {
|
||||||
|
errs := &interror.KustomizationErrors{}
|
||||||
|
result, err := a.loadResMapFromBasesAndResources()
|
||||||
|
if err != nil {
|
||||||
|
errs.Append(errors.Wrap(err, "loadResMapFromBasesAndResources"))
|
||||||
|
}
|
||||||
|
err = crds.RegisterCRDs(a.ldr, a.kustomization.CRDs)
|
||||||
|
if err != nil {
|
||||||
|
errs.Append(errors.Wrap(err, "RegisterCRDs"))
|
||||||
|
}
|
||||||
|
cms, err := resmap.NewResMapFromConfigMapArgs(
|
||||||
|
configmapandsecret.NewConfigMapFactory(a.fSys, a.ldr),
|
||||||
|
a.kustomization.ConfigMapGenerator)
|
||||||
|
if err != nil {
|
||||||
|
errs.Append(errors.Wrap(err, "NewResMapFromConfigMapArgs"))
|
||||||
|
}
|
||||||
|
secrets, err := resmap.NewResMapFromSecretArgs(
|
||||||
|
configmapandsecret.NewSecretFactory(a.fSys, a.ldr.Root()),
|
||||||
|
a.kustomization.SecretGenerator)
|
||||||
|
if err != nil {
|
||||||
|
errs.Append(errors.Wrap(err, "NewResMapFromSecretArgs"))
|
||||||
|
}
|
||||||
|
res, err := resmap.MergeWithoutOverride(cms, secrets)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "Merge")
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err = resmap.MergeWithOverride(result, res)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
patches, err := resmap.NewResourceSliceFromPatches(a.ldr, a.kustomization.Patches)
|
||||||
|
if err != nil {
|
||||||
|
errs.Append(errors.Wrap(err, "NewResourceSliceFromPatches"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(errs.Get()) > 0 {
|
||||||
|
return nil, errs
|
||||||
|
}
|
||||||
|
|
||||||
|
var r []transformers.Transformer
|
||||||
|
t, err := a.newTransformer(patches)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
r = append(r, t)
|
||||||
|
t, err = transformers.NewImageTagTransformer(a.kustomization.ImageTags)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
r = append(r, t)
|
||||||
|
|
||||||
|
err = transformers.NewMultiTransformer(r).Transform(result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets Bases and Resources as advertised.
|
||||||
|
func (a *Application) loadResMapFromBasesAndResources() (resmap.ResMap, error) {
|
||||||
|
bases, errs := a.loadCustomizedBases()
|
||||||
|
resources, err := resmap.NewResMapFromFiles(a.ldr, a.kustomization.Resources)
|
||||||
|
if err != nil {
|
||||||
|
errs.Append(errors.Wrap(err, "rawResources failed to read Resources"))
|
||||||
|
}
|
||||||
|
if len(errs.Get()) > 0 {
|
||||||
|
return nil, errs
|
||||||
|
}
|
||||||
|
return resmap.MergeWithoutOverride(resources, bases)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop through the Bases of this kustomization recursively loading resources.
|
||||||
|
// Combine into one ResMap, demanding unique Ids for each resource.
|
||||||
|
func (a *Application) loadCustomizedBases() (resmap.ResMap, *interror.KustomizationErrors) {
|
||||||
|
var list []resmap.ResMap
|
||||||
|
errs := &interror.KustomizationErrors{}
|
||||||
|
for _, path := range a.kustomization.Bases {
|
||||||
|
ldr, err := a.ldr.New(path)
|
||||||
|
if err != nil {
|
||||||
|
errs.Append(errors.Wrap(err, "couldn't make ldr for "+path))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
app, err := NewApplication(ldr, a.fSys)
|
||||||
|
if err != nil {
|
||||||
|
errs.Append(errors.Wrap(err, "couldn't make app for "+path))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
resMap, err := app.loadCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
errs.Append(errors.Wrap(err, "SemiResources"))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
list = append(list, resMap)
|
||||||
|
}
|
||||||
|
result, err := resmap.MergeWithoutOverride(list...)
|
||||||
|
if err != nil {
|
||||||
|
errs.Append(errors.Wrap(err, "Merge failed"))
|
||||||
|
}
|
||||||
|
return result, errs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Application) loadBasesAsFlatList() ([]*Application, error) {
|
||||||
|
var result []*Application
|
||||||
|
errs := &interror.KustomizationErrors{}
|
||||||
|
for _, path := range a.kustomization.Bases {
|
||||||
|
ldr, err := a.ldr.New(path)
|
||||||
|
if err != nil {
|
||||||
|
errs.Append(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
a, err := NewApplication(ldr, a.fSys)
|
||||||
|
if err != nil {
|
||||||
|
errs.Append(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result = append(result, a)
|
||||||
|
}
|
||||||
|
if len(errs.Get()) > 0 {
|
||||||
|
return nil, errs
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// newTransformer makes a Transformer that does everything except resolve generated names.
|
||||||
|
func (a *Application) newTransformer(patches []*resource.Resource) (transformers.Transformer, error) {
|
||||||
|
var r []transformers.Transformer
|
||||||
|
t, err := transformers.NewPatchTransformer(patches)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
r = append(r, t)
|
||||||
|
r = append(r, transformers.NewNamespaceTransformer(string(a.kustomization.Namespace)))
|
||||||
|
t, err = transformers.NewDefaultingNamePrefixTransformer(string(a.kustomization.NamePrefix))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
r = append(r, t)
|
||||||
|
t, err = transformers.NewDefaultingLabelsMapTransformer(a.kustomization.CommonLabels)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
r = append(r, t)
|
||||||
|
t, err = transformers.NewDefaultingAnnotationsMapTransformer(a.kustomization.CommonAnnotations)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
r = append(r, t)
|
||||||
|
return transformers.NewMultiTransformer(r), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Application) resolveRefVars(m resmap.ResMap) (map[string]string, error) {
|
||||||
|
result := map[string]string{}
|
||||||
|
vars, err := a.getAllVars()
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
for _, v := range vars {
|
||||||
|
id := resource.NewResId(v.ObjRef.GroupVersionKind(), v.ObjRef.Name)
|
||||||
|
if r, found := m[id]; found {
|
||||||
|
s, err := r.GetFieldValue(v.FieldRef.FieldPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to resolve referred var: %+v", v)
|
||||||
|
}
|
||||||
|
result[v.Name] = s
|
||||||
|
} else {
|
||||||
|
glog.Infof("couldn't resolve v: %v", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getAllVars returns all the "environment" style Var instances defined in the app.
|
||||||
|
func (a *Application) getAllVars() ([]types.Var, error) {
|
||||||
|
var result []types.Var
|
||||||
|
errs := &interror.KustomizationErrors{}
|
||||||
|
|
||||||
|
bases, err := a.loadBasesAsFlatList()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: computing vars and resources for bases can be combined
|
||||||
|
for _, b := range bases {
|
||||||
|
vars, err := b.getAllVars()
|
||||||
|
if err != nil {
|
||||||
|
errs.Append(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result = append(result, vars...)
|
||||||
|
}
|
||||||
|
for _, v := range a.kustomization.Vars {
|
||||||
|
v.Defaulting()
|
||||||
|
result = append(result, v)
|
||||||
|
}
|
||||||
|
if len(errs.Get()) > 0 {
|
||||||
|
return nil, errs
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,29 +18,30 @@ package app
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||||
|
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||||
|
"github.com/kubernetes-sigs/kustomize/pkg/internal/loadertest"
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/loader"
|
"github.com/kubernetes-sigs/kustomize/pkg/loader"
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/loader/loadertest"
|
"github.com/kubernetes-sigs/kustomize/pkg/resmap"
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/resource"
|
"github.com/kubernetes-sigs/kustomize/pkg/resource"
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
)
|
)
|
||||||
|
|
||||||
func setupTest(t *testing.T) loader.Loader {
|
const (
|
||||||
kustomizationContent := []byte(`
|
kustomizationContent1 = `
|
||||||
namePrefix: foo-
|
namePrefix: foo-
|
||||||
|
namespace: ns1
|
||||||
commonLabels:
|
commonLabels:
|
||||||
app: nginx
|
app: nginx
|
||||||
commonAnnotations:
|
commonAnnotations:
|
||||||
note: This is a test annotation
|
note: This is a test annotation
|
||||||
resources:
|
resources:
|
||||||
- deployment.yaml
|
- deployment.yaml
|
||||||
|
- namespace.yaml
|
||||||
configMapGenerator:
|
configMapGenerator:
|
||||||
- name: literalConfigMap
|
- name: literalConfigMap
|
||||||
literals:
|
literals:
|
||||||
@@ -52,184 +53,290 @@ secretGenerator:
|
|||||||
DB_USERNAME: "printf admin"
|
DB_USERNAME: "printf admin"
|
||||||
DB_PASSWORD: "printf somepw"
|
DB_PASSWORD: "printf somepw"
|
||||||
type: Opaque
|
type: Opaque
|
||||||
`)
|
`
|
||||||
deploymentContent := []byte(`apiVersion: apps/v1
|
deploymentContent = `apiVersion: apps/v1
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
metadata:
|
||||||
name: dply1
|
name: dply1
|
||||||
`)
|
kind: Deployment
|
||||||
|
`
|
||||||
|
namespaceContent = `apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: ns1
|
||||||
|
`
|
||||||
|
)
|
||||||
|
|
||||||
loader := loadertest.NewFakeLoader("/testpath")
|
func makeLoader1(t *testing.T) loader.Loader {
|
||||||
err := loader.AddFile("/testpath/"+constants.KustomizationFileName, kustomizationContent)
|
ldr := loadertest.NewFakeLoader("/testpath")
|
||||||
|
err := ldr.AddFile("/testpath/"+constants.KustomizationFileName, []byte(kustomizationContent1))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to setup fake loader.")
|
t.Fatalf("Failed to setup fake ldr.")
|
||||||
}
|
}
|
||||||
err = loader.AddFile("/testpath/deployment.yaml", deploymentContent)
|
err = ldr.AddFile("/testpath/deployment.yaml", []byte(deploymentContent))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to setup fake loader.")
|
t.Fatalf("Failed to setup fake ldr.")
|
||||||
}
|
}
|
||||||
return loader
|
err = ldr.AddFile("/testpath/namespace.yaml", []byte(namespaceContent))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to setup fake ldr.")
|
||||||
|
}
|
||||||
|
return ldr
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResources(t *testing.T) {
|
var deploy = schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"}
|
||||||
expected := resource.ResourceCollection{
|
var cmap = schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"}
|
||||||
types.GroupVersionKindName{
|
var secret = schema.GroupVersionKind{Version: "v1", Kind: "Secret"}
|
||||||
GVK: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"},
|
var ns = schema.GroupVersionKind{Version: "v1", Kind: "Namespace"}
|
||||||
Name: "dply1",
|
var svc = schema.GroupVersionKind{Version: "v1", Kind: "Service"}
|
||||||
}: &resource.Resource{
|
|
||||||
Data: &unstructured.Unstructured{
|
func TestResources1(t *testing.T) {
|
||||||
Object: map[string]interface{}{
|
expected := resmap.ResMap{
|
||||||
"apiVersion": "apps/v1",
|
resource.NewResId(deploy, "dply1"): resource.NewResourceFromMap(
|
||||||
"kind": "Deployment",
|
map[string]interface{}{
|
||||||
"metadata": map[string]interface{}{
|
"apiVersion": "apps/v1",
|
||||||
"name": "foo-dply1",
|
"kind": "Deployment",
|
||||||
"labels": map[string]interface{}{
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "foo-dply1",
|
||||||
|
"namespace": "ns1",
|
||||||
|
"labels": map[string]interface{}{
|
||||||
|
"app": "nginx",
|
||||||
|
},
|
||||||
|
"annotations": map[string]interface{}{
|
||||||
|
"note": "This is a test annotation",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"spec": map[string]interface{}{
|
||||||
|
"selector": map[string]interface{}{
|
||||||
|
"matchLabels": map[string]interface{}{
|
||||||
"app": "nginx",
|
"app": "nginx",
|
||||||
},
|
},
|
||||||
"annotations": map[string]interface{}{
|
|
||||||
"note": "This is a test annotation",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
"spec": map[string]interface{}{
|
"template": map[string]interface{}{
|
||||||
"selector": map[string]interface{}{
|
"metadata": map[string]interface{}{
|
||||||
"matchLabels": map[string]interface{}{
|
"annotations": map[string]interface{}{
|
||||||
|
"note": "This is a test annotation",
|
||||||
|
},
|
||||||
|
"labels": map[string]interface{}{
|
||||||
"app": "nginx",
|
"app": "nginx",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"template": map[string]interface{}{
|
},
|
||||||
"metadata": map[string]interface{}{
|
},
|
||||||
"annotations": map[string]interface{}{
|
}),
|
||||||
"note": "This is a test annotation",
|
resource.NewResId(cmap, "literalConfigMap"): resource.NewResourceFromMap(
|
||||||
},
|
map[string]interface{}{
|
||||||
"labels": map[string]interface{}{
|
"apiVersion": "v1",
|
||||||
"app": "nginx",
|
"kind": "ConfigMap",
|
||||||
},
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "foo-literalConfigMap-mc92bgcbh5",
|
||||||
|
"namespace": "ns1",
|
||||||
|
"labels": map[string]interface{}{
|
||||||
|
"app": "nginx",
|
||||||
|
},
|
||||||
|
"annotations": map[string]interface{}{
|
||||||
|
"note": "This is a test annotation",
|
||||||
|
},
|
||||||
|
"creationTimestamp": nil,
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"DB_USERNAME": "admin",
|
||||||
|
"DB_PASSWORD": "somepw",
|
||||||
|
},
|
||||||
|
}).SetBehavior(resource.BehaviorCreate),
|
||||||
|
resource.NewResId(secret, "secret"): resource.NewResourceFromMap(
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "Secret",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "foo-secret-877fcfhgt5",
|
||||||
|
"namespace": "ns1",
|
||||||
|
"labels": map[string]interface{}{
|
||||||
|
"app": "nginx",
|
||||||
|
},
|
||||||
|
"annotations": map[string]interface{}{
|
||||||
|
"note": "This is a test annotation",
|
||||||
|
},
|
||||||
|
"creationTimestamp": nil,
|
||||||
|
},
|
||||||
|
"type": string(corev1.SecretTypeOpaque),
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"DB_USERNAME": base64.StdEncoding.EncodeToString([]byte("admin")),
|
||||||
|
"DB_PASSWORD": base64.StdEncoding.EncodeToString([]byte("somepw")),
|
||||||
|
},
|
||||||
|
}).SetBehavior(resource.BehaviorCreate),
|
||||||
|
resource.NewResId(ns, "ns1"): resource.NewResourceFromMap(
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "Namespace",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "foo-ns1",
|
||||||
|
"labels": map[string]interface{}{
|
||||||
|
"app": "nginx",
|
||||||
|
},
|
||||||
|
"annotations": map[string]interface{}{
|
||||||
|
"note": "This is a test annotation",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
l := makeLoader1(t)
|
||||||
|
fakeFs := fs.MakeFakeFS()
|
||||||
|
fakeFs.Mkdir("/")
|
||||||
|
app, err := NewApplication(l, fakeFs)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected construction error %v", err)
|
||||||
|
}
|
||||||
|
actual, err := app.MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected Resources error %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(actual, expected) {
|
||||||
|
err = expected.ErrorIfNotEqual(actual)
|
||||||
|
t.Fatalf("unexpected inequality: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRawResources1(t *testing.T) {
|
||||||
|
expected := resmap.ResMap{
|
||||||
|
resource.NewResId(deploy, "dply1"): resource.NewResourceFromMap(
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "apps/v1",
|
||||||
|
"kind": "Deployment",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "dply1",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
resource.NewResId(ns, "ns1"): resource.NewResourceFromMap(
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "Namespace",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "ns1",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
l := makeLoader1(t)
|
||||||
|
app, err := NewApplication(l, fs.MakeFakeFS())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected construction error %v", err)
|
||||||
|
}
|
||||||
|
actual, err := app.MakeUncustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected RawResources error %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := expected.ErrorIfNotEqual(actual); err != nil {
|
||||||
|
t.Fatalf("unexpected inequality: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
kustomizationContentBase = `
|
||||||
|
namePrefix: foo-
|
||||||
|
commonLabels:
|
||||||
|
app: banana
|
||||||
|
resources:
|
||||||
|
- deployment.yaml
|
||||||
|
`
|
||||||
|
kustomizationContentOverlay = `
|
||||||
|
commonLabels:
|
||||||
|
env: staging
|
||||||
|
resources:
|
||||||
|
- service.yaml
|
||||||
|
bases:
|
||||||
|
- base
|
||||||
|
`
|
||||||
|
serviceContent = `apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: svc
|
||||||
|
spec:
|
||||||
|
type: LoadBalancer
|
||||||
|
`
|
||||||
|
)
|
||||||
|
|
||||||
|
func makeLoader2(t *testing.T) loader.Loader {
|
||||||
|
ldr := loadertest.NewFakeLoader("/testpath")
|
||||||
|
err := ldr.AddFile("/testpath/"+constants.KustomizationFileName, []byte(kustomizationContentOverlay))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
err = ldr.AddFile("/testpath/service.yaml", []byte(serviceContent))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to setup fake ldr.")
|
||||||
|
}
|
||||||
|
err = ldr.AddDirectory("/testpath/base")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to setup fake ldr.")
|
||||||
|
}
|
||||||
|
err = ldr.AddFile("/testpath/base/"+constants.KustomizationFileName, []byte(kustomizationContentBase))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to setup fake ldr.")
|
||||||
|
}
|
||||||
|
err = ldr.AddFile("/testpath/base/deployment.yaml", []byte(deploymentContent))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to setup fake ldr.")
|
||||||
|
}
|
||||||
|
return ldr
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: This test covers incorrect behavior; it should not pass.
|
||||||
|
// It asks for raw resources. The Service resource is returned in raw form,
|
||||||
|
// but the resources in the base are modified to have the banana label,
|
||||||
|
// the 'foo' name prefix, etc. This method exists only to support the
|
||||||
|
// diff command comparing customized to non-customized resources;
|
||||||
|
// perhaps it's not worth supporting the command.
|
||||||
|
func TestRawResources2(t *testing.T) {
|
||||||
|
expected := resmap.ResMap{
|
||||||
|
resource.NewResId(deploy, "dply1"): resource.NewResourceFromMap(
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "apps/v1",
|
||||||
|
"kind": "Deployment",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "foo-dply1",
|
||||||
|
"labels": map[string]interface{}{
|
||||||
|
"app": "banana",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"spec": map[string]interface{}{
|
||||||
|
"selector": map[string]interface{}{
|
||||||
|
"matchLabels": map[string]interface{}{
|
||||||
|
"app": "banana",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"template": map[string]interface{}{
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"labels": map[string]interface{}{
|
||||||
|
"app": "banana",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
}),
|
||||||
},
|
resource.NewResId(svc, "svc"): resource.NewResourceFromMap(
|
||||||
types.GroupVersionKindName{
|
map[string]interface{}{
|
||||||
GVK: schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"},
|
"apiVersion": "v1",
|
||||||
Name: "literalConfigMap",
|
"kind": "Service",
|
||||||
}: &resource.Resource{
|
"metadata": map[string]interface{}{
|
||||||
Data: &unstructured.Unstructured{
|
"name": "svc",
|
||||||
Object: map[string]interface{}{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "ConfigMap",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "foo-literalConfigMap-mc92bgcbh5",
|
|
||||||
"labels": map[string]interface{}{
|
|
||||||
"app": "nginx",
|
|
||||||
},
|
|
||||||
"annotations": map[string]interface{}{
|
|
||||||
"note": "This is a test annotation",
|
|
||||||
},
|
|
||||||
"creationTimestamp": nil,
|
|
||||||
},
|
|
||||||
"data": map[string]interface{}{
|
|
||||||
"DB_USERNAME": "admin",
|
|
||||||
"DB_PASSWORD": "somepw",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
"spec": map[string]interface{}{
|
||||||
},
|
"type": "LoadBalancer",
|
||||||
types.GroupVersionKindName{
|
|
||||||
GVK: schema.GroupVersionKind{Version: "v1", Kind: "Secret"},
|
|
||||||
Name: "secret",
|
|
||||||
}: &resource.Resource{
|
|
||||||
Data: &unstructured.Unstructured{
|
|
||||||
Object: map[string]interface{}{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "Secret",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "foo-secret-877fcfhgt5",
|
|
||||||
"labels": map[string]interface{}{
|
|
||||||
"app": "nginx",
|
|
||||||
},
|
|
||||||
"annotations": map[string]interface{}{
|
|
||||||
"note": "This is a test annotation",
|
|
||||||
},
|
|
||||||
"creationTimestamp": nil,
|
|
||||||
},
|
|
||||||
"type": string(corev1.SecretTypeOpaque),
|
|
||||||
"data": map[string]interface{}{
|
|
||||||
"DB_USERNAME": base64.StdEncoding.EncodeToString([]byte("admin")),
|
|
||||||
"DB_PASSWORD": base64.StdEncoding.EncodeToString([]byte("somepw")),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
}),
|
||||||
},
|
|
||||||
}
|
}
|
||||||
l := setupTest(t)
|
l := makeLoader2(t)
|
||||||
app, err := New(l)
|
app, err := NewApplication(l, fs.MakeFakeFS())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unexpected error %v", err)
|
t.Fatalf("Unexpected construction error %v", err)
|
||||||
}
|
}
|
||||||
actual, err := app.Resources()
|
actual, err := app.MakeUncustomizedResMap()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unexpected error %v", err)
|
t.Fatalf("Unexpected RawResources error %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(actual, expected) {
|
if err := expected.ErrorIfNotEqual(actual); err != nil {
|
||||||
err = compareMap(actual, expected)
|
t.Fatalf("unexpected inequality: %v", err)
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRawResources(t *testing.T) {
|
|
||||||
expected := resource.ResourceCollection{
|
|
||||||
types.GroupVersionKindName{
|
|
||||||
GVK: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"},
|
|
||||||
Name: "dply1",
|
|
||||||
}: &resource.Resource{
|
|
||||||
Data: &unstructured.Unstructured{
|
|
||||||
Object: map[string]interface{}{
|
|
||||||
"apiVersion": "apps/v1",
|
|
||||||
"kind": "Deployment",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "dply1",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
l := setupTest(t)
|
|
||||||
app, err := New(l)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected error %v", err)
|
|
||||||
}
|
|
||||||
actual, err := app.RawResources()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected error %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := compareMap(actual, expected); err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func compareMap(m1, m2 resource.ResourceCollection) error {
|
|
||||||
if len(m1) != len(m2) {
|
|
||||||
keySet1 := []types.GroupVersionKindName{}
|
|
||||||
keySet2 := []types.GroupVersionKindName{}
|
|
||||||
for GVKn := range m1 {
|
|
||||||
keySet1 = append(keySet1, GVKn)
|
|
||||||
}
|
|
||||||
for GVKn := range m1 {
|
|
||||||
keySet2 = append(keySet2, GVKn)
|
|
||||||
}
|
|
||||||
return fmt.Errorf("maps has different number of entries: %#v doesn't equals %#v", keySet1, keySet2)
|
|
||||||
}
|
|
||||||
for GVKn, obj1 := range m1 {
|
|
||||||
obj2, found := m2[GVKn]
|
|
||||||
if !found {
|
|
||||||
return fmt.Errorf("%#v doesn't exist in %#v", GVKn, m2)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(obj1, obj2) {
|
|
||||||
return fmt.Errorf("%#v doesn't match %#v", obj1, obj2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
98
pkg/commands/addbase.go
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
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 commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||||
|
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||||
|
)
|
||||||
|
|
||||||
|
type addBaseOptions struct {
|
||||||
|
baseDirectoryPaths string
|
||||||
|
}
|
||||||
|
|
||||||
|
// newCmdAddBase adds the file path of the kustomize base to the kustomization file.
|
||||||
|
func newCmdAddBase(fsys fs.FileSystem) *cobra.Command {
|
||||||
|
var o addBaseOptions
|
||||||
|
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "base",
|
||||||
|
Short: "Adds one or more bases to the kustomization.yaml in current directory",
|
||||||
|
Example: `
|
||||||
|
add base {filepath1},{filepath2}`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
err := o.Validate(args)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = o.Complete(cmd, args)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return o.RunAddBase(fsys)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates addBase command.
|
||||||
|
func (o *addBaseOptions) Validate(args []string) error {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return errors.New("must specify a base directory")
|
||||||
|
}
|
||||||
|
o.baseDirectoryPaths = args[0]
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Complete completes addBase command.
|
||||||
|
func (o *addBaseOptions) Complete(cmd *cobra.Command, args []string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunAddBase runs addBase command (do real work).
|
||||||
|
func (o *addBaseOptions) RunAddBase(fsys fs.FileSystem) error {
|
||||||
|
mf, err := newKustomizationFile(constants.KustomizationFileName, fsys)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
m, err := mf.read()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// split directory paths
|
||||||
|
paths := strings.Split(o.baseDirectoryPaths, ",")
|
||||||
|
for _, path := range paths {
|
||||||
|
if !fsys.Exists(path) {
|
||||||
|
return errors.New(path + " does not exist")
|
||||||
|
}
|
||||||
|
if stringInSlice(path, m.Bases) {
|
||||||
|
return fmt.Errorf("base %s already in kustomization file", path)
|
||||||
|
}
|
||||||
|
m.Bases = append(m.Bases, path)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return mf.write(m)
|
||||||
|
}
|
||||||
100
pkg/commands/addbase_test.go
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
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 commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||||
|
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
baseDirectoryPaths = "my/path/to/wonderful/base,other/path/to/even/more/wonderful/base"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAddBaseHappyPath(t *testing.T) {
|
||||||
|
fakeFS := fs.MakeFakeFS()
|
||||||
|
bases := strings.Split(baseDirectoryPaths, ",")
|
||||||
|
for _, base := range bases {
|
||||||
|
fakeFS.Mkdir(base)
|
||||||
|
}
|
||||||
|
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
||||||
|
|
||||||
|
cmd := newCmdAddBase(fakeFS)
|
||||||
|
args := []string{baseDirectoryPaths}
|
||||||
|
err := cmd.RunE(cmd, args)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected cmd error: %v", err)
|
||||||
|
}
|
||||||
|
content, err := fakeFS.ReadFile(constants.KustomizationFileName)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected read error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, base := range bases {
|
||||||
|
if !strings.Contains(string(content), base) {
|
||||||
|
t.Errorf("expected base name in kustomization")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddBaseAlreadyThere(t *testing.T) {
|
||||||
|
fakeFS := fs.MakeFakeFS()
|
||||||
|
// Create fake directories
|
||||||
|
bases := strings.Split(baseDirectoryPaths, ",")
|
||||||
|
for _, base := range bases {
|
||||||
|
fakeFS.Mkdir(base)
|
||||||
|
}
|
||||||
|
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
||||||
|
|
||||||
|
cmd := newCmdAddBase(fakeFS)
|
||||||
|
args := []string{baseDirectoryPaths}
|
||||||
|
err := cmd.RunE(cmd, args)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected cmd error: %v", err)
|
||||||
|
}
|
||||||
|
// adding an existing base should return an error
|
||||||
|
err = cmd.RunE(cmd, args)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected already there problem")
|
||||||
|
}
|
||||||
|
var expectedErrors []string
|
||||||
|
for _, base := range bases {
|
||||||
|
msg := "base " + base + " already in kustomization file"
|
||||||
|
expectedErrors = append(expectedErrors, msg)
|
||||||
|
if !stringInSlice(msg, expectedErrors) {
|
||||||
|
t.Errorf("unexpected error %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddBaseNoArgs(t *testing.T) {
|
||||||
|
fakeFS := fs.MakeFakeFS()
|
||||||
|
|
||||||
|
cmd := newCmdAddBase(fakeFS)
|
||||||
|
err := cmd.Execute()
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected error: %v", err)
|
||||||
|
}
|
||||||
|
if err.Error() != "must specify a base directory" {
|
||||||
|
t.Errorf("incorrect error: %v", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,12 +19,11 @@ package commands
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/util/fs"
|
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||||
)
|
)
|
||||||
|
|
||||||
type addPatchOptions struct {
|
type addPatchOptions struct {
|
||||||
@@ -32,7 +31,7 @@ type addPatchOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// newCmdAddPatch adds the name of a file containing a patch to the kustomization file.
|
// newCmdAddPatch adds the name of a file containing a patch to the kustomization file.
|
||||||
func newCmdAddPatch(out, errOut io.Writer, fsys fs.FileSystem) *cobra.Command {
|
func newCmdAddPatch(fsys fs.FileSystem) *cobra.Command {
|
||||||
var o addPatchOptions
|
var o addPatchOptions
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
@@ -49,7 +48,7 @@ func newCmdAddPatch(out, errOut io.Writer, fsys fs.FileSystem) *cobra.Command {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return o.RunAddPatch(out, errOut, fsys)
|
return o.RunAddPatch(fsys)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return cmd
|
return cmd
|
||||||
@@ -70,10 +69,9 @@ func (o *addPatchOptions) Complete(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RunAddPatch runs addPatch command (do real work).
|
// RunAddPatch runs addPatch command (do real work).
|
||||||
func (o *addPatchOptions) RunAddPatch(out, errOut io.Writer, fsys fs.FileSystem) error {
|
func (o *addPatchOptions) RunAddPatch(fsys fs.FileSystem) error {
|
||||||
_, err := fsys.Stat(o.patchFilePath)
|
if !fsys.Exists(o.patchFilePath) {
|
||||||
if err != nil {
|
return errors.New(o.patchFilePath + " doesn't exist")
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mf, err := newKustomizationFile(constants.KustomizationFileName, fsys)
|
mf, err := newKustomizationFile(constants.KustomizationFileName, fsys)
|
||||||
|
|||||||
@@ -17,14 +17,12 @@ limitations under the License.
|
|||||||
package commands
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"os"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/util/fs"
|
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -36,12 +34,11 @@ sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestAddPatchHappyPath(t *testing.T) {
|
func TestAddPatchHappyPath(t *testing.T) {
|
||||||
buf := bytes.NewBuffer([]byte{})
|
|
||||||
fakeFS := fs.MakeFakeFS()
|
fakeFS := fs.MakeFakeFS()
|
||||||
fakeFS.WriteFile(patchFileName, []byte(patchFileContent))
|
fakeFS.WriteFile(patchFileName, []byte(patchFileContent))
|
||||||
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
||||||
|
|
||||||
cmd := newCmdAddPatch(buf, os.Stderr, fakeFS)
|
cmd := newCmdAddPatch(fakeFS)
|
||||||
args := []string{patchFileName}
|
args := []string{patchFileName}
|
||||||
err := cmd.RunE(cmd, args)
|
err := cmd.RunE(cmd, args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -57,12 +54,11 @@ func TestAddPatchHappyPath(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAddPatchAlreadyThere(t *testing.T) {
|
func TestAddPatchAlreadyThere(t *testing.T) {
|
||||||
buf := bytes.NewBuffer([]byte{})
|
|
||||||
fakeFS := fs.MakeFakeFS()
|
fakeFS := fs.MakeFakeFS()
|
||||||
fakeFS.WriteFile(patchFileName, []byte(patchFileContent))
|
fakeFS.WriteFile(patchFileName, []byte(patchFileContent))
|
||||||
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
||||||
|
|
||||||
cmd := newCmdAddPatch(buf, os.Stderr, fakeFS)
|
cmd := newCmdAddPatch(fakeFS)
|
||||||
args := []string{patchFileName}
|
args := []string{patchFileName}
|
||||||
err := cmd.RunE(cmd, args)
|
err := cmd.RunE(cmd, args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -80,10 +76,9 @@ func TestAddPatchAlreadyThere(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAddPatchNoArgs(t *testing.T) {
|
func TestAddPatchNoArgs(t *testing.T) {
|
||||||
buf := bytes.NewBuffer([]byte{})
|
|
||||||
fakeFS := fs.MakeFakeFS()
|
fakeFS := fs.MakeFakeFS()
|
||||||
|
|
||||||
cmd := newCmdAddPatch(buf, os.Stderr, fakeFS)
|
cmd := newCmdAddPatch(fakeFS)
|
||||||
err := cmd.Execute()
|
err := cmd.Execute()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("expected error: %v", err)
|
t.Errorf("expected error: %v", err)
|
||||||
|
|||||||
@@ -19,12 +19,11 @@ package commands
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/util/fs"
|
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||||
)
|
)
|
||||||
|
|
||||||
type addResourceOptions struct {
|
type addResourceOptions struct {
|
||||||
@@ -32,7 +31,7 @@ type addResourceOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// newCmdAddResource adds the name of a file containing a resource to the kustomization file.
|
// newCmdAddResource adds the name of a file containing a resource to the kustomization file.
|
||||||
func newCmdAddResource(out, errOut io.Writer, fsys fs.FileSystem) *cobra.Command {
|
func newCmdAddResource(fsys fs.FileSystem) *cobra.Command {
|
||||||
var o addResourceOptions
|
var o addResourceOptions
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
@@ -49,7 +48,7 @@ func newCmdAddResource(out, errOut io.Writer, fsys fs.FileSystem) *cobra.Command
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return o.RunAddResource(out, errOut, fsys)
|
return o.RunAddResource(fsys)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return cmd
|
return cmd
|
||||||
@@ -70,12 +69,10 @@ func (o *addResourceOptions) Complete(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RunAddResource runs addResource command (do real work).
|
// RunAddResource runs addResource command (do real work).
|
||||||
func (o *addResourceOptions) RunAddResource(out, errOut io.Writer, fsys fs.FileSystem) error {
|
func (o *addResourceOptions) RunAddResource(fsys fs.FileSystem) error {
|
||||||
_, err := fsys.Stat(o.resourceFilePath)
|
if !fsys.Exists(o.resourceFilePath) {
|
||||||
if err != nil {
|
return errors.New(o.resourceFilePath + " does not exist")
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mf, err := newKustomizationFile(constants.KustomizationFileName, fsys)
|
mf, err := newKustomizationFile(constants.KustomizationFileName, fsys)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -17,14 +17,12 @@ limitations under the License.
|
|||||||
package commands
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"os"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/util/fs"
|
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -52,12 +50,11 @@ secretGenerator: []
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestAddResourceHappyPath(t *testing.T) {
|
func TestAddResourceHappyPath(t *testing.T) {
|
||||||
buf := bytes.NewBuffer([]byte{})
|
|
||||||
fakeFS := fs.MakeFakeFS()
|
fakeFS := fs.MakeFakeFS()
|
||||||
fakeFS.WriteFile(resourceFileName, []byte(resourceFileContent))
|
fakeFS.WriteFile(resourceFileName, []byte(resourceFileContent))
|
||||||
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
||||||
|
|
||||||
cmd := newCmdAddResource(buf, os.Stderr, fakeFS)
|
cmd := newCmdAddResource(fakeFS)
|
||||||
args := []string{resourceFileName}
|
args := []string{resourceFileName}
|
||||||
err := cmd.RunE(cmd, args)
|
err := cmd.RunE(cmd, args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -73,12 +70,11 @@ func TestAddResourceHappyPath(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAddResourceAlreadyThere(t *testing.T) {
|
func TestAddResourceAlreadyThere(t *testing.T) {
|
||||||
buf := bytes.NewBuffer([]byte{})
|
|
||||||
fakeFS := fs.MakeFakeFS()
|
fakeFS := fs.MakeFakeFS()
|
||||||
fakeFS.WriteFile(resourceFileName, []byte(resourceFileContent))
|
fakeFS.WriteFile(resourceFileName, []byte(resourceFileContent))
|
||||||
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
||||||
|
|
||||||
cmd := newCmdAddResource(buf, os.Stderr, fakeFS)
|
cmd := newCmdAddResource(fakeFS)
|
||||||
args := []string{resourceFileName}
|
args := []string{resourceFileName}
|
||||||
err := cmd.RunE(cmd, args)
|
err := cmd.RunE(cmd, args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -96,10 +92,9 @@ func TestAddResourceAlreadyThere(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAddResourceNoArgs(t *testing.T) {
|
func TestAddResourceNoArgs(t *testing.T) {
|
||||||
buf := bytes.NewBuffer([]byte{})
|
|
||||||
fakeFS := fs.MakeFakeFS()
|
fakeFS := fs.MakeFakeFS()
|
||||||
|
|
||||||
cmd := newCmdAddResource(buf, os.Stderr, fakeFS)
|
cmd := newCmdAddResource(fakeFS)
|
||||||
err := cmd.Execute()
|
err := cmd.Execute()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("expected error: %v", err)
|
t.Errorf("expected error: %v", err)
|
||||||
|
|||||||
@@ -26,9 +26,8 @@ import (
|
|||||||
|
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/app"
|
"github.com/kubernetes-sigs/kustomize/pkg/app"
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||||
|
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/loader"
|
"github.com/kubernetes-sigs/kustomize/pkg/loader"
|
||||||
kutil "github.com/kubernetes-sigs/kustomize/pkg/util"
|
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/util/fs"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type buildOptions struct {
|
type buildOptions struct {
|
||||||
@@ -36,7 +35,7 @@ type buildOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// newCmdBuild creates a new build command.
|
// newCmdBuild creates a new build command.
|
||||||
func newCmdBuild(out, errOut io.Writer, fs fs.FileSystem) *cobra.Command {
|
func newCmdBuild(out io.Writer, fs fs.FileSystem) *cobra.Command {
|
||||||
var o buildOptions
|
var o buildOptions
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
@@ -44,12 +43,13 @@ func newCmdBuild(out, errOut io.Writer, fs fs.FileSystem) *cobra.Command {
|
|||||||
Short: "Print current configuration per contents of " + constants.KustomizationFileName,
|
Short: "Print current configuration per contents of " + constants.KustomizationFileName,
|
||||||
Example: "Use the file somedir/" + constants.KustomizationFileName +
|
Example: "Use the file somedir/" + constants.KustomizationFileName +
|
||||||
" to generate a set of api resources:\nbuild somedir/",
|
" to generate a set of api resources:\nbuild somedir/",
|
||||||
|
SilenceUsage: true,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
err := o.Validate(args)
|
err := o.Validate(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return o.RunBuild(out, errOut, fs)
|
return o.RunBuild(out, fs)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return cmd
|
return cmd
|
||||||
@@ -69,8 +69,8 @@ func (o *buildOptions) Validate(args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RunBuild runs build command.
|
// RunBuild runs build command.
|
||||||
func (o *buildOptions) RunBuild(out, errOut io.Writer, fs fs.FileSystem) error {
|
func (o *buildOptions) RunBuild(out io.Writer, fSys fs.FileSystem) error {
|
||||||
l := loader.Init([]loader.SchemeLoader{loader.NewFileLoader(fs)})
|
l := loader.NewLoader(loader.NewFileLoader(fSys))
|
||||||
|
|
||||||
absPath, err := filepath.Abs(o.kustomizationPath)
|
absPath, err := filepath.Abs(o.kustomizationPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -82,19 +82,19 @@ func (o *buildOptions) RunBuild(out, errOut io.Writer, fs fs.FileSystem) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
application, err := app.New(rootLoader)
|
application, err := app.NewApplication(rootLoader, fSys)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
allResources, err := application.Resources()
|
allResources, err := application.MakeCustomizedResMap()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output the objects.
|
// Output the objects.
|
||||||
res, err := kutil.Encode(allResources)
|
res, err := allResources.EncodeAsYaml()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import (
|
|||||||
"github.com/ghodss/yaml"
|
"github.com/ghodss/yaml"
|
||||||
|
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/util/fs"
|
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ func TestBuildValidate(t *testing.T) {
|
|||||||
func TestBuild(t *testing.T) {
|
func TestBuild(t *testing.T) {
|
||||||
const updateEnvVar = "UPDATE_KUSTOMIZE_EXPECTED_DATA"
|
const updateEnvVar = "UPDATE_KUSTOMIZE_EXPECTED_DATA"
|
||||||
updateKustomizeExpected := os.Getenv(updateEnvVar) == "true"
|
updateKustomizeExpected := os.Getenv(updateEnvVar) == "true"
|
||||||
fs := fs.MakeRealFS()
|
fSys := fs.MakeRealFS()
|
||||||
|
|
||||||
testcases := sets.NewString()
|
testcases := sets.NewString()
|
||||||
filepath.Walk("testdata", func(path string, info os.FileInfo, err error) error {
|
filepath.Walk("testdata", func(path string, info os.FileInfo, err error) error {
|
||||||
@@ -104,49 +104,51 @@ func TestBuild(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, testcaseName := range testcases.List() {
|
for _, testcaseName := range testcases.List() {
|
||||||
t.Run(testcaseName, func(t *testing.T) {
|
t.Run(testcaseName, func(t *testing.T) { runBuildTestCase(t, testcaseName, updateKustomizeExpected, fSys) })
|
||||||
name := testcaseName
|
}
|
||||||
testcase := buildTestCase{}
|
|
||||||
testcaseDir := filepath.Join("testdata", "testcase-"+name)
|
}
|
||||||
testcaseData, err := ioutil.ReadFile(filepath.Join(testcaseDir, "test.yaml"))
|
|
||||||
if err != nil {
|
func runBuildTestCase(t *testing.T, testcaseName string, updateKustomizeExpected bool, fSys fs.FileSystem) {
|
||||||
t.Fatalf("%s: %v", name, err)
|
name := testcaseName
|
||||||
}
|
testcase := buildTestCase{}
|
||||||
if err := yaml.Unmarshal(testcaseData, &testcase); err != nil {
|
testcaseDir := filepath.Join("testdata", "testcase-"+name)
|
||||||
t.Fatalf("%s: %v", name, err)
|
testcaseData, err := ioutil.ReadFile(filepath.Join(testcaseDir, "test.yaml"))
|
||||||
}
|
if err != nil {
|
||||||
|
t.Fatalf("%s: %v", name, err)
|
||||||
ops := &buildOptions{
|
}
|
||||||
kustomizationPath: testcase.Filename,
|
if err := yaml.Unmarshal(testcaseData, &testcase); err != nil {
|
||||||
}
|
t.Fatalf("%s: %v", name, err)
|
||||||
buf := bytes.NewBuffer([]byte{})
|
}
|
||||||
err = ops.RunBuild(buf, os.Stderr, fs)
|
|
||||||
switch {
|
ops := &buildOptions{
|
||||||
case err != nil && len(testcase.ExpectedError) == 0:
|
kustomizationPath: testcase.Filename,
|
||||||
t.Errorf("unexpected error: %v", err)
|
}
|
||||||
case err != nil && len(testcase.ExpectedError) != 0:
|
buf := bytes.NewBuffer([]byte{})
|
||||||
if !strings.Contains(err.Error(), testcase.ExpectedError) {
|
err = ops.RunBuild(buf, fSys)
|
||||||
t.Errorf("expected error to contain %q but got: %v", testcase.ExpectedError, err)
|
switch {
|
||||||
}
|
case err != nil && len(testcase.ExpectedError) == 0:
|
||||||
return
|
t.Errorf("unexpected error: %v", err)
|
||||||
case err == nil && len(testcase.ExpectedError) != 0:
|
case err != nil && len(testcase.ExpectedError) != 0:
|
||||||
t.Errorf("unexpected no error")
|
if !strings.Contains(err.Error(), testcase.ExpectedError) {
|
||||||
}
|
t.Errorf("expected error to contain %q but got: %v", testcase.ExpectedError, err)
|
||||||
|
}
|
||||||
actualBytes := buf.Bytes()
|
return
|
||||||
if !updateKustomizeExpected {
|
case err == nil && len(testcase.ExpectedError) != 0:
|
||||||
expectedBytes, err := ioutil.ReadFile(testcase.ExpectedStdout)
|
t.Errorf("unexpected no error")
|
||||||
if err != nil {
|
}
|
||||||
t.Errorf("unexpected error: %v", err)
|
|
||||||
}
|
actualBytes := buf.Bytes()
|
||||||
if !reflect.DeepEqual(actualBytes, expectedBytes) {
|
if !updateKustomizeExpected {
|
||||||
t.Errorf("%s\ndoesn't equal expected:\n%s\n", actualBytes, expectedBytes)
|
expectedBytes, err := ioutil.ReadFile(testcase.ExpectedStdout)
|
||||||
}
|
if err != nil {
|
||||||
} else {
|
t.Errorf("unexpected error: %v", err)
|
||||||
ioutil.WriteFile(testcase.ExpectedStdout, actualBytes, 0644)
|
}
|
||||||
}
|
if !reflect.DeepEqual(actualBytes, expectedBytes) {
|
||||||
|
t.Errorf("%s\ndoesn't equal expected:\n%s\n", actualBytes, expectedBytes)
|
||||||
})
|
}
|
||||||
|
} else {
|
||||||
|
ioutil.WriteFile(testcase.ExpectedStdout, actualBytes, 0644)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// dataConfig encapsulates the options for add configmap/Secret commands.
|
// cMapFlagsAndArgs encapsulates the options for add configmap commands.
|
||||||
type dataConfig struct {
|
type cMapFlagsAndArgs struct {
|
||||||
// Name of configMap/Secret (required)
|
// Name of configMap/Secret (required)
|
||||||
Name string
|
Name string
|
||||||
// FileSources to derive the configMap/Secret from (optional)
|
// FileSources to derive the configMap/Secret from (optional)
|
||||||
@@ -34,7 +34,7 @@ type dataConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates required fields are set to support structured generation.
|
// Validate validates required fields are set to support structured generation.
|
||||||
func (a *dataConfig) Validate(args []string) error {
|
func (a *cMapFlagsAndArgs) Validate(args []string) error {
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return fmt.Errorf("name must be specified once")
|
return fmt.Errorf("name must be specified once")
|
||||||
}
|
}
|
||||||
@@ -21,7 +21,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestDataConfigValidation_NoName(t *testing.T) {
|
func TestDataConfigValidation_NoName(t *testing.T) {
|
||||||
config := dataConfig{}
|
config := cMapFlagsAndArgs{}
|
||||||
|
|
||||||
if config.Validate([]string{}) == nil {
|
if config.Validate([]string{}) == nil {
|
||||||
t.Fatal("Validation should fail if no name is specified")
|
t.Fatal("Validation should fail if no name is specified")
|
||||||
@@ -29,7 +29,7 @@ func TestDataConfigValidation_NoName(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDataConfigValidation_MoreThanOneName(t *testing.T) {
|
func TestDataConfigValidation_MoreThanOneName(t *testing.T) {
|
||||||
config := dataConfig{}
|
config := cMapFlagsAndArgs{}
|
||||||
|
|
||||||
if config.Validate([]string{"name", "othername"}) == nil {
|
if config.Validate([]string{"name", "othername"}) == nil {
|
||||||
t.Fatal("Validation should fail if more than one name is specified")
|
t.Fatal("Validation should fail if more than one name is specified")
|
||||||
@@ -39,12 +39,12 @@ func TestDataConfigValidation_MoreThanOneName(t *testing.T) {
|
|||||||
func TestDataConfigValidation_Flags(t *testing.T) {
|
func TestDataConfigValidation_Flags(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
config dataConfig
|
config cMapFlagsAndArgs
|
||||||
shouldFail bool
|
shouldFail bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "env-file-source and literal are both set",
|
name: "env-file-source and literal are both set",
|
||||||
config: dataConfig{
|
config: cMapFlagsAndArgs{
|
||||||
LiteralSources: []string{"one", "two"},
|
LiteralSources: []string{"one", "two"},
|
||||||
EnvFileSource: "three",
|
EnvFileSource: "three",
|
||||||
},
|
},
|
||||||
@@ -52,7 +52,7 @@ func TestDataConfigValidation_Flags(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "env-file-source and from-file are both set",
|
name: "env-file-source and from-file are both set",
|
||||||
config: dataConfig{
|
config: cMapFlagsAndArgs{
|
||||||
FileSources: []string{"one", "two"},
|
FileSources: []string{"one", "two"},
|
||||||
EnvFileSource: "three",
|
EnvFileSource: "three",
|
||||||
},
|
},
|
||||||
@@ -60,12 +60,12 @@ func TestDataConfigValidation_Flags(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "we don't have any option set",
|
name: "we don't have any option set",
|
||||||
config: dataConfig{},
|
config: cMapFlagsAndArgs{},
|
||||||
shouldFail: true,
|
shouldFail: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "we have from-file and literal ",
|
name: "we have from-file and literal ",
|
||||||
config: dataConfig{
|
config: cMapFlagsAndArgs{
|
||||||
LiteralSources: []string{"one", "two"},
|
LiteralSources: []string{"one", "two"},
|
||||||
FileSources: []string{"three", "four"},
|
FileSources: []string{"three", "four"},
|
||||||
},
|
},
|
||||||
@@ -14,15 +14,14 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Package commands holds the CLI glue mapping textual commands/args to method calls.
|
||||||
package commands
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"io"
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/util/fs"
|
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||||
"github.com/kubernetes-sigs/kustomize/version"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -37,15 +36,16 @@ func NewDefaultCommand() *cobra.Command {
|
|||||||
Long: `
|
Long: `
|
||||||
kustomize manages declarative configuration of Kubernetes.
|
kustomize manages declarative configuration of Kubernetes.
|
||||||
|
|
||||||
More info at https://github.com/kubernetes/kubectl/tree/master/cmd/kustomize
|
See https://github.com/kubernetes-sigs/kustomize
|
||||||
`,
|
`,
|
||||||
}
|
}
|
||||||
|
|
||||||
c.AddCommand(
|
c.AddCommand(
|
||||||
newCmdBuild(stdOut, stdErr, fsys),
|
// TODO: Make consistent API for newCmd* functions.
|
||||||
|
newCmdBuild(stdOut, fsys),
|
||||||
newCmdDiff(stdOut, stdErr, fsys),
|
newCmdDiff(stdOut, stdErr, fsys),
|
||||||
newCmdEdit(stdOut, stdErr, fsys),
|
newCmdEdit(fsys),
|
||||||
version.NewCmdVersion(stdOut),
|
newCmdVersion(stdOut),
|
||||||
)
|
)
|
||||||
c.PersistentFlags().AddGoFlagSet(flag.CommandLine)
|
c.PersistentFlags().AddGoFlagSet(flag.CommandLine)
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ More info at https://github.com/kubernetes/kubectl/tree/master/cmd/kustomize
|
|||||||
}
|
}
|
||||||
|
|
||||||
// newCmdEdit returns an instance of 'edit' subcommand.
|
// newCmdEdit returns an instance of 'edit' subcommand.
|
||||||
func newCmdEdit(stdOut, stdErr io.Writer, fsys fs.FileSystem) *cobra.Command {
|
func newCmdEdit(fsys fs.FileSystem) *cobra.Command {
|
||||||
c := &cobra.Command{
|
c := &cobra.Command{
|
||||||
Use: "edit",
|
Use: "edit",
|
||||||
Short: "Edits a kustomization file",
|
Short: "Edits a kustomization file",
|
||||||
@@ -71,43 +71,45 @@ func newCmdEdit(stdOut, stdErr io.Writer, fsys fs.FileSystem) *cobra.Command {
|
|||||||
Args: cobra.MinimumNArgs(1),
|
Args: cobra.MinimumNArgs(1),
|
||||||
}
|
}
|
||||||
c.AddCommand(
|
c.AddCommand(
|
||||||
newCmdAdd(stdOut, stdErr, fsys),
|
newCmdAdd(fsys),
|
||||||
newCmdSet(stdOut, stdErr, fsys),
|
newCmdSet(fsys),
|
||||||
)
|
)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// newAddCommand returns an instance of 'add' subcommand.
|
// newAddCommand returns an instance of 'add' subcommand.
|
||||||
func newCmdAdd(stdOut, stdErr io.Writer, fsys fs.FileSystem) *cobra.Command {
|
func newCmdAdd(fsys fs.FileSystem) *cobra.Command {
|
||||||
c := &cobra.Command{
|
c := &cobra.Command{
|
||||||
Use: "add",
|
Use: "add",
|
||||||
Short: "Adds configmap/resource/secret to the kustomization file.",
|
Short: "Adds configmap/resource/patch/base to the kustomization file.",
|
||||||
Long: "",
|
Long: "",
|
||||||
Example: `
|
Example: `
|
||||||
# Adds a configmap to the kustomization file
|
# Adds a configmap to the kustomization file
|
||||||
kustomize edit add configmap NAME --from-literal=k=v
|
kustomize edit add configmap NAME --from-literal=k=v
|
||||||
|
|
||||||
# Adds a secret to the kustomization file
|
|
||||||
kustomize edit add secret NAME --from-literal=k=v
|
|
||||||
|
|
||||||
# Adds a resource to the kustomization
|
# Adds a resource to the kustomization
|
||||||
kustomize edit add resource <filepath>
|
kustomize edit add resource <filepath>
|
||||||
|
|
||||||
# Adds a patch to the kustomization
|
# Adds a patch to the kustomization
|
||||||
kustomize edit add patch <filepath>
|
kustomize edit add patch <filepath>
|
||||||
|
|
||||||
|
# Adds one or more base directories to the kustomization
|
||||||
|
kustomize edit add base <filepath>
|
||||||
|
kustomize edit add base <filepath1>,<filepath2>,<filepath3>
|
||||||
`,
|
`,
|
||||||
Args: cobra.MinimumNArgs(1),
|
Args: cobra.MinimumNArgs(1),
|
||||||
}
|
}
|
||||||
c.AddCommand(
|
c.AddCommand(
|
||||||
newCmdAddResource(stdOut, stdErr, fsys),
|
newCmdAddResource(fsys),
|
||||||
newCmdAddPatch(stdOut, stdErr, fsys),
|
newCmdAddPatch(fsys),
|
||||||
newCmdAddConfigMap(stdErr, fsys),
|
newCmdAddConfigMap(fsys),
|
||||||
|
newCmdAddBase(fsys),
|
||||||
)
|
)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// newSetCommand returns an instance of 'set' subcommand.
|
// newSetCommand returns an instance of 'set' subcommand.
|
||||||
func newCmdSet(stdOut, stdErr io.Writer, fsys fs.FileSystem) *cobra.Command {
|
func newCmdSet(fsys fs.FileSystem) *cobra.Command {
|
||||||
c := &cobra.Command{
|
c := &cobra.Command{
|
||||||
Use: "set",
|
Use: "set",
|
||||||
Short: "Sets the value of different fields in kustomization file.",
|
Short: "Sets the value of different fields in kustomization file.",
|
||||||
@@ -120,7 +122,8 @@ func newCmdSet(stdOut, stdErr io.Writer, fsys fs.FileSystem) *cobra.Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
c.AddCommand(
|
c.AddCommand(
|
||||||
newCmdSetNamePrefix(stdOut, stdErr, fsys),
|
newCmdSetNamePrefix(fsys),
|
||||||
|
newCmdSetImageTag(fsys),
|
||||||
)
|
)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,18 +18,18 @@ package commands
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/configmapandsecret"
|
"github.com/kubernetes-sigs/kustomize/pkg/configmapandsecret"
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||||
|
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||||
|
"github.com/kubernetes-sigs/kustomize/pkg/loader"
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/util/fs"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func newCmdAddConfigMap(errOut io.Writer, fsys fs.FileSystem) *cobra.Command {
|
func newCmdAddConfigMap(fSys fs.FileSystem) *cobra.Command {
|
||||||
var config dataConfig
|
var flagsAndArgs cMapFlagsAndArgs
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "configmap NAME [--from-file=[key=]source] [--from-literal=key1=value1]",
|
Use: "configmap NAME [--from-file=[key=]source] [--from-literal=key1=value1]",
|
||||||
Short: "Adds a configmap to the kustomization file.",
|
Short: "Adds a configmap to the kustomization file.",
|
||||||
@@ -45,45 +45,50 @@ func newCmdAddConfigMap(errOut io.Writer, fsys fs.FileSystem) *cobra.Command {
|
|||||||
kustomize edit add configmap my-configmap --from-env-file=env/path.env
|
kustomize edit add configmap my-configmap --from-env-file=env/path.env
|
||||||
`,
|
`,
|
||||||
RunE: func(_ *cobra.Command, args []string) error {
|
RunE: func(_ *cobra.Command, args []string) error {
|
||||||
err := config.Validate(args)
|
err := flagsAndArgs.Validate(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load in the kustomization file.
|
// Load the kustomization file.
|
||||||
mf, err := newKustomizationFile(constants.KustomizationFileName, fsys)
|
mf, err := newKustomizationFile(constants.KustomizationFileName, fSys)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
m, err := mf.read()
|
kustomization, err := mf.read()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the config map to the kustomization file.
|
// Add the flagsAndArgs map to the kustomization file.
|
||||||
err = addConfigMap(m, config)
|
err = addConfigMap(
|
||||||
|
kustomization, flagsAndArgs,
|
||||||
|
configmapandsecret.NewConfigMapFactory(
|
||||||
|
fSys, loader.NewLoader(loader.NewFileLoader(fSys))))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write out the kustomization file with added configmap.
|
// Write out the kustomization file with added configmap.
|
||||||
return mf.write(m)
|
return mf.write(kustomization)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.Flags().StringSliceVar(
|
cmd.Flags().StringSliceVar(
|
||||||
&config.FileSources,
|
&flagsAndArgs.FileSources,
|
||||||
"from-file",
|
"from-file",
|
||||||
[]string{},
|
[]string{},
|
||||||
"Key file can be specified using its file path, in which case file basename will be used as configmap key, or optionally with a key and file path, in which case the given key will be used. Specifying a directory will iterate each named file in the directory whose basename is a valid configmap key.")
|
"Key file can be specified using its file path, in which case file basename will be used as configmap "+
|
||||||
|
"key, or optionally with a key and file path, in which case the given key will be used. Specifying a "+
|
||||||
|
"directory will iterate each named file in the directory whose basename is a valid configmap key.")
|
||||||
cmd.Flags().StringArrayVar(
|
cmd.Flags().StringArrayVar(
|
||||||
&config.LiteralSources,
|
&flagsAndArgs.LiteralSources,
|
||||||
"from-literal",
|
"from-literal",
|
||||||
[]string{},
|
[]string{},
|
||||||
"Specify a key and literal value to insert in configmap (i.e. mykey=somevalue)")
|
"Specify a key and literal value to insert in configmap (i.e. mykey=somevalue)")
|
||||||
cmd.Flags().StringVar(
|
cmd.Flags().StringVar(
|
||||||
&config.EnvFileSource,
|
&flagsAndArgs.EnvFileSource,
|
||||||
"from-env-file",
|
"from-env-file",
|
||||||
"",
|
"",
|
||||||
"Specify the path to a file to read lines of key=val pairs to create a configmap (i.e. a Docker .env file).")
|
"Specify the path to a file to read lines of key=val pairs to create a configmap (i.e. a Docker .env file).")
|
||||||
@@ -91,27 +96,27 @@ func newCmdAddConfigMap(errOut io.Writer, fsys fs.FileSystem) *cobra.Command {
|
|||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
// addConfigMap updates a configmap within a kustomization file, using the data in config.
|
// addConfigMap adds a configmap to a kustomization file.
|
||||||
// Note: error may leave kustomization file in an undefined state. Suggest passing a copy
|
// Note: error may leave kustomization file in an undefined state.
|
||||||
// of kustomization file.
|
// Suggest passing a copy of kustomization file.
|
||||||
func addConfigMap(m *types.Kustomization, config dataConfig) error {
|
func addConfigMap(
|
||||||
cm := getOrCreateConfigMap(m, config.Name)
|
k *types.Kustomization,
|
||||||
|
flagsAndArgs cMapFlagsAndArgs,
|
||||||
err := mergeData(&cm.DataSources, config)
|
factory *configmapandsecret.ConfigMapFactory) error {
|
||||||
|
cmArgs := makeConfigMapArgs(k, flagsAndArgs.Name)
|
||||||
|
err := mergeFlagsIntoCmArgs(&cmArgs.DataSources, flagsAndArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate by trying to create corev1.configmap.
|
// Validate by trying to create corev1.configmap.
|
||||||
_, _, err = configmapandsecret.MakeConfigmapAndGenerateName(*cm)
|
_, _, err = factory.MakeUnstructAndGenerateName(cmArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getOrCreateConfigMap(m *types.Kustomization, name string) *types.ConfigMapArgs {
|
func makeConfigMapArgs(m *types.Kustomization, name string) *types.ConfigMapArgs {
|
||||||
for i, v := range m.ConfigMapGenerator {
|
for i, v := range m.ConfigMapGenerator {
|
||||||
if name == v.Name {
|
if name == v.Name {
|
||||||
return &m.ConfigMapGenerator[i]
|
return &m.ConfigMapGenerator[i]
|
||||||
@@ -123,13 +128,12 @@ func getOrCreateConfigMap(m *types.Kustomization, name string) *types.ConfigMapA
|
|||||||
return &m.ConfigMapGenerator[len(m.ConfigMapGenerator)-1]
|
return &m.ConfigMapGenerator[len(m.ConfigMapGenerator)-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
func mergeData(src *types.DataSources, config dataConfig) error {
|
func mergeFlagsIntoCmArgs(src *types.DataSources, flags cMapFlagsAndArgs) error {
|
||||||
src.LiteralSources = append(src.LiteralSources, config.LiteralSources...)
|
src.LiteralSources = append(src.LiteralSources, flags.LiteralSources...)
|
||||||
src.FileSources = append(src.FileSources, config.FileSources...)
|
src.FileSources = append(src.FileSources, flags.FileSources...)
|
||||||
if src.EnvSource != "" && src.EnvSource != config.EnvFileSource {
|
if src.EnvSource != "" && src.EnvSource != flags.EnvFileSource {
|
||||||
return fmt.Errorf("updating existing env source '%s' not allowed.", src.EnvSource)
|
return fmt.Errorf("updating existing env source '%s' not allowed", src.EnvSource)
|
||||||
}
|
}
|
||||||
src.EnvSource = config.EnvFileSource
|
src.EnvSource = flags.EnvFileSource
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,17 +19,17 @@ package commands
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/util/fs"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewAddConfigMapIsNotNil(t *testing.T) {
|
func TestNewAddConfigMapIsNotNil(t *testing.T) {
|
||||||
if newCmdAddConfigMap(nil, fs.MakeFakeFS()) == nil {
|
if newCmdAddConfigMap(fs.MakeFakeFS()) == nil {
|
||||||
t.Fatal("newCmdAddConfigMap shouldn't be nil")
|
t.Fatal("newCmdAddConfigMap shouldn't be nil")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetOrCreateConfigMap(t *testing.T) {
|
func TestMakeConfigMapArgs(t *testing.T) {
|
||||||
cmName := "test-config-name"
|
cmName := "test-config-name"
|
||||||
|
|
||||||
kustomization := &types.Kustomization{
|
kustomization := &types.Kustomization{
|
||||||
@@ -39,24 +39,24 @@ func TestGetOrCreateConfigMap(t *testing.T) {
|
|||||||
if len(kustomization.ConfigMapGenerator) != 0 {
|
if len(kustomization.ConfigMapGenerator) != 0 {
|
||||||
t.Fatal("Initial kustomization should not have any configmaps")
|
t.Fatal("Initial kustomization should not have any configmaps")
|
||||||
}
|
}
|
||||||
cm := getOrCreateConfigMap(kustomization, cmName)
|
args := makeConfigMapArgs(kustomization, cmName)
|
||||||
|
|
||||||
if cm == nil {
|
if args == nil {
|
||||||
t.Fatalf("ConfigMap should always be non-nil")
|
t.Fatalf("args should always be non-nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(kustomization.ConfigMapGenerator) != 1 {
|
if len(kustomization.ConfigMapGenerator) != 1 {
|
||||||
t.Fatalf("Kustomization should have newly created configmap")
|
t.Fatalf("Kustomization should have newly created configmap")
|
||||||
}
|
}
|
||||||
|
|
||||||
if &kustomization.ConfigMapGenerator[len(kustomization.ConfigMapGenerator)-1] != cm {
|
if &kustomization.ConfigMapGenerator[len(kustomization.ConfigMapGenerator)-1] != args {
|
||||||
t.Fatalf("Pointer address for newly inserted configmap should be same")
|
t.Fatalf("Pointer address for newly inserted configmap generator should be same")
|
||||||
}
|
}
|
||||||
|
|
||||||
existingCM := getOrCreateConfigMap(kustomization, cmName)
|
args2 := makeConfigMapArgs(kustomization, cmName)
|
||||||
|
|
||||||
if existingCM != cm {
|
if args2 != args {
|
||||||
t.Fatalf("should have returned an existing cm with name: %v", cmName)
|
t.Fatalf("should have returned an existing args with name: %v", cmName)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(kustomization.ConfigMapGenerator) != 1 {
|
if len(kustomization.ConfigMapGenerator) != 1 {
|
||||||
@@ -64,10 +64,10 @@ func TestGetOrCreateConfigMap(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMergeData_LiteralSources(t *testing.T) {
|
func TestMergeFlagsIntoCmArgs_LiteralSources(t *testing.T) {
|
||||||
ds := &types.DataSources{}
|
ds := &types.DataSources{}
|
||||||
|
|
||||||
err := mergeData(ds, dataConfig{LiteralSources: []string{"k1=v1"}})
|
err := mergeFlagsIntoCmArgs(ds, cMapFlagsAndArgs{LiteralSources: []string{"k1=v1"}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Merge initial literal source should not return error")
|
t.Fatalf("Merge initial literal source should not return error")
|
||||||
}
|
}
|
||||||
@@ -76,7 +76,7 @@ func TestMergeData_LiteralSources(t *testing.T) {
|
|||||||
t.Fatalf("Initial literal source should have been added")
|
t.Fatalf("Initial literal source should have been added")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = mergeData(ds, dataConfig{LiteralSources: []string{"k2=v2"}})
|
err = mergeFlagsIntoCmArgs(ds, cMapFlagsAndArgs{LiteralSources: []string{"k2=v2"}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Merge second literal source should not return error")
|
t.Fatalf("Merge second literal source should not return error")
|
||||||
}
|
}
|
||||||
@@ -86,10 +86,10 @@ func TestMergeData_LiteralSources(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMergeData_FileSources(t *testing.T) {
|
func TestMergeFlagsIntoCmArgs_FileSources(t *testing.T) {
|
||||||
ds := &types.DataSources{}
|
ds := &types.DataSources{}
|
||||||
|
|
||||||
err := mergeData(ds, dataConfig{FileSources: []string{"file1"}})
|
err := mergeFlagsIntoCmArgs(ds, cMapFlagsAndArgs{FileSources: []string{"file1"}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Merge initial file source should not return error")
|
t.Fatalf("Merge initial file source should not return error")
|
||||||
}
|
}
|
||||||
@@ -98,7 +98,7 @@ func TestMergeData_FileSources(t *testing.T) {
|
|||||||
t.Fatalf("Initial file source should have been added")
|
t.Fatalf("Initial file source should have been added")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = mergeData(ds, dataConfig{FileSources: []string{"file2"}})
|
err = mergeFlagsIntoCmArgs(ds, cMapFlagsAndArgs{FileSources: []string{"file2"}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Merge second file source should not return error")
|
t.Fatalf("Merge second file source should not return error")
|
||||||
}
|
}
|
||||||
@@ -108,12 +108,12 @@ func TestMergeData_FileSources(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMergeData_EnvSource(t *testing.T) {
|
func TestMergeFlagsIntoCmArgs_EnvSource(t *testing.T) {
|
||||||
envFileName := "env1"
|
envFileName := "env1"
|
||||||
envFileName2 := "env2"
|
envFileName2 := "env2"
|
||||||
ds := &types.DataSources{}
|
ds := &types.DataSources{}
|
||||||
|
|
||||||
err := mergeData(ds, dataConfig{EnvFileSource: envFileName})
|
err := mergeFlagsIntoCmArgs(ds, cMapFlagsAndArgs{EnvFileSource: envFileName})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Merge initial env source should not return error")
|
t.Fatalf("Merge initial env source should not return error")
|
||||||
}
|
}
|
||||||
@@ -122,7 +122,7 @@ func TestMergeData_EnvSource(t *testing.T) {
|
|||||||
t.Fatalf("Initial env source filename should have been added")
|
t.Fatalf("Initial env source filename should have been added")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = mergeData(ds, dataConfig{EnvFileSource: envFileName2})
|
err = mergeFlagsIntoCmArgs(ds, cMapFlagsAndArgs{EnvFileSource: envFileName2})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("Updating env source should return an error")
|
t.Fatalf("Updating env source should return an error")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,10 +25,9 @@ import (
|
|||||||
|
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/app"
|
"github.com/kubernetes-sigs/kustomize/pkg/app"
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||||
|
"github.com/kubernetes-sigs/kustomize/pkg/diff"
|
||||||
|
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/loader"
|
"github.com/kubernetes-sigs/kustomize/pkg/loader"
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/util"
|
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/util/fs"
|
|
||||||
"k8s.io/utils/exec"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type diffOptions struct {
|
type diffOptions struct {
|
||||||
@@ -43,7 +42,7 @@ func newCmdDiff(out, errOut io.Writer, fs fs.FileSystem) *cobra.Command {
|
|||||||
Use: "diff [path]",
|
Use: "diff [path]",
|
||||||
Short: "diff between customized resources and uncustomized resources",
|
Short: "diff between customized resources and uncustomized resources",
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
err := o.Validate(cmd, args)
|
err := o.Validate(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -54,7 +53,7 @@ func newCmdDiff(out, errOut io.Writer, fs fs.FileSystem) *cobra.Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates diff command.
|
// Validate validates diff command.
|
||||||
func (o *diffOptions) Validate(cmd *cobra.Command, args []string) error {
|
func (o *diffOptions) Validate(args []string) error {
|
||||||
if len(args) > 1 {
|
if len(args) > 1 {
|
||||||
return errors.New("specify one path to " + constants.KustomizationFileName)
|
return errors.New("specify one path to " + constants.KustomizationFileName)
|
||||||
}
|
}
|
||||||
@@ -66,16 +65,10 @@ func (o *diffOptions) Validate(cmd *cobra.Command, args []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunDiff gets the differences between Application.Resources() and Application.RawResources().
|
// RunDiff gets the differences between Application.MakeCustomizedResMap() and Application.MakeUncustomizedResMap().
|
||||||
func (o *diffOptions) RunDiff(out, errOut io.Writer, fs fs.FileSystem) error {
|
func (o *diffOptions) RunDiff(out, errOut io.Writer, fSys fs.FileSystem) error {
|
||||||
printer := util.Printer{}
|
|
||||||
diff := util.DiffProgram{
|
|
||||||
Exec: exec.New(),
|
|
||||||
Stdout: out,
|
|
||||||
Stderr: errOut,
|
|
||||||
}
|
|
||||||
|
|
||||||
l := loader.Init([]loader.SchemeLoader{loader.NewFileLoader(fs)})
|
l := loader.NewLoader(loader.NewFileLoader(fSys))
|
||||||
|
|
||||||
absPath, err := filepath.Abs(o.kustomizationPath)
|
absPath, err := filepath.Abs(o.kustomizationPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -87,30 +80,18 @@ func (o *diffOptions) RunDiff(out, errOut io.Writer, fs fs.FileSystem) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
application, err := app.New(rootLoader)
|
application, err := app.NewApplication(rootLoader, fSys)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
resources, err := application.Resources()
|
transformedResources, err := application.MakeCustomizedResMap()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
rawResources, err := application.RawResources()
|
rawResources, err := application.MakeUncustomizedResMap()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
transformedDir, err := util.WriteToDir(resources, "transformed", printer)
|
return diff.RunDiff(rawResources, transformedResources, out, errOut)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer transformedDir.Delete()
|
|
||||||
|
|
||||||
noopDir, err := util.WriteToDir(rawResources, "noop", printer)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer noopDir.Delete()
|
|
||||||
|
|
||||||
return diff.Run(noopDir.Name, transformedDir.Name)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import (
|
|||||||
|
|
||||||
"github.com/ghodss/yaml"
|
"github.com/ghodss/yaml"
|
||||||
|
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/util/fs"
|
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -44,11 +44,12 @@ func TestDiff(t *testing.T) {
|
|||||||
const updateEnvVar = "UPDATE_KUSTOMIZE_EXPECTED_DATA"
|
const updateEnvVar = "UPDATE_KUSTOMIZE_EXPECTED_DATA"
|
||||||
updateKustomizeExpected := os.Getenv(updateEnvVar) == "true"
|
updateKustomizeExpected := os.Getenv(updateEnvVar) == "true"
|
||||||
|
|
||||||
noopDir, _ := regexp.Compile(`/tmp/noop-[0-9]*/`)
|
tempDir := regexp.QuoteMeta(filepath.Clean(os.TempDir()))
|
||||||
transformedDir, _ := regexp.Compile(`/tmp/transformed-[0-9]*/`)
|
noopDir, _ := regexp.Compile(tempDir + `/noop-[0-9]*/`)
|
||||||
|
transformedDir, _ := regexp.Compile(tempDir + `/transformed-[0-9]*/`)
|
||||||
timestamp, _ := regexp.Compile(`[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1]) (2[0-3]|[01][0-9]):[0-5][0-9]:[0-5][0-9].[0-9]* [+-]{1}[0-9]{4}`)
|
timestamp, _ := regexp.Compile(`[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1]) (2[0-3]|[01][0-9]):[0-5][0-9]:[0-5][0-9].[0-9]* [+-]{1}[0-9]{4}`)
|
||||||
|
|
||||||
fs := fs.MakeRealFS()
|
fSys := fs.MakeRealFS()
|
||||||
|
|
||||||
testcases := sets.NewString()
|
testcases := sets.NewString()
|
||||||
filepath.Walk("testdata", func(path string, info os.FileInfo, err error) error {
|
filepath.Walk("testdata", func(path string, info os.FileInfo, err error) error {
|
||||||
@@ -74,51 +75,57 @@ func TestDiff(t *testing.T) {
|
|||||||
|
|
||||||
for _, testcaseName := range testcases.List() {
|
for _, testcaseName := range testcases.List() {
|
||||||
t.Run(testcaseName, func(t *testing.T) {
|
t.Run(testcaseName, func(t *testing.T) {
|
||||||
name := testcaseName
|
runDiffTestCase(t, testcaseName, updateKustomizeExpected, fSys,
|
||||||
testcase := DiffTestCase{}
|
noopDir, transformedDir, timestamp)
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
diffOps := &diffOptions{
|
|
||||||
kustomizationPath: testcase.Filename,
|
|
||||||
}
|
|
||||||
buf := bytes.NewBuffer([]byte{})
|
|
||||||
err = diffOps.RunDiff(buf, os.Stderr, fs)
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
|
|
||||||
actualString := string(buf.Bytes())
|
|
||||||
actualString = noopDir.ReplaceAllString(actualString, "/tmp/noop/")
|
|
||||||
actualString = transformedDir.ReplaceAllString(actualString, "/tmp/transformed/")
|
|
||||||
actualString = timestamp.ReplaceAllString(actualString, "YYYY-MM-DD HH:MM:SS")
|
|
||||||
actualBytes := []byte(actualString)
|
|
||||||
if !updateKustomizeExpected {
|
|
||||||
expectedBytes, err := ioutil.ReadFile(testcase.ExpectedDiff)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(actualBytes, expectedBytes) {
|
|
||||||
t.Errorf("%s\ndoesn't equal expected:\n%s\n", actualBytes, expectedBytes)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ioutil.WriteFile(testcase.ExpectedDiff, actualBytes, 0644)
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func runDiffTestCase(t *testing.T, testcaseName string, updateKustomizeExpected bool, fs fs.FileSystem,
|
||||||
|
noopDir, transformedDir, timestamp *regexp.Regexp) {
|
||||||
|
name := testcaseName
|
||||||
|
testcase := DiffTestCase{}
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
diffOps := &diffOptions{
|
||||||
|
kustomizationPath: testcase.Filename,
|
||||||
|
}
|
||||||
|
buf := bytes.NewBuffer([]byte{})
|
||||||
|
err = diffOps.RunDiff(buf, os.Stderr, fs)
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
|
actualString := string(buf.Bytes())
|
||||||
|
actualString = noopDir.ReplaceAllString(actualString, "/tmp/noop/")
|
||||||
|
actualString = transformedDir.ReplaceAllString(actualString, "/tmp/transformed/")
|
||||||
|
actualString = timestamp.ReplaceAllString(actualString, "YYYY-MM-DD HH:MM:SS")
|
||||||
|
actualBytes := []byte(actualString)
|
||||||
|
if !updateKustomizeExpected {
|
||||||
|
expectedBytes, err := ioutil.ReadFile(testcase.ExpectedDiff)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(actualBytes, expectedBytes) {
|
||||||
|
t.Errorf("%s\ndoesn't equal expected:\n%s\n", actualBytes, expectedBytes)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ioutil.WriteFile(testcase.ExpectedDiff, actualBytes, 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,96 +0,0 @@
|
|||||||
/*
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package commands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/util/fs"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
const kustomizationTemplate = `
|
|
||||||
namePrefix: some-prefix
|
|
||||||
# Labels to add to all objects and selectors.
|
|
||||||
# These labels would also be used to form the selector for apply --prune
|
|
||||||
# Named differently than “labels” to avoid confusion with metadata for this object
|
|
||||||
commonLabels:
|
|
||||||
app: helloworld
|
|
||||||
commonAnnotations:
|
|
||||||
note: This is an example annotation
|
|
||||||
resources: []
|
|
||||||
#- service.yaml
|
|
||||||
#- ../some-dir/
|
|
||||||
# There could also be configmaps in Base, which would make these overlays
|
|
||||||
configMapGenerator: []
|
|
||||||
# There could be secrets in Base, if just using a fork/rebase workflow
|
|
||||||
secretGenerator: []
|
|
||||||
`
|
|
||||||
|
|
||||||
type initOptions struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewCmdInit makes the init command.
|
|
||||||
func newCmdInit(out, errOut io.Writer, fs fs.FileSystem) *cobra.Command {
|
|
||||||
var o initOptions
|
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
|
||||||
Use: "init",
|
|
||||||
Short: "Creates a file called \"" + constants.KustomizationFileName + "\" in the current directory",
|
|
||||||
Long: "Creates a file called \"" +
|
|
||||||
constants.KustomizationFileName + "\" in the current directory with example values.",
|
|
||||||
Example: `init`,
|
|
||||||
SilenceUsage: true,
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
err := o.Validate(cmd, args)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = o.Complete(cmd, args)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return o.RunInit(out, errOut, fs)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate validates init command.
|
|
||||||
func (o *initOptions) Validate(cmd *cobra.Command, args []string) error {
|
|
||||||
if len(args) > 0 {
|
|
||||||
return errors.New("The init command takes no arguments.")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Complete completes init command.
|
|
||||||
func (o *initOptions) Complete(cmd *cobra.Command, args []string) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunInit writes a kustomization file.
|
|
||||||
func (o *initOptions) RunInit(out, errOut io.Writer, fs fs.FileSystem) error {
|
|
||||||
if _, err := fs.Stat(constants.KustomizationFileName); err == nil {
|
|
||||||
return fmt.Errorf("%q already exists", constants.KustomizationFileName)
|
|
||||||
}
|
|
||||||
return fs.WriteFile(constants.KustomizationFileName, []byte(kustomizationTemplate))
|
|
||||||
}
|
|
||||||
@@ -25,9 +25,9 @@ import (
|
|||||||
"github.com/ghodss/yaml"
|
"github.com/ghodss/yaml"
|
||||||
|
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||||
|
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||||
interror "github.com/kubernetes-sigs/kustomize/pkg/internal/error"
|
interror "github.com/kubernetes-sigs/kustomize/pkg/internal/error"
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/util/fs"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type kustomizationFile struct {
|
type kustomizationFile struct {
|
||||||
@@ -35,7 +35,7 @@ type kustomizationFile struct {
|
|||||||
fsys fs.FileSystem
|
fsys fs.FileSystem
|
||||||
}
|
}
|
||||||
|
|
||||||
func newKustomizationFile(mPath string, fsys fs.FileSystem) (*kustomizationFile, error) {
|
func newKustomizationFile(mPath string, fsys fs.FileSystem) (*kustomizationFile, error) { // nolint
|
||||||
mf := &kustomizationFile{path: mPath, fsys: fsys}
|
mf := &kustomizationFile{path: mPath, fsys: fsys}
|
||||||
err := mf.validate()
|
err := mf.validate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -45,25 +45,23 @@ func newKustomizationFile(mPath string, fsys fs.FileSystem) (*kustomizationFile,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (mf *kustomizationFile) validate() error {
|
func (mf *kustomizationFile) validate() error {
|
||||||
f, err := mf.fsys.Stat(mf.path)
|
if !mf.fsys.Exists(mf.path) {
|
||||||
if err != nil {
|
|
||||||
errorMsg := fmt.Sprintf("Missing kustomization file '%s'.\n", mf.path)
|
errorMsg := fmt.Sprintf("Missing kustomization file '%s'.\n", mf.path)
|
||||||
merr := interror.KustomizationError{KustomizationPath: mf.path, ErrorMsg: errorMsg}
|
merr := interror.KustomizationError{KustomizationPath: mf.path, ErrorMsg: errorMsg}
|
||||||
return merr
|
return merr
|
||||||
}
|
}
|
||||||
if f.IsDir() {
|
if mf.fsys.IsDir(mf.path) {
|
||||||
mf.path = path.Join(mf.path, constants.KustomizationFileName)
|
mf.path = path.Join(mf.path, constants.KustomizationFileName)
|
||||||
_, err = mf.fsys.Stat(mf.path)
|
if !mf.fsys.Exists(mf.path) {
|
||||||
if err != nil {
|
|
||||||
errorMsg := fmt.Sprintf("Missing kustomization file '%s'.\n", mf.path)
|
errorMsg := fmt.Sprintf("Missing kustomization file '%s'.\n", mf.path)
|
||||||
merr := interror.KustomizationError{KustomizationPath: mf.path, ErrorMsg: errorMsg}
|
merr := interror.KustomizationError{KustomizationPath: mf.path, ErrorMsg: errorMsg}
|
||||||
return merr
|
return merr
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if !strings.HasSuffix(mf.path, constants.KustomizationFileName) {
|
if !strings.HasSuffix(mf.path, constants.KustomizationFileName) {
|
||||||
errorMsg := fmt.Sprintf("Kustomization file path (%s) should have %s suffix\n", mf.path, constants.KustomizationSuffix)
|
errorMsg := fmt.Sprintf("Kustomization file path (%s) should have %s suffix\n",
|
||||||
merr := interror.KustomizationError{KustomizationPath: mf.path, ErrorMsg: errorMsg}
|
mf.path, constants.KustomizationFileSuffix)
|
||||||
return merr
|
return interror.KustomizationError{KustomizationPath: mf.path, ErrorMsg: errorMsg}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -84,7 +82,7 @@ func (mf *kustomizationFile) read() (*types.Kustomization, error) {
|
|||||||
|
|
||||||
func (mf *kustomizationFile) write(kustomization *types.Kustomization) error {
|
func (mf *kustomizationFile) write(kustomization *types.Kustomization) error {
|
||||||
if kustomization == nil {
|
if kustomization == nil {
|
||||||
return errors.New("util: kustomization file arg is nil.")
|
return errors.New("util: kustomization file arg is nil")
|
||||||
}
|
}
|
||||||
bytes, err := yaml.Marshal(kustomization)
|
bytes, err := yaml.Marshal(kustomization)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -22,8 +22,8 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||||
|
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/util/fs"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestWriteAndRead(t *testing.T) {
|
func TestWriteAndRead(t *testing.T) {
|
||||||
@@ -62,7 +62,7 @@ func TestEmptyFile(t *testing.T) {
|
|||||||
func TestNewNotExist(t *testing.T) {
|
func TestNewNotExist(t *testing.T) {
|
||||||
badSuffix := "foo.bar"
|
badSuffix := "foo.bar"
|
||||||
fakeFS := fs.MakeFakeFS()
|
fakeFS := fs.MakeFakeFS()
|
||||||
fakeFS.Mkdir(".", 0644)
|
fakeFS.Mkdir(".")
|
||||||
fakeFS.Create(badSuffix)
|
fakeFS.Create(badSuffix)
|
||||||
_, err := newKustomizationFile(constants.KustomizationFileName, fakeFS)
|
_, err := newKustomizationFile(constants.KustomizationFileName, fakeFS)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -18,12 +18,11 @@ package commands
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/util/fs"
|
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||||
)
|
)
|
||||||
|
|
||||||
type setNamePrefixOptions struct {
|
type setNamePrefixOptions struct {
|
||||||
@@ -31,7 +30,7 @@ type setNamePrefixOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// newCmdSetNamePrefix sets the value of the namePrefix field in the kustomization.
|
// newCmdSetNamePrefix sets the value of the namePrefix field in the kustomization.
|
||||||
func newCmdSetNamePrefix(out, errOut io.Writer, fsys fs.FileSystem) *cobra.Command {
|
func newCmdSetNamePrefix(fsys fs.FileSystem) *cobra.Command {
|
||||||
var o setNamePrefixOptions
|
var o setNamePrefixOptions
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
@@ -52,7 +51,7 @@ and overwrite the value with "acme-" if the field does exist.
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return o.RunSetNamePrefix(out, errOut, fsys)
|
return o.RunSetNamePrefix(fsys)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return cmd
|
return cmd
|
||||||
@@ -74,7 +73,7 @@ func (o *setNamePrefixOptions) Complete(cmd *cobra.Command, args []string) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RunSetNamePrefix runs setNamePrefix command (does real work).
|
// RunSetNamePrefix runs setNamePrefix command (does real work).
|
||||||
func (o *setNamePrefixOptions) RunSetNamePrefix(out, errOut io.Writer, fsys fs.FileSystem) error {
|
func (o *setNamePrefixOptions) RunSetNamePrefix(fsys fs.FileSystem) error {
|
||||||
mf, err := newKustomizationFile(constants.KustomizationFileName, fsys)
|
mf, err := newKustomizationFile(constants.KustomizationFileName, fsys)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -17,14 +17,12 @@ limitations under the License.
|
|||||||
package commands
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"os"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/util/fs"
|
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -32,11 +30,10 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestSetNamePrefixHappyPath(t *testing.T) {
|
func TestSetNamePrefixHappyPath(t *testing.T) {
|
||||||
buf := bytes.NewBuffer([]byte{})
|
|
||||||
fakeFS := fs.MakeFakeFS()
|
fakeFS := fs.MakeFakeFS()
|
||||||
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
||||||
|
|
||||||
cmd := newCmdSetNamePrefix(buf, os.Stderr, fakeFS)
|
cmd := newCmdSetNamePrefix(fakeFS)
|
||||||
args := []string{goodPrefixValue}
|
args := []string{goodPrefixValue}
|
||||||
err := cmd.RunE(cmd, args)
|
err := cmd.RunE(cmd, args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -52,10 +49,9 @@ func TestSetNamePrefixHappyPath(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestSetNamePrefixNoArgs(t *testing.T) {
|
func TestSetNamePrefixNoArgs(t *testing.T) {
|
||||||
buf := bytes.NewBuffer([]byte{})
|
|
||||||
fakeFS := fs.MakeFakeFS()
|
fakeFS := fs.MakeFakeFS()
|
||||||
|
|
||||||
cmd := newCmdSetNamePrefix(buf, os.Stderr, fakeFS)
|
cmd := newCmdSetNamePrefix(fakeFS)
|
||||||
err := cmd.Execute()
|
err := cmd.Execute()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("expected error: %v", err)
|
t.Errorf("expected error: %v", err)
|
||||||
|
|||||||
111
pkg/commands/setimagetag.go
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
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 commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||||
|
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||||
|
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
type setImageTagOptions struct {
|
||||||
|
imageTagMap map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// newCmdSetImageTag sets the new tags for images in the kustomization.
|
||||||
|
func newCmdSetImageTag(fsys fs.FileSystem) *cobra.Command {
|
||||||
|
var o setImageTagOptions
|
||||||
|
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "imagetag",
|
||||||
|
Short: "Sets images and their new tags in the kustomization file",
|
||||||
|
Example: `
|
||||||
|
The command
|
||||||
|
set imagetag nginx:1.8.0 my-app:latest
|
||||||
|
will add
|
||||||
|
|
||||||
|
imageTags:
|
||||||
|
- name: nginx
|
||||||
|
newTag: 1.8.0
|
||||||
|
- name: my-app
|
||||||
|
newTag: latest
|
||||||
|
|
||||||
|
to the kustomization file if it doesn't exist,
|
||||||
|
and overwrite the previous newTag if the image name exists.
|
||||||
|
`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
err := o.Validate(args)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return o.RunSetImageTags(fsys)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates setImageTag command.
|
||||||
|
func (o *setImageTagOptions) Validate(args []string) error {
|
||||||
|
if len(args) == 0 {
|
||||||
|
return errors.New("No image and newTag specified.")
|
||||||
|
}
|
||||||
|
o.imageTagMap = make(map[string]string)
|
||||||
|
for _, arg := range args {
|
||||||
|
imagetag := strings.Split(arg, ":")
|
||||||
|
if len(imagetag) != 2 {
|
||||||
|
return errors.New("Invalid format of imagetag, must specify it as <image>:<newtag>")
|
||||||
|
}
|
||||||
|
o.imageTagMap[imagetag[0]] = imagetag[1]
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunSetImageTags runs setImageTags command (does real work).
|
||||||
|
func (o *setImageTagOptions) RunSetImageTags(fsys fs.FileSystem) error {
|
||||||
|
mf, err := newKustomizationFile(constants.KustomizationFileName, fsys)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m, err := mf.read()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
imageTagMap := map[string]string{}
|
||||||
|
for _, it := range m.ImageTags {
|
||||||
|
imageTagMap[it.Name] = it.NewTag
|
||||||
|
}
|
||||||
|
for key, value := range o.imageTagMap {
|
||||||
|
imageTagMap[key] = value
|
||||||
|
}
|
||||||
|
var imageTags []types.ImageTag
|
||||||
|
for key, value := range imageTagMap {
|
||||||
|
imageTags = append(imageTags, types.ImageTag{Name: key, NewTag: value})
|
||||||
|
}
|
||||||
|
sort.Slice(imageTags, func(i, j int) bool {
|
||||||
|
return imageTags[i].Name < imageTags[j].Name
|
||||||
|
})
|
||||||
|
|
||||||
|
m.ImageTags = imageTags
|
||||||
|
|
||||||
|
return mf.write(m)
|
||||||
|
}
|
||||||
97
pkg/commands/setimagetag_test.go
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
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 commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||||
|
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSetImageTagsHappyPath(t *testing.T) {
|
||||||
|
fakeFS := fs.MakeFakeFS()
|
||||||
|
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
||||||
|
|
||||||
|
cmd := newCmdSetImageTag(fakeFS)
|
||||||
|
args := []string{"image1:tag1", "image2:tag2"}
|
||||||
|
err := cmd.RunE(cmd, args)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected cmd error: %v", err)
|
||||||
|
}
|
||||||
|
content, err := fakeFS.ReadFile(constants.KustomizationFileName)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected read error: %v", err)
|
||||||
|
}
|
||||||
|
expected := []byte(`
|
||||||
|
imageTags:
|
||||||
|
- name: image1
|
||||||
|
newTag: tag1
|
||||||
|
- name: image2
|
||||||
|
newTag: tag2
|
||||||
|
`)
|
||||||
|
if !strings.Contains(string(content), string(expected)) {
|
||||||
|
t.Errorf("expected imageTags in kustomization file")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetImageTagsOverride(t *testing.T) {
|
||||||
|
fakeFS := fs.MakeFakeFS()
|
||||||
|
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
||||||
|
|
||||||
|
cmd := newCmdSetImageTag(fakeFS)
|
||||||
|
args := []string{"image1:tag1", "image2:tag1"}
|
||||||
|
err := cmd.RunE(cmd, args)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected cmd error: %v", err)
|
||||||
|
}
|
||||||
|
args = []string{"image2:tag2", "image3:tag3"}
|
||||||
|
err = cmd.RunE(cmd, args)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected cmd error: %v", err)
|
||||||
|
}
|
||||||
|
content, err := fakeFS.ReadFile(constants.KustomizationFileName)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected read error: %v", err)
|
||||||
|
}
|
||||||
|
expected := []byte(`
|
||||||
|
imageTags:
|
||||||
|
- name: image1
|
||||||
|
newTag: tag1
|
||||||
|
- name: image2
|
||||||
|
newTag: tag2
|
||||||
|
- name: image3
|
||||||
|
newTag: tag3
|
||||||
|
`)
|
||||||
|
if !strings.Contains(string(content), string(expected)) {
|
||||||
|
t.Errorf("expected imageTags in kustomization file %s", string(content))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetImageTagsNoArgs(t *testing.T) {
|
||||||
|
fakeFS := fs.MakeFakeFS()
|
||||||
|
|
||||||
|
cmd := newCmdSetImageTag(fakeFS)
|
||||||
|
err := cmd.Execute()
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected error: %v", err)
|
||||||
|
}
|
||||||
|
if err.Error() != "No image and newTag specified." {
|
||||||
|
t.Errorf("incorrect error: %v", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -32,6 +32,33 @@ diff -u -N /tmp/noop/apps_v1beta2_Deployment_nginx.yaml /tmp/transformed/apps_v1
|
|||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- image: nginx
|
- 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
|
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/noop/v1_Service_nginx.yaml YYYY-MM-DD HH:MM:SS
|
||||||
+++ /tmp/transformed/v1_Service_nginx.yaml YYYY-MM-DD HH:MM:SS
|
+++ /tmp/transformed/v1_Service_nginx.yaml YYYY-MM-DD HH:MM:SS
|
||||||
|
|||||||
@@ -44,3 +44,28 @@ spec:
|
|||||||
containers:
|
containers:
|
||||||
- image: nginx
|
- image: nginx
|
||||||
name: 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
|
||||||
|
|||||||
@@ -6,5 +6,4 @@ commonLabels:
|
|||||||
commonAnnotations:
|
commonAnnotations:
|
||||||
note: This is a test annotation
|
note: This is a test annotation
|
||||||
resources:
|
resources:
|
||||||
- deployment.yaml
|
- resources/*.yaml
|
||||||
- service.yaml
|
|
||||||
|
|||||||
13
pkg/commands/testdata/testcase-base-only/in/resources/networkpolicy.yaml
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
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
|
||||||
6
pkg/commands/testdata/testcase-configmaps/base/myapp/mycomponent/kustomization.yaml
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namePrefix: p1-
|
||||||
|
configMapGenerator:
|
||||||
|
- name: com1
|
||||||
|
behavior: create
|
||||||
|
literals:
|
||||||
|
- from=base
|
||||||
6
pkg/commands/testdata/testcase-configmaps/base/myapp/mycomponent2/kustomization.yaml
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namePrefix: p2-
|
||||||
|
configMapGenerator:
|
||||||
|
- name: com2
|
||||||
|
behavior: create
|
||||||
|
literals:
|
||||||
|
- from=base
|
||||||
16
pkg/commands/testdata/testcase-configmaps/expected.diff
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
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
|
||||||