List of strategic merge patches (#637)

* support List of strategic merge patches

* add test for List of patches

* handle List in SliceFromBytes

* add test for List of patches with anchor/reference

* reorganize kunstruct validate
This commit is contained in:
hyww
2018-12-21 02:08:18 +08:00
committed by Jingfang Liu
parent 900152f724
commit 4b25963c93
4 changed files with 144 additions and 5 deletions

View File

@@ -101,12 +101,16 @@ func (kf *KunstructuredFactoryImpl) Set(fs fs.FileSystem, ldr ifc.Loader) {
}
// validate validates that u has kind and name
// except for kind `List`, which doesn't require a name
func (kf *KunstructuredFactoryImpl) validate(u unstructured.Unstructured) error {
kind := u.GetKind()
if kind == "" {
return fmt.Errorf("missing kind in object %v", u)
} else if kind == "List" {
return nil
}
if u.GetName() == "" {
return fmt.Errorf("missing metadata.name in object %v", u)
}
if u.GetKind() == "" {
return fmt.Errorf("missing kind in object %v", u)
}
return nil
}

View File

@@ -33,6 +33,15 @@ func TestSliceFromBytes(t *testing.T) {
"name": "winnie",
},
})
testList := factory.FromMap(
map[string]interface{}{
"apiVersion": "v1",
"kind": "List",
"items": []interface{}{
testConfigMap.Map(),
testConfigMap.Map(),
},
})
tests := []struct {
name string
@@ -112,6 +121,24 @@ metadata:
expectedOut: nil,
expectedErr: true,
},
{
name: "List",
input: []byte(`
apiVersion: v1
kind: List
items:
- apiVersion: v1
kind: ConfigMap
metadata:
name: winnie
- apiVersion: v1
kind: ConfigMap
metadata:
name: winnie
`),
expectedOut: []ifc.Kunstructured{testList},
expectedErr: false,
},
}
for _, test := range tests {

View File

@@ -17,6 +17,8 @@ limitations under the License.
package resource
import (
"encoding/json"
"fmt"
"log"
"sigs.k8s.io/kustomize/pkg/fs"
@@ -78,8 +80,30 @@ func (rf *Factory) SliceFromBytes(in []byte) ([]*Resource, error) {
return nil, err
}
var result []*Resource
for _, u := range kunStructs {
result = append(result, rf.FromKunstructured(u))
for len(kunStructs) > 0 {
u := kunStructs[0]
kunStructs = kunStructs[1:]
if u.GetKind() == "List" {
items := u.Map()["items"]
itemsSlice, ok := items.([]interface{})
if !ok {
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
}

View File

@@ -49,10 +49,82 @@ metadata:
patch3 := `
WOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOT: woot
`
patchList := patch.StrategicMerge("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 := patch.StrategicMerge("patch5.yaml")
patch5 := `
apiVersion: v1
kind: List
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
`
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))
tests := []struct {
name string
@@ -78,6 +150,18 @@ WOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOT: woot
expectedOut: []*Resource{},
expectedErr: true,
},
{
name: "listOfPatches",
input: []patch.StrategicMerge{patchList},
expectedOut: []*Resource{testDeployment, testConfigMap},
expectedErr: false,
},
{
name: "listWithAnchorReference",
input: []patch.StrategicMerge{patchList2},
expectedOut: []*Resource{testDeploymentA, testDeploymentB},
expectedErr: false,
},
}
for _, test := range tests {
rs, err := factory.SliceFromPatches(l, test.input)