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 { for _, refvar := range vars {
refGVKN := resource.NewResId(refvar.ObjRef.GroupVersionKind(), refvar.ObjRef.Name) refGVKN := resource.NewResId(refvar.ObjRef.GroupVersionKind(), refvar.ObjRef.Name)
if r, found := resources[refGVKN]; found { 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 { if err != nil {
return nil, fmt.Errorf("failed to resolve referred var: %+v", refvar) 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 return nil, err
} }
for gvkn, obj := range in { for id, obj := range in {
f, err := dir.newFile(gvkn.String()) f, err := dir.newFile(id.String())
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = print(obj.Unstruct(), f) err = print(obj, f)
f.Close() f.Close()
if err != nil { if err != nil {
return nil, err return nil, err

View File

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

View File

@@ -19,7 +19,6 @@ package resmap
import ( import (
"bytes" "bytes"
"encoding/json"
"fmt" "fmt"
"io" "io"
"reflect" "reflect"
@@ -30,7 +29,6 @@ import (
"github.com/kubernetes-sigs/kustomize/pkg/loader" "github.com/kubernetes-sigs/kustomize/pkg/loader"
"github.com/kubernetes-sigs/kustomize/pkg/resource" "github.com/kubernetes-sigs/kustomize/pkg/resource"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
k8syaml "k8s.io/apimachinery/pkg/util/yaml" 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 `---`. // EncodeAsYaml encodes a ResMap to YAML; encoded objects separated by `---`.
func (m ResMap) EncodeAsYaml() ([]byte, error) { func (m ResMap) EncodeAsYaml() ([]byte, error) {
ids := []resource.ResId{} ids := []resource.ResId{}
for gvkn := range m { for id := range m {
ids = append(ids, gvkn) ids = append(ids, id)
} }
sort.Sort(IdSlice(ids)) sort.Sort(IdSlice(ids))
@@ -49,7 +47,7 @@ func (m ResMap) EncodeAsYaml() ([]byte, error) {
var b []byte var b []byte
buf := bytes.NewBuffer(b) buf := bytes.NewBuffer(b)
for _, id := range ids { for _, id := range ids {
obj := m[id].Unstruct() obj := m[id]
out, err := yaml.Marshal(obj) out, err := yaml.Marshal(obj)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -87,8 +85,8 @@ func (m1 ResMap) ErrorIfNotEqual(m2 ResMap) error {
if !found { if !found {
return fmt.Errorf("%#v doesn't exist in %#v", id, m2) return fmt.Errorf("%#v doesn't exist in %#v", id, m2)
} }
if !reflect.DeepEqual(obj1.Unstruct(), obj2.Unstruct()) { if !reflect.DeepEqual(obj1, obj2) {
return fmt.Errorf("%#v doesn't match %#v", obj1.Unstruct(), obj2.Unstruct()) return fmt.Errorf("%#v doesn't match %#v", obj1, obj2)
} }
} }
return nil return nil
@@ -153,11 +151,11 @@ func newResMapFromBytes(b []byte) (ResMap, error) {
result := ResMap{} result := ResMap{}
for _, res := range resources { for _, res := range resources {
gvkn := res.Id() id := res.Id()
if _, found := result[gvkn]; found { if _, found := result[id]; found {
return result, fmt.Errorf("GroupVersionKindName: %#v already exists b the map", gvkn) return result, fmt.Errorf("GroupVersionKindName: %#v already exists b the map", id)
} }
result[gvkn] = res result[id] = res
} }
return result, nil return result, nil
} }
@@ -165,11 +163,11 @@ func newResMapFromBytes(b []byte) (ResMap, error) {
func newResMapFromResourceSlice(resources []*resource.Resource) (ResMap, error) { func newResMapFromResourceSlice(resources []*resource.Resource) (ResMap, error) {
result := ResMap{} result := ResMap{}
for _, res := range resources { for _, res := range resources {
gvkn := res.Id() id := res.Id()
if _, found := result[gvkn]; found { if _, found := result[id]; found {
return nil, fmt.Errorf("duplicated %#v is not allowed", gvkn) return nil, fmt.Errorf("duplicated %#v is not allowed", id)
} }
result[gvkn] = res result[id] = res
} }
return result, nil return result, nil
} }
@@ -197,21 +195,17 @@ func newResourceSliceFromBytes(in []byte) ([]*resource.Resource, error) {
func Merge(maps ...ResMap) (ResMap, error) { func Merge(maps ...ResMap) (ResMap, error) {
result := ResMap{} result := ResMap{}
for _, m := range maps { for _, m := range maps {
for gvkn, obj := range m { for id, obj := range m {
if _, found := result[gvkn]; found { if _, found := result[id]; found {
return nil, fmt.Errorf("there is already an entry: %q", gvkn) return nil, fmt.Errorf("there is already an entry: %q", id)
} }
result[gvkn] = obj result[id] = obj
} }
} }
return result, nil return result, nil
} }
const behaviorCreate = "create"
const behaviorReplace = "replace"
const behaviorMerge = "merge"
// MergeWithOverride merges the entries in the ResMap slice with Override. // MergeWithOverride merges the entries in the ResMap slice with Override.
// If there is already an entry with the same Id , different actions are performed // If there is already an entry with the same Id , different actions are performed
// according to value of behavior field: // according to value of behavior field:
@@ -221,44 +215,30 @@ const behaviorMerge = "merge"
func MergeWithOverride(maps ...ResMap) (ResMap, error) { func MergeWithOverride(maps ...ResMap) (ResMap, error) {
result := ResMap{} result := ResMap{}
for _, m := range maps { for _, m := range maps {
for gvkn, resource := range m { for id, r := range m {
if _, found := result[gvkn]; found { if _, found := result[id]; found {
switch resource.Behavior() { switch r.Behavior() {
case "", behaviorCreate: case resource.BehaviorReplace:
return nil, fmt.Errorf("Create an existing gvkn %#v is not allowed", gvkn) glog.V(4).Infof("Replace object %v by %v", result[id].Object, r.Object)
case behaviorReplace: r.Replace(result[id])
glog.V(4).Infof("Replace object %v by %v", result[gvkn].Unstruct().Object, resource.Unstruct().Object) result[id] = r
resource.Replace(result[gvkn]) case resource.BehaviorMerge:
result[gvkn] = resource glog.V(4).Infof("Merge object %v with %v", result[id].Object, r.Object)
case behaviorMerge: r.Merge(result[id])
glog.V(4).Infof("Merge object %v with %v", result[gvkn].Unstruct().Object, resource.Unstruct().Object) result[id] = r
resource.Merge(result[gvkn]) glog.V(4).Infof("The merged object is %v", result[id].Object)
result[gvkn] = resource
glog.V(4).Infof("The merged object is %v", result[gvkn].Unstruct().Object)
default: 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 { } else {
switch resource.Behavior() { switch r.Behavior() {
case "", behaviorCreate: case resource.BehaviorMerge, resource.BehaviorReplace:
result[gvkn] = resource return nil, fmt.Errorf("Id %#v does not exist; cannot merge or replace.", id)
case behaviorMerge, behaviorReplace:
return nil, fmt.Errorf("No merge or replace is allowed for non existing gvkn %#v", gvkn)
default: default:
return nil, fmt.Errorf("The behavior of %#v must be create since it doesn't exist", gvkn) result[id] = r
} }
} }
} }
} }
return result, nil 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" corev1 "k8s.io/api/core/v1"
) )
func newFromSecretGenerator(p string, s types.SecretArgs) (*resource.Resource, error) { func newResourceFromSecretGenerator(p string, sArgs types.SecretArgs) (*resource.Resource, error) {
corev1secret := &corev1.Secret{} s, err := makeSecret(p, sArgs)
corev1secret.APIVersion = "v1" if err != nil {
corev1secret.Kind = "Secret" return nil, err
corev1secret.Name = s.Name
corev1secret.Type = corev1.SecretType(s.Type)
if corev1secret.Type == "" {
corev1secret.Type = corev1.SecretTypeOpaque
} }
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) out, err := createSecretKey(p, v)
if err != nil { if err != nil {
return nil, err return nil, err
} }
corev1secret.Data[k] = out s.Data[k] = out
} }
return s, nil
obj, err := newUnstructuredFromObject(corev1secret)
if err != nil {
return nil, err
}
return resource.NewResource(obj, s.Behavior), nil
} }
func createSecretKey(wd string, command string) ([]byte, error) { func createSecretKey(wd string, command string) ([]byte, error) {
@@ -65,7 +67,6 @@ func createSecretKey(wd string, command string) ([]byte, error) {
defer cancel() defer cancel()
cmd := exec.CommandContext(ctx, "sh", "-c", command) cmd := exec.CommandContext(ctx, "sh", "-c", command)
cmd.Dir = wd cmd.Dir = wd
return cmd.Output() return cmd.Output()
} }
@@ -74,7 +75,7 @@ func createSecretKey(wd string, command string) ([]byte, error) {
func NewResMapFromSecretArgs(p string, secretList []types.SecretArgs) (ResMap, error) { func NewResMapFromSecretArgs(p string, secretList []types.SecretArgs) (ResMap, error) {
allResources := []*resource.Resource{} allResources := []*resource.Resource{}
for _, secret := range secretList { for _, secret := range secretList {
res, err := newFromSecretGenerator(p, secret) res, err := newResourceFromSecretGenerator(p, secret)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -30,10 +30,10 @@ import (
var secret = schema.GroupVersionKind{Version: "v1", Kind: "Secret"} var secret = schema.GroupVersionKind{Version: "v1", Kind: "Secret"}
func TestNewFromSecretGenerators(t *testing.T) { func TestNewResMapFromSecretArgs(t *testing.T) {
secrets := []types.SecretArgs{ secrets := []types.SecretArgs{
{ {
Name: "secret", Name: "apple",
Commands: map[string]string{ Commands: map[string]string{
"DB_USERNAME": "printf admin", "DB_USERNAME": "printf admin",
"DB_PASSWORD": "printf somepw", "DB_PASSWORD": "printf somepw",
@@ -41,19 +41,20 @@ func TestNewFromSecretGenerators(t *testing.T) {
Type: "Opaque", Type: "Opaque",
}, },
} }
re, err := NewResMapFromSecretArgs(".", secrets) actual, err := NewResMapFromSecretArgs(".", secrets)
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
expected := ResMap{ expected := ResMap{
resource.NewResId(secret, "secret"): resource.NewResource( resource.NewResId(secret, "apple"): resource.NewBehaviorlessResource(
&unstructured.Unstructured{ &unstructured.Unstructured{
Object: map[string]interface{}{ Object: map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
"kind": "Secret", "kind": "Secret",
"metadata": map[string]interface{}{ "metadata": map[string]interface{}{
"name": "secret", "name": "apple",
"creationTimestamp": nil, "creationTimestamp": nil,
}, },
"type": string(corev1.SecretTypeOpaque), "type": string(corev1.SecretTypeOpaque),
@@ -62,11 +63,9 @@ func TestNewFromSecretGenerators(t *testing.T) {
"DB_PASSWORD": base64.StdEncoding.EncodeToString([]byte("somepw")), "DB_PASSWORD": base64.StdEncoding.EncodeToString([]byte("somepw")),
}, },
}, },
}, }),
""),
} }
if !reflect.DeepEqual(actual, expected) {
if !reflect.DeepEqual(re, expected) { t.Fatalf("%#v\ndoesn't match expected:\n%#v", actual, expected)
t.Fatalf("%#v\ndoesn't match expected:\n%#v", re, 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" "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 { type Resource struct {
unstruct *unstructured.Unstructured unstructured.Unstructured
behavior string b GenerationBehavior
} }
// NewResource returns a new instance of Resource. // NewResourceWithBehavior returns a new instance of Resource.
func NewResource(u *unstructured.Unstructured, b string) *Resource { func NewResourceWithBehavior(obj runtime.Object, b GenerationBehavior) (*Resource, error) {
return &Resource{unstruct: u, behavior: b} // 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. // NewBehaviorlessResource returns a new instance of Resource.
func NewBehaviorlessResource(u *unstructured.Unstructured) *Resource { func NewBehaviorlessResource(u *unstructured.Unstructured) *Resource {
return &Resource{unstruct: u} return &Resource{Unstructured: *u, b: BehaviorUnspecified}
} }
// Behavior returns the behavior for the resource. // Behavior returns the behavior for the resource.
func (r *Resource) Behavior() string { func (r *Resource) Behavior() GenerationBehavior {
return r.behavior return r.b
}
// 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. // Id returns the ResId for the resource.
func (r *Resource) Id() ResId { func (r *Resource) Id() ResId {
var empty ResId return NewResId(r.GroupVersionKind(), r.GetName())
if r.unstruct == nil {
return empty
}
gvk := r.unstruct.GroupVersionKind()
return NewResId(gvk, r.unstruct.GetName())
} }
func (r *Resource) Merge(other *Resource) { func (r *Resource) Merge(other *Resource) {
r.Replace(other) 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) { func (r *Resource) Replace(other *Resource) {
r.unstruct.SetLabels(mergeStringMaps(other.unstruct.GetLabels(), r.unstruct.GetLabels())) r.SetLabels(mergeStringMaps(other.GetLabels(), r.GetLabels()))
r.unstruct.SetAnnotations(mergeStringMaps(other.unstruct.GetAnnotations(), r.unstruct.GetAnnotations())) r.SetAnnotations(mergeStringMaps(other.GetAnnotations(), r.GetAnnotations()))
r.unstruct.SetName(other.unstruct.GetName()) r.SetName(other.GetName())
} }
// TODO: Add BinaryData once we sync to new k8s.io/api // 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. // fields specified in mapTransformer.
func (o *mapTransformer) Transform(m resmap.ResMap) error { func (o *mapTransformer) Transform(m resmap.ResMap) error {
for id := range m { for id := range m {
obj := m[id].Unstruct() objMap := m[id].UnstructuredContent()
objMap := obj.UnstructuredContent()
for _, path := range o.pathConfigs { for _, path := range o.pathConfigs {
if !selectByGVK(id.Gvk(), path.GroupVersionKind) { if !selectByGVK(id.Gvk(), path.GroupVersionKind) {
continue continue

View File

@@ -22,8 +22,8 @@ import (
"github.com/kubernetes-sigs/kustomize/pkg/hash" "github.com/kubernetes-sigs/kustomize/pkg/hash"
"github.com/kubernetes-sigs/kustomize/pkg/resmap" "github.com/kubernetes-sigs/kustomize/pkg/resmap"
"github.com/kubernetes-sigs/kustomize/pkg/resource"
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
) )
@@ -40,20 +40,20 @@ func NewNameHashTransformer() Transformer {
// Transform appends hash to configmaps and secrets. // Transform appends hash to configmaps and secrets.
func (o *nameHashTransformer) Transform(m resmap.ResMap) error { func (o *nameHashTransformer) Transform(m resmap.ResMap) error {
for id, obj := range m { for id, res := range m {
switch { switch {
case selectByGVK(id.Gvk(), &schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"}): 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"}): case selectByGVK(id.Gvk(), &schema.GroupVersionKind{Version: "v1", Kind: "Secret"}):
appendHashForSecret(obj.Unstruct()) appendHashForSecret(res)
} }
} }
return nil return nil
} }
func appendHashForConfigMap(obj *unstructured.Unstructured) error { func appendHashForConfigMap(res *resource.Resource) error {
cm, err := unstructuredToConfigmap(obj) cm, err := unstructuredToConfigmap(res)
if err != nil { if err != nil {
return err return err
} }
@@ -61,14 +61,14 @@ func appendHashForConfigMap(obj *unstructured.Unstructured) error {
if err != nil { if err != nil {
return err return err
} }
nameWithHash := fmt.Sprintf("%s-%s", obj.GetName(), h) nameWithHash := fmt.Sprintf("%s-%s", res.GetName(), h)
obj.SetName(nameWithHash) res.SetName(nameWithHash)
return nil return nil
} }
// TODO: Remove this function after we support hash unstructured objects // TODO: Remove this function after we support hash unstructured objects
func unstructuredToConfigmap(in *unstructured.Unstructured) (*v1.ConfigMap, error) { func unstructuredToConfigmap(res *resource.Resource) (*v1.ConfigMap, error) {
marshaled, err := json.Marshal(in) marshaled, err := json.Marshal(res)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -77,8 +77,8 @@ func unstructuredToConfigmap(in *unstructured.Unstructured) (*v1.ConfigMap, erro
return &out, err return &out, err
} }
func appendHashForSecret(obj *unstructured.Unstructured) error { func appendHashForSecret(res *resource.Resource) error {
secret, err := unstructuredToSecret(obj) secret, err := unstructuredToSecret(res)
if err != nil { if err != nil {
return err return err
} }
@@ -86,14 +86,14 @@ func appendHashForSecret(obj *unstructured.Unstructured) error {
if err != nil { if err != nil {
return err return err
} }
nameWithHash := fmt.Sprintf("%s-%s", obj.GetName(), h) nameWithHash := fmt.Sprintf("%s-%s", res.GetName(), h)
obj.SetName(nameWithHash) res.SetName(nameWithHash)
return nil return nil
} }
// TODO: Remove this function after we support hash unstructured objects // TODO: Remove this function after we support hash unstructured objects
func unstructuredToSecret(in *unstructured.Unstructured) (*v1.Secret, error) { func unstructuredToSecret(res *resource.Resource) (*v1.Secret, error) {
marshaled, err := json.Marshal(in) marshaled, err := json.Marshal(res)
if err != nil { if err != nil {
return nil, err 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 // 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, // 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() // then the old name is k.Name and the new name is v.GetName()
func (o *nameReferenceTransformer) Transform( func (o *nameReferenceTransformer) Transform(m resmap.ResMap) error {
m resmap.ResMap) error {
for id := range m { for id := range m {
obj := m[id].Unstruct() objMap := m[id].UnstructuredContent()
objMap := obj.UnstructuredContent()
for _, referencePathConfig := range o.pathConfigs { for _, referencePathConfig := range o.pathConfigs {
for _, path := range referencePathConfig.pathConfigs { for _, path := range referencePathConfig.pathConfigs {
if !selectByGVK(id.Gvk(), path.GroupVersionKind) { if !selectByGVK(id.Gvk(), path.GroupVersionKind) {
@@ -71,21 +69,19 @@ func (o *nameReferenceTransformer) Transform(
} }
func (o *nameReferenceTransformer) updateNameReference( func (o *nameReferenceTransformer) updateNameReference(
GVK schema.GroupVersionKind, GVK schema.GroupVersionKind, m resmap.ResMap) func(in interface{}) (interface{}, error) {
m resmap.ResMap,
) func(in interface{}) (interface{}, error) {
return func(in interface{}) (interface{}, error) { return func(in interface{}) (interface{}, error) {
s, ok := in.(string) s, ok := in.(string)
if !ok { if !ok {
return nil, fmt.Errorf("%#v is expectd to be %T", in, s) 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) { if !selectByGVK(id.Gvk(), &GVK) {
continue continue
} }
if id.Name() == s { if id.Name() == s {
return obj.Unstruct().GetName(), nil return res.GetName(), nil
} }
} }
return in, nil return in, nil

View File

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

View File

@@ -24,7 +24,6 @@ import (
"github.com/kubernetes-sigs/kustomize/pkg/resmap" "github.com/kubernetes-sigs/kustomize/pkg/resmap"
"github.com/kubernetes-sigs/kustomize/pkg/resource" "github.com/kubernetes-sigs/kustomize/pkg/resource"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/strategicpatch" "k8s.io/apimachinery/pkg/util/strategicpatch"
"k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/kubernetes/scheme"
@@ -63,15 +62,15 @@ func (o *overlayTransformer) Transform(baseResourceMap resmap.ResMap) error {
} }
merged := map[string]interface{}{} merged := map[string]interface{}{}
versionedObj, err := scheme.Scheme.New(id.Gvk()) versionedObj, err := scheme.Scheme.New(id.Gvk())
baseName := base.Unstruct().GetName() baseName := base.GetName()
switch { switch {
case runtime.IsNotRegisteredError(err): case runtime.IsNotRegisteredError(err):
// Use JSON merge patch to handle types w/o schema // Use JSON merge patch to handle types w/o schema
baseBytes, err := json.Marshal(base.Unstruct()) baseBytes, err := json.Marshal(base)
if err != nil { if err != nil {
return err return err
} }
patchBytes, err := json.Marshal(overlay.Unstruct()) patchBytes, err := json.Marshal(overlay)
if err != nil { if err != nil {
return err return err
} }
@@ -95,15 +94,15 @@ func (o *overlayTransformer) Transform(baseResourceMap resmap.ResMap) error {
return err return err
} }
merged, err = strategicpatch.StrategicMergeMapPatchUsingLookupPatchMeta( merged, err = strategicpatch.StrategicMergeMapPatchUsingLookupPatchMeta(
base.Unstruct().Object, base.Object,
overlay.Unstruct().Object, overlay.Object,
lookupPatchMeta) lookupPatchMeta)
if err != nil { if err != nil {
return err return err
} }
} }
base.Unstruct().SetName(baseName) base.SetName(baseName)
baseResourceMap[id].Unstruct().Object = merged baseResourceMap[id].Object = merged
} }
return nil return nil
} }
@@ -112,7 +111,6 @@ func (o *overlayTransformer) Transform(baseResourceMap resmap.ResMap) error {
// It errors out if there is conflict between patches. // It errors out if there is conflict between patches.
func (o *overlayTransformer) mergePatches() (resmap.ResMap, error) { func (o *overlayTransformer) mergePatches() (resmap.ResMap, error) {
rc := resmap.ResMap{} rc := resmap.ResMap{}
patches := resourcesToObjects(o.overlay)
for ix, patch := range o.overlay { for ix, patch := range o.overlay {
id := patch.Id() id := patch.Id()
existing, found := rc[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 { if err != nil {
return nil, err return nil, err
} }
if conflict { if conflict {
conflictingPatch, err := cd.findConflict(ix, patches) conflictingPatch, err := cd.findConflict(ix, o.overlay)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return nil, fmt.Errorf("there is conflict between %#v and %#v", conflictingPatch.Object, patch.Unstruct().Object) return nil, fmt.Errorf("there is conflict between %#v and %#v", conflictingPatch.Object, patch.Object)
} else {
merged, err := cd.mergePatches(existing.Unstruct(), patch.Unstruct())
if err != nil {
return nil, err
}
existing.SetUnstruct(merged)
} }
merged, err := cd.mergePatches(existing, patch)
if err != nil {
return nil, err
}
rc[id] = merged
} }
return rc, nil 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 ( import (
"encoding/json" "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/runtime"
"k8s.io/apimachinery/pkg/util/mergepatch" "k8s.io/apimachinery/pkg/util/mergepatch"
"k8s.io/apimachinery/pkg/util/strategicpatch" "k8s.io/apimachinery/pkg/util/strategicpatch"
) )
type conflictDetector interface { type conflictDetector interface {
hasConflict(patch1, patch2 *unstructured.Unstructured) (bool, error) hasConflict(patch1, patch2 *resource.Resource) (bool, error)
findConflict(conflictingPatchIdx int, patches []*unstructured.Unstructured) (*unstructured.Unstructured, error) findConflict(conflictingPatchIdx int, patches []*resource.Resource) (*resource.Resource, error)
mergePatches(patch1, patch2 *unstructured.Unstructured) (*unstructured.Unstructured, error) mergePatches(patch1, patch2 *resource.Resource) (*resource.Resource, error)
} }
type jsonMergePatch struct{} type jsonMergePatch struct{}
@@ -41,11 +41,11 @@ func newJMPConflictDetector() conflictDetector {
return &jsonMergePatch{} 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) 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 { for i, patch := range patches {
if i == conflictingPatchIdx { if i == conflictingPatchIdx {
continue continue
@@ -65,8 +65,8 @@ func (jmp *jsonMergePatch) findConflict(conflictingPatchIdx int, patches []*unst
return nil, nil return nil, nil
} }
func (jmp *jsonMergePatch) mergePatches(patch1, patch2 *unstructured.Unstructured) (*unstructured.Unstructured, error) { func (jmp *jsonMergePatch) mergePatches(patch1, patch2 *resource.Resource) (*resource.Resource, error) {
var merged unstructured.Unstructured var merged resource.Resource
var mergedMap map[string]interface{} var mergedMap map[string]interface{}
baseBytes, err := json.Marshal(patch1.Object) baseBytes, err := json.Marshal(patch1.Object)
if err != nil { if err != nil {
@@ -96,11 +96,11 @@ func newSMPConflictDetector(versionedObj runtime.Object) (conflictDetector, erro
return &strategicMergePatch{lookupPatchMeta: lookupPatchMeta}, err 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) 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 { for i, patch := range patches {
if i == conflictingPatchIdx { if i == conflictingPatchIdx {
continue continue
@@ -121,8 +121,8 @@ func (smp *strategicMergePatch) findConflict(conflictingPatchIdx int, patches []
return nil, nil return nil, nil
} }
func (smp *strategicMergePatch) mergePatches(patch1, patch2 *unstructured.Unstructured) (*unstructured.Unstructured, error) { func (smp *strategicMergePatch) mergePatches(patch1, patch2 *resource.Resource) (*resource.Resource, error) {
merged := unstructured.Unstructured{} merged := resource.Resource{}
mergeJsonMap, err := strategicpatch.MergeStrategicMergeMapPatchUsingLookupPatchMeta( mergeJsonMap, err := strategicpatch.MergeStrategicMergeMapPatchUsingLookupPatchMeta(
smp.lookupPatchMeta, patch1.Object, patch2.Object) smp.lookupPatchMeta, patch1.Object, patch2.Object)
merged.SetUnstructuredContent(mergeJsonMap) merged.SetUnstructuredContent(mergeJsonMap)

View File

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

View File

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