Compare commits

...

25 Commits

Author SHA1 Message Date
Kubernetes Prow Robot
c464fb0a81 Merge pull request #1436 from richardmarshall/kubectl_clarity
docs: Additional details for kubectl integration
2019-08-13 15:18:23 -07:00
Kubernetes Prow Robot
694cf23df8 Merge pull request #1432 from richardmarshall/lostreplicas
Retain replicas field in edit marshal path
2019-08-09 14:29:14 -07:00
Richard Marshall
e66656aa7f docs: Additional details for kubectl integration 2019-08-08 17:06:19 -07:00
Richard Marshall
eaae7af5fe Retain replicas field in edit marshal path 2019-08-08 15:45:56 -07:00
Kubernetes Prow Robot
f9fe138114 Merge pull request #1416 from anthonyho007/makefile
add Makefile for local development
2019-08-07 11:04:09 -07:00
Kubernetes Prow Robot
2a2a889c37 Merge pull request #1423 from sunny0826/master
Update zh-README.md & zh-example-README.md
2019-08-02 12:37:55 -07:00
郭旭东
34287e511f fix example-zh-README.md 2019-08-02 09:09:32 +08:00
Anthony Ho
e6fffc8ba4 add makefile 2019-08-01 11:23:38 -04:00
郭旭东
86f221611e Update zh-example-README.md 2019-08-01 15:30:11 +08:00
郭旭东
b4d6e89fa2 Update zh-README.md 2019-08-01 15:19:14 +08:00
Kubernetes Prow Robot
5937bd0259 Merge pull request #1394 from richardmarshall/namerefperformance
Simplify name reference candidate resmap building
2019-07-31 10:18:14 -07:00
Richard Marshall
ed3c29be12 Simplify name reference candidate resmap building
This patch removes a layer of looping in the name reference candiate
resmap building process by not checking if the resources already exist
in the new resmap.
2019-07-30 17:15:15 -07:00
Kubernetes Prow Robot
3d2e956b19 Merge pull request #1412 from richardmarshall/anchor_resmap_select
Automatically anchor resource selector patterns
2019-07-30 15:47:11 -07:00
Kubernetes Prow Robot
dd9d1f95e9 Merge pull request #1389 from Liujingfang1/repospec
make repospec memebers public
2019-07-30 15:27:51 -07:00
jingfangliu
a279c08f7d make repospec memebers public 2019-07-30 13:56:20 -07:00
Kubernetes Prow Robot
a798109161 Merge pull request #1413 from bai/fix-typo
Fix typo in patches definition
2019-07-30 10:14:51 -07:00
Vlad Gorodetsky
bafd6b5423 Fix typo in patches definition 2019-07-30 14:52:02 +03:00
Richard Marshall
963913f9ef Automatically anchor resource selector patterns 2019-07-29 17:57:33 -07:00
Jingfang Liu
46905588ac add document for inline patch (#1411) 2019-07-29 15:15:06 -07:00
Kubernetes Prow Robot
5426888df4 Merge pull request #1405 from Liujingfang1/inlinepatch
add inline patch support for Strategic Merge Patch and JSON patch
2019-07-29 14:28:49 -07:00
jingfangliu
35481ec6d9 add inline patch support for Strategic Merge Patch and JSON patch 2019-07-29 14:10:57 -07:00
Kubernetes Prow Robot
6c92c30e94 Merge pull request #1402 from damienr74/currentid-replicas
Allow replicas to find modified names.
2019-07-29 12:54:47 -07:00
Damien Robichaud
02f6b3ec98 Allow replicas to find modified names.
Also allows to test for modified resmaps instead of directly loading
them.
2019-07-26 18:00:59 -07:00
Kubernetes Prow Robot
a9848f2738 Merge pull request #1403 from Liujingfang1/inlinepatch
add testing for patch transformers
2019-07-26 15:05:57 -07:00
jingfangliu
b4038a6cd2 add testting for patch transformers 2019-07-26 14:02:52 -07:00
25 changed files with 725 additions and 75 deletions

1
.gitignore vendored
View File

@@ -4,6 +4,7 @@
*.dll
*.so
*.dylib
/kustomize
# Test binary, build with `go test -c`
*.test

28
Makefile Normal file
View File

@@ -0,0 +1,28 @@
BIN_NAME=kustomize
export GO111MODULE=on
all: test build
test: generate-code test-lint test-go
test-go:
go test -v ./...
test-lint:
golangci-lint run ./...
generate-code:
./plugin/generateBuiltins.sh $(GOPATH)
build:
go build -o $(BIN_NAME) cmd/kustomize/main.go
install:
go install $(PWD)/cmd/kustomize
clean:
go clean
rm -f $(BIN_NAME)
.PHONY: test build install clean generate-code test-go test-lint

View File

@@ -24,8 +24,16 @@ these [instructions](docs/INSTALL.md).
Browse the [docs](docs) or jump right into the
tested [examples](examples).
kustomize [v2.0.3] is available in [kubectl v1.14 and v1.15][kubectl].
## kubectl integration
Since [v1.14][kubectl announcement] the kustomize build system has been included in kubectl.
| kubectl version | kustomize version |
|---------|--------|
| v1.15.x | [v2.0.3](https://github.com/kubernetes-sigs/kustomize/tree/v2.0.3) |
| v1.14.x | [v2.0.3](https://github.com/kubernetes-sigs/kustomize/tree/v2.0.3) |
For examples and guides for using the kubectl integration please see the [kubectl book] or the [kubernetes documentation].
## Usage
@@ -161,7 +169,9 @@ is governed by the [Kubernetes Code of Conduct].
[imageBase]: docs/images/base.jpg
[imageOverlay]: docs/images/overlay.jpg
[kind/feature]: https://github.com/kubernetes-sigs/kustomize/labels/kind%2Ffeature
[kubectl]: https://kubernetes.io/blog/2019/03/25/kubernetes-1-14-release-announcement
[kubectl announcement]: https://kubernetes.io/blog/2019/03/25/kubernetes-1-14-release-announcement
[kubectl book]: https://kubectl.docs.kubernetes.io/pages/app_customization/introduction.html
[kubernetes documentation]: https://kubernetes.io/docs/tasks/manage-kubernetes-objects/kustomization/
[kubernetes style]: docs/glossary.md#kubernetes-style-object
[kustomization]: docs/glossary.md#kustomization
[overlay]: docs/glossary.md#overlay

View File

@@ -38,6 +38,7 @@ What transformations (customizations) should be applied?
| [namePrefix](#nameprefix) | string | Prepends value to the names of all resources |
| [nameSuffix](#namesuffix) | string | The value is appended to the names of all resources. |
| [replicas](#replicas) | list | Replicas modifies the number of replicas of a resource. |
| [patches](#patches) | list | Each entry should resolve to a patch that can be applied to multiple targets. |
|[patchesStrategicMerge](#patchesstrategicmerge)| list |Each entry in this list should resolve to a partial or complete resource definition file.|
|[patchesJson6902](#patchesjson6902)| list |Each entry in this list should resolve to a kubernetes object and a JSON patch that will be applied to the object.|
|[transformers](#transformers)|list|[plugin](plugins) configuration files|
@@ -263,11 +264,47 @@ resource type is ConfigMap or Secret.
nameSuffix: -v2
```
### patches
Each entry in this list should resolve to an Patch object,
which includes a patch and a target selector.
The patch can be either a strategic merge patch or a JSON patch.
it can be either a patch file or an inline string.
The target selects
resources by group, version, kind, name, namespace,
labelSelector and annotationSelector. A resource
which matches all the specified fields is selected
to apply the patch.
```
patches:
- path: patch.yaml
target:
group: apps
version: v1
kind: Deployment
name: deploy.*
labelSelector: "env=dev"
annotationSelector: "zone=west"
- patch: |-
- op: replace
path: some/existing/path
value: new value
target:
kind: MyKind
labelSelector: "env=dev"
```
The `name` and `namespace` fields of the patch target selector are
automatically anchored regular expressions. This means that the value `myapp`
is equivalent to `^myapp$`.
### patchesStrategicMerge
Each entry in this list should be a relative path
Each entry in this list should be either a relative
file path or an inline content
resolving to a partial or complete resource
definition file.
definition.
The names in these (possibly partial) resource
files must match names already loaded via the
@@ -286,6 +323,22 @@ patchesStrategicMerge:
- deployment_increase_memory.yaml
```
The patch content can be a inline string as well.
```
patchesStrategicMerge:
- |-
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
template:
spec:
containers:
- name: nginx
image: nignx:latest
```
### patchesJson6902
Each entry in this list should resolve to
@@ -330,6 +383,23 @@ patchesJson6902:
path: add_service_annotation.yaml
```
The patch content can be an inline string as well:
```
patchesJson6902:
- target:
version: v1
kind: Deployment
name: my-deployment
patch: |-
- op: add
path: /some/new/path
value: value
- op: replace
path: /some/existing/path
value: "new value"
```
### replicas
Replicas modified the number of replicas for a resource.

View File

@@ -19,6 +19,8 @@
## 发行说明
* [3.1](../v3.1.0.md) - 2019年7月下旬扩展 patches 和改进的资源匹配。
* [3.0](../v3.0.0.md) - 2019年6月下旬插件开发者发布。
* [2.1](../v2.1.0.md) - 2019年6月18日

View File

@@ -26,6 +26,8 @@ go get sigs.k8s.io/kustomize/v3/cmd/kustomize
* [json patch](jsonpatch.md) -在 kustomization 中应用 json patch 。
* [patch multiple objects](patchMultipleObjects.md) - 通过一个patch来修改多个资源。
高级用法
- generator 插件:
@@ -34,6 +36,10 @@ go get sigs.k8s.io/kustomize/v3/cmd/kustomize
* [secret generation](../secretGeneratorPlugin.md) - 生成 Secret。
- transformer 插件:
* [validation transformer](../validationTransformer/README.md) - 通过 transformer 验证资源。
- 定制内建 transformer 配置
* [transformer configs](../transformerconfigs/README.md) - 自定义 transformer 配置。

1
go.sum
View File

@@ -149,7 +149,6 @@ k8s.io/klog v0.3.3 h1:niceAagH1tzskmaie/icWd7ci1wbG7Bf2c6YGcQv+3c=
k8s.io/klog v0.3.3/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/kube-openapi v0.0.0-20190603182131-db7b694dc208 h1:5sW+fEHvlJI3Ngolx30CmubFulwH28DhKjGf70Xmtco=
k8s.io/kube-openapi v0.0.0-20190603182131-db7b694dc208/go.mod h1:nfDlWeOsu3pUf4yWGL+ERqohP4YsZcBJXWMK+gkzOA4=
sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=

View File

@@ -67,6 +67,7 @@ func determineFieldOrder() []string {
"GeneratorOptions",
"Vars",
"Images",
"Replicas",
"Configurations",
"Generators",
"Transformers",

View File

@@ -46,6 +46,7 @@ func TestFieldOrder(t *testing.T) {
"GeneratorOptions",
"Vars",
"Images",
"Replicas",
"Configurations",
"Generators",
"Transformers",

View File

@@ -35,14 +35,14 @@ func ClonerUsingGitExec(repoSpec *RepoSpec) error {
if err != nil {
return errors.Wrap(err, "no 'git' program on path")
}
repoSpec.cloneDir, err = fs.NewTmpConfirmedDir()
repoSpec.Dir, err = fs.NewTmpConfirmedDir()
if err != nil {
return err
}
cmd := exec.Command(
gitProgram,
"init",
repoSpec.cloneDir.String())
repoSpec.Dir.String())
var out bytes.Buffer
cmd.Stdout = &out
err = cmd.Run()
@@ -50,7 +50,7 @@ func ClonerUsingGitExec(repoSpec *RepoSpec) error {
return errors.Wrapf(
err,
"trouble initializing empty git repo in %s",
repoSpec.cloneDir.String())
repoSpec.Dir.String())
}
cmd = exec.Command(
@@ -60,7 +60,7 @@ func ClonerUsingGitExec(repoSpec *RepoSpec) error {
"origin",
repoSpec.CloneSpec())
cmd.Stdout = &out
cmd.Dir = repoSpec.cloneDir.String()
cmd.Dir = repoSpec.Dir.String()
err = cmd.Run()
if err != nil {
return errors.Wrapf(
@@ -68,20 +68,20 @@ func ClonerUsingGitExec(repoSpec *RepoSpec) error {
"trouble adding remote %s",
repoSpec.CloneSpec())
}
if repoSpec.ref == "" {
repoSpec.ref = "master"
if repoSpec.Ref == "" {
repoSpec.Ref = "master"
}
cmd = exec.Command(
gitProgram,
"fetch",
"--depth=1",
"origin",
repoSpec.ref)
repoSpec.Ref)
cmd.Stdout = &out
cmd.Dir = repoSpec.cloneDir.String()
cmd.Dir = repoSpec.Dir.String()
err = cmd.Run()
if err != nil {
return errors.Wrapf(err, "trouble fetching %s", repoSpec.ref)
return errors.Wrapf(err, "trouble fetching %s", repoSpec.Ref)
}
cmd = exec.Command(
@@ -90,11 +90,11 @@ func ClonerUsingGitExec(repoSpec *RepoSpec) error {
"--hard",
"FETCH_HEAD")
cmd.Stdout = &out
cmd.Dir = repoSpec.cloneDir.String()
cmd.Dir = repoSpec.Dir.String()
err = cmd.Run()
if err != nil {
return errors.Wrapf(
err, "trouble hard resetting empty repository to %s", repoSpec.ref)
err, "trouble hard resetting empty repository to %s", repoSpec.Ref)
}
return nil
}
@@ -105,7 +105,7 @@ func ClonerUsingGitExec(repoSpec *RepoSpec) error {
// used in a test.
func DoNothingCloner(dir fs.ConfirmedDir) Cloner {
return func(rs *RepoSpec) error {
rs.cloneDir = dir
rs.Dir = dir
return nil
}
}

View File

@@ -39,36 +39,36 @@ type RepoSpec struct {
raw string
// Host, e.g. github.com
host string
Host string
// orgRepo name (organization/repoName),
// e.g. kubernetes-sigs/kustomize
orgRepo string
OrgRepo string
// ConfirmedDir where the orgRepo is cloned to.
cloneDir fs.ConfirmedDir
// Dir where the orgRepo is cloned to.
Dir fs.ConfirmedDir
// Relative path in the repository, and in the cloneDir,
// to a Kustomization.
path string
Path string
// Branch or tag reference.
ref string
Ref string
// e.g. .git or empty in case of _git is present
gitSuffix string
GitSuffix string
}
// CloneSpec returns a string suitable for "git clone {spec}".
func (x *RepoSpec) CloneSpec() string {
if isAzureHost(x.host) || isAWSHost(x.host) {
return x.host + x.orgRepo
if isAzureHost(x.Host) || isAWSHost(x.Host) {
return x.Host + x.OrgRepo
}
return x.host + x.orgRepo + x.gitSuffix
return x.Host + x.OrgRepo + x.GitSuffix
}
func (x *RepoSpec) CloneDir() fs.ConfirmedDir {
return x.cloneDir
return x.Dir
}
func (x *RepoSpec) Raw() string {
@@ -76,11 +76,11 @@ func (x *RepoSpec) Raw() string {
}
func (x *RepoSpec) AbsPath() string {
return x.cloneDir.Join(x.path)
return x.Dir.Join(x.Path)
}
func (x *RepoSpec) Cleaner(fSys fs.FileSystem) func() error {
return func() error { return fSys.RemoveAll(x.cloneDir.String()) }
return func() error { return fSys.RemoveAll(x.Dir.String()) }
}
// From strings like git@github.com:someOrg/someRepo.git or
@@ -98,8 +98,8 @@ func NewRepoSpecFromUrl(n string) (*RepoSpec, error) {
return nil, fmt.Errorf("url lacks host: %s", n)
}
return &RepoSpec{
raw: n, host: host, orgRepo: orgRepo,
cloneDir: notCloned, path: path, ref: gitRef, gitSuffix: gitSuffix}, nil
raw: n, Host: host, OrgRepo: orgRepo,
Dir: notCloned, Path: path, Ref: gitRef, GitSuffix: gitSuffix}, nil
}
const (

View File

@@ -72,17 +72,17 @@ func TestNewRepoSpecFromUrl(t *testing.T) {
if err != nil {
t.Errorf("problem %v", err)
}
if rs.host != hostSpec {
bad = append(bad, []string{"host", uri, rs.host, hostSpec})
if rs.Host != hostSpec {
bad = append(bad, []string{"host", uri, rs.Host, hostSpec})
}
if rs.orgRepo != orgRepo {
bad = append(bad, []string{"orgRepo", uri, rs.orgRepo, orgRepo})
if rs.OrgRepo != orgRepo {
bad = append(bad, []string{"orgRepo", uri, rs.OrgRepo, orgRepo})
}
if rs.path != pathName {
bad = append(bad, []string{"path", uri, rs.path, pathName})
if rs.Path != pathName {
bad = append(bad, []string{"path", uri, rs.Path, pathName})
}
if rs.ref != hrefArg {
bad = append(bad, []string{"ref", uri, rs.ref, hrefArg})
if rs.Ref != hrefArg {
bad = append(bad, []string{"ref", uri, rs.Ref, hrefArg})
}
}
}
@@ -201,9 +201,9 @@ func TestNewRepoSpecFromUrl_CloneSpecs(t *testing.T) {
t.Errorf("AbsPath expected to be %v, but got %v on %s",
testcase.absPath, rs.AbsPath(), testcase.input)
}
if rs.ref != testcase.ref {
if rs.Ref != testcase.ref {
t.Errorf("ref expected to be %v, but got %v on %s",
testcase.ref, rs.ref, testcase.input)
testcase.ref, rs.Ref, testcase.input)
}
}
}

View File

@@ -138,11 +138,16 @@ func (th *KustTestHarness) ErrorFromLoadAndRunTransformer(
func (th *KustTestHarness) RunTransformer(
config, input string) (resmap.ResMap, error) {
transConfig, err := th.rf.RF().FromBytes([]byte(config))
resMap, err := th.rf.NewResMapFromBytes([]byte(input))
if err != nil {
th.t.Fatalf("Err: %v", err)
}
resMap, err := th.rf.NewResMapFromBytes([]byte(input))
return th.RunTransformerFromResMap(config, resMap)
}
func (th *KustTestHarness) RunTransformerFromResMap(
config string, resMap resmap.ResMap) (resmap.ResMap, error) {
transConfig, err := th.rf.RF().FromBytes([]byte(config))
if err != nil {
th.t.Fatalf("Err: %v", err)
}

View File

@@ -552,7 +552,7 @@ func (m *resWrangler) makeCopy(copier resCopier) ResMap {
// SubsetThatCouldBeReferencedByResource implements ResMap.
func (m *resWrangler) SubsetThatCouldBeReferencedByResource(
inputRes *resource.Resource) ResMap {
result := New()
result := newOne()
inputId := inputRes.CurId()
isInputIdNamespaceable := inputId.IsNamespaceableKind()
rctxm := inputRes.PrefixesSuffixesEquals
@@ -563,15 +563,16 @@ func (m *resWrangler) SubsetThatCouldBeReferencedByResource(
resId := r.CurId()
if (!isInputIdNamespaceable || !resId.IsNamespaceableKind() || resId.IsNsEquals(inputId)) &&
r.InSameKustomizeCtx(rctxm) {
err := result.Append(r)
if err != nil {
panic(err)
}
result.append(r)
}
}
return result
}
func (m *resWrangler) append(res *resource.Resource) {
m.rList = append(m.rList, res)
}
// AppendAll implements ResMap.
func (m *resWrangler) AppendAll(other ResMap) error {
if other == nil {
@@ -650,11 +651,18 @@ func (m *resWrangler) appendReplaceOrMerge(
return nil
}
func anchorRegex(pattern string) string {
if pattern == "" {
return pattern
}
return "^" + pattern + "$"
}
// Select returns a list of resources that
// are selected by a Selector
func (m *resWrangler) Select(s types.Selector) ([]*resource.Resource, error) {
ns := regexp.MustCompile(s.Namespace)
nm := regexp.MustCompile(s.Name)
ns := regexp.MustCompile(anchorRegex(s.Namespace))
nm := regexp.MustCompile(anchorRegex(s.Name))
var result []*resource.Resource
for _, r := range m.Resources() {
curId := r.CurId()

View File

@@ -41,6 +41,12 @@ metadata:
app: name3
annotations:
bar: baz
---
apiVersion: group1/v1
kind: Kind2
metadata:
name: x-name1
namespace: x-default
`))
if err != nil {
t.Fatalf("unexpected error %v", err)
@@ -56,13 +62,13 @@ func TestFindPatchTargets(t *testing.T) {
}{
{
target: types.Selector{
Name: "name*",
Name: "name.*",
},
count: 3,
},
{
target: types.Selector{
Name: "name*",
Name: "name.*",
AnnotationSelector: "foo=bar",
},
count: 2,
@@ -78,7 +84,7 @@ func TestFindPatchTargets(t *testing.T) {
Gvk: gvk.Gvk{
Kind: "Kind1",
},
Name: "name*",
Name: "name.*",
},
count: 2,
},
@@ -92,7 +98,7 @@ func TestFindPatchTargets(t *testing.T) {
target: types.Selector{
Name: "",
},
count: 3,
count: 4,
},
{
target: types.Selector{
@@ -104,18 +110,60 @@ func TestFindPatchTargets(t *testing.T) {
target: types.Selector{
Namespace: "",
},
count: 3,
count: 4,
},
{
target: types.Selector{
Namespace: "default",
Name: "name*",
Name: "name.*",
Gvk: gvk.Gvk{
Kind: "Kind1",
},
},
count: 1,
},
{
target: types.Selector{
Name: "^name.*",
},
count: 3,
},
{
target: types.Selector{
Name: "name.*$",
},
count: 3,
},
{
target: types.Selector{
Name: "^name.*$",
},
count: 3,
},
{
target: types.Selector{
Namespace: "^def.*",
},
count: 2,
},
{
target: types.Selector{
Namespace: "def.*$",
},
count: 2,
},
{
target: types.Selector{
Namespace: "^def.*$",
},
count: 2,
},
{
target: types.Selector{
Namespace: "default",
},
count: 2,
},
}
for _, testcase := range testcases {
actual, err := rm.Select(testcase.target)

View File

@@ -0,0 +1,243 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package target_test
import (
"sigs.k8s.io/kustomize/v3/pkg/kusttest"
"testing"
)
func makeResourcesForPatchTest(th *kusttest_test.KustTestHarness) {
th.WriteF("/app/base/deployment.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
labels:
app: nginx
spec:
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- name: nginx-persistent-storage
mountPath: /tmp/ps
volumes:
- name: nginx-persistent-storage
emptyDir: {}
- configMap:
name: configmap-in-base
name: configmap-in-base
`)
}
func TestStrategicMergePatchInline(t *testing.T) {
th := kusttest_test.NewKustTestHarness(t, "/app/base")
makeResourcesForPatchTest(th)
th.WriteK("/app/base", `
resources:
- deployment.yaml
patchesStrategicMerge:
- |-
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
template:
spec:
containers:
- name: nginx
image: image1
`)
m, err := th.MakeKustTarget().MakeCustomizedResMap()
if err != nil {
t.Fatalf("Err: %v", err)
}
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: nginx
spec:
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: image1
name: nginx
volumeMounts:
- mountPath: /tmp/ps
name: nginx-persistent-storage
volumes:
- emptyDir: {}
name: nginx-persistent-storage
- configMap:
name: configmap-in-base
name: configmap-in-base
`)
}
func TestJSONPatchInline(t *testing.T) {
th := kusttest_test.NewKustTestHarness(t, "/app/base")
makeResourcesForPatchTest(th)
th.WriteK("/app/base", `
resources:
- deployment.yaml
patchesJson6902:
- target:
group: apps
version: v1
kind: Deployment
name: nginx
patch: |-
- op: replace
path: /spec/template/spec/containers/0/image
value: image1
`)
m, err := th.MakeKustTarget().MakeCustomizedResMap()
if err != nil {
t.Fatalf("Err: %v", err)
}
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: nginx
spec:
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: image1
name: nginx
volumeMounts:
- mountPath: /tmp/ps
name: nginx-persistent-storage
volumes:
- emptyDir: {}
name: nginx-persistent-storage
- configMap:
name: configmap-in-base
name: configmap-in-base
`)
}
func TestExtendedPatchInlineJSON(t *testing.T) {
th := kusttest_test.NewKustTestHarness(t, "/app/base")
makeResourcesForPatchTest(th)
th.WriteK("/app/base", `
resources:
- deployment.yaml
patches:
- target:
kind: Deployment
name: nginx
patch: |-
- op: replace
path: /spec/template/spec/containers/0/image
value: image1
`)
m, err := th.MakeKustTarget().MakeCustomizedResMap()
if err != nil {
t.Fatalf("Err: %v", err)
}
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: nginx
spec:
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: image1
name: nginx
volumeMounts:
- mountPath: /tmp/ps
name: nginx-persistent-storage
volumes:
- emptyDir: {}
name: nginx-persistent-storage
- configMap:
name: configmap-in-base
name: configmap-in-base
`)
}
func TestExtendedPatchInlineYAML(t *testing.T) {
th := kusttest_test.NewKustTestHarness(t, "/app/base")
makeResourcesForPatchTest(th)
th.WriteK("/app/base", `
resources:
- deployment.yaml
patches:
- target:
kind: Deployment
name: nginx
patch: |-
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
template:
spec:
containers:
- name: nginx
image: image1
`)
m, err := th.MakeKustTarget().MakeCustomizedResMap()
if err != nil {
t.Fatalf("Err: %v", err)
}
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: nginx
spec:
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: image1
name: nginx
volumeMounts:
- mountPath: /tmp/ps
name: nginx-persistent-storage
volumes:
- emptyDir: {}
name: nginx-persistent-storage
- configMap:
name: configmap-in-base
name: configmap-in-base
`)
}

View File

@@ -156,7 +156,7 @@ func (kt *KustTarget) configureBuiltinPatchJson6902Transformer(
for _, args := range kt.kustomization.PatchesJson6902 {
c.Target = *args.Target
c.Path = args.Path
c.JsonOp = "" // Not implemented for kustomization file yet.
c.JsonOp = args.Patch
p := builtin.NewPatchJson6902TransformerPlugin()
err = kt.configureBuiltinPlugin(p, c, "patchJson6902")
if err != nil {
@@ -178,7 +178,6 @@ func (kt *KustTarget) configureBuiltinPatchStrategicMergeTransformer(
Patches string `json:"patches,omitempty" yaml:"patches,omitempty"`
}
c.Paths = kt.kustomization.PatchesStrategicMerge
c.Patches = "" // Not implemented for kustomization file yet
p := builtin.NewPatchStrategicMergeTransformerPlugin()
err = kt.configureBuiltinPlugin(p, c, "patchStrategicMerge")
if err != nil {

View File

@@ -331,6 +331,9 @@ type PatchJson6902 struct {
// relative file path for a json patch file inside a kustomization
Path string `json:"path,omitempty" yaml:"path,omitempty"`
// inline patch string
Patch string `json:"patch,omitempty" yaml:"patch,omitempty"`
}
// PatchTarget represents the kubernetes object that the patch is applied to

View File

@@ -15,7 +15,7 @@ type PatchStrategicMergeTransformerPlugin struct {
rf *resmap.Factory
loadedPatches []*resource.Resource
Paths []types.PatchStrategicMerge `json:"paths,omitempty" yaml:"paths,omitempty"`
Patches string `json:patches,omitempty" yaml:"patches,omitempty"`
Patches string `json:"patches,omitempty" yaml:"patches,omitempty"`
}
//noinspection GoUnusedGlobalVariable
@@ -35,11 +35,18 @@ func (p *PatchStrategicMergeTransformerPlugin) Config(
return fmt.Errorf("empty file path and empty patch content")
}
if len(p.Paths) != 0 {
res, err := p.rf.RF().SliceFromPatches(ldr, p.Paths)
if err != nil {
return err
for _, onePath := range p.Paths {
res, err := p.rf.RF().SliceFromBytes([]byte(onePath))
if err == nil {
p.loadedPatches = append(p.loadedPatches, res...)
continue
}
res, err = p.rf.RF().SliceFromPatches(ldr, []types.PatchStrategicMerge{onePath})
if err != nil {
return err
}
p.loadedPatches = append(p.loadedPatches, res...)
}
p.loadedPatches = res
}
if p.Patches != "" {
res, err := p.rf.RF().SliceFromBytes([]byte(p.Patches))

View File

@@ -34,8 +34,15 @@ func (p *ReplicaCountTransformerPlugin) Config(
}
func (p *ReplicaCountTransformerPlugin) Transform(m resmap.ResMap) error {
found := false
for i, replicaSpec := range p.FieldSpecs {
for _, res := range m.GetMatchingResourcesByOriginalId(p.createMatcher(i)) {
matcher := p.createMatcher(i)
matchOriginal := m.GetMatchingResourcesByOriginalId(matcher)
matchCurrent := m.GetMatchingResourcesByCurrentId(matcher)
for _, res := range append(matchOriginal, matchCurrent...) {
found = true
err := transformers.MutateField(
res.Map(), replicaSpec.PathSlice(),
replicaSpec.CreateIfNotPresent, p.addReplicas)
@@ -45,6 +52,15 @@ func (p *ReplicaCountTransformerPlugin) Transform(m resmap.ResMap) error {
}
}
if !found {
gvks := make([]string, len(p.FieldSpecs))
for i, replicaSpec := range p.FieldSpecs {
gvks[i] = replicaSpec.Gvk.String()
}
return fmt.Errorf("Resource with name %s does not match a config with the following GVK %v",
p.Replica.Name, gvks)
}
return nil
}

View File

@@ -250,7 +250,7 @@ spec:
`)
}
func TestPatchJson6902TransformerWithInline(t *testing.T) {
func TestPatchJson6902TransformerWithInlineJSON(t *testing.T) {
tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset()
@@ -290,3 +290,47 @@ spec:
dnsPolicy: ClusterFirst
`)
}
func TestPatchJson6902TransformerWithInlineYAML(t *testing.T) {
tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset()
tc.BuildGoPlugin(
"builtin", "", "PatchJson6902Transformer")
th := kusttest_test.NewKustTestPluginHarness(t, "/app")
rm := th.LoadAndRunTransformer(`
apiVersion: builtin
kind: PatchJson6902Transformer
metadata:
name: notImportantHere
target:
group: apps
version: v1
kind: Deployment
name: myDeploy
jsonOp: |-
- op: add
path: /spec/template/spec/dnsPolicy
value: ClusterFirst
`, target)
th.AssertActualEqualsExpected(rm, `
apiVersion: apps/v1
kind: Deployment
metadata:
name: myDeploy
spec:
replica: 2
template:
metadata:
labels:
old-label: old-value
spec:
containers:
- image: nginx
name: nginx
dnsPolicy: ClusterFirst
`)
}

View File

@@ -18,7 +18,7 @@ type plugin struct {
rf *resmap.Factory
loadedPatches []*resource.Resource
Paths []types.PatchStrategicMerge `json:"paths,omitempty" yaml:"paths,omitempty"`
Patches string `json:patches,omitempty" yaml:"patches,omitempty"`
Patches string `json:"patches,omitempty" yaml:"patches,omitempty"`
}
//noinspection GoUnusedGlobalVariable
@@ -36,11 +36,18 @@ func (p *plugin) Config(
return fmt.Errorf("empty file path and empty patch content")
}
if len(p.Paths) != 0 {
res, err := p.rf.RF().SliceFromPatches(ldr, p.Paths)
if err != nil {
return err
for _, onePath := range p.Paths {
res, err := p.rf.RF().SliceFromBytes([]byte(onePath))
if err == nil {
p.loadedPatches = append(p.loadedPatches, res...)
continue
}
res, err = p.rf.RF().SliceFromPatches(ldr, []types.PatchStrategicMerge{onePath})
if err != nil {
return err
}
p.loadedPatches = append(p.loadedPatches, res...)
}
p.loadedPatches = res
}
if p.Patches != "" {
res, err := p.rf.RF().SliceFromBytes([]byte(p.Patches))

View File

@@ -78,7 +78,9 @@ paths:
t.Fatalf("expected error")
}
if !strings.Contains(err.Error(),
"cannot read file \"/app/patch.yaml\"") {
"cannot read file \"/app/patch.yaml\"") &&
!strings.Contains(err.Error(),
"cannot unmarshal string") {
t.Fatalf("unexpected err: %v", err)
}
}
@@ -181,7 +183,7 @@ spec:
`)
}
func TestPatchStrategicMergeTransformerWithInline(t *testing.T) {
func TestPatchStrategicMergeTransformerWithInlineJSON(t *testing.T) {
tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset()
@@ -216,6 +218,58 @@ spec:
`)
}
func TestPatchStrategicMergeTransformerWithInlineYAML(t *testing.T) {
tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset()
tc.BuildGoPlugin(
"builtin", "", "PatchStrategicMergeTransformer")
th := kusttest_test.NewKustTestPluginHarness(t, "/app")
rm := th.LoadAndRunTransformer(`
apiVersion: builtin
kind: PatchStrategicMergeTransformer
metadata:
name: notImportantHere
patches: |-
apiVersion: apps/v1
metadata:
name: myDeploy
kind: Deployment
spec:
replica: 3
---
apiVersion: apps/v1
metadata:
name: myDeploy
kind: Deployment
spec:
template:
spec:
containers:
- name: nginx
image: nginx:latest
`, target)
th.AssertActualEqualsExpected(rm, `
apiVersion: apps/v1
kind: Deployment
metadata:
name: myDeploy
spec:
replica: 3
template:
metadata:
labels:
old-label: old-value
spec:
containers:
- image: nginx:latest
name: nginx
`)
}
func TestPatchStrategicMergeTransformerMultiplePatches(t *testing.T) {
tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset()
@@ -1226,4 +1280,4 @@ paths:
`, target)
th.AssertActualEqualsExpected(rm, ``)
}
}

View File

@@ -35,8 +35,15 @@ func (p *plugin) Config(
}
func (p *plugin) Transform(m resmap.ResMap) error {
found := false
for i, replicaSpec := range p.FieldSpecs {
for _, res := range m.GetMatchingResourcesByOriginalId(p.createMatcher(i)) {
matcher := p.createMatcher(i)
matchOriginal := m.GetMatchingResourcesByOriginalId(matcher)
matchCurrent := m.GetMatchingResourcesByCurrentId(matcher)
for _, res := range append(matchOriginal, matchCurrent...) {
found = true
err := transformers.MutateField(
res.Map(), replicaSpec.PathSlice(),
replicaSpec.CreateIfNotPresent, p.addReplicas)
@@ -46,6 +53,15 @@ func (p *plugin) Transform(m resmap.ResMap) error {
}
}
if !found {
gvks := make([]string, len(p.FieldSpecs))
for i, replicaSpec := range p.FieldSpecs {
gvks[i] = replicaSpec.Gvk.String()
}
return fmt.Errorf("Resource with name %s does not match a config with the following GVK %v",
p.Replica.Name, gvks)
}
return nil
}

View File

@@ -151,3 +151,85 @@ spec:
app: app
`)
}
func TestMatchesCurrentID(t *testing.T) {
tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset()
tc.BuildGoPlugin("builtin", "", "PrefixSuffixTransformer")
tc.BuildGoPlugin("builtin", "", "ReplicaCountTransformer")
th := kusttest_test.NewKustTestPluginHarness(t, "/app")
rm := th.LoadAndRunTransformer(`
apiVersion: builtin
kind: PrefixSuffixTransformer
metadata:
name: notImportantHere
suffix: -test
fieldSpecs:
- path: metadata/name
`, `
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment`)
rm, _ = th.RunTransformerFromResMap(`
apiVersion: builtin
kind: ReplicaCountTransformer
metadata:
name: notImportantHere
replica:
name: deployment-test
count: 23
fieldSpecs:
- path: spec/replicas
create: true
kind: Deployment`, rm)
th.AssertActualEqualsExpected(rm, `
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-test
spec:
replicas: 23
`)
}
func TestNoMatch(t *testing.T) {
tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset()
tc.BuildGoPlugin("builtin", "", "ReplicaCountTransformer")
th := kusttest_test.NewKustTestPluginHarness(t, "/app")
err := th.ErrorFromLoadAndRunTransformer(`
apiVersion: builtin
kind: ReplicaCountTransformer
metadata:
name: notImportantHere
replica:
name: service
count: 3
fieldSpecs:
- path: spec/replicas
create: true
kind: Deployment
`, `
kind: Service
metadata:
name: service
spec:
`)
if err == nil {
t.Fatalf("No match should return an error")
}
if err.Error() !=
"Resource with name service does not match a config with the following GVK [~G_~V_Deployment]" {
t.Fatalf("Unexpected error: %v", err)
}
}