@@ -83,114 +83,174 @@ type Options struct {
8383}
8484
8585// Prune deletes the set of resources which were previously applied
86- // (retrieved from previous inventory objects) but omitted in
87- // the current apply. Prune also delete all previous inventory
88- // objects. Returns an error if there was a problem.
89- func (po * PruneOptions ) Prune (localInv inventory.InventoryInfo , localObjs []* unstructured.Unstructured , currentUIDs sets.String ,
90- taskContext * taskrunner.TaskContext , o Options ) error {
86+ // but omitted in the current apply. Calculates the set of objects
87+ // to prune by removing the currently applied objects from the union
88+ // set of the previously applied objects and currently applied objects
89+ // stored in the cluster inventory. As a final step, stores the current
90+ // inventory which is all the successfully applied objects and the
91+ // prune failures. Does not stop when encountering prune failures.
92+ // Returns an error for unrecoverable errors.
93+ //
94+ // Parameters:
95+ // localInv - locally read inventory object
96+ // localObjs - locally read, currently applied (attempted) objects
97+ // currentUIDs - UIDs for successfully applied objects
98+ // taskContext - task for apply/prune
99+ func (po * PruneOptions ) Prune (localInv inventory.InventoryInfo ,
100+ localObjs []* unstructured.Unstructured ,
101+ currentUIDs sets.String ,
102+ taskContext * taskrunner.TaskContext ,
103+ o Options ) error {
104+ // Validate parameters
91105 if localInv == nil {
92106 return fmt .Errorf ("the local inventory object can't be nil" )
93107 }
94- invNamespace := localInv .Namespace ()
95- klog .V (4 ).Infof ("prune local inventory object: %s/%s" , invNamespace , localInv .Name ())
96108 // Get the list of Object Meta from the local objects.
97109 localIds := object .UnstructuredsToObjMetas (localObjs )
98110 // Create the set of namespaces for currently (locally) applied objects, including
99111 // the namespace the inventory object lives in (if it's not cluster-scoped). When
100112 // pruning, check this set of namespaces to ensure these namespaces are not deleted.
101- localNamespaces := mergeObjNamespaces (localObjs )
102- if invNamespace != "" {
103- localNamespaces .Insert (invNamespace )
104- }
105- clusterObjs , err := po .InvClient .GetClusterObjs (localInv )
113+ localNamespaces := localNamespaces (localInv , localIds )
114+ clusterInv , err := po .InvClient .GetClusterObjs (localInv )
106115 if err != nil {
107116 return err
108117 }
109- klog .V (4 ).Infof ("prune %d currently applied objects" , len (currentUIDs ))
110- klog .V (4 ).Infof ("prune %d previously applied objects" , len (clusterObjs ))
118+ klog .V (4 ).Infof ("prune: %d objects attempted to apply" , len (localIds ))
119+ klog .V (4 ).Infof ("prune: %d objects successfully applied" , len (currentUIDs ))
120+ klog .V (4 ).Infof ("prune: %d union objects stored in cluster inventory" , len (clusterInv ))
121+ pruneObjs := object .SetDiff (clusterInv , localIds )
122+ klog .V (4 ).Infof ("prune: %d objects to prune (clusterInv - localIds)" , len (pruneObjs ))
111123 // Sort the resources in reverse order using the same rules as is
112124 // used for apply.
113- sort .Sort (sort .Reverse (ordering .SortableMetas (clusterObjs )))
114- for _ , clusterObj := range clusterObjs {
115- mapping , err := po .mapper .RESTMapping (clusterObj .GroupKind )
116- if err != nil {
117- localIds = append (localIds , clusterObj )
118- taskContext .EventChannel () <- createPruneFailedEvent (clusterObj , err )
119- taskContext .CaptureResourceFailure (clusterObj )
120- continue
121- }
122- namespacedClient := po .client .Resource (mapping .Resource ).Namespace (clusterObj .Namespace )
123- obj , err := namespacedClient .Get (context .TODO (), clusterObj .Name , metav1.GetOptions {})
125+ sort .Sort (sort .Reverse (ordering .SortableMetas (pruneObjs )))
126+ // Store prune failures to ensure they remain in the inventory.
127+ pruneFailures := []object.ObjMetadata {}
128+ for _ , pruneObj := range pruneObjs {
129+ klog .V (5 ).Infof ("attempting prune: %s" , pruneObj )
130+ obj , err := po .getObject (pruneObj )
124131 if err != nil {
125132 // Object not found in cluster, so no need to delete it; skip to next object.
126133 if apierrors .IsNotFound (err ) {
134+ klog .V (5 ).Infof ("%s/%s not found in cluster--skipping" ,
135+ pruneObj .Namespace , pruneObj .Name )
127136 continue
128137 }
129- localIds = append (localIds , clusterObj )
130- taskContext .EventChannel () <- createPruneFailedEvent (clusterObj , err )
131- taskContext .CaptureResourceFailure (clusterObj )
132- continue
133- }
134- metadata , err := meta .Accessor (obj )
135- if err != nil {
136- localIds = append (localIds , clusterObj )
137- taskContext .EventChannel () <- createPruneFailedEvent (clusterObj , err )
138- taskContext .CaptureResourceFailure (clusterObj )
138+ if klog .V (5 ) {
139+ klog .Errorf ("prune obj (%s/%s) UID retrival error: %s" ,
140+ pruneObj .Namespace , pruneObj .Name , err )
141+ }
142+ pruneFailures = append (pruneFailures , pruneObj )
143+ taskContext .EventChannel () <- createPruneFailedEvent (pruneObj , err )
144+ taskContext .CaptureResourceFailure (pruneObj )
139145 continue
140146 }
141- // If this cluster object is not also a currently applied
142- // object, then it has been omitted--prune it. If the cluster
143- // object is part of the local apply set, skip it.
144- uid := string (metadata .GetUID ())
145- klog .V (7 ).Infof ("prune previously applied object UID: %s" , uid )
147+ // Do not prune objects that are in set of currently applied objects.
148+ uid := string (obj .GetUID ())
146149 if currentUIDs .Has (uid ) {
147- klog .V (7 ).Infof ("prune object in current apply; do not prune: %s" , uid )
150+ klog .V (5 ).Infof ("prune object in current apply; do not prune: %s" , uid )
148151 continue
149152 }
150153 // Handle lifecycle directive preventing deletion.
151154 if ! canPrune (localInv , obj , o .InventoryPolicy , uid ) {
152- taskContext .EventChannel () <- createPruneEvent (clusterObj , obj , event .PruneSkipped )
153- localIds = append (localIds , clusterObj )
155+ klog .V (4 ).Infof ("skip prune for lifecycle directive %s/%s" , pruneObj .Namespace , pruneObj .Name )
156+ taskContext .EventChannel () <- createPruneEvent (pruneObj , obj , event .PruneSkipped )
157+ pruneFailures = append (pruneFailures , pruneObj )
154158 continue
155159 }
156160 // If regular pruning (not destroying), skip deleting namespace containing
157161 // currently applied objects.
158162 if ! po .Destroy {
159- if clusterObj .GroupKind == object .CoreV1Namespace .GroupKind () &&
160- localNamespaces .Has (clusterObj .Name ) {
161- klog .V (4 ).Infof ("skip pruning namespace: %s" , clusterObj .Name )
162- taskContext .EventChannel () <- createPruneEvent (clusterObj , obj , event .PruneSkipped )
163- localIds = append (localIds , clusterObj )
164- taskContext .CaptureResourceFailure (clusterObj )
163+ if pruneObj .GroupKind == object .CoreV1Namespace .GroupKind () &&
164+ localNamespaces .Has (pruneObj .Name ) {
165+ klog .V (4 ).Infof ("skip pruning namespace: %s" , pruneObj .Name )
166+ taskContext .EventChannel () <- createPruneEvent (pruneObj , obj , event .PruneSkipped )
167+ pruneFailures = append (pruneFailures , pruneObj )
168+ taskContext .CaptureResourceFailure (pruneObj )
165169 continue
166170 }
167171 }
168172 if ! o .DryRunStrategy .ClientOrServerDryRun () {
169- klog .V (4 ).Infof ("prune object delete: %s/%s" , clusterObj .Namespace , clusterObj .Name )
170- err = namespacedClient . Delete ( context . TODO (), clusterObj . Name , metav1. DeleteOptions {} )
173+ klog .V (4 ).Infof ("prune object delete: %s/%s" , pruneObj .Namespace , pruneObj .Name )
174+ namespacedClient , err := po . namespacedClient ( pruneObj )
171175 if err != nil {
172- taskContext .EventChannel () <- createPruneFailedEvent (clusterObj , err )
173- localIds = append (localIds , clusterObj )
174- taskContext .CaptureResourceFailure (clusterObj )
176+ if klog .V (4 ) {
177+ klog .Errorf ("prune failed for %s/%s (%s)" , pruneObj .Namespace , pruneObj .Name , err )
178+ }
179+ taskContext .EventChannel () <- createPruneFailedEvent (pruneObj , err )
180+ pruneFailures = append (pruneFailures , pruneObj )
181+ taskContext .CaptureResourceFailure (pruneObj )
175182 continue
176183 }
184+ err = namespacedClient .Delete (context .TODO (), pruneObj .Name , metav1.DeleteOptions {})
185+ if err != nil {
186+ if klog .V (4 ) {
187+ klog .Errorf ("prune failed for %s/%s (%s)" , pruneObj .Namespace , pruneObj .Name , err )
188+ }
189+ taskContext .EventChannel () <- createPruneFailedEvent (pruneObj , err )
190+ pruneFailures = append (pruneFailures , pruneObj )
191+ taskContext .CaptureResourceFailure (pruneObj )
192+ continue
193+ }
194+ }
195+ taskContext .EventChannel () <- createPruneEvent (pruneObj , obj , event .Pruned )
196+ }
197+ // Calculate final inventory items, ensuring only successfully applied
198+ // objects are in the inventory along with prune failures.
199+ finalInventory := []object.ObjMetadata {}
200+ for _ , localObj := range localIds {
201+ obj , err := po .getObject (localObj )
202+ if err != nil {
203+ if klog .V (4 ) {
204+ klog .Errorf ("error retrieving object for inventory determination: %s" , err )
205+ }
206+ continue
207+ }
208+ uid := string (obj .GetUID ())
209+ if currentUIDs .Has (uid ) {
210+ klog .V (5 ).Infof ("adding final inventory object %s/%s" , localObj .Namespace , localObj .Name )
211+ finalInventory = append (finalInventory , localObj )
212+ } else {
213+ klog .V (5 ).Infof ("uid not found (%s); not adding final inventory obj %s/%s" ,
214+ uid , localObj .Namespace , localObj .Name )
177215 }
178- taskContext .EventChannel () <- createPruneEvent (clusterObj , obj , event .Pruned )
179216 }
180- return po .InvClient .Replace (localInv , localIds )
217+ klog .V (4 ).Infof ("final inventory %d successfully applied objects" , len (finalInventory ))
218+ finalInventory = append (finalInventory , pruneFailures ... )
219+ klog .V (4 ).Infof ("final inventory %d objects after appending prune failures" , len (finalInventory ))
220+ return po .InvClient .Replace (localInv , finalInventory )
181221}
182222
183- // mergeObjNamespaces returns a set of strings of all the namespaces
184- // for non cluster-scoped objects. These namespaces are forced to
185- // lower-case.
186- func mergeObjNamespaces (objs []* unstructured.Unstructured ) sets.String {
223+ func (po * PruneOptions ) namespacedClient (obj object.ObjMetadata ) (dynamic.ResourceInterface , error ) {
224+ mapping , err := po .mapper .RESTMapping (obj .GroupKind )
225+ if err != nil {
226+ return nil , err
227+ }
228+ return po .client .Resource (mapping .Resource ).Namespace (obj .Namespace ), nil
229+ }
230+
231+ func (po * PruneOptions ) getObject (obj object.ObjMetadata ) (* unstructured.Unstructured , error ) {
232+ namespacedClient , err := po .namespacedClient (obj )
233+ if err != nil {
234+ return nil , err
235+ }
236+ return namespacedClient .Get (context .TODO (), obj .Name , metav1.GetOptions {})
237+ }
238+
239+ // localNamespaces returns a set of strings of all the namespaces
240+ // for the passed non cluster-scoped localObjs, plus the namespace
241+ // of the passed inventory object.
242+ func localNamespaces (localInv inventory.InventoryInfo , localObjs []object.ObjMetadata ) sets.String {
187243 namespaces := sets .NewString ()
188- for _ , obj := range objs {
189- namespace := strings .TrimSpace (strings .ToLower (obj .GetNamespace () ))
244+ for _ , obj := range localObjs {
245+ namespace := strings .TrimSpace (strings .ToLower (obj .Namespace ))
190246 if namespace != "" {
191247 namespaces .Insert (namespace )
192248 }
193249 }
250+ invNamespace := strings .TrimSpace (strings .ToLower (localInv .Namespace ()))
251+ if invNamespace != "" {
252+ namespaces .Insert (invNamespace )
253+ }
194254 return namespaces
195255}
196256
0 commit comments