Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: factor optional "soak time" into freight availability #3100

Merged
merged 17 commits into from
Jan 10, 2025
Merged
Show file tree
Hide file tree
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
136 changes: 120 additions & 16 deletions api/v1alpha1/freight_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package v1alpha1
import (
"context"
"fmt"
"time"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
)
Expand Down Expand Up @@ -85,26 +87,128 @@ func GetFreightByAlias(
return &freightList.Items[0], nil
}

// IsFreightAvailable answers whether the specified Freight is available to the
// specified Stage.
func IsFreightAvailable(stage *Stage, freight *Freight) bool {
if stage == nil || freight == nil || stage.Namespace != freight.Namespace {
return false
// ListFreightByCurrentStage returns a list of Freight resources that think
// they're currently in use by the Stage specified.
func ListFreightByCurrentStage(
ctx context.Context,
c client.Client,
stage *Stage,
) ([]Freight, error) {
freightList := FreightList{}
if err := c.List(
ctx,
&freightList,
client.InNamespace(stage.Namespace),
client.MatchingFields{"currentlyIn": stage.Name},
); err != nil {
return nil, fmt.Errorf(
"error listing Freight in namespace %q with current stage %q: %w",
stage.Namespace,
stage.Name,
err,
)
}
return freightList.Items, nil
}

// IsCurrentlyIn returns whether the Freight is currently in the specified
// Stage.
func (f *Freight) IsCurrentlyIn(stage string) bool {
// NB: This method exists for convenience. It doesn't require the caller to
// know anything about the Freight status' internal data structure.
_, in := f.Status.CurrentlyIn[stage]
return in
}

// IsVerifiedIn returns whether the Freight has been verified in the specified
// Stage.
func (f *Freight) IsVerifiedIn(stage string) bool {
// NB: This method exists for convenience. It doesn't require the caller to
// know anything about the Freight status' internal data structure.
_, verified := f.Status.VerifiedIn[stage]
return verified
}

// IsApprovedFor returns whether the Freight has been approved for the specified
// Stage.
func (f *Freight) IsApprovedFor(stage string) bool {
// NB: This method exists for convenience. It doesn't require the caller to
// know anything about the Freight status' internal data structure.
_, approved := f.Status.ApprovedFor[stage]
return approved
}

// GetLongestSoak returns the longest soak time for the Freight in the specified
// Stage if it's been verified in that Stage. If it has not, zero will be
// returned instead. If the Freight is currently in use by the specified Stage,
// the current soak time is calculated and compared to the longest completed
// soak time on record.
func (f *Freight) GetLongestSoak(stage string) time.Duration {
if _, verified := f.Status.VerifiedIn[stage]; !verified {
return 0
}
if _, approved := freight.Status.ApprovedFor[stage.Name]; approved {
return true
var longestCompleted time.Duration
if record, isVerified := f.Status.VerifiedIn[stage]; isVerified && record.LongestCompletedSoak != nil {
longestCompleted = record.LongestCompletedSoak.Duration
}
for _, req := range stage.Spec.RequestedFreight {
if freight.Origin.Equals(&req.Origin) {
if req.Sources.Direct {
return true
}
for _, source := range req.Sources.Stages {
if _, verified := freight.Status.VerifiedIn[source]; verified {
return true
var current time.Duration
if record, isCurrent := f.Status.CurrentlyIn[stage]; isCurrent {
current = time.Since(record.Since.Time)
}
return time.Duration(max(longestCompleted.Nanoseconds(), current.Nanoseconds()))
}

// AddCurrentStage updates the Freight status to reflect that the Freight is
// currently in the specified Stage.
func (f *FreightStatus) AddCurrentStage(stage string, since time.Time) {
if _, alreadyIn := f.CurrentlyIn[stage]; !alreadyIn {
if f.CurrentlyIn == nil {
f.CurrentlyIn = make(map[string]CurrentStage)
}
f.CurrentlyIn[stage] = CurrentStage{
Since: &metav1.Time{Time: since},
}
}
}

// RemoveCurrentStage updates the Freight status to reflect that the Freight is
// no longer in the specified Stage. If the Freight was verified in the
// specified Stage, the longest completed soak time will be updated if
// necessary.
func (f *FreightStatus) RemoveCurrentStage(stage string) {
if record, in := f.CurrentlyIn[stage]; in {
if _, verified := f.VerifiedIn[stage]; verified {
soak := time.Since(record.Since.Time)
if soak > f.VerifiedIn[stage].LongestCompletedSoak.Duration {
f.VerifiedIn[stage] = VerifiedStage{
LongestCompletedSoak: &metav1.Duration{Duration: soak},
}
}
}
delete(f.CurrentlyIn, stage)
}
}

// AddVerifiedStage updates the Freight status to reflect that the Freight has
// been verified in the specified Stage.
func (f *FreightStatus) AddVerifiedStage(stage string, verifiedAt time.Time) {
if _, verified := f.VerifiedIn[stage]; !verified {
record := VerifiedStage{VerifiedAt: &metav1.Time{Time: verifiedAt}}
if f.VerifiedIn == nil {
f.VerifiedIn = map[string]VerifiedStage{stage: record}
}
f.VerifiedIn[stage] = record
}
}

// AddApprovedStage updates the Freight status to reflect that the Freight has
// been approved for the specified Stage.
func (f *FreightStatus) AddApprovedStage(stage string, approvedAt time.Time) {
if _, approved := f.ApprovedFor[stage]; !approved {
record := ApprovedStage{ApprovedAt: &metav1.Time{Time: approvedAt}}
if f.ApprovedFor == nil {
f.ApprovedFor = map[string]ApprovedStage{stage: record}
}
f.ApprovedFor[stage] = record
}
return false
}
Loading
Loading