Skip to content

Commit

Permalink
Improve kpack conditions.
Browse files Browse the repository at this point in the history
Conditions should always have 'reason' filled, per https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties ("This field may not be empty").
Additionally, 'message' should be filled in for Unknown or Error conditions so that users know how to resolve the issue

Signed-off-by: Evan Anderson <[email protected]>
  • Loading branch information
Evan Anderson committed May 23, 2023
1 parent 21404dd commit d11fcad
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 18 deletions.
1 change: 1 addition & 0 deletions pkg/apis/build/v1alpha2/image_lifecycle.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
const (
BuilderNotFound = "BuilderNotFound"
BuilderNotReady = "BuilderNotReady"
BuilderReady = "BuilderReady"
)

func (im *Image) BuilderNotFound() corev1alpha1.Conditions {
Expand Down
29 changes: 24 additions & 5 deletions pkg/reconciler/image/image_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -796,8 +796,10 @@ func testImageReconciler(t *testing.T, when spec.G, it spec.S) {
ObservedGeneration: originalGeneration,
Conditions: corev1alpha1.Conditions{
{
Type: corev1alpha1.ConditionReady,
Status: corev1.ConditionUnknown,
Type: corev1alpha1.ConditionReady,
Status: corev1.ConditionFalse,
Reason: buildapi.BuilderNotReady,
Message: "Builder builder-name is not ready: something went wrong",
},
{
Type: buildapi.ConditionBuilderReady,
Expand Down Expand Up @@ -2106,6 +2108,7 @@ func testImageReconciler(t *testing.T, when spec.G, it spec.S) {
{
Type: buildapi.ConditionBuilderReady,
Status: corev1.ConditionTrue,
Reason: buildapi.BuilderReady,
},
},
},
Expand Down Expand Up @@ -2168,10 +2171,12 @@ func testImageReconciler(t *testing.T, when spec.G, it spec.S) {
{
Type: corev1alpha1.ConditionSucceeded,
Status: corev1.ConditionTrue,
Reason: image.UpToDateReason,
},
{
Type: buildapi.ConditionBuilderReady,
Status: corev1.ConditionTrue,
Reason: buildapi.BuilderReady,
},
},
},
Expand Down Expand Up @@ -2210,10 +2215,12 @@ func testImageReconciler(t *testing.T, when spec.G, it spec.S) {
{
Type: corev1alpha1.ConditionReady,
Status: corev1.ConditionTrue,
Reason: image.UpToDateReason,
},
{
Type: buildapi.ConditionBuilderReady,
Status: corev1.ConditionTrue,
Reason: buildapi.BuilderReady,
},
},
},
Expand Down Expand Up @@ -2265,7 +2272,7 @@ func testImageReconciler(t *testing.T, when spec.G, it spec.S) {
})
})

