Skip to content

Commit

Permalink
feat: Add inventory.StatusPolicy
Browse files Browse the repository at this point in the history
- StatusPolicyNone disables inventory status updates.
- StatusPolicyAll fully enables inventory status updates.
- This allows an opt-out feature for working around the problem
  that adding status can make the inventory larger than the max
  etcd object size, causing the applier to exit without applying
  or pruning anything. With StatusPolicyNone, the user can still
  safely prune objects to make their inventory smaller, and then
  re-enable the status with StatusPolicyAll.
- Note: the default ConfigMap does not currently support status,
  so this only affects custom inventory impls.
  • Loading branch information
karlkfi committed Mar 5, 2022
1 parent 16847b4 commit 2bdc059
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 35 deletions.
5 changes: 3 additions & 2 deletions pkg/inventory/inventory-client-factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ type ClientFactory interface {

// ClusterClientFactory is a factory that creates instances of ClusterClient inventory client.
type ClusterClientFactory struct {
StatusPolicy StatusPolicy
}

func (ClusterClientFactory) NewClient(factory cmdutil.Factory) (Client, error) {
return NewClient(factory, WrapInventoryObj, InvInfoToConfigMap)
func (ccf ClusterClientFactory) NewClient(factory cmdutil.Factory) (Client, error) {
return NewClient(factory, WrapInventoryObj, InvInfoToConfigMap, ccf.StatusPolicy)
}
10 changes: 9 additions & 1 deletion pkg/inventory/inventory-client.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ type ClusterClient struct {
mapper meta.RESTMapper
InventoryFactoryFunc StorageFactoryFunc
invToUnstructuredFunc ToUnstructuredFunc
statusPolicy StatusPolicy
}

var _ Client = &ClusterClient{}
Expand All @@ -62,7 +63,9 @@ var _ Client = &ClusterClient{}
// Client interface or an error.
func NewClient(factory cmdutil.Factory,
invFunc StorageFactoryFunc,
invToUnstructuredFunc ToUnstructuredFunc) (*ClusterClient, error) {
invToUnstructuredFunc ToUnstructuredFunc,
statusPolicy StatusPolicy,
) (*ClusterClient, error) {
dc, err := factory.DynamicClient()
if err != nil {
return nil, err
Expand All @@ -81,6 +84,7 @@ func NewClient(factory cmdutil.Factory,
mapper: mapper,
InventoryFactoryFunc: invFunc,
invToUnstructuredFunc: invToUnstructuredFunc,
statusPolicy: statusPolicy,
}
return &clusterClient, nil
}
Expand Down Expand Up @@ -451,6 +455,10 @@ func (cic *ClusterClient) getMapping(obj *unstructured.Unstructured) (*meta.REST
}

func (cic *ClusterClient) updateStatus(obj *unstructured.Unstructured, dryRun common.DryRunStrategy) error {
if cic.statusPolicy != StatusPolicyAll {
klog.V(4).Infof("inventory status update skipped (StatusPolicy: %s)", cic.statusPolicy)
return nil
}
if dryRun.ClientOrServerDryRun() {
klog.V(4).Infof("dry-run update inventory status: not updated")
return nil
Expand Down
70 changes: 39 additions & 31 deletions pkg/inventory/inventory-client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,11 @@ func podData(name string) map[string]string {

func TestGetClusterInventoryInfo(t *testing.T) {
tests := map[string]struct {
inv Info
localObjs object.ObjMetadataSet
objStatus []actuation.ObjectStatus
isError bool
statusPolicy StatusPolicy
inv Info
localObjs object.ObjMetadataSet
objStatus []actuation.ObjectStatus
isError bool
}{
"Nil local inventory object is an error": {
inv: nil,
Expand Down Expand Up @@ -83,7 +84,7 @@ func TestGetClusterInventoryInfo(t *testing.T) {
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
invClient, err := NewClient(tf,
WrapInventoryObj, InvInfoToConfigMap)
WrapInventoryObj, InvInfoToConfigMap, tc.statusPolicy)
require.NoError(t, err)

var inv *unstructured.Unstructured
Expand Down Expand Up @@ -116,11 +117,12 @@ func TestGetClusterInventoryInfo(t *testing.T) {

func TestMerge(t *testing.T) {
tests := map[string]struct {
localInv Info
localObjs object.ObjMetadataSet
clusterObjs object.ObjMetadataSet
pruneObjs object.ObjMetadataSet
isError bool
statusPolicy StatusPolicy
localInv Info
localObjs object.ObjMetadataSet
clusterObjs object.ObjMetadataSet
pruneObjs object.ObjMetadataSet
isError bool
}{
"Nil local inventory object is error": {
localInv: nil,
Expand Down Expand Up @@ -188,7 +190,7 @@ func TestMerge(t *testing.T) {
tf.FakeDynamicClient.PrependReactor("list", "configmaps", toReactionFunc(tc.clusterObjs))
// Create the local inventory object storing "tc.localObjs"
invClient, err := NewClient(tf,
WrapInventoryObj, InvInfoToConfigMap)
WrapInventoryObj, InvInfoToConfigMap, tc.statusPolicy)
require.NoError(t, err)

// Call "Merge" to create the union of clusterObjs and localObjs.
Expand All @@ -212,10 +214,11 @@ func TestMerge(t *testing.T) {

func TestCreateInventory(t *testing.T) {
tests := map[string]struct {
inv Info
localObjs object.ObjMetadataSet
error string
objStatus []actuation.ObjectStatus
statusPolicy StatusPolicy
inv Info
localObjs object.ObjMetadataSet
error string
objStatus []actuation.ObjectStatus
}{
"Nil local inventory object is an error": {
inv: nil,
Expand Down Expand Up @@ -260,7 +263,7 @@ func TestCreateInventory(t *testing.T) {
})

invClient, err := NewClient(tf,
WrapInventoryObj, InvInfoToConfigMap)
WrapInventoryObj, InvInfoToConfigMap, tc.statusPolicy)
require.NoError(t, err)
inv := invClient.invToUnstructuredFunc(tc.inv)
if inv != nil {
Expand Down Expand Up @@ -288,10 +291,11 @@ func TestCreateInventory(t *testing.T) {

func TestReplace(t *testing.T) {
tests := map[string]struct {
localObjs object.ObjMetadataSet
clusterObjs object.ObjMetadataSet
objStatus []actuation.ObjectStatus
data map[string]string
statusPolicy StatusPolicy
localObjs object.ObjMetadataSet
clusterObjs object.ObjMetadataSet
objStatus []actuation.ObjectStatus
data map[string]string
}{
"Cluster and local inventories empty": {
localObjs: object.ObjMetadataSet{},
Expand Down Expand Up @@ -336,7 +340,8 @@ func TestReplace(t *testing.T) {
defer tf.Cleanup()

// Client and server dry-run do not throw errors.
invClient, err := NewClient(tf, WrapInventoryObj, InvInfoToConfigMap)
invClient, err := NewClient(tf,
WrapInventoryObj, InvInfoToConfigMap, StatusPolicyAll)
require.NoError(t, err)
err = invClient.Replace(copyInventory(), object.ObjMetadataSet{}, nil, common.DryRunClient)
if err != nil {
Expand All @@ -351,7 +356,7 @@ func TestReplace(t *testing.T) {
t.Run(name, func(t *testing.T) {
// Create inventory client, and store the cluster objs in the inventory object.
invClient, err := NewClient(tf,
WrapInventoryObj, InvInfoToConfigMap)
WrapInventoryObj, InvInfoToConfigMap, tc.statusPolicy)
require.NoError(t, err)
wrappedInv := invClient.InventoryFactoryFunc(inventoryObj)
if err := wrappedInv.Store(tc.clusterObjs, tc.objStatus); err != nil {
Expand Down Expand Up @@ -388,9 +393,10 @@ func TestReplace(t *testing.T) {

func TestGetClusterObjs(t *testing.T) {
tests := map[string]struct {
localInv Info
clusterObjs object.ObjMetadataSet
isError bool
statusPolicy StatusPolicy
localInv Info
clusterObjs object.ObjMetadataSet
isError bool
}{
"Nil cluster inventory is error": {
localInv: nil,
Expand Down Expand Up @@ -421,7 +427,7 @@ func TestGetClusterObjs(t *testing.T) {
tf.FakeDynamicClient.PrependReactor("list", "configmaps", toReactionFunc(tc.clusterObjs))

invClient, err := NewClient(tf,
WrapInventoryObj, InvInfoToConfigMap)
WrapInventoryObj, InvInfoToConfigMap, tc.statusPolicy)
require.NoError(t, err)
clusterObjs, err := invClient.GetClusterObjs(tc.localInv)
if tc.isError {
Expand All @@ -442,9 +448,10 @@ func TestGetClusterObjs(t *testing.T) {

func TestDeleteInventoryObj(t *testing.T) {
tests := map[string]struct {
inv Info
localObjs object.ObjMetadataSet
objStatus []actuation.ObjectStatus
statusPolicy StatusPolicy
inv Info
localObjs object.ObjMetadataSet
objStatus []actuation.ObjectStatus
}{
"Nil local inventory object is an error": {
inv: nil,
Expand Down Expand Up @@ -483,7 +490,7 @@ func TestDeleteInventoryObj(t *testing.T) {
defer tf.Cleanup()

invClient, err := NewClient(tf,
WrapInventoryObj, InvInfoToConfigMap)
WrapInventoryObj, InvInfoToConfigMap, tc.statusPolicy)
require.NoError(t, err)
inv := invClient.invToUnstructuredFunc(tc.inv)
if inv != nil {
Expand All @@ -500,6 +507,7 @@ func TestDeleteInventoryObj(t *testing.T) {

func TestApplyInventoryNamespace(t *testing.T) {
testCases := map[string]struct {
statusPolicy StatusPolicy
namespace *unstructured.Unstructured
dryRunStrategy common.DryRunStrategy
reactorError error
Expand Down Expand Up @@ -529,7 +537,7 @@ func TestApplyInventoryNamespace(t *testing.T) {
})

invClient, err := NewClient(tf,
WrapInventoryObj, InvInfoToConfigMap)
WrapInventoryObj, InvInfoToConfigMap, tc.statusPolicy)
require.NoError(t, err)
err = invClient.ApplyInventoryNamespace(tc.namespace, tc.dryRunStrategy)
assert.NoError(t, err)
Expand Down
18 changes: 18 additions & 0 deletions pkg/inventory/status-policy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright 2022 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0

package inventory

// StatusPolicy specifies whether the inventory client should apply status to
// the inventory object. The status contains the actuation and reconcile stauts
// of each object in the inventory.
//go:generate stringer -type=StatusPolicy -linecomment
type StatusPolicy int

const (
// StatusPolicyNone disables inventory status updates.
StatusPolicyNone StatusPolicy = iota // None

// StatusPolicyAll fully enables inventory status updates.
StatusPolicyAll // All
)
24 changes: 24 additions & 0 deletions pkg/inventory/statuspolicy_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion test/e2e/customprovider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ type CustomClientFactory struct {
}

func (CustomClientFactory) NewClient(factory util.Factory) (inventory.Client, error) {
return inventory.NewClient(factory, WrapInventoryObj, invToUnstructuredFunc)
// TODO: add status to custom inventory crd and enable StatusPolicyAll
return inventory.NewClient(factory,
WrapInventoryObj, invToUnstructuredFunc, inventory.StatusPolicyNone)
}

func invToUnstructuredFunc(inv inventory.Info) *unstructured.Unstructured {
Expand Down

0 comments on commit 2bdc059

Please sign in to comment.