Skip to content

Commit

Permalink
Merge pull request #1370 from buildpacks-community/builder-up-to-date
Browse files Browse the repository at this point in the history
Add Builder UpToDate Condition
  • Loading branch information
tomkennedy513 authored Nov 17, 2023
2 parents 20bfcc5 + 1a042b8 commit 6f588dd
Show file tree
Hide file tree
Showing 18 changed files with 430 additions and 36 deletions.
3 changes: 3 additions & 0 deletions config/builder.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ spec:
- name: Ready
type: string
jsonPath: ".status.conditions[?(@.type==\"Ready\")].status"
- name: UpToDate
type: string
jsonPath: ".status.conditions[?(@.type==\"UpToDate\")].status"
conversion:
strategy: Webhook
webhook:
Expand Down
3 changes: 3 additions & 0 deletions config/clusterbuilder.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ spec:
- name: Ready
type: string
jsonPath: ".status.conditions[?(@.type==\"Ready\")].status"
- name: UpToDate
type: string
jsonPath: ".status.conditions[?(@.type==\"UpToDate\")].status"
names:
kind: ClusterBuilder
listKind: ClusterBuilderList
Expand Down
10 changes: 10 additions & 0 deletions docs/builders.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,13 @@ version) in the following order:
`paketo-buildpacks/gradle` is a sub-buildpack of `paketo-buildpacks/java`)
1. As a sub-buildpack of any ClusterBuildpacks
1. As a sub-buildpack in the ClusterStore specified in the Builder spec.

### <a id='status'></a>Builder Status Conditions

Builders and ClusterBuilders have two Conditions that represent the overall status of the Builder.

The 'Ready' condition is used to show that the Builder is able to be used in Builds

The 'UpToDate' condition indicates whether the most recent reconcile of the Builder
was successful. When this condition is false, that means that the Builder may not have the
latest Stack or Buildpacks due to ongoing reconcile failures.
21 changes: 20 additions & 1 deletion pkg/apis/build/v1alpha2/builder_lifecycle.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ func (bs *BuilderStatus) BuilderRecord(record BuilderRecord) {
Type: corev1alpha1.ConditionReady,
Status: corev1.ConditionTrue,
},
{
Type: ConditionUpToDate,
Status: corev1.ConditionTrue,
LastTransitionTime: corev1alpha1.VolatileTime{Inner: v1.Now()},
},
}
bs.Order = record.Order
bs.ObservedStoreGeneration = record.ObservedStoreGeneration
Expand All @@ -38,12 +43,26 @@ func (bs *BuilderStatus) BuilderRecord(record BuilderRecord) {
}

