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
32 changes: 20 additions & 12 deletions .github/workflows/pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Unit Tests and Build Docker Image

on:
schedule:
- cron: '0 14 * * *'
- cron: "0 14 * * *"
pull_request:
branches:
- develop
Expand All @@ -12,7 +12,7 @@ on:
- develop

merge_group:
types: [checks_requested]
types: [checks_requested]

jobs:
build-and-test:
Expand All @@ -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@v6
with:
Expand All @@ -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
Expand All @@ -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:
Expand All @@ -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
Expand Down Expand Up @@ -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@v6
with:
Expand Down
9 changes: 8 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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; \
Expand Down
10 changes: 5 additions & 5 deletions cldy/clients.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -513,8 +513,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, "+
Expand All @@ -540,8 +540,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,
}

Expand Down
10 changes: 5 additions & 5 deletions cldy/clients_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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",
},
Expand Down
2 changes: 1 addition & 1 deletion kubecost/adapters/metricsquerier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
50 changes: 25 additions & 25 deletions pkg/cluster/dynamic.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down Expand Up @@ -162,7 +162,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(),
})
Expand All @@ -173,8 +173,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")
Expand Down Expand Up @@ -202,8 +202,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 {
Expand Down Expand Up @@ -265,19 +265,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, ".")...)
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/env/unifiedagentenv.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion pkg/nodes/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
Loading