quorate/services/project-delivery/controller/recovery.go

109 lines
3.7 KiB
Go
Raw Permalink Normal View History

package main
import (
"context"
"slices"
"strings"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
func (c *controller) recoverBuilds(ctx context.Context, delivery *projectDelivery) (bool, error) {
known := make(map[string]bool)
for _, attempt := range delivery.Status.Points {
known[attempt.WorkflowName] = true
}
continuous := delivery.Status.Continuous
if continuous != nil && continuous.Attempt != nil {
known[continuous.Attempt.WorkflowName] = true
if continuous.Attempt.History != nil {
for _, name := range continuous.Attempt.History.Workflows {
known[name] = true
}
}
}
items, err := c.client.Resource(workflowGVR).Namespace(delivery.Metadata.Namespace).List(ctx, metav1.ListOptions{LabelSelector: purposeAnnotation + "=build"})
if err != nil {
return false, err
}
slices.SortFunc(items.Items, func(a, b unstructured.Unstructured) int {
left, right := a.GetCreationTimestamp(), b.GetCreationTimestamp()
if order := left.Time.Compare(right.Time); order != 0 {
return order
}
return strings.Compare(a.GetName(), b.GetName())
})
recovered := false
if continuous != nil && continuous.Attempt != nil && continuous.Attempt.CreatedAt == nil {
for _, item := range items.Items {
if item.GetName() == continuous.Attempt.WorkflowName && ownedBy(&item, delivery.Metadata.UID) {
created := item.GetCreationTimestamp()
continuous.Attempt.CreatedAt = &created.Time
recovered = true
break
}
}
}
for _, item := range items.Items {
knownBuild := known[item.GetName()]
if knownBuild {
if continuous == nil || continuous.Attempt == nil || continuous.Attempt.History == nil ||
!slices.Contains(continuous.Attempt.History.Workflows, item.GetName()) || continuous.Attempt.CreatedAt == nil {
continue
}
created := item.GetCreationTimestamp()
if !created.Time.After(*continuous.Attempt.CreatedAt) {
continue
}
}
if !ownedBy(&item, delivery.Metadata.UID) {
continue
}
state, err := c.workflow(ctx, delivery.Metadata.Namespace, item.GetName())
if err != nil {
return false, err
}
attempt := &attemptStatus{
WorkflowName: item.GetName(), Ref: state.Input["source-ref"], Revision: state.Input["source-revision"],
SourceIdentity: item.GetAnnotations()[sourceKey], RetryOf: item.GetAnnotations()[retryKey],
Phase: phaseOrPending(state.Phase), Input: state.Input, CreatedAt: state.CreatedAt,
}
if knownBuild && attempt.SourceIdentity == "" && continuous.Delivered != nil && continuous.Delivered.Revision == attempt.Revision {
attempt.SourceIdentity = continuous.Delivered.Identity
}
if result, found := state.Outputs["delivery-result"]; found {
attempt.Result = opaqueResult(result)
}
if attempt.Ref == "" {
if slices.ContainsFunc(delivery.Status.Points, func(point attemptStatus) bool { return !terminalPhase(point.Phase) }) {
continue
}
delivery.Status.Points = append(delivery.Status.Points, *attempt)
} else {
if continuous == nil {
continuous = &continuousStatus{}
delivery.Status.Continuous = continuous
}
if previous := continuous.Attempt; previous != nil {
if !terminalPhase(previous.Phase) {
continue
}
if previous.CreatedAt != nil && previous.CreatedAt.After(*attempt.CreatedAt) {
rememberAttempt(continuous, previous)
addHistory(previous.History, attempt)
recovered = true
continue
}
}
rememberAttempt(continuous, attempt)
recordDelivered(continuous)
}
if attempt.RetryOf != "" && attempt.RetryOf == delivery.Metadata.Annotations[retryAnnotation] {
delivery.Status.LastHandledRetry = attempt.RetryOf
}
observeQualBuild(delivery, attempt)
recovered = true
}
return recovered, nil
}