Skip to content

Commit e74d625

Browse files
committed
Demonstrate new inventory client interface
1 parent 8c2dae2 commit e74d625

File tree

4 files changed

+219
-16
lines changed

4 files changed

+219
-16
lines changed

pkg/apply/task/inv_add_task.go

+36-2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"sigs.k8s.io/cli-utils/pkg/apply/taskrunner"
1919
"sigs.k8s.io/cli-utils/pkg/common"
2020
"sigs.k8s.io/cli-utils/pkg/inventory"
21+
"sigs.k8s.io/cli-utils/pkg/inventory2"
2122
"sigs.k8s.io/cli-utils/pkg/object"
2223
)
2324

@@ -30,12 +31,13 @@ var (
3031
// before the actual object is applied.
3132
type InvAddTask struct {
3233
TaskName string
33-
InvClient inventory.Client
34+
InvClient inventory2.Client
3435
DynamicClient dynamic.Interface
3536
Mapper meta.RESTMapper
3637
InvInfo inventory.Info
3738
Objects object.UnstructuredSet
3839
DryRun common.DryRunStrategy
40+
StatusPolicy inventory.StatusPolicy
3941
}
4042

4143
func (i *InvAddTask) Name() string {
@@ -71,11 +73,43 @@ func (i *InvAddTask) Start(taskContext *taskrunner.TaskContext) {
7173
}
7274
klog.V(4).Infof("merging %d local objects into inventory", len(i.Objects))
7375
currentObjs := object.UnstructuredSetToObjMetadataSet(i.Objects)
74-
_, err := i.InvClient.Merge(i.InvInfo, currentObjs, i.DryRun)
76+
err := i.extendInventory(currentObjs)
7577
i.sendTaskResult(taskContext, err)
7678
}()
7779
}
7880

81+
// extendInventory adds the specified objects to the inventory, if not already
82+
// present.
83+
func (i *InvAddTask) extendInventory(objs object.ObjMetadataSet) error {
84+
if len(objs) == 0 {
85+
return nil
86+
}
87+
id := inventory2.ID{
88+
Name: i.InvInfo.Name(),
89+
Namespace: i.InvInfo.Namespace(),
90+
}
91+
inv, err := i.InvClient.Get(context.TODO(), id)
92+
if err != nil {
93+
return fmt.Errorf("getting inventory: %w")
94+
}
95+
96+
oldObjs := inventory.ObjMetadataSetFromObjectReferenceList(inv.Spec.Objects)
97+
newObjs := oldObjs.Union(objs)
98+
inv.Spec.Objects = inventory.ObjectReferenceListFromObjMetadataSet(newObjs)
99+
100+
if err = i.InvClient.Update(context.TODO(), inv, i.updateOptionList()...); err != nil {
101+
return fmt.Errorf("updating inventory: %w")
102+
}
103+
return nil
104+
}
105+
106+
func (i *InvAddTask) updateOptionList() []inventory2.UpdateOption {
107+
return []inventory2.UpdateOption{
108+
inventory2.WithDryRun(i.DryRun),
109+
inventory2.WithStatus(i.StatusPolicy),
110+
}
111+
}
112+
79113
// Cancel is not supported by the InvAddTask.
80114
func (i *InvAddTask) Cancel(_ *taskrunner.TaskContext) {}
81115

pkg/apply/task/inv_set_task.go

+51-14
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,29 @@
44
package task
55

66
import (
7+
"context"
8+
"fmt"
9+
710
apierrors "k8s.io/apimachinery/pkg/api/errors"
811
"k8s.io/klog/v2"
12+
"sigs.k8s.io/cli-utils/pkg/apis/actuation"
913
"sigs.k8s.io/cli-utils/pkg/apply/event"
1014
"sigs.k8s.io/cli-utils/pkg/apply/taskrunner"
1115
"sigs.k8s.io/cli-utils/pkg/common"
1216
"sigs.k8s.io/cli-utils/pkg/inventory"
17+
"sigs.k8s.io/cli-utils/pkg/inventory2"
1318
"sigs.k8s.io/cli-utils/pkg/object"
1419
)
1520

