Skip to content
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
4 changes: 2 additions & 2 deletions Earthfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ VERSION --try --raw-output 0.8

PROJECT crossplane/crossplane-runtime

ARG --global GO_VERSION=1.24.4
ARG --global GO_VERSION=1.25.9

# reviewable checks that a branch is ready for review. Run it before opening a
# pull request. It will catch a lot of the things our CI workflow will catch.
Expand Down Expand Up @@ -102,7 +102,7 @@ go-test:

# go-lint lints Go code.
go-lint:
ARG GOLANGCI_LINT_VERSION=v2.2.1
ARG GOLANGCI_LINT_VERSION=v2.11.4
FROM +go-modules
# This cache is private because golangci-lint doesn't support concurrent runs.
CACHE --id go-lint --sharing private /root/.cache/golangci-lint
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/crossplane/crossplane-runtime/v2

go 1.24.0
go 1.25.9

require (
dario.cat/mergo v1.0.1
Expand Down
6 changes: 3 additions & 3 deletions pkg/event/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ limitations under the License.
package event

import (
"maps"

"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/tools/record"
)
Expand Down Expand Up @@ -107,9 +109,7 @@ func (r *APIRecorder) Event(obj runtime.Object, e Event) {
// annotations with all recorded events.
func (r *APIRecorder) WithAnnotations(keysAndValues ...string) Recorder {
ar := NewAPIRecorder(r.kube)
for k, v := range r.annotations {
ar.annotations[k] = v
}
maps.Copy(ar.annotations, r.annotations)

sliceMap(keysAndValues, ar.annotations)

Expand Down
1 change: 0 additions & 1 deletion pkg/event/event_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

// Package event records Kubernetes events.
package event

import (
Expand Down
6 changes: 3 additions & 3 deletions pkg/fieldpath/fieldpath.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,13 @@ func (sg Segments) String() string {
switch s.Type {
case SegmentField:
if s.Field == wildcard || strings.ContainsRune(s.Field, period) {
b.WriteString(fmt.Sprintf("[%s]", s.Field))
fmt.Fprintf(&b, "[%s]", s.Field)
continue
}

b.WriteString(fmt.Sprintf(".%s", s.Field))
fmt.Fprintf(&b, ".%s", s.Field)
case SegmentIndex:
b.WriteString(fmt.Sprintf("[%d]", s.Index))
fmt.Fprintf(&b, "[%d]", s.Index)
}
}

Expand Down
1 change: 1 addition & 0 deletions pkg/fieldpath/paved.go
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,7 @@ func (p *Paved) GetStringObject(path string) (map[string]string, error) {
}

so := make(map[string]string)

for k, in := range o {
s, ok := in.(string)
if !ok {
Expand Down
1 change: 1 addition & 0 deletions pkg/fieldpath/paved_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,7 @@ func TestSetValue(t *testing.T) {
res := make([]any, DefaultMaxFieldPathIndex+2)
res[0] = "a"
res[DefaultMaxFieldPathIndex+1] = "c"

return res
}(),
},
Expand Down
18 changes: 12 additions & 6 deletions pkg/gate/gate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,14 @@ func TestGateIntegration(t *testing.T) {
reason: "Should call function when single dependency is met",
setup: func(g *gate.Gate[string]) chan bool {
called := make(chan bool, 1)

g.Register(func() {
called <- true
}, "condition1")

// Set condition to true (will be initialized as false first)
g.Set("condition1", true)

return called
},
want: want{
Expand All @@ -119,13 +121,15 @@ func TestGateIntegration(t *testing.T) {
reason: "Should call function when all dependencies are met",
setup: func(g *gate.Gate[string]) chan bool {
called := make(chan bool, 1)

g.Register(func() {
called <- true
}, "condition1", "condition2")

// Set both conditions to true
g.Set("condition1", true)
g.Set("condition2", true)

return called
},
want: want{
Expand All @@ -136,12 +140,14 @@ func TestGateIntegration(t *testing.T) {
reason: "Should not call function when only some dependencies are met",
setup: func(g *gate.Gate[string]) chan bool {
called := make(chan bool, 1)

g.Register(func() {
called <- true
}, "condition1", "condition2")

// Set only one condition to true
g.Set("condition1", true)

return called
},
want: want{
Expand Down Expand Up @@ -170,13 +176,15 @@ func TestGateIntegration(t *testing.T) {
reason: "Should call function when dependency is met, even if unset later",
setup: func(g *gate.Gate[string]) chan bool {
called := make(chan bool, 1)

g.Register(func() {
called <- true
}, "condition1")

// Set condition to true then false (function already called when true)
g.Set("condition1", true)
g.Set("condition1", false)

return called
},
want: want{
Expand All @@ -187,6 +195,7 @@ func TestGateIntegration(t *testing.T) {
reason: "Should call function only once even if conditions change after",
setup: func(g *gate.Gate[string]) chan bool {
called := make(chan bool, 2) // Buffer for potential multiple calls

g.Register(func() {
called <- true
}, "condition1")
Expand All @@ -195,6 +204,7 @@ func TestGateIntegration(t *testing.T) {
g.Set("condition1", true)
g.Set("condition1", false)
g.Set("condition1", true)

return called
},
want: want{
Expand Down Expand Up @@ -244,15 +254,11 @@ func TestGateConcurrency(t *testing.T) {

// Register functions concurrently
for range numGoroutines {
wg.Add(1)

go func() {
defer wg.Done()

wg.Go(func() {
g.Register(func() {
callCount <- struct{}{}
}, "shared-condition")
}()
})
}

// Wait for all registrations
Expand Down
2 changes: 1 addition & 1 deletion pkg/logging/klog.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ type requestThrottlingFilter struct {
logr.LogSink
}

func (l *requestThrottlingFilter) Info(level int, msg string, keysAndValues ...interface{}) {
func (l *requestThrottlingFilter) Info(level int, msg string, keysAndValues ...any) {
if !strings.Contains(msg, "Waited for ") || !strings.Contains(msg, " request: ") {
return
}
Expand Down
25 changes: 8 additions & 17 deletions pkg/meta/meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ limitations under the License.
package meta

import (
"maps"
"slices"
"time"

corev1 "k8s.io/api/core/v1"
Expand Down Expand Up @@ -65,6 +67,7 @@ const (

// ReferenceTo returns an object reference to the supplied object, presumed to
// be of the supplied group, version, and kind.
//
// Deprecated: use a more specific reference type, such as TypedReference or
// Reference instead of the overly verbose ObjectReference.
// See https://github.com/crossplane/crossplane-runtime/issues/49
Expand Down Expand Up @@ -166,10 +169,8 @@ func AddControllerReference(o metav1.Object, r metav1.OwnerReference) error {
// AddFinalizer to the supplied Kubernetes object's metadata.
func AddFinalizer(o metav1.Object, finalizer string) {
f := o.GetFinalizers()
for _, e := range f {
if e == finalizer {
return
}
if slices.Contains(f, finalizer) {
return
}

o.SetFinalizers(append(f, finalizer))
Expand All @@ -190,13 +191,7 @@ func RemoveFinalizer(o metav1.Object, finalizer string) {
// FinalizerExists checks whether given finalizer is already set.
func FinalizerExists(o metav1.Object, finalizer string) bool {
f := o.GetFinalizers()
for _, e := range f {
if e == finalizer {
return true
}
}

return false
return slices.Contains(f, finalizer)
}

// AddLabels to the supplied object.
Expand All @@ -207,9 +202,7 @@ func AddLabels(o metav1.Object, labels map[string]string) {
return
}

for k, v := range labels {
l[k] = v
}
maps.Copy(l, labels)

o.SetLabels(l)
}
Expand All @@ -236,9 +229,7 @@ func AddAnnotations(o metav1.Object, annotations map[string]string) {
return
}

for k, v := range annotations {
a[k] = v
}
maps.Copy(a, annotations)

o.SetAnnotations(a)
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/meta/meta_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1087,6 +1087,7 @@ func TestExternalCreateSucceededDuring(t *testing.T) {
o := &corev1.Pod{}
t := time.Now().Add(-2 * time.Minute)
SetExternalCreateSucceeded(o, t)

return o
}(),
d: 1 * time.Minute,
Expand All @@ -1099,6 +1100,7 @@ func TestExternalCreateSucceededDuring(t *testing.T) {
o := &corev1.Pod{}
t := time.Now().Add(-30 * time.Second)
SetExternalCreateSucceeded(o, t)

return o
}(),
d: 1 * time.Minute,
Expand Down Expand Up @@ -1196,6 +1198,7 @@ func TestIsPaused(t *testing.T) {
p.SetAnnotations(map[string]string{
AnnotationKeyReconciliationPaused: "true",
})

return p
}(),
want: true,
Expand All @@ -1210,6 +1213,7 @@ func TestIsPaused(t *testing.T) {
p.SetAnnotations(map[string]string{
AnnotationKeyReconciliationPaused: "",
})

return p
}(),
want: false,
Expand Down
2 changes: 1 addition & 1 deletion pkg/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ func (p *PackageParser) Parse(_ context.Context, reader io.ReadCloser) (*Package
// is useful for filtering out empty YAML documents that would otherwise
// cause issues when decoded.
func isEmptyYAML(y []byte) bool {
for _, line := range strings.Split(string(y), "\n") {
for line := range strings.SplitSeq(string(y), "\n") {
trimmed := strings.TrimLeftFunc(line, unicode.IsSpace)
// We don't want to return an empty document with only separators that
// have nothing in-between.
Expand Down
1 change: 1 addition & 0 deletions pkg/ratelimiter/reconciler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ func TestReconcile(t *testing.T) {
// Rate limit the request once.
r := NewReconciler("test", inner, &predictableRateLimiter{d: 8 * time.Second})
r.Reconcile(context.Background(), reconcile.Request{NamespacedName: types.NamespacedName{Name: "limited"}})

return r
}(),
args: args{
Expand Down
10 changes: 9 additions & 1 deletion pkg/reconciler/managed/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ func TestAPISecretPublisher(t *testing.T) {

type args struct {
ctx context.Context
mg resource.LegacyManaged
mg resource.LegacyManaged //nolint:staticcheck // Tests need to cover the legacy managed path.
c ConnectionDetails
}

Expand Down Expand Up @@ -183,12 +183,14 @@ func TestAPISecretPublisher(t *testing.T) {
fields: fields{
secret: resource.ApplyFn(func(ctx context.Context, o client.Object, ao ...resource.ApplyOption) error {
want := resource.ConnectionSecretFor(mg, fake.GVK(mg))

want.Data = cd
for _, fn := range ao {
if err := fn(ctx, o, want); err != nil {
return err
}
}

return nil
}),
typer: fake.SchemeWith(&fake.LegacyManaged{}),
Expand All @@ -208,10 +210,12 @@ func TestAPISecretPublisher(t *testing.T) {
fields: fields{
secret: resource.ApplyFn(func(_ context.Context, o client.Object, _ ...resource.ApplyOption) error {
want := resource.ConnectionSecretFor(mg, fake.GVK(mg))

want.Data = cd
if diff := cmp.Diff(want, o); diff != "" {
t.Errorf("-want, +got:\n%s", diff)
}

return nil
}),
typer: fake.SchemeWith(&fake.LegacyManaged{}),
Expand Down Expand Up @@ -302,12 +306,14 @@ func TestAPILocalSecretPublisher(t *testing.T) {
fields: fields{
secret: resource.ApplyFn(func(ctx context.Context, o client.Object, ao ...resource.ApplyOption) error {
want := resource.LocalConnectionSecretFor(mg, fake.GVK(mg))

want.Data = cd
for _, fn := range ao {
if err := fn(ctx, o, want); err != nil {
return err
}
}

return nil
}),
typer: fake.SchemeWith(&fake.ModernManaged{}),
Expand All @@ -327,10 +333,12 @@ func TestAPILocalSecretPublisher(t *testing.T) {
fields: fields{
secret: resource.ApplyFn(func(_ context.Context, o client.Object, _ ...resource.ApplyOption) error {
want := resource.LocalConnectionSecretFor(mg, fake.GVK(mg))

want.Data = cd
if diff := cmp.Diff(want, o); diff != "" {
t.Errorf("-want, +got:\n%s", diff)
}

return nil
}),
typer: fake.SchemeWith(&fake.ModernManaged{}),
Expand Down
2 changes: 1 addition & 1 deletion pkg/reconciler/managed/changelogger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ func equateApproxTimepb(margin time.Duration) []cmp.Option {
protocmp.Transform(),
cmp.FilterPath(
func(p cmp.Path) bool {
if p.Last().Type() == reflect.TypeOf(protocmp.Message{}) {
if p.Last().Type() == reflect.TypeFor[protocmp.Message]() {
a, b := p.Last().Values()
return msgIsTimestamp(a) && msgIsTimestamp(b)
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/reconciler/managed/policies.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type ManagementPoliciesResolver struct {

// LegacyManagementPoliciesResolver is used to perform management policy checks
// based on the management policy and if the management policy feature is enabled.
//
// Deprecated: this is for LegacyManaged types, that had deletion policy.
// ModernManaged resources should use ManagementPoliciesResolver.
type LegacyManagementPoliciesResolver struct {
Expand Down Expand Up @@ -129,6 +130,7 @@ func NewManagementPoliciesResolver(managementPolicyEnabled bool, managementPolic
// NewLegacyManagementPoliciesResolver returns an ManagementPolicyChecker based
// on the management policies and if the management policies feature
// is enabled.
//
// Deprecated: this is intended for LegacyManaged resources that had deletionPolicy
// ModernManaged resources should use NewManagementPoliciesResolver.
func NewLegacyManagementPoliciesResolver(managementPolicyEnabled bool, managementPolicy xpv1.ManagementPolicies, deletionPolicy xpv1.DeletionPolicy, o ...ManagementPoliciesResolverOption) ManagementPoliciesChecker {
Expand Down
Loading
Loading