Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions pkg/controller/profilebundle/profilebundle_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,43 @@ func (r *ReconcileProfileBundle) Reconcile(ctx context.Context, request reconcil
// Pod already exists and its init container at least ran - don't requeue
reqLogger.Info("Skip reconcile: Workload already up-to-date", "Deployment.Namespace", found.Namespace, "Deployment.Name", found.Name)

// If the ProfileBundle is still pending, the profileparser hasn't
// finished updating the status yet.
if instance.Status.DataStreamStatus == compliancev1alpha1.DataStreamPending {
// Check if the profileparser init container already completed.
// If the pod is Running and the profileparser finished, it means
// the profileparser ran for a previous content configuration and
// won't run again. This can happen when the operator restarts
// after updating the Deployment but before the rollout completes,
// leaving the old pod as the only one. Force a new rollout so
// the profileparser re-runs with the current content.
if profileparserCompleted(relevantPod) {
reqLogger.Info("ProfileBundle pending but profileparser already completed, forcing rollout",
"Pod.Name", relevantPod.Name, "Pod.Phase", relevantPod.Status.Phase)
updatedDepl := found.DeepCopy()
if updatedDepl.Spec.Template.Annotations == nil {
updatedDepl.Spec.Template.Annotations = make(map[string]string)
}
updatedDepl.Spec.Template.Annotations["compliance.openshift.io/restart"] = time.Now().Format(time.RFC3339)
err = r.Client.Update(context.TODO(), updatedDepl)
if err != nil {
reqLogger.Error(err, "Couldn't force profileparser rollout")
return reconcile.Result{}, err
}
}

// Requeue using the workqueue's built-in rate limiter, which
// applies exponential backoff automatically. This avoids
// flooding the logs when the ProfileBundle is stuck pending
// (e.g., due to a bad content image).
//
// See the TypedReconciler interface documentation for details:
// sigs.k8s.io/controller-runtime/pkg/reconcile/reconcile.go
reqLogger.Info("ProfileBundle still pending, requeueing to check status",
"Pod.Name", relevantPod.Name, "Pod.Phase", relevantPod.Status.Phase)
return reconcile.Result{Requeue: true}, nil
}

// Handle upgrades
if instance.Status.DataStreamStatus == compliancev1alpha1.DataStreamValid &&
instance.Status.Conditions.GetCondition("Ready") == nil {
Expand Down Expand Up @@ -577,6 +614,17 @@ func podStartupError(pod *corev1.Pod) bool {
return false
}

// profileparserCompleted returns true if the profileparser init container
// has already terminated successfully.
func profileparserCompleted(pod *corev1.Pod) bool {
for _, initStatus := range pod.Status.InitContainerStatuses {
if initStatus.Name == "profileparser" {
return initStatus.State.Terminated != nil && initStatus.State.Terminated.ExitCode == 0
}
}
return false
}

func workloadNeedsUpdate(image string, depl *appsv1.Deployment) bool {
initContainers := depl.Spec.Template.Spec.InitContainers
if len(initContainers) != 2 {
Expand Down
Loading