mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-14 02:20:53 +00:00
Make resource, resmap public.
This commit is contained in:
179
api/resource/factory.go
Normal file
179
api/resource/factory.go
Normal file
@@ -0,0 +1,179 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package resource
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/v3/api/ifc"
|
||||
"sigs.k8s.io/kustomize/v3/api/types"
|
||||
"sigs.k8s.io/kustomize/v3/internal/kusterr"
|
||||
)
|
||||
|
||||
// Factory makes instances of Resource.
|
||||
type Factory struct {
|
||||
kf ifc.KunstructuredFactory
|
||||
}
|
||||
|
||||
// NewFactory makes an instance of Factory.
|
||||
func NewFactory(kf ifc.KunstructuredFactory) *Factory {
|
||||
return &Factory{kf: kf}
|
||||
}
|
||||
|
||||
func (rf *Factory) Hasher() ifc.KunstructuredHasher {
|
||||
return rf.kf.Hasher()
|
||||
}
|
||||
|
||||
// FromMap returns a new instance of Resource.
|
||||
func (rf *Factory) FromMap(m map[string]interface{}) *Resource {
|
||||
return rf.makeOne(rf.kf.FromMap(m), nil)
|
||||
}
|
||||
|
||||
// FromMapWithName returns a new instance with the given "original" name.
|
||||
func (rf *Factory) FromMapWithName(n string, m map[string]interface{}) *Resource {
|
||||
return rf.makeOne(rf.kf.FromMap(m), nil).setOriginalName(n)
|
||||
}
|
||||
|
||||
// FromMapWithNamespace returns a new instance with the given "original" namespace.
|
||||
func (rf *Factory) FromMapWithNamespace(n string, m map[string]interface{}) *Resource {
|
||||
return rf.makeOne(rf.kf.FromMap(m), nil).setOriginalNs(n)
|
||||
}
|
||||
|
||||
// FromMapWithNamespaceAndName returns a new instance with the given "original" namespace.
|
||||
func (rf *Factory) FromMapWithNamespaceAndName(ns string, n string, m map[string]interface{}) *Resource {
|
||||
return rf.makeOne(rf.kf.FromMap(m), nil).setOriginalNs(ns).setOriginalName(n)
|
||||
}
|
||||
|
||||
// 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 rf.makeOne(rf.kf.FromMap(m), types.NewGenArgs(args, option))
|
||||
}
|
||||
|
||||
// FromKunstructured returns a new instance of Resource.
|
||||
func (rf *Factory) FromKunstructured(u ifc.Kunstructured) *Resource {
|
||||
return rf.makeOne(u, nil)
|
||||
}
|
||||
|
||||
// makeOne returns a new instance of Resource.
|
||||
func (rf *Factory) makeOne(
|
||||
u ifc.Kunstructured, o *types.GenArgs) *Resource {
|
||||
if u == nil {
|
||||
log.Fatal("unstruct ifc must not be null")
|
||||
}
|
||||
if o == nil {
|
||||
o = types.NewGenArgs(nil, nil)
|
||||
}
|
||||
r := &Resource{
|
||||
Kunstructured: u,
|
||||
options: o,
|
||||
}
|
||||
return r.setOriginalName(r.GetName()).setOriginalNs(r.GetNamespace())
|
||||
}
|
||||
|
||||
// SliceFromPatches returns a slice of resources given a patch path
|
||||
// slice from a kustomization file.
|
||||
func (rf *Factory) SliceFromPatches(
|
||||
ldr ifc.Loader, paths []types.PatchStrategicMerge) ([]*Resource, error) {
|
||||
var result []*Resource
|
||||
for _, path := range paths {
|
||||
content, err := ldr.Load(string(path))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := rf.SliceFromBytes(content)
|
||||
if err != nil {
|
||||
return nil, kusterr.Handler(err, string(path))
|
||||
}
|
||||
result = append(result, res...)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// FromBytes unmarshals bytes into one Resource.
|
||||
func (rf *Factory) FromBytes(in []byte) (*Resource, error) {
|
||||
result, err := rf.SliceFromBytes(in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(result) != 1 {
|
||||
return nil, fmt.Errorf(
|
||||
"expected 1 resource, found %d in %v", len(result), in)
|
||||
}
|
||||
return result[0], nil
|
||||
}
|
||||
|
||||
// SliceFromBytes unmarshals bytes into a Resource slice.
|
||||
func (rf *Factory) SliceFromBytes(in []byte) ([]*Resource, error) {
|
||||
kunStructs, err := rf.kf.SliceFromBytes(in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var result []*Resource
|
||||
for len(kunStructs) > 0 {
|
||||
u := kunStructs[0]
|
||||
kunStructs = kunStructs[1:]
|
||||
if strings.HasSuffix(u.GetKind(), "List") {
|
||||
items := u.Map()["items"]
|
||||
itemsSlice, ok := items.([]interface{})
|
||||
if !ok {
|
||||
if items == nil {
|
||||
// an empty list
|
||||
continue
|
||||
}
|
||||
return nil, fmt.Errorf("items in List is type %T, expected array", items)
|
||||
}
|
||||
for _, item := range itemsSlice {
|
||||
itemJSON, err := json.Marshal(item)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
innerU, err := rf.kf.SliceFromBytes(itemJSON)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// append innerU to kunStructs so nested Lists can be handled
|
||||
kunStructs = append(kunStructs, innerU...)
|
||||
}
|
||||
} else {
|
||||
result = append(result, rf.FromKunstructured(u))
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// MakeConfigMap makes an instance of Resource for ConfigMap
|
||||
func (rf *Factory) MakeConfigMap(
|
||||
kvLdr ifc.KvLoader,
|
||||
options *types.GeneratorOptions,
|
||||
args *types.ConfigMapArgs) (*Resource, error) {
|
||||
u, err := rf.kf.MakeConfigMap(kvLdr, options, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rf.makeOne(
|
||||
u,
|
||||
types.NewGenArgs(
|
||||
&types.GeneratorArgs{Behavior: args.Behavior},
|
||||
options)), nil
|
||||
}
|
||||
|
||||
// MakeSecret makes an instance of Resource for Secret
|
||||
func (rf *Factory) MakeSecret(
|
||||
kvLdr ifc.KvLoader,
|
||||
options *types.GeneratorOptions,
|
||||
args *types.SecretArgs) (*Resource, error) {
|
||||
u, err := rf.kf.MakeSecret(kvLdr, options, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rf.makeOne(
|
||||
u,
|
||||
types.NewGenArgs(
|
||||
&types.GeneratorArgs{Behavior: args.Behavior},
|
||||
options)), nil
|
||||
}
|
||||
211
api/resource/factory_test.go
Normal file
211
api/resource/factory_test.go
Normal file
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
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_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
. "sigs.k8s.io/kustomize/v3/api/resource"
|
||||
"sigs.k8s.io/kustomize/v3/api/types"
|
||||
"sigs.k8s.io/kustomize/v3/internal/loadertest"
|
||||
)
|
||||
|
||||
func TestSliceFromPatches(t *testing.T) {
|
||||
|
||||
patchGood1 := types.PatchStrategicMerge("patch1.yaml")
|
||||
patch1 := `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: pooh
|
||||
`
|
||||
patchGood2 := types.PatchStrategicMerge("patch2.yaml")
|
||||
patch2 := `
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: winnie
|
||||
namespace: hundred-acre-wood
|
||||
---
|
||||
# some comment
|
||||
---
|
||||
---
|
||||
`
|
||||
patchBad := types.PatchStrategicMerge("patch3.yaml")
|
||||
patch3 := `
|
||||
WOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOT: woot
|
||||
`
|
||||
patchList := types.PatchStrategicMerge("patch4.yaml")
|
||||
patch4 := `
|
||||
apiVersion: v1
|
||||
kind: List
|
||||
items:
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: pooh
|
||||
- apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: winnie
|
||||
namespace: hundred-acre-wood
|
||||
`
|
||||
patchList2 := types.PatchStrategicMerge("patch5.yaml")
|
||||
patch5 := `
|
||||
apiVersion: v1
|
||||
kind: DeploymentList
|
||||
items:
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: deployment-a
|
||||
spec: &hostAliases
|
||||
template:
|
||||
spec:
|
||||
hostAliases:
|
||||
- hostnames:
|
||||
- a.example.com
|
||||
ip: 8.8.8.8
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: deployment-b
|
||||
spec:
|
||||
<<: *hostAliases
|
||||
`
|
||||
patchList3 := types.PatchStrategicMerge("patch6.yaml")
|
||||
patch6 := `
|
||||
apiVersion: v1
|
||||
kind: List
|
||||
items:
|
||||
`
|
||||
patchList4 := types.PatchStrategicMerge("patch7.yaml")
|
||||
patch7 := `
|
||||
apiVersion: v1
|
||||
kind: List
|
||||
`
|
||||
testDeploymentSpec := map[string]interface{}{
|
||||
"template": map[string]interface{}{
|
||||
"spec": map[string]interface{}{
|
||||
"hostAliases": []interface{}{
|
||||
map[string]interface{}{
|
||||
"hostnames": []interface{}{
|
||||
"a.example.com",
|
||||
},
|
||||
"ip": "8.8.8.8",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
testDeploymentA := factory.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "deployment-a",
|
||||
},
|
||||
"spec": testDeploymentSpec,
|
||||
})
|
||||
testDeploymentB := factory.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "deployment-b",
|
||||
},
|
||||
"spec": testDeploymentSpec,
|
||||
})
|
||||
l := loadertest.NewFakeLoader("/")
|
||||
l.AddFile("/"+string(patchGood1), []byte(patch1))
|
||||
l.AddFile("/"+string(patchGood2), []byte(patch2))
|
||||
l.AddFile("/"+string(patchBad), []byte(patch3))
|
||||
l.AddFile("/"+string(patchList), []byte(patch4))
|
||||
l.AddFile("/"+string(patchList2), []byte(patch5))
|
||||
l.AddFile("/"+string(patchList3), []byte(patch6))
|
||||
l.AddFile("/"+string(patchList4), []byte(patch7))
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input []types.PatchStrategicMerge
|
||||
expectedOut []*Resource
|
||||
expectedErr bool
|
||||
}{
|
||||
{
|
||||
name: "happy",
|
||||
input: []types.PatchStrategicMerge{patchGood1, patchGood2},
|
||||
expectedOut: []*Resource{testDeployment, testConfigMap},
|
||||
expectedErr: false,
|
||||
},
|
||||
{
|
||||
name: "badFileName",
|
||||
input: []types.PatchStrategicMerge{patchGood1, "doesNotExist"},
|
||||
expectedOut: []*Resource{},
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
name: "badData",
|
||||
input: []types.PatchStrategicMerge{patchGood1, patchBad},
|
||||
expectedOut: []*Resource{},
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
name: "listOfPatches",
|
||||
input: []types.PatchStrategicMerge{patchList},
|
||||
expectedOut: []*Resource{testDeployment, testConfigMap},
|
||||
expectedErr: false,
|
||||
},
|
||||
{
|
||||
name: "listWithAnchorReference",
|
||||
input: []types.PatchStrategicMerge{patchList2},
|
||||
expectedOut: []*Resource{testDeploymentA, testDeploymentB},
|
||||
expectedErr: false,
|
||||
},
|
||||
{
|
||||
name: "listWithNoEntries",
|
||||
input: []types.PatchStrategicMerge{patchList3},
|
||||
expectedOut: []*Resource{},
|
||||
expectedErr: false,
|
||||
},
|
||||
{
|
||||
name: "listWithNo'items:'",
|
||||
input: []types.PatchStrategicMerge{patchList4},
|
||||
expectedOut: []*Resource{},
|
||||
expectedErr: false,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
rs, err := factory.SliceFromPatches(l, test.input)
|
||||
if test.expectedErr && err == nil {
|
||||
t.Fatalf("%v: should return error", test.name)
|
||||
}
|
||||
if !test.expectedErr && err != nil {
|
||||
t.Fatalf("%v: unexpected error: %s", test.name, err)
|
||||
}
|
||||
if len(rs) != len(test.expectedOut) {
|
||||
t.Fatalf("%s: length mismatch %d != %d",
|
||||
test.name, len(rs), len(test.expectedOut))
|
||||
}
|
||||
for i := range rs {
|
||||
if !reflect.DeepEqual(test.expectedOut[i], rs[i]) {
|
||||
t.Fatalf("%s: Got: %v\nexpected:%v",
|
||||
test.name, test.expectedOut[i], rs[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
327
api/resource/resource.go
Normal file
327
api/resource/resource.go
Normal file
@@ -0,0 +1,327 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package resource implements representations of k8s API resources as "unstructured" objects.
|
||||
package resource
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/v3/api/ifc"
|
||||
"sigs.k8s.io/kustomize/v3/api/resid"
|
||||
"sigs.k8s.io/kustomize/v3/api/types"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
// Resource is map representation of a Kubernetes API resource object
|
||||
// paired with a GenerationBehavior.
|
||||
type Resource struct {
|
||||
ifc.Kunstructured
|
||||
originalName string
|
||||
originalNs string
|
||||
options *types.GenArgs
|
||||
refBy []resid.ResId
|
||||
refVarNames []string
|
||||
namePrefixes []string
|
||||
nameSuffixes []string
|
||||
}
|
||||
|
||||
// ResCtx is an interface describing the contextual added
|
||||
// kept kustomize in the context of each Resource object.
|
||||
// Currently mainly the name prefix and name suffix are added.
|
||||
type ResCtx interface {
|
||||
AddNamePrefix(p string)
|
||||
AddNameSuffix(s string)
|
||||
GetOutermostNamePrefix() string
|
||||
GetOutermostNameSuffix() string
|
||||
GetNamePrefixes() []string
|
||||
GetNameSuffixes() []string
|
||||
}
|
||||
|
||||
// ResCtxMatcher returns true if two Resources are being
|
||||
// modified in the same kustomize context.
|
||||
type ResCtxMatcher func(ResCtx) bool
|
||||
|
||||
// DeepCopy returns a new copy of resource
|
||||
func (r *Resource) DeepCopy() *Resource {
|
||||
rc := &Resource{
|
||||
Kunstructured: r.Kunstructured.Copy(),
|
||||
}
|
||||
rc.copyOtherFields(r)
|
||||
return rc
|
||||
}
|
||||
|
||||
// Replace performs replace with other resource.
|
||||
func (r *Resource) Replace(other *Resource) {
|
||||
r.SetLabels(mergeStringMaps(other.GetLabels(), r.GetLabels()))
|
||||
r.SetAnnotations(
|
||||
mergeStringMaps(other.GetAnnotations(), r.GetAnnotations()))
|
||||
r.SetName(other.GetName())
|
||||
r.SetNamespace(other.GetNamespace())
|
||||
r.copyOtherFields(other)
|
||||
}
|
||||
|
||||
func (r *Resource) copyOtherFields(other *Resource) {
|
||||
r.originalName = other.originalName
|
||||
r.originalNs = other.originalNs
|
||||
r.options = other.options
|
||||
r.refBy = other.copyRefBy()
|
||||
r.refVarNames = copyStringSlice(other.refVarNames)
|
||||
r.namePrefixes = copyStringSlice(other.namePrefixes)
|
||||
r.nameSuffixes = copyStringSlice(other.nameSuffixes)
|
||||
}
|
||||
|
||||
func (r *Resource) Equals(o *Resource) bool {
|
||||
return r.ReferencesEqual(o) &&
|
||||
reflect.DeepEqual(r.Kunstructured, o.Kunstructured)
|
||||
}
|
||||
|
||||
func (r *Resource) ReferencesEqual(o *Resource) bool {
|
||||
setSelf := make(map[resid.ResId]bool)
|
||||
setOther := make(map[resid.ResId]bool)
|
||||
for _, ref := range o.refBy {
|
||||
setOther[ref] = true
|
||||
}
|
||||
for _, ref := range r.refBy {
|
||||
if _, ok := setOther[ref]; !ok {
|
||||
return false
|
||||
}
|
||||
setSelf[ref] = true
|
||||
}
|
||||
return len(setSelf) == len(setOther)
|
||||
}
|
||||
|
||||
func (r *Resource) KunstructEqual(o *Resource) bool {
|
||||
return reflect.DeepEqual(r.Kunstructured, o.Kunstructured)
|
||||
}
|
||||
|
||||
// Merge performs merge with other resource.
|
||||
func (r *Resource) Merge(other *Resource) {
|
||||
r.Replace(other)
|
||||
mergeConfigmap(r.Map(), other.Map(), r.Map())
|
||||
}
|
||||
|
||||
func (r *Resource) copyRefBy() []resid.ResId {
|
||||
if r.refBy == nil {
|
||||
return nil
|
||||
}
|
||||
s := make([]resid.ResId, len(r.refBy))
|
||||
copy(s, r.refBy)
|
||||
return s
|
||||
}
|
||||
|
||||
func copyStringSlice(s []string) []string {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
c := make([]string, len(s))
|
||||
copy(c, s)
|
||||
return c
|
||||
}
|
||||
|
||||
// Implements ResCtx AddNamePrefix
|
||||
func (r *Resource) AddNamePrefix(p string) {
|
||||
r.namePrefixes = append(r.namePrefixes, p)
|
||||
}
|
||||
|
||||
// Implements ResCtx AddNameSuffix
|
||||
func (r *Resource) AddNameSuffix(s string) {
|
||||
r.nameSuffixes = append(r.nameSuffixes, s)
|
||||
}
|
||||
|
||||
// Implements ResCtx GetOutermostNamePrefix
|
||||
func (r *Resource) GetOutermostNamePrefix() string {
|
||||
if len(r.namePrefixes) == 0 {
|
||||
return ""
|
||||
}
|
||||
return r.namePrefixes[len(r.namePrefixes)-1]
|
||||
}
|
||||
|
||||
// Implements ResCtx GetOutermostNameSuffix
|
||||
func (r *Resource) GetOutermostNameSuffix() string {
|
||||
if len(r.nameSuffixes) == 0 {
|
||||
return ""
|
||||
}
|
||||
return r.nameSuffixes[len(r.nameSuffixes)-1]
|
||||
}
|
||||
|
||||
func sameEndingSubarray(a, b []string) bool {
|
||||
compareLen := len(b)
|
||||
if len(a) < len(b) {
|
||||
compareLen = len(a)
|
||||
}
|
||||
|
||||
if compareLen == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
alen := len(a) - 1
|
||||
blen := len(b) - 1
|
||||
for i := 0; i <= compareLen-1; i++ {
|
||||
if a[alen-i] != b[blen-i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Implements ResCtx GetNamePrefixes
|
||||
func (r *Resource) GetNamePrefixes() []string {
|
||||
return r.namePrefixes
|
||||
}
|
||||
|
||||
// Implements ResCtx GetNameSuffixes
|
||||
func (r *Resource) GetNameSuffixes() []string {
|
||||
return r.nameSuffixes
|
||||
}
|
||||
|
||||
// OutermostPrefixSuffixEquals returns true if both resources
|
||||
// outer suffix and prefix matches.
|
||||
func (r *Resource) OutermostPrefixSuffixEquals(o ResCtx) bool {
|
||||
return (r.GetOutermostNamePrefix() == o.GetOutermostNamePrefix()) && (r.GetOutermostNameSuffix() == o.GetOutermostNameSuffix())
|
||||
}
|
||||
|
||||
// PrefixesSuffixesEquals is conceptually doing the same task
|
||||
// as OutermostPrefixSuffix but performs a deeper comparison
|
||||
// of the suffix and prefix slices.
|
||||
//
|
||||
// Important note: The PrefixSuffixTransformer is stacking the
|
||||
// prefix values in the reverse order of appearance in
|
||||
// the transformed name. For this reason the sameEndingSubarray
|
||||
// method is used (as opposed to the sameBeginningSubarray)
|
||||
// to compare the prefix slice. In the same spirit, the
|
||||
// GetOutermostNamePrefix is using the last element of the
|
||||
// nameprefix slice and not the first.
|
||||
func (r *Resource) PrefixesSuffixesEquals(o ResCtx) bool {
|
||||
return sameEndingSubarray(r.GetNamePrefixes(), o.GetNamePrefixes()) && sameEndingSubarray(r.GetNameSuffixes(), o.GetNameSuffixes())
|
||||
}
|
||||
|
||||
// This is used to compute if a referrer could potentially be impacted
|
||||
// by the change of name of a referral.
|
||||
func (r *Resource) InSameKustomizeCtx(rctxm ResCtxMatcher) bool {
|
||||
return rctxm(r)
|
||||
}
|
||||
|
||||
func (r *Resource) GetOriginalName() string {
|
||||
return r.originalName
|
||||
}
|
||||
|
||||
// Making this public would be bad.
|
||||
func (r *Resource) setOriginalName(n string) *Resource {
|
||||
r.originalName = n
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *Resource) GetOriginalNs() string {
|
||||
return r.originalNs
|
||||
}
|
||||
|
||||
// Making this public would be bad.
|
||||
func (r *Resource) setOriginalNs(n string) *Resource {
|
||||
r.originalNs = n
|
||||
return r
|
||||
}
|
||||
|
||||
// String returns resource as JSON.
|
||||
func (r *Resource) String() string {
|
||||
bs, err := r.MarshalJSON()
|
||||
if err != nil {
|
||||
return "<" + err.Error() + ">"
|
||||
}
|
||||
return strings.TrimSpace(string(bs)) + r.options.String()
|
||||
}
|
||||
|
||||
// AsYAML returns the resource in Yaml form.
|
||||
// Easier to read than JSON.
|
||||
func (r *Resource) AsYAML() ([]byte, error) {
|
||||
json, err := r.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return yaml.JSONToYAML(json)
|
||||
}
|
||||
|
||||
// SetOptions updates the generator options for the resource.
|
||||
func (r *Resource) SetOptions(o *types.GenArgs) {
|
||||
r.options = o
|
||||
}
|
||||
|
||||
// Behavior returns the behavior for the resource.
|
||||
func (r *Resource) Behavior() types.GenerationBehavior {
|
||||
return r.options.Behavior()
|
||||
}
|
||||
|
||||
// NeedHashSuffix checks if the resource need a hash suffix
|
||||
func (r *Resource) NeedHashSuffix() bool {
|
||||
return r.options != nil && r.options.NeedsHashSuffix()
|
||||
}
|
||||
|
||||
// GetNamespace returns the namespace the resource thinks it's in.
|
||||
func (r *Resource) GetNamespace() string {
|
||||
namespace, _ := r.GetString("metadata.namespace")
|
||||
// if err, namespace is empty, so no need to check.
|
||||
return namespace
|
||||
}
|
||||
|
||||
// OrgId returns the original, immutable ResId for the resource.
|
||||
// This doesn't have to be unique in a ResMap.
|
||||
// TODO: compute this once and save it in the resource.
|
||||
func (r *Resource) OrgId() resid.ResId {
|
||||
return resid.NewResIdWithNamespace(
|
||||
r.GetGvk(), r.GetOriginalName(), r.GetOriginalNs())
|
||||
}
|
||||
|
||||
// CurId returns a ResId for the resource using the
|
||||
// mutable parts of the resource.
|
||||
// This should be unique in any ResMap.
|
||||
func (r *Resource) CurId() resid.ResId {
|
||||
return resid.NewResIdWithNamespace(
|
||||
r.GetGvk(), r.GetName(), r.GetNamespace())
|
||||
}
|
||||
|
||||
// GetRefBy returns the ResIds that referred to current resource
|
||||
func (r *Resource) GetRefBy() []resid.ResId {
|
||||
return r.refBy
|
||||
}
|
||||
|
||||
// AppendRefBy appends a ResId into the refBy list
|
||||
func (r *Resource) AppendRefBy(id resid.ResId) {
|
||||
r.refBy = append(r.refBy, id)
|
||||
}
|
||||
|
||||
// GetRefVarNames returns vars that refer to current resource
|
||||
func (r *Resource) GetRefVarNames() []string {
|
||||
return r.refVarNames
|
||||
}
|
||||
|
||||
// AppendRefVarName appends a name of a var into the refVar list
|
||||
func (r *Resource) AppendRefVarName(variable types.Var) {
|
||||
r.refVarNames = append(r.refVarNames, variable.Name)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
138
api/resource/resource_test.go
Normal file
138
api/resource/resource_test.go
Normal file
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
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_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"sigs.k8s.io/kustomize/v3/api/resid"
|
||||
. "sigs.k8s.io/kustomize/v3/api/resource"
|
||||
"sigs.k8s.io/kustomize/v3/api/types"
|
||||
"sigs.k8s.io/kustomize/v3/k8sdeps/kunstruct"
|
||||
)
|
||||
|
||||
var factory = NewFactory(
|
||||
kunstruct.NewKunstructuredFactoryImpl())
|
||||
|
||||
var testConfigMap = factory.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "winnie",
|
||||
"namespace": "hundred-acre-wood",
|
||||
},
|
||||
})
|
||||
|
||||
const genArgOptions = "{nsfx:false,beh:unspecified}"
|
||||
|
||||
const configMapAsString = `{"apiVersion":"v1","kind":"ConfigMap","metadata":{"name":"winnie","namespace":"hundred-acre-wood"}}`
|
||||
|
||||
var testDeployment = factory.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "pooh",
|
||||
},
|
||||
})
|
||||
|
||||
const deploymentAsString = `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"name":"pooh"}}`
|
||||
|
||||
func TestAsYAML(t *testing.T) {
|
||||
expected := `apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: pooh
|
||||
`
|
||||
yaml, err := testDeployment.AsYAML()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(yaml) != expected {
|
||||
t.Fatalf("--- expected\n%s\n--- got\n%s\n", expected, string(yaml))
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceString(t *testing.T) {
|
||||
tests := []struct {
|
||||
in *Resource
|
||||
s string
|
||||
}{
|
||||
{
|
||||
in: testConfigMap,
|
||||
s: configMapAsString + genArgOptions,
|
||||
},
|
||||
{
|
||||
in: testDeployment,
|
||||
s: deploymentAsString + genArgOptions,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
if test.in.String() != test.s {
|
||||
t.Fatalf("Expected %s == %s", test.in.String(), test.s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceId(t *testing.T) {
|
||||
tests := []struct {
|
||||
in *Resource
|
||||
id resid.ResId
|
||||
}{
|
||||
{
|
||||
in: testConfigMap,
|
||||
id: resid.NewResIdWithNamespace(
|
||||
resid.Gvk{Version: "v1", Kind: "ConfigMap"}, "winnie", "hundred-acre-wood"),
|
||||
},
|
||||
{
|
||||
in: testDeployment,
|
||||
id: resid.NewResId(resid.Gvk{Group: "apps", Version: "v1", Kind: "Deployment"}, "pooh"),
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
if test.in.OrgId() != test.id {
|
||||
t.Fatalf("Expected %v, but got %v\n", test.id, test.in.OrgId())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeepCopy(t *testing.T) {
|
||||
r := factory.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "pooh",
|
||||
},
|
||||
})
|
||||
r.AppendRefBy(resid.NewResId(resid.Gvk{Group: "somegroup", Kind: "MyKind"}, "random"))
|
||||
|
||||
var1 := types.Var{
|
||||
Name: "SERVICE_ONE",
|
||||
ObjRef: types.Target{
|
||||
Gvk: resid.Gvk{Version: "v1", Kind: "Service"},
|
||||
Name: "backendOne"},
|
||||
}
|
||||
r.AppendRefVarName(var1)
|
||||
|
||||
cr := r.DeepCopy()
|
||||
if !reflect.DeepEqual(r, cr) {
|
||||
t.Errorf("expected %v\nbut got%v", r, cr)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user