From ee9638f0425866f0a7888e152ce533d1491f0edb Mon Sep 17 00:00:00 2001 From: thomasvn Date: Mon, 11 May 2026 21:29:55 -0700 Subject: [PATCH 1/3] Ran `go fix` on this codebase. --- cldy/clients.go | 10 ++--- kubecost/adapters/metricsquerier_test.go | 2 +- pkg/cluster/dynamic.go | 48 ++++++++++++------------ pkg/env/unifiedagentenv.go | 4 +- pkg/nodes/request.go | 2 +- 5 files changed, 33 insertions(+), 33 deletions(-) diff --git a/cldy/clients.go b/cldy/clients.go index 378f6496..250be652 100644 --- a/cldy/clients.go +++ b/cldy/clients.go @@ -345,7 +345,7 @@ func (s *ApptioServiceImpl) testUpload() error { // upload to Apptio's S3 bucket func (s *ApptioServiceImpl) getUploadURL(payload UploadPayload) (uploadURL string, rErr error) { url := fmt.Sprintf("%s%s", s.CloudabilityURL, clustersUploadEndpoint) - body, err := json.Marshal(map[string]interface{}{ + body, err := json.Marshal(map[string]any{ "clusterUID": payload.ClusterUID, "fileName": payload.FileName, "agentVersion": payload.AgentVersion, @@ -531,8 +531,8 @@ type CustomS3Uploader struct { func newUploadClient(s3Region string) (*CustomS3Uploader, error) { sess, err := session.NewSession(&aws.Config{ - Region: aws.String(s3Region), - MaxRetries: aws.Int(3)}, + Region: new(s3Region), + MaxRetries: new(3)}, ) if err != nil { return nil, fmt.Errorf("could not establish AWS Session, "+ @@ -558,8 +558,8 @@ func (cs3c CustomS3Client) Upload(payload UploadPayload) (err error) { } sampleToUpload := &s3manager.UploadInput{ - Bucket: aws.String(cs3c.S3Bucket), - Key: aws.String(key), + Bucket: new(cs3c.S3Bucket), + Key: new(key), Body: fileReader, } diff --git a/kubecost/adapters/metricsquerier_test.go b/kubecost/adapters/metricsquerier_test.go index adff6791..c2d0a494 100644 --- a/kubecost/adapters/metricsquerier_test.go +++ b/kubecost/adapters/metricsquerier_test.go @@ -157,7 +157,7 @@ func TestMinuteMetricsTick(t *testing.T) { metricsQuerier := NewMetricsQuerierAdapter(snapshot.Metrics) // tick an hour - for i := 0; i < 60; i++ { + for range 60 { // this will advance the "now" in the snapshotter context *current = current.Add(time.Minute) diff --git a/pkg/cluster/dynamic.go b/pkg/cluster/dynamic.go index 7e0d8852..8a6bf6bb 100644 --- a/pkg/cluster/dynamic.go +++ b/pkg/cluster/dynamic.go @@ -43,21 +43,21 @@ func LoadInformerConfig() InformerConfig { var ( cacheResourceMap = map[reflect.Type]schema.GroupVersionResource{ - reflect.TypeOf(corev1.Namespace{}): {Version: "v1", Resource: "namespaces"}, - reflect.TypeOf(corev1.Node{}): {Version: "v1", Resource: "nodes"}, - reflect.TypeOf(corev1.Pod{}): {Version: "v1", Resource: "pods"}, - reflect.TypeOf(corev1.Service{}): {Version: "v1", Resource: "services"}, - reflect.TypeOf(corev1.PersistentVolume{}): {Version: "v1", Resource: "persistentvolumes"}, - reflect.TypeOf(corev1.PersistentVolumeClaim{}): {Version: "v1", Resource: "persistentvolumeclaims"}, - reflect.TypeOf(corev1.ReplicationController{}): {Version: "v1", Resource: "replicationcontrollers"}, - reflect.TypeOf(appsv1.Deployment{}): {Group: "apps", Version: "v1", Resource: "deployments"}, - reflect.TypeOf(appsv1.DaemonSet{}): {Group: "apps", Version: "v1", Resource: "daemonsets"}, - reflect.TypeOf(appsv1.StatefulSet{}): {Group: "apps", Version: "v1", Resource: "statefulsets"}, - reflect.TypeOf(appsv1.ReplicaSet{}): {Group: "apps", Version: "v1", Resource: "replicasets"}, - reflect.TypeOf(stv1.StorageClass{}): {Group: "storage.k8s.io", Version: "v1", Resource: "storageclasses"}, - reflect.TypeOf(batchv1.Job{}): {Group: "batch", Version: "v1", Resource: "jobs"}, - reflect.TypeOf(policyv1.PodDisruptionBudget{}): {Group: "policy", Version: "v1", Resource: "poddisruptionbudgets"}, - reflect.TypeOf(corev1.ResourceQuota{}): {Version: "v1", Resource: "resourcequotas"}, + reflect.TypeFor[corev1.Namespace](): {Version: "v1", Resource: "namespaces"}, + reflect.TypeFor[corev1.Node](): {Version: "v1", Resource: "nodes"}, + reflect.TypeFor[corev1.Pod](): {Version: "v1", Resource: "pods"}, + reflect.TypeFor[corev1.Service](): {Version: "v1", Resource: "services"}, + reflect.TypeFor[corev1.PersistentVolume](): {Version: "v1", Resource: "persistentvolumes"}, + reflect.TypeFor[corev1.PersistentVolumeClaim](): {Version: "v1", Resource: "persistentvolumeclaims"}, + reflect.TypeFor[corev1.ReplicationController](): {Version: "v1", Resource: "replicationcontrollers"}, + reflect.TypeFor[appsv1.Deployment](): {Group: "apps", Version: "v1", Resource: "deployments"}, + reflect.TypeFor[appsv1.DaemonSet](): {Group: "apps", Version: "v1", Resource: "daemonsets"}, + reflect.TypeFor[appsv1.StatefulSet](): {Group: "apps", Version: "v1", Resource: "statefulsets"}, + reflect.TypeFor[appsv1.ReplicaSet](): {Group: "apps", Version: "v1", Resource: "replicasets"}, + reflect.TypeFor[stv1.StorageClass](): {Group: "storage.k8s.io", Version: "v1", Resource: "storageclasses"}, + reflect.TypeFor[batchv1.Job](): {Group: "batch", Version: "v1", Resource: "jobs"}, + reflect.TypeFor[policyv1.PodDisruptionBudget](): {Group: "policy", Version: "v1", Resource: "poddisruptionbudgets"}, + reflect.TypeFor[corev1.ResourceQuota](): {Version: "v1", Resource: "resourcequotas"}, } // fields to trim on specific resources if parseMetricsData is enabled gvkToSanitizePaths = map[schema.GroupVersionKind][]string{ @@ -161,7 +161,7 @@ func NewDynamicClusterCache( cache.shortLivedPods = []*corev1.Pod{} // add delete event on pods informer to track short-lived pods - _, eventErr := cache.ForResource(cacheResourceMap[reflect.TypeOf(corev1.Pod{})]).Informer(). + _, eventErr := cache.ForResource(cacheResourceMap[reflect.TypeFor[corev1.Pod]()]).Informer(). AddEventHandler(cache2.ResourceEventHandlerFuncs{ DeleteFunc: cache.captureShortLivedPodFunc(), }) @@ -172,8 +172,8 @@ func NewDynamicClusterCache( return &cache, nil } -func (dcc *DynamicClusterCache) captureShortLivedPodFunc() func(pod interface{}) { - return func(pod interface{}) { +func (dcc *DynamicClusterCache) captureShortLivedPodFunc() func(pod any) { + return func(pod any) { unstructuredPod, ok := pod.(*unstructured.Unstructured) if !ok { log.Warnf("failed to cast interface to unstructured, not capturing delete event") @@ -201,8 +201,8 @@ func (dcc *DynamicClusterCache) addShortLivedPod(pod *corev1.Pod) { // GetTransformFunc returns the correct transform to apply based on parseMetricsData flag // when enabled, sensitive information from k8s resources will be stripped -func GetTransformFunc(parseMetricsData bool) func(resource interface{}) (interface{}, error) { - return func(resource interface{}) (interface{}, error) { +func GetTransformFunc(parseMetricsData bool) func(resource any) (any, error) { + return func(resource any) (any, error) { var casted *unstructured.Unstructured var ok bool if casted, ok = resource.(*unstructured.Unstructured); !ok { @@ -264,19 +264,19 @@ func cleanContainers(resource *unstructured.Unstructured, gvk schema.GroupVersio log.Warnf("an error occurred getting resources containers %v", err) continue } - containers, ok := containersUnstructured.([]interface{}) + containers, ok := containersUnstructured.([]any) if !ok { log.Warnf("containers field is not a list. Not cleaning resource") continue } - for i := 0; i < len(containers); i++ { + for i := range containers { if parseMetricsData { for _, pathToContainer := range gvkToSanitizePaths[containerGVK] { - unstructured.RemoveNestedField(containers[i].(map[string]interface{}), strings.Split(pathToContainer, ".")...) + unstructured.RemoveNestedField(containers[i].(map[string]any), strings.Split(pathToContainer, ".")...) } } for _, pathToContainer := range gvkToTrimPaths[containerGVK] { - unstructured.RemoveNestedField(containers[i].(map[string]interface{}), strings.Split(pathToContainer, ".")...) + unstructured.RemoveNestedField(containers[i].(map[string]any), strings.Split(pathToContainer, ".")...) } } } diff --git a/pkg/env/unifiedagentenv.go b/pkg/env/unifiedagentenv.go index 7f164175..75a972f7 100644 --- a/pkg/env/unifiedagentenv.go +++ b/pkg/env/unifiedagentenv.go @@ -175,8 +175,8 @@ func GetSanitizeData() bool { // getValueWithPotentialPrefixOrDefault attempts to read the environment variable raw and then with the specified prefix, // converting it to the relevant type if found. Necessary that it doesn't default immediately. -func getValueWithPotentialPrefixOrDefault[T any](envVariable string, prefix string, defaultValue T, convert func(interface{}) T) T { - var envValue interface{} +func getValueWithPotentialPrefixOrDefault[T any](envVariable string, prefix string, defaultValue T, convert func(any) T) T { + var envValue any // Attempt without prefix first envValue = viper.Get(envVariable) diff --git a/pkg/nodes/request.go b/pkg/nodes/request.go index 0c346468..f07faeeb 100644 --- a/pkg/nodes/request.go +++ b/pkg/nodes/request.go @@ -16,7 +16,7 @@ import ( func (c *Client) AttemptEndPoint(method string, URL string, bearerToken string) ([]byte, error) { attempts := c.retries + 1 - for i := uint(0); i < attempts; i++ { + for i := range attempts { if i > 0 { time.Sleep(time.Duration(int64(math.Pow(2, float64(i)))) * time.Second) } From f14d6087f6b080aa0903556b33283f4689c031c9 Mon Sep 17 00:00:00 2001 From: thomasvn Date: Tue, 12 May 2026 22:27:50 -0700 Subject: [PATCH 2/3] Add `go fix` to CI --- .github/workflows/pr.yaml | 32 ++++++++++++++++++++------------ Makefile | 9 ++++++++- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 7e103b99..fc0afab8 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -2,7 +2,7 @@ name: Unit Tests and Build Docker Image on: schedule: - - cron: '0 14 * * *' + - cron: "0 14 * * *" pull_request: branches: - develop @@ -12,7 +12,7 @@ on: - develop merge_group: - types: [checks_requested] + types: [checks_requested] jobs: build-and-test: @@ -21,13 +21,15 @@ jobs: contents: read packages: write outputs: - image-tag: ${{ steps.meta.outcome == 'success' && fromJSON(steps.meta.outputs.json).tags[0] || '' }} + image-tag: + ${{ steps.meta.outcome == 'success' && + fromJSON(steps.meta.outputs.json).tags[0] || '' }} steps: - name: Install Go uses: actions/setup-go@v5 with: - go-version: 'stable' + go-version: "stable" - uses: actions/checkout@v4 with: @@ -43,7 +45,8 @@ jobs: # The directory is important. We cover both OC and KCM with the # ./kubecost/**/go.sum hash. Critically important because of the # "replace" in KCM's go.mod - key: ${{ runner.os }}-go-${{ hashFiles('./ibm-finops-agent/**/go.sum') }} + key: + ${{ runner.os }}-go-${{ hashFiles('./ibm-finops-agent/**/go.sum') }} - name: Test working-directory: ./ibm-finops-agent @@ -55,6 +58,11 @@ jobs: run: | make ci-lint + - name: Check go fix + working-directory: ./ibm-finops-agent + run: | + make go-fix-check + - name: get-pr-info shell: bash env: @@ -71,10 +79,10 @@ jobs: name: code-coverage retention-days: 5 path: | - coverage.out - pr_num.txt - base.txt - head.txt + coverage.out + pr_num.txt + base.txt + head.txt - name: Build Go binary working-directory: ./ibm-finops-agent @@ -127,13 +135,13 @@ jobs: strategy: fail-fast: false matrix: - k8s-version: ['v1.34.0', 'v1.33.0', 'v1.32.0', 'v1.31.0'] - + k8s-version: ["v1.34.0", "v1.33.0", "v1.32.0", "v1.31.0"] + steps: - name: Install Go uses: actions/setup-go@v5 with: - go-version: 'stable' + go-version: "stable" - uses: actions/checkout@v4 with: diff --git a/Makefile b/Makefile index 2c0618db..af86f21f 100644 --- a/Makefile +++ b/Makefile @@ -21,10 +21,17 @@ envtest: $(ENVTEST) ## Download envtest-setup locally if necessary. $(ENVTEST): $(LOCALBIN) @test -s $(LOCALBIN)/setup-envtest || GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest -ci-lint: +ci-lint: go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.2.1 golangci-lint run +# go-fix-check runs the Go fix tool to update deprecated or outdated API usage +# to current equivalents (e.g. old error patterns, renamed stdlib identifiers). +# -diff prints a unified diff instead of rewriting files, and exits non-zero +# if the diff is non-empty, which causes CI to fail. +go-fix-check: + go fix -diff ./... + # $(call TEST_KUBERNETES, image_tag, prefix, git_commit) define TEST_KUBERNETES KUBERNETES_VERSION=$(1) IMAGE=$(IMAGE_TAG) TEMP_DIR=$(TEMP_DIR) e2e/e2e.sh; \ From b413bd5476a0aa92c97f42e302e88587b0b1521c Mon Sep 17 00:00:00 2001 From: thomasvn Date: Wed, 5 Aug 2026 10:25:06 -0700 Subject: [PATCH 3/3] Run `go fix` again --- cldy/clients.go | 2 +- cldy/clients_test.go | 10 +++++----- pkg/cluster/dynamic.go | 32 ++++++++++++++++---------------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/cldy/clients.go b/cldy/clients.go index 587b4b9a..727a1ed2 100644 --- a/cldy/clients.go +++ b/cldy/clients.go @@ -356,7 +356,7 @@ func (s *ApptioServiceImpl) getUploadURL(payload UploadPayload) (uploadURL strin agentVersion = "0.0.0" } - body, err := json.Marshal(map[string]interface{}{ + body, err := json.Marshal(map[string]any{ "clusterUID": payload.ClusterUID, "fileName": payload.FileName, "agentVersion": agentVersion, diff --git a/cldy/clients_test.go b/cldy/clients_test.go index 32c89de4..6987d282 100644 --- a/cldy/clients_test.go +++ b/cldy/clients_test.go @@ -25,7 +25,7 @@ var _ = Describe("Client Proxy", func() { It("should not be used when Proxy URL is not set", func() { config := cldy.ApptioConfig{ - ProxyURL: &url.URL{}, + ProxyURL: &url.URL{}, } proxyFunc := cldy.BuildProxyFunc(config) @@ -42,7 +42,7 @@ var _ = Describe("Client Proxy", func() { }) It("should be used when Proxy URL is set", func() { config := cldy.ApptioConfig{ - ProxyURL: proxyURL, + ProxyURL: proxyURL, } proxyFunc := cldy.BuildProxyFunc(config) @@ -235,11 +235,11 @@ var _ = Describe("ApptioService agent version sanitization", func() { return resp, nil case strings.Contains(r.URL.Path, "clusters/upload"): // getUploadURL: capture agentVersion from body - var body map[string]interface{} + var body map[string]any Expect(json.NewDecoder(r.Body).Decode(&body)).To(Succeed()) capturedVersion = body["agentVersion"].(string) - resp := map[string]interface{}{ - "result": map[string]interface{}{ + resp := map[string]any{ + "result": map[string]any{ "location": "https://s3.example.com/upload", "requestId": "req-123", }, diff --git a/pkg/cluster/dynamic.go b/pkg/cluster/dynamic.go index d434a3a7..4da4528f 100644 --- a/pkg/cluster/dynamic.go +++ b/pkg/cluster/dynamic.go @@ -43,22 +43,22 @@ func LoadInformerConfig() InformerConfig { var ( cacheResourceMap = map[reflect.Type]schema.GroupVersionResource{ - reflect.TypeOf(corev1.Namespace{}): {Version: "v1", Resource: "namespaces"}, - reflect.TypeOf(corev1.Node{}): {Version: "v1", Resource: "nodes"}, - reflect.TypeOf(corev1.Pod{}): {Version: "v1", Resource: "pods"}, - reflect.TypeOf(corev1.Service{}): {Version: "v1", Resource: "services"}, - reflect.TypeOf(corev1.PersistentVolume{}): {Version: "v1", Resource: "persistentvolumes"}, - reflect.TypeOf(corev1.PersistentVolumeClaim{}): {Version: "v1", Resource: "persistentvolumeclaims"}, - reflect.TypeOf(corev1.ReplicationController{}): {Version: "v1", Resource: "replicationcontrollers"}, - reflect.TypeOf(appsv1.Deployment{}): {Group: "apps", Version: "v1", Resource: "deployments"}, - reflect.TypeOf(appsv1.DaemonSet{}): {Group: "apps", Version: "v1", Resource: "daemonsets"}, - reflect.TypeOf(appsv1.StatefulSet{}): {Group: "apps", Version: "v1", Resource: "statefulsets"}, - reflect.TypeOf(appsv1.ReplicaSet{}): {Group: "apps", Version: "v1", Resource: "replicasets"}, - reflect.TypeOf(stv1.StorageClass{}): {Group: "storage.k8s.io", Version: "v1", Resource: "storageclasses"}, - reflect.TypeOf(batchv1.Job{}): {Group: "batch", Version: "v1", Resource: "jobs"}, - reflect.TypeOf(batchv1.CronJob{}): {Group: "batch", Version: "v1", Resource: "cronjobs"}, - reflect.TypeOf(policyv1.PodDisruptionBudget{}): {Group: "policy", Version: "v1", Resource: "poddisruptionbudgets"}, - reflect.TypeOf(corev1.ResourceQuota{}): {Version: "v1", Resource: "resourcequotas"}, + reflect.TypeFor[corev1.Namespace](): {Version: "v1", Resource: "namespaces"}, + reflect.TypeFor[corev1.Node](): {Version: "v1", Resource: "nodes"}, + reflect.TypeFor[corev1.Pod](): {Version: "v1", Resource: "pods"}, + reflect.TypeFor[corev1.Service](): {Version: "v1", Resource: "services"}, + reflect.TypeFor[corev1.PersistentVolume](): {Version: "v1", Resource: "persistentvolumes"}, + reflect.TypeFor[corev1.PersistentVolumeClaim](): {Version: "v1", Resource: "persistentvolumeclaims"}, + reflect.TypeFor[corev1.ReplicationController](): {Version: "v1", Resource: "replicationcontrollers"}, + reflect.TypeFor[appsv1.Deployment](): {Group: "apps", Version: "v1", Resource: "deployments"}, + reflect.TypeFor[appsv1.DaemonSet](): {Group: "apps", Version: "v1", Resource: "daemonsets"}, + reflect.TypeFor[appsv1.StatefulSet](): {Group: "apps", Version: "v1", Resource: "statefulsets"}, + reflect.TypeFor[appsv1.ReplicaSet](): {Group: "apps", Version: "v1", Resource: "replicasets"}, + reflect.TypeFor[stv1.StorageClass](): {Group: "storage.k8s.io", Version: "v1", Resource: "storageclasses"}, + reflect.TypeFor[batchv1.Job](): {Group: "batch", Version: "v1", Resource: "jobs"}, + reflect.TypeFor[batchv1.CronJob](): {Group: "batch", Version: "v1", Resource: "cronjobs"}, + reflect.TypeFor[policyv1.PodDisruptionBudget](): {Group: "policy", Version: "v1", Resource: "poddisruptionbudgets"}, + reflect.TypeFor[corev1.ResourceQuota](): {Version: "v1", Resource: "resourcequotas"}, } // fields to trim on specific resources if parseMetricsData is enabled gvkToSanitizePaths = map[schema.GroupVersionKind][]string{