Skip to content

Commit 5c6804c

Browse files
committed
track status of each deployed component in AppWrapper Status
1 parent 5addc12 commit 5c6804c

File tree

5 files changed

+107
-31
lines changed

5 files changed

+107
-31
lines changed

api/v1beta2/appwrapper_types.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,24 @@ type AppWrapperStatus struct {
105105
//+listType=map
106106
//+listMapKey=type
107107
Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
108+
109+
// ComponentStatus parallels the Components array in the Spec and tracks the actually deployed resources
110+
ComponentStatus []AppWrapperComponentStatus `json:"componentStatus,omitempty"`
111+
}
112+
113+
// AppWrapperComponentStatus tracks the status of a single managed Component
114+
type AppWrapperComponentStatus struct {
115+
// Name is the name of the Component
116+
Name string `json:"name"`
117+
118+
// Kind is the Kind of the Component
119+
Kind string `json:"kind"`
120+
121+
// APIVersion is the APIVersion of the Component
122+
APIVersion string `json:"apiVersion"`
123+
124+
// Deployed is true when the AppWrapper controller believes the Component is currently deployed
125+
Deployed bool `json:"deployed"`
108126
}
109127

110128
// AppWrapperPhase is the phase of the appwrapper

api/v1beta2/zz_generated.deepcopy.go

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/bases/workload.codeflare.dev_appwrappers.yaml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,33 @@ spec:
165165
status:
166166
description: AppWrapperStatus defines the observed state of the appwrapper
167167
properties:
168+
componentStatus:
169+
description: ComponentStatus parallels the Components array in the
170+
Spec and tracks the actually deployed resources
171+
items:
172+
description: AppWrapperComponentStatus tracks the status of a single
173+
managed Component
174+
properties:
175+
apiVersion:
176+
description: APIVersion is the APIVersion of the Component
177+
type: string
178+
deployed:
179+
description: Deployed is true when the AppWrapper controller
180+
believes the Component is currently deployed
181+
type: boolean
182+
kind:
183+
description: Kind is the Kind of the Component
184+
type: string
185+
name:
186+
description: Name is the name of the Component
187+
type: string
188+
required:
189+
- apiVersion
190+
- deployed
191+
- kind
192+
- name
193+
type: object
194+
type: array
168195
conditions:
169196
description: |-
170197
Conditions hold the latest available observations of the AppWrapper current state.

internal/controller/appwrapper/appwrapper_controller.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ func (r *AppWrapperReconciler) Reconcile(ctx context.Context, req ctrl.Request)
144144
return ctrl.Result{}, err
145145
}
146146
}
147+
aw.Status.ComponentStatus = make([]workloadv1beta2.AppWrapperComponentStatus, len(aw.Spec.Components))
147148
return r.updateStatus(ctx, aw, workloadv1beta2.AppWrapperSuspended)
148149

149150
case workloadv1beta2.AppWrapperSuspended: // no components deployed

internal/controller/appwrapper/resource_management.go

