Combine generatorArgs and generatorOptions into options of Resource

This commit is contained in:
Jingfang Liu
2019-01-31 15:14:10 -08:00
parent b6d4101808
commit 84057436d6
14 changed files with 202 additions and 77 deletions

View File

@@ -35,7 +35,7 @@ func NewNameHashTransformer() transformers.Transformer {
// Transform appends hash to generated resources.
func (o *nameHashTransformer) Transform(m resmap.ResMap) error {
for _, res := range m {
if res.IsGenerated() {
if res.NeedHashSuffix() {
h, err := NewKustHash().Hash(res.Map())
if err != nil {
return err

View File

@@ -21,10 +21,10 @@ import (
"testing"
"sigs.k8s.io/kustomize/k8sdeps/kunstruct"
"sigs.k8s.io/kustomize/pkg/ifc"
"sigs.k8s.io/kustomize/pkg/resid"
"sigs.k8s.io/kustomize/pkg/resmap"
"sigs.k8s.io/kustomize/pkg/resource"
"sigs.k8s.io/kustomize/pkg/types"
)
func TestNameHashTransformer(t *testing.T) {
@@ -81,14 +81,14 @@ func TestNameHashTransformer(t *testing.T) {
},
},
}),
resid.NewResId(secret, "secret1"): rf.FromMap(
resid.NewResId(secret, "secret1"): rf.FromMapAndOption(
map[string]interface{}{
"apiVersion": "v1",
"kind": "Secret",
"metadata": map[string]interface{}{
"name": "secret1",
},
}).SetBehavior(ifc.BehaviorCreate),
}, &types.GeneratorArgs{Behavior: "create"}, &types.GeneratorOptions{DisableNameSuffixHash: false}),
}
expected := resmap.ResMap{
@@ -142,14 +142,14 @@ func TestNameHashTransformer(t *testing.T) {
},
},
}),
resid.NewResId(secret, "secret1"): rf.FromMap(
resid.NewResId(secret, "secret1"): rf.FromMapAndOption(
map[string]interface{}{
"apiVersion": "v1",
"kind": "Secret",
"metadata": map[string]interface{}{
"name": "secret1-7kc45hd5f7",
},
}).SetBehavior(ifc.BehaviorCreate),
}, &types.GeneratorArgs{Behavior: "create"}, &types.GeneratorOptions{DisableNameSuffixHash: false}),
}
tran := NewNameHashTransformer()

View File

