Skip to content

Commit a4e3b45

Browse files
committed
Inventory client uses factory function
1 parent 400dc9f commit a4e3b45

File tree

6 files changed

+109
-90
lines changed

6 files changed

+109
-90
lines changed

pkg/apply/destroyer.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ type Destroyer struct {
4444
ioStreams genericclioptions.IOStreams
4545
ApplyOptions *apply.ApplyOptions
4646
PruneOptions *prune.PruneOptions
47+
invClient inventory.InventoryClient
4748
DryRunStrategy common.DryRunStrategy
4849
}
4950

@@ -64,6 +65,7 @@ func (d *Destroyer) Initialize(cmd *cobra.Command, paths []string) error {
6465
if err != nil {
6566
return errors.WrapPrefix(err, "error creating inventory client", 1)
6667
}
68+
d.invClient = invClient
6769
err = d.PruneOptions.Initialize(d.factory, invClient)
6870
if err != nil {
6971
return errors.WrapPrefix(err, "error setting up PruneOptions", 1)
@@ -97,7 +99,7 @@ func (d *Destroyer) Run() <-chan event.Event {
9799
// deleting everything. We can ignore the error, since the Prune
98100
// will catch the same problems.
99101
invInfo, nonInvInfos, _ := inventory.SplitInfos(infos)
100-
invInfo, err = inventory.ClearInventoryObj(invInfo)
102+
invInfo, err = d.invClient.ClearInventoryObj(invInfo)
101103
if err != nil {
102104
ch <- event.Event{
103105
Type: event.ErrorType,
@@ -121,8 +123,8 @@ func (d *Destroyer) Run() <-chan event.Event {
121123
// Now delete the inventory object as well.
122124
inv := inventory.FindInventoryObj(infos)
123125
if inv != nil {
124-
d.PruneOptions.InvClient.SetDryRunStrategy(d.DryRunStrategy)
125-
_ = d.PruneOptions.InvClient.DeleteInventoryObj(inv)
126+
d.invClient.SetDryRunStrategy(d.DryRunStrategy)
127+
_ = d.invClient.DeleteInventoryObj(inv)
126128
}
127129

128130
// Close the tempChannel to signal to the event transformer that

pkg/inventory/fake-inventory-client.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ func (fic *FakeInventoryClient) DeleteInventoryObj(inv *resource.Info) error {
6363
return nil
6464
}
6565

66+
// ClearInventoryObj implements InventoryClient interface function. It does nothing for now.
67+
func (fic *FakeInventoryClient) ClearInventoryObj(inv *resource.Info) (*resource.Info, error) {
68+
return inv, nil
69+
}
70+
6671
func (fic *FakeInventoryClient) SetDryRunStrategy(drs common.DryRunStrategy) {}
6772

6873
// SetError forces an error on the subsequent client call if it returns an error.

pkg/inventory/inventory-client.go

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,22 @@ type InventoryClient interface {
3434
Replace(inv *resource.Info, objs []object.ObjMetadata) error
3535
// DeleteInventoryObj deletes the passed inventory object from the APIServer.
3636
DeleteInventoryObj(inv *resource.Info) error
37+
// ClearInventoryObj clears all obj references from the passed inventory object,
38+
// returning the cleared inventory object or an error.
39+
ClearInventoryObj(inv *resource.Info) (*resource.Info, error)
3740
// SetDryRunStrategy sets the dry run strategy on whether this we actually mutate.
3841
SetDryRunStrategy(drs common.DryRunStrategy)
3942
}
4043

4144
// ClusterInventoryClient is a concrete implementation of the
4245
// InventoryClient interface.
4346
type ClusterInventoryClient struct {
44-
builderFunc func() *resource.Builder
45-
mapper meta.RESTMapper
46-
validator validation.Schema
47-
clientFunc func(*meta.RESTMapping) (resource.RESTClient, error)
48-
dryRunStrategy common.DryRunStrategy
47+
builderFunc func() *resource.Builder
48+
mapper meta.RESTMapper
49+
validator validation.Schema
50+
clientFunc func(*meta.RESTMapping) (resource.RESTClient, error)
51+
dryRunStrategy common.DryRunStrategy
52+
InventoryFactoryFunc InventoryFactoryFunc
4953
}
5054

5155
var _ InventoryClient = &ClusterInventoryClient{}
@@ -64,11 +68,12 @@ func NewInventoryClient(factory cmdutil.Factory) (*ClusterInventoryClient, error
6468
}
6569
builderFunc := factory.NewBuilder
6670
clusterInventoryClient := ClusterInventoryClient{
67-
builderFunc: builderFunc,
68-
mapper: mapper,
69-
validator: validator,
70-
clientFunc: factory.UnstructuredClientForMapping,
71-
dryRunStrategy: common.DryRunNone,
71+
builderFunc: builderFunc,
72+
mapper: mapper,
73+
validator: validator,
74+
clientFunc: factory.UnstructuredClientForMapping,
75+
dryRunStrategy: common.DryRunNone,
76+
InventoryFactoryFunc: WrapInventoryObj,
7277
}
7378
return &clusterInventoryClient, nil
7479
}
@@ -88,7 +93,7 @@ func (cic *ClusterInventoryClient) Merge(localInv *resource.Info, objs []object.
8893
}
8994
if clusterInv == nil {
9095
// Wrap inventory object and store the inventory in it.
91-
inv := WrapInventoryObj(localInv)
96+
inv := cic.InventoryFactoryFunc(localInv)
9297
if err := inv.Store(objs); err != nil {
9398
return nil, err
9499
}
@@ -114,7 +119,7 @@ func (cic *ClusterInventoryClient) Merge(localInv *resource.Info, objs []object.
114119
unionObjs := object.Union(clusterObjs, objs)
115120
klog.V(4).Infof("num objects to prune: %d", len(pruneIds))
116121
klog.V(4).Infof("num merged objects to store in inventory: %d", len(unionObjs))
117-
wrappedInv := WrapInventoryObj(clusterInv)
122+
wrappedInv := cic.InventoryFactoryFunc(clusterInv)
118123
if err = wrappedInv.Store(unionObjs); err != nil {
119124
return pruneIds, err
120125
}
@@ -148,7 +153,7 @@ func (cic *ClusterInventoryClient) Replace(localInv *resource.Info, objs []objec
148153
if err != nil {
149154
return err
150155
}
151-
wrappedInv := WrapInventoryObj(clusterInv)
156+
wrappedInv := cic.InventoryFactoryFunc(clusterInv)
152157
if err = wrappedInv.Store(objs); err != nil {
153158
return err
154159
}
@@ -178,7 +183,7 @@ func (cic *ClusterInventoryClient) GetClusterObjs(localInv *resource.Info) ([]ob
178183
if clusterInv == nil {
179184
return []object.ObjMetadata{}, nil
180185
}
181-
wrapped := WrapInventoryObj(clusterInv)
186+
wrapped := cic.InventoryFactoryFunc(clusterInv)
182187
return wrapped.Load()
183188
}
184189

@@ -254,7 +259,7 @@ func (cic *ClusterInventoryClient) mergeClusterInventory(invInfos []*resource.In
254259
// choosing the first inventory object as the one to retain.
255260
sort.Sort(ordering.SortableInfos(invInfos))
256261
retained := invInfos[0]
257-
wrapRetained := WrapInventoryObj(retained)
262+
wrapRetained := cic.InventoryFactoryFunc(retained)
258263
retainedObjs, err := wrapRetained.Load()
259264
if err != nil {
260265
return nil, err
@@ -263,7 +268,7 @@ func (cic *ClusterInventoryClient) mergeClusterInventory(invInfos []*resource.In
263268
// the retained objects.
264269
for i := 1; i < len(invInfos); i++ {
265270
merge := invInfos[i]
266-
wrapMerge := WrapInventoryObj(merge)
271+
wrapMerge := cic.InventoryFactoryFunc(merge)
267272
mergeObjs, err := wrapMerge.Load()
268273
if err != nil {
269274
return nil, err
@@ -382,6 +387,22 @@ func (cic *ClusterInventoryClient) DeleteInventoryObj(info *resource.Info) error
382387
return err
383388
}
384389

390+
// ClearInventoryObj sets an empty inventory, which is used in destroy. Returns the
391+
// cleared inventory or an error if one occurred.
392+
func (cic *ClusterInventoryClient) ClearInventoryObj(invInfo *resource.Info) (*resource.Info, error) {
393+
if invInfo == nil {
394+
return nil, fmt.Errorf("clearing nil inventory object")
395+
}
396+
if !IsInventoryObject(invInfo) {
397+
return nil, fmt.Errorf("attempting to clear non-inventory object")
398+
}
399+
wrapped := cic.InventoryFactoryFunc(invInfo)
400+
if err := wrapped.Store([]object.ObjMetadata{}); err != nil {
401+
return nil, err
402+
}
403+
return wrapped.GetObject()
404+
}
405+
385406
// SetDryRun sets whether the inventory client will mutate the inventory
386407
// object in the cluster.
387408
func (cic *ClusterInventoryClient) SetDryRunStrategy(drs common.DryRunStrategy) {

pkg/inventory/inventory-client_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,64 @@ func TestDeleteInventoryObj(t *testing.T) {
472472
}
473473
}
474474

475+
func TestClearInventoryObject(t *testing.T) {
476+
pod1 := ignoreErrInfoToObjMeta(pod1Info)
477+
pod3 := ignoreErrInfoToObjMeta(pod3Info)
478+
inv := storeObjsInInventory(invInfo, []object.ObjMetadata{pod1, pod3})
479+
tests := map[string]struct {
480+
invInfo *resource.Info
481+
isError bool
482+
}{
483+
"Nil info should error": {
484+
invInfo: nil,
485+
isError: true,
486+
},
487+
"Info with nil Object should error": {
488+
invInfo: nilInfo,
489+
isError: true,
490+
},
491+
"Single non-inventory object should error": {
492+
invInfo: pod1Info,
493+
isError: true,
494+
},
495+
"Single inventory object without data should stay cleared": {
496+
invInfo: invInfo,
497+
isError: false,
498+
},
499+
"Single inventory object with data should be cleared": {
500+
invInfo: inv,
501+
isError: false,
502+
},
503+
}
504+
tf := cmdtesting.NewTestFactory().WithNamespace(testNamespace)
505+
defer tf.Cleanup()
506+
507+
for name, tc := range tests {
508+
t.Run(name, func(t *testing.T) {
509+
invClient, _ := NewInventoryClient(tf)
510+
invInfo, err := invClient.ClearInventoryObj(tc.invInfo)
511+
if tc.isError {
512+
if err == nil {
513+
t.Errorf("Should have produced an error, but returned none.")
514+
}
515+
}
516+
if !tc.isError {
517+
if err != nil {
518+
t.Fatalf("Received unexpected error: %s", err)
519+
}
520+
wrapped := WrapInventoryObj(invInfo)
521+
objs, err := wrapped.Load()
522+
if err != nil {
523+
t.Fatalf("Received unexpected error: %s", err)
524+
}
525+
if len(objs) > 0 {
526+
t.Errorf("Inventory object inventory not cleared: %#v\n", objs)
527+
}
528+
}
529+
})
530+
}
531+
}
532+
475533
type invAndObjs struct {
476534
inv *resource.Info
477535
invObjs []object.ObjMetadata

pkg/inventory/inventory.go

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ type Inventory interface {
3838
GetObject() (*resource.Info, error)
3939
}
4040

41+
// InventoryFactoryFunc creates the object which implements the Inventory
42+
// interface from the passed info object.
43+
type InventoryFactoryFunc func(*resource.Info) Inventory
44+
4145
// FindInventoryObj returns the "Inventory" object (ConfigMap with
4246
// inventory label) if it exists, or nil if it does not exist.
4347
func FindInventoryObj(infos []*resource.Info) *resource.Info {
@@ -108,22 +112,6 @@ func SplitInfos(infos []*resource.Info) (*resource.Info, []*resource.Info, error
108112
return invs[0], resources, nil
109113
}
110114

111-
// ClearInventoryObj finds the inventory object in the list of objects,
112-
// and sets an empty inventory. Returns an error if once occurred.
113-
func ClearInventoryObj(invInfo *resource.Info) (*resource.Info, error) {
114-
if invInfo == nil {
115-
return nil, fmt.Errorf("clearing nil inventory object")
116-
}
117-
if !IsInventoryObject(invInfo) {
118-
return nil, fmt.Errorf("attempting to clear non-inventory object")
119-
}
120-
wrapped := WrapInventoryObj(invInfo)
121-
if err := wrapped.Store([]object.ObjMetadata{}); err != nil {
122-
return nil, err
123-
}
124-
return wrapped.GetObject()
125-
}
126-
127115
// addSuffixToName adds the passed suffix (usually a hash) as a suffix
128116
// to the name of the passed object stored in the Info struct. Returns
129117
// an error if name stored in the object differs from the name in

pkg/inventory/inventory_test.go

Lines changed: 0 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -354,61 +354,6 @@ func TestSplitInfos(t *testing.T) {
354354
}
355355
}
356356

357-
func TestClearInventoryObject(t *testing.T) {
358-
pod1 := ignoreErrInfoToObjMeta(pod1Info)
359-
pod3 := ignoreErrInfoToObjMeta(pod3Info)
360-
inv := storeObjsInInventory(invInfo, []object.ObjMetadata{pod1, pod3})
361-
tests := map[string]struct {
362-
invInfo *resource.Info
363-
isError bool
364-
}{
365-
"Nil info should error": {
366-
invInfo: nil,
367-
isError: true,
368-
},
369-
"Info with nil Object should error": {
370-
invInfo: nilInfo,
371-
isError: true,
372-
},
373-
"Single non-inventory object should error": {
374-
invInfo: pod1Info,
375-
isError: true,
376-
},
377-
"Single inventory object without data should stay cleared": {
378-
invInfo: invInfo,
379-
isError: false,
380-
},
381-
"Single inventory object with data should be cleared": {
382-
invInfo: inv,
383-
isError: false,
384-
},
385-
}
386-
387-
for name, tc := range tests {
388-
t.Run(name, func(t *testing.T) {
389-
invInfo, err := ClearInventoryObj(tc.invInfo)
390-
if tc.isError {
391-
if err == nil {
392-
t.Errorf("Should have produced an error, but returned none.")
393-
}
394-
}
395-
if !tc.isError {
396-
if err != nil {
397-
t.Fatalf("Received unexpected error: %s", err)
398-
}
399-
wrapped := WrapInventoryObj(invInfo)
400-
objs, err := wrapped.Load()
401-
if err != nil {
402-
t.Fatalf("Received unexpected error: %s", err)
403-
}
404-
if len(objs) > 0 {
405-
t.Errorf("Inventory object inventory not cleared: %#v\n", objs)
406-
}
407-
}
408-
})
409-
}
410-
}
411-
412357
func TestAddSuffixToName(t *testing.T) {
413358
tests := []struct {
414359
info *resource.Info

0 commit comments

Comments
 (0)