mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-07-17 17:52:12 +00:00
Merge pull request #2075 from seans3/kustomize-apply-deps
Adds inventory hash to grouping object.
This commit is contained in:
@@ -4,6 +4,7 @@ go 1.13
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/spf13/cobra v0.0.5
|
github.com/spf13/cobra v0.0.5
|
||||||
|
k8s.io/api v0.17.0
|
||||||
k8s.io/apimachinery v0.17.0
|
k8s.io/apimachinery v0.17.0
|
||||||
k8s.io/cli-runtime v0.17.0
|
k8s.io/cli-runtime v0.17.0
|
||||||
k8s.io/client-go v0.17.0
|
k8s.io/client-go v0.17.0
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ package kubectlcobra
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"hash/fnv"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/api/meta"
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
@@ -13,7 +16,10 @@ import (
|
|||||||
"k8s.io/cli-runtime/pkg/resource"
|
"k8s.io/cli-runtime/pkg/resource"
|
||||||
)
|
)
|
||||||
|
|
||||||
const GroupingLabel = "kustomize.k8s.io/group-id"
|
const (
|
||||||
|
GroupingLabel = "kustomize.config.k8s.io/inventory-id"
|
||||||
|
GroupingHash = "kustomize.config.k8s.io/inventory-hash"
|
||||||
|
)
|
||||||
|
|
||||||
// isGroupingObject returns true if the passed object has the
|
// isGroupingObject returns true if the passed object has the
|
||||||
// grouping label.
|
// grouping label.
|
||||||
@@ -102,11 +108,30 @@ func addInventoryToGroupingObj(infos []*resource.Info) error {
|
|||||||
if groupingObj == nil {
|
if groupingObj == nil {
|
||||||
return fmt.Errorf("Grouping object not found")
|
return fmt.Errorf("Grouping object not found")
|
||||||
}
|
}
|
||||||
err := unstructured.SetNestedStringMap(groupingObj.UnstructuredContent(), inventoryMap, "data")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if len(inventoryMap) > 0 {
|
||||||
|
// Adds the inventory map to the ConfigMap "data" section.
|
||||||
|
err := unstructured.SetNestedStringMap(groupingObj.UnstructuredContent(),
|
||||||
|
inventoryMap, "data")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Adds the hash of the inventory strings as an annotation to the
|
||||||
|
// grouping object. Inventory strings must be sorted to make hash
|
||||||
|
// deterministic.
|
||||||
|
inventoryList := mapKeysToSlice(inventoryMap)
|
||||||
|
sort.Strings(inventoryList)
|
||||||
|
invHash, err := calcInventoryHash(inventoryList)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
annotations := groupingObj.GetAnnotations()
|
||||||
|
if annotations == nil {
|
||||||
|
annotations = map[string]string{}
|
||||||
|
}
|
||||||
|
annotations[GroupingHash] = strconv.FormatUint(uint64(invHash), 16)
|
||||||
|
groupingObj.SetAnnotations(annotations)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,3 +168,46 @@ func retrieveInventoryFromGroupingObj(infos []*resource.Info) ([]*Inventory, err
|
|||||||
}
|
}
|
||||||
return inventory, nil
|
return inventory, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// calcInventoryHash returns an unsigned int32 representing the hash
|
||||||
|
// of the inventory strings. If there is an error writing bytes to
|
||||||
|
// the hash, then the error is returned; nil is returned otherwise.
|
||||||
|
// Used to quickly identify the set of resources in the grouping object.
|
||||||
|
func calcInventoryHash(inv []string) (uint32, error) {
|
||||||
|
h := fnv.New32a()
|
||||||
|
for _, is := range inv {
|
||||||
|
_, err := h.Write([]byte(is))
|
||||||
|
if err != nil {
|
||||||
|
return uint32(0), err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return h.Sum32(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// retrieveInventoryHash takes a grouping object (encapsulated by
|
||||||
|
// a resource.Info), and returns the string representing the hash
|
||||||
|
// of the grouping inventory; returns empty string if the grouping
|
||||||
|
// object is not in Unstructured format, or if the hash annotation
|
||||||
|
// does not exist.
|
||||||
|
func retrieveInventoryHash(groupingInfo *resource.Info) string {
|
||||||
|
var invHash = ""
|
||||||
|
groupingObj, ok := groupingInfo.Object.(*unstructured.Unstructured)
|
||||||
|
if ok {
|
||||||
|
annotations := groupingObj.GetAnnotations()
|
||||||
|
if annotations != nil {
|
||||||
|
invHash = annotations[GroupingHash]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return invHash
|
||||||
|
}
|
||||||
|
|
||||||
|
// mapKeysToSlice returns the map keys as a slice of strings.
|
||||||
|
func mapKeysToSlice(m map[string]string) []string {
|
||||||
|
s := make([]string, len(m))
|
||||||
|
i := 0
|
||||||
|
for k := range m {
|
||||||
|
s[i] = k
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ package kubectlcobra
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
@@ -90,6 +92,28 @@ var pod3Info = &resource.Info{
|
|||||||
Object: &pod3,
|
Object: &pod3,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var nonUnstructuredGroupingObj = &corev1.ConfigMap{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Namespace: testNamespace,
|
||||||
|
Name: groupingObjName,
|
||||||
|
Labels: map[string]string{
|
||||||
|
GroupingLabel: "true",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var nonUnstructuredGroupingInfo = &resource.Info{
|
||||||
|
Namespace: testNamespace,
|
||||||
|
Name: groupingObjName,
|
||||||
|
Object: nonUnstructuredGroupingObj,
|
||||||
|
}
|
||||||
|
|
||||||
|
var nilInfo = &resource.Info{
|
||||||
|
Namespace: testNamespace,
|
||||||
|
Name: groupingObjName,
|
||||||
|
Object: nil,
|
||||||
|
}
|
||||||
|
|
||||||
func TestIsGroupingObject(t *testing.T) {
|
func TestIsGroupingObject(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
obj runtime.Object
|
obj runtime.Object
|
||||||
@@ -249,6 +273,14 @@ func TestAddRetrieveInventoryToFromGroupingObject(t *testing.T) {
|
|||||||
isError: true,
|
isError: true,
|
||||||
},
|
},
|
||||||
// Grouping object without other objects is OK.
|
// Grouping object without other objects is OK.
|
||||||
|
{
|
||||||
|
infos: []*resource.Info{groupingInfo, nilInfo},
|
||||||
|
isError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
infos: []*resource.Info{nonUnstructuredGroupingInfo},
|
||||||
|
isError: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
infos: []*resource.Info{groupingInfo},
|
infos: []*resource.Info{groupingInfo},
|
||||||
expected: []*Inventory{},
|
expected: []*Inventory{},
|
||||||
@@ -266,6 +298,7 @@ func TestAddRetrieveInventoryToFromGroupingObject(t *testing.T) {
|
|||||||
expected: []*Inventory{},
|
expected: []*Inventory{},
|
||||||
isError: true,
|
isError: true,
|
||||||
},
|
},
|
||||||
|
// Basic test case: one grouping object, one pod.
|
||||||
{
|
{
|
||||||
infos: []*resource.Info{groupingInfo, pod1Info},
|
infos: []*resource.Info{groupingInfo, pod1Info},
|
||||||
expected: []*Inventory{
|
expected: []*Inventory{
|
||||||
@@ -414,6 +447,15 @@ func TestAddRetrieveInventoryToFromGroupingObject(t *testing.T) {
|
|||||||
t.Errorf("Expected inventory (%s) not found", expected)
|
t.Errorf("Expected inventory (%s) not found", expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// If the grouping object has an inventory, check the
|
||||||
|
// grouping object has an inventory hash.
|
||||||
|
groupingInfo, exists := findGroupingObject(test.infos)
|
||||||
|
if exists && len(test.expected) > 0 {
|
||||||
|
invHash := retrieveInventoryHash(groupingInfo)
|
||||||
|
if len(invHash) == 0 {
|
||||||
|
t.Errorf("Grouping object missing inventory hash")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user