Lines changed: 41 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -179,42 +179,59 @@ func (r *AppWrapperReconciler) createComponent(ctx context.Context, aw *workload
179179

180180
func (r *AppWrapperReconciler) createComponents(ctx context.Context, aw *workloadv1beta2.AppWrapper) (error, bool) {
181181
for componentIdx := range aw.Spec.Components {
182-
_, err, fatal := r.createComponent(ctx, aw, componentIdx)
183-
if err != nil {
184-
return err, fatal
182+
if !aw.Status.ComponentStatus[componentIdx].Deployed {
183+
obj, err, fatal := r.createComponent(ctx, aw, componentIdx)
184+
if err != nil {
185+
return err, fatal
186+
}
187+
aw.Status.ComponentStatus[componentIdx].Name = obj.GetName()
188+
aw.Status.ComponentStatus[componentIdx].Kind = obj.GetKind()
189+
aw.Status.ComponentStatus[componentIdx].APIVersion = obj.GetAPIVersion()
190+
aw.Status.ComponentStatus[componentIdx].Deployed = true
185191
}
186192
}
187193
return nil, false
188194
}
189195

190196
func (r *AppWrapperReconciler) deleteComponents(ctx context.Context, aw *workloadv1beta2.AppWrapper) bool {
197+
deleteIfPresent := func(idx int, opts ...client.DeleteOption) bool {
198+
cs := &aw.Status.ComponentStatus[idx]
199+
if !cs.Deployed {
200+
return false // not deployed
201+
}
202+
obj := &metav1.PartialObjectMetadata{
203+
TypeMeta: metav1.TypeMeta{Kind: cs.Kind, APIVersion: cs.APIVersion},
204+
ObjectMeta: metav1.ObjectMeta{Name: cs.Name, Namespace: aw.Namespace},
205+
}
206+
if err := r.Delete(ctx, obj, opts...); err != nil {
207+
if apierrors.IsNotFound(err) {
208+
// Has already been undeployed; update componentStatus and return not deployed
209+
cs.Deployed = false
210+
return false
211+
} else {
212+
log.FromContext(ctx).Error(err, "Deletion error")
213+
return true // unexpected error ==> still present
214+
}
215+
}
216+
return true // still present
217+
}
218+
191219
meta.SetStatusCondition(&aw.Status.Conditions, metav1.Condition{
192220
Type: string(workloadv1beta2.DeletingResources),
193221
Status: metav1.ConditionTrue,
194222
Reason: "DeletionInitiated",
195223
})
196-
log := log.FromContext(ctx)
197-
remaining := 0
198-
for _, component := range aw.Spec.Components {
199-
obj, err := parseComponent(aw, component.Template.Raw)
200-
if err != nil {
201-
log.Error(err, "Parsing error")
202-
continue
203-
}
204-
if err := r.Delete(ctx, obj, client.PropagationPolicy(metav1.DeletePropagationBackground)); err != nil {
205-
if !apierrors.IsNotFound(err) {
206-
log.Error(err, "Deletion error")
207-
}
208-
continue
209-
}
210-
remaining++ // no error deleting resource, resource therefore still exists
224+
225+
componentsRemaining := false
226+
for componentIdx := range aw.Spec.Components {
227+
componentsRemaining = deleteIfPresent(componentIdx, client.PropagationPolicy(metav1.DeletePropagationBackground)) || componentsRemaining
211228
}
212229

213230
deletionGracePeriod := r.forcefulDeletionGraceDuration(ctx, aw)
214231
whenInitiated := meta.FindStatusCondition(aw.Status.Conditions, string(workloadv1beta2.DeletingResources)).LastTransitionTime
215232
gracePeriodExpired := time.Now().After(whenInitiated.Time.Add(deletionGracePeriod))
216233

217-
if remaining > 0 && !gracePeriodExpired {
234+
if componentsRemaining && !gracePeriodExpired {
218235
// Resources left and deadline hasn't expired, just requeue the deletion
219236
return false
220237
}
@@ -224,10 +241,10 @@ func (r *AppWrapperReconciler) deleteComponents(ctx context.Context, aw *workloa
224241
client.UnsafeDisableDeepCopy,
225242
client.InNamespace(aw.Namespace),
226243
client.MatchingLabels{AppWrapperLabel: aw.Name}); err != nil {
227-
log.Error(err, "Pod list error")
244+
log.FromContext(ctx).Error(err, "Pod list error")
228245
}
229246

230-
if remaining == 0 && len(pods.Items) == 0 {
247+
if !componentsRemaining && len(pods.Items) == 0 {
231248
// no resources or pods left; deletion is complete
232249
clearCondition(aw, workloadv1beta2.DeletingResources, "DeletionComplete", "")
233250
return true
@@ -238,20 +255,13 @@ func (r *AppWrapperReconciler) deleteComponents(ctx context.Context, aw *workloa
238255
// force deletion of pods first
239256
for _, pod := range pods.Items {
240257
if err := r.Delete(ctx, &pod, client.GracePeriodSeconds(0)); err != nil {
241-
log.Error(err, "Forceful pod deletion error")
258+
log.FromContext(ctx).Error(err, "Forceful pod deletion error")
242259
}
243260
}
244261
} else {
245262
// force deletion of wrapped resources once pods are gone
246-
for _, component := range aw.Spec.Components {
247-
obj, err := parseComponent(aw, component.Template.Raw)
248-
if err != nil {
249-
log.Error(err, "Parsing error")
250-
continue
251-
}
252-
if err := r.Delete(ctx, obj, client.GracePeriodSeconds(0)); err != nil && !apierrors.IsNotFound(err) {
253-
log.Error(err, "Forceful deletion error")
254-
}
263+
for componentIdx := range aw.Spec.Components {
264+
_ = deleteIfPresent(componentIdx, client.GracePeriodSeconds(0))
255265
}
256266
}
257267
}

0 commit comments

Comments
 (0)