@@ -162,7 +162,7 @@ func TestNewFromConfigMaps(t *testing.T) {
filepath: "/whatever/project/app.env",
content: "DB_USERNAME=admin\nDB_PASSWORD=somepw",
expected: ResMap{
resid.NewResId(cmap, "envConfigMap"): rf.FromMap(
resid.NewResId(cmap, "envConfigMap"): rf.FromMapAndOption(
map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
@@ -173,7 +173,7 @@ func TestNewFromConfigMaps(t *testing.T) {
"DB_USERNAME": "admin",
"DB_PASSWORD": "somepw",
},
}).SetBehavior(ifc.BehaviorCreate),
}, &types.GeneratorArgs{}, nil),
},
},
{
@@ -190,7 +190,7 @@ func TestNewFromConfigMaps(t *testing.T) {
filepath: "/whatever/project/app-init.ini",
content: "FOO=bar\nBAR=baz\n",
expected: ResMap{
resid.NewResId(cmap, "fileConfigMap"): rf.FromMap(
resid.NewResId(cmap, "fileConfigMap"): rf.FromMapAndOption(
map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
@@ -202,7 +202,7 @@ func TestNewFromConfigMaps(t *testing.T) {
BAR=baz
`,
},
}).SetBehavior(ifc.BehaviorCreate),
}, &types.GeneratorArgs{}, nil),
},
},
{
@@ -218,7 +218,7 @@ BAR=baz
},
},
expected: ResMap{
resid.NewResId(cmap, "literalConfigMap"): rf.FromMap(
resid.NewResId(cmap, "literalConfigMap"): rf.FromMapAndOption(
map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
@@ -231,7 +231,7 @@ BAR=baz
"c": "Good Morning",
"d": "false",
},
}).SetBehavior(ifc.BehaviorCreate),
}, &types.GeneratorArgs{}, nil),
},
},
// TODO: add testcase for data coming from multiple sources like
@@ -279,7 +279,7 @@ func TestNewResMapFromSecretArgs(t *testing.T) {
}
expected := ResMap{
resid.NewResId(secret, "apple"): rf.FromMap(
resid.NewResId(secret, "apple"): rf.FromMapAndOption(
map[string]interface{}{
"apiVersion": "v1",
"kind": "Secret",
@@ -291,7 +291,7 @@ func TestNewResMapFromSecretArgs(t *testing.T) {
"DB_USERNAME": base64.StdEncoding.EncodeToString([]byte("admin")),
"DB_PASSWORD": base64.StdEncoding.EncodeToString([]byte("somepw")),
},
}).SetBehavior(ifc.BehaviorCreate),
}, &types.GeneratorArgs{}, nil),
}
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("%#v\ndoesn't match expected:\n%#v", actual, expected)

View File

@@ -24,9 +24,9 @@ import (
"sort"
"github.com/ghodss/yaml"
"sigs.k8s.io/kustomize/pkg/ifc"
"sigs.k8s.io/kustomize/pkg/resid"
"sigs.k8s.io/kustomize/pkg/resource"
"sigs.k8s.io/kustomize/pkg/types"
)
// ResMap is a map from ResId to Resource.
@@ -114,8 +114,7 @@ func (m ResMap) ErrorIfNotEqual(m2 ResMap) error {
func (m ResMap) DeepCopy(rf *resource.Factory) ResMap {
mcopy := make(ResMap)
for id, obj := range m {
mcopy[id] = rf.FromKunstructured(obj.Copy())
mcopy[id].SetBehavior(obj.Behavior())
mcopy[id] = obj.DeepCopy()
}
return mcopy
}
@@ -182,20 +181,18 @@ func MergeWithOverride(maps ...ResMap) (ResMap, error) {
if len(matchedId) == 1 {
id = matchedId[0]
switch r.Behavior() {
case ifc.BehaviorReplace:
case types.BehaviorReplace:
r.Replace(result[id])
result[id] = r
result[id].SetBehavior(ifc.BehaviorCreate)
case ifc.BehaviorMerge:
case types.BehaviorMerge:
r.Merge(result[id])
result[id] = r
result[id].SetBehavior(ifc.BehaviorCreate)
default:
return nil, fmt.Errorf("id %#v exists; must merge or replace", id)
}
} else if len(matchedId) == 0 {
switch r.Behavior() {
case ifc.BehaviorMerge, ifc.BehaviorReplace:
case types.BehaviorMerge, types.BehaviorReplace:
return nil, fmt.Errorf("id %#v does not exist; cannot merge or replace", id)
default:
result[id] = r

View File

@@ -22,9 +22,9 @@ import (
"sigs.k8s.io/kustomize/k8sdeps/kunstruct"
"sigs.k8s.io/kustomize/pkg/gvk"
"sigs.k8s.io/kustomize/pkg/ifc"
"sigs.k8s.io/kustomize/pkg/resid"
"sigs.k8s.io/kustomize/pkg/resource"
"sigs.k8s.io/kustomize/pkg/types"
)
var deploy = gvk.Gvk{Group: "apps", Version: "v1", Kind: "Deployment"}
@@ -435,10 +435,10 @@ func TestMergeWithoutOverride(t *testing.T) {
}
}
func generateMergeFixtures(b ifc.GenerationBehavior) []ResMap {
func generateMergeFixtures(b types.GenerationBehavior) []ResMap {
input1 := ResMap{
resid.NewResId(cmap, "cmap"): rf.FromMap(
resid.NewResId(cmap, "cmap"): rf.FromMapAndOption(
map[string]interface{}{
"apiVersion": "apps/v1",
"kind": "ConfigMap",
@@ -449,10 +449,12 @@ func generateMergeFixtures(b ifc.GenerationBehavior) []ResMap {
"a": "x",
"b": "y",
},
}),
}, &types.GeneratorArgs{
Behavior: "create",
}, nil),
}
input2 := ResMap{
resid.NewResId(cmap, "cmap"): rf.FromMap(
resid.NewResId(cmap, "cmap"): rf.FromMapAndOption(
map[string]interface{}{
"apiVersion": "apps/v1",
"kind": "ConfigMap",
@@ -464,16 +466,16 @@ func generateMergeFixtures(b ifc.GenerationBehavior) []ResMap {
"b": "v",
"c": "w",
},
}),
}, &types.GeneratorArgs{
Behavior: b.String(),
}, nil),
}
input1[resid.NewResId(cmap, "cmap")].SetBehavior(ifc.BehaviorCreate)
input2[resid.NewResId(cmap, "cmap")].SetBehavior(b)
return []ResMap{input1, input2}
}
func TestMergeWithOverride(t *testing.T) {
expected := ResMap{
resid.NewResId(cmap, "cmap"): rf.FromMap(
resid.NewResId(cmap, "cmap"): rf.FromMapAndOption(
map[string]interface{}{
"apiVersion": "apps/v1",
"kind": "ConfigMap",
@@ -487,10 +489,11 @@ func TestMergeWithOverride(t *testing.T) {
"b": "v",
"c": "w",
},
}),
}, &types.GeneratorArgs{
Behavior: "create",
}, nil),
}
expected[resid.NewResId(cmap, "cmap")].SetBehavior(ifc.BehaviorCreate)
merged, err := MergeWithOverride(generateMergeFixtures(ifc.BehaviorMerge)...)
merged, err := MergeWithOverride(generateMergeFixtures(types.BehaviorMerge)...)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
@@ -514,7 +517,7 @@ func TestMergeWithOverride(t *testing.T) {
t.Fatalf("%#v doesn't equal expected %#v", merged2, expected)
}
inputs := generateMergeFixtures(ifc.BehaviorReplace)
inputs := generateMergeFixtures(types.BehaviorReplace)
replaced, err := MergeWithOverride(inputs...)
if err != nil {
t.Fatalf("unexpected error: %v", err)
@@ -524,7 +527,7 @@ func TestMergeWithOverride(t *testing.T) {
t.Fatalf("%#v doesn't equal expected %#v", replaced, expectedReplaced)
}
_, err = MergeWithOverride(generateMergeFixtures(ifc.BehaviorUnspecified)...)
_, err = MergeWithOverride(generateMergeFixtures(types.BehaviorUnspecified)...)
if err == nil {
t.Fatal("Merging with GenerationBehavior BehaviorUnspecified should return an error but does not")
}

View File

@@ -41,7 +41,16 @@ func NewFactory(kf ifc.KunstructuredFactory) *Factory {
func (rf *Factory) FromMap(m map[string]interface{}) *Resource {
return &Resource{
Kunstructured: rf.kf.FromMap(m),
b: ifc.BehaviorUnspecified}
options: types.NewGenArgs(nil, nil),
}
}
// FromMapAndOption returns a new instance of Resource with given options.
func (rf *Factory) FromMapAndOption(m map[string]interface{}, args *types.GeneratorArgs, option *types.GeneratorOptions) *Resource {
return &Resource{
Kunstructured: rf.kf.FromMap(m),
options: types.NewGenArgs(args, option),
}
}
// FromKunstructured returns a new instance of Resource.
@@ -50,7 +59,10 @@ func (rf *Factory) FromKunstructured(
if u == nil {
log.Fatal("unstruct ifc must not be null")
}
return &Resource{Kunstructured: u, b: ifc.BehaviorUnspecified}
return &Resource{
Kunstructured: u,
options: types.NewGenArgs(nil, nil),
}
}
// SliceFromPatches returns a slice of resources given a patch path
@@ -118,7 +130,7 @@ func (rf *Factory) MakeConfigMap(args *types.ConfigMapArgs, options *types.Gener
if err != nil {
return nil, err
}
return &Resource{Kunstructured: u, b: fixBehavior(args.Behavior)}, nil
return &Resource{Kunstructured: u, options: types.NewGenArgs(&types.GeneratorArgs{Behavior: args.Behavior}, options)}, nil
}
// MakeSecret makes an instance of Resource for Secret
@@ -127,13 +139,5 @@ func (rf *Factory) MakeSecret(args *types.SecretArgs, options *types.GeneratorOp
if err != nil {
return nil, err
}
return &Resource{Kunstructured: u, b: fixBehavior(args.Behavior)}, nil
}
func fixBehavior(s string) ifc.GenerationBehavior {
b := ifc.NewGenerationBehavior(s)
if b == ifc.BehaviorUnspecified {
return ifc.BehaviorCreate
}
return b
return &Resource{Kunstructured: u, options: types.NewGenArgs(&types.GeneratorArgs{Behavior: args.Behavior}, options)}, nil
}

View File

@@ -22,13 +22,14 @@ import (
"sigs.k8s.io/kustomize/pkg/ifc"
"sigs.k8s.io/kustomize/pkg/resid"
"sigs.k8s.io/kustomize/pkg/types"
)
// Resource is map representation of a Kubernetes API resource object
// paired with a GenerationBehavior.
type Resource struct {
ifc.Kunstructured
b ifc.GenerationBehavior
options *types.GenArgs
}
// String returns resource as JSON.
@@ -37,23 +38,25 @@ func (r *Resource) String() string {
if err != nil {
return "<" + err.Error() + ">"
}
return r.b.String() + ":" + strings.TrimSpace(string(bs))
return strings.TrimSpace(string(bs))
}
// DeepCopy returns a new copy of resource
func (r *Resource) DeepCopy() *Resource {
return &Resource{
Kunstructured: r.Kunstructured.Copy(),
options: r.options,
}
}
// Behavior returns the behavior for the resource.
func (r *Resource) Behavior() ifc.GenerationBehavior {
return r.b
func (r *Resource) Behavior() types.GenerationBehavior {
return r.options.Behavior()
}
// SetBehavior changes the resource to the new behavior
func (r *Resource) SetBehavior(b ifc.GenerationBehavior) *Resource {
r.b = b
return r
}
// IsGenerated checks if the resource is generated from a generator
func (r *Resource) IsGenerated() bool {
return r.b != ifc.BehaviorUnspecified
// NeedAppendHash checks if the resource need a hash suffix
func (r *Resource) NeedHashSuffix() bool {
return r.options != nil && r.options.NeedsHashSuffix()
}
// Id returns the ResId for the resource.
@@ -74,6 +77,7 @@ func (r *Resource) Replace(other *Resource) {
r.SetAnnotations(
mergeStringMaps(other.GetAnnotations(), r.GetAnnotations()))
r.SetName(other.GetName())
r.options = other.options
}
// TODO: Add BinaryData once we sync to new k8s.io/api

View File

@@ -37,7 +37,7 @@ var testConfigMap = factory.FromMap(
},
})
const testConfigMapString = `unspecified:{"apiVersion":"v1","kind":"ConfigMap","metadata":{"name":"winnie","namespace":"hundred-acre-wood"}}`
const testConfigMapString = `{"apiVersion":"v1","kind":"ConfigMap","metadata":{"name":"winnie","namespace":"hundred-acre-wood"}}`
var testDeployment = factory.FromMap(
map[string]interface{}{
@@ -48,7 +48,7 @@ var testDeployment = factory.FromMap(
},
})
const testDeploymentString = `unspecified:{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"name":"pooh"}}`
const testDeploymentString = `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"name":"pooh"}}`
func TestResourceString(t *testing.T) {
tests := []struct {

View File

@@ -0,0 +1,71 @@
/*
Copyright 2019 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 target
import (
"testing"
)
func TestGeneratorOptionsWithBases(t *testing.T) {
th := NewKustTestHarness(t, "/app/overlay")
th.writeK("/app/base", `
apiVersion: v1beta1
kind: Kustomization
generatorOptions:
disableNameSuffixHash: true
labels:
foo: bar
configMapGenerator:
- name: shouldNotHaveHash
literals:
- foo=bar
`)
th.writeK("/app/overlay", `
apiVersion: v1beta1
kind: Kustomization
bases:
- ../base
generatorOptions:
disableNameSuffixHash: false
labels:
fruit: apple
configMapGenerator:
- name: shouldHaveHash
literals:
- fruit=apple
`)
m, err := th.makeKustTarget().MakeCustomizedResMap()
if err != nil {
t.Fatalf("Err: %v", err)
}
th.assertActualEqualsExpected(m, `
apiVersion: v1
data:
fruit: apple
kind: ConfigMap
metadata:
labels:
fruit: apple
name: shouldHaveHash-2k9hc848ff
---
apiVersion: v1
data:
foo: bar
kind: ConfigMap
metadata:
labels:
foo: bar
name: shouldNotHaveHash
`)
}

View File

@@ -117,15 +117,9 @@ func (kt *KustTarget) MakeCustomizedResMap() (resmap.ResMap, error) {
if err != nil {
return nil, err
}
if kt.shouldAddHashSuffixesToGeneratedResources() {
// This effects only generated resources.
// It changes only the Name field in the
// resource held in the ResMap's value, not
// the Name in the key in the ResMap.
err := ra.Transform(kt.tFactory.MakeHashTransformer())
if err != nil {
return nil, err
}
err = ra.Transform(kt.tFactory.MakeHashTransformer())
if err != nil {
return nil, err
}
// Given that names have changed (prefixs/suffixes added),
// fix all the back references to those names.

View File

@@ -126,7 +126,7 @@ func TestResources1(t *testing.T) {
}),
resid.NewResIdWithPrefixSuffixNamespace(
gvk.Gvk{Version: "v1", Kind: "ConfigMap"},
"literalConfigMap", "foo-", "-bar", "ns1"): th.fromMap(
"literalConfigMap", "foo-", "-bar", "ns1"): th.fromMapAndOption(
map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
@@ -144,10 +144,10 @@ func TestResources1(t *testing.T) {
"DB_USERNAME": "admin",
"DB_PASSWORD": "somepw",
},
}).SetBehavior(ifc.BehaviorCreate),
}, &types.GeneratorArgs{}, nil),
resid.NewResIdWithPrefixSuffixNamespace(
gvk.Gvk{Version: "v1", Kind: "Secret"},
"secret", "foo-", "-bar", "ns1"): th.fromMap(
"secret", "foo-", "-bar", "ns1"): th.fromMapAndOption(
map[string]interface{}{
"apiVersion": "v1",
"kind": "Secret",
@@ -166,7 +166,7 @@ func TestResources1(t *testing.T) {
"DB_USERNAME": base64.StdEncoding.EncodeToString([]byte("admin")),
"DB_PASSWORD": base64.StdEncoding.EncodeToString([]byte("somepw")),
},
}).SetBehavior(ifc.BehaviorCreate),
}, &types.GeneratorArgs{}, nil),
resid.NewResIdWithPrefixSuffixNamespace(
gvk.Gvk{Version: "v1", Kind: "Namespace"},
"ns1", "foo-", "-bar", ""): th.fromMap(

View File

@@ -32,6 +32,7 @@ import (
"sigs.k8s.io/kustomize/pkg/internal/loadertest"
"sigs.k8s.io/kustomize/pkg/resmap"
"sigs.k8s.io/kustomize/pkg/resource"
"sigs.k8s.io/kustomize/pkg/types"
)
type KustTestHarness struct {
@@ -84,6 +85,10 @@ func (th *KustTestHarness) fromMap(m map[string]interface{}) *resource.Resource
return th.rf.RF().FromMap(m)
}
func (th *KustTestHarness) fromMapAndOption(m map[string]interface{}, args *types.GeneratorArgs, option *types.GeneratorOptions) *resource.Resource {
return th.rf.RF().FromMapAndOption(m, args, option)
}
func (th *KustTestHarness) writeDefaultConfigs(fName string) {
m := defaultconfig.GetDefaultFieldSpecsAsMap()
var content []byte

47
pkg/types/genargs.go Normal file
View File

@@ -0,0 +1,47 @@
/*
Copyright 2019 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 types
// GenArgs contains both generator args and options
type GenArgs struct {
args *GeneratorArgs
opts *GeneratorOptions
}
// NewGenArgs returns a new object of GenArgs
func NewGenArgs(args *GeneratorArgs, opts *GeneratorOptions) *GenArgs {
return &GenArgs{
args: args,
opts: opts,
}
}
// NeedHashSuffix returns true if the hash suffix is needed.
// It is needed when the two conditions are both met
// 1) GenArgs is not nil
// 2) DisableNameSuffixHash in GeneratorOptions is not set to true
func (g *GenArgs) NeedsHashSuffix() bool {
return g.args != nil && (g.opts == nil || g.opts.DisableNameSuffixHash == false)
}
// Behavior returns Behavior field of GeneratorArgs
func (g *GenArgs) Behavior() GenerationBehavior {
if g.args == nil {
return BehaviorUnspecified
}
return NewGenerationBehavior(g.args.Behavior)
}

View File

@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package ifc
package types
// GenerationBehavior specifies generation behavior of configmaps, secrets and maybe other resources.
type GenerationBehavior int