1621
// DeleteOrUpdateInvTask encapsulates structures necessary to set the
1722
// inventory references at the end of the apply/prune.
1823
type DeleteOrUpdateInvTask struct {
1924
TaskName string
20-
InvClient inventory.Client
25+
InvClient inventory2.Client
2126
InvInfo inventory.Info
2227
PrevInventory object.ObjMetadataSet
2328
DryRun common.DryRunStrategy
29+
StatusPolicy inventory.StatusPolicy
2430
// if Destroy is set, the inventory will be deleted if all objects were successfully pruned
2531
Destroy bool
2632
}
@@ -47,12 +53,14 @@ func (i *DeleteOrUpdateInvTask) Identifiers() object.ObjMetadataSet {
4753
// If Destroy is false, the inventory will be updated.
4854
func (i *DeleteOrUpdateInvTask) Start(taskContext *taskrunner.TaskContext) {
4955
go func() {
56+
klog.V(2).Infof("inventory set task starting (name: %q)", i.TaskName)
5057
var err error
5158
if i.Destroy && i.destroySuccessful(taskContext) {
5259
err = i.deleteInventory()
5360
} else {
5461
err = i.updateInventory(taskContext)
5562
}
63+
klog.V(2).Infof("inventory set task completing (name: %q)", i.TaskName)
5664
taskContext.TaskChannel() <- taskrunner.TaskResult{Err: err}
5765
}()
5866
}
@@ -84,7 +92,16 @@ func (i *DeleteOrUpdateInvTask) StatusUpdate(_ *taskrunner.TaskContext, _ object
8492
// - Deleted resources (successful)
8593
// - Abandoned resources (successful)
8694
func (i *DeleteOrUpdateInvTask) updateInventory(taskContext *taskrunner.TaskContext) error {
87-
klog.V(2).Infof("inventory set task starting (name: %q)", i.TaskName)
95+
id := inventory2.ID{
96+
Name: i.InvInfo.Name(),
97+
Namespace: i.InvInfo.Namespace(),
98+
}
99+
inv, err := i.InvClient.Get(context.TODO(), id)
100+
if err != nil {
101+
return fmt.Errorf("getting inventory: %w")
102+
}
103+
prevObjs := inventory.ObjMetadataSetFromObjectReferenceList(inv.Spec.Objects).Unique()
104+
88105
invObjs := object.ObjMetadataSet{}
89106

90107
// TODO: Just use InventoryManager.Store()
@@ -100,7 +117,7 @@ func (i *DeleteOrUpdateInvTask) updateInventory(taskContext *taskrunner.TaskCont
100117
// This will remove new resources that failed to apply from the inventory,
101118
// because even tho they were added by InvAddTask, the PrevInventory
102119
// represents the inventory before the pipeline has run.
103-
applyFailures := i.PrevInventory.Intersection(im.FailedApplies())
120+
applyFailures := prevObjs.Intersection(im.FailedApplies())
104121
klog.V(4).Infof("keep in inventory %d failed applies", len(applyFailures))
105122
invObjs = invObjs.Union(applyFailures)
106123

@@ -109,7 +126,7 @@ func (i *DeleteOrUpdateInvTask) updateInventory(taskContext *taskrunner.TaskCont
109126
// It's likely that all the skipped applies are already in the inventory,
110127
// because the apply filters all currently depend on cluster state,
111128
// but we're doing the intersection anyway just to be sure.
112-
applySkips := i.PrevInventory.Intersection(im.SkippedApplies())
129+
applySkips := prevObjs.Intersection(im.SkippedApplies())
113130
klog.V(4).Infof("keep in inventory %d skipped applies", len(applySkips))
114131
invObjs = invObjs.Union(applySkips)
115132

@@ -118,7 +135,7 @@ func (i *DeleteOrUpdateInvTask) updateInventory(taskContext *taskrunner.TaskCont
118135
// It's likely that all the delete failures are already in the inventory,
119136
// because the set of resources to prune comes from the inventory,
120137
// but we're doing the intersection anyway just to be sure.
121-
pruneFailures := i.PrevInventory.Intersection(im.FailedDeletes())
138+
pruneFailures := prevObjs.Intersection(im.FailedDeletes())
122139
klog.V(4).Infof("set inventory %d failed prunes", len(pruneFailures))
123140
invObjs = invObjs.Union(pruneFailures)
124141

@@ -127,19 +144,19 @@ func (i *DeleteOrUpdateInvTask) updateInventory(taskContext *taskrunner.TaskCont
127144
// It's likely that all the skipped deletes are already in the inventory,
128145
// because the set of resources to prune comes from the inventory,
129146
// but we're doing the intersection anyway just to be sure.
130-
pruneSkips := i.PrevInventory.Intersection(im.SkippedDeletes())
147+
pruneSkips := prevObjs.Intersection(im.SkippedDeletes())
131148
klog.V(4).Infof("keep in inventory %d skipped prunes", len(pruneSkips))
132149
invObjs = invObjs.Union(pruneSkips)
133150

134151
// If an object failed to reconcile and was previously stored in the inventory,
135152
// then keep it in the inventory so it can be waited on next time.
136-
reconcileFailures := i.PrevInventory.Intersection(im.FailedReconciles())
153+
reconcileFailures := prevObjs.Intersection(im.FailedReconciles())
137154
klog.V(4).Infof("set inventory %d failed reconciles", len(reconcileFailures))
138155
invObjs = invObjs.Union(reconcileFailures)
139156

140157
// If an object timed out reconciling and was previously stored in the inventory,
141158
// then keep it in the inventory so it can be waited on next time.
142-
reconcileTimeouts := i.PrevInventory.Intersection(im.TimeoutReconciles())
159+
reconcileTimeouts := prevObjs.Intersection(im.TimeoutReconciles())
143160
klog.V(4).Infof("keep in inventory %d timeout reconciles", len(reconcileTimeouts))
144161
invObjs = invObjs.Union(reconcileTimeouts)
145162

@@ -150,24 +167,44 @@ func (i *DeleteOrUpdateInvTask) updateInventory(taskContext *taskrunner.TaskCont
150167

151168
// If an object is invalid and was previously stored in the inventory,
152169
// then keep it in the inventory so it can be applied/pruned next time.
153-
invalidObjects := i.PrevInventory.Intersection(taskContext.InvalidObjects())
170+
invalidObjects := prevObjs.Intersection(taskContext.InvalidObjects())
154171
klog.V(4).Infof("keep in inventory %d invalid objects", len(invalidObjects))
155172
invObjs = invObjs.Union(invalidObjects)
156173

174+
// Update inventory spec in memory
175+
inv.Spec.Objects = inventory.ObjectReferenceListFromObjMetadataSet(invObjs)
176+
157177
klog.V(4).Infof("get the apply status for %d objects", len(invObjs))
158-
objStatus := taskContext.InventoryManager().Inventory().Status.Objects
178+
inv.Status.Objects = taskContext.InventoryManager().Inventory().Status.Objects
159179

160180
klog.V(4).Infof("set inventory %d total objects", len(invObjs))
161-
err := i.InvClient.Replace(i.InvInfo, invObjs, objStatus, i.DryRun)
181+
if err = i.InvClient.Update(context.TODO(), inv, i.updateOptionList()...); err != nil {
182+
return fmt.Errorf("updating inventory: %w")
183+
}
184+
return nil
185+
}
162186

163-
klog.V(2).Infof("inventory set task completing (name: %q)", i.TaskName)
164-
return err
187+
func (i *DeleteOrUpdateInvTask) updateOptionList() []inventory2.UpdateOption {
188+
return []inventory2.UpdateOption{
189+
inventory2.WithDryRun(i.DryRun),
190+
inventory2.WithStatus(i.StatusPolicy),
191+
}
192+
}
193+
194+
func (i *DeleteOrUpdateInvTask) deleteOptionList() []inventory2.DeleteOption {
195+
return []inventory2.DeleteOption{
196+
inventory2.WithDryRun(i.DryRun),
197+
inventory2.WithStatus(i.StatusPolicy),
198+
}
165199
}
166200

167201
// deleteInventory deletes the inventory object from the cluster.
168202
func (i *DeleteOrUpdateInvTask) deleteInventory() error {
169203
klog.V(2).Infof("delete inventory task starting (name: %q)", i.Name())
170-
err := i.InvClient.DeleteInventoryObj(i.InvInfo, i.DryRun)
204+
inv := &actuation.Inventory{}
205+
inv.SetName(i.InvInfo.Name())
206+
inv.SetName(i.InvInfo.Namespace())
207+
err := i.InvClient.Delete(context.TODO(), inv, i.deleteOptionList()...)
171208
// Not found is not error, since this means it was already deleted.
172209
if apierrors.IsNotFound(err) {
173210
err = nil

pkg/inventory/type-conv.go

+16
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,19 @@ func ObjMetadataFromObjectReference(ref actuation.ObjectReference) object.ObjMet
3030
Namespace: ref.Namespace,
3131
}
3232
}
33+
34+
func ObjectReferenceListFromObjMetadataSet(ids []object.ObjMetadata) []actuation.ObjectReference {
35+
var refs []actuation.ObjectReference
36+
for _, ref := range ids {
37+
refs = append(refs, ObjectReferenceFromObjMetadata(ref))
38+
}
39+
return refs
40+
}
41+
42+
func ObjMetadataSetFromObjectReferenceList(refs []actuation.ObjectReference) object.ObjMetadataSet {
43+
var ids object.ObjMetadataSet
44+
for _, ref := range refs {
45+
ids = append(ids, ObjMetadataFromObjectReference(ref))
46+
}
47+
return ids
48+
}

pkg/inventory2/client.go

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package inventory2
2+
3+
import (
4+
"context"
5+
6+
"sigs.k8s.io/cli-utils/pkg/apis/actuation"
7+
"sigs.k8s.io/cli-utils/pkg/common"
8+
"sigs.k8s.io/cli-utils/pkg/inventory"
9+
"sigs.k8s.io/controller-runtime/pkg/client"
10+
)
11+
12+
type ID client.ObjectKey
13+
14+
type Client interface {
15+
ReadClient
16+
WriteClient
17+
}
18+
19+
type ReadClient interface {
20+
Get(ctx context.Context, id ID, opts ...GetOption) (*actuation.Inventory, error)
21+
List(ctx context.Context, opts ...ListOption) error
22+
}
23+
24+
type WriteClient interface {
25+
Create(ctx context.Context, inv *actuation.Inventory, opts ...CreateOption) error
26+
Update(ctx context.Context, inv *actuation.Inventory, opts ...UpdateOption) error
27+
Delete(ctx context.Context, inv *actuation.Inventory, opts ...DeleteOption) error
28+
}
29+
30+
type CreateOption interface {
31+
ApplyCreateOptions(opts *CreateOptions)
32+
}
33+
34+
type CreateOptions struct {
35+
DryRunStrategy common.DryRunStrategy
36+
StatusPolicy inventory.StatusPolicy
37+
}
38+
39+
type GetOption interface {
40+
ApplyGetOptions(opts *GetOptions)
41+
}
42+
43+
type GetOptions struct {
44+
ResourceVersion string
45+
LabelSelector string
46+
}
47+
48+
type UpdateOption interface {
49+
ApplyUpdateOptions(opts *UpdateOptions)
50+
}
51+
52+
type UpdateOptions struct {
53+
DryRunStrategy common.DryRunStrategy
54+
StatusPolicy inventory.StatusPolicy
55+
}
56+
57+
type DeleteOption interface {
58+
ApplyDeleteOptions(opts *DeleteOptions)
59+
}
60+
61+
type DeleteOptions struct {
62+
DryRunStrategy common.DryRunStrategy
63+
}
64+
65+
type ListOption interface {
66+
ApplyListOptions(opts *ListOptions)
67+
}
68+
69+
type ListOptions struct {
70+
ResourceVersion string
71+
LabelSelector string
72+
}
73+
74+
func WithDryRun(strategy common.DryRunStrategy) DryRunOption {
75+
return DryRunOption(strategy)
76+
}
77+
78+
type DryRunOption common.DryRunStrategy
79+
80+
func (o DryRunOption) ApplyCreateOptions(opts *CreateOptions) {
81+
opts.DryRunStrategy = common.DryRunStrategy(o)
82+
}
83+
84+
func (o DryRunOption) ApplyUpdateOptions(opts *UpdateOptions) {
85+
opts.DryRunStrategy = common.DryRunStrategy(o)
86+
}
87+
88+
func (o DryRunOption) ApplyDeleteOptions(opts *DeleteOptions) {
89+
opts.DryRunStrategy = common.DryRunStrategy(o)
90+
}
91+
92+
var _ CreateOption = DryRunOption(common.DryRunServer)
93+
var _ UpdateOption = DryRunOption(common.DryRunServer)
94+
var _ DeleteOption = DryRunOption(common.DryRunServer)
95+
96+
func WithStatus(policy inventory.StatusPolicy) StatusOption {
97+
return StatusOption(policy)
98+
}
99+
100+
type StatusOption common.DryRunStrategy
101+
102+
func (o StatusOption) ApplyCreateOptions(opts *CreateOptions) {
103+
opts.DryRunStrategy = common.DryRunStrategy(o)
104+
}
105+
106+
func (o StatusOption) ApplyUpdateOptions(opts *UpdateOptions) {
107+
opts.DryRunStrategy = common.DryRunStrategy(o)
108+
}
109+
110+
func (o StatusOption) ApplyDeleteOptions(opts *DeleteOptions) {
111+
opts.DryRunStrategy = common.DryRunStrategy(o)
112+
}
113+
114+
var _ CreateOption = StatusOption(inventory.StatusPolicyAll)
115+
var _ UpdateOption = StatusOption(inventory.StatusPolicyAll)
116+
var _ DeleteOption = StatusOption(inventory.StatusPolicyAll)

0 commit comments

Comments
 (0)