Skip to content
Open
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
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ require (
k8s.io/kube-aggregator v0.32.1
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f
sigs.k8s.io/cli-utils v0.37.2
sigs.k8s.io/controller-runtime v0.18.4
)

require (
Expand All @@ -38,6 +39,7 @@ require (
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/emicklei/go-restful/v3 v3.12.1 // indirect
github.com/evanphx/json-patch/v5 v5.9.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtz
github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/evanphx/json-patch v5.9.11+incompatible h1:ixHHqfcGvxhWkniF1tWxBHA0yb4Z+d1UQi45df52xW8=
github.com/evanphx/json-patch v5.9.11+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg=
github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
Expand Down Expand Up @@ -277,6 +279,8 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0 h1:CPT0ExVicCzcp
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw=
sigs.k8s.io/cli-utils v0.37.2 h1:GOfKw5RV2HDQZDJlru5KkfLO1tbxqMoyn1IYUxqBpNg=
sigs.k8s.io/cli-utils v0.37.2/go.mod h1:V+IZZr4UoGj7gMJXklWBg6t5xbdThFBcpj4MrZuCYco=
sigs.k8s.io/controller-runtime v0.18.4 h1:87+guW1zhvuPLh1PHybKdYFLU0YJp4FhJRmiHvm5BZw=
sigs.k8s.io/controller-runtime v0.18.4/go.mod h1:TVoGrfdpbA9VRFaRnKgk9P5/atA0pMwq+f+msb9M8Sg=
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
sigs.k8s.io/structured-merge-diff/v4 v4.4.3 h1:sCP7Vv3xx/CWIuTPVN38lUPx0uw0lcLfzaiDa8Ja01A=
Expand Down
61 changes: 55 additions & 6 deletions pkg/condition/condition.go
Original file line number Diff line number Diff line change
@@ -1,32 +1,49 @@
package condition

import (
"github.com/rancher/wrangler/v3/pkg/condition/types"
"reflect"
"sigs.k8s.io/controller-runtime/pkg/client"
"time"

"github.com/rancher/wrangler/v3/pkg/generic"
"github.com/sirupsen/logrus"

"github.com/rancher/wrangler/v3/pkg/generic"
)

type Cond string

func (c Cond) HasCondition(obj interface{}) bool {
condSlice := getValue(obj, "Status", "Conditions")
if !condSlice.IsValid() {
condSlice = getValue(obj, "Conditions")
}

foundCond := findCond(obj, condSlice, string(c))
if foundCond != nil {
return true
}

return false
}

func (c Cond) GetStatus(obj interface{}) string {
return getStatus(obj, string(c))
}

func (c Cond) SetError(obj interface{}, reason string, err error) {
if err == nil || err == generic.ErrSkip {
c.True(obj)
c.Message(obj, "")
c.Reason(obj, reason)
c.SetMessage(obj, "")
c.SetReason(obj, reason)
return
}
if reason == "" {
reason = "Error"
}
c.False(obj)
c.Message(obj, err.Error())
c.Reason(obj, reason)
c.SetMessage(obj, err.Error())
c.SetReason(obj, reason)
}

func (c Cond) MatchesError(obj interface{}, reason string, err error) bool {
Expand Down Expand Up @@ -98,7 +115,12 @@ func (c Cond) CreateUnknownIfNotExists(obj interface{}) {
}
}

// Deprecated: Use SetReason instead
func (c Cond) Reason(obj interface{}, reason string) {
c.SetReason(obj, reason)
}

func (c Cond) SetReason(obj interface{}, reason string) {
cond := findOrCreateCond(obj, string(c))
getFieldValue(cond, "Reason").SetString(reason)
}
Expand All @@ -113,11 +135,16 @@ func (c Cond) GetReason(obj interface{}) string {

func (c Cond) SetMessageIfBlank(obj interface{}, message string) {
if c.GetMessage(obj) == "" {
c.Message(obj, message)
c.SetMessage(obj, message)
}
}

// Deprecated: Use SetMessage instead
func (c Cond) Message(obj interface{}, message string) {
c.SetMessage(obj, message)
}

func (c Cond) SetMessage(obj interface{}, message string) {
cond := findOrCreateCond(obj, string(c))
setValue(cond, "Message", message)
}
Expand All @@ -130,6 +157,28 @@ func (c Cond) GetMessage(obj interface{}) string {
return getFieldValue(*cond, "Message").String()
}

func (c Cond) Name() string {
return string(c)
}

func (c Cond) ToK8sCondition() types.Condition {
return &MetaV1ConditionHandler{
RootCondition: c,
}
}

func (c Cond) ToFluentBuilder(obj client.Object) types.FluentCondition {
if obj != nil {
handler := MetaV1ConditionFluentBuilder{
RootCondition: c,
}
return handler.Target(obj)
}
return &MetaV1ConditionFluentBuilder{
RootCondition: c,
}
}

func touchTS(value reflect.Value) {
now := time.Now().UTC().Format(time.RFC3339)
getFieldValue(value, "LastUpdateTime").SetString(now)
Expand Down
213 changes: 213 additions & 0 deletions pkg/condition/condition_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
package condition

import (
"errors"
"github.com/rancher/wrangler/v3/pkg/genericcondition"
"github.com/stretchr/testify/assert"
v1 "k8s.io/api/core/v1"
"testing"
)

type testObjStatus struct {
Conditions []genericcondition.GenericCondition `json:"conditions"`
}

type testResourceObj struct {
Status testObjStatus `json:"status"`
}

func newGenericCondition(condition Cond, message string) genericcondition.GenericCondition {
return genericcondition.GenericCondition{
Type: string(condition),
Status: v1.ConditionTrue,
Message: message,
}
}

func newTestObj(conditions ...Cond) testResourceObj {
newObj := testResourceObj{
Status: testObjStatus{
Conditions: []genericcondition.GenericCondition{},
},
}
newConditions := make([]genericcondition.GenericCondition, 0, len(conditions))
if len(conditions) > 0 {
for _, condition := range conditions {
newConditions = append(
newConditions,
newGenericCondition(condition, "Hello World"),
)
}
}
newObj.Status.Conditions = newConditions

return newObj
}

const (
TestCondtion Cond = "Test"
AnotherTestCondtion Cond = "SecondTest"
)

func TestHasCondition(t *testing.T) {
testObj := newTestObj(TestCondtion)
assert.Equal(t, true, TestCondtion.HasCondition(&testObj))
assert.Equal(t, true, TestCondtion.HasCondition(&testObj.Status))

assert.Equal(t, false, AnotherTestCondtion.HasCondition(&testObj))
assert.Equal(t, false, AnotherTestCondtion.HasCondition(&testObj.Status))
}

func TestGetStatus(t *testing.T) {
testObj := newTestObj(TestCondtion)
assert.Equal(t, "True", TestCondtion.GetStatus(&testObj))
assert.Equal(t, "True", TestCondtion.GetStatus(&testObj.Status))

assert.Equal(t, "", AnotherTestCondtion.GetStatus(&testObj))
assert.Equal(t, "", AnotherTestCondtion.GetStatus(&testObj.Status))
}

func TestSetStatus(t *testing.T) {
testObj := newTestObj(TestCondtion)
assert.Equal(t, "True", TestCondtion.GetStatus(&testObj))
assert.Equal(t, "True", TestCondtion.GetStatus(&testObj.Status))
TestCondtion.SetStatus(&testObj, "False")
assert.Equal(t, "False", TestCondtion.GetStatus(&testObj))
assert.Equal(t, "False", TestCondtion.GetStatus(&testObj.Status))

assert.Equal(t, "", AnotherTestCondtion.GetStatus(&testObj))
assert.Equal(t, "", AnotherTestCondtion.GetStatus(&testObj.Status))
AnotherTestCondtion.SetStatus(&testObj, "Unknown")
assert.Equal(t, "Unknown", AnotherTestCondtion.GetStatus(&testObj))
assert.Equal(t, "Unknown", AnotherTestCondtion.GetStatus(&testObj.Status))
}

func TestSetStatusBool(t *testing.T) {
testObj := newTestObj(TestCondtion)
assert.Equal(t, "True", TestCondtion.GetStatus(&testObj))
assert.Equal(t, "True", TestCondtion.GetStatus(&testObj.Status))
TestCondtion.SetStatusBool(&testObj, false)
assert.Equal(t, "False", TestCondtion.GetStatus(&testObj))
assert.Equal(t, "False", TestCondtion.GetStatus(&testObj.Status))

assert.Equal(t, "", AnotherTestCondtion.GetStatus(&testObj))
assert.Equal(t, "", AnotherTestCondtion.GetStatus(&testObj.Status))
AnotherTestCondtion.SetStatusBool(&testObj, true)
assert.Equal(t, "True", AnotherTestCondtion.GetStatus(&testObj))
assert.Equal(t, "True", AnotherTestCondtion.GetStatus(&testObj.Status))
}

func TestBoolHelpers(t *testing.T) {
testObj := newTestObj(TestCondtion)
assert.Equal(t, "True", TestCondtion.GetStatus(&testObj))
assert.Equal(t, "True", TestCondtion.GetStatus(&testObj.Status))
TestCondtion.False(&testObj)
assert.Equal(t, "False", TestCondtion.GetStatus(&testObj))
assert.Equal(t, "False", TestCondtion.GetStatus(&testObj.Status))

assert.Equal(t, "", AnotherTestCondtion.GetStatus(&testObj))
assert.Equal(t, "", AnotherTestCondtion.GetStatus(&testObj.Status))
AnotherTestCondtion.True(&testObj)
assert.Equal(t, "True", AnotherTestCondtion.GetStatus(&testObj))
assert.Equal(t, "True", AnotherTestCondtion.GetStatus(&testObj.Status))
AnotherTestCondtion.False(&testObj)
assert.Equal(t, "False", AnotherTestCondtion.GetStatus(&testObj))
assert.Equal(t, "False", AnotherTestCondtion.GetStatus(&testObj.Status))
}

func TestBoolConditoins(t *testing.T) {
testObj := newTestObj(TestCondtion)
assert.True(t, TestCondtion.IsTrue(&testObj))
assert.True(t, TestCondtion.IsTrue(&testObj.Status))
TestCondtion.False(&testObj)
assert.True(t, TestCondtion.IsFalse(&testObj))
assert.True(t, TestCondtion.IsFalse(&testObj.Status))

assert.Equal(t, "", AnotherTestCondtion.GetStatus(&testObj))
assert.Equal(t, "", AnotherTestCondtion.GetStatus(&testObj.Status))
AnotherTestCondtion.True(&testObj)
assert.True(t, AnotherTestCondtion.IsTrue(&testObj))
assert.True(t, AnotherTestCondtion.IsTrue(&testObj.Status))
AnotherTestCondtion.False(&testObj)
assert.True(t, AnotherTestCondtion.IsFalse(&testObj))
assert.True(t, AnotherTestCondtion.IsFalse(&testObj.Status))
}

func TestUnknownHelpers(t *testing.T) {
testObj := newTestObj(TestCondtion)
assert.False(t, TestCondtion.IsUnknown(&testObj))
assert.False(t, AnotherTestCondtion.IsUnknown(&testObj))
AnotherTestCondtion.SetMessage(&testObj, "Test Message, will default status to unknown")
assert.True(t, AnotherTestCondtion.IsUnknown(&testObj))

TestCondtion.Unknown(&testObj)
assert.True(t, TestCondtion.IsUnknown(&testObj))
}

func TestCreateUnknownIfNotExists(t *testing.T) {
testObj := newTestObj(TestCondtion)
assert.False(t, TestCondtion.IsUnknown(&testObj))
assert.False(t, AnotherTestCondtion.IsUnknown(&testObj))
AnotherTestCondtion.CreateUnknownIfNotExists(&testObj)
assert.True(t, AnotherTestCondtion.IsUnknown(&testObj))
TestCondtion.CreateUnknownIfNotExists(&testObj)
assert.False(t, TestCondtion.IsUnknown(&testObj))
}

func TestReasonMethods(t *testing.T) {
testObj := newTestObj(TestCondtion)
TestCondtion.SetReason(&testObj, "Because I Said So")
assert.Equal(t, "Because I Said So", TestCondtion.GetReason(&testObj))

assert.Equal(t, "", AnotherTestCondtion.GetReason(&testObj))
AnotherTestCondtion.Reason(&testObj, "Because Tom Said So")
assert.Equal(t, "Because Tom Said So", AnotherTestCondtion.GetReason(&testObj))
}

func TestSetMessageIfBlank(t *testing.T) {
testObj := newTestObj(TestCondtion)
TestCondtion.SetMessageIfBlank(&testObj, "This will be ignored")
assert.NotEqual(t, "This will be ignored", TestCondtion.GetMessage(&testObj))

assert.Equal(t, "", AnotherTestCondtion.GetMessage(&testObj))
AnotherTestCondtion.SetMessageIfBlank(&testObj, "This will be updated")
assert.Equal(t, "This will be updated", AnotherTestCondtion.GetMessage(&testObj))
AnotherTestCondtion.SetMessageIfBlank(&testObj, "This will NOT be updated")
assert.Equal(t, "This will be updated", AnotherTestCondtion.GetMessage(&testObj))
}

func TestMessageMethods(t *testing.T) {
testObj := newTestObj(TestCondtion)
assert.Equal(t, "Hello World", TestCondtion.GetMessage(&testObj))
TestCondtion.SetMessage(&testObj, "")
assert.Equal(t, "", TestCondtion.GetMessage(&testObj))

AnotherTestCondtion.SetMessage(&testObj, "This will be updated")
assert.Equal(t, "This will be updated", AnotherTestCondtion.GetMessage(&testObj))
}

func TestErrorMethods(t *testing.T) {
const SubStatusCondition Cond = "SomeStepSpecificState"

testError := errors.New("some test error")

testObj := newTestObj(TestCondtion)
TestCondtion.False(&testObj)
SubStatusCondition.False(&testObj)
SubStatusCondition.SetError(&testObj, "", testError)

assert.Equal(t, "Error", SubStatusCondition.GetReason(&testObj))
assert.Equal(t, "some test error", SubStatusCondition.GetMessage(&testObj))
assert.True(t, SubStatusCondition.MatchesError(&testObj, "Error", testError))
assert.True(t, SubStatusCondition.MatchesError(&testObj, "", testError))

SubStatusCondition.SetError(&testObj, "Because it Broke", testError)
assert.False(t, SubStatusCondition.MatchesError(&testObj, "", testError))
assert.True(t, SubStatusCondition.MatchesError(&testObj, "Because it Broke", testError))
assert.False(t, SubStatusCondition.MatchesError(&testObj, "Because something else Broke", testError))

SubStatusCondition.SetError(&testObj, "Because something else Broke", nil)
assert.False(t, SubStatusCondition.MatchesError(&testObj, "Because something else Broke", testError))
assert.True(t, SubStatusCondition.MatchesError(&testObj, "Because something else Broke", nil))

}
Loading