Skip to content

Commit ac40271

Browse files
committed
Make sure inventories are consistenly looked up based on either name or label
1 parent 6e08985 commit ac40271

File tree

10 files changed

+148
-28
lines changed

10 files changed

+148
-28
lines changed

pkg/apply/task/apply_task_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -921,6 +921,10 @@ func (fi *fakeInventoryInfo) ID() string {
921921
return "id"
922922
}
923923

924+
func (fi *fakeInventoryInfo) Strategy() inventory.InventoryStrategy {
925+
return inventory.NameStrategy
926+
}
927+
924928
func addOwningInventory(obj *unstructured.Unstructured, id string) *unstructured.Unstructured {
925929
if obj == nil {
926930
return nil

pkg/inventory/inventory-client.go

Lines changed: 88 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,30 @@ func (cic *ClusterInventoryClient) replaceInventory(inv *unstructured.Unstructur
199199

200200
// DeleteInventoryObj deletes the inventory object from the cluster.
201201
func (cic *ClusterInventoryClient) DeleteInventoryObj(localInv InventoryInfo) error {
202-
return cic.deleteInventoryObj(cic.invToUnstructuredFunc(localInv))
202+
if localInv == nil {
203+
return fmt.Errorf("retrieving cluster inventory object with nil local inventory")
204+
}
205+
switch localInv.Strategy() {
206+
case NameStrategy:
207+
return cic.deleteInventoryObjByName(cic.invToUnstructuredFunc(localInv))
208+
case LabelStrategy:
209+
return cic.deleteInventoryObjsByLabel(localInv)
210+
default:
211+
panic(fmt.Errorf("unknown inventory strategy: %s", localInv.Strategy()))
212+
}
213+
}
214+
215+
func (cic *ClusterInventoryClient) deleteInventoryObjsByLabel(inv InventoryInfo) error {
216+
clusterInvObjs, err := cic.getClusterInventoryObjsByLabel(inv)
217+
if err != nil {
218+
return err
219+
}
220+
for _, invObj := range clusterInvObjs {
221+
if err := cic.deleteInventoryObjByName(invObj); err != nil {
222+
return err
223+
}
224+
}
225+
return nil
203226
}
204227

205228
// GetClusterObjs returns the objects stored in the cluster inventory object, or
@@ -228,6 +251,37 @@ func (cic *ClusterInventoryClient) GetClusterObjs(localInv InventoryInfo) ([]obj
228251
// TODO(seans3): Remove the special case code to merge multiple cluster inventory
229252
// objects once we've determined that this case is no longer possible.
230253
func (cic *ClusterInventoryClient) GetClusterInventoryInfo(inv InventoryInfo) (*unstructured.Unstructured, error) {
254+
if inv == nil {
255+
return nil, fmt.Errorf("inventoryInfo must be specified")
256+
}
257+
258+
var clusterInvObjects []*unstructured.Unstructured
259+
var err error
260+
switch inv.Strategy() {
261+
case NameStrategy:
262+
clusterInvObjects, err = cic.getClusterInventoryObjsByName(inv)
263+
case LabelStrategy:
264+
clusterInvObjects, err = cic.getClusterInventoryObjsByLabel(inv)
265+
default:
266+
panic(fmt.Errorf("unknown inventory strategy: %s", inv.Strategy()))
267+
}
268+
if err != nil {
269+
return nil, err
270+
}
271+
272+
var clusterInv *unstructured.Unstructured
273+
if len(clusterInvObjects) == 1 {
274+
clusterInv = clusterInvObjects[0]
275+
} else if len(clusterInvObjects) > 1 {
276+
clusterInv, err = cic.mergeClusterInventory(clusterInvObjects)
277+
if err != nil {
278+
return nil, err
279+
}
280+
}
281+
return clusterInv, nil
282+
}
283+
284+
func (cic *ClusterInventoryClient) getClusterInventoryObjsByLabel(inv InventoryInfo) ([]*unstructured.Unstructured, error) {
231285
localInv := cic.invToUnstructuredFunc(inv)
232286
if localInv == nil {
233287
return nil, fmt.Errorf("retrieving cluster inventory object with nil local inventory")
@@ -260,16 +314,37 @@ func (cic *ClusterInventoryClient) GetClusterInventoryInfo(inv InventoryInfo) (*
260314
if err != nil {
261315
return nil, err
262316
}
263-
var clusterInv *unstructured.Unstructured
264-
if len(retrievedInventoryInfos) == 1 {
265-
clusterInv = object.InfoToUnstructured(retrievedInventoryInfos[0])
266-
} else if len(retrievedInventoryInfos) > 1 {
267-
clusterInv, err = cic.mergeClusterInventory(object.InfosToUnstructureds(retrievedInventoryInfos))
268-
if err != nil {
269-
return nil, err
270-
}
317+
return object.InfosToUnstructureds(retrievedInventoryInfos), nil
318+
}
319+
320+
func (cic *ClusterInventoryClient) getClusterInventoryObjsByName(inv InventoryInfo) ([]*unstructured.Unstructured, error) {
321+
localInv := cic.invToUnstructuredFunc(inv)
322+
if localInv == nil {
323+
return nil, fmt.Errorf("retrieving cluster inventory object with nil local inventory")
271324
}
272-
return clusterInv, nil
325+
326+
invInfo, err := cic.toInfo(localInv)
327+
if err != nil {
328+
return nil, err
329+
}
330+
331+
helper, err := cic.helperFromInfo(invInfo)
332+
if err != nil {
333+
return nil, err
334+
}
335+
336+
res, err := helper.Get(inv.Namespace(), inv.Name())
337+
if err != nil && !apierrors.IsNotFound(err) {
338+
return nil, err
339+
}
340+
if apierrors.IsNotFound(err) {
341+
return []*unstructured.Unstructured{}, nil
342+
}
343+
clusterInv, ok := res.(*unstructured.Unstructured)
344+
if !ok {
345+
return nil, fmt.Errorf("retrieved cluster inventory object is not of type *Unstructured")
346+
}
347+
return []*unstructured.Unstructured{clusterInv}, nil
273348
}
274349

275350
func (cic *ClusterInventoryClient) UpdateLabels(inv InventoryInfo, labels map[string]string) error {
@@ -335,7 +410,7 @@ func (cic *ClusterInventoryClient) mergeClusterInventory(invObjs []*unstructured
335410
// Finally, delete the other inventory objects.
336411
for i := 1; i < len(invObjs); i++ {
337412
merge := invObjs[i]
338-
if err := cic.deleteInventoryObj(merge); err != nil {
413+
if err := cic.deleteInventoryObjByName(merge); err != nil {
339414
return nil, err
340415
}
341416
}
@@ -399,9 +474,9 @@ func (cic *ClusterInventoryClient) createInventoryObj(obj *unstructured.Unstruct
399474
return invInfo.Refresh(createdObj, ignoreError)
400475
}
401476

402-
// deleteInventoryObj deletes the passed inventory object from the APIServer, or
477+
// deleteInventoryObjByName deletes the passed inventory object from the APIServer, or
403478
// an error if one occurs.
404-
func (cic *ClusterInventoryClient) deleteInventoryObj(obj *unstructured.Unstructured) error {
479+
func (cic *ClusterInventoryClient) deleteInventoryObjByName(obj *unstructured.Unstructured) error {
405480
if cic.dryRunStrategy.ClientOrServerDryRun() {
406481
klog.V(4).Infof("dry-run delete inventory object: not deleted")
407482
return nil

pkg/inventory/inventory-client_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,7 @@ func TestDeleteInventoryObj(t *testing.T) {
470470
if inv != nil {
471471
inv = storeObjsInInventory(tc.inv, tc.localObjs)
472472
}
473-
err := invClient.deleteInventoryObj(inv)
473+
err := invClient.deleteInventoryObjByName(inv)
474474
if err != nil {
475475
t.Fatalf("unexpected error received: %s", err)
476476
}

pkg/inventory/inventory-info.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@
33

44
package inventory
55

6+
type InventoryStrategy string
7+
8+
const (
9+
NameStrategy InventoryStrategy = "name"
10+
LabelStrategy InventoryStrategy = "label"
11+
)
12+
613
// InventoryInfo provides the minimal information for the applier
714
// to create, look up and update an inventory.
815
// The inventory object can be any type, the Provider in the applier
@@ -21,4 +28,6 @@ type InventoryInfo interface {
2128
// The Provider contained in the applier should know
2229
// if the Id is necessary and how to use it for pruning objects.
2330
ID() string
31+
32+
Strategy() InventoryStrategy
2433
}

pkg/inventory/inventorycm.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ func (icm *InventoryConfigMap) ID() string {
7070
return strings.TrimSpace(inventoryLabel)
7171
}
7272

73+
func (icm *InventoryConfigMap) Strategy() InventoryStrategy {
74+
return LabelStrategy
75+
}
76+
7377
func (icm *InventoryConfigMap) UnstructuredInventory() *unstructured.Unstructured {
7478
return icm.inv
7579
}

pkg/inventory/policy_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ func (i *fakeInventoryInfo) ID() string {
2525
return i.id
2626
}
2727

28+
func (i *fakeInventoryInfo) Strategy() InventoryStrategy {
29+
return NameStrategy
30+
}
31+
2832
func testObjectWithAnnotation(key, val string) *unstructured.Unstructured {
2933
obj := &unstructured.Unstructured{
3034
Object: map[string]interface{}{

test/e2e/apply_and_destroy_test.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package e2e
55

66
import (
77
"context"
8+
"fmt"
89
"time"
910

1011
. "github.com/onsi/ginkgo"
@@ -19,14 +20,15 @@ import (
1920
func applyAndDestroyTest(c client.Client, invConfig InventoryConfig, inventoryName, namespaceName string) {
2021
By("Apply resources")
2122
applier := invConfig.ApplierFactoryFunc()
23+
inventoryID := fmt.Sprintf("%s-%s", inventoryName, namespaceName)
2224

23-
inv := invConfig.InvWrapperFunc(invConfig.InventoryFactoryFunc(inventoryName, namespaceName, "test"))
25+
applyInv := createInventoryInfo(invConfig, inventoryName, namespaceName, inventoryID)
2426

2527
resources := []*unstructured.Unstructured{
2628
deploymentManifest(namespaceName),
2729
}
2830

29-
applyCh := applier.Run(context.TODO(), inv, resources, apply.Options{
31+
applyCh := applier.Run(context.TODO(), applyInv, resources, apply.Options{
3032
ReconcileTimeout: 2 * time.Minute,
3133
EmitStatusEvents: true,
3234
})
@@ -53,13 +55,14 @@ func applyAndDestroyTest(c client.Client, invConfig InventoryConfig, inventoryNa
5355
Expect(err).ToNot(HaveOccurred())
5456

5557
By("Verify inventory")
56-
invConfig.InvSizeVerifyFunc(c, inventoryName, namespaceName, 1)
58+
invConfig.InvSizeVerifyFunc(c, inventoryName, namespaceName, inventoryID, 1)
5759

5860
By("Destroy resources")
5961
destroyer := invConfig.DestroyerFactoryFunc()
6062

63+
destroyInv := createInventoryInfo(invConfig, inventoryName, namespaceName, inventoryID)
6164
option := &apply.DestroyerOption{InventoryPolicy: inventory.AdoptIfNoInventory}
62-
destroyerEvents := runCollectNoErr(destroyer.Run(inv, option))
65+
destroyerEvents := runCollectNoErr(destroyer.Run(destroyInv, option))
6366
err = verifyEvents([]expEvent{
6467
{
6568
eventType: event.DeleteType,
@@ -70,3 +73,14 @@ func applyAndDestroyTest(c client.Client, invConfig InventoryConfig, inventoryNa
7073
}, destroyerEvents)
7174
Expect(err).ToNot(HaveOccurred())
7275
}
76+
77+
func createInventoryInfo(invConfig InventoryConfig, inventoryName, namespaceName, inventoryID string) inventory.InventoryInfo {
78+
switch invConfig.InventoryStrategy {
79+
case inventory.NameStrategy:
80+
return invConfig.InvWrapperFunc(invConfig.InventoryFactoryFunc(inventoryName, namespaceName, randomString("inventory-")))
81+
case inventory.LabelStrategy:
82+
return invConfig.InvWrapperFunc(invConfig.InventoryFactoryFunc(randomString("inventory-"), namespaceName, inventoryID))
83+
default:
84+
panic(fmt.Errorf("unknown inventory strategy %q", invConfig.InventoryStrategy))
85+
}
86+
}

test/e2e/customprovider/provider.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,10 @@ func (i InventoryCustomType) Name() string {
127127
return i.inv.GetName()
128128
}
129129

130+
func (i InventoryCustomType) Strategy() inventory.InventoryStrategy {
131+
return inventory.NameStrategy
132+
}
133+
130134
func (i InventoryCustomType) ID() string {
131135
labels := i.inv.GetLabels()
132136
id, found := labels[common.InventoryLabel]

test/e2e/e2e_test.go

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,11 @@ type inventoryFactoryFunc func(name, namespace, id string) *unstructured.Unstruc
3232
type invWrapperFunc func(*unstructured.Unstructured) inventory.InventoryInfo
3333
type applierFactoryFunc func() *apply.Applier
3434
type destroyerFactoryFunc func() *apply.Destroyer
35-
type invSizeVerifyFunc func(c client.Client, name, namespace string, count int)
35+
type invSizeVerifyFunc func(c client.Client, name, namespace, id string, count int)
3636
type invCountVerifyFunc func(c client.Client, namespace string, count int)
3737

3838
type InventoryConfig struct {
39+
InventoryStrategy inventory.InventoryStrategy
3940
InventoryFactoryFunc inventoryFactoryFunc
4041
InvWrapperFunc invWrapperFunc
4142
ApplierFactoryFunc applierFactoryFunc
@@ -46,6 +47,7 @@ type InventoryConfig struct {
4647

4748
var inventoryConfigs = map[string]InventoryConfig{
4849
"ConfigMap (default)": {
50+
InventoryStrategy: inventory.LabelStrategy,
4951
InventoryFactoryFunc: cmInventoryManifest,
5052
InvWrapperFunc: inventory.WrapInventoryInfoObj,
5153
ApplierFactoryFunc: newDefaultInvApplier,
@@ -54,6 +56,7 @@ var inventoryConfigs = map[string]InventoryConfig{
5456
InvCountVerifyFunc: defaultInvCountVerifyFunc,
5557
},
5658
"Custom Type": {
59+
InventoryStrategy: inventory.NameStrategy,
5760
InventoryFactoryFunc: customInventoryManifest,
5861
InvWrapperFunc: customprovider.WrapInventoryInfoObj,
5962
ApplierFactoryFunc: newCustomInvApplier,
@@ -196,12 +199,15 @@ func newDefaultInvProvider() provider.Provider {
196199
return provider.NewProvider(newFactory())
197200
}
198201

199-
func defaultInvSizeVerifyFunc(c client.Client, name, namespace string, count int) {
200-
var cm v1.ConfigMap
201-
err := c.Get(context.TODO(), types.NamespacedName{
202-
Name: name,
203-
Namespace: namespace,
204-
}, &cm)
202+
func defaultInvSizeVerifyFunc(c client.Client, name, namespace, id string, count int) {
203+
var cmList v1.ConfigMapList
204+
err := c.List(context.TODO(), &cmList,
205+
client.MatchingLabels(map[string]string{common.InventoryLabel: id}),
206+
client.InNamespace(namespace))
207+
Expect(err).ToNot(HaveOccurred())
208+
209+
Expect(len(cmList.Items)).To(Equal(1))
210+
cm := cmList.Items[0]
205211
Expect(err).ToNot(HaveOccurred())
206212

207213
data := cm.Data
@@ -241,8 +247,8 @@ func newFactory() util.Factory {
241247
return util.NewFactory(matchVersionKubeConfigFlags)
242248
}
243249

244-
func customInvSizeVerifyFunc(c client.Client, name, namespace string, count int) {
245-
var u unstructured.Unstructured
250+
func customInvSizeVerifyFunc(c client.Client, name, namespace, _ string, count int) {
251+
var u unstructured.UnstructuredList
246252
u.SetGroupVersionKind(customprovider.InventoryGVK)
247253
err := c.Get(context.TODO(), types.NamespacedName{
248254
Name: name,

test/e2e/inventory_policy_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ func inventoryPolicyAdoptIfNoInventoryTest(c client.Client, invConfig InventoryC
141141
Expect(d.ObjectMeta.Annotations["config.k8s.io/owning-inventory"]).To(Equal(invName))
142142

143143
invConfig.InvCountVerifyFunc(c, namespaceName, 1)
144-
invConfig.InvSizeVerifyFunc(c, invName, namespaceName, 1)
144+
invConfig.InvSizeVerifyFunc(c, invName, namespaceName, invName, 1)
145145
}
146146

147147
func inventoryPolicyAdoptAllTest(c client.Client, invConfig InventoryConfig, namespaceName string) {

0 commit comments

Comments
 (0)