Merge pull request #66 from monopole/reduceIndirection

Reduce indirection in code w/r to the unstruct types.
This commit is contained in:
Jeff Regan
2018-06-08 08:59:47 -07:00
committed by GitHub
16 changed files with 221 additions and 211 deletions

View File

@@ -318,7 +318,8 @@ func (a *applicationImpl) resolveRefVars(resources resmap.ResMap) (map[string]st
for _, refvar := range vars {
refGVKN := resource.NewResId(refvar.ObjRef.GroupVersionKind(), refvar.ObjRef.Name)
if r, found := resources[refGVKN]; found {
s, err := resource.GetFieldValue(r.Unstruct().UnstructuredContent(), strings.Split(refvar.FieldRef.FieldPath, "."))
s, err := resource.GetFieldValue(
r.UnstructuredContent(), strings.Split(refvar.FieldRef.FieldPath, "."))
if err != nil {
return nil, fmt.Errorf("failed to resolve referred var: %+v", refvar)
}

View File

@@ -51,12 +51,12 @@ func writeYamlToNewDir(in resmap.ResMap, prefix string) (*directory, error) {
return nil, err
}
for gvkn, obj := range in {
f, err := dir.newFile(gvkn.String())
for id, obj := range in {
f, err := dir.newFile(id.String())
if err != nil {
return nil, err
}
err = print(obj.Unstruct(), f)
err = print(obj, f)
f.Close()
if err != nil {
return nil, err

View File

@@ -28,42 +28,37 @@ import (
"k8s.io/apimachinery/pkg/util/validation"
)
func newResourceFromConfigMap(l loader.Loader, cm types.ConfigMapArgs) (*resource.Resource, error) {
corev1CM, err := makeConfigMap(l, cm)
func newResourceFromConfigMap(l loader.Loader, cmArgs types.ConfigMapArgs) (*resource.Resource, error) {
cm, err := makeConfigMap(l, cmArgs)
if err != nil {
return nil, err
}
data, err := newUnstructuredFromObject(corev1CM)
if err != nil {
return nil, err
}
return resource.NewResource(data, cm.Behavior), nil
return resource.NewResourceWithBehavior(cm, resource.NewGenerationBehavior(cmArgs.Behavior))
}
func makeConfigMap(l loader.Loader, cm types.ConfigMapArgs) (*corev1.ConfigMap, error) {
func makeConfigMap(l loader.Loader, cmArgs types.ConfigMapArgs) (*corev1.ConfigMap, error) {
var envPairs, literalPairs, filePairs []kvPair
var err error
corev1cm := &corev1.ConfigMap{}
corev1cm.APIVersion = "v1"
corev1cm.Kind = "ConfigMap"
corev1cm.Name = cm.Name
corev1cm.Data = map[string]string{}
cm := &corev1.ConfigMap{}
cm.APIVersion = "v1"
cm.Kind = "ConfigMap"
cm.Name = cmArgs.Name
cm.Data = map[string]string{}
if cm.EnvSource != "" {
envPairs, err = keyValuesFromEnvFile(l, cm.EnvSource)
if cmArgs.EnvSource != "" {
envPairs, err = keyValuesFromEnvFile(l, cmArgs.EnvSource)
if err != nil {
return nil, fmt.Errorf("error reading keys from env source file: %s %v", cm.EnvSource, err)
return nil, fmt.Errorf("error reading keys from env source file: %s %v", cmArgs.EnvSource, err)
}
}
literalPairs, err = keyValuesFromLiteralSources(cm.LiteralSources)
literalPairs, err = keyValuesFromLiteralSources(cmArgs.LiteralSources)
if err != nil {
return nil, fmt.Errorf("error reading key values from literal sources: %v", err)
}
filePairs, err = keyValuesFromFileSources(l, cm.FileSources)
filePairs, err = keyValuesFromFileSources(l, cmArgs.FileSources)
if err != nil {
return nil, fmt.Errorf("error reading key values from file sources: %v", err)
}
@@ -72,13 +67,13 @@ func makeConfigMap(l loader.Loader, cm types.ConfigMapArgs) (*corev1.ConfigMap,
// merge key value pairs from all the sources
for _, kv := range allPairs {
err = addKV(corev1cm.Data, kv)
err = addKV(cm.Data, kv)
if err != nil {
return nil, fmt.Errorf("error adding key in configmap: %v", err)
}
}
return corev1cm, nil
return cm, nil
}
func keyValuesFromEnvFile(l loader.Loader, path string) ([]kvPair, error) {

View File

@@ -19,7 +19,6 @@ package resmap
import (
"bytes"
"encoding/json"
"fmt"
"io"
"reflect"
@@ -30,7 +29,6 @@ import (
"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"
)
@@ -40,8 +38,8 @@ 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)
for id := range m {
ids = append(ids, id)
}
sort.Sort(IdSlice(ids))
@@ -49,7 +47,7 @@ func (m ResMap) EncodeAsYaml() ([]byte, error) {
var b []byte
buf := bytes.NewBuffer(b)
for _, id := range ids {
obj := m[id].Unstruct()
obj := m[id]
out, err := yaml.Marshal(obj)
if err != nil {
return nil, err
@@ -87,8 +85,8 @@ func (m1 ResMap) ErrorIfNotEqual(m2 ResMap) error {
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())
if !reflect.DeepEqual(obj1, obj2) {
return fmt.Errorf("%#v doesn't match %#v", obj1, obj2)
}
}
return nil
@@ -153,11 +151,11 @@ func newResMapFromBytes(b []byte) (ResMap, error) {
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)
id := res.Id()
if _, found := result[id]; found {
return result, fmt.Errorf("GroupVersionKindName: %#v already exists b the map", id)
}
result[gvkn] = res
result[id] = res
}
return result, nil
}
@@ -165,11 +163,11 @@ func newResMapFromBytes(b []byte) (ResMap, error) {
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)
id := res.Id()
if _, found := result[id]; found {
return nil, fmt.Errorf("duplicated %#v is not allowed", id)
}
result[gvkn] = res
result[id] = res
}
return result, nil
}
@@ -197,21 +195,17 @@ func newResourceSliceFromBytes(in []byte) ([]*resource.Resource, error) {
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)
for id, obj := range m {
if _, found := result[id]; found {
return nil, fmt.Errorf("there is already an entry: %q", id)
}
result[gvkn] = obj
result[id] = 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:
@@ -221,44 +215,30 @@ const behaviorMerge = "merge"
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)
for id, r := range m {
if _, found := result[id]; found {
switch r.Behavior() {
case resource.BehaviorReplace:
glog.V(4).Infof("Replace object %v by %v", result[id].Object, r.Object)
r.Replace(result[id])
result[id] = r
case resource.BehaviorMerge:
glog.V(4).Infof("Merge object %v with %v", result[id].Object, r.Object)
r.Merge(result[id])
result[id] = r
glog.V(4).Infof("The merged object is %v", result[id].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)
return nil, fmt.Errorf("Id %#v exists; must merge or replace.", id)
}
} 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)
switch r.Behavior() {
case resource.BehaviorMerge, resource.BehaviorReplace:
return nil, fmt.Errorf("Id %#v does not exist; cannot merge or replace.", id)
default:
return nil, fmt.Errorf("The behavior of %#v must be create since it doesn't exist", gvkn)
result[id] = r
}
}
}
}
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
}

View File

@@ -28,32 +28,34 @@ import (
corev1 "k8s.io/api/core/v1"
)
func newFromSecretGenerator(p string, s types.SecretArgs) (*resource.Resource, error) {
corev1secret := &corev1.Secret{}
corev1secret.APIVersion = "v1"
corev1secret.Kind = "Secret"
corev1secret.Name = s.Name
corev1secret.Type = corev1.SecretType(s.Type)
if corev1secret.Type == "" {
corev1secret.Type = corev1.SecretTypeOpaque
func newResourceFromSecretGenerator(p string, sArgs types.SecretArgs) (*resource.Resource, error) {
s, err := makeSecret(p, sArgs)
if err != nil {
return nil, err
}
corev1secret.Data = map[string][]byte{}
return resource.NewResourceWithBehavior(
s, resource.NewGenerationBehavior(sArgs.Behavior))
}
for k, v := range s.Commands {
func makeSecret(p string, sArgs types.SecretArgs) (*corev1.Secret, error) {
s := &corev1.Secret{}
s.APIVersion = "v1"
s.Kind = "Secret"
s.Name = sArgs.Name
s.Type = corev1.SecretType(sArgs.Type)
if s.Type == "" {
s.Type = corev1.SecretTypeOpaque
}
s.Data = map[string][]byte{}
for k, v := range sArgs.Commands {
out, err := createSecretKey(p, v)
if err != nil {
return nil, err
}
corev1secret.Data[k] = out
s.Data[k] = out
}
obj, err := newUnstructuredFromObject(corev1secret)
if err != nil {
return nil, err
}
return resource.NewResource(obj, s.Behavior), nil
return s, nil
}
func createSecretKey(wd string, command string) ([]byte, error) {
@@ -65,7 +67,6 @@ func createSecretKey(wd string, command string) ([]byte, error) {
defer cancel()
cmd := exec.CommandContext(ctx, "sh", "-c", command)
cmd.Dir = wd
return cmd.Output()
}
@@ -74,7 +75,7 @@ func createSecretKey(wd string, command string) ([]byte, error) {
func NewResMapFromSecretArgs(p string, secretList []types.SecretArgs) (ResMap, error) {
allResources := []*resource.Resource{}
for _, secret := range secretList {
res, err := newFromSecretGenerator(p, secret)
res, err := newResourceFromSecretGenerator(p, secret)
if err != nil {
return nil, err
}

View File

@@ -30,10 +30,10 @@ import (
var secret = schema.GroupVersionKind{Version: "v1", Kind: "Secret"}
func TestNewFromSecretGenerators(t *testing.T) {
func TestNewResMapFromSecretArgs(t *testing.T) {
secrets := []types.SecretArgs{
{
Name: "secret",
Name: "apple",
Commands: map[string]string{
"DB_USERNAME": "printf admin",
"DB_PASSWORD": "printf somepw",
@@ -41,19 +41,20 @@ func TestNewFromSecretGenerators(t *testing.T) {
Type: "Opaque",
},
}
re, err := NewResMapFromSecretArgs(".", secrets)
actual, err := NewResMapFromSecretArgs(".", secrets)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
expected := ResMap{
resource.NewResId(secret, "secret"): resource.NewResource(
resource.NewResId(secret, "apple"): resource.NewBehaviorlessResource(
&unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "v1",
"kind": "Secret",
"metadata": map[string]interface{}{
"name": "secret",
"name": "apple",
"creationTimestamp": nil,
},
"type": string(corev1.SecretTypeOpaque),
@@ -62,11 +63,9 @@ func TestNewFromSecretGenerators(t *testing.T) {
"DB_PASSWORD": base64.StdEncoding.EncodeToString([]byte("somepw")),
},
},
},
""),
}),
}
if !reflect.DeepEqual(re, expected) {
t.Fatalf("%#v\ndoesn't match expected:\n%#v", re, expected)
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("%#v\ndoesn't match expected:\n%#v", actual, expected)
}
}

View File

@@ -0,0 +1,59 @@
/*
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
// GenerationBehavior specifies generation behavior of configmaps, secrets and maybe other resources.
type GenerationBehavior int
const (
// Unspecified behavior typically treated as a Create.
BehaviorUnspecified GenerationBehavior = iota
// Make a new resource.
BehaviorCreate
// Replace a resource.
BehaviorReplace
// Attempt to merge a new resource with an existing resource.
BehaviorMerge
)
// String converts a GenerationBehavior to a string.
func (b GenerationBehavior) String() string {
switch b {
case BehaviorReplace:
return "replace"
case BehaviorMerge:
return "merge"
case BehaviorCreate:
return "create"
default:
return "unspecified"
}
}
// NewGenerationBehavior converts a string to a GenerationBehavior.
func NewGenerationBehavior(s string) GenerationBehavior {
switch s {
case "replace":
return BehaviorReplace
case "merge":
return BehaviorMerge
case "create":
return BehaviorCreate
default:
return BehaviorUnspecified
}
}

View File

@@ -25,56 +25,49 @@ import (
"k8s.io/apimachinery/pkg/runtime"
)
// Resource is a Kubernetes Resource Object paired with a behavior.
// Resource is an "Unstructured" (json/map form) Kubernetes API resource object
// paired with a GenerationBehavior.
type Resource struct {
unstruct *unstructured.Unstructured
behavior string
unstructured.Unstructured
b GenerationBehavior
}
// NewResource returns a new instance of Resource.
func NewResource(u *unstructured.Unstructured, b string) *Resource {
return &Resource{unstruct: u, behavior: b}
// NewResourceWithBehavior returns a new instance of Resource.
func NewResourceWithBehavior(obj runtime.Object, b GenerationBehavior) (*Resource, error) {
// Convert obj to a byte stream, then convert that to JSON (Unstructured).
marshaled, err := json.Marshal(obj)
if err != nil {
return nil, err
}
var u unstructured.Unstructured
err = u.UnmarshalJSON(marshaled)
return &Resource{Unstructured: u, b: b}, nil
}
// NewBehaviorlessResource returns a new instance of Resource.
func NewBehaviorlessResource(u *unstructured.Unstructured) *Resource {
return &Resource{unstruct: u}
return &Resource{Unstructured: *u, b: BehaviorUnspecified}
}
// 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
func (r *Resource) Behavior() GenerationBehavior {
return r.b
}
// Id returns the ResId for the resource.
func (r *Resource) Id() ResId {
var empty ResId
if r.unstruct == nil {
return empty
}
gvk := r.unstruct.GroupVersionKind()
return NewResId(gvk, r.unstruct.GetName())
return NewResId(r.GroupVersionKind(), r.GetName())
}
func (r *Resource) Merge(other *Resource) {
r.Replace(other)
mergeConfigmap(r.unstruct.Object, other.unstruct.Object, r.unstruct.Object)
mergeConfigmap(r.Object, other.Object, r.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())
r.SetLabels(mergeStringMaps(other.GetLabels(), r.GetLabels()))
r.SetAnnotations(mergeStringMaps(other.GetAnnotations(), r.GetAnnotations()))
r.SetName(other.GetName())
}
// TODO: Add BinaryData once we sync to new k8s.io/api

View File

@@ -57,8 +57,7 @@ func NewMapTransformer(pc []PathConfig, m map[string]string) (Transformer, error
// fields specified in mapTransformer.
func (o *mapTransformer) Transform(m resmap.ResMap) error {
for id := range m {
obj := m[id].Unstruct()
objMap := obj.UnstructuredContent()
objMap := m[id].UnstructuredContent()
for _, path := range o.pathConfigs {
if !selectByGVK(id.Gvk(), path.GroupVersionKind) {
continue

View File

@@ -22,8 +22,8 @@ import (
"github.com/kubernetes-sigs/kustomize/pkg/hash"
"github.com/kubernetes-sigs/kustomize/pkg/resmap"
"github.com/kubernetes-sigs/kustomize/pkg/resource"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
)
@@ -40,20 +40,20 @@ func NewNameHashTransformer() Transformer {
// Transform appends hash to configmaps and secrets.
func (o *nameHashTransformer) Transform(m resmap.ResMap) error {
for id, obj := range m {
for id, res := range m {
switch {
case selectByGVK(id.Gvk(), &schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"}):
appendHashForConfigMap(obj.Unstruct())
appendHashForConfigMap(res)
case selectByGVK(id.Gvk(), &schema.GroupVersionKind{Version: "v1", Kind: "Secret"}):
appendHashForSecret(obj.Unstruct())
appendHashForSecret(res)
}
}
return nil
}
func appendHashForConfigMap(obj *unstructured.Unstructured) error {
cm, err := unstructuredToConfigmap(obj)
func appendHashForConfigMap(res *resource.Resource) error {
cm, err := unstructuredToConfigmap(res)
if err != nil {
return err
}
@@ -61,14 +61,14 @@ func appendHashForConfigMap(obj *unstructured.Unstructured) error {
if err != nil {
return err
}
nameWithHash := fmt.Sprintf("%s-%s", obj.GetName(), h)
obj.SetName(nameWithHash)
nameWithHash := fmt.Sprintf("%s-%s", res.GetName(), h)
res.SetName(nameWithHash)
return nil
}
// TODO: Remove this function after we support hash unstructured objects
func unstructuredToConfigmap(in *unstructured.Unstructured) (*v1.ConfigMap, error) {
marshaled, err := json.Marshal(in)
func unstructuredToConfigmap(res *resource.Resource) (*v1.ConfigMap, error) {
marshaled, err := json.Marshal(res)
if err != nil {
return nil, err
}
@@ -77,8 +77,8 @@ func unstructuredToConfigmap(in *unstructured.Unstructured) (*v1.ConfigMap, erro
return &out, err
}
func appendHashForSecret(obj *unstructured.Unstructured) error {
secret, err := unstructuredToSecret(obj)
func appendHashForSecret(res *resource.Resource) error {
secret, err := unstructuredToSecret(res)
if err != nil {
return err
}
@@ -86,14 +86,14 @@ func appendHashForSecret(obj *unstructured.Unstructured) error {
if err != nil {
return err
}
nameWithHash := fmt.Sprintf("%s-%s", obj.GetName(), h)
obj.SetName(nameWithHash)
nameWithHash := fmt.Sprintf("%s-%s", res.GetName(), h)
res.SetName(nameWithHash)
return nil
}
// TODO: Remove this function after we support hash unstructured objects
func unstructuredToSecret(in *unstructured.Unstructured) (*v1.Secret, error) {
marshaled, err := json.Marshal(in)
func unstructuredToSecret(res *resource.Resource) (*v1.Secret, error) {
marshaled, err := json.Marshal(res)
if err != nil {
return nil, err
}

View File

@@ -49,11 +49,9 @@ func NewNameReferenceTransformer(pc []referencePathConfig) (Transformer, error)
// The old name is in the key in the map and the new name is in the object
// 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 resmap.ResMap) error {
func (o *nameReferenceTransformer) Transform(m resmap.ResMap) error {
for id := range m {
obj := m[id].Unstruct()
objMap := obj.UnstructuredContent()
objMap := m[id].UnstructuredContent()
for _, referencePathConfig := range o.pathConfigs {
for _, path := range referencePathConfig.pathConfigs {
if !selectByGVK(id.Gvk(), path.GroupVersionKind) {
@@ -71,21 +69,19 @@ func (o *nameReferenceTransformer) Transform(
}
func (o *nameReferenceTransformer) updateNameReference(
GVK schema.GroupVersionKind,
m resmap.ResMap,
) func(in interface{}) (interface{}, error) {
GVK schema.GroupVersionKind, m resmap.ResMap) func(in interface{}) (interface{}, error) {
return func(in interface{}) (interface{}, error) {
s, ok := in.(string)
if !ok {
return nil, fmt.Errorf("%#v is expectd to be %T", in, s)
}
for id, obj := range m {
for id, res := range m {
if !selectByGVK(id.Gvk(), &GVK) {
continue
}
if id.Name() == s {
return obj.Unstruct().GetName(), nil
return res.GetName(), nil
}
}
return in, nil

View File

@@ -36,8 +36,8 @@ func NewNamespaceTransformer(ns string) Transformer {
// Transform adds the namespace.
func (o *namespaceTransformer) Transform(m resmap.ResMap) error {
for _, obj := range m {
obj.Unstruct().SetNamespace(o.namespace)
for _, res := range m {
res.SetNamespace(o.namespace)
}
return nil
}

View File

@@ -24,7 +24,6 @@ import (
"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"
"k8s.io/apimachinery/pkg/util/strategicpatch"
"k8s.io/client-go/kubernetes/scheme"
@@ -63,15 +62,15 @@ func (o *overlayTransformer) Transform(baseResourceMap resmap.ResMap) error {
}
merged := map[string]interface{}{}
versionedObj, err := scheme.Scheme.New(id.Gvk())
baseName := base.Unstruct().GetName()
baseName := base.GetName()
switch {
case runtime.IsNotRegisteredError(err):
// Use JSON merge patch to handle types w/o schema
baseBytes, err := json.Marshal(base.Unstruct())
baseBytes, err := json.Marshal(base)
if err != nil {
return err
}
patchBytes, err := json.Marshal(overlay.Unstruct())
patchBytes, err := json.Marshal(overlay)
if err != nil {
return err
}
@@ -95,15 +94,15 @@ func (o *overlayTransformer) Transform(baseResourceMap resmap.ResMap) error {
return err
}
merged, err = strategicpatch.StrategicMergeMapPatchUsingLookupPatchMeta(
base.Unstruct().Object,
overlay.Unstruct().Object,
base.Object,
overlay.Object,
lookupPatchMeta)
if err != nil {
return err
}
}
base.Unstruct().SetName(baseName)
baseResourceMap[id].Unstruct().Object = merged
base.SetName(baseName)
baseResourceMap[id].Object = merged
}
return nil
}
@@ -112,7 +111,6 @@ func (o *overlayTransformer) Transform(baseResourceMap resmap.ResMap) error {
// It errors out if there is conflict between patches.
func (o *overlayTransformer) mergePatches() (resmap.ResMap, error) {
rc := resmap.ResMap{}
patches := resourcesToObjects(o.overlay)
for ix, patch := range o.overlay {
id := patch.Id()
existing, found := rc[id]
@@ -135,31 +133,22 @@ func (o *overlayTransformer) mergePatches() (resmap.ResMap, error) {
}
}
conflict, err := cd.hasConflict(existing.Unstruct(), patch.Unstruct())
conflict, err := cd.hasConflict(existing, patch)
if err != nil {
return nil, err
}
if conflict {
conflictingPatch, err := cd.findConflict(ix, patches)
conflictingPatch, err := cd.findConflict(ix, o.overlay)
if err != nil {
return nil, err
}
return nil, fmt.Errorf("there is conflict between %#v and %#v", conflictingPatch.Object, patch.Unstruct().Object)
} else {
merged, err := cd.mergePatches(existing.Unstruct(), patch.Unstruct())
if err != nil {
return nil, err
}
existing.SetUnstruct(merged)
return nil, fmt.Errorf("there is conflict between %#v and %#v", conflictingPatch.Object, patch.Object)
}
merged, err := cd.mergePatches(existing, patch)
if err != nil {
return nil, err
}
rc[id] = merged
}
return rc, nil
}
func resourcesToObjects(rs []*resource.Resource) []*unstructured.Unstructured {
objectList := make([]*unstructured.Unstructured, len(rs))
for i := range rs {
objectList[i] = rs[i].Unstruct()
}
return objectList
}

View File

@@ -19,18 +19,18 @@ package transformers
import (
"encoding/json"
jsonpatch "github.com/evanphx/json-patch"
"github.com/evanphx/json-patch"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"github.com/kubernetes-sigs/kustomize/pkg/resource"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/mergepatch"
"k8s.io/apimachinery/pkg/util/strategicpatch"
)
type conflictDetector interface {
hasConflict(patch1, patch2 *unstructured.Unstructured) (bool, error)
findConflict(conflictingPatchIdx int, patches []*unstructured.Unstructured) (*unstructured.Unstructured, error)
mergePatches(patch1, patch2 *unstructured.Unstructured) (*unstructured.Unstructured, error)
hasConflict(patch1, patch2 *resource.Resource) (bool, error)
findConflict(conflictingPatchIdx int, patches []*resource.Resource) (*resource.Resource, error)
mergePatches(patch1, patch2 *resource.Resource) (*resource.Resource, error)
}
type jsonMergePatch struct{}
@@ -41,11 +41,11 @@ func newJMPConflictDetector() conflictDetector {
return &jsonMergePatch{}
}
func (jmp *jsonMergePatch) hasConflict(patch1, patch2 *unstructured.Unstructured) (bool, error) {
func (jmp *jsonMergePatch) hasConflict(patch1, patch2 *resource.Resource) (bool, error) {
return mergepatch.HasConflicts(patch1.Object, patch2.Object)
}
func (jmp *jsonMergePatch) findConflict(conflictingPatchIdx int, patches []*unstructured.Unstructured) (*unstructured.Unstructured, error) {
func (jmp *jsonMergePatch) findConflict(conflictingPatchIdx int, patches []*resource.Resource) (*resource.Resource, error) {
for i, patch := range patches {
if i == conflictingPatchIdx {
continue
@@ -65,8 +65,8 @@ func (jmp *jsonMergePatch) findConflict(conflictingPatchIdx int, patches []*unst
return nil, nil
}
func (jmp *jsonMergePatch) mergePatches(patch1, patch2 *unstructured.Unstructured) (*unstructured.Unstructured, error) {
var merged unstructured.Unstructured
func (jmp *jsonMergePatch) mergePatches(patch1, patch2 *resource.Resource) (*resource.Resource, error) {
var merged resource.Resource
var mergedMap map[string]interface{}
baseBytes, err := json.Marshal(patch1.Object)
if err != nil {
@@ -96,11 +96,11 @@ func newSMPConflictDetector(versionedObj runtime.Object) (conflictDetector, erro
return &strategicMergePatch{lookupPatchMeta: lookupPatchMeta}, err
}
func (smp *strategicMergePatch) hasConflict(patch1, patch2 *unstructured.Unstructured) (bool, error) {
func (smp *strategicMergePatch) hasConflict(patch1, patch2 *resource.Resource) (bool, error) {
return strategicpatch.MergingMapsHaveConflicts(patch1.Object, patch2.Object, smp.lookupPatchMeta)
}
func (smp *strategicMergePatch) findConflict(conflictingPatchIdx int, patches []*unstructured.Unstructured) (*unstructured.Unstructured, error) {
func (smp *strategicMergePatch) findConflict(conflictingPatchIdx int, patches []*resource.Resource) (*resource.Resource, error) {
for i, patch := range patches {
if i == conflictingPatchIdx {
continue
@@ -121,8 +121,8 @@ func (smp *strategicMergePatch) findConflict(conflictingPatchIdx int, patches []
return nil, nil
}
func (smp *strategicMergePatch) mergePatches(patch1, patch2 *unstructured.Unstructured) (*unstructured.Unstructured, error) {
merged := unstructured.Unstructured{}
func (smp *strategicMergePatch) mergePatches(patch1, patch2 *resource.Resource) (*resource.Resource, error) {
merged := resource.Resource{}
mergeJsonMap, err := strategicpatch.MergeStrategicMergeMapPatchUsingLookupPatchMeta(
smp.lookupPatchMeta, patch1.Object, patch2.Object)
merged.SetUnstructuredContent(mergeJsonMap)

View File

@@ -58,8 +58,7 @@ func NewNamePrefixTransformer(pc []PathConfig, np string) (Transformer, error) {
// Transform prepends the name prefix.
func (o *namePrefixTransformer) Transform(m resmap.ResMap) error {
for id := range m {
obj := m[id].Unstruct()
objMap := obj.UnstructuredContent()
objMap := m[id].UnstructuredContent()
for _, path := range o.pathConfigs {
if !selectByGVK(id.Gvk(), path.GroupVersionKind) {
continue

View File

@@ -45,8 +45,7 @@ func NewRefVarTransformer(vars map[string]string) (Transformer, error) {
// 3. Add remaining service environment vars
func (rv *refvarTransformer) Transform(resources resmap.ResMap) error {
for GVKn := range resources {
obj := resources[GVKn].Unstruct()
objMap := obj.UnstructuredContent()
objMap := resources[GVKn].UnstructuredContent()
for _, pc := range rv.pathConfigs {
if !selectByGVK(GVKn.Gvk(), pc.GroupVersionKind) {
continue