func (bs *BuilderStatus) ErrorCreate(err error) {

readyCondition := corev1alpha1.Condition{
LastTransitionTime: corev1alpha1.VolatileTime{Inner: v1.Now()},
Type: corev1alpha1.ConditionReady,
Status: corev1.ConditionTrue,
}
if bs.LatestImage == "" {
readyCondition.Status = corev1.ConditionFalse
readyCondition.Message = NoLatestImageMessage
readyCondition.Reason = NoLatestImageReason
}

bs.Status = corev1alpha1.Status{
Conditions: corev1alpha1.Conditions{
readyCondition,
{
Type: corev1alpha1.ConditionReady,
Type: ConditionUpToDate,
Status: corev1.ConditionFalse,
LastTransitionTime: corev1alpha1.VolatileTime{Inner: v1.Now()},
Reason: ReconcileFailedReason,
Message: err.Error(),
},
},
Expand Down
1 change: 1 addition & 0 deletions pkg/apis/build/v1alpha2/builder_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ type BuilderResource interface {
GetNamespace() string
BuildBuilderSpec() corev1alpha1.BuildBuilderSpec
Ready() bool
UpToDate() bool
BuildpackMetadata() corev1alpha1.BuildpackMetadataList
RunImage() string
GetKind() string
Expand Down
8 changes: 6 additions & 2 deletions pkg/apis/build/v1alpha2/builder_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@ import (
)

const (
BuilderKind = "Builder"
BuilderCRName = "builders.kpack.io"
BuilderKind = "Builder"
BuilderCRName = "builders.kpack.io"
ConditionUpToDate corev1alpha1.ConditionType = "UpToDate"
NoLatestImageReason string = "NoLatestImage"
NoLatestImageMessage string = "Builder has no latestImage"
ReconcileFailedReason string = "ReconcileFailed"
)

// +genclient
Expand Down
5 changes: 5 additions & 0 deletions pkg/apis/build/v1alpha2/image_builds_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,7 @@ func testImageBuilds(t *testing.T, when spec.G, it spec.S) {

type TestBuilderResource struct {
BuilderReady bool
BuilderUpToDate bool
BuilderMetadata []corev1alpha1.BuildpackMetadata
ImagePullSecrets []corev1.LocalObjectReference
Kind string
Expand All @@ -396,6 +397,10 @@ func (t TestBuilderResource) Ready() bool {
return t.BuilderReady
}

func (t TestBuilderResource) UpToDate() bool {
return t.BuilderUpToDate
}

func (t TestBuilderResource) BuildpackMetadata() corev1alpha1.BuildpackMetadataList {
return t.BuilderMetadata
}
Expand Down
8 changes: 5 additions & 3 deletions pkg/apis/build/v1alpha2/image_lifecycle.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ import (
)

const (
BuilderNotFound = "BuilderNotFound"
BuilderNotReady = "BuilderNotReady"
BuilderReady = "BuilderReady"
BuilderNotFound = "BuilderNotFound"
BuilderNotReady = "BuilderNotReady"
BuilderReady = "BuilderReady"
BuilderNotUpToDate = "BuilderNotUpToDate"
BuilderUpToDate = "BuilderUpToDate"
)

func (im *Image) BuilderNotFound() corev1alpha1.Conditions {
Expand Down
1 change: 1 addition & 0 deletions pkg/apis/build/v1alpha2/image_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,3 +136,4 @@ func (i *Image) NamespacedName() types.NamespacedName {
}

const ConditionBuilderReady corev1alpha1.ConditionType = "BuilderReady"
const ConditionBuilderUpToDate corev1alpha1.ConditionType = "BuilderUpToDate"
5 changes: 5 additions & 0 deletions pkg/duckbuilder/duck_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ func (b *DuckBuilder) Ready() bool {
(b.Generation == b.Status.ObservedGeneration)
}

func (b *DuckBuilder) UpToDate() bool {
return b.Status.GetCondition(buildapi.ConditionUpToDate).IsTrue() &&
(b.Generation == b.Status.ObservedGeneration)
}

func (b *DuckBuilder) BuildBuilderSpec() corev1alpha1.BuildBuilderSpec {
return corev1alpha1.BuildBuilderSpec{
Image: b.Status.LatestImage,
Expand Down
33 changes: 33 additions & 0 deletions pkg/duckbuilder/duck_builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ func testDuckBuilder(t *testing.T, when spec.G, it spec.S) {
Type: corev1alpha1.ConditionReady,
Status: corev1.ConditionTrue,
},
{
Type: buildapi.ConditionUpToDate,
Status: corev1.ConditionTrue,
},
},
},
BuilderMetadata: corev1alpha1.BuildpackMetadataList{
Expand Down Expand Up @@ -81,6 +85,35 @@ func testDuckBuilder(t *testing.T, when spec.G, it spec.S) {
})
})

when("UpToDate", func() {

it("ready when UpToDate condition is true", func() {
require.True(t, duckBuilder.UpToDate())
})

it("not ready without conditions", func() {
duckBuilder.Status.Conditions = nil

require.False(t, duckBuilder.UpToDate())
})

it("not ready when not ready", func() {
duckBuilder.Status.Conditions = corev1alpha1.Conditions{
{
Type: buildapi.ConditionUpToDate,
Status: corev1.ConditionFalse,
},
}

require.False(t, duckBuilder.UpToDate())
})

it("not UpToDate when generation does not match observed generation", func() {
duckBuilder.Generation = duckBuilder.Status.ObservedGeneration + 1

require.False(t, duckBuilder.UpToDate())
})
})
it("BuildBuilderSpec provides latest image and pull secrets", func() {
require.Equal(t, corev1alpha1.BuildBuilderSpec{
Image: "some/builder@sha256:12345678",
Expand Down
40 changes: 40 additions & 0 deletions pkg/reconciler/builder/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,10 @@ func testBuilderReconciler(t *testing.T, when spec.G, it spec.S) {
Type: corev1alpha1.ConditionReady,
Status: corev1.ConditionTrue,
},
{
Type: buildapi.ConditionUpToDate,
Status: corev1.ConditionTrue,
},
},
},
BuilderMetadata: []corev1alpha1.BuildpackMetadata{
Expand Down Expand Up @@ -320,6 +324,10 @@ func testBuilderReconciler(t *testing.T, when spec.G, it spec.S) {
Type: corev1alpha1.ConditionReady,
Status: corev1.ConditionTrue,
},
{
Type: buildapi.ConditionUpToDate,
Status: corev1.ConditionTrue,
},
},
},
BuilderMetadata: []corev1alpha1.BuildpackMetadata{},
Expand Down Expand Up @@ -383,6 +391,10 @@ func testBuilderReconciler(t *testing.T, when spec.G, it spec.S) {
Type: corev1alpha1.ConditionReady,
Status: corev1.ConditionTrue,
},
{
Type: buildapi.ConditionUpToDate,
Status: corev1.ConditionTrue,
},
},
},
BuilderMetadata: []corev1alpha1.BuildpackMetadata{
Expand Down Expand Up @@ -436,6 +448,13 @@ func testBuilderReconciler(t *testing.T, when spec.G, it spec.S) {
{
Type: corev1alpha1.ConditionReady,
Status: corev1.ConditionFalse,
Reason: buildapi.NoLatestImageReason,
Message: buildapi.NoLatestImageMessage,
},
{
Type: buildapi.ConditionUpToDate,
Status: corev1.ConditionFalse,
Reason: buildapi.ReconcileFailedReason,
Message: "create error",
},
},
Expand Down Expand Up @@ -491,6 +510,13 @@ func testBuilderReconciler(t *testing.T, when spec.G, it spec.S) {
{
Type: corev1alpha1.ConditionReady,
Status: corev1.ConditionFalse,
Reason: buildapi.NoLatestImageReason,
Message: buildapi.NoLatestImageMessage,
},
{
Type: buildapi.ConditionUpToDate,
Status: corev1.ConditionFalse,
Reason: buildapi.ReconcileFailedReason,
Message: "Error: clusterstack 'some-stack' is not ready",
},
},
Expand Down Expand Up @@ -526,6 +552,13 @@ func testBuilderReconciler(t *testing.T, when spec.G, it spec.S) {
{
Type: corev1alpha1.ConditionReady,
Status: corev1.ConditionFalse,
Reason: buildapi.NoLatestImageReason,
Message: buildapi.NoLatestImageMessage,
},
{
Type: buildapi.ConditionUpToDate,
Status: corev1.ConditionFalse,
Reason: buildapi.ReconcileFailedReason,
Message: `clusterstore.kpack.io "some-store" not found`,
},
},
Expand Down Expand Up @@ -561,6 +594,13 @@ func testBuilderReconciler(t *testing.T, when spec.G, it spec.S) {
{
Type: corev1alpha1.ConditionReady,
Status: corev1.ConditionFalse,
Reason: buildapi.NoLatestImageReason,
Message: buildapi.NoLatestImageMessage,
},
{
Type: buildapi.ConditionUpToDate,
Status: corev1.ConditionFalse,
Reason: buildapi.ReconcileFailedReason,
Message: `clusterstack.kpack.io "some-stack" not found`,
},
},
Expand Down
41 changes: 40 additions & 1 deletion pkg/reconciler/clusterbuilder/clusterbuilder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,6 @@ func testClusterBuilderReconciler(t *testing.T, when spec.G, it spec.S) {
APIVersion: "kpack.io/v1alpha2",
},
}

builder := &buildapi.ClusterBuilder{
ObjectMeta: metav1.ObjectMeta{
Name: builderName,
Expand Down Expand Up @@ -233,6 +232,10 @@ func testClusterBuilderReconciler(t *testing.T, when spec.G, it spec.S) {
Type: corev1alpha1.ConditionReady,
Status: corev1.ConditionTrue,
},
{
Type: buildapi.ConditionUpToDate,
Status: corev1.ConditionTrue,
},
},
},
BuilderMetadata: []corev1alpha1.BuildpackMetadata{
Expand Down Expand Up @@ -308,6 +311,10 @@ func testClusterBuilderReconciler(t *testing.T, when spec.G, it spec.S) {
Type: corev1alpha1.ConditionReady,
Status: corev1.ConditionTrue,
},
{
Type: buildapi.ConditionUpToDate,
Status: corev1.ConditionTrue,
},
},
},
BuilderMetadata: []corev1alpha1.BuildpackMetadata{},
Expand Down Expand Up @@ -363,6 +370,10 @@ func testClusterBuilderReconciler(t *testing.T, when spec.G, it spec.S) {
Type: corev1alpha1.ConditionReady,
Status: corev1.ConditionTrue,
},
{
Type: buildapi.ConditionUpToDate,
Status: corev1.ConditionTrue,
},
},
},
BuilderMetadata: []corev1alpha1.BuildpackMetadata{
Expand Down Expand Up @@ -405,7 +416,14 @@ func testClusterBuilderReconciler(t *testing.T, when spec.G, it spec.S) {
{
Type: corev1alpha1.ConditionReady,
Status: corev1.ConditionFalse,
Message: buildapi.NoLatestImageMessage,
Reason: buildapi.NoLatestImageReason,
},
{
Type: buildapi.ConditionUpToDate,
Status: corev1.ConditionFalse,
Message: "create error",
Reason: buildapi.ReconcileFailedReason,
},
},
},
Expand Down Expand Up @@ -472,7 +490,14 @@ func testClusterBuilderReconciler(t *testing.T, when spec.G, it spec.S) {
{
Type: corev1alpha1.ConditionReady,
Status: corev1.ConditionFalse,
Message: buildapi.NoLatestImageMessage,
Reason: buildapi.NoLatestImageReason,
},
{
Type: buildapi.ConditionUpToDate,
Status: corev1.ConditionFalse,
Message: "stack some-stack is not ready",
Reason: buildapi.ReconcileFailedReason,
},
},
},
Expand Down Expand Up @@ -510,6 +535,13 @@ func testClusterBuilderReconciler(t *testing.T, when spec.G, it spec.S) {
{
Type: corev1alpha1.ConditionReady,
Status: corev1.ConditionFalse,
Message: buildapi.NoLatestImageMessage,
Reason: buildapi.NoLatestImageReason,
},
{
Type: buildapi.ConditionUpToDate,
Status: corev1.ConditionFalse,
Reason: buildapi.ReconcileFailedReason,
Message: `clusterstore.kpack.io "some-store" not found`,
},
},
Expand Down Expand Up @@ -548,6 +580,13 @@ func testClusterBuilderReconciler(t *testing.T, when spec.G, it spec.S) {
{
Type: corev1alpha1.ConditionReady,
Status: corev1.ConditionFalse,
Message: buildapi.NoLatestImageMessage,
Reason: buildapi.NoLatestImageReason,
},
{
Type: buildapi.ConditionUpToDate,
Status: corev1.ConditionFalse,
Reason: buildapi.ReconcileFailedReason,
Message: `clusterstack.kpack.io "some-stack" not found`,
},
},
Expand Down
Loading

0 comments on commit 6f588dd

Please sign in to comment.