mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-07-01 02:11:20 +00:00
Remove the wrappy layer.
This commit is contained in:
@@ -10,28 +10,33 @@ import (
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/ifc"
|
||||
"sigs.k8s.io/kustomize/api/internal/generators"
|
||||
"sigs.k8s.io/kustomize/api/internal/kusterr"
|
||||
"sigs.k8s.io/kustomize/api/konfig"
|
||||
"sigs.k8s.io/kustomize/api/resid"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// Factory makes instances of Resource.
|
||||
type Factory struct {
|
||||
kf ifc.KunstructuredFactory
|
||||
hasher ifc.KunstructuredHasher
|
||||
}
|
||||
|
||||
// NewFactory makes an instance of Factory.
|
||||
func NewFactory(kf ifc.KunstructuredFactory) *Factory {
|
||||
return &Factory{kf: kf}
|
||||
func NewFactory(h ifc.KunstructuredHasher) *Factory {
|
||||
return &Factory{hasher: h}
|
||||
}
|
||||
|
||||
// Hasher returns an ifc.KunstructuredHasher
|
||||
func (rf *Factory) Hasher() ifc.KunstructuredHasher {
|
||||
return rf.kf.Hasher()
|
||||
return rf.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)
|
||||
return rf.FromMapAndOption(m, nil)
|
||||
}
|
||||
|
||||
// FromMapWithName returns a new instance with the given "original" name.
|
||||
@@ -41,35 +46,30 @@ func (rf *Factory) FromMapWithName(n string, m map[string]interface{}) *Resource
|
||||
|
||||
// FromMapWithNamespaceAndName returns a new instance with the given "original" namespace.
|
||||
func (rf *Factory) FromMapWithNamespaceAndName(ns string, n string, m map[string]interface{}) *Resource {
|
||||
r := rf.makeOne(rf.kf.FromMap(m), nil)
|
||||
r := rf.FromMapAndOption(m, nil)
|
||||
return r.setPreviousId(ns, n, r.GetKind())
|
||||
}
|
||||
|
||||
// FromMapAndOption returns a new instance of Resource with given options.
|
||||
func (rf *Factory) FromMapAndOption(
|
||||
m map[string]interface{}, args *types.GeneratorArgs) *Resource {
|
||||
return rf.makeOne(rf.kf.FromMap(m), types.NewGenArgs(args))
|
||||
}
|
||||
|
||||
// FromKunstructured returns a new instance of Resource.
|
||||
func (rf *Factory) FromKunstructured(u ifc.Kunstructured) *Resource {
|
||||
return rf.makeOne(u, nil)
|
||||
n, err := yaml.FromMap(m)
|
||||
if err != nil {
|
||||
// TODO: return err instead of log.
|
||||
log.Fatal(err)
|
||||
}
|
||||
return rf.makeOne(n, types.NewGenArgs(args))
|
||||
}
|
||||
|
||||
// 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")
|
||||
func (rf *Factory) makeOne(rn *yaml.RNode, o *types.GenArgs) *Resource {
|
||||
if rn == nil {
|
||||
log.Fatal("RNode must not be null")
|
||||
}
|
||||
if o == nil {
|
||||
o = types.NewGenArgs(nil)
|
||||
}
|
||||
r := &Resource{
|
||||
kunStr: u,
|
||||
options: o,
|
||||
}
|
||||
return r
|
||||
return &Resource{kunStr: rn, options: o}
|
||||
}
|
||||
|
||||
// SliceFromPatches returns a slice of resources given a patch path
|
||||
@@ -106,47 +106,135 @@ func (rf *Factory) FromBytes(in []byte) (*Resource, error) {
|
||||
|
||||
// SliceFromBytes unmarshals bytes into a Resource slice.
|
||||
func (rf *Factory) SliceFromBytes(in []byte) ([]*Resource, error) {
|
||||
kunStructs, err := rf.kf.SliceFromBytes(in)
|
||||
nodes, err := rf.RNodesFromBytes(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") {
|
||||
m, err := u.Map()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return rf.resourcesFromRNodes(nodes), nil
|
||||
}
|
||||
|
||||
// ResourcesFromRNodes converts RNodes to Resources.
|
||||
func (rf *Factory) ResourcesFromRNodes(
|
||||
nodes []*yaml.RNode) (result []*Resource, err error) {
|
||||
nodes, err = rf.dropBadNodes(nodes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rf.resourcesFromRNodes(nodes), nil
|
||||
}
|
||||
|
||||
// resourcesFromRNode assumes all nodes are good.
|
||||
func (rf *Factory) resourcesFromRNodes(
|
||||
nodes []*yaml.RNode) (result []*Resource) {
|
||||
for _, n := range nodes {
|
||||
result = append(result, rf.makeOne(n, nil))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (rf *Factory) RNodesFromBytes(b []byte) (result []*yaml.RNode, err error) {
|
||||
nodes, err := kio.FromBytes(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nodes, err = rf.dropBadNodes(nodes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for len(nodes) > 0 {
|
||||
n0 := nodes[0]
|
||||
nodes = nodes[1:]
|
||||
kind := n0.GetKind()
|
||||
if !strings.HasSuffix(kind, "List") {
|
||||
result = append(result, n0)
|
||||
continue
|
||||
}
|
||||
// Convert a FooList into a slice of Foo.
|
||||
var m map[string]interface{}
|
||||
m, err = n0.Map()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items, ok := m["items"]
|
||||
if !ok {
|
||||
// treat as an empty list
|
||||
continue
|
||||
}
|
||||
slice, ok := items.([]interface{})
|
||||
if !ok {
|
||||
if items == nil {
|
||||
// an empty list
|
||||
continue
|
||||
}
|
||||
items := m["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 nil, fmt.Errorf(
|
||||
"expected array in %s/items, but found %T", kind, items)
|
||||
}
|
||||
innerNodes, err := rf.convertObjectSliceToNodeSlice(slice)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nodes = append(nodes, innerNodes...)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// convertObjectSlice converts a list of objects to a list of RNode.
|
||||
func (rf *Factory) convertObjectSliceToNodeSlice(
|
||||
objects []interface{}) (result []*yaml.RNode, err error) {
|
||||
var bytes []byte
|
||||
var nodes []*yaml.RNode
|
||||
for _, obj := range objects {
|
||||
bytes, err = json.Marshal(obj)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
nodes, err = kio.FromBytes(bytes)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
nodes, err = rf.dropBadNodes(nodes)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
result = append(result, nodes...)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// dropBadNodes may drop some nodes from its input argument.
|
||||
func (rf *Factory) dropBadNodes(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
var result []*yaml.RNode
|
||||
for _, n := range nodes {
|
||||
ignore, err := rf.shouldIgnore(n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !ignore {
|
||||
result = append(result, n)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// shouldIgnore returns true if there's some reason to ignore the node.
|
||||
func (rf *Factory) shouldIgnore(n *yaml.RNode) (bool, error) {
|
||||
if n.IsNilOrEmpty() {
|
||||
return true, nil
|
||||
}
|
||||
md, err := n.GetValidatedMetadata()
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
_, ignore := md.ObjectMeta.Annotations[konfig.IgnoredByKustomizeAnnotation]
|
||||
if ignore {
|
||||
return true, nil
|
||||
}
|
||||
if foundNil, path := n.HasNilEntryInList(); foundNil {
|
||||
return true, fmt.Errorf("empty item at %v in object %v", path, n)
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// SliceFromBytesWithNames unmarshals bytes into a Resource slice with specified original
|
||||
// name.
|
||||
func (rf *Factory) SliceFromBytesWithNames(names []string, in []byte) ([]*Resource, error) {
|
||||
@@ -165,18 +253,18 @@ func (rf *Factory) SliceFromBytesWithNames(names []string, in []byte) ([]*Resour
|
||||
|
||||
// MakeConfigMap makes an instance of Resource for ConfigMap
|
||||
func (rf *Factory) MakeConfigMap(kvLdr ifc.KvLoader, args *types.ConfigMapArgs) (*Resource, error) {
|
||||
u, err := rf.kf.MakeConfigMap(kvLdr, args)
|
||||
rn, err := generators.MakeConfigMap(kvLdr, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rf.makeOne(u, types.NewGenArgs(&args.GeneratorArgs)), nil
|
||||
return rf.makeOne(rn, types.NewGenArgs(&args.GeneratorArgs)), nil
|
||||
}
|
||||
|
||||
// MakeSecret makes an instance of Resource for Secret
|
||||
func (rf *Factory) MakeSecret(kvLdr ifc.KvLoader, args *types.SecretArgs) (*Resource, error) {
|
||||
u, err := rf.kf.MakeSecret(kvLdr, args)
|
||||
rn, err := generators.MakeSecret(kvLdr, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rf.makeOne(u, types.NewGenArgs(&args.GeneratorArgs)), nil
|
||||
return rf.makeOne(rn, types.NewGenArgs(&args.GeneratorArgs)), nil
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ package resource_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -151,35 +152,33 @@ spec:
|
||||
for name := range testCases {
|
||||
tc := testCases[name]
|
||||
t.Run(name, func(t *testing.T) {
|
||||
result, err := factory.SliceFromBytes([]byte(tc.input))
|
||||
result, err := factory.RNodesFromBytes([]byte(tc.input))
|
||||
if err != nil {
|
||||
t.Fatalf("%v: fails with err: %v", name, err)
|
||||
}
|
||||
if len(result) != len(tc.expected) {
|
||||
for i := range result {
|
||||
bytes, err := result[i].AsYAML()
|
||||
str, err := result[i].String()
|
||||
if err != nil {
|
||||
t.Fatalf("%v: result to YAML fails with err: %v", name, err)
|
||||
}
|
||||
tmp := string(bytes)
|
||||
t.Logf("--- %d:\n%s", i, tmp)
|
||||
t.Logf("--- %d:\n%s", i, str)
|
||||
}
|
||||
t.Fatalf(
|
||||
"%v: actual len %d != expected len %d",
|
||||
name, len(result), len(tc.expected))
|
||||
}
|
||||
for i := range tc.expected {
|
||||
bytes, err := result[i].AsYAML()
|
||||
str, err := result[i].String()
|
||||
if err != nil {
|
||||
t.Fatalf("%v: result to YAML fails with err: %v", name, err)
|
||||
}
|
||||
tmp := string(bytes)
|
||||
if tmp != tc.expected[i] {
|
||||
if str != tc.expected[i] {
|
||||
t.Fatalf(
|
||||
"%v: string mismatch in item %d\n"+
|
||||
"actual:\n-----\n%s\n-----\n"+
|
||||
"expected:\n-----\n%s\n-----\n",
|
||||
name, i, tmp, tc.expected[i])
|
||||
name, i, str, tc.expected[i])
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -307,38 +306,32 @@ kind: List
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
tests := map[string]struct {
|
||||
input []types.PatchStrategicMerge
|
||||
expectedOut []*Resource
|
||||
expectedErr bool
|
||||
}{
|
||||
{
|
||||
name: "happy",
|
||||
"happy": {
|
||||
input: []types.PatchStrategicMerge{patchGood1, patchGood2},
|
||||
expectedOut: []*Resource{testDeployment, testConfigMap},
|
||||
expectedErr: false,
|
||||
},
|
||||
{
|
||||
name: "badFileName",
|
||||
"badFileName": {
|
||||
input: []types.PatchStrategicMerge{patchGood1, "doesNotExist"},
|
||||
expectedOut: []*Resource{},
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
name: "badData",
|
||||
"badData": {
|
||||
input: []types.PatchStrategicMerge{patchGood1, patchBad},
|
||||
expectedOut: []*Resource{},
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
name: "listOfPatches",
|
||||
"listOfPatches": {
|
||||
input: []types.PatchStrategicMerge{patchList},
|
||||
expectedOut: []*Resource{testDeployment, testConfigMap},
|
||||
expectedErr: false,
|
||||
},
|
||||
{
|
||||
name: "listWithAnchorReference",
|
||||
"listWithAnchorReference": {
|
||||
input: []types.PatchStrategicMerge{patchList2},
|
||||
expectedOut: []*Resource{testDeploymentA, testDeploymentB},
|
||||
// The error using kyaml is:
|
||||
@@ -349,34 +342,340 @@ kind: List
|
||||
// TODO(#3271) This shouldn't have an error, but does when kyaml is used.
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
name: "listWithNoEntries",
|
||||
"listWithNoEntries": {
|
||||
input: []types.PatchStrategicMerge{patchList3},
|
||||
expectedOut: []*Resource{},
|
||||
expectedErr: false,
|
||||
},
|
||||
{
|
||||
name: "listWithNoItems",
|
||||
"listWithNoItems": {
|
||||
input: []types.PatchStrategicMerge{patchList4},
|
||||
expectedOut: []*Resource{},
|
||||
expectedErr: false,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
rs, err := factory.SliceFromPatches(ldr, test.input)
|
||||
if err != nil {
|
||||
assert.True(t, test.expectedErr,
|
||||
fmt.Sprintf("in test %s, got unexpected error: %v", test.name, err))
|
||||
continue
|
||||
}
|
||||
assert.False(t, test.expectedErr, "expected no error in "+test.name)
|
||||
assert.Equal(t, len(test.expectedOut), len(rs))
|
||||
for i := range rs {
|
||||
expYaml, err := test.expectedOut[i].AsYAML()
|
||||
assert.NoError(t, err)
|
||||
actYaml, err := rs[i].AsYAML()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expYaml, actYaml)
|
||||
}
|
||||
for n, test := range tests {
|
||||
t.Run(n, func(t *testing.T) {
|
||||
rs, err := factory.SliceFromPatches(ldr, test.input)
|
||||
if err != nil {
|
||||
assert.True(t, test.expectedErr,
|
||||
fmt.Sprintf("in test %s, got unexpected error: %v", n, err))
|
||||
return
|
||||
}
|
||||
assert.False(t, test.expectedErr, "expected no error in "+n)
|
||||
assert.Equal(t, len(test.expectedOut), len(rs))
|
||||
for i := range rs {
|
||||
expYaml, err := test.expectedOut[i].AsYAML()
|
||||
assert.NoError(t, err)
|
||||
actYaml, err := rs[i].AsYAML()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expYaml, actYaml)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHash(t *testing.T) {
|
||||
input := `
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: foo
|
||||
data:
|
||||
one: ""
|
||||
binaryData:
|
||||
two: ""
|
||||
`
|
||||
expect := "698h7c7t9m"
|
||||
k, err := factory.SliceFromBytes([]byte(input))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
result, err := k[0].Hash(factory.Hasher())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if result != expect {
|
||||
t.Fatalf("expect %s but got %s", expect, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSliceFromBytesMore(t *testing.T) {
|
||||
testConfigMap :=
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "winnie",
|
||||
},
|
||||
}
|
||||
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 := map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "deployment-a",
|
||||
},
|
||||
"spec": testDeploymentSpec,
|
||||
}
|
||||
testDeploymentB := map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "deployment-b",
|
||||
},
|
||||
"spec": testDeploymentSpec,
|
||||
}
|
||||
testDeploymentList :=
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "DeploymentList",
|
||||
"items": []interface{}{
|
||||
testDeploymentA,
|
||||
testDeploymentB,
|
||||
},
|
||||
}
|
||||
|
||||
type expected struct {
|
||||
out []map[string]interface{}
|
||||
isErr bool
|
||||
}
|
||||
|
||||
testCases := map[string]struct {
|
||||
input []byte
|
||||
exp expected
|
||||
}{
|
||||
"garbage": {
|
||||
input: []byte("garbageIn: garbageOut"),
|
||||
exp: expected{
|
||||
isErr: true,
|
||||
},
|
||||
},
|
||||
"noBytes": {
|
||||
input: []byte{},
|
||||
exp: expected{
|
||||
out: []map[string]interface{}{},
|
||||
},
|
||||
},
|
||||
"goodJson": {
|
||||
input: []byte(`
|
||||
{"apiVersion":"v1","kind":"ConfigMap","metadata":{"name":"winnie"}}
|
||||
`),
|
||||
exp: expected{
|
||||
out: []map[string]interface{}{testConfigMap},
|
||||
},
|
||||
},
|
||||
"goodYaml1": {
|
||||
input: []byte(`
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: winnie
|
||||
`),
|
||||
exp: expected{
|
||||
out: []map[string]interface{}{testConfigMap},
|
||||
},
|
||||
},
|
||||
"goodYaml2": {
|
||||
input: []byte(`
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: winnie
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: winnie
|
||||
`),
|
||||
exp: expected{
|
||||
out: []map[string]interface{}{testConfigMap, testConfigMap},
|
||||
},
|
||||
},
|
||||
"localConfigYaml": {
|
||||
input: []byte(`
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: winnie-skip
|
||||
annotations:
|
||||
# this annotation causes the Resource to be ignored by kustomize
|
||||
config.kubernetes.io/local-config: ""
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: winnie
|
||||
`),
|
||||
exp: expected{
|
||||
out: []map[string]interface{}{testConfigMap},
|
||||
},
|
||||
},
|
||||
"garbageInOneOfTwoObjects": {
|
||||
input: []byte(`
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: winnie
|
||||
---
|
||||
WOOOOOOOOOOOOOOOOOOOOOOOOT: woot
|
||||
`),
|
||||
exp: expected{
|
||||
isErr: true,
|
||||
},
|
||||
},
|
||||
"emptyObjects": {
|
||||
input: []byte(`
|
||||
---
|
||||
#a comment
|
||||
|
||||
---
|
||||
|
||||
`),
|
||||
exp: expected{
|
||||
out: []map[string]interface{}{},
|
||||
},
|
||||
},
|
||||
"Missing .metadata.name in object": {
|
||||
input: []byte(`
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
annotations:
|
||||
foo: bar
|
||||
`),
|
||||
exp: expected{
|
||||
isErr: true,
|
||||
},
|
||||
},
|
||||
"nil value in list": {
|
||||
input: []byte(`
|
||||
apiVersion: builtin
|
||||
kind: ConfigMapGenerator
|
||||
metadata:
|
||||
name: kube100-site
|
||||
labels:
|
||||
app: web
|
||||
testList:
|
||||
- testA
|
||||
-
|
||||
`),
|
||||
exp: expected{
|
||||
isErr: true,
|
||||
},
|
||||
},
|
||||
"List": {
|
||||
input: []byte(`
|
||||
apiVersion: v1
|
||||
kind: List
|
||||
items:
|
||||
- apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: winnie
|
||||
- apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: winnie
|
||||
`),
|
||||
exp: expected{
|
||||
out: []map[string]interface{}{
|
||||
testConfigMap,
|
||||
testConfigMap},
|
||||
},
|
||||
},
|
||||
"ConfigMapList": {
|
||||
input: []byte(`
|
||||
apiVersion: v1
|
||||
kind: ConfigMapList
|
||||
items:
|
||||
- apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: winnie
|
||||
- apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: winnie
|
||||
`),
|
||||
exp: expected{
|
||||
out: []map[string]interface{}{
|
||||
testConfigMap,
|
||||
testConfigMap,
|
||||
},
|
||||
},
|
||||
},
|
||||
"listWithAnchors": {
|
||||
input: []byte(`
|
||||
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
|
||||
`),
|
||||
exp: expected{
|
||||
// TODO(3271): This should work.
|
||||
// https://github.com/kubernetes-sigs/kustomize/issues/3271
|
||||
// json.Marshal(obj) fails on the 2nd list item.
|
||||
// The value of the 1st list item's first spec field is
|
||||
// map[string]interface{}
|
||||
// The value of the 2nd list item's first spec field is
|
||||
// map[interface{}]interface{}
|
||||
// which causes a encoding/json.UnsupportedTypeError.
|
||||
isErr: true,
|
||||
out: []map[string]interface{}{testDeploymentList},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for n := range testCases {
|
||||
tc := testCases[n]
|
||||
t.Run(n, func(t *testing.T) {
|
||||
rs, err := factory.RNodesFromBytes(tc.input)
|
||||
if err != nil {
|
||||
assert.True(t, tc.exp.isErr)
|
||||
return
|
||||
}
|
||||
assert.False(t, tc.exp.isErr)
|
||||
assert.Equal(t, len(tc.exp.out), len(rs))
|
||||
for i := range rs {
|
||||
rsMap, err := rs[i].Map()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(
|
||||
t, fmt.Sprintf("%v", tc.exp.out[i]), fmt.Sprintf("%v", rsMap))
|
||||
m, _ := rs[i].Map()
|
||||
if !reflect.DeepEqual(tc.exp.out[i], m) {
|
||||
t.Fatalf("%s:\nexpected: %v\n actual: %v",
|
||||
n, tc.exp.out[i], m)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
|
||||
"sigs.k8s.io/kustomize/api/filters/patchstrategicmerge"
|
||||
"sigs.k8s.io/kustomize/api/ifc"
|
||||
"sigs.k8s.io/kustomize/api/internal/wrappy"
|
||||
"sigs.k8s.io/kustomize/api/konfig"
|
||||
"sigs.k8s.io/kustomize/api/resid"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
@@ -26,7 +25,7 @@ import (
|
||||
// paired with metadata used by kustomize.
|
||||
// For more history, see sigs.k8s.io/kustomize/api/ifc.Unstructured
|
||||
type Resource struct {
|
||||
kunStr ifc.Kunstructured
|
||||
kunStr *kyaml.RNode
|
||||
options *types.GenArgs
|
||||
refBy []resid.ResId
|
||||
refVarNames []string
|
||||
@@ -55,23 +54,24 @@ var buildAnnotations = []string{
|
||||
buildAnnotationAllowKindChange,
|
||||
}
|
||||
|
||||
func (r *Resource) AsRNode() *kyaml.RNode {
|
||||
return r.kunStr.Copy()
|
||||
}
|
||||
|
||||
func (r *Resource) ResetPrimaryData(incoming *Resource) {
|
||||
r.kunStr = incoming.Copy()
|
||||
r.kunStr = incoming.kunStr.Copy()
|
||||
}
|
||||
|
||||
func (r *Resource) GetAnnotations() map[string]string {
|
||||
annotations := r.kunStr.GetAnnotations()
|
||||
if annotations == nil {
|
||||
annotations, err := r.kunStr.GetAnnotations()
|
||||
if err != nil || annotations == nil {
|
||||
return make(map[string]string)
|
||||
}
|
||||
return annotations
|
||||
}
|
||||
|
||||
func (r *Resource) Copy() ifc.Kunstructured {
|
||||
return r.kunStr.Copy()
|
||||
}
|
||||
|
||||
func (r *Resource) GetFieldValue(f string) (interface{}, error) {
|
||||
//nolint:staticcheck
|
||||
return r.kunStr.GetFieldValue(f)
|
||||
}
|
||||
|
||||
@@ -84,7 +84,16 @@ func (r *Resource) GetBinaryDataMap() map[string]string {
|
||||
}
|
||||
|
||||
func (r *Resource) GetGvk() resid.Gvk {
|
||||
return r.kunStr.GetGvk()
|
||||
meta, err := r.kunStr.GetMeta()
|
||||
if err != nil {
|
||||
return resid.GvkFromString("")
|
||||
}
|
||||
g, v := resid.ParseGroupVersion(meta.APIVersion)
|
||||
return resid.Gvk{Group: g, Version: v, Kind: meta.Kind}
|
||||
}
|
||||
|
||||
func (r *Resource) Hash(h ifc.KunstructuredHasher) (string, error) {
|
||||
return h.Hash(r.kunStr)
|
||||
}
|
||||
|
||||
func (r *Resource) GetKind() string {
|
||||
@@ -92,7 +101,11 @@ func (r *Resource) GetKind() string {
|
||||
}
|
||||
|
||||
func (r *Resource) GetLabels() map[string]string {
|
||||
return r.kunStr.GetLabels()
|
||||
l, err := r.kunStr.GetLabels()
|
||||
if err != nil {
|
||||
return map[string]string{}
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func (r *Resource) GetName() string {
|
||||
@@ -100,16 +113,17 @@ func (r *Resource) GetName() string {
|
||||
}
|
||||
|
||||
func (r *Resource) GetSlice(p string) ([]interface{}, error) {
|
||||
//nolint:staticcheck
|
||||
return r.kunStr.GetSlice(p)
|
||||
}
|
||||
|
||||
func (r *Resource) GetString(p string) (string, error) {
|
||||
//nolint:staticcheck
|
||||
return r.kunStr.GetString(p)
|
||||
}
|
||||
|
||||
func (r *Resource) IsEmpty() (bool, error) {
|
||||
m, err := r.kunStr.Map()
|
||||
return len(m) == 0, err
|
||||
func (r *Resource) IsEmpty() bool {
|
||||
return r.kunStr.IsNilOrEmpty()
|
||||
}
|
||||
|
||||
func (r *Resource) Map() (map[string]interface{}, error) {
|
||||
@@ -146,7 +160,10 @@ func (r *Resource) SetBinaryDataMap(m map[string]string) {
|
||||
}
|
||||
|
||||
func (r *Resource) SetGvk(gvk resid.Gvk) {
|
||||
r.kunStr.SetGvk(gvk)
|
||||
r.kunStr.SetMapField(
|
||||
kyaml.NewScalarRNode(gvk.Kind), kyaml.KindField)
|
||||
r.kunStr.SetMapField(
|
||||
kyaml.NewScalarRNode(gvk.ApiVersion()), kyaml.APIVersionField)
|
||||
}
|
||||
|
||||
func (r *Resource) SetLabels(m map[string]string) {
|
||||
@@ -193,7 +210,7 @@ type ResCtxMatcher func(ResCtx) bool
|
||||
// DeepCopy returns a new copy of resource
|
||||
func (r *Resource) DeepCopy() *Resource {
|
||||
rc := &Resource{
|
||||
kunStr: r.Copy(),
|
||||
kunStr: r.kunStr.Copy(),
|
||||
}
|
||||
rc.copyOtherFields(r)
|
||||
return rc
|
||||
@@ -541,11 +558,7 @@ func (r *Resource) ApplySmPatch(patch *Resource) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
empty, err := r.IsEmpty()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if empty {
|
||||
if r.IsEmpty() {
|
||||
return nil
|
||||
}
|
||||
if !patch.KindChangeAllowed() {
|
||||
@@ -559,15 +572,12 @@ func (r *Resource) ApplySmPatch(patch *Resource) error {
|
||||
}
|
||||
|
||||
func (r *Resource) ApplyFilter(f kio.Filter) error {
|
||||
if wn, ok := r.kunStr.(*wrappy.WNode); ok {
|
||||
l, err := f.Filter([]*kyaml.RNode{wn.AsRNode()})
|
||||
if len(l) == 0 {
|
||||
// Hack to deal with deletion.
|
||||
r.kunStr = wrappy.NewWNode()
|
||||
}
|
||||
return err
|
||||
l, err := f.Filter([]*kyaml.RNode{r.kunStr})
|
||||
if len(l) == 0 {
|
||||
// The node was deleted. The following makes r.IsEmpty() true.
|
||||
r.kunStr = nil
|
||||
}
|
||||
return filtersutil.ApplyToJSON(f, r)
|
||||
return err
|
||||
}
|
||||
|
||||
func mergeStringMaps(maps ...map[string]string) map[string]string {
|
||||
|
||||
@@ -695,7 +695,7 @@ spec:
|
||||
}
|
||||
}
|
||||
|
||||
func TestResource_StorePreviousId(t *testing.T) {
|
||||
func TestResourceStorePreviousId(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
input string
|
||||
newName string
|
||||
@@ -1079,3 +1079,54 @@ func TestSameEndingSubarray(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetGvk(t *testing.T) {
|
||||
r, err := factory.FromBytes([]byte(`
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: clown
|
||||
spec:
|
||||
numReplicas: 1
|
||||
`))
|
||||
assert.NoError(t, err)
|
||||
|
||||
gvk := r.GetGvk()
|
||||
expected := "apps"
|
||||
actual := gvk.Group
|
||||
if expected != actual {
|
||||
t.Fatalf("expected '%s', got '%s'", expected, actual)
|
||||
}
|
||||
expected = "v1"
|
||||
actual = gvk.Version
|
||||
if expected != actual {
|
||||
t.Fatalf("expected '%s', got '%s'", expected, actual)
|
||||
}
|
||||
expected = "Deployment"
|
||||
actual = gvk.Kind
|
||||
if expected != actual {
|
||||
t.Fatalf("expected '%s', got '%s'", expected, actual)
|
||||
}
|
||||
}
|
||||
func TestSetGvk(t *testing.T) {
|
||||
r, err := factory.FromBytes([]byte(`
|
||||
apiVersion: v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: clown
|
||||
spec:
|
||||
numReplicas: 1
|
||||
`))
|
||||
assert.NoError(t, err)
|
||||
r.SetGvk(resid.GvkFromString("grp_ver_knd"))
|
||||
gvk := r.GetGvk()
|
||||
if expected, actual := "grp", gvk.Group; expected != actual {
|
||||
t.Fatalf("expected '%s', got '%s'", expected, actual)
|
||||
}
|
||||
if expected, actual := "ver", gvk.Version; expected != actual {
|
||||
t.Fatalf("expected '%s', got '%s'", expected, actual)
|
||||
}
|
||||
if expected, actual := "knd", gvk.Kind; expected != actual {
|
||||
t.Fatalf("expected '%s', got '%s'", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user