mirror of https://github.com/fluxcd/cli-utils.git
Capture the apply status for individual resources after each apply.
If the inventory object supports status field, it is updated after setting the inventory list at then of one apply process. BREAKING CHANGE: Update the inventory client and inventory interfaces to pass the apply/reconcile status.
This commit is contained in:
parent
b450b79991
commit
6bd4b565e8
|
|
@ -126,7 +126,7 @@ func createInventoryInfo(children ...*unstructured.Unstructured) inventory.Info
|
|||
inventoryObjCopy := inventoryObj.DeepCopy()
|
||||
wrappedInv := inventory.WrapInventoryObj(inventoryObjCopy)
|
||||
objs := object.UnstructuredSetToObjMetadataSet(children)
|
||||
if err := wrappedInv.Store(objs); err != nil {
|
||||
if err := wrappedInv.Store(objs, nil); err != nil {
|
||||
return nil
|
||||
}
|
||||
obj, err := wrappedInv.GetObject()
|
||||
|
|
|
|||
|
|
@ -114,8 +114,11 @@ func (i *InvSetTask) Start(taskContext *taskrunner.TaskContext) {
|
|||
klog.V(4).Infof("keep in inventory %d invalid objects", len(invalidObjects))
|
||||
invObjs = invObjs.Union(invalidObjects)
|
||||
|
||||
klog.V(4).Infof("get the apply status for %d objects", len(invObjs))
|
||||
objStatus := taskContext.InventoryManager().Inventory().Status.Objects
|
||||
|
||||
klog.V(4).Infof("set inventory %d total objects", len(invObjs))
|
||||
err := i.InvClient.Replace(i.InvInfo, invObjs, i.DryRun)
|
||||
err := i.InvClient.Replace(i.InvInfo, invObjs, objStatus, i.DryRun)
|
||||
|
||||
klog.V(2).Infof("inventory set task completing (name: %q)", i.Name())
|
||||
taskContext.TaskChannel() <- taskrunner.TaskResult{Err: err}
|
||||
|
|
|
|||
|
|
@ -6,14 +6,16 @@ package inventory
|
|||
import (
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||
"sigs.k8s.io/cli-utils/pkg/apis/actuation"
|
||||
"sigs.k8s.io/cli-utils/pkg/common"
|
||||
"sigs.k8s.io/cli-utils/pkg/object"
|
||||
)
|
||||
|
||||
// FakeClient is a testing implementation of the Client interface.
|
||||
type FakeClient struct {
|
||||
Objs object.ObjMetadataSet
|
||||
Err error
|
||||
Objs object.ObjMetadataSet
|
||||
Status []actuation.ObjectStatus
|
||||
Err error
|
||||
}
|
||||
|
||||
var (
|
||||
|
|
@ -57,12 +59,13 @@ func (fic *FakeClient) Merge(_ Info, objs object.ObjMetadataSet, _ common.DryRun
|
|||
|
||||
// Replace the stored cluster inventory objs with the passed obj, or an
|
||||
// error if one is set up.
|
||||
|
||||
func (fic *FakeClient) Replace(_ Info, objs object.ObjMetadataSet, _ common.DryRunStrategy) error {
|
||||
func (fic *FakeClient) Replace(_ Info, objs object.ObjMetadataSet, status []actuation.ObjectStatus,
|
||||
_ common.DryRunStrategy) error {
|
||||
if fic.Err != nil {
|
||||
return fic.Err
|
||||
}
|
||||
fic.Objs = objs
|
||||
fic.Status = status
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import (
|
|||
"k8s.io/klog/v2"
|
||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||
"k8s.io/kubectl/pkg/util"
|
||||
"sigs.k8s.io/cli-utils/pkg/apis/actuation"
|
||||
"sigs.k8s.io/cli-utils/pkg/common"
|
||||
"sigs.k8s.io/cli-utils/pkg/object"
|
||||
)
|
||||
|
|
@ -34,7 +35,7 @@ type Client interface {
|
|||
Merge(inv Info, objs object.ObjMetadataSet, dryRun common.DryRunStrategy) (object.ObjMetadataSet, error)
|
||||
// Replace replaces the set of objects stored in the inventory
|
||||
// object with the passed set of objects, or an error if one occurs.
|
||||
Replace(inv Info, objs object.ObjMetadataSet, dryRun common.DryRunStrategy) error
|
||||
Replace(inv Info, objs object.ObjMetadataSet, status []actuation.ObjectStatus, dryRun common.DryRunStrategy) error
|
||||
// DeleteInventoryObj deletes the passed inventory object from the APIServer.
|
||||
DeleteInventoryObj(inv Info, dryRun common.DryRunStrategy) error
|
||||
// ApplyInventoryNamespace applies the Namespace that the inventory object should be in.
|
||||
|
|
@ -100,8 +101,9 @@ func (cic *ClusterClient) Merge(localInv Info, objs object.ObjMetadataSet, dryRu
|
|||
}
|
||||
if clusterInv == nil {
|
||||
// Wrap inventory object and store the inventory in it.
|
||||
status := getObjStatus(nil, objs)
|
||||
inv := cic.InventoryFactoryFunc(invObj)
|
||||
if err := inv.Store(objs); err != nil {
|
||||
if err := inv.Store(objs, status); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
invInfo, err := inv.GetObject()
|
||||
|
|
@ -126,10 +128,11 @@ func (cic *ClusterClient) Merge(localInv Info, objs object.ObjMetadataSet, dryRu
|
|||
}
|
||||
pruneIds = clusterObjs.Diff(objs)
|
||||
unionObjs := clusterObjs.Union(objs)
|
||||
status := getObjStatus(pruneIds, unionObjs)
|
||||
klog.V(4).Infof("num objects to prune: %d", len(pruneIds))
|
||||
klog.V(4).Infof("num merged objects to store in inventory: %d", len(unionObjs))
|
||||
wrappedInv := cic.InventoryFactoryFunc(clusterInv)
|
||||
if err = wrappedInv.Store(unionObjs); err != nil {
|
||||
if err = wrappedInv.Store(unionObjs, status); err != nil {
|
||||
return pruneIds, err
|
||||
}
|
||||
clusterInv, err = wrappedInv.GetObject()
|
||||
|
|
@ -158,7 +161,8 @@ func (cic *ClusterClient) Merge(localInv Info, objs object.ObjMetadataSet, dryRu
|
|||
|
||||
// Replace stores the passed objects in the cluster inventory object, or
|
||||
// an error if one occurred.
|
||||
func (cic *ClusterClient) Replace(localInv Info, objs object.ObjMetadataSet, dryRun common.DryRunStrategy) error {
|
||||
func (cic *ClusterClient) Replace(localInv Info, objs object.ObjMetadataSet, status []actuation.ObjectStatus,
|
||||
dryRun common.DryRunStrategy) error {
|
||||
// Skip entire function for dry-run.
|
||||
if dryRun.ClientOrServerDryRun() {
|
||||
klog.V(4).Infoln("dry-run replace inventory object: not applied")
|
||||
|
|
@ -172,7 +176,7 @@ func (cic *ClusterClient) Replace(localInv Info, objs object.ObjMetadataSet, dry
|
|||
if err != nil {
|
||||
return fmt.Errorf("failed to read inventory from cluster: %w", err)
|
||||
}
|
||||
clusterInv, err = cic.replaceInventory(clusterInv, objs)
|
||||
clusterInv, err = cic.replaceInventory(clusterInv, objs, status)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -193,9 +197,10 @@ func (cic *ClusterClient) Replace(localInv Info, objs object.ObjMetadataSet, dry
|
|||
}
|
||||
|
||||
// replaceInventory stores the passed objects into the passed inventory object.
|
||||
func (cic *ClusterClient) replaceInventory(inv *unstructured.Unstructured, objs object.ObjMetadataSet) (*unstructured.Unstructured, error) {
|
||||
func (cic *ClusterClient) replaceInventory(inv *unstructured.Unstructured, objs object.ObjMetadataSet,
|
||||
status []actuation.ObjectStatus) (*unstructured.Unstructured, error) {
|
||||
wrappedInv := cic.InventoryFactoryFunc(inv)
|
||||
if err := wrappedInv.Store(objs); err != nil {
|
||||
if err := wrappedInv.Store(objs, status); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clusterInv, err := wrappedInv.GetObject()
|
||||
|
|
@ -493,3 +498,28 @@ func (cic *ClusterClient) hasSubResource(groupVersion, resource, subresource str
|
|||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// getObjStatus returns the list of object status
|
||||
// at the beginning of an apply process.
|
||||
func getObjStatus(pruneIds, unionIds []object.ObjMetadata) []actuation.ObjectStatus {
|
||||
status := []actuation.ObjectStatus{}
|
||||
for _, obj := range unionIds {
|
||||
status = append(status,
|
||||
actuation.ObjectStatus{
|
||||
ObjectReference: ObjectReferenceFromObjMetadata(obj),
|
||||
Strategy: actuation.ActuationStrategyApply,
|
||||
Actuation: actuation.ActuationPending,
|
||||
Reconcile: actuation.ReconcilePending,
|
||||
})
|
||||
}
|
||||
for _, obj := range pruneIds {
|
||||
status = append(status,
|
||||
actuation.ObjectStatus{
|
||||
ObjectReference: ObjectReferenceFromObjMetadata(obj),
|
||||
Strategy: actuation.ActuationStrategyDelete,
|
||||
Actuation: actuation.ActuationPending,
|
||||
Reconcile: actuation.ReconcilePending,
|
||||
})
|
||||
}
|
||||
return status
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,10 @@
|
|||
package inventory
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
|
@ -13,14 +15,31 @@ import (
|
|||
"k8s.io/cli-runtime/pkg/resource"
|
||||
clienttesting "k8s.io/client-go/testing"
|
||||
cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
|
||||
"sigs.k8s.io/cli-utils/pkg/apis/actuation"
|
||||
"sigs.k8s.io/cli-utils/pkg/common"
|
||||
"sigs.k8s.io/cli-utils/pkg/object"
|
||||
)
|
||||
|
||||
func podStatus(info *resource.Info) actuation.ObjectStatus {
|
||||
return actuation.ObjectStatus{
|
||||
ObjectReference: ObjectReferenceFromObjMetadata(ignoreErrInfoToObjMeta(info)),
|
||||
Strategy: actuation.ActuationStrategyApply,
|
||||
Actuation: actuation.ActuationSucceeded,
|
||||
Reconcile: actuation.ReconcileSucceeded,
|
||||
}
|
||||
}
|
||||
|
||||
func podData(name string) map[string]string {
|
||||
return map[string]string{
|
||||
fmt.Sprintf("test-inventory-namespace_%s__Pod", name): "{\"actuation\":\"Succeeded\",\"reconcile\":\"Succeeded\",\"strategy\":\"Apply\"}",
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetClusterInventoryInfo(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
inv Info
|
||||
localObjs object.ObjMetadataSet
|
||||
objStatus []actuation.ObjectStatus
|
||||
isError bool
|
||||
}{
|
||||
"Nil local inventory object is an error": {
|
||||
|
|
@ -38,7 +57,8 @@ func TestGetClusterInventoryInfo(t *testing.T) {
|
|||
localObjs: object.ObjMetadataSet{
|
||||
ignoreErrInfoToObjMeta(pod2Info),
|
||||
},
|
||||
isError: false,
|
||||
objStatus: []actuation.ObjectStatus{podStatus(pod2Info)},
|
||||
isError: false,
|
||||
},
|
||||
"Local inventory with multiple objects": {
|
||||
inv: localInv,
|
||||
|
|
@ -46,6 +66,11 @@ func TestGetClusterInventoryInfo(t *testing.T) {
|
|||
ignoreErrInfoToObjMeta(pod1Info),
|
||||
ignoreErrInfoToObjMeta(pod2Info),
|
||||
ignoreErrInfoToObjMeta(pod3Info)},
|
||||
objStatus: []actuation.ObjectStatus{
|
||||
podStatus(pod1Info),
|
||||
podStatus(pod2Info),
|
||||
podStatus(pod3Info),
|
||||
},
|
||||
isError: false,
|
||||
},
|
||||
}
|
||||
|
|
@ -61,7 +86,7 @@ func TestGetClusterInventoryInfo(t *testing.T) {
|
|||
|
||||
var inv *unstructured.Unstructured
|
||||
if tc.inv != nil {
|
||||
inv = storeObjsInInventory(tc.inv, tc.localObjs)
|
||||
inv = storeObjsInInventory(tc.inv, tc.localObjs, tc.objStatus)
|
||||
}
|
||||
clusterInv, err := invClient.GetClusterInventoryInfo(WrapInventoryInfoObj(inv))
|
||||
if tc.isError {
|
||||
|
|
@ -188,6 +213,7 @@ func TestCreateInventory(t *testing.T) {
|
|||
inv Info
|
||||
localObjs object.ObjMetadataSet
|
||||
error string
|
||||
objStatus []actuation.ObjectStatus
|
||||
}{
|
||||
"Nil local inventory object is an error": {
|
||||
inv: nil,
|
||||
|
|
@ -203,6 +229,7 @@ func TestCreateInventory(t *testing.T) {
|
|||
localObjs: object.ObjMetadataSet{
|
||||
ignoreErrInfoToObjMeta(pod2Info),
|
||||
},
|
||||
objStatus: []actuation.ObjectStatus{podStatus(pod2Info)},
|
||||
},
|
||||
"Local inventory with multiple objects": {
|
||||
inv: localInv,
|
||||
|
|
@ -210,6 +237,11 @@ func TestCreateInventory(t *testing.T) {
|
|||
ignoreErrInfoToObjMeta(pod1Info),
|
||||
ignoreErrInfoToObjMeta(pod2Info),
|
||||
ignoreErrInfoToObjMeta(pod3Info)},
|
||||
objStatus: []actuation.ObjectStatus{
|
||||
podStatus(pod1Info),
|
||||
podStatus(pod2Info),
|
||||
podStatus(pod3Info),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -230,7 +262,7 @@ func TestCreateInventory(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
inv := invClient.invToUnstructuredFunc(tc.inv)
|
||||
if inv != nil {
|
||||
inv = storeObjsInInventory(tc.inv, tc.localObjs)
|
||||
inv = storeObjsInInventory(tc.inv, tc.localObjs, tc.objStatus)
|
||||
}
|
||||
_, err = invClient.createInventoryObj(inv, common.DryRunNone)
|
||||
if tc.error != "" {
|
||||
|
|
@ -242,7 +274,11 @@ func TestCreateInventory(t *testing.T) {
|
|||
expectedInventory := tc.localObjs.ToStringMap()
|
||||
// handle empty inventories special to avoid problems with empty vs nil maps
|
||||
if len(expectedInventory) != 0 || len(storedInventory) != 0 {
|
||||
assert.Equal(t, expectedInventory, storedInventory)
|
||||
for key := range expectedInventory {
|
||||
if _, found := storedInventory[key]; !found {
|
||||
t.Errorf("%s not found in the stored inventory", key)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -252,10 +288,13 @@ func TestReplace(t *testing.T) {
|
|||
tests := map[string]struct {
|
||||
localObjs object.ObjMetadataSet
|
||||
clusterObjs object.ObjMetadataSet
|
||||
objStatus []actuation.ObjectStatus
|
||||
data map[string]string
|
||||
}{
|
||||
"Cluster and local inventories empty": {
|
||||
localObjs: object.ObjMetadataSet{},
|
||||
clusterObjs: object.ObjMetadataSet{},
|
||||
data: map[string]string{},
|
||||
},
|
||||
"Cluster and local inventories same": {
|
||||
localObjs: object.ObjMetadataSet{
|
||||
|
|
@ -264,6 +303,8 @@ func TestReplace(t *testing.T) {
|
|||
clusterObjs: object.ObjMetadataSet{
|
||||
ignoreErrInfoToObjMeta(pod1Info),
|
||||
},
|
||||
objStatus: []actuation.ObjectStatus{podStatus(pod1Info)},
|
||||
data: podData("pod-1"),
|
||||
},
|
||||
"Cluster two obj, local one": {
|
||||
localObjs: object.ObjMetadataSet{
|
||||
|
|
@ -273,6 +314,8 @@ func TestReplace(t *testing.T) {
|
|||
ignoreErrInfoToObjMeta(pod1Info),
|
||||
ignoreErrInfoToObjMeta(pod3Info),
|
||||
},
|
||||
objStatus: []actuation.ObjectStatus{podStatus(pod1Info), podStatus(pod3Info)},
|
||||
data: podData("pod-1"),
|
||||
},
|
||||
"Cluster multiple objs, local multiple different objs": {
|
||||
localObjs: object.ObjMetadataSet{
|
||||
|
|
@ -282,6 +325,8 @@ func TestReplace(t *testing.T) {
|
|||
ignoreErrInfoToObjMeta(pod1Info),
|
||||
ignoreErrInfoToObjMeta(pod2Info),
|
||||
ignoreErrInfoToObjMeta(pod3Info)},
|
||||
objStatus: []actuation.ObjectStatus{podStatus(pod2Info), podStatus(pod1Info), podStatus(pod3Info)},
|
||||
data: podData("pod-2"),
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -291,11 +336,11 @@ func TestReplace(t *testing.T) {
|
|||
// Client and server dry-run do not throw errors.
|
||||
invClient, err := NewClient(tf, WrapInventoryObj, InvInfoToConfigMap)
|
||||
require.NoError(t, err)
|
||||
err = invClient.Replace(copyInventory(), object.ObjMetadataSet{}, common.DryRunClient)
|
||||
err = invClient.Replace(copyInventory(), object.ObjMetadataSet{}, nil, common.DryRunClient)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error received: %s", err)
|
||||
}
|
||||
err = invClient.Replace(copyInventory(), object.ObjMetadataSet{}, common.DryRunServer)
|
||||
err = invClient.Replace(copyInventory(), object.ObjMetadataSet{}, nil, common.DryRunServer)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error received: %s", err)
|
||||
}
|
||||
|
|
@ -307,7 +352,7 @@ func TestReplace(t *testing.T) {
|
|||
WrapInventoryObj, InvInfoToConfigMap)
|
||||
require.NoError(t, err)
|
||||
wrappedInv := invClient.InventoryFactoryFunc(inventoryObj)
|
||||
if err := wrappedInv.Store(tc.clusterObjs); err != nil {
|
||||
if err := wrappedInv.Store(tc.clusterObjs, tc.objStatus); err != nil {
|
||||
t.Fatalf("unexpected error storing inventory objects: %s", err)
|
||||
}
|
||||
inv, err := wrappedInv.GetObject()
|
||||
|
|
@ -315,7 +360,7 @@ func TestReplace(t *testing.T) {
|
|||
t.Fatalf("unexpected error storing inventory objects: %s", err)
|
||||
}
|
||||
// Call replaceInventory with the new set of "localObjs"
|
||||
inv, err = invClient.replaceInventory(inv, tc.localObjs)
|
||||
inv, err = invClient.replaceInventory(inv, tc.localObjs, tc.objStatus)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error received: %s", err)
|
||||
}
|
||||
|
|
@ -328,6 +373,13 @@ func TestReplace(t *testing.T) {
|
|||
if !tc.localObjs.Equal(actualObjs) {
|
||||
t.Errorf("expected objects (%s), got (%s)", tc.localObjs, actualObjs)
|
||||
}
|
||||
data, _, err := unstructured.NestedStringMap(inv.Object, "data")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error received: %s", err)
|
||||
}
|
||||
if diff := cmp.Diff(data, tc.data); diff != "" {
|
||||
t.Fatalf(diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -390,6 +442,7 @@ func TestDeleteInventoryObj(t *testing.T) {
|
|||
tests := map[string]struct {
|
||||
inv Info
|
||||
localObjs object.ObjMetadataSet
|
||||
objStatus []actuation.ObjectStatus
|
||||
}{
|
||||
"Nil local inventory object is an error": {
|
||||
inv: nil,
|
||||
|
|
@ -404,6 +457,7 @@ func TestDeleteInventoryObj(t *testing.T) {
|
|||
localObjs: object.ObjMetadataSet{
|
||||
ignoreErrInfoToObjMeta(pod2Info),
|
||||
},
|
||||
objStatus: []actuation.ObjectStatus{podStatus(pod2Info)},
|
||||
},
|
||||
"Local inventory with multiple objects": {
|
||||
inv: localInv,
|
||||
|
|
@ -411,6 +465,11 @@ func TestDeleteInventoryObj(t *testing.T) {
|
|||
ignoreErrInfoToObjMeta(pod1Info),
|
||||
ignoreErrInfoToObjMeta(pod2Info),
|
||||
ignoreErrInfoToObjMeta(pod3Info)},
|
||||
objStatus: []actuation.ObjectStatus{
|
||||
podStatus(pod1Info),
|
||||
podStatus(pod2Info),
|
||||
podStatus(pod3Info),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -426,7 +485,7 @@ func TestDeleteInventoryObj(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
inv := invClient.invToUnstructuredFunc(tc.inv)
|
||||
if inv != nil {
|
||||
inv = storeObjsInInventory(tc.inv, tc.localObjs)
|
||||
inv = storeObjsInInventory(tc.inv, tc.localObjs, tc.objStatus)
|
||||
}
|
||||
err = invClient.deleteInventoryObjByName(inv, drs)
|
||||
if err != nil {
|
||||
|
|
@ -454,3 +513,10 @@ func toReactionFunc(objs object.ObjMetadataSet) clienttesting.ReactionFunc {
|
|||
return true, list, err
|
||||
}
|
||||
}
|
||||
|
||||
func storeObjsInInventory(info Info, objs object.ObjMetadataSet, status []actuation.ObjectStatus) *unstructured.Unstructured {
|
||||
wrapped := WrapInventoryObj(InvInfoToConfigMap(info))
|
||||
_ = wrapped.Store(objs, status)
|
||||
inv, _ := wrapped.GetObject()
|
||||
return inv
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import (
|
|||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/klog/v2"
|
||||
"sigs.k8s.io/cli-utils/pkg/apis/actuation"
|
||||
"sigs.k8s.io/cli-utils/pkg/common"
|
||||
"sigs.k8s.io/cli-utils/pkg/object"
|
||||
)
|
||||
|
|
@ -31,7 +32,7 @@ type Storage interface {
|
|||
// Load retrieves the set of object metadata from the inventory object
|
||||
Load() (object.ObjMetadataSet, error)
|
||||
// Store the set of object metadata in the inventory object
|
||||
Store(objs object.ObjMetadataSet) error
|
||||
Store(objs object.ObjMetadataSet, status []actuation.ObjectStatus) error
|
||||
// GetObject returns the object that stores the inventory
|
||||
GetObject() (*unstructured.Unstructured, error)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -420,10 +420,3 @@ func copyInventory() Info {
|
|||
u := inventoryObj.DeepCopy()
|
||||
return WrapInventoryInfoObj(u)
|
||||
}
|
||||
|
||||
func storeObjsInInventory(info Info, objs object.ObjMetadataSet) *unstructured.Unstructured {
|
||||
wrapped := WrapInventoryObj(InvInfoToConfigMap(info))
|
||||
_ = wrapped.Store(objs)
|
||||
inv, _ := wrapped.GetObject()
|
||||
return inv
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,9 +9,11 @@
|
|||
package inventory
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"sigs.k8s.io/cli-utils/pkg/apis/actuation"
|
||||
"sigs.k8s.io/cli-utils/pkg/common"
|
||||
"sigs.k8s.io/cli-utils/pkg/object"
|
||||
)
|
||||
|
|
@ -42,8 +44,9 @@ func InvInfoToConfigMap(inv Info) *unstructured.Unstructured {
|
|||
// the Inventory interface. This wrapper loads and stores the
|
||||
// object metadata (inventory) to and from the wrapped ConfigMap.
|
||||
type ConfigMap struct {
|
||||
inv *unstructured.Unstructured
|
||||
objMetas object.ObjMetadataSet
|
||||
inv *unstructured.Unstructured
|
||||
objMetas object.ObjMetadataSet
|
||||
objStatus []actuation.ObjectStatus
|
||||
}
|
||||
|
||||
var _ Info = &ConfigMap{}
|
||||
|
|
@ -94,8 +97,9 @@ func (icm *ConfigMap) Load() (object.ObjMetadataSet, error) {
|
|||
// Store is an Inventory interface function implemented to store
|
||||
// the object metadata in the wrapped ConfigMap. Actual storing
|
||||
// happens in "GetObject".
|
||||
func (icm *ConfigMap) Store(objMetas object.ObjMetadataSet) error {
|
||||
func (icm *ConfigMap) Store(objMetas object.ObjMetadataSet, status []actuation.ObjectStatus) error {
|
||||
icm.objMetas = objMetas
|
||||
icm.objStatus = status
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -103,11 +107,14 @@ func (icm *ConfigMap) Store(objMetas object.ObjMetadataSet) error {
|
|||
// or an error if one occurs.
|
||||
func (icm *ConfigMap) GetObject() (*unstructured.Unstructured, error) {
|
||||
// Create the objMap of all the resources, and compute the hash.
|
||||
objMap := buildObjMap(icm.objMetas)
|
||||
objMap, err := buildObjMap(icm.objMetas, icm.objStatus)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Create the inventory object by copying the template.
|
||||
invCopy := icm.inv.DeepCopy()
|
||||
// Adds the inventory map to the ConfigMap "data" section.
|
||||
err := unstructured.SetNestedStringMap(invCopy.UnstructuredContent(),
|
||||
err = unstructured.SetNestedStringMap(invCopy.UnstructuredContent(),
|
||||
objMap, "data")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -115,10 +122,33 @@ func (icm *ConfigMap) GetObject() (*unstructured.Unstructured, error) {
|
|||
return invCopy, nil
|
||||
}
|
||||
|
||||
func buildObjMap(objMetas object.ObjMetadataSet) map[string]string {
|
||||
func buildObjMap(objMetas object.ObjMetadataSet, objStatus []actuation.ObjectStatus) (map[string]string, error) {
|
||||
objMap := map[string]string{}
|
||||
for _, objMetadata := range objMetas {
|
||||
objMap[objMetadata.String()] = ""
|
||||
objStatusMap := map[object.ObjMetadata]actuation.ObjectStatus{}
|
||||
for _, status := range objStatus {
|
||||
objStatusMap[ObjMetadataFromObjectReference(status.ObjectReference)] = status
|
||||
}
|
||||
return objMap
|
||||
for _, objMetadata := range objMetas {
|
||||
if status, found := objStatusMap[objMetadata]; found {
|
||||
objMap[objMetadata.String()] = stringFrom(status)
|
||||
} else {
|
||||
// This should never happen since the objStatus have captured all the
|
||||
// object status
|
||||
return nil, fmt.Errorf("object status not found for %s", objMetadata)
|
||||
}
|
||||
}
|
||||
return objMap, nil
|
||||
}
|
||||
|
||||
func stringFrom(status actuation.ObjectStatus) string {
|
||||
tmp := map[string]string{
|
||||
"strategy": status.Strategy.String(),
|
||||
"actuation": status.Actuation.String(),
|
||||
"reconcile": status.Reconcile.String(),
|
||||
}
|
||||
data, err := json.Marshal(tmp)
|
||||
if err != nil || string(data) == "{}" {
|
||||
return ""
|
||||
}
|
||||
return string(data)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,77 @@
|
|||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package inventory
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"sigs.k8s.io/cli-utils/pkg/apis/actuation"
|
||||
"sigs.k8s.io/cli-utils/pkg/object"
|
||||
)
|
||||
|
||||
func TestBuildObjMap(t *testing.T) {
|
||||
obj1 := actuation.ObjectReference{
|
||||
Group: "group1",
|
||||
Kind: "Kind",
|
||||
Namespace: "ns",
|
||||
Name: "na",
|
||||
}
|
||||
obj2 := actuation.ObjectReference{
|
||||
Group: "group2",
|
||||
Kind: "Kind",
|
||||
Namespace: "ns",
|
||||
Name: "na",
|
||||
}
|
||||
|
||||
tests := map[string]struct {
|
||||
objSet object.ObjMetadataSet
|
||||
objStatus []actuation.ObjectStatus
|
||||
expected map[string]string
|
||||
hasError bool
|
||||
}{
|
||||
"objMetadata matches the status": {
|
||||
objSet: object.ObjMetadataSet{ObjMetadataFromObjectReference(obj1), ObjMetadataFromObjectReference(obj2)},
|
||||
objStatus: []actuation.ObjectStatus{
|
||||
{
|
||||
ObjectReference: obj1,
|
||||
Strategy: actuation.ActuationStrategyApply,
|
||||
Actuation: actuation.ActuationSucceeded,
|
||||
Reconcile: actuation.ReconcilePending,
|
||||
},
|
||||
{
|
||||
ObjectReference: obj2,
|
||||
Strategy: actuation.ActuationStrategyDelete,
|
||||
Actuation: actuation.ActuationSkipped,
|
||||
Reconcile: actuation.ReconcileSucceeded,
|
||||
},
|
||||
},
|
||||
expected: map[string]string{
|
||||
"ns_na_group1_Kind": `{"actuation":"Succeeded","reconcile":"Pending","strategy":"Apply"}`,
|
||||
"ns_na_group2_Kind": `{"actuation":"Skipped","reconcile":"Succeeded","strategy":"Delete"}`,
|
||||
},
|
||||
},
|
||||
"empty object status list": {
|
||||
objSet: object.ObjMetadataSet{ObjMetadataFromObjectReference(obj1), ObjMetadataFromObjectReference(obj2)},
|
||||
hasError: true,
|
||||
},
|
||||
}
|
||||
for name, tc := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
actual, err := buildObjMap(tc.objSet, tc.objStatus)
|
||||
if tc.hasError {
|
||||
if err == nil {
|
||||
t.Fatalf("expected erroe, but not happened")
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if diff := cmp.Diff(actual, tc.expected); diff != "" {
|
||||
t.Errorf(diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/kubectl/pkg/cmd/util"
|
||||
"sigs.k8s.io/cli-utils/pkg/apis/actuation"
|
||||
"sigs.k8s.io/cli-utils/pkg/common"
|
||||
"sigs.k8s.io/cli-utils/pkg/inventory"
|
||||
"sigs.k8s.io/cli-utils/pkg/object"
|
||||
|
|
@ -156,7 +157,7 @@ func (i InventoryCustomType) Load() (object.ObjMetadataSet, error) {
|
|||
return inv, nil
|
||||
}
|
||||
|
||||
func (i InventoryCustomType) Store(objs object.ObjMetadataSet) error {
|
||||
func (i InventoryCustomType) Store(objs object.ObjMetadataSet, _ []actuation.ObjectStatus) error {
|
||||
var inv []interface{}
|
||||
for _, obj := range objs {
|
||||
inv = append(inv, map[string]interface{}{
|
||||
|
|
|
|||
Loading…
Reference in New Issue