1
16
17 package apply
18
19 import (
20 "context"
21 "fmt"
22 "sync"
23
24 "k8s.io/apimachinery/pkg/api/meta"
25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26 "k8s.io/apimachinery/pkg/runtime"
27 "k8s.io/apimachinery/pkg/types"
28 "k8s.io/apimachinery/pkg/util/sets"
29 "k8s.io/cli-runtime/pkg/genericiooptions"
30 "k8s.io/cli-runtime/pkg/printers"
31 "k8s.io/client-go/dynamic"
32 "k8s.io/klog/v2"
33 cmdutil "k8s.io/kubectl/pkg/cmd/util"
34 )
35
36 type ApplySetDeleteOptions struct {
37 CascadingStrategy metav1.DeletionPropagation
38 DryRunStrategy cmdutil.DryRunStrategy
39 GracePeriod int
40
41 Printer printers.ResourcePrinter
42
43 IOStreams genericiooptions.IOStreams
44 }
45
46
47 type PruneObject struct {
48 Name string
49 Namespace string
50 Mapping *meta.RESTMapping
51 Object runtime.Object
52 }
53
54
55 func (p *PruneObject) String() string {
56 s := p.Mapping.GroupVersionKind.GroupKind().String()
57
58 if p.Namespace != "" {
59 s += " " + p.Namespace + "/" + p.Name
60 } else {
61 s += " " + p.Name
62 }
63 return s
64 }
65
66
67
68 func (a *ApplySet) FindAllObjectsToPrune(ctx context.Context, dynamicClient dynamic.Interface, visitedUids sets.Set[types.UID]) ([]PruneObject, error) {
69 type task struct {
70 namespace string
71 restMapping *meta.RESTMapping
72
73 err error
74 results []PruneObject
75 }
76 var tasks []*task
77
78
79
80 for gvk, resource := range a.AllPrunableResources() {
81 scope := resource.restMapping.Scope
82
83 switch scope.Name() {
84 case meta.RESTScopeNameNamespace:
85 for _, namespace := range a.AllPrunableNamespaces() {
86 if namespace == "" {
87
88 return nil, fmt.Errorf("unexpectedly encountered empty namespace during prune of namespace-scoped resource %v", gvk)
89 }
90 tasks = append(tasks, &task{
91 namespace: namespace,
92 restMapping: resource.restMapping,
93 })
94 }
95
96 case meta.RESTScopeNameRoot:
97 tasks = append(tasks, &task{
98 restMapping: resource.restMapping,
99 })
100
101 default:
102 return nil, fmt.Errorf("unhandled scope %q", scope.Name())
103 }
104 }
105
106 var wg sync.WaitGroup
107
108 for i := range tasks {
109 task := tasks[i]
110 wg.Add(1)
111 go func() {
112 defer wg.Done()
113
114 results, err := a.findObjectsToPrune(ctx, dynamicClient, visitedUids, task.namespace, task.restMapping)
115 if err != nil {
116 task.err = fmt.Errorf("listing %v objects for pruning: %w", task.restMapping.GroupVersionKind.String(), err)
117 } else {
118 task.results = results
119 }
120 }()
121 }
122
123 wg.Wait()
124
125 var allObjects []PruneObject
126 for _, task := range tasks {
127 if task.err != nil {
128 return nil, task.err
129 }
130 allObjects = append(allObjects, task.results...)
131 }
132 return allObjects, nil
133 }
134
135 func (a *ApplySet) pruneAll(ctx context.Context, dynamicClient dynamic.Interface, visitedUids sets.Set[types.UID], deleteOptions *ApplySetDeleteOptions) error {
136 allObjects, err := a.FindAllObjectsToPrune(ctx, dynamicClient, visitedUids)
137 if err != nil {
138 return err
139 }
140
141 return a.deleteObjects(ctx, dynamicClient, allObjects, deleteOptions)
142 }
143
144 func (a *ApplySet) findObjectsToPrune(ctx context.Context, dynamicClient dynamic.Interface, visitedUids sets.Set[types.UID], namespace string, mapping *meta.RESTMapping) ([]PruneObject, error) {
145 applysetLabelSelector := a.LabelSelectorForMembers()
146
147 opt := metav1.ListOptions{
148 LabelSelector: applysetLabelSelector,
149 }
150
151 klog.V(2).Infof("listing objects for pruning; namespace=%q, resource=%v", namespace, mapping.Resource)
152 objects, err := dynamicClient.Resource(mapping.Resource).Namespace(namespace).List(ctx, opt)
153 if err != nil {
154 return nil, err
155 }
156
157 var pruneObjects []PruneObject
158 for i := range objects.Items {
159 obj := &objects.Items[i]
160
161 uid := obj.GetUID()
162 if visitedUids.Has(uid) {
163 continue
164 }
165 name := obj.GetName()
166 pruneObjects = append(pruneObjects, PruneObject{
167 Name: name,
168 Namespace: namespace,
169 Mapping: mapping,
170 Object: obj,
171 })
172
173 }
174 return pruneObjects, nil
175 }
176
177 func (a *ApplySet) deleteObjects(ctx context.Context, dynamicClient dynamic.Interface, pruneObjects []PruneObject, opt *ApplySetDeleteOptions) error {
178 for i := range pruneObjects {
179 pruneObject := &pruneObjects[i]
180
181 name := pruneObject.Name
182 namespace := pruneObject.Namespace
183 mapping := pruneObject.Mapping
184
185 if opt.DryRunStrategy != cmdutil.DryRunClient {
186 if err := runDelete(ctx, namespace, name, mapping, dynamicClient, opt.CascadingStrategy, opt.GracePeriod, opt.DryRunStrategy == cmdutil.DryRunServer); err != nil {
187 return fmt.Errorf("pruning %v: %w", pruneObject.String(), err)
188 }
189 }
190
191 opt.Printer.PrintObj(pruneObject.Object, opt.IOStreams.Out)
192
193 }
194 return nil
195 }
196
View as plain text