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

Add conditions pkg to support Build CRDs to operate on Conditions #475

Merged
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
29 changes: 29 additions & 0 deletions deploy/crds/build.dev_buildruns_crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,35 @@ spec:
description: CompletionTime is the time the build completed.
format: date-time
type: string
conditions:
description: Conditions
items:
description: Condition defines the required fields for populating
Build controllers Conditions
properties:
lastTransitionTime:
description: LastTransitionTime last time the condition transit
from one status to another.
format: date-time
type: string
message:
description: A human readable message indicating details about
the transition.
type: string
reason:
description: The reason for the condition last transition.
type: string
status:
description: Status of the condition, one of True, False, Unknown.
type: string
type:
description: Type of condition
type: string
required:
- status
- type
type: object
type: array
latestTaskRunRef:
description: PodName is the name of the pod responsible for executing
this task's steps.
Expand Down
26 changes: 26 additions & 0 deletions pkg/apis/build/v1alpha1/buildrun_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package v1alpha1

import (
"github.com/shipwright-io/build/pkg/conditions"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
Expand Down Expand Up @@ -43,6 +44,9 @@ type BuildRunSpec struct {
// BuildRunStatus defines the observed state of BuildRun
type BuildRunStatus struct {

// Conditions
Conditions conditions.Conditions `json:"conditions,omitempty"`

// The Succeeded status of the TaskRun
// +optional
Succeeded corev1.ConditionStatus `json:"succeeded,omitempty"`
Expand Down Expand Up @@ -117,3 +121,25 @@ type BuildRunList struct {
func init() {
SchemeBuilder.Register(&BuildRun{}, &BuildRunList{})
}

// SetConditions implements the conditions.StatusConditions interface,
// this is require to get access to the Conditions Manager
func (brs *BuildRunStatus) SetConditions(c conditions.Conditions) {
brs.Conditions = c
}

// GetConditions implements the conditions.StatusConditions interface,
// this is require to get access to the Conditions Manager
func (brs *BuildRunStatus) GetConditions() *conditions.Conditions {
return &brs.Conditions
}

// GetCondition returns a condition based on a type from a list of Conditions
func (brs *BuildRunStatus) GetCondition(t conditions.Type) *conditions.Condition {
return conditions.Manage(brs).GetCondition(t)
}

// SetCondition updates a list of conditions with the provided condition
func (brs *BuildRunStatus) SetCondition(c *conditions.Condition) {
conditions.Manage(brs).SetCondition(c)
}
8 changes: 8 additions & 0 deletions pkg/apis/build/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

90 changes: 90 additions & 0 deletions pkg/conditions/conditions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright The Shipwright Contributors
//
// SPDX-License-Identifier: Apache-2.0

package conditions

//
// This class is heavily based on
// https://github.com/knative/pkg/blob/master/apis/condition_set.go
// but contains a more simplified approach.
//
// This class is intended to enable any Build CRD that might require to
// operate and support Conditions.
//
// For any Build CRD that requires to operate on Conditions, they should
// only need to implement the StatusConditions interface.
//

// StatusConditions provides access to the conditions of an
// object that have a Status field
type StatusConditions interface {
GetConditions() *Conditions
SetConditions(Conditions)
}

// Access is the interface that allows retrieval
// of a particular condition
type Access interface {
GetCondition(t Type) *Condition
}

// Manager is the interface that allows to operate
// on a particular condition, by getting or setting it
type Manager interface {
Access
SetCondition(*Condition)
}

// Implementor implements the Manager interface
type Implementor struct {
Connect StatusConditions
}

// Verify that Implementor implements Manager
var _ Manager = (*Implementor)(nil)

// Manage enables an object that implements the
// StatusConditions interface to get access to the Manager
func Manage(status StatusConditions) Manager {
return Implementor{
Connect: status,
}
}

// GetCondition retrieves a particular condition based
// on the type
func (i Implementor) GetCondition(t Type) *Condition {
if i.Connect == nil {
return nil
}

for _, c := range *i.Connect.GetConditions() {
if c.Type == t {
return &c
}
}

return nil
}

// SetCondition updates a condition by generating the same list
// of conditions with the provided one
// This does not preserve the order when multiple conditions exist.
func (i Implementor) SetCondition(aCondition *Condition) {
if i.Connect == nil {
return
}

var conditions Conditions

for _, c := range *i.Connect.GetConditions() {
if c.Type != aCondition.Type {
conditions = append(conditions, c)
}
}
conditions = append(conditions, *aCondition)

i.Connect.SetConditions(conditions)

}
17 changes: 17 additions & 0 deletions pkg/conditions/conditions_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright The Shipwright Contributors
//
// SPDX-License-Identifier: Apache-2.0

package conditions_test

import (
"testing"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

func TestConditions(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Conditions Suite")
}
114 changes: 114 additions & 0 deletions pkg/conditions/conditions_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Copyright The Shipwright Contributors
//
// SPDX-License-Identifier: Apache-2.0

package conditions_test

import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/shipwright-io/build/pkg/conditions"
"github.com/shipwright-io/build/test"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

var _ = Describe("Conditions", func() {

var (
ctl test.Catalog
)

Context("Operating on Conditions", func() {

It("should be able to get a manager based on a buildrun status", func() {
// BuildRun sample with an embedded condition of the type Succeeded
br := ctl.BuildRunWithSucceededCondition()
m := conditions.Manage(&br.Status)
Expect(m).ToNot(BeNil())

c := m.GetCondition(conditions.Succeeded)
Expect(c).ToNot(BeNil())
})

It("should be able to retrieve an existing condition message", func() {
// BuildRun sample with an embedded condition of the type Succeeded
br := ctl.BuildRunWithSucceededCondition()

// BuildRun implements StatusConditions, therefore it can operate on
// an existing Condition
msg := br.Status.GetCondition(conditions.Succeeded).GetMessage()
Expect(msg).To(Equal("foo is not bar"))
})

It("should be able to retrieve an existing condition reason", func() {
// BuildRun sample with an embedded condition of the type Succeeded
br := ctl.BuildRunWithSucceededCondition()

reason := br.Status.GetCondition(conditions.Succeeded).GetReason()
Expect(reason).To(Equal("foobar"))
})

It("should be able to retrieve an existing condition status", func() {
// BuildRun sample with an embedded condition of the type Succeeded
br := ctl.BuildRunWithSucceededCondition()

status := br.Status.GetCondition(conditions.Succeeded).GetStatus()
Expect(status).To(Equal(corev1.ConditionUnknown))
})

It("should return nil if a condition is not available when operating on it", func() {
br := ctl.DefaultBuildRun("foo", "bar")

// when getting a condition that does not exists on the BuildRun, do not
// panic but rather return a nil
cond := br.Status.GetCondition(conditions.Succeeded)
Expect(cond).To(BeNil())
})

It("should be able to set a condition based on a type", func() {
br := ctl.DefaultBuildRun("foo", "bar")

// generate a condition of the type Succeeded
tmpCond := &conditions.Condition{
Type: conditions.Succeeded,
Status: corev1.ConditionUnknown,
Message: "foobar",
Reason: "foo is bar",
LastTransitionTime: metav1.Now(),
}

// set the condition on the BuildRun resource
br.Status.SetCondition(tmpCond)

condType := br.Status.GetCondition(conditions.Succeeded).Type
Expect(condType).To(Equal(conditions.Succeeded))

condMsg := br.Status.GetCondition(conditions.Succeeded).GetMessage()
Expect(condMsg).To(Equal("foobar"))
})

It("should be able to update an existing condition based on a type", func() {
// BuildRun sample with an embedded condition of the type Succeeded
br := ctl.BuildRunWithSucceededCondition()

reason := br.Status.GetCondition(conditions.Succeeded).GetReason()
Expect(reason).To(Equal("foobar"))

// generate a condition in order to update the existing one
tmpCond := &conditions.Condition{
Type: conditions.Succeeded,
Status: corev1.ConditionUnknown,
Message: "foobar was updated",
Reason: "foo is bar",
LastTransitionTime: metav1.Now(),
}

// update the condition on the BuildRun resource
br.Status.SetCondition(tmpCond)

condMsg := br.Status.GetCondition(conditions.Succeeded).GetMessage()
Expect(condMsg).To(Equal("foobar was updated"))
})
})
})
Loading