mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-10 16:42:51 +00:00
Introduce ResId and ResMap.
This commit is contained in:
@@ -25,6 +25,7 @@ import (
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||
interror "github.com/kubernetes-sigs/kustomize/pkg/internal/error"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/loader"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resmap"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resource"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/transformers"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
||||
@@ -32,14 +33,14 @@ import (
|
||||
|
||||
type Application interface {
|
||||
// Resources computes and returns the resources for the app.
|
||||
Resources() (resource.ResourceCollection, error)
|
||||
Resources() (resmap.ResMap, error)
|
||||
// SemiResources computes and returns the resources without name hash and name reference for the app
|
||||
SemiResources() (resource.ResourceCollection, error)
|
||||
SemiResources() (resmap.ResMap, error)
|
||||
// RawResources computes and returns the raw resources from the kustomization file.
|
||||
// It contains resources from
|
||||
// 1) untransformed resources from current kustomization file
|
||||
// 2) transformed resources from sub packages
|
||||
RawResources() (resource.ResourceCollection, error)
|
||||
RawResources() (resmap.ResMap, error)
|
||||
}
|
||||
|
||||
var _ Application = &applicationImpl{}
|
||||
@@ -68,7 +69,7 @@ func New(loader loader.Loader) (Application, error) {
|
||||
// Resources computes and returns the resources from the kustomization file.
|
||||
// The namehashing for configmap/secrets and resolving name reference is only done
|
||||
// in the most top overlay once at the end of getting resources.
|
||||
func (a *applicationImpl) Resources() (resource.ResourceCollection, error) {
|
||||
func (a *applicationImpl) Resources() (resmap.ResMap, error) {
|
||||
res, err := a.SemiResources()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -85,32 +86,32 @@ func (a *applicationImpl) Resources() (resource.ResourceCollection, error) {
|
||||
}
|
||||
|
||||
// SemiResources computes and returns the resources without name hash and name reference for the app
|
||||
func (a *applicationImpl) SemiResources() (resource.ResourceCollection, error) {
|
||||
func (a *applicationImpl) SemiResources() (resmap.ResMap, error) {
|
||||
errs := &interror.KustomizationErrors{}
|
||||
raw, err := a.rawResources()
|
||||
if err != nil {
|
||||
errs.Append(err)
|
||||
}
|
||||
|
||||
cms, err := resource.NewFromConfigMaps(a.loader, a.kustomization.ConfigMapGenerator)
|
||||
cms, err := resmap.NewResMapFromConfigMapArgs(a.loader, a.kustomization.ConfigMapGenerator)
|
||||
if err != nil {
|
||||
errs.Append(err)
|
||||
}
|
||||
secrets, err := resource.NewFromSecretGenerators(a.loader.Root(), a.kustomization.SecretGenerator)
|
||||
secrets, err := resmap.NewResMapFromSecretArgs(a.loader.Root(), a.kustomization.SecretGenerator)
|
||||
if err != nil {
|
||||
errs.Append(err)
|
||||
}
|
||||
res, err := resource.Merge(cms, secrets)
|
||||
res, err := resmap.Merge(cms, secrets)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
allRes, err := resource.MergeWithOverride(raw, res)
|
||||
allRes, err := resmap.MergeWithOverride(raw, res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
patches, err := resource.NewFromPatches(a.loader, a.kustomization.Patches)
|
||||
patches, err := resmap.NewResourceSliceFromPatches(a.loader, a.kustomization.Patches)
|
||||
if err != nil {
|
||||
errs.Append(err)
|
||||
}
|
||||
@@ -134,7 +135,7 @@ func (a *applicationImpl) SemiResources() (resource.ResourceCollection, error) {
|
||||
// RawResources computes and returns the raw resources from the kustomization file.
|
||||
// The namehashing for configmap/secrets and resolving name reference is only done
|
||||
// in the most top overlay once at the end of getting resources.
|
||||
func (a *applicationImpl) RawResources() (resource.ResourceCollection, error) {
|
||||
func (a *applicationImpl) RawResources() (resmap.ResMap, error) {
|
||||
res, err := a.rawResources()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -150,9 +151,9 @@ func (a *applicationImpl) RawResources() (resource.ResourceCollection, error) {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (a *applicationImpl) rawResources() (resource.ResourceCollection, error) {
|
||||
func (a *applicationImpl) rawResources() (resmap.ResMap, error) {
|
||||
subAppResources, errs := a.subAppResources()
|
||||
resources, err := resource.NewFromResources(a.loader, a.kustomization.Resources)
|
||||
resources, err := resmap.NewResMapFromFiles(a.loader, a.kustomization.Resources)
|
||||
if err != nil {
|
||||
errs.Append(err)
|
||||
}
|
||||
@@ -161,11 +162,11 @@ func (a *applicationImpl) rawResources() (resource.ResourceCollection, error) {
|
||||
return nil, errs
|
||||
}
|
||||
|
||||
return resource.Merge(resources, subAppResources)
|
||||
return resmap.Merge(resources, subAppResources)
|
||||
}
|
||||
|
||||
func (a *applicationImpl) subAppResources() (resource.ResourceCollection, *interror.KustomizationErrors) {
|
||||
sliceOfSubAppResources := []resource.ResourceCollection{}
|
||||
func (a *applicationImpl) subAppResources() (resmap.ResMap, *interror.KustomizationErrors) {
|
||||
sliceOfSubAppResources := []resmap.ResMap{}
|
||||
errs := &interror.KustomizationErrors{}
|
||||
for _, pkgPath := range a.kustomization.Bases {
|
||||
subloader, err := a.loader.New(pkgPath)
|
||||
@@ -186,7 +187,7 @@ func (a *applicationImpl) subAppResources() (resource.ResourceCollection, *inter
|
||||
}
|
||||
sliceOfSubAppResources = append(sliceOfSubAppResources, subAppResources)
|
||||
}
|
||||
allResources, err := resource.Merge(sliceOfSubAppResources...)
|
||||
allResources, err := resmap.Merge(sliceOfSubAppResources...)
|
||||
if err != nil {
|
||||
errs.Append(err)
|
||||
}
|
||||
|
||||
@@ -18,15 +18,14 @@ package app
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/loader"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/loader/loadertest"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resmap"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resource"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
@@ -71,13 +70,14 @@ metadata:
|
||||
return loader
|
||||
}
|
||||
|
||||
var deploy = schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"}
|
||||
var cmap = schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"}
|
||||
var secret = schema.GroupVersionKind{Version: "v1", Kind: "Secret"}
|
||||
|
||||
func TestResources(t *testing.T) {
|
||||
expected := resource.ResourceCollection{
|
||||
types.GroupVersionKindName{
|
||||
GVK: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"},
|
||||
Name: "dply1",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
expected := resmap.ResMap{
|
||||
resource.NewResId(deploy, "dply1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
@@ -108,13 +108,9 @@ func TestResources(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
types.GroupVersionKindName{
|
||||
GVK: schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"},
|
||||
Name: "literalConfigMap",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
}),
|
||||
resource.NewResId(cmap, "literalConfigMap"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
@@ -133,13 +129,9 @@ func TestResources(t *testing.T) {
|
||||
"DB_PASSWORD": "somepw",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
types.GroupVersionKindName{
|
||||
GVK: schema.GroupVersionKind{Version: "v1", Kind: "Secret"},
|
||||
Name: "secret",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
}),
|
||||
resource.NewResId(secret, "secret"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Secret",
|
||||
@@ -159,8 +151,7 @@ func TestResources(t *testing.T) {
|
||||
"DB_PASSWORD": base64.StdEncoding.EncodeToString([]byte("somepw")),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
l := setupTest(t)
|
||||
app, err := New(l)
|
||||
@@ -173,18 +164,15 @@ func TestResources(t *testing.T) {
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
err = compareMap(actual, expected)
|
||||
err = expected.ErrorIfNotEqual(actual)
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRawResources(t *testing.T) {
|
||||
expected := resource.ResourceCollection{
|
||||
types.GroupVersionKindName{
|
||||
GVK: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"},
|
||||
Name: "dply1",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
expected := resmap.ResMap{
|
||||
resource.NewResId(deploy, "dply1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
@@ -192,8 +180,7 @@ func TestRawResources(t *testing.T) {
|
||||
"name": "dply1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
l := setupTest(t)
|
||||
app, err := New(l)
|
||||
@@ -205,31 +192,7 @@ func TestRawResources(t *testing.T) {
|
||||
t.Fatalf("Unexpected error %v", err)
|
||||
}
|
||||
|
||||
if err := compareMap(actual, expected); err != nil {
|
||||
if err := expected.ErrorIfNotEqual(actual); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func compareMap(m1, m2 resource.ResourceCollection) error {
|
||||
if len(m1) != len(m2) {
|
||||
keySet1 := []types.GroupVersionKindName{}
|
||||
keySet2 := []types.GroupVersionKindName{}
|
||||
for GVKn := range m1 {
|
||||
keySet1 = append(keySet1, GVKn)
|
||||
}
|
||||
for GVKn := range m1 {
|
||||
keySet2 = append(keySet2, GVKn)
|
||||
}
|
||||
return fmt.Errorf("maps has different number of entries: %#v doesn't equals %#v", keySet1, keySet2)
|
||||
}
|
||||
for GVKn, obj1 := range m1 {
|
||||
obj2, found := m2[GVKn]
|
||||
if !found {
|
||||
return fmt.Errorf("%#v doesn't exist in %#v", GVKn, m2)
|
||||
}
|
||||
if !reflect.DeepEqual(obj1, obj2) {
|
||||
return fmt.Errorf("%#v doesn't match %#v", obj1, obj2)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ import (
|
||||
|
||||
cutil "github.com/kubernetes-sigs/kustomize/pkg/configmapandsecret/util"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/hash"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resource"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
@@ -122,51 +121,6 @@ func makeSecret(secret types.SecretArgs, path string) (*corev1.Secret, error) {
|
||||
return corev1secret, nil
|
||||
}
|
||||
|
||||
func populateMap(m resource.ResourceCollection, obj *unstructured.Unstructured, newName string) error {
|
||||
oldName := obj.GetName()
|
||||
gvk := obj.GroupVersionKind()
|
||||
gvkn := types.GroupVersionKindName{GVK: gvk, Name: oldName}
|
||||
|
||||
if _, found := m[gvkn]; found {
|
||||
return fmt.Errorf("The <name: %q, GroupVersionKind: %v> already exists in the map", oldName, gvk)
|
||||
}
|
||||
obj.SetName(newName)
|
||||
m[gvkn] = &resource.Resource{Data: obj}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MakeConfigMapsResourceCollection returns a map of <GVK, oldName> -> unstructured object.
|
||||
func MakeConfigMapsResourceCollection(maps []types.ConfigMapArgs) (resource.ResourceCollection, error) {
|
||||
m := resource.ResourceCollection{}
|
||||
for _, cm := range maps {
|
||||
unstructuredConfigMap, nameWithHash, err := MakeConfigmapAndGenerateName(cm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = populateMap(m, unstructuredConfigMap, nameWithHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// MakeSecretsResourceCollection returns a map of <GVK, oldName> -> unstructured object.
|
||||
func MakeSecretsResourceCollection(secrets []types.SecretArgs, path string) (resource.ResourceCollection, error) {
|
||||
m := resource.ResourceCollection{}
|
||||
for _, secret := range secrets {
|
||||
unstructuredSecret, nameWithHash, err := MakeSecretAndGenerateName(secret, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = populateMap(m, unstructuredSecret, nameWithHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func createSecretKey(wd string, command string) ([]byte, error) {
|
||||
fi, err := os.Stat(wd)
|
||||
if err != nil || !fi.IsDir() {
|
||||
|
||||
@@ -5,12 +5,11 @@ import (
|
||||
|
||||
"io"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resource"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resmap"
|
||||
)
|
||||
|
||||
// RunDiff runs system diff program to compare two ResourceCollections.
|
||||
func RunDiff(raw, transformed resource.ResourceCollection,
|
||||
out, errOut io.Writer) error {
|
||||
// RunDiff runs system diff program to compare two Maps.
|
||||
func RunDiff(raw, transformed resmap.ResMap, out, errOut io.Writer) error {
|
||||
transformedDir, err := writeYamlToNewDir(transformed, "transformed")
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -26,10 +25,10 @@ func RunDiff(raw, transformed resource.ResourceCollection,
|
||||
return newProgram(out, errOut).run(noopDir.name(), transformedDir.name())
|
||||
}
|
||||
|
||||
// writeYamlToNewDir writes each obj in ResourceCollection to a file in a new directory.
|
||||
// writeYamlToNewDir writes each obj in ResMap to a file in a new directory.
|
||||
// The directory's name will begin with the given prefix.
|
||||
// Each file is named with GroupVersionKindName.
|
||||
func writeYamlToNewDir(in resource.ResourceCollection, prefix string) (*directory, error) {
|
||||
func writeYamlToNewDir(in resmap.ResMap, prefix string) (*directory, error) {
|
||||
dir, err := newDirectory(prefix)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -40,7 +39,7 @@ func writeYamlToNewDir(in resource.ResourceCollection, prefix string) (*director
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = print(obj.Data, f)
|
||||
err = print(obj.Unstruct(), f)
|
||||
f.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package resource
|
||||
package resmap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -22,22 +22,23 @@ import (
|
||||
|
||||
cutil "github.com/kubernetes-sigs/kustomize/pkg/configmapandsecret/util"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/loader"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resource"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
)
|
||||
|
||||
func newFromConfigMap(l loader.Loader, cm types.ConfigMapArgs) (*Resource, error) {
|
||||
func newResourceFromConfigMap(l loader.Loader, cm types.ConfigMapArgs) (*resource.Resource, error) {
|
||||
corev1CM, err := makeConfigMap(l, cm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := objectToUnstructured(corev1CM)
|
||||
data, err := newUnstructuredFromObject(corev1CM)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Resource{Data: data, Behavior: cm.Behavior}, nil
|
||||
return resource.NewResource(data, cm.Behavior), nil
|
||||
}
|
||||
|
||||
func makeConfigMap(l loader.Loader, cm types.ConfigMapArgs) (*corev1.ConfigMap, error) {
|
||||
@@ -130,15 +131,15 @@ func addKV(m map[string]string, kv kvPair) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewFromConfigMaps returns a Resource slice given a configmap metadata slice from kustomization file.
|
||||
func NewFromConfigMaps(loader loader.Loader, cmList []types.ConfigMapArgs) (ResourceCollection, error) {
|
||||
allResources := []*Resource{}
|
||||
// NewResMapFromConfigMapArgs returns a Resource slice given a configmap metadata slice from kustomization file.
|
||||
func NewResMapFromConfigMapArgs(loader loader.Loader, cmList []types.ConfigMapArgs) (ResMap, error) {
|
||||
allResources := []*resource.Resource{}
|
||||
for _, cm := range cmList {
|
||||
res, err := newFromConfigMap(loader, cm)
|
||||
res, err := newResourceFromConfigMap(loader, cm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
allResources = append(allResources, res)
|
||||
}
|
||||
return resourceCollectionFromResources(allResources)
|
||||
return newResMapFromResourceSlice(allResources)
|
||||
}
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package resource_test
|
||||
package resmap
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
@@ -27,13 +27,15 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
var cmap = schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"}
|
||||
|
||||
func TestNewFromConfigMaps(t *testing.T) {
|
||||
type testCase struct {
|
||||
description string
|
||||
input []types.ConfigMapArgs
|
||||
filepath string
|
||||
content string
|
||||
expected resource.ResourceCollection
|
||||
expected ResMap
|
||||
}
|
||||
|
||||
l := loadertest.NewFakeLoader("/home/seans/project/")
|
||||
@@ -50,12 +52,9 @@ func TestNewFromConfigMaps(t *testing.T) {
|
||||
},
|
||||
filepath: "/home/seans/project/app.env",
|
||||
content: "DB_USERNAME=admin\nDB_PASSWORD=somepw",
|
||||
expected: resource.ResourceCollection{
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"},
|
||||
Name: "envConfigMap",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
expected: ResMap{
|
||||
resource.NewResId(cmap, "envConfigMap"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
@@ -68,8 +67,7 @@ func TestNewFromConfigMaps(t *testing.T) {
|
||||
"DB_PASSWORD": "somepw",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -83,12 +81,9 @@ func TestNewFromConfigMaps(t *testing.T) {
|
||||
},
|
||||
filepath: "/home/seans/project/app-init.ini",
|
||||
content: "FOO=bar\nBAR=baz\n",
|
||||
expected: resource.ResourceCollection{
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"},
|
||||
Name: "fileConfigMap",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
expected: ResMap{
|
||||
resource.NewResId(cmap, "fileConfigMap"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
@@ -102,8 +97,7 @@ BAR=baz
|
||||
`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -116,12 +110,9 @@ BAR=baz
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: resource.ResourceCollection{
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"},
|
||||
Name: "literalConfigMap",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
expected: ResMap{
|
||||
resource.NewResId(cmap, "literalConfigMap"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
@@ -134,8 +125,7 @@ BAR=baz
|
||||
"b": "y",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
// TODO: add testcase for data coming from multiple sources like
|
||||
@@ -147,7 +137,7 @@ BAR=baz
|
||||
if ferr := l.AddFile(tc.filepath, []byte(tc.content)); ferr != nil {
|
||||
t.Fatalf("Error adding fake file: %v\n", ferr)
|
||||
}
|
||||
r, err := resource.NewFromConfigMaps(l, tc.input)
|
||||
r, err := NewResMapFromConfigMapArgs(l, tc.input)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
@@ -14,22 +14,24 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package types
|
||||
package resmap
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resource"
|
||||
)
|
||||
|
||||
// ByGVKN implements the sort interface.
|
||||
type ByGVKN []GroupVersionKindName
|
||||
// IdSlice implements the sort interface.
|
||||
type IdSlice []resource.ResId
|
||||
|
||||
var _ sort.Interface = ByGVKN{}
|
||||
var _ sort.Interface = IdSlice{}
|
||||
|
||||
func (a ByGVKN) Len() int { return len(a) }
|
||||
func (a ByGVKN) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a ByGVKN) Less(i, j int) bool {
|
||||
if a[i].GVK.String() != a[j].GVK.String() {
|
||||
return a[i].GVK.String() < a[j].GVK.String()
|
||||
func (a IdSlice) Len() int { return len(a) }
|
||||
func (a IdSlice) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a IdSlice) Less(i, j int) bool {
|
||||
if a[i].Gvk().String() != a[j].Gvk().String() {
|
||||
return a[i].Gvk().String() < a[j].Gvk().String()
|
||||
}
|
||||
return a[i].Name < a[j].Name
|
||||
return a[i].Name() < a[j].Name()
|
||||
}
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package resource
|
||||
package resmap
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
@@ -13,7 +13,7 @@ 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 resource
|
||||
package resmap
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
247
pkg/resmap/resmap.go
Normal file
247
pkg/resmap/resmap.go
Normal file
@@ -0,0 +1,247 @@
|
||||
package resmap
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"sort"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/golang/glog"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/loader"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resource"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
k8syaml "k8s.io/apimachinery/pkg/util/yaml"
|
||||
)
|
||||
|
||||
// ResMap is a map from ResId to Resource
|
||||
type ResMap map[resource.ResId]*resource.Resource
|
||||
|
||||
// EncodeAsYaml encodes a ResMap to YAML; encoded objects separated by `---`.
|
||||
func (m ResMap) EncodeAsYaml() ([]byte, error) {
|
||||
ids := []resource.ResId{}
|
||||
for gvkn := range m {
|
||||
ids = append(ids, gvkn)
|
||||
}
|
||||
sort.Sort(IdSlice(ids))
|
||||
|
||||
firstObj := true
|
||||
var b []byte
|
||||
buf := bytes.NewBuffer(b)
|
||||
for _, id := range ids {
|
||||
obj := m[id].Unstruct()
|
||||
out, err := yaml.Marshal(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if firstObj {
|
||||
firstObj = false
|
||||
} else {
|
||||
_, err = buf.WriteString("---\n")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
_, err = buf.Write(out)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (m1 ResMap) ErrorIfNotEqual(m2 ResMap) error {
|
||||
if len(m1) != len(m2) {
|
||||
keySet1 := []resource.ResId{}
|
||||
keySet2 := []resource.ResId{}
|
||||
for id := range m1 {
|
||||
keySet1 = append(keySet1, id)
|
||||
}
|
||||
for id := range m2 {
|
||||
keySet2 = append(keySet2, id)
|
||||
}
|
||||
return fmt.Errorf("maps has different number of entries: %#v doesn't equals %#v", keySet1, keySet2)
|
||||
}
|
||||
for id, obj1 := range m1 {
|
||||
obj2, found := m2[id]
|
||||
if !found {
|
||||
return fmt.Errorf("%#v doesn't exist in %#v", id, m2)
|
||||
}
|
||||
if !reflect.DeepEqual(obj1.Unstruct(), obj2.Unstruct()) {
|
||||
return fmt.Errorf("%#v doesn't match %#v", obj1.Unstruct(), obj2.Unstruct())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m ResMap) insert(newName string, obj *unstructured.Unstructured) error {
|
||||
oldName := obj.GetName()
|
||||
gvk := obj.GroupVersionKind()
|
||||
id := resource.NewResId(gvk, oldName)
|
||||
|
||||
if _, found := m[id]; found {
|
||||
return fmt.Errorf("The <name: %q, GroupVersionKind: %v> already exists in the map", oldName, gvk)
|
||||
}
|
||||
obj.SetName(newName)
|
||||
m[id] = resource.NewBehaviorlessResource(obj)
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewResourceSliceFromPatches returns a slice of Resources given a patch path slice from kustomization file.
|
||||
func NewResourceSliceFromPatches(
|
||||
loader loader.Loader, paths []string) ([]*resource.Resource, error) {
|
||||
result := []*resource.Resource{}
|
||||
for _, path := range paths {
|
||||
content, err := loader.Load(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := newResourceSliceFromBytes(content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result, res...)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// NewResMapFromFiles returns a ResMap given a resource path slice.
|
||||
func NewResMapFromFiles(loader loader.Loader, paths []string) (ResMap, error) {
|
||||
result := []ResMap{}
|
||||
for _, path := range paths {
|
||||
content, err := loader.Load(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := newResMapFromBytes(content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result, res)
|
||||
}
|
||||
return Merge(result...)
|
||||
}
|
||||
|
||||
// newResMapFromBytes decodes a list of objects in byte array format.
|
||||
func newResMapFromBytes(b []byte) (ResMap, error) {
|
||||
resources, err := newResourceSliceFromBytes(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := ResMap{}
|
||||
for _, res := range resources {
|
||||
gvkn := res.Id()
|
||||
if _, found := result[gvkn]; found {
|
||||
return result, fmt.Errorf("GroupVersionKindName: %#v already exists b the map", gvkn)
|
||||
}
|
||||
result[gvkn] = res
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func newResMapFromResourceSlice(resources []*resource.Resource) (ResMap, error) {
|
||||
result := ResMap{}
|
||||
for _, res := range resources {
|
||||
gvkn := res.Id()
|
||||
if _, found := result[gvkn]; found {
|
||||
return nil, fmt.Errorf("duplicated %#v is not allowed", gvkn)
|
||||
}
|
||||
result[gvkn] = res
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func newResourceSliceFromBytes(in []byte) ([]*resource.Resource, error) {
|
||||
decoder := k8syaml.NewYAMLOrJSONDecoder(bytes.NewReader(in), 1024)
|
||||
result := []*resource.Resource{}
|
||||
|
||||
var err error
|
||||
for {
|
||||
var out unstructured.Unstructured
|
||||
err = decoder.Decode(&out)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
result = append(result, resource.NewBehaviorlessResource(&out))
|
||||
}
|
||||
if err != io.EOF {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Merge combines many maps to one.
|
||||
func Merge(maps ...ResMap) (ResMap, error) {
|
||||
result := ResMap{}
|
||||
for _, m := range maps {
|
||||
for gvkn, obj := range m {
|
||||
if _, found := result[gvkn]; found {
|
||||
return nil, fmt.Errorf("there is already an entry: %q", gvkn)
|
||||
}
|
||||
result[gvkn] = obj
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
const behaviorCreate = "create"
|
||||
const behaviorReplace = "replace"
|
||||
const behaviorMerge = "merge"
|
||||
|
||||
// MergeWithOverride merges the entries in the ResMap slice with Override.
|
||||
// If there is already an entry with the same Id , different actions are performed
|
||||
// according to value of behavior field:
|
||||
// 'create': create a new one;
|
||||
// 'replace': replace the data only; keep the labels and annotations
|
||||
// 'merge': merge the data; keep the labels and annotations
|
||||
func MergeWithOverride(maps ...ResMap) (ResMap, error) {
|
||||
result := ResMap{}
|
||||
for _, m := range maps {
|
||||
for gvkn, resource := range m {
|
||||
if _, found := result[gvkn]; found {
|
||||
switch resource.Behavior() {
|
||||
case "", behaviorCreate:
|
||||
return nil, fmt.Errorf("Create an existing gvkn %#v is not allowed", gvkn)
|
||||
case behaviorReplace:
|
||||
glog.V(4).Infof("Replace object %v by %v", result[gvkn].Unstruct().Object, resource.Unstruct().Object)
|
||||
resource.Replace(result[gvkn])
|
||||
result[gvkn] = resource
|
||||
case behaviorMerge:
|
||||
glog.V(4).Infof("Merge object %v with %v", result[gvkn].Unstruct().Object, resource.Unstruct().Object)
|
||||
resource.Merge(result[gvkn])
|
||||
result[gvkn] = resource
|
||||
glog.V(4).Infof("The merged object is %v", result[gvkn].Unstruct().Object)
|
||||
default:
|
||||
return nil, fmt.Errorf("The behavior of %#v must be one of merge and replace since it already exists in the base", gvkn)
|
||||
}
|
||||
} else {
|
||||
switch resource.Behavior() {
|
||||
case "", behaviorCreate:
|
||||
result[gvkn] = resource
|
||||
case behaviorMerge, behaviorReplace:
|
||||
return nil, fmt.Errorf("No merge or replace is allowed for non existing gvkn %#v", gvkn)
|
||||
default:
|
||||
return nil, fmt.Errorf("The behavior of %#v must be create since it doesn't exist", gvkn)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func newUnstructuredFromObject(in runtime.Object) (*unstructured.Unstructured, error) {
|
||||
marshaled, err := json.Marshal(in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var out unstructured.Unstructured
|
||||
err = out.UnmarshalJSON(marshaled)
|
||||
return &out, err
|
||||
}
|
||||
222
pkg/resmap/resmap_test.go
Normal file
222
pkg/resmap/resmap_test.go
Normal file
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
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 resmap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/loader/loadertest"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resource"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
var deploy = schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"}
|
||||
var statefulset = schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "StatefulSet"}
|
||||
|
||||
func TestEncodeAsYaml(t *testing.T) {
|
||||
encoded := []byte(`apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: cm1
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: cm2
|
||||
`)
|
||||
input := ResMap{
|
||||
resource.NewResId(cmap, "cm1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "cm1",
|
||||
},
|
||||
},
|
||||
}),
|
||||
resource.NewResId(cmap, "cm2"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "cm2",
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
out, err := input.EncodeAsYaml()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(out, encoded) {
|
||||
t.Fatalf("%s doesn't match expected %s", out, encoded)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewMapFromFiles(t *testing.T) {
|
||||
|
||||
resourceStr := `apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dply1
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dply2
|
||||
`
|
||||
|
||||
l := loadertest.NewFakeLoader("/home/seans/project")
|
||||
if ferr := l.AddFile("/home/seans/project/deployment.yaml", []byte(resourceStr)); ferr != nil {
|
||||
t.Fatalf("Error adding fake file: %v\n", ferr)
|
||||
}
|
||||
expected := ResMap{resource.NewResId(deploy, "dply1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "dply1",
|
||||
},
|
||||
},
|
||||
}),
|
||||
resource.NewResId(deploy, "dply2"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "dply2",
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
||||
m, _ := NewResMapFromFiles(l, []string{"/home/seans/project/deployment.yaml"})
|
||||
if len(m) != 2 {
|
||||
t.Fatalf("%#v should contain 2 appResource, but got %d", m, len(m))
|
||||
}
|
||||
|
||||
if err := expected.ErrorIfNotEqual(m); err != nil {
|
||||
t.Fatalf("actual doesn't match expected: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewMapFromBytes(t *testing.T) {
|
||||
encoded := []byte(`apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: cm1
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: cm2
|
||||
`)
|
||||
expected := ResMap{
|
||||
resource.NewResId(cmap, "cm1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "cm1",
|
||||
},
|
||||
},
|
||||
}),
|
||||
resource.NewResId(cmap, "cm2"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "cm2",
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
m, err := newResMapFromBytes(encoded)
|
||||
fmt.Printf("%v\n", m)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(m, expected) {
|
||||
t.Fatalf("%#v doesn't match expected %#v", m, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMerge(t *testing.T) {
|
||||
input1 := ResMap{
|
||||
resource.NewResId(deploy, "deploy1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "foo-deploy1",
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
input2 := ResMap{
|
||||
resource.NewResId(statefulset, "stateful1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "StatefulSet",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "bar-stateful",
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
input := []ResMap{input1, input2}
|
||||
expected := ResMap{
|
||||
resource.NewResId(deploy, "deploy1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "foo-deploy1",
|
||||
},
|
||||
},
|
||||
}),
|
||||
resource.NewResId(statefulset, "stateful1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "StatefulSet",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "bar-stateful",
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
merged, err := Merge(input...)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(merged, expected) {
|
||||
t.Fatalf("%#v doesn't equal expected %#v", merged, expected)
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package resource
|
||||
package resmap
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -23,11 +23,12 @@ import (
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resource"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func newFromSecretGenerator(p string, s types.SecretArgs) (*Resource, error) {
|
||||
func newFromSecretGenerator(p string, s types.SecretArgs) (*resource.Resource, error) {
|
||||
corev1secret := &corev1.Secret{}
|
||||
corev1secret.APIVersion = "v1"
|
||||
corev1secret.Kind = "Secret"
|
||||
@@ -46,13 +47,13 @@ func newFromSecretGenerator(p string, s types.SecretArgs) (*Resource, error) {
|
||||
corev1secret.Data[k] = out
|
||||
}
|
||||
|
||||
obj, err := objectToUnstructured(corev1secret)
|
||||
obj, err := newUnstructuredFromObject(corev1secret)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Resource{Data: obj, Behavior: s.Behavior}, nil
|
||||
return resource.NewResource(obj, s.Behavior), nil
|
||||
}
|
||||
|
||||
func createSecretKey(wd string, command string) ([]byte, error) {
|
||||
@@ -68,10 +69,10 @@ func createSecretKey(wd string, command string) ([]byte, error) {
|
||||
return cmd.Output()
|
||||
}
|
||||
|
||||
// NewFromSecretGenerators takes a SecretGenerator slice and executes its command in directory p
|
||||
// NewResMapFromSecretArgs takes a SecretArgs slice and executes its command in directory p
|
||||
// then writes the output to a Resource slice and return it.
|
||||
func NewFromSecretGenerators(p string, secretList []types.SecretArgs) (ResourceCollection, error) {
|
||||
allResources := []*Resource{}
|
||||
func NewResMapFromSecretArgs(p string, secretList []types.SecretArgs) (ResMap, error) {
|
||||
allResources := []*resource.Resource{}
|
||||
for _, secret := range secretList {
|
||||
res, err := newFromSecretGenerator(p, secret)
|
||||
if err != nil {
|
||||
@@ -79,5 +80,5 @@ func NewFromSecretGenerators(p string, secretList []types.SecretArgs) (ResourceC
|
||||
}
|
||||
allResources = append(allResources, res)
|
||||
}
|
||||
return resourceCollectionFromResources(allResources)
|
||||
return newResMapFromResourceSlice(allResources)
|
||||
}
|
||||
@@ -14,19 +14,22 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package resource
|
||||
package resmap
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resource"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
var secret = schema.GroupVersionKind{Version: "v1", Kind: "Secret"}
|
||||
|
||||
func TestNewFromSecretGenerators(t *testing.T) {
|
||||
secrets := []types.SecretArgs{
|
||||
{
|
||||
@@ -38,17 +41,14 @@ func TestNewFromSecretGenerators(t *testing.T) {
|
||||
Type: "Opaque",
|
||||
},
|
||||
}
|
||||
re, err := NewFromSecretGenerators(".", secrets)
|
||||
re, err := NewResMapFromSecretArgs(".", secrets)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
expected := ResourceCollection{
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Version: "v1", Kind: "Secret"},
|
||||
Name: "secret",
|
||||
}: &Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
expected := ResMap{
|
||||
resource.NewResId(secret, "secret"): resource.NewResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Secret",
|
||||
@@ -63,7 +63,7 @@ func TestNewFromSecretGenerators(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
""),
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(re, expected) {
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package resource
|
||||
|
||||
import (
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/loader"
|
||||
)
|
||||
|
||||
// NewFromResources returns a ResourceCollection given a resource path slice from kustomization file.
|
||||
func NewFromResources(loader loader.Loader, paths []string) (ResourceCollection, error) {
|
||||
allResources := []ResourceCollection{}
|
||||
for _, path := range paths {
|
||||
content, err := loader.Load(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := decodeToResourceCollection(content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
allResources = append(allResources, res)
|
||||
}
|
||||
return Merge(allResources...)
|
||||
}
|
||||
|
||||
// NewFromPatches returns a slice of Resources given a patch path slice from kustomization file.
|
||||
func NewFromPatches(loader loader.Loader, paths []string) ([]*Resource, error) {
|
||||
allResources := []*Resource{}
|
||||
for _, path := range paths {
|
||||
content, err := loader.Load(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := decode(content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
allResources = append(allResources, res...)
|
||||
}
|
||||
return allResources, nil
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package resource
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/loader/loadertest"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
func TestNewFromPaths(t *testing.T) {
|
||||
|
||||
resourceStr := `apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dply1
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dply2
|
||||
`
|
||||
|
||||
l := loadertest.NewFakeLoader("/home/seans/project")
|
||||
if ferr := l.AddFile("/home/seans/project/deployment.yaml", []byte(resourceStr)); ferr != nil {
|
||||
t.Fatalf("Error adding fake file: %v\n", ferr)
|
||||
}
|
||||
expected := ResourceCollection{
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"},
|
||||
Name: "dply1",
|
||||
}: &Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "dply1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"},
|
||||
Name: "dply2",
|
||||
}: &Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "dply2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
resources, _ := NewFromResources(l, []string{"/home/seans/project/deployment.yaml"})
|
||||
if len(resources) != 2 {
|
||||
t.Fatalf("%#v should contain 2 appResource, but got %d", resources, len(resources))
|
||||
}
|
||||
|
||||
if err := compareMap(resources, expected); err != nil {
|
||||
t.Fatalf("actual doesn't match expected: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func compareMap(m1, m2 ResourceCollection) error {
|
||||
if len(m1) != len(m2) {
|
||||
keySet1 := []types.GroupVersionKindName{}
|
||||
keySet2 := []types.GroupVersionKindName{}
|
||||
for GVKn := range m1 {
|
||||
keySet1 = append(keySet1, GVKn)
|
||||
}
|
||||
for GVKn := range m1 {
|
||||
keySet2 = append(keySet2, GVKn)
|
||||
}
|
||||
return fmt.Errorf("maps has different number of entries: %#v doesn't equals %#v", keySet1, keySet2)
|
||||
}
|
||||
for GVKn, obj1 := range m1 {
|
||||
obj2, found := m2[GVKn]
|
||||
if !found {
|
||||
return fmt.Errorf("%#v doesn't exist in %#v", GVKn, m2)
|
||||
}
|
||||
if !reflect.DeepEqual(obj1.Data, obj2.Data) {
|
||||
return fmt.Errorf("%#v doesn't match %#v", obj1.Data, obj2.Data)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -14,16 +14,37 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package types
|
||||
package resource
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
// GroupVersionKindName contains GroupVersionKind and original name of the resource.
|
||||
type GroupVersionKindName struct {
|
||||
// ResId conflates GroupVersionKind with a textual name to uniquely identify a kubernetes resource (object).
|
||||
type ResId struct {
|
||||
// GroupVersionKind of the resource.
|
||||
GVK schema.GroupVersionKind
|
||||
gvk schema.GroupVersionKind
|
||||
// original name of the resource before transformation.
|
||||
Name string
|
||||
name string
|
||||
}
|
||||
|
||||
func NewResId(g schema.GroupVersionKind, n string) ResId {
|
||||
return ResId{gvk: g, name: n}
|
||||
}
|
||||
|
||||
func (n ResId) String() string {
|
||||
if n.gvk.Group == "" {
|
||||
return strings.Join([]string{n.gvk.Version, n.gvk.Kind, n.name}, "_") + ".yaml"
|
||||
}
|
||||
return strings.Join([]string{n.gvk.Group, n.gvk.Version, n.gvk.Kind, n.name}, "_") + ".yaml"
|
||||
}
|
||||
|
||||
func (n ResId) Gvk() schema.GroupVersionKind {
|
||||
return n.gvk
|
||||
}
|
||||
|
||||
func (n ResId) Name() string {
|
||||
return n.name
|
||||
}
|
||||
@@ -17,35 +17,89 @@ limitations under the License.
|
||||
package resource
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"sort"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// Resource represents a Kubernetes Resource Object for ex. Deployment, Server
|
||||
// ConfigMap etc.
|
||||
// Resource is a Kubernetes Resource Object paired with a behavior.
|
||||
type Resource struct {
|
||||
Data *unstructured.Unstructured
|
||||
Behavior string
|
||||
unstruct *unstructured.Unstructured
|
||||
behavior string
|
||||
}
|
||||
|
||||
// GVKN returns Group/Version/Kind/Name for the resource.
|
||||
func (r *Resource) GVKN() types.GroupVersionKindName {
|
||||
var emptyZVKN types.GroupVersionKindName
|
||||
if r.Data == nil {
|
||||
return emptyZVKN
|
||||
// NewResource returns a new instance of Resource.
|
||||
func NewResource(u *unstructured.Unstructured, b string) *Resource {
|
||||
return &Resource{unstruct: u, behavior: b}
|
||||
}
|
||||
|
||||
// NewBehaviorlessResource returns a new instance of Resource.
|
||||
func NewBehaviorlessResource(u *unstructured.Unstructured) *Resource {
|
||||
return &Resource{unstruct: u}
|
||||
}
|
||||
|
||||
// Behavior returns the behavior for the resource.
|
||||
func (r *Resource) Behavior() string {
|
||||
return r.behavior
|
||||
}
|
||||
|
||||
// Unstruct returns the unstructured object holding the resource.
|
||||
func (r *Resource) Unstruct() *unstructured.Unstructured {
|
||||
return r.unstruct
|
||||
}
|
||||
|
||||
// SetUnstruct sets a new member.
|
||||
func (r *Resource) SetUnstruct(u *unstructured.Unstructured) {
|
||||
r.unstruct = u
|
||||
}
|
||||
|
||||
// Id returns the ResId for the resource.
|
||||
func (r *Resource) Id() ResId {
|
||||
var empty ResId
|
||||
if r.unstruct == nil {
|
||||
return empty
|
||||
}
|
||||
gvk := r.Data.GroupVersionKind()
|
||||
return types.GroupVersionKindName{GVK: gvk, Name: r.Data.GetName()}
|
||||
gvk := r.unstruct.GroupVersionKind()
|
||||
return NewResId(gvk, r.unstruct.GetName())
|
||||
}
|
||||
|
||||
func objectToUnstructured(in runtime.Object) (*unstructured.Unstructured, error) {
|
||||
func (r *Resource) Merge(other *Resource) {
|
||||
r.Replace(other)
|
||||
mergeConfigmap(r.unstruct.Object, other.unstruct.Object, r.unstruct.Object)
|
||||
}
|
||||
|
||||
func (r *Resource) Replace(other *Resource) {
|
||||
r.unstruct.SetLabels(mergeStringMaps(other.unstruct.GetLabels(), r.unstruct.GetLabels()))
|
||||
r.unstruct.SetAnnotations(mergeStringMaps(other.unstruct.GetAnnotations(), r.unstruct.GetAnnotations()))
|
||||
r.unstruct.SetName(other.unstruct.GetName())
|
||||
}
|
||||
|
||||
// TODO: Add BinaryData once we sync to new k8s.io/api
|
||||
func mergeConfigmap(mergedTo map[string]interface{}, maps ...map[string]interface{}) {
|
||||
mergedMap := map[string]interface{}{}
|
||||
for _, m := range maps {
|
||||
datamap, ok := m["data"].(map[string]interface{})
|
||||
if ok {
|
||||
for key, value := range datamap {
|
||||
mergedMap[key] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
mergedTo["data"] = mergedMap
|
||||
}
|
||||
|
||||
func mergeStringMaps(maps ...map[string]string) map[string]string {
|
||||
result := map[string]string{}
|
||||
for _, m := range maps {
|
||||
for key, value := range m {
|
||||
result[key] = value
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func newUnstructuredFromObject(in runtime.Object) (*unstructured.Unstructured, error) {
|
||||
marshaled, err := json.Marshal(in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -54,38 +108,3 @@ func objectToUnstructured(in runtime.Object) (*unstructured.Unstructured, error)
|
||||
err = out.UnmarshalJSON(marshaled)
|
||||
return &out, err
|
||||
}
|
||||
|
||||
// ResourceCollection is a map from GroupVersionKindName to Resource
|
||||
type ResourceCollection map[types.GroupVersionKindName]*Resource
|
||||
|
||||
// EncodeAsYaml encodes the map `in` and output the encoded objects separated by `---`.
|
||||
func (in ResourceCollection) EncodeAsYaml() ([]byte, error) {
|
||||
gvknList := []types.GroupVersionKindName{}
|
||||
for gvkn := range in {
|
||||
gvknList = append(gvknList, gvkn)
|
||||
}
|
||||
sort.Sort(types.ByGVKN(gvknList))
|
||||
|
||||
firstObj := true
|
||||
var b []byte
|
||||
buf := bytes.NewBuffer(b)
|
||||
for _, gvkn := range gvknList {
|
||||
obj := in[gvkn].Data
|
||||
out, err := yaml.Marshal(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !firstObj {
|
||||
_, err = buf.WriteString("---\n")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
_, err = buf.Write(out)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
firstObj = false
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package resource
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
func TestEncode(t *testing.T) {
|
||||
encoded := []byte(`apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: cm1
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: cm2
|
||||
`)
|
||||
input := ResourceCollection{
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"},
|
||||
Name: "cm1",
|
||||
}: &Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "cm1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"},
|
||||
Name: "cm2",
|
||||
}: &Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "cm2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
out, err := input.EncodeAsYaml()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(out, encoded) {
|
||||
t.Fatalf("%s doesn't match expected %s", out, encoded)
|
||||
}
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package resource
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
k8syaml "k8s.io/apimachinery/pkg/util/yaml"
|
||||
)
|
||||
|
||||
// decode decodes a list of objects in byte array format
|
||||
func decode(in []byte) ([]*Resource, error) {
|
||||
decoder := k8syaml.NewYAMLOrJSONDecoder(bytes.NewReader(in), 1024)
|
||||
resources := []*Resource{}
|
||||
|
||||
var err error
|
||||
for {
|
||||
var out unstructured.Unstructured
|
||||
err = decoder.Decode(&out)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
resources = append(resources, &Resource{Data: &out})
|
||||
}
|
||||
if err != io.EOF {
|
||||
return nil, err
|
||||
}
|
||||
return resources, nil
|
||||
}
|
||||
|
||||
// decodeToResourceCollection decodes a list of objects in byte array format.
|
||||
// it will return a ResourceCollection.
|
||||
func decodeToResourceCollection(in []byte) (ResourceCollection, error) {
|
||||
resources, err := decode(in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
into := ResourceCollection{}
|
||||
for _, res := range resources {
|
||||
gvkn := res.GVKN()
|
||||
if _, found := into[gvkn]; found {
|
||||
return into, fmt.Errorf("GroupVersionKindName: %#v already exists in the map", gvkn)
|
||||
}
|
||||
into[gvkn] = res
|
||||
}
|
||||
return into, nil
|
||||
}
|
||||
|
||||
func resourceCollectionFromResources(resources []*Resource) (ResourceCollection, error) {
|
||||
out := ResourceCollection{}
|
||||
for _, res := range resources {
|
||||
gvkn := res.GVKN()
|
||||
if _, found := out[gvkn]; found {
|
||||
return nil, fmt.Errorf("duplicated %#v is not allowed", gvkn)
|
||||
}
|
||||
out[gvkn] = res
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Merge will merge all of the entries in the slice of ResourceCollection.
|
||||
func Merge(rcs ...ResourceCollection) (ResourceCollection, error) {
|
||||
all := ResourceCollection{}
|
||||
for _, rc := range rcs {
|
||||
for gvkn, obj := range rc {
|
||||
if _, found := all[gvkn]; found {
|
||||
return nil, fmt.Errorf("there is already an entry: %q", gvkn)
|
||||
}
|
||||
all[gvkn] = obj
|
||||
}
|
||||
}
|
||||
|
||||
return all, nil
|
||||
}
|
||||
|
||||
const behaviorCreate = "create"
|
||||
const behaviorReplace = "replace"
|
||||
const behaviorMerge = "merge"
|
||||
|
||||
// MergeWithOverride merges the entries in the ResourceCollection slice with Override.
|
||||
// If there is already an entry with the same GVKN , different actions are performed
|
||||
// according to value of Behavior field:
|
||||
// 'create': create a new one;
|
||||
// 'replace': replace the data only; keep the labels and annotations
|
||||
// 'merge': merge the data; keep the labels and annotations
|
||||
func MergeWithOverride(rcs ...ResourceCollection) (ResourceCollection, error) {
|
||||
all := ResourceCollection{}
|
||||
|
||||
for _, rc := range rcs {
|
||||
for gvkn, obj := range rc {
|
||||
if _, found := all[gvkn]; found {
|
||||
switch obj.Behavior {
|
||||
case "", behaviorCreate:
|
||||
return nil, fmt.Errorf("Create an existing gvkn %#v is not allowed", gvkn)
|
||||
case behaviorReplace:
|
||||
glog.V(4).Infof("Replace object %v by %v", all[gvkn].Data.Object, obj.Data.Object)
|
||||
obj.replace(all[gvkn])
|
||||
all[gvkn] = obj
|
||||
case behaviorMerge:
|
||||
glog.V(4).Infof("Merge object %v with %v", all[gvkn].Data.Object, obj.Data.Object)
|
||||
obj.merge(all[gvkn])
|
||||
all[gvkn] = obj
|
||||
glog.V(4).Infof("The merged object is %v", all[gvkn].Data.Object)
|
||||
default:
|
||||
return nil, fmt.Errorf("The behavior of %#v must be one of merge and replace since it already exists in the base", gvkn)
|
||||
}
|
||||
} else {
|
||||
switch obj.Behavior {
|
||||
case "", behaviorCreate:
|
||||
all[gvkn] = obj
|
||||
case behaviorMerge, behaviorReplace:
|
||||
return nil, fmt.Errorf("No merge or replace is allowed for non existing gvkn %#v", gvkn)
|
||||
default:
|
||||
return nil, fmt.Errorf("The behavior of %#v must be create since it doesn't exist", gvkn)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return all, nil
|
||||
}
|
||||
func (r *Resource) replace(other *Resource) {
|
||||
r.Data.SetLabels(mergeMap(other.Data.GetLabels(), r.Data.GetLabels()))
|
||||
r.Data.SetAnnotations(mergeMap(other.Data.GetAnnotations(), r.Data.GetAnnotations()))
|
||||
r.Data.SetName(other.Data.GetName())
|
||||
}
|
||||
|
||||
func (r *Resource) merge(other *Resource) {
|
||||
r.replace(other)
|
||||
mergeConfigmap(r.Data.Object, other.Data.Object, r.Data.Object)
|
||||
}
|
||||
|
||||
func mergeMap(maps ...map[string]string) map[string]string {
|
||||
mergedMap := map[string]string{}
|
||||
for _, m := range maps {
|
||||
for key, value := range m {
|
||||
mergedMap[key] = value
|
||||
}
|
||||
}
|
||||
return mergedMap
|
||||
}
|
||||
|
||||
// TODO: Add BinaryData once we sync to new k8s.io/api
|
||||
func mergeConfigmap(mergedTo map[string]interface{}, maps ...map[string]interface{}) {
|
||||
mergedMap := map[string]interface{}{}
|
||||
for _, m := range maps {
|
||||
datamap, ok := m["data"].(map[string]interface{})
|
||||
if ok {
|
||||
for key, value := range datamap {
|
||||
mergedMap[key] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
mergedTo["data"] = mergedMap
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package resource
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
func TestDecodeToResourceCollection(t *testing.T) {
|
||||
encoded := []byte(`apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: cm1
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: cm2
|
||||
`)
|
||||
expected := ResourceCollection{
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"},
|
||||
Name: "cm1",
|
||||
}: &Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "cm1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"},
|
||||
Name: "cm2",
|
||||
}: &Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "cm2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
m, err := decodeToResourceCollection(encoded)
|
||||
fmt.Printf("%v\n", m)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(m, expected) {
|
||||
t.Fatalf("%#v doesn't match expected %#v", m, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMerge(t *testing.T) {
|
||||
input1 := ResourceCollection{
|
||||
types.GroupVersionKindName{
|
||||
GVK: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"},
|
||||
Name: "deploy1",
|
||||
}: &Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "foo-deploy1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
input2 := ResourceCollection{
|
||||
types.GroupVersionKindName{
|
||||
GVK: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "StatefulSet"},
|
||||
Name: "stateful1",
|
||||
}: &Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "StatefulSet",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "bar-stateful",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
input := []ResourceCollection{input1, input2}
|
||||
expected := ResourceCollection{
|
||||
types.GroupVersionKindName{
|
||||
GVK: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"},
|
||||
Name: "deploy1",
|
||||
}: &Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "foo-deploy1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
types.GroupVersionKindName{
|
||||
GVK: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "StatefulSet"},
|
||||
Name: "stateful1",
|
||||
}: &Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "StatefulSet",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "bar-stateful",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
merged, err := Merge(input...)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(merged, expected) {
|
||||
t.Fatalf("%#v doesn't equal expected %#v", merged, expected)
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resource"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resmap"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
||||
)
|
||||
|
||||
@@ -56,12 +56,12 @@ func NewMapTransformer(pc []PathConfig, m map[string]string) (Transformer, error
|
||||
|
||||
// Transform apply each <key, value> pair in the mapTransformer to the
|
||||
// fields specified in mapTransformer.
|
||||
func (o *mapTransformer) Transform(m resource.ResourceCollection) error {
|
||||
for gvkn := range m {
|
||||
obj := m[gvkn].Data
|
||||
func (o *mapTransformer) Transform(m resmap.ResMap) error {
|
||||
for id := range m {
|
||||
obj := m[id].Unstruct()
|
||||
objMap := obj.UnstructuredContent()
|
||||
for _, path := range o.pathConfigs {
|
||||
if !types.SelectByGVK(gvkn.GVK, path.GroupVersionKind) {
|
||||
if !types.SelectByGVK(id.Gvk(), path.GroupVersionKind) {
|
||||
continue
|
||||
}
|
||||
err := mutateField(objMap, path.Path, path.CreateIfNotPresent, o.addMap)
|
||||
|
||||
@@ -20,18 +20,23 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resmap"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resource"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
var service = schema.GroupVersionKind{Version: "v1", Kind: "Service"}
|
||||
var secret = schema.GroupVersionKind{Version: "v1", Kind: "Secret"}
|
||||
var cmap = schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"}
|
||||
var deploy = schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"}
|
||||
var statefulset = schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "StatefulSet"}
|
||||
var foo = schema.GroupVersionKind{Group: "example.com", Version: "v1", Kind: "Foo"}
|
||||
|
||||
func TestLabelsRun(t *testing.T) {
|
||||
m := resource.ResourceCollection{
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"},
|
||||
Name: "cm1",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
m := resmap.ResMap{
|
||||
resource.NewResId(cmap, "cm1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
@@ -39,13 +44,9 @@ func TestLabelsRun(t *testing.T) {
|
||||
"name": "cm1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"},
|
||||
Name: "deploy1",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
}),
|
||||
resource.NewResId(deploy, "deploy1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"group": "apps",
|
||||
"apiVersion": "v1",
|
||||
@@ -71,13 +72,9 @@ func TestLabelsRun(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Version: "v1", Kind: "Service"},
|
||||
Name: "svc1",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
}),
|
||||
resource.NewResId(service, "svc1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Service",
|
||||
@@ -93,15 +90,11 @@ func TestLabelsRun(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
expected := resource.ResourceCollection{
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"},
|
||||
Name: "cm1",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
expected := resmap.ResMap{
|
||||
resource.NewResId(cmap, "cm1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
@@ -113,13 +106,9 @@ func TestLabelsRun(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"},
|
||||
Name: "deploy1",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
}),
|
||||
resource.NewResId(deploy, "deploy1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"group": "apps",
|
||||
"apiVersion": "v1",
|
||||
@@ -157,13 +146,9 @@ func TestLabelsRun(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Version: "v1", Kind: "Service"},
|
||||
Name: "svc1",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
}),
|
||||
resource.NewResId(service, "svc1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Service",
|
||||
@@ -187,8 +172,7 @@ func TestLabelsRun(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
||||
lt, err := NewDefaultingLabelsMapTransformer(map[string]string{"label-key1": "label-value1", "label-key2": "label-value2"})
|
||||
@@ -200,7 +184,7 @@ func TestLabelsRun(t *testing.T) {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(m, expected) {
|
||||
err = compareMap(m, expected)
|
||||
err = expected.ErrorIfNotEqual(m)
|
||||
t.Fatalf("actual doesn't match expected: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -284,12 +268,9 @@ func makeAnnotatededService() *unstructured.Unstructured {
|
||||
}
|
||||
|
||||
func TestAnnotationsRun(t *testing.T) {
|
||||
m := resource.ResourceCollection{
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"},
|
||||
Name: "cm1",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
m := resmap.ResMap{
|
||||
resource.NewResId(cmap, "cm1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
@@ -297,13 +278,9 @@ func TestAnnotationsRun(t *testing.T) {
|
||||
"name": "cm1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"},
|
||||
Name: "deploy1",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
}),
|
||||
resource.NewResId(deploy, "deploy1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"group": "apps",
|
||||
"apiVersion": "v1",
|
||||
@@ -329,13 +306,9 @@ func TestAnnotationsRun(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Version: "v1", Kind: "Service"},
|
||||
Name: "svc1",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
}),
|
||||
resource.NewResId(service, "svc1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Service",
|
||||
@@ -351,15 +324,11 @@ func TestAnnotationsRun(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
expected := resource.ResourceCollection{
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"},
|
||||
Name: "cm1",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
expected := resmap.ResMap{
|
||||
resource.NewResId(cmap, "cm1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
@@ -371,13 +340,9 @@ func TestAnnotationsRun(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"},
|
||||
Name: "deploy1",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
}),
|
||||
resource.NewResId(deploy, "deploy1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"group": "apps",
|
||||
"apiVersion": "v1",
|
||||
@@ -411,13 +376,9 @@ func TestAnnotationsRun(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Version: "v1", Kind: "Service"},
|
||||
Name: "svc1",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
}),
|
||||
resource.NewResId(service, "svc1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Service",
|
||||
@@ -437,8 +398,7 @@ func TestAnnotationsRun(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
at, err := NewDefaultingAnnotationsMapTransformer(map[string]string{"anno-key1": "anno-value1", "anno-key2": "anno-value2"})
|
||||
if err != nil {
|
||||
@@ -449,7 +409,7 @@ func TestAnnotationsRun(t *testing.T) {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(m, expected) {
|
||||
err = compareMap(m, expected)
|
||||
err = expected.ErrorIfNotEqual(m)
|
||||
t.Fatalf("actual doesn't match expected: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ limitations under the License.
|
||||
|
||||
package transformers
|
||||
|
||||
import "github.com/kubernetes-sigs/kustomize/pkg/resource"
|
||||
import "github.com/kubernetes-sigs/kustomize/pkg/resmap"
|
||||
|
||||
// multiTransformer contains a list of transformers.
|
||||
type multiTransformer struct {
|
||||
@@ -34,7 +34,7 @@ func NewMultiTransformer(t []Transformer) Transformer {
|
||||
}
|
||||
|
||||
// Transform prepends the name prefix.
|
||||
func (o *multiTransformer) Transform(m resource.ResourceCollection) error {
|
||||
func (o *multiTransformer) Transform(m resmap.ResMap) error {
|
||||
for _, t := range o.transformers {
|
||||
err := t.Transform(m)
|
||||
if err != nil {
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/hash"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resource"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resmap"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
@@ -40,14 +40,14 @@ func NewNameHashTransformer() Transformer {
|
||||
}
|
||||
|
||||
// Transform appends hash to configmaps and secrets.
|
||||
func (o *nameHashTransformer) Transform(m resource.ResourceCollection) error {
|
||||
for gvkn, obj := range m {
|
||||
func (o *nameHashTransformer) Transform(m resmap.ResMap) error {
|
||||
for id, obj := range m {
|
||||
switch {
|
||||
case types.SelectByGVK(gvkn.GVK, &schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"}):
|
||||
appendHashForConfigMap(obj.Data)
|
||||
case types.SelectByGVK(id.Gvk(), &schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"}):
|
||||
appendHashForConfigMap(obj.Unstruct())
|
||||
|
||||
case types.SelectByGVK(gvkn.GVK, &schema.GroupVersionKind{Version: "v1", Kind: "Secret"}):
|
||||
appendHashForSecret(obj.Data)
|
||||
case types.SelectByGVK(id.Gvk(), &schema.GroupVersionKind{Version: "v1", Kind: "Secret"}):
|
||||
appendHashForSecret(obj.Unstruct())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -20,18 +20,15 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resmap"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resource"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
func TestNameHashTransformer(t *testing.T) {
|
||||
objs := resource.ResourceCollection{
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"},
|
||||
Name: "cm1",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
objs := resmap.ResMap{
|
||||
resource.NewResId(cmap, "cm1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
@@ -39,13 +36,9 @@ func TestNameHashTransformer(t *testing.T) {
|
||||
"name": "cm1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"},
|
||||
Name: "deploy1",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
}),
|
||||
resource.NewResId(deploy, "deploy1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"group": "apps",
|
||||
"apiVersion": "v1",
|
||||
@@ -71,13 +64,9 @@ func TestNameHashTransformer(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Version: "v1", Kind: "Service"},
|
||||
Name: "svc1",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
}),
|
||||
resource.NewResId(service, "svc1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Service",
|
||||
@@ -93,13 +82,9 @@ func TestNameHashTransformer(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Version: "v1", Kind: "Secret"},
|
||||
Name: "secret1",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
}),
|
||||
resource.NewResId(secret, "secret1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Secret",
|
||||
@@ -107,16 +92,12 @@ func TestNameHashTransformer(t *testing.T) {
|
||||
"name": "secret1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
||||
expected := resource.ResourceCollection{
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"},
|
||||
Name: "cm1",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
expected := resmap.ResMap{
|
||||
resource.NewResId(cmap, "cm1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
@@ -124,13 +105,9 @@ func TestNameHashTransformer(t *testing.T) {
|
||||
"name": "cm1-m462kdfb68",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"},
|
||||
Name: "deploy1",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
}),
|
||||
resource.NewResId(deploy, "deploy1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"group": "apps",
|
||||
"apiVersion": "v1",
|
||||
@@ -156,13 +133,9 @@ func TestNameHashTransformer(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Version: "v1", Kind: "Service"},
|
||||
Name: "svc1",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
}),
|
||||
resource.NewResId(service, "svc1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Service",
|
||||
@@ -178,13 +151,9 @@ func TestNameHashTransformer(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Version: "v1", Kind: "Secret"},
|
||||
Name: "secret1",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
}),
|
||||
resource.NewResId(secret, "secret1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Secret",
|
||||
@@ -192,15 +161,14 @@ func TestNameHashTransformer(t *testing.T) {
|
||||
"name": "secret1-7kc45hd5f7",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
||||
tran := NewNameHashTransformer()
|
||||
tran.Transform(objs)
|
||||
|
||||
if !reflect.DeepEqual(objs, expected) {
|
||||
err := compareMap(objs, expected)
|
||||
err := expected.ErrorIfNotEqual(objs)
|
||||
t.Fatalf("actual doesn't match expected: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resource"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resmap"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
@@ -51,13 +51,13 @@ func NewNameReferenceTransformer(pc []referencePathConfig) (Transformer, error)
|
||||
// associated with the key. e.g. if <k, v> is one of the key-value pair in the map,
|
||||
// then the old name is k.Name and the new name is v.GetName()
|
||||
func (o *nameReferenceTransformer) Transform(
|
||||
m resource.ResourceCollection) error {
|
||||
for GVKn := range m {
|
||||
obj := m[GVKn].Data
|
||||
m resmap.ResMap) error {
|
||||
for id := range m {
|
||||
obj := m[id].Unstruct()
|
||||
objMap := obj.UnstructuredContent()
|
||||
for _, referencePathConfig := range o.pathConfigs {
|
||||
for _, path := range referencePathConfig.pathConfigs {
|
||||
if !types.SelectByGVK(GVKn.GVK, path.GroupVersionKind) {
|
||||
if !types.SelectByGVK(id.Gvk(), path.GroupVersionKind) {
|
||||
continue
|
||||
}
|
||||
err := mutateField(objMap, path.Path, path.CreateIfNotPresent,
|
||||
@@ -71,25 +71,9 @@ func (o *nameReferenceTransformer) Transform(
|
||||
return nil
|
||||
}
|
||||
|
||||
// noMatchingGVKNError indicates failing to find a gvkn.GroupVersionKindName.
|
||||
type noMatchingGVKNError struct {
|
||||
message string
|
||||
}
|
||||
|
||||
// newNoMatchingGVKNError constructs an instance of noMatchingGVKNError with
|
||||
// a given error message.
|
||||
func newNoMatchingGVKNError(errMsg string) noMatchingGVKNError {
|
||||
return noMatchingGVKNError{errMsg}
|
||||
}
|
||||
|
||||
// Error returns the error in string format.
|
||||
func (err noMatchingGVKNError) Error() string {
|
||||
return err.message
|
||||
}
|
||||
|
||||
func (o *nameReferenceTransformer) updateNameReference(
|
||||
GVK schema.GroupVersionKind,
|
||||
m resource.ResourceCollection,
|
||||
m resmap.ResMap,
|
||||
) func(in interface{}) (interface{}, error) {
|
||||
return func(in interface{}) (interface{}, error) {
|
||||
s, ok := in.(string)
|
||||
@@ -97,12 +81,12 @@ func (o *nameReferenceTransformer) updateNameReference(
|
||||
return nil, fmt.Errorf("%#v is expectd to be %T", in, s)
|
||||
}
|
||||
|
||||
for GVKn, obj := range m {
|
||||
if !types.SelectByGVK(GVKn.GVK, &GVK) {
|
||||
for id, obj := range m {
|
||||
if !types.SelectByGVK(id.Gvk(), &GVK) {
|
||||
continue
|
||||
}
|
||||
if GVKn.Name == s {
|
||||
return obj.Data.GetName(), nil
|
||||
if id.Name() == s {
|
||||
return obj.Unstruct().GetName(), nil
|
||||
}
|
||||
}
|
||||
return in, nil
|
||||
|
||||
@@ -20,18 +20,15 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resmap"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resource"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
func TestNameReferenceRun(t *testing.T) {
|
||||
m := resource.ResourceCollection{
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"},
|
||||
Name: "cm1",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
m := resmap.ResMap{
|
||||
resource.NewResId(cmap, "cm1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
@@ -39,13 +36,9 @@ func TestNameReferenceRun(t *testing.T) {
|
||||
"name": "someprefix-cm1-somehash",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Version: "v1", Kind: "Secret"},
|
||||
Name: "secret1",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
}),
|
||||
resource.NewResId(secret, "secret1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Secret",
|
||||
@@ -53,13 +46,9 @@ func TestNameReferenceRun(t *testing.T) {
|
||||
"name": "someprefix-secret1-somehash",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"},
|
||||
Name: "deploy1",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
}),
|
||||
resource.NewResId(deploy, "deploy1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"group": "apps",
|
||||
"apiVersion": "v1",
|
||||
@@ -122,16 +111,12 @@ func TestNameReferenceRun(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
||||
expected := resource.ResourceCollection{
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"},
|
||||
Name: "cm1",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
expected := resmap.ResMap{
|
||||
resource.NewResId(cmap, "cm1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
@@ -139,13 +124,9 @@ func TestNameReferenceRun(t *testing.T) {
|
||||
"name": "someprefix-cm1-somehash",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Version: "v1", Kind: "Secret"},
|
||||
Name: "secret1",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
}),
|
||||
resource.NewResId(secret, "secret1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Secret",
|
||||
@@ -153,13 +134,9 @@ func TestNameReferenceRun(t *testing.T) {
|
||||
"name": "someprefix-secret1-somehash",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"},
|
||||
Name: "deploy1",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
}),
|
||||
resource.NewResId(deploy, "deploy1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"group": "apps",
|
||||
"apiVersion": "v1",
|
||||
@@ -222,8 +199,7 @@ func TestNameReferenceRun(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
||||
nrt, err := NewDefaultingNameReferenceTransformer()
|
||||
@@ -232,7 +208,7 @@ func TestNameReferenceRun(t *testing.T) {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(m, expected) {
|
||||
err = compareMap(m, expected)
|
||||
err = expected.ErrorIfNotEqual(m)
|
||||
t.Fatalf("actual doesn't match expected: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ limitations under the License.
|
||||
|
||||
package transformers
|
||||
|
||||
import "github.com/kubernetes-sigs/kustomize/pkg/resource"
|
||||
import "github.com/kubernetes-sigs/kustomize/pkg/resmap"
|
||||
|
||||
// noOpTransformer contains a no-op transformer.
|
||||
type noOpTransformer struct{}
|
||||
@@ -29,6 +29,6 @@ func NewNoOpTransformer() Transformer {
|
||||
}
|
||||
|
||||
// Transform does nothing.
|
||||
func (o *noOpTransformer) Transform(_ resource.ResourceCollection) error {
|
||||
func (o *noOpTransformer) Transform(_ resmap.ResMap) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resmap"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resource"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
@@ -45,8 +46,8 @@ func NewOverlayTransformer(overlay []*resource.Resource) (Transformer, error) {
|
||||
}
|
||||
|
||||
// Transform apply the overlay on top of the base resources.
|
||||
func (o *overlayTransformer) Transform(baseResourceMap resource.ResourceCollection) error {
|
||||
// Merge and then index the patches by GVKN.
|
||||
func (o *overlayTransformer) Transform(baseResourceMap resmap.ResMap) error {
|
||||
// Merge and then index the patches by Id.
|
||||
overlays, err := o.mergePatches()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -55,22 +56,22 @@ func (o *overlayTransformer) Transform(baseResourceMap resource.ResourceCollecti
|
||||
// Strategic merge the resources exist in both base and overlay.
|
||||
for _, overlay := range overlays {
|
||||
// Merge overlay with base resource.
|
||||
gvkn := overlay.GVKN()
|
||||
base, found := baseResourceMap[gvkn]
|
||||
id := overlay.Id()
|
||||
base, found := baseResourceMap[id]
|
||||
if !found {
|
||||
return fmt.Errorf("failed to find an object with %#v to apply the patch", gvkn.GVK)
|
||||
return fmt.Errorf("failed to find an object with %#v to apply the patch", id.Gvk())
|
||||
}
|
||||
merged := map[string]interface{}{}
|
||||
versionedObj, err := scheme.Scheme.New(gvkn.GVK)
|
||||
baseName := base.Data.GetName()
|
||||
versionedObj, err := scheme.Scheme.New(id.Gvk())
|
||||
baseName := base.Unstruct().GetName()
|
||||
switch {
|
||||
case runtime.IsNotRegisteredError(err):
|
||||
// Use JSON merge patch to handle types w/o schema
|
||||
baseBytes, err := json.Marshal(base.Data)
|
||||
baseBytes, err := json.Marshal(base.Unstruct())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
patchBytes, err := json.Marshal(overlay.Data)
|
||||
patchBytes, err := json.Marshal(overlay.Unstruct())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -94,33 +95,33 @@ func (o *overlayTransformer) Transform(baseResourceMap resource.ResourceCollecti
|
||||
return err
|
||||
}
|
||||
merged, err = strategicpatch.StrategicMergeMapPatchUsingLookupPatchMeta(
|
||||
base.Data.Object,
|
||||
overlay.Data.Object,
|
||||
base.Unstruct().Object,
|
||||
overlay.Unstruct().Object,
|
||||
lookupPatchMeta)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
base.Data.SetName(baseName)
|
||||
baseResourceMap[gvkn].Data.Object = merged
|
||||
base.Unstruct().SetName(baseName)
|
||||
baseResourceMap[id].Unstruct().Object = merged
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// mergePatches merge and index patches by GVKN.
|
||||
// mergePatches merge and index patches by Id.
|
||||
// It errors out if there is conflict between patches.
|
||||
func (o *overlayTransformer) mergePatches() (resource.ResourceCollection, error) {
|
||||
rc := resource.ResourceCollection{}
|
||||
func (o *overlayTransformer) mergePatches() (resmap.ResMap, error) {
|
||||
rc := resmap.ResMap{}
|
||||
patches := resourcesToObjects(o.overlay)
|
||||
for ix, patch := range o.overlay {
|
||||
gvkn := patch.GVKN()
|
||||
existing, found := rc[gvkn]
|
||||
id := patch.Id()
|
||||
existing, found := rc[id]
|
||||
if !found {
|
||||
rc[gvkn] = patch
|
||||
rc[id] = patch
|
||||
continue
|
||||
}
|
||||
|
||||
versionedObj, err := scheme.Scheme.New(gvkn.GVK)
|
||||
versionedObj, err := scheme.Scheme.New(id.Gvk())
|
||||
if err != nil && !runtime.IsNotRegisteredError(err) {
|
||||
return nil, err
|
||||
}
|
||||
@@ -134,7 +135,7 @@ func (o *overlayTransformer) mergePatches() (resource.ResourceCollection, error)
|
||||
}
|
||||
}
|
||||
|
||||
conflict, err := cd.hasConflict(existing.Data, patch.Data)
|
||||
conflict, err := cd.hasConflict(existing.Unstruct(), patch.Unstruct())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -143,13 +144,13 @@ func (o *overlayTransformer) mergePatches() (resource.ResourceCollection, error)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, fmt.Errorf("there is conflict between %#v and %#v", conflictingPatch.Object, patch.Data.Object)
|
||||
return nil, fmt.Errorf("there is conflict between %#v and %#v", conflictingPatch.Object, patch.Unstruct().Object)
|
||||
} else {
|
||||
merged, err := cd.mergePatches(existing.Data, patch.Data)
|
||||
merged, err := cd.mergePatches(existing.Unstruct(), patch.Unstruct())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
existing.Data = merged
|
||||
existing.SetUnstruct(merged)
|
||||
}
|
||||
}
|
||||
return rc, nil
|
||||
@@ -158,7 +159,7 @@ func (o *overlayTransformer) mergePatches() (resource.ResourceCollection, error)
|
||||
func resourcesToObjects(rs []*resource.Resource) []*unstructured.Unstructured {
|
||||
objectList := make([]*unstructured.Unstructured, len(rs))
|
||||
for i := range rs {
|
||||
objectList[i] = rs[i].Data
|
||||
objectList[i] = rs[i].Unstruct()
|
||||
}
|
||||
return objectList
|
||||
}
|
||||
|
||||
@@ -21,18 +21,15 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resmap"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resource"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
func TestOverlayRun(t *testing.T) {
|
||||
base := resource.ResourceCollection{
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"},
|
||||
Name: "deploy1",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
base := resmap.ResMap{
|
||||
resource.NewResId(deploy, "deploy1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
@@ -57,35 +54,32 @@ func TestOverlayRun(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
overlay := []*resource.Resource{
|
||||
{
|
||||
Data: &unstructured.Unstructured{
|
||||
Object: 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{}{
|
||||
"another-label": "foo",
|
||||
},
|
||||
resource.NewBehaviorlessResource(&unstructured.Unstructured{
|
||||
Object: 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{}{
|
||||
"another-label": "foo",
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"containers": []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "nginx",
|
||||
"image": "nginx:latest",
|
||||
"env": []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "SOMEENV",
|
||||
"value": "BAR",
|
||||
},
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"containers": []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "nginx",
|
||||
"image": "nginx:latest",
|
||||
"env": []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "SOMEENV",
|
||||
"value": "BAR",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -95,13 +89,11 @@ func TestOverlayRun(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
),
|
||||
}
|
||||
expected := resource.ResourceCollection{
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"},
|
||||
Name: "deploy1",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
expected := resmap.ResMap{
|
||||
resource.NewResId(deploy, "deploy1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
@@ -133,8 +125,7 @@ func TestOverlayRun(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
lt, err := NewOverlayTransformer(overlay)
|
||||
if err != nil {
|
||||
@@ -145,18 +136,15 @@ func TestOverlayRun(t *testing.T) {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(base, expected) {
|
||||
err = compareMap(base, expected)
|
||||
err = expected.ErrorIfNotEqual(base)
|
||||
t.Fatalf("actual doesn't match expected: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiplePatches(t *testing.T) {
|
||||
base := resource.ResourceCollection{
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"},
|
||||
Name: "deploy1",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
base := resmap.ResMap{
|
||||
resource.NewResId(deploy, "deploy1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
@@ -176,30 +164,27 @@ func TestMultiplePatches(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
overlay := []*resource.Resource{
|
||||
{
|
||||
Data: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "deploy1",
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"template": map[string]interface{}{
|
||||
"spec": map[string]interface{}{
|
||||
"containers": []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "nginx",
|
||||
"image": "nginx:latest",
|
||||
"env": []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "SOMEENV",
|
||||
"value": "BAR",
|
||||
},
|
||||
resource.NewBehaviorlessResource(&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "deploy1",
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"template": map[string]interface{}{
|
||||
"spec": map[string]interface{}{
|
||||
"containers": []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "nginx",
|
||||
"image": "nginx:latest",
|
||||
"env": []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "SOMEENV",
|
||||
"value": "BAR",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -209,31 +194,30 @@ func TestMultiplePatches(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Data: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "deploy1",
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"template": map[string]interface{}{
|
||||
"spec": map[string]interface{}{
|
||||
"containers": []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "nginx",
|
||||
"env": []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "ANOTHERENV",
|
||||
"value": "HELLO",
|
||||
},
|
||||
),
|
||||
resource.NewBehaviorlessResource(&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "deploy1",
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"template": map[string]interface{}{
|
||||
"spec": map[string]interface{}{
|
||||
"containers": []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "nginx",
|
||||
"env": []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "ANOTHERENV",
|
||||
"value": "HELLO",
|
||||
},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"name": "busybox",
|
||||
"image": "busybox",
|
||||
},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"name": "busybox",
|
||||
"image": "busybox",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -241,13 +225,11 @@ func TestMultiplePatches(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
),
|
||||
}
|
||||
expected := resource.ResourceCollection{
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"},
|
||||
Name: "deploy1",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
expected := resmap.ResMap{
|
||||
resource.NewResId(deploy, "deploy1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
@@ -281,8 +263,7 @@ func TestMultiplePatches(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
lt, err := NewOverlayTransformer(overlay)
|
||||
if err != nil {
|
||||
@@ -293,18 +274,15 @@ func TestMultiplePatches(t *testing.T) {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(base, expected) {
|
||||
err = compareMap(base, expected)
|
||||
err = expected.ErrorIfNotEqual(base)
|
||||
t.Fatalf("actual doesn't match expected: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiplePatchesWithConflict(t *testing.T) {
|
||||
base := resource.ResourceCollection{
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"},
|
||||
Name: "deploy1",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
base := resmap.ResMap{
|
||||
resource.NewResId(deploy, "deploy1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
@@ -324,30 +302,27 @@ func TestMultiplePatchesWithConflict(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
overlay := []*resource.Resource{
|
||||
{
|
||||
Data: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "deploy1",
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"template": map[string]interface{}{
|
||||
"spec": map[string]interface{}{
|
||||
"containers": []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "nginx",
|
||||
"image": "nginx:latest",
|
||||
"env": []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "SOMEENV",
|
||||
"value": "BAR",
|
||||
},
|
||||
resource.NewBehaviorlessResource(&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "deploy1",
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"template": map[string]interface{}{
|
||||
"spec": map[string]interface{}{
|
||||
"containers": []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "nginx",
|
||||
"image": "nginx:latest",
|
||||
"env": []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "SOMEENV",
|
||||
"value": "BAR",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -357,22 +332,21 @@ func TestMultiplePatchesWithConflict(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Data: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "deploy1",
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"template": map[string]interface{}{
|
||||
"spec": map[string]interface{}{
|
||||
"containers": []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "nginx",
|
||||
"image": "nginx:1.7.9",
|
||||
},
|
||||
),
|
||||
resource.NewBehaviorlessResource(&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "deploy1",
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"template": map[string]interface{}{
|
||||
"spec": map[string]interface{}{
|
||||
"containers": []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "nginx",
|
||||
"image": "nginx:1.7.9",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -380,6 +354,7 @@ func TestMultiplePatchesWithConflict(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
),
|
||||
}
|
||||
|
||||
lt, err := NewOverlayTransformer(overlay)
|
||||
@@ -396,12 +371,9 @@ func TestMultiplePatchesWithConflict(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNoSchemaOverlayRun(t *testing.T) {
|
||||
base := resource.ResourceCollection{
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Group: "example.com", Version: "v1", Kind: "Foo"},
|
||||
Name: "my-foo",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
base := resmap.ResMap{
|
||||
resource.NewResId(foo, "my-foo"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "example.com/v1",
|
||||
"kind": "Foo",
|
||||
@@ -415,34 +387,29 @@ func TestNoSchemaOverlayRun(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
overlay := []*resource.Resource{
|
||||
{
|
||||
Data: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "example.com/v1",
|
||||
"kind": "Foo",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "my-foo",
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"bar": map[string]interface{}{
|
||||
"B": nil,
|
||||
"C": "Z",
|
||||
},
|
||||
resource.NewBehaviorlessResource(&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "example.com/v1",
|
||||
"kind": "Foo",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "my-foo",
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"bar": map[string]interface{}{
|
||||
"B": nil,
|
||||
"C": "Z",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
),
|
||||
}
|
||||
expected := resource.ResourceCollection{
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Group: "example.com", Version: "v1", Kind: "Foo"},
|
||||
Name: "my-foo",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
expected := resmap.ResMap{
|
||||
resource.NewResId(foo, "my-foo"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "example.com/v1",
|
||||
"kind": "Foo",
|
||||
@@ -456,8 +423,7 @@ func TestNoSchemaOverlayRun(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
||||
lt, err := NewOverlayTransformer(overlay)
|
||||
@@ -468,18 +434,15 @@ func TestNoSchemaOverlayRun(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if err = compareMap(base, expected); err != nil {
|
||||
if err = expected.ErrorIfNotEqual(base); err != nil {
|
||||
t.Fatalf("actual doesn't match expected: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoSchemaMultiplePatches(t *testing.T) {
|
||||
base := resource.ResourceCollection{
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Group: "example.com", Version: "v1", Kind: "Foo"},
|
||||
Name: "my-foo",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
base := resmap.ResMap{
|
||||
resource.NewResId(foo, "my-foo"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "example.com/v1",
|
||||
"kind": "Foo",
|
||||
@@ -493,54 +456,48 @@ func TestNoSchemaMultiplePatches(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
overlay := []*resource.Resource{
|
||||
{
|
||||
Data: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "example.com/v1",
|
||||
"kind": "Foo",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "my-foo",
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"bar": map[string]interface{}{
|
||||
"B": nil,
|
||||
"C": "Z",
|
||||
},
|
||||
resource.NewBehaviorlessResource(&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "example.com/v1",
|
||||
"kind": "Foo",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "my-foo",
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"bar": map[string]interface{}{
|
||||
"B": nil,
|
||||
"C": "Z",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Data: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "example.com/v1",
|
||||
"kind": "Foo",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "my-foo",
|
||||
),
|
||||
resource.NewBehaviorlessResource(&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "example.com/v1",
|
||||
"kind": "Foo",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "my-foo",
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"bar": map[string]interface{}{
|
||||
"C": "Z",
|
||||
"D": "W",
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"bar": map[string]interface{}{
|
||||
"C": "Z",
|
||||
"D": "W",
|
||||
},
|
||||
"baz": map[string]interface{}{
|
||||
"hello": "world",
|
||||
},
|
||||
"baz": map[string]interface{}{
|
||||
"hello": "world",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
),
|
||||
}
|
||||
expected := resource.ResourceCollection{
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Group: "example.com", Version: "v1", Kind: "Foo"},
|
||||
Name: "my-foo",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
expected := resmap.ResMap{
|
||||
resource.NewResId(foo, "my-foo"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "example.com/v1",
|
||||
"kind": "Foo",
|
||||
@@ -558,8 +515,7 @@ func TestNoSchemaMultiplePatches(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
||||
lt, err := NewOverlayTransformer(overlay)
|
||||
@@ -570,18 +526,15 @@ func TestNoSchemaMultiplePatches(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if err = compareMap(base, expected); err != nil {
|
||||
if err = expected.ErrorIfNotEqual(base); err != nil {
|
||||
t.Fatalf("actual doesn't match expected: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoSchemaMultiplePatchesWithConflict(t *testing.T) {
|
||||
base := resource.ResourceCollection{
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Group: "example.com", Version: "v1", Kind: "Foo"},
|
||||
Name: "my-foo",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
base := resmap.ResMap{
|
||||
resource.NewResId(foo, "my-foo"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "example.com/v1",
|
||||
"kind": "Foo",
|
||||
@@ -595,43 +548,38 @@ func TestNoSchemaMultiplePatchesWithConflict(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
overlay := []*resource.Resource{
|
||||
{
|
||||
Data: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "example.com/v1",
|
||||
"kind": "Foo",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "my-foo",
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"bar": map[string]interface{}{
|
||||
"B": nil,
|
||||
"C": "Z",
|
||||
},
|
||||
resource.NewBehaviorlessResource(&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "example.com/v1",
|
||||
"kind": "Foo",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "my-foo",
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"bar": map[string]interface{}{
|
||||
"B": nil,
|
||||
"C": "Z",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Data: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "example.com/v1",
|
||||
"kind": "Foo",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "my-foo",
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"bar": map[string]interface{}{
|
||||
"C": "NOT_Z",
|
||||
},
|
||||
}),
|
||||
resource.NewBehaviorlessResource(&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "example.com/v1",
|
||||
"kind": "Foo",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "my-foo",
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"bar": map[string]interface{}{
|
||||
"C": "NOT_Z",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
||||
lt, err := NewOverlayTransformer(overlay)
|
||||
|
||||
@@ -20,14 +20,14 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
// PathConfig contains the configuration of a field, including the GVK it ties to,
|
||||
// PathConfig contains the configuration of a field, including the gvk it ties to,
|
||||
// path to the field, etc.
|
||||
type PathConfig struct {
|
||||
// If true, it will create the path if it is not found.
|
||||
CreateIfNotPresent bool
|
||||
// The GVK that this path tied to.
|
||||
// If unset, it applied to any GVK
|
||||
// If some fields are set, it applies to all matching GVK.
|
||||
// The gvk that this path tied to.
|
||||
// If unset, it applied to any gvk
|
||||
// If some fields are set, it applies to all matching gvk.
|
||||
GroupVersionKind *schema.GroupVersionKind
|
||||
// Path to the field that will be munged.
|
||||
Path []string
|
||||
@@ -48,8 +48,8 @@ type PathConfig struct {
|
||||
// }
|
||||
type referencePathConfig struct {
|
||||
// referencedGVK is the GroupVersionKind that is referenced by
|
||||
// the PathConfig's GVK in the path of PathConfig.Path.
|
||||
// the PathConfig's gvk in the path of PathConfig.Path.
|
||||
referencedGVK schema.GroupVersionKind
|
||||
// PathConfig is the GVK that is referencing the referencedGVK object's name.
|
||||
// PathConfig is the gvk that is referencing the referencedGVK object's name.
|
||||
pathConfigs []PathConfig
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resource"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resmap"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
||||
)
|
||||
|
||||
@@ -57,12 +57,12 @@ func NewNamePrefixTransformer(pc []PathConfig, np string) (Transformer, error) {
|
||||
}
|
||||
|
||||
// Transform prepends the name prefix.
|
||||
func (o *namePrefixTransformer) Transform(m resource.ResourceCollection) error {
|
||||
for gvkn := range m {
|
||||
obj := m[gvkn].Data
|
||||
func (o *namePrefixTransformer) Transform(m resmap.ResMap) error {
|
||||
for id := range m {
|
||||
obj := m[id].Unstruct()
|
||||
objMap := obj.UnstructuredContent()
|
||||
for _, path := range o.pathConfigs {
|
||||
if !types.SelectByGVK(gvkn.GVK, path.GroupVersionKind) {
|
||||
if !types.SelectByGVK(id.Gvk(), path.GroupVersionKind) {
|
||||
continue
|
||||
}
|
||||
err := mutateField(objMap, path.Path, path.CreateIfNotPresent, o.addPrefix)
|
||||
|
||||
@@ -20,18 +20,15 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resmap"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resource"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
func TestPrefixNameRun(t *testing.T) {
|
||||
m := resource.ResourceCollection{
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"},
|
||||
Name: "cm1",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
m := resmap.ResMap{
|
||||
resource.NewResId(cmap, "cm1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
@@ -39,13 +36,9 @@ func TestPrefixNameRun(t *testing.T) {
|
||||
"name": "cm1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"},
|
||||
Name: "cm2",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
}),
|
||||
resource.NewResId(cmap, "cm2"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
@@ -53,15 +46,11 @@ func TestPrefixNameRun(t *testing.T) {
|
||||
"name": "cm2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
expected := resource.ResourceCollection{
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"},
|
||||
Name: "cm1",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
expected := resmap.ResMap{
|
||||
resource.NewResId(cmap, "cm1"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
@@ -69,13 +58,9 @@ func TestPrefixNameRun(t *testing.T) {
|
||||
"name": "someprefix-cm1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
GVK: schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"},
|
||||
Name: "cm2",
|
||||
}: &resource.Resource{
|
||||
Data: &unstructured.Unstructured{
|
||||
}),
|
||||
resource.NewResId(cmap, "cm2"): resource.NewBehaviorlessResource(
|
||||
&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
@@ -83,8 +68,7 @@ func TestPrefixNameRun(t *testing.T) {
|
||||
"name": "someprefix-cm2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
||||
npt, err := NewDefaultingNamePrefixTransformer("someprefix-")
|
||||
@@ -96,7 +80,7 @@ func TestPrefixNameRun(t *testing.T) {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(m, expected) {
|
||||
err = compareMap(m, expected)
|
||||
err = expected.ErrorIfNotEqual(m)
|
||||
t.Fatalf("actual doesn't match expected: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,10 @@ limitations under the License.
|
||||
|
||||
package transformers
|
||||
|
||||
import "github.com/kubernetes-sigs/kustomize/pkg/resource"
|
||||
import "github.com/kubernetes-sigs/kustomize/pkg/resmap"
|
||||
|
||||
// Transformer can transform objects.
|
||||
type Transformer interface {
|
||||
// Transform modifies objects in a map, e.g. add prefixes or additional labels.
|
||||
Transform(m resource.ResourceCollection) error
|
||||
Transform(m resmap.ResMap) error
|
||||
}
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package transformers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resource"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
||||
)
|
||||
|
||||
func compareMap(m1, m2 resource.ResourceCollection) error {
|
||||
if len(m1) != len(m2) {
|
||||
keySet1 := []types.GroupVersionKindName{}
|
||||
keySet2 := []types.GroupVersionKindName{}
|
||||
for GVKn := range m1 {
|
||||
keySet1 = append(keySet1, GVKn)
|
||||
}
|
||||
for GVKn := range m1 {
|
||||
keySet2 = append(keySet2, GVKn)
|
||||
}
|
||||
return fmt.Errorf("maps has different number of entries: %#v doesn't equals %#v", keySet1, keySet2)
|
||||
}
|
||||
for GVKn, obj1 := range m1 {
|
||||
obj2, found := m2[GVKn]
|
||||
if !found {
|
||||
return fmt.Errorf("%#v doesn't exist in %#v", GVKn, m2)
|
||||
}
|
||||
if !reflect.DeepEqual(obj1.Data, obj2.Data) {
|
||||
return fmt.Errorf("%#v doesn't match %#v", obj1.Data, obj2.Data)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -67,7 +67,7 @@ type ConfigMapArgs struct {
|
||||
// hash(content of configmap).
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
|
||||
// Behavior of configmap, must be one of create, merge and replace
|
||||
// behavior of configmap, must be one of create, merge and replace
|
||||
// 'create': create a new one;
|
||||
// 'replace': replace the existing one;
|
||||
// 'merge': merge the existing one.
|
||||
@@ -84,7 +84,7 @@ type SecretArgs struct {
|
||||
// hash(content of secret).
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
|
||||
// Behavior of secretGenerator, must be one of create, merge and replace
|
||||
// behavior of secretGenerator, must be one of create, merge and replace
|
||||
// 'create': create a new one;
|
||||
// 'replace': replace the existing one;
|
||||
// 'merge': merge the existing one.
|
||||
|
||||
@@ -17,25 +17,16 @@ limitations under the License.
|
||||
package types
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
func (gvkn GroupVersionKindName) String() string {
|
||||
if gvkn.GVK.Group == "" {
|
||||
return strings.Join([]string{gvkn.GVK.Version, gvkn.GVK.Kind, gvkn.Name}, "_") + ".yaml"
|
||||
}
|
||||
return strings.Join([]string{gvkn.GVK.Group, gvkn.GVK.Version, gvkn.GVK.Kind, gvkn.Name}, "_") + ".yaml"
|
||||
}
|
||||
|
||||
// SelectByGVK returns true if `selector` selects `in`; otherwise, false.
|
||||
// If `selector` and `in` are the same, return true.
|
||||
// If `selector` is nil, it is considered as a wildcard and always return true.
|
||||
// e.g. selector <Group: "", Version: "", Kind: "Deployemt"> CAN select
|
||||
// <Group: "extensions", Version: "v1beta1", Kind: "Deployemt">.
|
||||
// selector <Group: "apps", Version: "", Kind: "Deployemt"> CANNOT select
|
||||
// <Group: "extensions", Version: "v1beta1", Kind: "Deployemt">.
|
||||
// e.g. selector <Group: "", Version: "", Kind: "Deployment"> CAN select
|
||||
// <Group: "extensions", Version: "v1beta1", Kind: "Deployment">.
|
||||
// selector <Group: "apps", Version: "", Kind: "Deployment"> CANNOT select
|
||||
// <Group: "extensions", Version: "v1beta1", Kind: "Deployment">.
|
||||
func SelectByGVK(in schema.GroupVersionKind, selector *schema.GroupVersionKind) bool {
|
||||
if selector == nil {
|
||||
return true
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
func TestFilterByGVK(t *testing.T) {
|
||||
func TestSelectByGVK(t *testing.T) {
|
||||
type testCase struct {
|
||||
description string
|
||||
in schema.GroupVersionKind
|
||||
@@ -37,7 +37,7 @@ func TestFilterByGVK(t *testing.T) {
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
description: "GVK matches",
|
||||
description: "gvk matches",
|
||||
in: schema.GroupVersionKind{
|
||||
Group: "group1",
|
||||
Version: "version1",
|
||||
Reference in New Issue
Block a user