it("reports unknown when last build was successful and builder is not ready", func() {
it("reports false when last build was successful and builder is not ready", func() {
imageWithBuilder.Status.BuildCounter = 1
imageWithBuilder.Status.LatestBuildRef = "image-name-build-1"
imageWithBuilder.Status.LatestImage = "some/image@some-old-sha"
Expand All @@ -2291,8 +2298,10 @@ func testImageReconciler(t *testing.T, when spec.G, it spec.S) {
ObservedGeneration: originalGeneration,
Conditions: corev1alpha1.Conditions{
{
Type: corev1alpha1.ConditionReady,
Status: corev1.ConditionUnknown,
Type: corev1alpha1.ConditionReady,
Status: corev1.ConditionFalse,
Reason: buildapi.BuilderNotReady,
Message: "Builder builder-name is not ready",
},
{
Type: buildapi.ConditionBuilderReady,
Expand Down Expand Up @@ -2377,11 +2386,13 @@ func testImageReconciler(t *testing.T, when spec.G, it spec.S) {
{
Type: corev1alpha1.ConditionReady,
Status: corev1.ConditionFalse,
Reason: image.BuildFailedReason,
Message: failureMessage,
},
{
Type: buildapi.ConditionBuilderReady,
Status: corev1.ConditionTrue,
Reason: buildapi.BuilderReady,
},
},
},
Expand Down Expand Up @@ -2594,11 +2605,13 @@ func conditionReadyUnknown() corev1alpha1.Conditions {
{
Type: corev1alpha1.ConditionReady,
Status: corev1.ConditionUnknown,
Reason: image.ResolverNotReadyReason,
Message: "SourceResolver image-name-source is not ready",
},
{
Type: buildapi.ConditionBuilderReady,
Status: corev1.ConditionTrue,
Reason: buildapi.BuilderReady,
},
}
}
Expand All @@ -2608,11 +2621,13 @@ func conditionBuildExecuting(buildName string) corev1alpha1.Conditions {
{
Type: corev1alpha1.ConditionReady,
Status: corev1.ConditionUnknown,
Reason: image.BuildRunningReason,
Message: fmt.Sprintf("%s is executing", buildName),
},
{
Type: buildapi.ConditionBuilderReady,
Status: corev1.ConditionTrue,
Reason: buildapi.BuilderReady,
},
}
}
Expand All @@ -2622,10 +2637,12 @@ func conditionReady() corev1alpha1.Conditions {
{
Type: corev1alpha1.ConditionReady,
Status: corev1.ConditionTrue,
Reason: image.UpToDateReason,
},
{
Type: buildapi.ConditionBuilderReady,
Status: corev1.ConditionTrue,
Reason: buildapi.BuilderReady,
},
}
}
Expand All @@ -2635,10 +2652,12 @@ func conditionNotReady() corev1alpha1.Conditions {
{
Type: corev1alpha1.ConditionReady,
Status: corev1.ConditionFalse,
Reason: image.BuildFailedReason,
},
{
Type: buildapi.ConditionBuilderReady,
Status: corev1.ConditionTrue,
Reason: buildapi.BuilderReady,
},
}
}
60 changes: 47 additions & 13 deletions pkg/reconciler/image/reconcile_build.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@ import (
corev1alpha1 "github.com/pivotal/kpack/pkg/apis/core/v1alpha1"
)

const BuildRunningReason = "BuildRunning"
const (
BuildRunningReason = "BuildRunning"
ResolverNotReadyReason = "ResolverNotReady"
BuildFailedReason = "BuildFailed"
UpToDateReason = "UpToDate"
)

func (c *Reconciler) reconcileBuild(ctx context.Context, image *buildapi.Image, latestBuild *buildapi.Build, sourceResolver *buildapi.SourceResolver, builder buildapi.BuilderResource, buildCacheName string) (buildapi.ImageStatus, error) {
currentBuildNumber, err := buildCounter(latestBuild)
Expand Down Expand Up @@ -55,7 +60,7 @@ func (c *Reconciler) reconcileBuild(ctx context.Context, image *buildapi.Image,
case corev1.ConditionFalse:
return buildapi.ImageStatus{
Status: corev1alpha1.Status{
Conditions: noScheduledBuild(result.ConditionStatus, builder, latestBuild, sourceResolver),
Conditions: noScheduledBuild(result, builder, latestBuild, sourceResolver),
},
LatestBuildRef: latestBuild.BuildRef(),
LatestBuildReason: latestBuild.BuildReason(),
Expand All @@ -70,28 +75,50 @@ func (c *Reconciler) reconcileBuild(ctx context.Context, image *buildapi.Image,
}
}

func noScheduledBuild(buildNeeded corev1.ConditionStatus, builder buildapi.BuilderResource, build *buildapi.Build, sourceResolver *buildapi.SourceResolver) corev1alpha1.Conditions {
if buildNeeded == corev1.ConditionUnknown {
message := ""
func noScheduledBuild(buildNeeded buildRequiredResult, builder buildapi.BuilderResource, build *buildapi.Build, sourceResolver *buildapi.SourceResolver) corev1alpha1.Conditions {
if !builder.Ready() {
return corev1alpha1.Conditions{
{
Type: corev1alpha1.ConditionReady,
Status: corev1.ConditionFalse,
Reason: buildapi.BuilderNotReady,
Message: builderError(builder),
LastTransitionTime: corev1alpha1.VolatileTime{Inner: metav1.Now()},
},
builderCondition(builder),
}
}
if buildNeeded.ConditionStatus == corev1.ConditionUnknown {
message := "Build status unknown"
if !sourceResolver.Ready() {
message = fmt.Sprintf("SourceResolver %s is not ready", sourceResolver.GetName())
}
return corev1alpha1.Conditions{
{
Type: corev1alpha1.ConditionReady,
Status: corev1.ConditionUnknown,
Reason: ResolverNotReadyReason,
Message: message,
LastTransitionTime: corev1alpha1.VolatileTime{Inner: metav1.Now()},
},
builderCondition(builder),
}
}

buildReason := UpToDateReason
buildMessage := "Last build succeeded"

if !build.Status.GetCondition(corev1alpha1.ConditionSucceeded).IsTrue() {
buildReason = BuildFailedReason
buildMessage = "Last build did not succeed"
}

return corev1alpha1.Conditions{
{
Type: corev1alpha1.ConditionReady,
Status: unknownStatusIfNil(build.Status.GetCondition(corev1alpha1.ConditionSucceeded)),
Message: emptyMessageIfNil(build.Status.GetCondition(corev1alpha1.ConditionSucceeded)),
Reason: buildReason,
Message: defaultMessageIfNil(build.Status.GetCondition(corev1alpha1.ConditionSucceeded), buildMessage),
LastTransitionTime: corev1alpha1.VolatileTime{Inner: metav1.Now()},
},
builderCondition(builder),
Expand All @@ -106,9 +133,12 @@ func unknownStatusIfNil(condition *corev1alpha1.Condition) corev1.ConditionStatu
return condition.Status
}

func emptyMessageIfNil(condition *corev1alpha1.Condition) string {
// Copies the message from the specified condition, or fills in a default message if nil.
// We should always have a message for non-successful conditions, as that conveys
// information to the user about what is expected.
func defaultMessageIfNil(condition *corev1alpha1.Condition, defaultMessage string) string {
if condition == nil {
return ""
return defaultMessage
}
return condition.Message
}
Expand All @@ -126,6 +156,7 @@ func builderCondition(builder buildapi.BuilderResource) corev1alpha1.Condition {
return corev1alpha1.Condition{
Type: buildapi.ConditionBuilderReady,
Status: corev1.ConditionTrue,
Reason: buildapi.BuilderReady,
LastTransitionTime: corev1alpha1.VolatileTime{Inner: metav1.Now()},
}
}
Expand All @@ -145,12 +176,14 @@ func scheduledBuildCondition(build *buildapi.Build) corev1alpha1.Conditions {
{
Type: corev1alpha1.ConditionReady,
Status: corev1.ConditionUnknown,
LastTransitionTime: corev1alpha1.VolatileTime{Inner: metav1.Now()},
Reason: BuildRunningReason,
Message: fmt.Sprintf("%s is executing", build.Name),
LastTransitionTime: corev1alpha1.VolatileTime{Inner: metav1.Now()},
},
{
Type: buildapi.ConditionBuilderReady,
Status: corev1.ConditionTrue,
Reason: buildapi.BuilderReady,
LastTransitionTime: corev1alpha1.VolatileTime{Inner: metav1.Now()},
},
}
Expand All @@ -168,10 +201,11 @@ func buildCounter(build *buildapi.Build) (int64, error) {
func buildRunningCondition(build *buildapi.Build, builder buildapi.BuilderResource) corev1alpha1.Conditions {
return corev1alpha1.Conditions{
{
Type: corev1alpha1.ConditionReady,
Status: corev1.ConditionUnknown,
Reason: BuildRunningReason,
Message: emptyMessageIfNil(build.Status.GetCondition(corev1alpha1.ConditionSucceeded)),
Type: corev1alpha1.ConditionReady,
Status: corev1.ConditionUnknown,
Reason: BuildRunningReason,
Message: defaultMessageIfNil(build.Status.GetCondition(corev1alpha1.ConditionSucceeded),
fmt.Sprintf("%s is executing", build.Name)),
LastTransitionTime: corev1alpha1.VolatileTime{Inner: metav1.Now()},
},
builderCondition(builder),
Expand Down

0 comments on commit d11fcad

Please sign in to comment.