mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-05-17 18:25:26 +00:00
Merge pull request #300 from Liujingfang1/patchtransformer
Add transformer to apply json patch6902
This commit is contained in:
9
Gopkg.lock
generated
9
Gopkg.lock
generated
@@ -251,6 +251,14 @@
|
||||
revision = "ca39e5af3ece67bbcda3d0f4f56a8e24d9f2dad4"
|
||||
version = "1.1.3"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:10e1dfc240bcd0fce14366215fd2b070e79d9b9460b27382db4423ad74204f2b"
|
||||
name = "github.com/krishicks/yaml-patch"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "83cc9ac50becbbfafb86a89167f3bc5372e8e530"
|
||||
version = "v0.0.10"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:d9e483f4b9e306facf126bd90b02d512bd22ea4471e1568867e32221a8abbb16"
|
||||
@@ -495,6 +503,7 @@
|
||||
"github.com/ghodss/yaml",
|
||||
"github.com/golang/glog",
|
||||
"github.com/hashicorp/go-getter",
|
||||
"github.com/krishicks/yaml-patch",
|
||||
"github.com/pkg/errors",
|
||||
"github.com/spf13/cobra",
|
||||
"k8s.io/api/core/v1",
|
||||
|
||||
@@ -60,3 +60,7 @@
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/hashicorp/go-getter"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/krishicks/yaml-patch"
|
||||
version = "0.0.10"
|
||||
|
||||
@@ -16,18 +16,25 @@ limitations under the License.
|
||||
|
||||
package patch
|
||||
|
||||
import (
|
||||
yamlpatch "github.com/krishicks/yaml-patch"
|
||||
)
|
||||
|
||||
// PatchJson6902 represents a json patch for an object
|
||||
// with format documented https://tools.ietf.org/html/rfc6902.
|
||||
type PatchJson6902 struct {
|
||||
// Relative file path within the kustomization for a json patch file.
|
||||
Path string `json:"path" yaml:"path"`
|
||||
|
||||
// Target refers to a Kubernetes object that the json patch will be
|
||||
// applied to. It must refer to a Kubernetes resource under the
|
||||
// purview of this kustomization. Target should use the
|
||||
// raw name of the object (the name specified in its YAML,
|
||||
// before addition of a namePrefix).
|
||||
Target Target `json:"target" yaml:"target"`
|
||||
Target *Target `json:"target" yaml:"target"`
|
||||
|
||||
// jsonPatch is a list of operations in YAML format that follows JSON 6902 rule.
|
||||
JsonPatch yamlpatch.Patch `json:"jsonPatch,omitempty" yaml:"jsonPatch,omitempty"`
|
||||
|
||||
// relative file path for a json patch file inside a kustomization
|
||||
Path string `json:"path,omitempty" yaml:"path,omitempty"`
|
||||
}
|
||||
|
||||
// Target represents the kubernetes object that the patch is applied to
|
||||
|
||||
83
pkg/patch/transformer/factory.go
Normal file
83
pkg/patch/transformer/factory.go
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
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 transformer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
yamlpatch "github.com/krishicks/yaml-patch"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/loader"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/patch"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resource"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/transformers"
|
||||
)
|
||||
|
||||
// PatchJson6902Factory makes PatchJson6902 transformers.
|
||||
type PatchJson6902Factory struct {
|
||||
targetId resource.ResId
|
||||
operationsYAML yamlpatch.Patch
|
||||
operationsJSON jsonpatch.Patch
|
||||
}
|
||||
|
||||
// NewPatchJson6902Factory returns a new PatchJson6902Factory.
|
||||
func NewPatchJson6902Factory(l loader.Loader, p patch.PatchJson6902) (*PatchJson6902Factory, error) {
|
||||
|
||||
if p.Target == nil {
|
||||
return nil, fmt.Errorf("must specify the target field in patchesJson6902")
|
||||
}
|
||||
if p.Path != "" && p.JsonPatch != nil {
|
||||
return nil, fmt.Errorf("cannot specify path and jsonPath at the same time")
|
||||
}
|
||||
|
||||
targetId := resource.NewResIdWithPrefixNamespace(
|
||||
schema.GroupVersionKind{
|
||||
Group: p.Target.Group,
|
||||
Version: p.Target.Version,
|
||||
Kind: p.Target.Kind,
|
||||
},
|
||||
p.Target.Name,
|
||||
"",
|
||||
p.Target.Namespace,
|
||||
)
|
||||
|
||||
if p.JsonPatch != nil {
|
||||
return &PatchJson6902Factory{targetId: targetId, operationsYAML: p.JsonPatch}, nil
|
||||
}
|
||||
if p.Path != "" {
|
||||
rawOp, err := l.Load(p.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
patch, err := jsonpatch.DecodePatch(rawOp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &PatchJson6902Factory{targetId: targetId, operationsJSON: patch}, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// MakePatchJson6902Transformer returns a transformer for applying Json6902 patch
|
||||
func (f *PatchJson6902Factory) MakePatchJson6902Transformer() (transformers.Transformer, error) {
|
||||
if f.operationsJSON != nil {
|
||||
return newPatchJson6902JSONTransformer(f.targetId, f.operationsJSON)
|
||||
}
|
||||
return newPatchJson6902YAMLTransformer(f.targetId, f.operationsYAML)
|
||||
}
|
||||
156
pkg/patch/transformer/factory_test.go
Normal file
156
pkg/patch/transformer/factory_test.go
Normal file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
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 transformer
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/internal/loadertest"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/patch"
|
||||
)
|
||||
|
||||
func TestNewPatchJson6902FactoryNull(t *testing.T) {
|
||||
p := patch.PatchJson6902{
|
||||
Target: &patch.Target{
|
||||
Name: "some-name",
|
||||
},
|
||||
}
|
||||
f, err := NewPatchJson6902Factory(nil, p)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error : %v", err)
|
||||
}
|
||||
if f != nil {
|
||||
t.Fatal("a nil should be returned")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewPatchJson6902FactoryNoTarget(t *testing.T) {
|
||||
p := patch.PatchJson6902{}
|
||||
_, err := NewPatchJson6902Factory(nil, p)
|
||||
if err == nil {
|
||||
t.Fatal("expected error")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "must specify the target field in patchesJson6902") {
|
||||
t.Fatalf("incorrect error returned: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewPatchJson6902FactoryConflict(t *testing.T) {
|
||||
jsonPatch := []byte(`
|
||||
target:
|
||||
name: some-name
|
||||
kind: Deployment
|
||||
jsonPatch:
|
||||
- op: replace
|
||||
path: /spec/template/spec/containers/0/name
|
||||
value: my-nginx
|
||||
- op: add
|
||||
path: /spec/template/spec/containers/0/command
|
||||
value: [arg1,arg2,arg3]
|
||||
path: /some/dir/some/file
|
||||
`)
|
||||
p := patch.PatchJson6902{}
|
||||
err := yaml.Unmarshal(jsonPatch, &p)
|
||||
if err != nil {
|
||||
t.Fatalf("expected error %v", err)
|
||||
}
|
||||
_, err = NewPatchJson6902Factory(nil, p)
|
||||
if err == nil {
|
||||
t.Fatal("expected error")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "cannot specify path and jsonPath at the same time") {
|
||||
t.Fatalf("incorrect error returned %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewPatchJson6902FactoryJSON(t *testing.T) {
|
||||
ldr := loadertest.NewFakeLoader("/testpath")
|
||||
operations := []byte(`[
|
||||
{"op": "replace", "path": "/spec/template/spec/containers/0/name", "value": "my-nginx"},
|
||||
{"op": "add", "path": "/spec/replica", "value": "3"},
|
||||
{"op": "add", "path": "/spec/template/spec/containers/0/command", "value": ["arg1", "arg2", "arg3"]}
|
||||
]`)
|
||||
err := ldr.AddFile("/testpath/patch.json", operations)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to setup fake ldr.")
|
||||
}
|
||||
|
||||
jsonPatch := []byte(`
|
||||
target:
|
||||
kind: Deployment
|
||||
name: some-name
|
||||
path: /testpath/patch.json
|
||||
`)
|
||||
p := patch.PatchJson6902{}
|
||||
err = yaml.Unmarshal(jsonPatch, &p)
|
||||
if err != nil {
|
||||
t.Fatal("expected error")
|
||||
}
|
||||
|
||||
f, err := NewPatchJson6902Factory(ldr, p)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error : %v", err)
|
||||
}
|
||||
if f == nil {
|
||||
t.Fatalf("the returned factory shouldn't be nil ")
|
||||
}
|
||||
|
||||
_, err = f.MakePatchJson6902Transformer()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error : %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewPatchJson6902FactoryYAML(t *testing.T) {
|
||||
jsonPatch := []byte(`
|
||||
target:
|
||||
name: some-name
|
||||
kind: Deployment
|
||||
jsonPatch:
|
||||
- op: replace
|
||||
path: /spec/template/spec/containers/0/name
|
||||
value: my-nginx
|
||||
- op: add
|
||||
path: /spec/replica
|
||||
value: 3
|
||||
- op: add
|
||||
path: /spec/template/spec/containers/0/command
|
||||
value: ["arg1", "arg2", "arg3"]
|
||||
`)
|
||||
p := patch.PatchJson6902{}
|
||||
err := yaml.Unmarshal(jsonPatch, &p)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error : %v", err)
|
||||
}
|
||||
|
||||
f, err := NewPatchJson6902Factory(nil, p)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error : %v", err)
|
||||
}
|
||||
if f == nil {
|
||||
t.Fatalf("the returned factory shouldn't be nil ")
|
||||
}
|
||||
|
||||
_, err = f.MakePatchJson6902Transformer()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error : %v", err)
|
||||
}
|
||||
|
||||
}
|
||||
62
pkg/patch/transformer/patchjson6902json.go
Normal file
62
pkg/patch/transformer/patchjson6902json.go
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
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 transformer
|
||||
|
||||
import (
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resmap"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resource"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/transformers"
|
||||
)
|
||||
|
||||
// patchJson6902Transformer applies patches.
|
||||
type patchJson6902JSONTransformer struct {
|
||||
target resource.ResId
|
||||
patch jsonpatch.Patch
|
||||
}
|
||||
|
||||
var _ transformers.Transformer = &patchJson6902JSONTransformer{}
|
||||
|
||||
// newPatchJson6902JSONTransformer constructs a PatchJson6902 transformer.
|
||||
func newPatchJson6902JSONTransformer(t resource.ResId, p jsonpatch.Patch) (transformers.Transformer, error) {
|
||||
if len(p) == 0 {
|
||||
return transformers.NewNoOpTransformer(), nil
|
||||
}
|
||||
return &patchJson6902JSONTransformer{target: t, patch: p}, nil
|
||||
}
|
||||
|
||||
// Transform apply the json patches on top of the base resources.
|
||||
func (t *patchJson6902JSONTransformer) Transform(baseResourceMap resmap.ResMap) error {
|
||||
obj, err := findTargetObj(baseResourceMap, t.target)
|
||||
if obj == nil {
|
||||
return err
|
||||
}
|
||||
rawObj, err := obj.Unstructured.MarshalJSON()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
modifiedObj, err := t.patch.Apply(rawObj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = obj.UnmarshalJSON(modifiedObj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
113
pkg/patch/transformer/patchjson6902json_test.go
Normal file
113
pkg/patch/transformer/patchjson6902json_test.go
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
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 transformer
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resmap"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resource"
|
||||
)
|
||||
|
||||
func TestJsonPatchJSONTransformer_Transform(t *testing.T) {
|
||||
id := resource.NewResId(deploy, "deploy1")
|
||||
base := resmap.ResMap{
|
||||
id: resource.NewResourceFromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "deploy1",
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"template": map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]interface{}{
|
||||
"old-label": "old-value",
|
||||
},
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"containers": []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "nginx",
|
||||
"image": "nginx",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
||||
operations := []byte(`[
|
||||
{"op": "replace", "path": "/spec/template/spec/containers/0/name", "value": "my-nginx"},
|
||||
{"op": "add", "path": "/spec/replica", "value": "3"},
|
||||
{"op": "add", "path": "/spec/template/spec/containers/0/command", "value": ["arg1", "arg2", "arg3"]}
|
||||
]`)
|
||||
patch, err := jsonpatch.DecodePatch(operations)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error : %v", err)
|
||||
}
|
||||
expected := resmap.ResMap{
|
||||
id: resource.NewResourceFromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "deploy1",
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"replica": "3",
|
||||
"template": map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]interface{}{
|
||||
"old-label": "old-value",
|
||||
},
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"containers": []interface{}{
|
||||
map[string]interface{}{
|
||||
"image": "nginx",
|
||||
"name": "my-nginx",
|
||||
"command": []interface{}{
|
||||
"arg1",
|
||||
"arg2",
|
||||
"arg3",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
jpt, err := newPatchJson6902JSONTransformer(id, patch)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error : %v", err)
|
||||
}
|
||||
err = jpt.Transform(base)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(base, expected) {
|
||||
err = expected.ErrorIfNotEqual(base)
|
||||
t.Fatalf("actual doesn't match expected: %v", err)
|
||||
}
|
||||
}
|
||||
63
pkg/patch/transformer/patchjson6902yaml.go
Normal file
63
pkg/patch/transformer/patchjson6902yaml.go
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
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 transformer
|
||||
|
||||
import (
|
||||
"github.com/ghodss/yaml"
|
||||
yamlpatch "github.com/krishicks/yaml-patch"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resmap"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resource"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/transformers"
|
||||
)
|
||||
|
||||
// patchJson6902Transformer applies patches.
|
||||
type patchJson6902YAMLTransformer struct {
|
||||
target resource.ResId
|
||||
patch yamlpatch.Patch
|
||||
}
|
||||
|
||||
var _ transformers.Transformer = &patchJson6902YAMLTransformer{}
|
||||
|
||||
// newPatchJson6902YAMLTransformer constructs a PatchJson6902 transformer.
|
||||
func newPatchJson6902YAMLTransformer(t resource.ResId, p yamlpatch.Patch) (transformers.Transformer, error) {
|
||||
if len(p) == 0 {
|
||||
return transformers.NewNoOpTransformer(), nil
|
||||
}
|
||||
return &patchJson6902YAMLTransformer{target: t, patch: p}, nil
|
||||
}
|
||||
|
||||
// Transform apply the json patches on top of the base resources.
|
||||
func (t *patchJson6902YAMLTransformer) Transform(baseResourceMap resmap.ResMap) error {
|
||||
obj, err := findTargetObj(baseResourceMap, t.target)
|
||||
if obj == nil {
|
||||
return err
|
||||
}
|
||||
rawObj, err := yaml.Marshal(obj.Unstructured.Object)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
modifiedObj, err := t.patch.Apply(rawObj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = yaml.Unmarshal(modifiedObj, &obj.Unstructured.Object)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
128
pkg/patch/transformer/patchjson6902yaml_test.go
Normal file
128
pkg/patch/transformer/patchjson6902yaml_test.go
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
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 transformer
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
yamlpatch "github.com/krishicks/yaml-patch"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resmap"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resource"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
var deploy = schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"}
|
||||
|
||||
func TestJsonPatchYAMLTransformer_Transform(t *testing.T) {
|
||||
id := resource.NewResId(deploy, "deploy1")
|
||||
base := resmap.ResMap{
|
||||
id: resource.NewResourceFromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "deploy1",
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"template": map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]interface{}{
|
||||
"old-label": "old-value",
|
||||
},
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"containers": []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "nginx",
|
||||
"image": "nginx",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
||||
var image, replica, command interface{}
|
||||
image = "my-nginx"
|
||||
replica = "3"
|
||||
command = []string{"arg1", "arg2", "arg3"}
|
||||
patch := yamlpatch.Patch{
|
||||
{
|
||||
Op: "replace",
|
||||
Path: "/spec/template/spec/containers/0/name",
|
||||
Value: yamlpatch.NewNode(&image),
|
||||
},
|
||||
{
|
||||
Op: "add",
|
||||
Path: "/spec/replica",
|
||||
Value: yamlpatch.NewNode(&replica),
|
||||
},
|
||||
{
|
||||
Op: "add",
|
||||
Path: "/spec/template/spec/containers/0/command",
|
||||
Value: yamlpatch.NewNode(&command),
|
||||
},
|
||||
}
|
||||
|
||||
expected := resmap.ResMap{
|
||||
id: resource.NewResourceFromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "deploy1",
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"replica": "3",
|
||||
"template": map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]interface{}{
|
||||
"old-label": "old-value",
|
||||
},
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"containers": []interface{}{
|
||||
map[string]interface{}{
|
||||
"image": "nginx",
|
||||
"name": "my-nginx",
|
||||
"command": []interface{}{
|
||||
"arg1",
|
||||
"arg2",
|
||||
"arg3",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
jpt, err := newPatchJson6902YAMLTransformer(id, patch)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error : %v", err)
|
||||
}
|
||||
err = jpt.Transform(base)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(base, expected) {
|
||||
err = expected.ErrorIfNotEqual(base)
|
||||
t.Fatalf("actual doesn't match expected: %v", err)
|
||||
}
|
||||
}
|
||||
47
pkg/patch/transformer/util.go
Normal file
47
pkg/patch/transformer/util.go
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
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 transformer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resmap"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resource"
|
||||
)
|
||||
|
||||
func findTargetObj(m resmap.ResMap, targetId resource.ResId) (*resource.Resource, error) {
|
||||
matchedIds := m.FindByGVKN(targetId)
|
||||
if targetId.Namespace() != "" {
|
||||
ids := []resource.ResId{}
|
||||
for _, id := range matchedIds {
|
||||
if id.Namespace() == targetId.Namespace() {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
}
|
||||
matchedIds = ids
|
||||
}
|
||||
|
||||
if len(matchedIds) == 0 {
|
||||
log.Printf("Couldn't find any object to apply the json patch %v, skipping it.", targetId)
|
||||
return nil, nil
|
||||
}
|
||||
if len(matchedIds) > 1 {
|
||||
return nil, fmt.Errorf("found multiple objects that the patch can apply %v", matchedIds)
|
||||
}
|
||||
return m[matchedIds[0]], nil
|
||||
}
|
||||
201
vendor/github.com/krishicks/yaml-patch/LICENSE
generated
vendored
Normal file
201
vendor/github.com/krishicks/yaml-patch/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
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.
|
||||
17
vendor/github.com/krishicks/yaml-patch/Makefile
generated
vendored
Normal file
17
vendor/github.com/krishicks/yaml-patch/Makefile
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
CGO_ENABLED=0
|
||||
|
||||
all: windows linux darwin
|
||||
|
||||
linux:
|
||||
GOOS=linux GOARCH=amd64 go build -o yaml_patch_linux cmd/yaml-patch/*.go
|
||||
|
||||
windows:
|
||||
GOOS=windows GOARCH=amd64 go build -o yaml_patch.exe cmd/yaml-patch/*.go
|
||||
|
||||
darwin:
|
||||
GOOS=darwin GOARCH=amd64 go build -o yaml_patch_darwin cmd/yaml-patch/*.go
|
||||
|
||||
clean:
|
||||
rm yaml_patch_linux
|
||||
rm yaml_patch.exe
|
||||
rm yaml_patch_darwin
|
||||
86
vendor/github.com/krishicks/yaml-patch/README.md
generated
vendored
Normal file
86
vendor/github.com/krishicks/yaml-patch/README.md
generated
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
# yaml-patch
|
||||
|
||||
`yaml-patch` is a version of Evan Phoenix's
|
||||
[json-patch](https://github.com/evanphx/json-patch), which is an implementation
|
||||
of [JavaScript Object Notation (JSON) Patch](https://tools.ietf.org/html/rfc6902),
|
||||
but for YAML.
|
||||
|
||||
|
||||
## Installing
|
||||
|
||||
`go get github.com/krishicks/yaml-patch`
|
||||
|
||||
If you want to use the CLI:
|
||||
|
||||
`go get github.com/krishicks/yaml-patch/cmd/yaml-patch`
|
||||
|
||||
## API
|
||||
|
||||
Given the following RFC6902-ish YAML document, `ops`:
|
||||
|
||||
```
|
||||
---
|
||||
- op: add
|
||||
path: /baz/waldo
|
||||
value: fred
|
||||
```
|
||||
|
||||
And the following YAML that is to be modified, `src`:
|
||||
|
||||
```
|
||||
---
|
||||
foo: bar
|
||||
baz:
|
||||
quux: grault
|
||||
```
|
||||
|
||||
Decode the ops file into a patch:
|
||||
|
||||
```
|
||||
patch, err := yamlpatch.DecodePatch(ops)
|
||||
// handle err
|
||||
```
|
||||
|
||||
Then apply that patch to the document:
|
||||
|
||||
```
|
||||
dst, err := patch.Apply(src)
|
||||
// handle err
|
||||
|
||||
// do something with dst
|
||||
```
|
||||
|
||||
### Example
|
||||
|
||||
```
|
||||
doc := []byte(`---
|
||||
foo: bar
|
||||
baz:
|
||||
quux: grault
|
||||
`)
|
||||
|
||||
ops := []byte(`---
|
||||
- op: add
|
||||
path: /baz/waldo
|
||||
value: fred
|
||||
`)
|
||||
|
||||
patch, err := yamlpatch.DecodePatch(ops)
|
||||
if err != nil {
|
||||
log.Fatalf("decoding patch failed: %s", err)
|
||||
}
|
||||
|
||||
bs, err := patch.Apply(doc)
|
||||
if err != nil {
|
||||
log.Fatalf("applying patch failed: %s", err)
|
||||
}
|
||||
|
||||
fmt.Println(string(bs))
|
||||
```
|
||||
|
||||
```
|
||||
baz:
|
||||
quux: grault
|
||||
waldo: fred
|
||||
foo: bar
|
||||
```
|
||||
39
vendor/github.com/krishicks/yaml-patch/cmd/yaml-patch/file_flag.go
generated
vendored
Normal file
39
vendor/github.com/krishicks/yaml-patch/cmd/yaml-patch/file_flag.go
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
package main
|
||||
|
||||
// Thanks, Concourse!
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// FileFlag is a flag for passing a path to a file on disk. The file is
|
||||
// expected to be a file, not a directory, that actually exists.
|
||||
type FileFlag string
|
||||
|
||||
// UnmarshalFlag implements go-flag's Unmarshaler interface
|
||||
func (f *FileFlag) UnmarshalFlag(value string) error {
|
||||
stat, err := os.Stat(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if stat.IsDir() {
|
||||
return fmt.Errorf("path '%s' is a directory, not a file", value)
|
||||
}
|
||||
|
||||
abs, err := filepath.Abs(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*f = FileFlag(abs)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Path is the path to the file
|
||||
func (f FileFlag) Path() string {
|
||||
return string(f)
|
||||
}
|
||||
57
vendor/github.com/krishicks/yaml-patch/cmd/yaml-patch/main.go
generated
vendored
Normal file
57
vendor/github.com/krishicks/yaml-patch/cmd/yaml-patch/main.go
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
flags "github.com/jessevdk/go-flags"
|
||||
yamlpatch "github.com/krishicks/yaml-patch"
|
||||
)
|
||||
|
||||
type opts struct {
|
||||
OpsFiles []FileFlag `long:"ops-file" short:"o" value-name:"PATH" description:"Path to file with one or more operations"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
var o opts
|
||||
_, err := flags.Parse(&o)
|
||||
if err != nil {
|
||||
log.Fatalf("error: %s\n", err)
|
||||
}
|
||||
|
||||
placeholderWrapper := yamlpatch.NewPlaceholderWrapper("{{", "}}")
|
||||
|
||||
var patches []yamlpatch.Patch
|
||||
for _, opsFile := range o.OpsFiles {
|
||||
var bs []byte
|
||||
bs, err = ioutil.ReadFile(opsFile.Path())
|
||||
if err != nil {
|
||||
log.Fatalf("error reading opsfile: %s", err)
|
||||
}
|
||||
|
||||
var patch yamlpatch.Patch
|
||||
patch, err = yamlpatch.DecodePatch(placeholderWrapper.Wrap(bs))
|
||||
if err != nil {
|
||||
log.Fatalf("error decoding opsfile: %s", err)
|
||||
}
|
||||
|
||||
patches = append(patches, patch)
|
||||
}
|
||||
|
||||
doc, err := ioutil.ReadAll(os.Stdin)
|
||||
if err != nil {
|
||||
log.Fatalf("error reading from stdin: %s", err)
|
||||
}
|
||||
|
||||
mdoc := placeholderWrapper.Wrap(doc)
|
||||
for _, patch := range patches {
|
||||
mdoc, err = patch.Apply(mdoc)
|
||||
if err != nil {
|
||||
log.Fatalf("error applying patch: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("%s", placeholderWrapper.Unwrap(mdoc))
|
||||
}
|
||||
18
vendor/github.com/krishicks/yaml-patch/cmd/yaml-patch/main_suite_test.go
generated
vendored
Normal file
18
vendor/github.com/krishicks/yaml-patch/cmd/yaml-patch/main_suite_test.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
package main_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/onsi/gomega/gexec"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
var _ = AfterSuite(func() {
|
||||
gexec.CleanupBuildArtifacts()
|
||||
})
|
||||
|
||||
func TestMain(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Main Suite")
|
||||
}
|
||||
14
vendor/github.com/krishicks/yaml-patch/cmd/yaml-patch/main_test.go
generated
vendored
Normal file
14
vendor/github.com/krishicks/yaml-patch/cmd/yaml-patch/main_test.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
package main_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/onsi/gomega/gexec"
|
||||
)
|
||||
|
||||
var _ = Describe("yaml-patch", func() {
|
||||
It("builds", func() {
|
||||
_, err := gexec.Build("github.com/krishicks/yaml-patch/cmd/yaml-patch")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
})
|
||||
167
vendor/github.com/krishicks/yaml-patch/container.go
generated
vendored
Normal file
167
vendor/github.com/krishicks/yaml-patch/container.go
generated
vendored
Normal file
@@ -0,0 +1,167 @@
|
||||
package yamlpatch
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Container is the interface for performing operations on Nodes
|
||||
type Container interface {
|
||||
Get(key string) (*Node, error)
|
||||
Set(key string, val *Node) error
|
||||
Add(key string, val *Node) error
|
||||
Remove(key string) error
|
||||
}
|
||||
|
||||
type nodeMap map[interface{}]*Node
|
||||
|
||||
func (n *nodeMap) Set(key string, val *Node) error {
|
||||
(*n)[key] = val
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *nodeMap) Add(key string, val *Node) error {
|
||||
(*n)[key] = val
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *nodeMap) Get(key string) (*Node, error) {
|
||||
return (*n)[key], nil
|
||||
}
|
||||
|
||||
func (n *nodeMap) Remove(key string) error {
|
||||
_, ok := (*n)[key]
|
||||
if !ok {
|
||||
return fmt.Errorf("Unable to remove nonexistent key: %s", key)
|
||||
}
|
||||
|
||||
delete(*n, key)
|
||||
return nil
|
||||
}
|
||||
|
||||
type nodeSlice []*Node
|
||||
|
||||
func (n *nodeSlice) Set(index string, val *Node) error {
|
||||
i, err := strconv.Atoi(index)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sz := len(*n)
|
||||
if i+1 > sz {
|
||||
sz = i + 1
|
||||
}
|
||||
|
||||
ary := make([]*Node, sz)
|
||||
|
||||
cur := *n
|
||||
|
||||
copy(ary, cur)
|
||||
|
||||
if i >= len(ary) {
|
||||
return fmt.Errorf("Unable to access invalid index: %d", i)
|
||||
}
|
||||
|
||||
ary[i] = val
|
||||
|
||||
*n = ary
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *nodeSlice) Add(index string, val *Node) error {
|
||||
if index == "-" {
|
||||
*n = append(*n, val)
|
||||
return nil
|
||||
}
|
||||
|
||||
i, err := strconv.Atoi(index)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ary := make([]*Node, len(*n)+1)
|
||||
|
||||
cur := *n
|
||||
|
||||
copy(ary[0:i], cur[0:i])
|
||||
ary[i] = val
|
||||
copy(ary[i+1:], cur[i:])
|
||||
|
||||
*n = ary
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *nodeSlice) Get(index string) (*Node, error) {
|
||||
i, err := strconv.Atoi(index)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if i >= 0 && i <= len(*n)-1 {
|
||||
return (*n)[i], nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("Unable to access invalid index: %d", i)
|
||||
}
|
||||
|
||||
func (n *nodeSlice) Remove(index string) error {
|
||||
i, err := strconv.Atoi(index)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cur := *n
|
||||
|
||||
if i >= len(cur) {
|
||||
return fmt.Errorf("Unable to remove invalid index: %d", i)
|
||||
}
|
||||
|
||||
ary := make([]*Node, len(cur)-1)
|
||||
|
||||
copy(ary[0:i], cur[0:i])
|
||||
copy(ary[i:], cur[i+1:])
|
||||
|
||||
*n = ary
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func findContainer(c Container, path *OpPath) (Container, string, error) {
|
||||
parts, key, err := path.Decompose()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
foundContainer := c
|
||||
|
||||
for _, part := range parts {
|
||||
node, err := foundContainer.Get(decodePatchKey(part))
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
if node == nil {
|
||||
return nil, "", fmt.Errorf("path does not exist: %s", path)
|
||||
}
|
||||
|
||||
foundContainer = node.Container()
|
||||
}
|
||||
|
||||
return foundContainer, decodePatchKey(key), nil
|
||||
}
|
||||
|
||||
// From http://tools.ietf.org/html/rfc6901#section-4 :
|
||||
//
|
||||
// Evaluation of each reference token begins by decoding any escaped
|
||||
// character sequence. This is performed by first transforming any
|
||||
// occurrence of the sequence '~1' to '/', and then transforming any
|
||||
// occurrence of the sequence '~0' to '~'.
|
||||
|
||||
var (
|
||||
rfc6901Decoder = strings.NewReplacer("~1", "/", "~0", "~")
|
||||
)
|
||||
|
||||
func decodePatchKey(k string) string {
|
||||
return rfc6901Decoder.Replace(k)
|
||||
}
|
||||
83
vendor/github.com/krishicks/yaml-patch/node.go
generated
vendored
Normal file
83
vendor/github.com/krishicks/yaml-patch/node.go
generated
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
package yamlpatch
|
||||
|
||||
import "reflect"
|
||||
|
||||
// Node holds a YAML document that has not yet been processed into a NodeMap or
|
||||
// NodeSlice
|
||||
type Node struct {
|
||||
raw *interface{}
|
||||
container Container
|
||||
}
|
||||
|
||||
// NewNode returns a new Node. It expects a pointer to an interface{}
|
||||
func NewNode(raw *interface{}) *Node {
|
||||
return &Node{
|
||||
raw: raw,
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalYAML implements yaml.Marshaler, and returns the correct interface{}
|
||||
// to be marshaled
|
||||
func (n *Node) MarshalYAML() (interface{}, error) {
|
||||
if n.container != nil {
|
||||
return n.container, nil
|
||||
}
|
||||
|
||||
return *n.raw, nil
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements yaml.Unmarshaler
|
||||
func (n *Node) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
var data interface{}
|
||||
|
||||
err := unmarshal(&data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
n.raw = &data
|
||||
return nil
|
||||
}
|
||||
|
||||
// Empty returns whether the raw value is nil
|
||||
func (n *Node) Empty() bool {
|
||||
return *n.raw == nil
|
||||
}
|
||||
|
||||
// Container returns the node as a Container
|
||||
func (n *Node) Container() Container {
|
||||
if n.container != nil {
|
||||
return n.container
|
||||
}
|
||||
|
||||
switch rt := (*n.raw).(type) {
|
||||
case []interface{}:
|
||||
c := make(nodeSlice, len(rt))
|
||||
n.container = &c
|
||||
|
||||
for i := range rt {
|
||||
c[i] = NewNode(&rt[i])
|
||||
}
|
||||
case map[interface{}]interface{}:
|
||||
c := make(nodeMap, len(rt))
|
||||
n.container = &c
|
||||
|
||||
for k := range rt {
|
||||
v := rt[k]
|
||||
c[k] = NewNode(&v)
|
||||
}
|
||||
}
|
||||
|
||||
return n.container
|
||||
}
|
||||
|
||||
// Equal compares the values of the raw interfaces that the YAML was
|
||||
// unmarshaled into
|
||||
func (n *Node) Equal(other *Node) bool {
|
||||
return reflect.DeepEqual(*n.raw, *other.raw)
|
||||
}
|
||||
|
||||
// Value returns the raw value of the node
|
||||
func (n *Node) Value() interface{} {
|
||||
return *n.raw
|
||||
}
|
||||
181
vendor/github.com/krishicks/yaml-patch/operation.go
generated
vendored
Normal file
181
vendor/github.com/krishicks/yaml-patch/operation.go
generated
vendored
Normal file
@@ -0,0 +1,181 @@
|
||||
package yamlpatch
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Op is a type alias
|
||||
type Op string
|
||||
|
||||
// Ops
|
||||
const (
|
||||
opAdd Op = "add"
|
||||
opRemove Op = "remove"
|
||||
opReplace Op = "replace"
|
||||
opMove Op = "move"
|
||||
opCopy Op = "copy"
|
||||
opTest Op = "test"
|
||||
)
|
||||
|
||||
// OpPath is an RFC6902 'pointer'
|
||||
type OpPath string
|
||||
|
||||
// Decompose returns the pointer's components:
|
||||
// "/foo" => [], "foo"
|
||||
// "/foo/1" => ["foo"], "1"
|
||||
// "/foo/1/bar" => ["foo", "1"], "bar"
|
||||
func (p *OpPath) Decompose() ([]string, string, error) {
|
||||
path := string(*p)
|
||||
|
||||
if !strings.HasPrefix(path, "/") {
|
||||
return nil, "", fmt.Errorf("operation path is missing leading '/': %s", path)
|
||||
}
|
||||
|
||||
parts := strings.Split(path, "/")[1:]
|
||||
|
||||
return parts[:len(parts)-1], parts[len(parts)-1], nil
|
||||
}
|
||||
|
||||
// ContainsExtendedSyntax returns whether the OpPath uses the "key=value"
|
||||
// format, as in "/foo/name=bar", where /foo points at an array that contains
|
||||
// an object with a key "name" that has a value "bar"
|
||||
func (p *OpPath) ContainsExtendedSyntax() bool {
|
||||
return strings.Contains(string(*p), "=")
|
||||
}
|
||||
|
||||
// String returns the OpPath as a string
|
||||
func (p *OpPath) String() string {
|
||||
return string(*p)
|
||||
}
|
||||
|
||||
// Operation is an RFC6902 'Operation'
|
||||
// https://tools.ietf.org/html/rfc6902#section-4
|
||||
type Operation struct {
|
||||
Op Op `yaml:"op,omitempty"`
|
||||
Path OpPath `yaml:"path,omitempty"`
|
||||
From OpPath `yaml:"from,omitempty"`
|
||||
Value *Node `yaml:"value,omitempty"`
|
||||
}
|
||||
|
||||
// Perform executes the operation on the given container
|
||||
func (o *Operation) Perform(c Container) error {
|
||||
var err error
|
||||
|
||||
switch o.Op {
|
||||
case opAdd:
|
||||
err = tryAdd(c, o)
|
||||
case opRemove:
|
||||
err = tryRemove(c, o)
|
||||
case opReplace:
|
||||
err = tryReplace(c, o)
|
||||
case opMove:
|
||||
err = tryMove(c, o)
|
||||
case opCopy:
|
||||
err = tryCopy(c, o)
|
||||
case opTest:
|
||||
err = tryTest(c, o)
|
||||
default:
|
||||
err = fmt.Errorf("Unexpected op: %s", o.Op)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func tryAdd(doc Container, op *Operation) error {
|
||||
con, key, err := findContainer(doc, &op.Path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("yamlpatch add operation does not apply: doc is missing path: %s", op.Path)
|
||||
}
|
||||
|
||||
return con.Add(key, op.Value)
|
||||
}
|
||||
|
||||
func tryRemove(doc Container, op *Operation) error {
|
||||
con, key, err := findContainer(doc, &op.Path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("yamlpatch remove operation does not apply: doc is missing path: %s", op.Path)
|
||||
}
|
||||
|
||||
return con.Remove(key)
|
||||
}
|
||||
|
||||
func tryReplace(doc Container, op *Operation) error {
|
||||
con, key, err := findContainer(doc, &op.Path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("yamlpatch replace operation does not apply: doc is missing path: %s", op.Path)
|
||||
}
|
||||
|
||||
val, err := con.Get(key)
|
||||
if val == nil || err != nil {
|
||||
return fmt.Errorf("yamlpatch replace operation does not apply: doc is missing key: %s", op.Path)
|
||||
}
|
||||
|
||||
return con.Set(key, op.Value)
|
||||
}
|
||||
|
||||
func tryMove(doc Container, op *Operation) error {
|
||||
con, key, err := findContainer(doc, &op.From)
|
||||
if err != nil {
|
||||
return fmt.Errorf("yamlpatch move operation does not apply: doc is missing from path: %s", op.From)
|
||||
}
|
||||
|
||||
val, err := con.Get(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = con.Remove(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
con, key, err = findContainer(doc, &op.Path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("yamlpatch move operation does not apply: doc is missing destination path: %s", op.Path)
|
||||
}
|
||||
|
||||
return con.Set(key, val)
|
||||
}
|
||||
|
||||
func tryCopy(doc Container, op *Operation) error {
|
||||
con, key, err := findContainer(doc, &op.From)
|
||||
if err != nil {
|
||||
return fmt.Errorf("copy operation does not apply: doc is missing from path: %s", op.From)
|
||||
}
|
||||
|
||||
val, err := con.Get(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
con, key, err = findContainer(doc, &op.Path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("copy operation does not apply: doc is missing destination path: %s", op.Path)
|
||||
}
|
||||
|
||||
return con.Set(key, val)
|
||||
}
|
||||
|
||||
func tryTest(doc Container, op *Operation) error {
|
||||
con, key, err := findContainer(doc, &op.Path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("test operation does not apply: doc is missing from path: %s", op.From)
|
||||
}
|
||||
|
||||
val, err := con.Get(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if op.Value.Empty() && val == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if op.Value.Equal(val) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.New("test failed")
|
||||
}
|
||||
60
vendor/github.com/krishicks/yaml-patch/patch.go
generated
vendored
Normal file
60
vendor/github.com/krishicks/yaml-patch/patch.go
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
package yamlpatch
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
// Patch is an ordered collection of operations.
|
||||
type Patch []Operation
|
||||
|
||||
// DecodePatch decodes the passed YAML document as if it were an RFC 6902 patch
|
||||
func DecodePatch(bs []byte) (Patch, error) {
|
||||
var p Patch
|
||||
|
||||
err := yaml.Unmarshal(bs, &p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// Apply returns a YAML document that has been mutated per the patch
|
||||
func (p Patch) Apply(doc []byte) ([]byte, error) {
|
||||
var iface interface{}
|
||||
err := yaml.Unmarshal(doc, &iface)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed unmarshaling doc: %s\n\n%s", string(doc), err)
|
||||
}
|
||||
|
||||
var c Container
|
||||
c = NewNode(&iface).Container()
|
||||
|
||||
for _, op := range p {
|
||||
pathfinder := NewPathFinder(c)
|
||||
if op.Path.ContainsExtendedSyntax() {
|
||||
paths := pathfinder.Find(string(op.Path))
|
||||
if paths == nil {
|
||||
return nil, fmt.Errorf("could not expand pointer: %s", op.Path)
|
||||
}
|
||||
|
||||
for _, path := range paths {
|
||||
newOp := op
|
||||
newOp.Path = OpPath(path)
|
||||
err = newOp.Perform(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = op.Perform(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return yaml.Marshal(c)
|
||||
}
|
||||
606
vendor/github.com/krishicks/yaml-patch/patch_test.go
generated
vendored
Normal file
606
vendor/github.com/krishicks/yaml-patch/patch_test.go
generated
vendored
Normal file
@@ -0,0 +1,606 @@
|
||||
package yamlpatch_test
|
||||
|
||||
import (
|
||||
yamlpatch "github.com/krishicks/yaml-patch"
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/extensions/table"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("Patch", func() {
|
||||
Describe("Apply", func() {
|
||||
DescribeTable(
|
||||
"positive cases",
|
||||
func(doc, ops, expectedYAML string) {
|
||||
patch, err := yamlpatch.DecodePatch([]byte(ops))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
actualBytes, err := patch.Apply([]byte(doc))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
var actualIface interface{}
|
||||
err = yaml.Unmarshal(actualBytes, &actualIface)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
var expectedIface interface{}
|
||||
err = yaml.Unmarshal([]byte(expectedYAML), &expectedIface)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(actualIface).To(Equal(expectedIface))
|
||||
},
|
||||
Entry("adding an element to an object",
|
||||
`---
|
||||
foo: bar
|
||||
`,
|
||||
`---
|
||||
- op: add
|
||||
path: /baz
|
||||
value: qux
|
||||
`,
|
||||
`---
|
||||
foo: bar
|
||||
baz: qux
|
||||
`,
|
||||
),
|
||||
Entry("adding an element to an array",
|
||||
`---
|
||||
foo: [bar,baz]
|
||||
`,
|
||||
`---
|
||||
- op: add
|
||||
path: /foo/1
|
||||
value: qux
|
||||
`,
|
||||
`---
|
||||
foo: [bar,qux,baz]
|
||||
`,
|
||||
),
|
||||
Entry("removing an element from an object",
|
||||
`---
|
||||
foo: bar
|
||||
baz: qux
|
||||
`,
|
||||
`---
|
||||
- op: remove
|
||||
path: /baz
|
||||
`,
|
||||
`---
|
||||
foo: bar
|
||||
`,
|
||||
),
|
||||
Entry("removing an element from an array",
|
||||
`---
|
||||
foo: [bar,qux,baz]
|
||||
`,
|
||||
`---
|
||||
- op: remove
|
||||
path: /foo/1
|
||||
`,
|
||||
`---
|
||||
foo: [bar,baz]
|
||||
`,
|
||||
),
|
||||
Entry("replacing an element in an object",
|
||||
`---
|
||||
foo: bar
|
||||
baz: qux
|
||||
`,
|
||||
`---
|
||||
- op: replace
|
||||
path: /baz
|
||||
value: boo
|
||||
`,
|
||||
`---
|
||||
foo: bar
|
||||
baz: boo
|
||||
`,
|
||||
),
|
||||
Entry("moving an element in an object",
|
||||
`---
|
||||
foo:
|
||||
bar: baz
|
||||
waldo: fred
|
||||
qux:
|
||||
corge: grault
|
||||
`,
|
||||
`---
|
||||
- op: move
|
||||
from: /foo/waldo
|
||||
path: /qux/thud
|
||||
`,
|
||||
`---
|
||||
foo:
|
||||
bar: baz
|
||||
qux:
|
||||
corge: grault
|
||||
thud: fred
|
||||
`,
|
||||
),
|
||||
Entry("moving an element in an array",
|
||||
`---
|
||||
foo: [all, grass, cows, eat]
|
||||
`,
|
||||
`---
|
||||
- op: move
|
||||
from: /foo/1
|
||||
path: /foo/3
|
||||
`,
|
||||
`---
|
||||
foo: [all, cows, eat, grass]
|
||||
`,
|
||||
),
|
||||
Entry("adding an object to an object",
|
||||
`---
|
||||
foo: bar
|
||||
`,
|
||||
`---
|
||||
- op: add
|
||||
path: /child
|
||||
value:
|
||||
grandchild: {}
|
||||
`,
|
||||
`---
|
||||
foo: bar
|
||||
child:
|
||||
grandchild: {}
|
||||
`,
|
||||
),
|
||||
Entry("appending an element to an array",
|
||||
`---
|
||||
foo: [bar]
|
||||
`,
|
||||
`---
|
||||
- op: add
|
||||
path: /foo/-
|
||||
value: [abc,def]
|
||||
`,
|
||||
`---
|
||||
foo: [bar, [abc, def]]
|
||||
`,
|
||||
),
|
||||
Entry("removing a nil element from an object",
|
||||
`---
|
||||
foo: bar
|
||||
qux:
|
||||
baz: 1
|
||||
bar: ~
|
||||
`,
|
||||
`---
|
||||
- op: remove
|
||||
path: /qux/bar
|
||||
`,
|
||||
`---
|
||||
foo: bar
|
||||
qux:
|
||||
baz: 1
|
||||
`,
|
||||
),
|
||||
Entry("adding a nil element to an object",
|
||||
`---
|
||||
foo: bar
|
||||
`,
|
||||
`---
|
||||
- op: add
|
||||
path: /baz
|
||||
value: ~
|
||||
`,
|
||||
`---
|
||||
foo: bar
|
||||
baz: ~
|
||||
`,
|
||||
),
|
||||
Entry("replacing the sole element in an array",
|
||||
`---
|
||||
foo: [bar]
|
||||
`,
|
||||
`---
|
||||
- op: replace
|
||||
path: /foo/0
|
||||
value: baz
|
||||
`,
|
||||
`---
|
||||
foo: [baz]
|
||||
`,
|
||||
),
|
||||
Entry("replacing an element in an array within a root array",
|
||||
`---
|
||||
- foo: [bar, qux, baz]
|
||||
`,
|
||||
`---
|
||||
- op: replace
|
||||
path: /0/foo/0
|
||||
value: bum
|
||||
`,
|
||||
`---
|
||||
- foo: [bum, qux, baz]
|
||||
`,
|
||||
),
|
||||
Entry("copying an element in an array within a root array with an index",
|
||||
`---
|
||||
- foo: [bar, qux, baz]
|
||||
bar: [qux, baz]
|
||||
`,
|
||||
`---
|
||||
- op: copy
|
||||
from: /0/foo/0
|
||||
path: /0/bar/0
|
||||
`,
|
||||
`---
|
||||
- foo: [bar, qux, baz]
|
||||
bar: [bar, baz]
|
||||
`,
|
||||
),
|
||||
Entry("testing for the existence of a nil value in an object",
|
||||
`---
|
||||
baz: ~
|
||||
`,
|
||||
`---
|
||||
- op: test
|
||||
path: /baz
|
||||
value: ~
|
||||
`,
|
||||
`---
|
||||
baz: ~
|
||||
`,
|
||||
),
|
||||
Entry("testing for the existence of a nil key in an object",
|
||||
`---
|
||||
baz: ~
|
||||
`,
|
||||
`---
|
||||
- op: test
|
||||
path: /foo
|
||||
value: ~
|
||||
`,
|
||||
`---
|
||||
baz: ~
|
||||
`,
|
||||
),
|
||||
Entry("testing for the existence of a nil value in an object",
|
||||
`---
|
||||
baz: qux
|
||||
`,
|
||||
`---
|
||||
- op: test
|
||||
path: /baz
|
||||
value: qux
|
||||
`,
|
||||
`---
|
||||
baz: qux
|
||||
`,
|
||||
),
|
||||
Entry("testing for the existence of an element in an array",
|
||||
`---
|
||||
foo: [a, 2, c]
|
||||
`,
|
||||
`---
|
||||
- op: test
|
||||
path: /foo/1
|
||||
value: 2
|
||||
`,
|
||||
`---
|
||||
foo: [a, 2, c]
|
||||
`,
|
||||
),
|
||||
Entry("testing for the existence of an element in an object using escape ordering",
|
||||
`---
|
||||
baz/foo: qux
|
||||
`,
|
||||
`---
|
||||
- op: test
|
||||
path: /baz~1foo
|
||||
value: qux
|
||||
`,
|
||||
`---
|
||||
baz/foo: qux
|
||||
`,
|
||||
),
|
||||
Entry("testing for the existence of an object",
|
||||
`---
|
||||
foo:
|
||||
- bar: baz
|
||||
qux: corge
|
||||
`,
|
||||
`---
|
||||
- op: test
|
||||
path: /foo/0
|
||||
value:
|
||||
bar: baz
|
||||
qux: corge
|
||||
`,
|
||||
`---
|
||||
foo:
|
||||
- bar: baz
|
||||
qux: corge
|
||||
`,
|
||||
),
|
||||
XEntry("copying an element in an array within a root array to a destination without an index",
|
||||
// this is in jsonpatch, but I'd like confirmation from the spec that this is intended
|
||||
`---
|
||||
- foo: [bar, qux, baz]
|
||||
bar: [qux, baz]
|
||||
`,
|
||||
`---
|
||||
- op: copy
|
||||
from: /0/foo/0
|
||||
path: /0/bar
|
||||
`,
|
||||
`---
|
||||
- foo: [bar, qux, baz]
|
||||
bar: [bar, qux, baz]
|
||||
`,
|
||||
),
|
||||
XEntry("copying an element in an array within a root array to a destination without an index",
|
||||
`---
|
||||
- foo:
|
||||
bar: [qux, baz]
|
||||
baz:
|
||||
qux: bum
|
||||
`,
|
||||
`---
|
||||
- op: copy
|
||||
from: /0/foo/bar
|
||||
path: /0/baz/bar
|
||||
`,
|
||||
`---
|
||||
- foo: [bar, qux, baz]
|
||||
bar: [bar, qux, baz]
|
||||
`,
|
||||
),
|
||||
)
|
||||
|
||||
DescribeTable(
|
||||
"with extended syntax",
|
||||
func(doc, ops, expectedYAML string) {
|
||||
patch, err := yamlpatch.DecodePatch([]byte(ops))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
actualBytes, err := patch.Apply([]byte(doc))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
var actualIface interface{}
|
||||
err = yaml.Unmarshal(actualBytes, &actualIface)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
var expectedIface interface{}
|
||||
err = yaml.Unmarshal([]byte(expectedYAML), &expectedIface)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(actualIface).To(Equal(expectedIface))
|
||||
},
|
||||
Entry("a path that begins with a composite key",
|
||||
`---
|
||||
- foo: bar
|
||||
`,
|
||||
`---
|
||||
- op: replace
|
||||
path: /foo=bar
|
||||
value:
|
||||
baz: quux
|
||||
`,
|
||||
`---
|
||||
- baz: quux
|
||||
`,
|
||||
),
|
||||
Entry("a path that begins with an array index and ends with a composite key",
|
||||
`---
|
||||
- waldo:
|
||||
- thud: boo
|
||||
- foo: bar
|
||||
- corge: grault
|
||||
`,
|
||||
`---
|
||||
- op: replace
|
||||
path: /0/foo=bar
|
||||
value:
|
||||
baz: quux
|
||||
`,
|
||||
`---
|
||||
- waldo:
|
||||
- thud: boo
|
||||
- baz: quux
|
||||
- corge: grault
|
||||
`,
|
||||
),
|
||||
Entry("a path that begins with an object key and ends with a composite key",
|
||||
`---
|
||||
waldo:
|
||||
- thud: boo
|
||||
- foo: bar
|
||||
corge: grault
|
||||
`,
|
||||
`---
|
||||
- op: replace
|
||||
path: /waldo/foo=bar
|
||||
value:
|
||||
baz: quux
|
||||
`,
|
||||
`---
|
||||
waldo:
|
||||
- thud: boo
|
||||
- baz: quux
|
||||
corge: grault
|
||||
`,
|
||||
),
|
||||
Entry("a path that doesn't end with a composite key",
|
||||
`---
|
||||
jobs:
|
||||
- name: upgrade-opsmgr
|
||||
serial: true
|
||||
plan:
|
||||
- get: pivnet-opsmgr
|
||||
- put: something-else
|
||||
`,
|
||||
`---
|
||||
- op: replace
|
||||
path: /jobs/name=upgrade-opsmgr/plan/1
|
||||
value:
|
||||
get: something-else
|
||||
`,
|
||||
`---
|
||||
jobs:
|
||||
- name: upgrade-opsmgr
|
||||
serial: true
|
||||
plan:
|
||||
- get: pivnet-opsmgr
|
||||
- get: something-else
|
||||
`,
|
||||
),
|
||||
Entry("removes multiple entries in a single op",
|
||||
`---
|
||||
foo:
|
||||
- bar: baz
|
||||
- waldo: fred
|
||||
qux:
|
||||
corge: grault
|
||||
thud:
|
||||
- waldo: fred
|
||||
- bar: baz
|
||||
`,
|
||||
`---
|
||||
- op: remove
|
||||
path: /waldo=fred
|
||||
`,
|
||||
`---
|
||||
foo:
|
||||
- bar: baz
|
||||
qux:
|
||||
corge: grault
|
||||
thud:
|
||||
- bar: baz
|
||||
`,
|
||||
),
|
||||
)
|
||||
|
||||
DescribeTable(
|
||||
"failure cases",
|
||||
func(doc, ops string) {
|
||||
patch, err := yamlpatch.DecodePatch([]byte(ops))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
_, err = patch.Apply([]byte(doc))
|
||||
Expect(err).To(HaveOccurred())
|
||||
},
|
||||
Entry("adding an element to an object with a bad pointer",
|
||||
`---
|
||||
foo: bar
|
||||
`,
|
||||
`---
|
||||
- op: add
|
||||
path: /baz/bat
|
||||
value: qux
|
||||
`,
|
||||
),
|
||||
Entry("removing an element from an object with a bad pointer",
|
||||
`---
|
||||
a:
|
||||
b:
|
||||
d: 1
|
||||
`,
|
||||
`---
|
||||
- op: remove
|
||||
path: /a/b/c
|
||||
`,
|
||||
),
|
||||
Entry("moving an element in an object with a bad pointer",
|
||||
`---
|
||||
a:
|
||||
b:
|
||||
d: 1
|
||||
`,
|
||||
`---
|
||||
- op: move
|
||||
from: /a/b/c
|
||||
path: /a/b/e
|
||||
`,
|
||||
),
|
||||
Entry("removing an element from an array with a bad pointer",
|
||||
`---
|
||||
a:
|
||||
b: [1]
|
||||
`,
|
||||
`---
|
||||
- op: remove
|
||||
path: /a/b/1
|
||||
`,
|
||||
),
|
||||
Entry("moving an element from an array with a bad pointer",
|
||||
`---
|
||||
a:
|
||||
b: [1]
|
||||
`,
|
||||
`---
|
||||
- op: move
|
||||
from: /a/b/1
|
||||
path: /a/b/2
|
||||
`,
|
||||
),
|
||||
Entry("an operation with an invalid pathz field",
|
||||
`---
|
||||
foo: bar
|
||||
`,
|
||||
`---
|
||||
- op: add
|
||||
pathz: /baz
|
||||
value: qux
|
||||
`,
|
||||
),
|
||||
Entry("an add operation with an empty path",
|
||||
`---
|
||||
foo: bar
|
||||
`,
|
||||
`---
|
||||
- op: add
|
||||
path: ''
|
||||
value: qux
|
||||
`,
|
||||
),
|
||||
Entry("a replace operation on an array with an invalid path",
|
||||
`---
|
||||
name:
|
||||
foo:
|
||||
bat
|
||||
qux:
|
||||
bum
|
||||
`,
|
||||
`---
|
||||
- op: replace
|
||||
path: /foo/2
|
||||
value: bum
|
||||
`,
|
||||
),
|
||||
)
|
||||
})
|
||||
|
||||
Describe("DecodePatch", func() {
|
||||
It("returns an empty patch when given nil", func() {
|
||||
patch, err := yamlpatch.DecodePatch(nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(patch).To(HaveLen(0))
|
||||
})
|
||||
|
||||
It("returns a patch with a single op when given a single op", func() {
|
||||
ops := []byte(
|
||||
`---
|
||||
- op: add
|
||||
path: /baz
|
||||
value: qux`)
|
||||
|
||||
patch, err := yamlpatch.DecodePatch(ops)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
var v interface{} = "qux"
|
||||
value := yamlpatch.NewNode(&v)
|
||||
Expect(patch).To(Equal(yamlpatch.Patch{
|
||||
{
|
||||
Op: "add",
|
||||
Path: "/baz",
|
||||
Value: value,
|
||||
},
|
||||
}))
|
||||
})
|
||||
})
|
||||
})
|
||||
109
vendor/github.com/krishicks/yaml-patch/pathfinder.go
generated
vendored
Normal file
109
vendor/github.com/krishicks/yaml-patch/pathfinder.go
generated
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
package yamlpatch
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// PathFinder can be used to find RFC6902-standard paths given non-standard
|
||||
// (key=value) pointer syntax
|
||||
type PathFinder struct {
|
||||
root Container
|
||||
}
|
||||
|
||||
// NewPathFinder takes an interface that represents a YAML document and returns
|
||||
// a new PathFinder
|
||||
func NewPathFinder(container Container) *PathFinder {
|
||||
return &PathFinder{
|
||||
root: container,
|
||||
}
|
||||
}
|
||||
|
||||
// Find expands the given path into all matching paths, returning the canonical
|
||||
// versions of those matching paths
|
||||
func (p *PathFinder) Find(path string) []string {
|
||||
parts := strings.Split(path, "/")
|
||||
|
||||
if parts[1] == "" {
|
||||
return []string{"/"}
|
||||
}
|
||||
|
||||
routes := map[string]Container{
|
||||
"": p.root,
|
||||
}
|
||||
|
||||
for _, part := range parts[1:] {
|
||||
routes = find(decodePatchKey(part), routes)
|
||||
}
|
||||
|
||||
var paths []string
|
||||
for k := range routes {
|
||||
paths = append(paths, k)
|
||||
}
|
||||
|
||||
return paths
|
||||
}
|
||||
|
||||
func find(part string, routes map[string]Container) map[string]Container {
|
||||
matches := map[string]Container{}
|
||||
|
||||
for prefix, container := range routes {
|
||||
if part == "-" {
|
||||
for k := range routes {
|
||||
matches[fmt.Sprintf("%s/-", k)] = routes[k]
|
||||
}
|
||||
return matches
|
||||
}
|
||||
|
||||
if kv := strings.Split(part, "="); len(kv) == 2 {
|
||||
if newMatches := findAll(prefix, kv[0], kv[1], container); len(newMatches) > 0 {
|
||||
matches = newMatches
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if node, err := container.Get(part); err == nil {
|
||||
path := fmt.Sprintf("%s/%s", prefix, part)
|
||||
if node == nil {
|
||||
matches[path] = container
|
||||
} else {
|
||||
matches[path] = node.Container()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return matches
|
||||
}
|
||||
|
||||
func findAll(prefix, findKey, findValue string, container Container) map[string]Container {
|
||||
if container == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if v, err := container.Get(findKey); err == nil && v != nil {
|
||||
if vs, ok := v.Value().(string); ok && vs == findValue {
|
||||
return map[string]Container{
|
||||
prefix: container,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
matches := map[string]Container{}
|
||||
|
||||
switch it := container.(type) {
|
||||
case *nodeMap:
|
||||
for k, v := range *it {
|
||||
for route, match := range findAll(fmt.Sprintf("%s/%s", prefix, k), findKey, findValue, v.Container()) {
|
||||
matches[route] = match
|
||||
}
|
||||
}
|
||||
case *nodeSlice:
|
||||
for i, v := range *it {
|
||||
for route, match := range findAll(fmt.Sprintf("%s/%d", prefix, i), findKey, findValue, v.Container()) {
|
||||
matches[route] = match
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return matches
|
||||
}
|
||||
74
vendor/github.com/krishicks/yaml-patch/pathfinder_test.go
generated
vendored
Normal file
74
vendor/github.com/krishicks/yaml-patch/pathfinder_test.go
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
package yamlpatch_test
|
||||
|
||||
import (
|
||||
yamlpatch "github.com/krishicks/yaml-patch"
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/extensions/table"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("Pathfinder", func() {
|
||||
var pathfinder *yamlpatch.PathFinder
|
||||
|
||||
BeforeEach(func() {
|
||||
var iface interface{}
|
||||
|
||||
bs := []byte(`
|
||||
jobs:
|
||||
- name: job1
|
||||
plan:
|
||||
- get: A
|
||||
args:
|
||||
- arg: arg1
|
||||
- arg: arg2
|
||||
bool: true
|
||||
- get: B
|
||||
- get: C/D
|
||||
|
||||
- name: job2
|
||||
plan:
|
||||
- aggregate:
|
||||
- get: C
|
||||
- get: A
|
||||
`)
|
||||
|
||||
err := yaml.Unmarshal(bs, &iface)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
container := yamlpatch.NewNode(&iface).Container()
|
||||
pathfinder = yamlpatch.NewPathFinder(container)
|
||||
})
|
||||
|
||||
Describe("Find", func() {
|
||||
DescribeTable(
|
||||
"should",
|
||||
func(path string, expected []string) {
|
||||
actual := pathfinder.Find(path)
|
||||
Expect(actual).To(HaveLen(len(expected)))
|
||||
for _, el := range expected {
|
||||
Expect(actual).To(ContainElement(el))
|
||||
}
|
||||
},
|
||||
Entry("return a route for the root object", "/", []string{"/"}),
|
||||
Entry("return a route for an object under the root", "/jobs", []string{"/jobs"}),
|
||||
Entry("return a route for an element within an object under the root", "/jobs/0", []string{"/jobs/0"}),
|
||||
Entry("return a route for an object within an element within an object under the root", "/jobs/0/plan", []string{"/jobs/0/plan"}),
|
||||
Entry("return a route for an object within an element within an object under the root", "/jobs/0/plan/1", []string{"/jobs/0/plan/1"}),
|
||||
Entry("return routes for multiple matches", "/jobs/get=A", []string{"/jobs/0/plan/0", "/jobs/1/plan/0/aggregate/1"}),
|
||||
Entry("return a route for a single submatch with help", "/jobs/get=A/args/arg=arg2", []string{"/jobs/0/plan/0/args/1"}),
|
||||
Entry("return a route for a single submatch with no help", "/jobs/get=A/arg=arg2", []string{"/jobs/0/plan/0/args/1"}),
|
||||
Entry("return a route for a single submatch with help using escape ordering", "/jobs/get=C~1D", []string{"/jobs/0/plan/2"}),
|
||||
Entry("return a route when given a pointer with a leaf that does not exist", "/jobs/name=job1/nonexistent", []string{"/jobs/0/nonexistent"}),
|
||||
Entry("return a route when given a pointer with an array thingy", "/jobs/name=job1/plan/-", []string{"/jobs/0/plan/-"}),
|
||||
)
|
||||
DescribeTable(
|
||||
"should not",
|
||||
func(path string) {
|
||||
Expect(pathfinder.Find(path)).To(BeNil())
|
||||
},
|
||||
Entry("return any routes when given a bad index", "/jobs/2"),
|
||||
Entry("return any routes when given a bad index", "/jobs/-1"),
|
||||
)
|
||||
})
|
||||
})
|
||||
51
vendor/github.com/krishicks/yaml-patch/placeholder_wrapper.go
generated
vendored
Normal file
51
vendor/github.com/krishicks/yaml-patch/placeholder_wrapper.go
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
package yamlpatch
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// PlaceholderWrapper can be used to wrap placeholders that make YAML invalid
|
||||
// in single quotes to make otherwise valid YAML
|
||||
type PlaceholderWrapper struct {
|
||||
LeftSide string
|
||||
RightSide string
|
||||
unwrappedRegex *regexp.Regexp
|
||||
wrappedRegex *regexp.Regexp
|
||||
}
|
||||
|
||||
// NewPlaceholderWrapper returns a new PlaceholderWrapper which knows how to
|
||||
// wrap and unwrap the provided left and right sides of a placeholder, e.g. {{
|
||||
// and }}
|
||||
func NewPlaceholderWrapper(left, right string) *PlaceholderWrapper {
|
||||
escapedLeft := regexp.QuoteMeta(left)
|
||||
escapedRight := regexp.QuoteMeta(right)
|
||||
unwrappedRegex := regexp.MustCompile(`\s` + escapedLeft + `([^` + escapedRight + `]+)` + escapedRight)
|
||||
wrappedRegex := regexp.MustCompile(`\s'` + escapedLeft + `([^` + escapedRight + `]+)` + escapedRight + `'`)
|
||||
|
||||
return &PlaceholderWrapper{
|
||||
LeftSide: left,
|
||||
RightSide: right,
|
||||
unwrappedRegex: unwrappedRegex,
|
||||
wrappedRegex: wrappedRegex,
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap the placeholder in single quotes to make it valid YAML
|
||||
func (w *PlaceholderWrapper) Wrap(input []byte) []byte {
|
||||
if !w.unwrappedRegex.Match(input) {
|
||||
return input
|
||||
}
|
||||
|
||||
return w.unwrappedRegex.ReplaceAll(input, []byte(fmt.Sprintf(` '%s$1%s'`, w.LeftSide, w.RightSide)))
|
||||
}
|
||||
|
||||
// Unwrap the single quotes from the placeholder to make it invalid YAML
|
||||
// (again)
|
||||
func (w *PlaceholderWrapper) Unwrap(input []byte) []byte {
|
||||
if !w.wrappedRegex.Match(input) {
|
||||
return input
|
||||
}
|
||||
|
||||
return w.wrappedRegex.ReplaceAll(input, []byte(fmt.Sprintf(` %s$1%s`, w.LeftSide, w.RightSide)))
|
||||
}
|
||||
93
vendor/github.com/krishicks/yaml-patch/placeholder_wrapper_test.go
generated
vendored
Normal file
93
vendor/github.com/krishicks/yaml-patch/placeholder_wrapper_test.go
generated
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
package yamlpatch_test
|
||||
|
||||
import (
|
||||
yamlpatch "github.com/krishicks/yaml-patch"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("PlaceholderWrapper", func() {
|
||||
var placeholderWrapper *yamlpatch.PlaceholderWrapper
|
||||
|
||||
BeforeEach(func() {
|
||||
placeholderWrapper = yamlpatch.NewPlaceholderWrapper("{{", "}}")
|
||||
})
|
||||
|
||||
Describe("Wrap", func() {
|
||||
It("returns the original content", func() {
|
||||
input := []byte(`content without any placeholders`)
|
||||
actual := placeholderWrapper.Wrap(input)
|
||||
Expect(actual).To(Equal(input))
|
||||
})
|
||||
|
||||
It("returns the content with the placeholder wrapped when the content contains a placeholder", func() {
|
||||
input := []byte(`content with a {{placeholder}}`)
|
||||
expected := []byte(`content with a '{{placeholder}}'`)
|
||||
actual := placeholderWrapper.Wrap(input)
|
||||
Expect(actual).To(Equal(expected))
|
||||
})
|
||||
|
||||
It("returns the content with the placeholder wrapped when the content contains a line with only a placeholder", func() {
|
||||
input := []byte(`
|
||||
content: |
|
||||
{{placeholder}}
|
||||
`)
|
||||
expected := []byte(`
|
||||
content: |
|
||||
'{{placeholder}}'
|
||||
`)
|
||||
actual := placeholderWrapper.Wrap(input)
|
||||
Expect(actual).To(Equal(expected))
|
||||
})
|
||||
|
||||
It("returns the original content when the content contains an already-wrapped placeholder", func() {
|
||||
input := []byte(`content with a wrapped '{{placeholder}}'`)
|
||||
actual := placeholderWrapper.Wrap(input)
|
||||
Expect(string(actual)).To(Equal(string(input)))
|
||||
})
|
||||
|
||||
It("supports alternate placeholders", func() {
|
||||
placeholderWrapper = yamlpatch.NewPlaceholderWrapper("((", "))")
|
||||
input := []byte(`content with an ((alternate-placeholder))`)
|
||||
expected := []byte(`content with an '((alternate-placeholder))'`)
|
||||
actual := placeholderWrapper.Wrap(input)
|
||||
Expect(actual).To(Equal(expected))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Unwrap", func() {
|
||||
It("returns the original content", func() {
|
||||
input := []byte(`content without any placeholders`)
|
||||
actual := placeholderWrapper.Unwrap(input)
|
||||
Expect(string(actual)).To(Equal(string(input)))
|
||||
})
|
||||
|
||||
It("returns the content with the placeholder unwrapped when the content contains a wrapped placeholder", func() {
|
||||
input := []byte(`content with a '{{placeholder}}'`)
|
||||
expected := []byte(`content with a {{placeholder}}`)
|
||||
actual := placeholderWrapper.Unwrap(input)
|
||||
Expect(string(actual)).To(Equal(string(expected)))
|
||||
})
|
||||
|
||||
It("returns the content with the placeholder unwrapped when the content contains a line with only a wrapped placeholder", func() {
|
||||
input := []byte(`
|
||||
content: |
|
||||
'{{placeholder}}'
|
||||
`)
|
||||
expected := []byte(`
|
||||
content: |
|
||||
{{placeholder}}
|
||||
`)
|
||||
actual := placeholderWrapper.Unwrap(input)
|
||||
Expect(string(actual)).To(Equal(string(expected)))
|
||||
})
|
||||
|
||||
It("supports alternate placeholders", func() {
|
||||
placeholderWrapper = yamlpatch.NewPlaceholderWrapper("((", "))")
|
||||
input := []byte(`content with an '((alternate-placeholder))'`)
|
||||
expected := []byte(`content with an ((alternate-placeholder))`)
|
||||
actual := placeholderWrapper.Unwrap(input)
|
||||
Expect(actual).To(Equal(expected))
|
||||
})
|
||||
})
|
||||
})
|
||||
13
vendor/github.com/krishicks/yaml-patch/yaml_patch_suite_test.go
generated
vendored
Normal file
13
vendor/github.com/krishicks/yaml-patch/yaml_patch_suite_test.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
package yamlpatch_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestYamlPatch(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "YamlPatch Suite")
|
||||
}
|
||||
Reference in New Issue
Block a user