diff --git a/Makefile b/Makefile index 142bee76..faa2c8ed 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ SHELL = /usr/bin/env bash -o pipefail .SHELLFLAGS = -ec .PHONY: all -all: tidy vet fmt lint test ## Run the full verification pipeline (tidy, vet, fmt, lint, test). +all: tidy vet fmt lint fix test ## Run the full verification pipeline (tidy, vet, fmt, lint, fix, test). ##@ Development @@ -31,6 +31,10 @@ lint: golangci-lint ## Run golangci-lint against the whole module. tidy: ## Sync go.mod and go.sum with the module's imports. go mod tidy +.PHONY: fix +fix: ## Modernize and update Go source code to improve code + go fix ./... + .PHONY: test test: ## Run unit tests under ./pkg/... with the race detector and coverage enabled. go test -race -cover ./pkg/... diff --git a/pkg/kstatus/polling/clusterreader/caching_reader_test.go b/pkg/kstatus/polling/clusterreader/caching_reader_test.go index 4f1aafd6..06c9a424 100644 --- a/pkg/kstatus/polling/clusterreader/caching_reader_test.go +++ b/pkg/kstatus/polling/clusterreader/caching_reader_test.go @@ -67,10 +67,10 @@ func TestSync(t *testing.T) { clusterObjs: map[gkNamespace][]unstructured.Unstructured{ {GroupKind: deploymentGVK.GroupKind(), Namespace: "Foo"}: { { - Object: map[string]interface{}{ + Object: map[string]any{ "apiVersion": "apps/v1", "kind": "Deployment", - "metadata": map[string]interface{}{ + "metadata": map[string]any{ "name": "deployment-1", "namespace": "Foo", }, @@ -79,10 +79,10 @@ func TestSync(t *testing.T) { }, {GroupKind: deploymentGVK.GroupKind(), Namespace: "Bar"}: { { - Object: map[string]interface{}{ + Object: map[string]any{ "apiVersion": "apps/v1", "kind": "Deployment", - "metadata": map[string]interface{}{ + "metadata": map[string]any{ "name": "deployment-2", "namespace": "Bar", }, @@ -101,13 +101,13 @@ func TestSync(t *testing.T) { expectedCached: map[gkNamespace]cacheEntry{ {GroupKind: deploymentGVK.GroupKind(), Namespace: "Foo"}: { resources: unstructured.UnstructuredList{ - Object: map[string]interface{}{"apiVersion": "apps/v1", "kind": "Deployment"}, + Object: map[string]any{"apiVersion": "apps/v1", "kind": "Deployment"}, Items: []unstructured.Unstructured{ { - Object: map[string]interface{}{ + Object: map[string]any{ "apiVersion": "apps/v1", "kind": "Deployment", - "metadata": map[string]interface{}{ + "metadata": map[string]any{ "name": "deployment-1", "namespace": "Foo", }, @@ -118,23 +118,23 @@ func TestSync(t *testing.T) { }, {GroupKind: rsGVK.GroupKind(), Namespace: "Foo"}: { resources: unstructured.UnstructuredList{ - Object: map[string]interface{}{"apiVersion": "apps/v1", "kind": "ReplicaSet"}, + Object: map[string]any{"apiVersion": "apps/v1", "kind": "ReplicaSet"}, }, }, {GroupKind: podGVK.GroupKind(), Namespace: "Foo"}: { resources: unstructured.UnstructuredList{ - Object: map[string]interface{}{"apiVersion": "v1", "kind": "Pod"}, + Object: map[string]any{"apiVersion": "v1", "kind": "Pod"}, }, }, {GroupKind: deploymentGVK.GroupKind(), Namespace: "Bar"}: { resources: unstructured.UnstructuredList{ - Object: map[string]interface{}{"apiVersion": "apps/v1", "kind": "Deployment"}, + Object: map[string]any{"apiVersion": "apps/v1", "kind": "Deployment"}, Items: []unstructured.Unstructured{ { - Object: map[string]interface{}{ + Object: map[string]any{ "apiVersion": "apps/v1", "kind": "Deployment", - "metadata": map[string]interface{}{ + "metadata": map[string]any{ "name": "deployment-2", "namespace": "Bar", }, @@ -145,12 +145,12 @@ func TestSync(t *testing.T) { }, {GroupKind: rsGVK.GroupKind(), Namespace: "Bar"}: { resources: unstructured.UnstructuredList{ - Object: map[string]interface{}{"apiVersion": "apps/v1", "kind": "ReplicaSet"}, + Object: map[string]any{"apiVersion": "apps/v1", "kind": "ReplicaSet"}, }, }, {GroupKind: podGVK.GroupKind(), Namespace: "Bar"}: { resources: unstructured.UnstructuredList{ - Object: map[string]interface{}{"apiVersion": "v1", "kind": "Pod"}, + Object: map[string]any{"apiVersion": "v1", "kind": "Pod"}, }, }, }, @@ -160,12 +160,12 @@ func TestSync(t *testing.T) { barPodGKN := gkNamespace{GroupKind: podGVK.GroupKind(), Namespace: "Bar"} // 1001 = 3 pages of 500 barObjs := make([]unstructured.Unstructured, 1001) - for i := 0; i < len(barObjs); i++ { + for i := range barObjs { barObjs[i] = unstructured.Unstructured{ - Object: map[string]interface{}{ + Object: map[string]any{ "apiVersion": podGVK.GroupVersion().String(), "kind": podGVK.Kind, - "metadata": map[string]interface{}{ + "metadata": map[string]any{ "name": fmt.Sprintf("pod-%d", i), "namespace": barPodGKN.Namespace, }, @@ -198,7 +198,7 @@ func TestSync(t *testing.T) { expectedCached: map[gkNamespace]cacheEntry{ barPodGKN: { resources: unstructured.UnstructuredList{ - Object: map[string]interface{}{ + Object: map[string]any{ "apiVersion": podGVK.GroupVersion().String(), "kind": podGVK.Kind, }, diff --git a/pkg/kstatus/polling/event/event_test.go b/pkg/kstatus/polling/event/event_test.go index da6a610e..0724a94f 100644 --- a/pkg/kstatus/polling/event/event_test.go +++ b/pkg/kstatus/polling/event/event_test.go @@ -31,8 +31,8 @@ func TestDeepEqual(t *testing.T) { Name: "Foo", }, Resource: &unstructured.Unstructured{ - Object: map[string]interface{}{ - "metadata": map[string]interface{}{ + Object: map[string]any{ + "metadata": map[string]any{ "generation": int64(1), }, }, @@ -50,8 +50,8 @@ func TestDeepEqual(t *testing.T) { Name: "Foo", }, Resource: &unstructured.Unstructured{ - Object: map[string]interface{}{ - "metadata": map[string]interface{}{ + Object: map[string]any{ + "metadata": map[string]any{ "generation": int64(2), }, }, diff --git a/pkg/kstatus/polling/statusreaders/common_test.go b/pkg/kstatus/polling/statusreaders/common_test.go index c11b0888..eed6bbb4 100644 --- a/pkg/kstatus/polling/statusreaders/common_test.go +++ b/pkg/kstatus/polling/statusreaders/common_test.go @@ -196,10 +196,10 @@ spec: `, listObjects: []unstructured.Unstructured{ { - Object: map[string]interface{}{ + Object: map[string]any{ "apiVersion": "apps/v1", "kind": "ReplicaSet", - "metadata": map[string]interface{}{ + "metadata": map[string]any{ "name": "Foo-12345", "namespace": "default", }, diff --git a/pkg/kstatus/polling/statusreaders/pod_controller_test.go b/pkg/kstatus/polling/statusreaders/pod_controller_test.go index 43c6a954..788cb4d9 100644 --- a/pkg/kstatus/polling/statusreaders/pod_controller_test.go +++ b/pkg/kstatus/polling/statusreaders/pod_controller_test.go @@ -232,8 +232,8 @@ func newPodWithPhase(name, namespace string, phase corev1.PodPhase) *unstructure func newUnschedulablePod(name, namespace string) *unstructured.Unstructured { pod := newPodWithPhase(name, namespace, corev1.PodPending) - _ = unstructured.SetNestedSlice(pod.Object, []interface{}{ - map[string]interface{}{ + _ = unstructured.SetNestedSlice(pod.Object, []any{ + map[string]any{ "type": string(corev1.PodScheduled), "status": string(corev1.ConditionFalse), "reason": corev1.PodReasonUnschedulable, diff --git a/pkg/kstatus/polling/testutil/testing.go b/pkg/kstatus/polling/testutil/testing.go index 25276fbc..8719e2a2 100644 --- a/pkg/kstatus/polling/testutil/testing.go +++ b/pkg/kstatus/polling/testutil/testing.go @@ -11,7 +11,7 @@ import ( ) func YamlToUnstructured(t *testing.T, yml string) *unstructured.Unstructured { - m := make(map[string]interface{}) + m := make(map[string]any) err := yaml.Unmarshal([]byte(yml), &m) if err != nil { t.Fatalf("error parsing yaml: %v", err) diff --git a/pkg/kstatus/status/core.go b/pkg/kstatus/status/core.go index 4cd6704e..5d5dbbd8 100644 --- a/pkg/kstatus/status/core.go +++ b/pkg/kstatus/status/core.go @@ -466,14 +466,14 @@ func podConditions(u *unstructured.Unstructured) (*Result, error) { } } -func getCrashLoopingContainers(obj map[string]interface{}) ([]string, bool, error) { +func getCrashLoopingContainers(obj map[string]any) ([]string, bool, error) { var containerNames []string css, found, err := unstructured.NestedSlice(obj, "status", "containerStatuses") if !found || err != nil { return containerNames, found, err } for _, item := range css { - cs := item.(map[string]interface{}) + cs := item.(map[string]any) n, found := cs["name"] if !found { continue @@ -483,13 +483,13 @@ func getCrashLoopingContainers(obj map[string]interface{}) ([]string, bool, erro if !found { continue } - state := s.(map[string]interface{}) + state := s.(map[string]any) ws, found := state["waiting"] if !found { continue } - waitingState := ws.(map[string]interface{}) + waitingState := ws.(map[string]any) r, found := waitingState["reason"] if !found { diff --git a/pkg/kstatus/status/status.go b/pkg/kstatus/status/status.go index a8f2e28d..82cd37e6 100644 --- a/pkg/kstatus/status/status.go +++ b/pkg/kstatus/status/status.go @@ -6,6 +6,7 @@ package status import ( "errors" "fmt" + "slices" "time" corev1 "k8s.io/api/core/v1" @@ -52,10 +53,8 @@ func (s Status) String() string { // not a valid status. func FromStringOrDie(text string) Status { s := Status(text) - for _, r := range Statuses { - if s == r { - return s - } + if slices.Contains(Statuses, s) { + return s } panic(fmt.Errorf("string has invalid status: %s", s)) } @@ -196,7 +195,7 @@ func Augment(u *unstructured.Unstructured) error { } if !found { - conditions = make([]interface{}, 0) + conditions = make([]any, 0) } currentTime := time.Now().UTC().Format(time.RFC3339) @@ -204,7 +203,7 @@ func Augment(u *unstructured.Unstructured) error { for _, resCondition := range res.Conditions { present := false for _, c := range conditions { - condition, ok := c.(map[string]interface{}) + condition, ok := c.(map[string]any) if !ok { return errors.New("condition does not have the expected structure") } @@ -228,7 +227,7 @@ func Augment(u *unstructured.Unstructured) error { } } if !present { - conditions = append(conditions, map[string]interface{}{ + conditions = append(conditions, map[string]any{ "lastTransitionTime": currentTime, "lastUpdateTime": currentTime, "message": resCondition.Message, diff --git a/pkg/kstatus/status/status_augment_test.go b/pkg/kstatus/status/status_augment_test.go index 6221b500..c9e5d859 100644 --- a/pkg/kstatus/status/status_augment_test.go +++ b/pkg/kstatus/status/status_augment_test.go @@ -34,8 +34,8 @@ metadata: var timestamp = time.Now().Add(-1 * time.Minute).UTC().Format(time.RFC3339) -func addConditions(t *testing.T, u *unstructured.Unstructured, conditions []map[string]interface{}) { - conds := make([]interface{}, 0) +func addConditions(t *testing.T, u *unstructured.Unstructured, conditions []map[string]any) { + conds := make([]any, 0) for _, c := range conditions { conds = append(conds, c) } @@ -48,12 +48,12 @@ func addConditions(t *testing.T, u *unstructured.Unstructured, conditions []map[ func TestAugmentConditions(t *testing.T) { testCases := map[string]struct { manifest string - withConditions []map[string]interface{} + withConditions []map[string]any expectedConditions []Condition }{ "no existing conditions": { manifest: pod, - withConditions: []map[string]interface{}{}, + withConditions: []map[string]any{}, expectedConditions: []Condition{ { Type: ConditionReconciling, @@ -64,7 +64,7 @@ func TestAugmentConditions(t *testing.T) { }, "has other existing conditions": { manifest: pod, - withConditions: []map[string]interface{}{ + withConditions: []map[string]any{ { "lastTransitionTime": timestamp, "lastUpdateTime": timestamp, @@ -88,7 +88,7 @@ func TestAugmentConditions(t *testing.T) { }, "already has condition of standard type InProgress": { manifest: pod, - withConditions: []map[string]interface{}{ + withConditions: []map[string]any{ { "lastTransitionTime": timestamp, "lastUpdateTime": timestamp, @@ -107,7 +107,7 @@ func TestAugmentConditions(t *testing.T) { }, "already has condition of standard type Failed": { manifest: pod, - withConditions: []map[string]interface{}{ + withConditions: []map[string]any{ { "lastTransitionTime": timestamp, "lastUpdateTime": timestamp, @@ -126,13 +126,12 @@ func TestAugmentConditions(t *testing.T) { }, "custom resource with no conditions": { manifest: custom, - withConditions: []map[string]interface{}{}, + withConditions: []map[string]any{}, expectedConditions: []Condition{}, }, } for tn, tc := range testCases { - tc := tc t.Run(tn, func(t *testing.T) { u := y2u(t, tc.manifest) addConditions(t, u, tc.withConditions) diff --git a/pkg/kstatus/status/status_compute_test.go b/pkg/kstatus/status/status_compute_test.go index ca8b5317..226d880e 100644 --- a/pkg/kstatus/status/status_compute_test.go +++ b/pkg/kstatus/status/status_compute_test.go @@ -249,7 +249,6 @@ func TestPodStatus(t *testing.T) { } for tn, tc := range testCases { - tc := tc t.Run(tn, func(t *testing.T) { runStatusTest(t, tc) }) @@ -300,7 +299,6 @@ func TestPVCStatus(t *testing.T) { } for tn, tc := range testCases { - tc := tc t.Run(tn, func(t *testing.T) { runStatusTest(t, tc) }) @@ -462,7 +460,6 @@ func TestStsStatus(t *testing.T) { } for tn, tc := range testCases { - tc := tc t.Run(tn, func(t *testing.T) { runStatusTest(t, tc) }) @@ -624,7 +621,6 @@ func TestDaemonsetStatus(t *testing.T) { } for tn, tc := range testCases { - tc := tc t.Run(tn, func(t *testing.T) { runStatusTest(t, tc) }) @@ -784,7 +780,6 @@ func TestDeploymentStatus(t *testing.T) { } for tn, tc := range testCases { - tc := tc t.Run(tn, func(t *testing.T) { runStatusTest(t, tc) }) @@ -961,7 +956,6 @@ func TestReplicasetStatus(t *testing.T) { } for tn, tc := range testCases { - tc := tc t.Run(tn, func(t *testing.T) { runStatusTest(t, tc) }) @@ -1016,7 +1010,6 @@ func TestPDBStatus(t *testing.T) { } for tn, tc := range testCases { - tc := tc t.Run(tn, func(t *testing.T) { runStatusTest(t, tc) }) @@ -1144,7 +1137,6 @@ func TestCRDGenericStatus(t *testing.T) { } for tn, tc := range testCases { - tc := tc t.Run(tn, func(t *testing.T) { runStatusTest(t, tc) }) @@ -1262,7 +1254,6 @@ func TestJobStatus(t *testing.T) { } for tn, tc := range testCases { - tc := tc t.Run(tn, func(t *testing.T) { runStatusTest(t, tc) }) @@ -1311,7 +1302,6 @@ func TestCronJobStatus(t *testing.T) { } for tn, tc := range testCases { - tc := tc t.Run(tn, func(t *testing.T) { runStatusTest(t, tc) }) @@ -1404,7 +1394,6 @@ func TestServiceStatus(t *testing.T) { } for tn, tc := range testCases { - tc := tc t.Run(tn, func(t *testing.T) { runStatusTest(t, tc) }) @@ -1516,7 +1505,6 @@ func TestCRDStatus(t *testing.T) { } for tn, tc := range testCases { - tc := tc t.Run(tn, func(t *testing.T) { runStatusTest(t, tc) }) diff --git a/pkg/kstatus/status/util.go b/pkg/kstatus/status/util.go index b22152cc..595bdba2 100644 --- a/pkg/kstatus/status/util.go +++ b/pkg/kstatus/status/util.go @@ -76,7 +76,7 @@ type BasicCondition struct { } // GetObjectWithConditions return typed object -func GetObjectWithConditions(in map[string]interface{}) (*ObjWithConditions, error) { +func GetObjectWithConditions(in map[string]any) (*ObjWithConditions, error) { var out = new(ObjWithConditions) err := runtime.DefaultUnstructuredConverter.FromUnstructured(in, out) if err != nil { @@ -100,7 +100,7 @@ func getConditionWithStatus(conditions []BasicCondition, conditionType string, s } // GetStringField return field as string defaulting to value if not found -func GetStringField(obj map[string]interface{}, fieldPath string, defaultValue string) string { +func GetStringField(obj map[string]any, fieldPath string, defaultValue string) string { var rv = defaultValue fields := strings.Split(fieldPath, ".") @@ -120,7 +120,7 @@ func GetStringField(obj map[string]interface{}, fieldPath string, defaultValue s } // GetIntField return field as string defaulting to value if not found -func GetIntField(obj map[string]interface{}, fieldPath string, defaultValue int) int { +func GetIntField(obj map[string]any, fieldPath string, defaultValue int) int { fields := strings.Split(fieldPath, ".") if fields[0] == "" { fields = fields[1:] diff --git a/pkg/kstatus/status/util_test.go b/pkg/kstatus/status/util_test.go index 89333bf1..b8066e6e 100644 --- a/pkg/kstatus/status/util_test.go +++ b/pkg/kstatus/status/util_test.go @@ -9,18 +9,18 @@ import ( "github.com/stretchr/testify/assert" ) -var testObj = map[string]interface{}{ - "f1": map[string]interface{}{ - "f2": map[string]interface{}{ +var testObj = map[string]any{ + "f1": map[string]any{ + "f2": map[string]any{ "i32": int32(32), "i64": int64(64), "float": 64.02, - "ms": []interface{}{ - map[string]interface{}{"f1f2ms0f1": 22}, - map[string]interface{}{"f1f2ms1f1": "index1"}, + "ms": []any{ + map[string]any{"f1f2ms0f1": 22}, + map[string]any{"f1f2ms1f1": "index1"}, }, - "msbad": []interface{}{ - map[string]interface{}{"f1f2ms0f1": 22}, + "msbad": []any{ + map[string]any{"f1f2ms0f1": 22}, 32, }, }, @@ -28,10 +28,10 @@ var testObj = map[string]interface{}{ "ride": "dragon", - "status": map[string]interface{}{ - "conditions": []interface{}{ - map[string]interface{}{"f1f2ms0f1": 22}, - map[string]interface{}{"f1f2ms1f1": "index1"}, + "status": map[string]any{ + "conditions": []any{ + map[string]any{"f1f2ms0f1": 22}, + map[string]any{"f1f2ms1f1": "index1"}, }, }, } diff --git a/pkg/kstatus/watcher/default_status_watcher_test.go b/pkg/kstatus/watcher/default_status_watcher_test.go index 92cd4e68..ff0df6cf 100644 --- a/pkg/kstatus/watcher/default_status_watcher_test.go +++ b/pkg/kstatus/watcher/default_status_watcher_test.go @@ -271,7 +271,7 @@ status: ` func yamlToUnstructured(t *testing.T, yml string) *unstructured.Unstructured { - m := make(map[string]interface{}) + m := make(map[string]any) err := yaml.Unmarshal([]byte(yml), &m) if err != nil { t.Fatalf("error parsing yaml: %v", err) @@ -950,7 +950,7 @@ func TestEventHandlerConcurrency(t *testing.T) { pods := make([]*unstructured.Unstructured, numPods) ids := make(object.ObjMetadataSet, numPods) - for i := 0; i < numPods; i++ { + for i := range numPods { pod := &unstructured.Unstructured{} pod.SetGroupVersionKind(v1.SchemeGroupVersion.WithKind("Pod")) pod.SetNamespace("default") diff --git a/pkg/kstatus/watcher/object_status_reporter.go b/pkg/kstatus/watcher/object_status_reporter.go index 6ae3f1e5..0981df76 100644 --- a/pkg/kstatus/watcher/object_status_reporter.go +++ b/pkg/kstatus/watcher/object_status_reporter.go @@ -472,7 +472,7 @@ func (w *ObjectStatusReporter) syncEventHandler( ) cache.ResourceEventHandler { var handler cache.ResourceEventHandlerFuncs - handler.AddFunc = func(iobj interface{}) { + handler.AddFunc = func(iobj any) { // Bail early if the context is cancelled, to avoid unnecessary work. if ctx.Err() != nil { return @@ -521,7 +521,7 @@ func (w *ObjectStatusReporter) syncEventHandler( } } - handler.UpdateFunc = func(_, iobj interface{}) { + handler.UpdateFunc = func(_, iobj any) { // Bail early if the context is cancelled, to avoid unnecessary work. if ctx.Err() != nil { return @@ -570,7 +570,7 @@ func (w *ObjectStatusReporter) syncEventHandler( } } - handler.DeleteFunc = func(iobj interface{}) { + handler.DeleteFunc = func(iobj any) { // Bail early if the context is cancelled, to avoid unnecessary work. if ctx.Err() != nil { return @@ -630,7 +630,7 @@ func (w *ObjectStatusReporter) asyncEventHandler( ) cache.ResourceEventHandler { var handler cache.ResourceEventHandlerFuncs - handler.AddFunc = func(iobj interface{}) { + handler.AddFunc = func(iobj any) { if ctx.Err() != nil { return } @@ -660,9 +660,7 @@ func (w *ObjectStatusReporter) asyncEventHandler( latestRVs.Store(id, rv) objCopy := obj.DeepCopy() - wg.Add(1) - go func() { - defer wg.Done() + wg.Go(func() { select { case sem <- struct{}{}: defer func() { <-sem }() @@ -695,10 +693,10 @@ func (w *ObjectStatusReporter) asyncEventHandler( Type: event.ResourceUpdateEvent, Resource: rs, } - }() + }) } - handler.UpdateFunc = func(_, iobj interface{}) { + handler.UpdateFunc = func(_, iobj any) { if ctx.Err() != nil { return } @@ -728,9 +726,7 @@ func (w *ObjectStatusReporter) asyncEventHandler( latestRVs.Store(id, rv) objCopy := obj.DeepCopy() - wg.Add(1) - go func() { - defer wg.Done() + wg.Go(func() { select { case sem <- struct{}{}: defer func() { <-sem }() @@ -763,10 +759,10 @@ func (w *ObjectStatusReporter) asyncEventHandler( Type: event.ResourceUpdateEvent, Resource: rs, } - }() + }) } - handler.DeleteFunc = func(iobj interface{}) { + handler.DeleteFunc = func(iobj any) { if ctx.Err() != nil { return } diff --git a/pkg/kstatus/watcher/unschedulable.go b/pkg/kstatus/watcher/unschedulable.go index 9ff8b508..3f97784b 100644 --- a/pkg/kstatus/watcher/unschedulable.go +++ b/pkg/kstatus/watcher/unschedulable.go @@ -4,6 +4,8 @@ package watcher import ( + "slices" + "github.com/fluxcd/cli-utils/pkg/kstatus/polling/event" "github.com/fluxcd/cli-utils/pkg/kstatus/status" "github.com/fluxcd/cli-utils/pkg/object" @@ -28,12 +30,7 @@ func isObjectUnschedulable(rs *event.ResourceStatus) bool { return true } // recurse through generated resources - for _, subRS := range rs.GeneratedResources { - if isObjectUnschedulable(subRS) { - return true - } - } - return false + return slices.ContainsFunc(rs.GeneratedResources, isObjectUnschedulable) } // isPodUnschedulable returns true if the object is a pod and is unschedulable @@ -50,12 +47,12 @@ func isPodUnschedulable(obj *unstructured.Unstructured) bool { if err != nil || !found { return false } - cnds, ok := icnds.([]interface{}) + cnds, ok := icnds.([]any) if !ok { return false } for _, icnd := range cnds { - cnd, ok := icnd.(map[string]interface{}) + cnd, ok := icnd.(map[string]any) if !ok { return false } diff --git a/pkg/object/field.go b/pkg/object/field.go index 7d530b4f..0e212cfd 100644 --- a/pkg/object/field.go +++ b/pkg/object/field.go @@ -13,8 +13,8 @@ import ( // NestedField gets a value from a KRM map, if it exists, otherwise nil. // Fields can be string (map key) or int (array index). -func NestedField(obj map[string]interface{}, fields ...interface{}) (interface{}, bool, error) { - var val interface{} = obj +func NestedField(obj map[string]any, fields ...any) (any, bool, error) { + var val any = obj for i, field := range fields { if val == nil { @@ -22,7 +22,7 @@ func NestedField(obj map[string]interface{}, fields ...interface{}) (interface{} } switch typedField := field.(type) { case string: - if m, ok := val.(map[string]interface{}); ok { + if m, ok := val.(map[string]any); ok { val, ok = m[typedField] if !ok { // not in map @@ -32,7 +32,7 @@ func NestedField(obj map[string]interface{}, fields ...interface{}) (interface{} return nil, false, InvalidType(fields[:i+1], val, "map[string]interface{}") } case int: - if s, ok := val.([]interface{}); ok { + if s, ok := val.([]any); ok { if typedField >= len(s) { // index out of range return nil, false, nil @@ -50,14 +50,14 @@ func NestedField(obj map[string]interface{}, fields ...interface{}) (interface{} // InvalidType returns a *Error indicating "invalid value type". This is used // to report malformed values (e.g. found int, expected string). -func InvalidType(fieldPath []interface{}, value interface{}, validTypes string) *field.Error { +func InvalidType(fieldPath []any, value any, validTypes string) *field.Error { return Invalid(fieldPath, value, fmt.Sprintf("found type %T, expected %s", value, validTypes)) } // Invalid returns a *Error indicating "invalid value". This is used // to report malformed values (e.g. failed regex match, too long, out of bounds). -func Invalid(fieldPath []interface{}, value interface{}, detail string) *field.Error { +func Invalid(fieldPath []any, value any, detail string) *field.Error { return &field.Error{ Type: field.ErrorTypeInvalid, Field: FieldPath(fieldPath), @@ -68,7 +68,7 @@ func Invalid(fieldPath []interface{}, value interface{}, detail string) *field.E // NotFound returns a *Error indicating "value not found". This is // used to report failure to find a requested value (e.g. looking up an ID). -func NotFound(fieldPath []interface{}, value interface{}) *field.Error { +func NotFound(fieldPath []any, value any) *field.Error { return &field.Error{ Type: field.ErrorTypeNotFound, Field: FieldPath(fieldPath), @@ -83,7 +83,7 @@ func NotFound(fieldPath []interface{}, value interface{}) *field.Error { // Complex strings will be wrapped with square brackets and double quotes. // Integers will be wrapped with square brackets. // All other types will be formatted best-effort within square brackets. -func FieldPath(fieldPath []interface{}) string { +func FieldPath(fieldPath []any) string { var sb strings.Builder for _, field := range fieldPath { switch typedField := field.(type) { diff --git a/pkg/object/field_test.go b/pkg/object/field_test.go index 21146c9e..efe38749 100644 --- a/pkg/object/field_test.go +++ b/pkg/object/field_test.go @@ -11,80 +11,80 @@ import ( func TestFieldPath(t *testing.T) { tests := map[string]struct { - fieldPath []interface{} + fieldPath []any expected string }{ "empty path": { - fieldPath: []interface{}{}, + fieldPath: []any{}, expected: "", }, "kind": { - fieldPath: []interface{}{"kind"}, + fieldPath: []any{"kind"}, expected: ".kind", }, "metadata.name": { - fieldPath: []interface{}{"metadata", "name"}, + fieldPath: []any{"metadata", "name"}, expected: ".metadata.name", }, "spec.versions[1].name": { - fieldPath: []interface{}{"spec", "versions", 1, "name"}, + fieldPath: []any{"spec", "versions", 1, "name"}, expected: ".spec.versions[1].name", }, "numeric": { - fieldPath: []interface{}{"spec", "123"}, + fieldPath: []any{"spec", "123"}, expected: `.spec["123"]`, }, "alphanumeric, ends with number": { - fieldPath: []interface{}{"spec", "abc123"}, + fieldPath: []any{"spec", "abc123"}, expected: `.spec.abc123`, }, "alphanumeric, ends with hyphen": { - fieldPath: []interface{}{"spec", "abc123-"}, + fieldPath: []any{"spec", "abc123-"}, expected: `.spec["abc123-"]`, }, "alphanumeric, ends with underscore": { - fieldPath: []interface{}{"spec", "abc123_"}, + fieldPath: []any{"spec", "abc123_"}, expected: `.spec["abc123_"]`, }, "alphanumeric, starts with hyphen": { - fieldPath: []interface{}{"spec", "-abc123"}, + fieldPath: []any{"spec", "-abc123"}, expected: `.spec["-abc123"]`, }, "alphanumeric, starts with underscore": { - fieldPath: []interface{}{"spec", "_abc123"}, + fieldPath: []any{"spec", "_abc123"}, expected: `.spec["_abc123"]`, }, "alphanumeric, starts with number": { - fieldPath: []interface{}{"spec", "_abc123"}, + fieldPath: []any{"spec", "_abc123"}, expected: `.spec["_abc123"]`, }, "alphanumeric, intrnal hyphen": { - fieldPath: []interface{}{"spec", "abc-123"}, + fieldPath: []any{"spec", "abc-123"}, expected: `.spec.abc-123`, }, "alphanumeric, intrnal underscore": { - fieldPath: []interface{}{"spec", "abc_123"}, + fieldPath: []any{"spec", "abc_123"}, expected: `.spec.abc_123`, }, "space": { - fieldPath: []interface{}{"spec", "abc 123"}, + fieldPath: []any{"spec", "abc 123"}, expected: `.spec["abc 123"]`, }, "tab": { - fieldPath: []interface{}{"spec", "abc\t123"}, + fieldPath: []any{"spec", "abc\t123"}, expected: `.spec["abc\t123"]`, }, "linebreak": { - fieldPath: []interface{}{"spec", "abc\n123"}, + fieldPath: []any{"spec", "abc\n123"}, expected: `.spec["abc\n123"]`, }, // result from invalid input doesn't matter, as long as it doesn't panic "invalid type: float": { - fieldPath: []interface{}{"spec", float64(-1.0)}, + fieldPath: []any{"spec", float64(-1.0)}, expected: `.spec[-1]`, }, "invalid type: struct": { - fieldPath: []interface{}{"spec", struct{ Field string }{Field: "value"}}, + fieldPath: []any{"spec", struct{ Field string }{Field: "value"}}, expected: `.spec[struct { Field string }{Field:"value"}]`, }, } diff --git a/pkg/object/infos_test.go b/pkg/object/infos_test.go index 3a01d1ae..f1432aef 100644 --- a/pkg/object/infos_test.go +++ b/pkg/object/infos_test.go @@ -20,12 +20,12 @@ func TestUnstructuredToInfo(t *testing.T) { }{ "with path annotation": { obj: &unstructured.Unstructured{ - Object: map[string]interface{}{ + Object: map[string]any{ "apiVersion": "apps/v1", "kind": "Deployment", - "metadata": map[string]interface{}{ + "metadata": map[string]any{ "name": "foo", - "annotations": map[string]interface{}{ + "annotations": map[string]any{ kioutil.PathAnnotation: "deployment.yaml", }, }, @@ -37,12 +37,12 @@ func TestUnstructuredToInfo(t *testing.T) { }, "with both new and legacy annotations": { obj: &unstructured.Unstructured{ - Object: map[string]interface{}{ + Object: map[string]any{ "apiVersion": "apps/v1", "kind": "Deployment", - "metadata": map[string]interface{}{ + "metadata": map[string]any{ "name": "foo", - "annotations": map[string]interface{}{ + "annotations": map[string]any{ kioutil.PathAnnotation: "deployment.yaml", kioutil.LegacyPathAnnotation: "deployment.yaml", //nolint:staticcheck }, @@ -55,10 +55,10 @@ func TestUnstructuredToInfo(t *testing.T) { }, "without path annotation": { obj: &unstructured.Unstructured{ - Object: map[string]interface{}{ + Object: map[string]any{ "apiVersion": "apps/v1", "kind": "Deployment", - "metadata": map[string]interface{}{ + "metadata": map[string]any{ "name": "foo", "namespace": "bar", }, diff --git a/pkg/object/objmetadata_set.go b/pkg/object/objmetadata_set.go index 31a8b473..8f65e3d6 100644 --- a/pkg/object/objmetadata_set.go +++ b/pkg/object/objmetadata_set.go @@ -6,6 +6,7 @@ package object import ( "hash/fnv" + "slices" "sort" "strconv" ) @@ -54,12 +55,7 @@ func (setA ObjMetadataSet) Equal(setB ObjMetadataSet) bool { // Contains checks if the provided ObjMetadata exists in the set. func (setA ObjMetadataSet) Contains(id ObjMetadata) bool { - for _, om := range setA { - if om == id { - return true - } - } - return false + return slices.Contains(setA, id) } // Remove the object from the set and return the updated set. @@ -75,12 +71,7 @@ func (setA ObjMetadataSet) Remove(obj ObjMetadata) ObjMetadataSet { // Intersection returns the set of unique objects in both set A and set B. func (setA ObjMetadataSet) Intersection(setB ObjMetadataSet) ObjMetadataSet { - var maxlen int - if len(setA) > len(setB) { - maxlen = len(setA) - } else { - maxlen = len(setB) - } + maxlen := max(len(setA), len(setB)) mapI := make(map[ObjMetadata]struct{}, maxlen) mapB := setB.ToMap() for _, a := range setA { diff --git a/pkg/object/unstructured.go b/pkg/object/unstructured.go index 57a16d06..6eb2c89e 100644 --- a/pkg/object/unstructured.go +++ b/pkg/object/unstructured.go @@ -132,14 +132,14 @@ func LookupResourceScope(u *unstructured.Unstructured, crds []*unstructured.Unst return nil, err } if !found || group == "" { - return nil, NotFound([]interface{}{"spec", "group"}, group) + return nil, NotFound([]any{"spec", "group"}, group) } kind, found, err := NestedField(crd.Object, "spec", "names", "kind") if err != nil { return nil, err } if !found || kind == "" { - return nil, NotFound([]interface{}{"spec", "kind"}, group) + return nil, NotFound([]any{"spec", "kind"}, group) } if gvk.Kind != kind || gvk.Group != group { continue @@ -163,7 +163,7 @@ func LookupResourceScope(u *unstructured.Unstructured, crds []*unstructured.Unst case "Cluster": return meta.RESTScopeRoot, nil default: - return nil, Invalid([]interface{}{"spec", "scope"}, scopeName, + return nil, Invalid([]any{"spec", "scope"}, scopeName, "expected Namespaced or Cluster") } } @@ -178,14 +178,14 @@ func crdDefinesVersion(crd *unstructured.Unstructured, version string) (bool, er return false, err } if !found { - return false, NotFound([]interface{}{"spec", "versions"}, versions) + return false, NotFound([]any{"spec", "versions"}, versions) } - versionsSlice, ok := versions.([]interface{}) + versionsSlice, ok := versions.([]any) if !ok { - return false, InvalidType([]interface{}{"spec", "versions"}, versions, "[]interface{}") + return false, InvalidType([]any{"spec", "versions"}, versions, "[]interface{}") } if len(versionsSlice) == 0 { - return false, Invalid([]interface{}{"spec", "versions"}, versionsSlice, "must not be empty") + return false, Invalid([]any{"spec", "versions"}, versionsSlice, "must not be empty") } for i := range versionsSlice { name, found, err := NestedField(crd.Object, "spec", "versions", i, "name") @@ -193,7 +193,7 @@ func crdDefinesVersion(crd *unstructured.Unstructured, version string) (bool, er return false, err } if !found { - return false, NotFound([]interface{}{"spec", "versions", i, "name"}, name) + return false, NotFound([]any{"spec", "versions", i, "name"}, name) } if name == version { return true, nil diff --git a/pkg/testutil/asserter.go b/pkg/testutil/asserter.go index 597d8e82..7bb19368 100644 --- a/pkg/testutil/asserter.go +++ b/pkg/testutil/asserter.go @@ -30,7 +30,7 @@ var DefaultAsserter = NewAsserter(cmpopts.EquateErrors()) // EqualMatcher returns a new EqualMatcher with the Asserter's options and the // specified expected value. -func (a *Asserter) EqualMatcher(expected interface{}) *EqualMatcher { +func (a *Asserter) EqualMatcher(expected any) *EqualMatcher { return &EqualMatcher{ Expected: expected, Options: a.Options, @@ -39,7 +39,7 @@ func (a *Asserter) EqualMatcher(expected interface{}) *EqualMatcher { // Equal fails the test if the actual value does not deeply equal the // expected value. Prints a diff on failure. -func (a *Asserter) Equal(t *testing.T, expected, actual interface{}, msgAndArgs ...interface{}) { +func (a *Asserter) Equal(t *testing.T, expected, actual any, msgAndArgs ...any) { t.Helper() // print the caller's file:line, instead of this func, on failure matcher := a.EqualMatcher(expected) match, err := matcher.Match(actual) @@ -54,14 +54,14 @@ func (a *Asserter) Equal(t *testing.T, expected, actual interface{}, msgAndArgs // AssertEqual fails the test if the actual value does not deeply equal the // expected value. Prints a diff on failure. -func AssertEqual(t *testing.T, expected, actual interface{}, msgAndArgs ...interface{}) { +func AssertEqual(t *testing.T, expected, actual any, msgAndArgs ...any) { t.Helper() // print the caller's file:line, instead of this func, on failure DefaultAsserter.Equal(t, expected, actual, msgAndArgs...) } // NotEqual fails the test if the actual value deeply equals the // expected value. Prints a diff on failure. -func (a *Asserter) NotEqual(t *testing.T, expected, actual interface{}, msgAndArgs ...interface{}) { +func (a *Asserter) NotEqual(t *testing.T, expected, actual any, msgAndArgs ...any) { t.Helper() // print the caller's file:line, instead of this func, on failure matcher := a.EqualMatcher(expected) match, err := matcher.Match(actual) @@ -76,7 +76,7 @@ func (a *Asserter) NotEqual(t *testing.T, expected, actual interface{}, msgAndAr // AssertNotEqual fails the test if the actual value deeply equals the // expected value. Prints a diff on failure. -func AssertNotEqual(t *testing.T, expected, actual interface{}, msgAndArgs ...interface{}) { +func AssertNotEqual(t *testing.T, expected, actual any, msgAndArgs ...any) { t.Helper() // print the caller's file:line, instead of this func, on failure DefaultAsserter.NotEqual(t, expected, actual, msgAndArgs...) } diff --git a/pkg/testutil/matcher.go b/pkg/testutil/matcher.go index c9d3ca0f..96bd74d6 100644 --- a/pkg/testutil/matcher.go +++ b/pkg/testutil/matcher.go @@ -18,18 +18,18 @@ import ( // // Example Usage: // Expect(receivedEvents).To(testutil.Equal(expectedEvents)) -func Equal(expected interface{}) *EqualMatcher { +func Equal(expected any) *EqualMatcher { return DefaultAsserter.EqualMatcher(expected) } type EqualMatcher struct { - Expected interface{} + Expected any Options cmp.Options explanation error } -func (cm *EqualMatcher) Match(actual interface{}) (bool, error) { +func (cm *EqualMatcher) Match(actual any) (bool, error) { match := cmp.Equal(cm.Expected, actual, cm.Options...) if !match { cm.explanation = errors.New(cmp.Diff(cm.Expected, actual, cm.Options...)) @@ -37,12 +37,12 @@ func (cm *EqualMatcher) Match(actual interface{}) (bool, error) { return match, nil } -func (cm *EqualMatcher) FailureMessage(actual interface{}) string { +func (cm *EqualMatcher) FailureMessage(actual any) string { return "\n" + format.Message(actual, "to deeply equal", cm.Expected) + "\nDiff (- Expected, + Actual):\n" + indent(cm.explanation.Error(), 1) } -func (cm *EqualMatcher) NegatedFailureMessage(actual interface{}) string { +func (cm *EqualMatcher) NegatedFailureMessage(actual any) string { return "\n" + format.Message(actual, "not to deeply equal", cm.Expected) + "\nDiff (- Expected, + Actual):\n" + indent(cm.explanation.Error(), 1) }