@@ -19,9 +19,11 @@ package appwrapper
19
19
import (
20
20
"context"
21
21
"fmt"
22
+ "time"
22
23
23
24
workloadv1beta2 "github.com/project-codeflare/appwrapper/api/v1beta2"
24
25
"github.com/project-codeflare/appwrapper/pkg/utils"
26
+ v1 "k8s.io/api/core/v1"
25
27
apierrors "k8s.io/apimachinery/pkg/api/errors"
26
28
"k8s.io/apimachinery/pkg/api/meta"
27
29
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -164,7 +166,11 @@ func (r *AppWrapperReconciler) createComponents(ctx context.Context, aw *workloa
164
166
}
165
167
166
168
func (r * AppWrapperReconciler ) deleteComponents (ctx context.Context , aw * workloadv1beta2.AppWrapper ) bool {
167
- // TODO forceful deletion: See https://github.com/project-codeflare/appwrapper/issues/36
169
+ meta .SetStatusCondition (& aw .Status .Conditions , metav1.Condition {
170
+ Type : string (workloadv1beta2 .DeletingResources ),
171
+ Status : metav1 .ConditionTrue ,
172
+ Reason : "DeletionInitiated" ,
173
+ })
168
174
log := log .FromContext (ctx )
169
175
remaining := 0
170
176
for _ , component := range aw .Spec .Components {
@@ -181,5 +187,59 @@ func (r *AppWrapperReconciler) deleteComponents(ctx context.Context, aw *workloa
181
187
}
182
188
remaining ++ // no error deleting resource, resource therefore still exists
183
189
}
184
- return remaining == 0
190
+
191
+ deletionGracePeriod := r .deletionGraceDuration (ctx , aw )
192
+ if deletionGracePeriod <= 0 {
193
+ // forced deletion is disabled; once remaining is 0 we are done
194
+ if remaining == 0 {
195
+ clearCondition (aw , workloadv1beta2 .DeletingResources , "DeletionComplete" , "" )
196
+ return true
197
+ } else {
198
+ return false
199
+ }
200
+ }
201
+
202
+ pods := & v1.PodList {Items : []v1.Pod {}}
203
+ if err := r .List (ctx , pods ,
204
+ client .UnsafeDisableDeepCopy ,
205
+ client .InNamespace (aw .Namespace ),
206
+ client.MatchingLabels {AppWrapperLabel : aw .Name }); err != nil {
207
+ log .Error (err , "Pod list error" )
208
+ }
209
+
210
+ if remaining == 0 && len (pods .Items ) == 0 {
211
+ // no resources, no pods, deletion is complete
212
+ clearCondition (aw , workloadv1beta2 .DeletingResources , "DeletionComplete" , "" )
213
+ return true
214
+ }
215
+
216
+ whenInitiated := meta .FindStatusCondition (aw .Status .Conditions , string (workloadv1beta2 .DeletingResources )).LastTransitionTime
217
+ if time .Now ().Before (whenInitiated .Time .Add (deletionGracePeriod )) {
218
+ // Deadline hasn't expired, just requeue the deletion
219
+ return false
220
+ }
221
+
222
+ if len (pods .Items ) > 0 {
223
+ // force deletion of pods first
224
+ for _ , pod := range pods .Items {
225
+ if err := r .Delete (ctx , & pod , client .GracePeriodSeconds (0 )); err != nil {
226
+ log .Error (err , "Forceful pod deletion error" )
227
+ }
228
+ }
229
+ } else {
230
+ // force deletion of wrapped resources once pods are gone
231
+ for _ , component := range aw .Spec .Components {
232
+ obj , err := parseComponent (aw , component .Template .Raw )
233
+ if err != nil {
234
+ log .Error (err , "Parsing error" )
235
+ continue
236
+ }
237
+ if err := r .Delete (ctx , obj , client .GracePeriodSeconds (0 )); err != nil && ! apierrors .IsNotFound (err ) {
238
+ log .Error (err , "Forceful deletion error" )
239
+ }
240
+ }
241
+ }
242
+
243
+ // requeue deletion
244
+ return false
185
245
}
0 commit comments