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:
Jingfang Liu 2022-02-10 14:12:45 -08:00
parent b450b79991
commit 6bd4b565e8
10 changed files with 244 additions and 40 deletions

View File

@ -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()

View File

@ -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}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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)
}
})
}
}

View File

@ -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{}{