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
46 changes: 43 additions & 3 deletions cluster/kube/builder/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"strings"
)

type Deployment interface {
Expand Down Expand Up @@ -37,6 +38,10 @@ func (b *deployment) Create() (*appsv1.Deployment, error) { // nolint:golint,unp
maxSurge := intstr.FromInt32(0)
maxUnavailable := intstr.FromInt32(1)

container := b.container()
image := container.Image
automountServiceAccountToken := b.determineAutomountServiceAccountToken(image)

Comment on lines +41 to +44
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Don’t force AutomountServiceAccountToken=false for non-allowlisted images — preserve k8s default.

Setting this field to false flips Kubernetes’ default (nil → SA/pod default, commonly true) and may break workloads that expect a token. Recommend setting it only when you need to enable it (log-collector), and leaving it nil otherwise.

Apply:

@@
-	container := b.container()
-	image := container.Image
-	automountServiceAccountToken := b.determineAutomountServiceAccountToken(image)
+	container := b.container()
+	image := container.Image
+	automount := b.determineAutomountServiceAccountToken(image)
+	var automountPtr *bool
+	if automount {
+		v := true
+		automountPtr = &v
+	}
@@
-					AutomountServiceAccountToken: &automountServiceAccountToken,
+					AutomountServiceAccountToken: automountPtr,
 					Containers:                   []corev1.Container{container},
@@
-	image := container.Image
-	automountServiceAccountToken := b.determineAutomountServiceAccountToken(image)
-	uobj.Spec.Template.Spec.AutomountServiceAccountToken = &automountServiceAccountToken
+	image := container.Image
+	automount := b.determineAutomountServiceAccountToken(image)
+	var automountPtr *bool
+	if automount {
+		v := true
+		automountPtr = &v
+	}
+	uobj.Spec.Template.Spec.AutomountServiceAccountToken = automountPtr

Also applies to: 73-75, 99-102


🏁 Script executed:

#!/bin/bash
set -e

# Locate the determineAutomountServiceAccountToken definition
rg -nP --type=go '\bfunc\s+\(b \*Builder\)\s+determineAutomountServiceAccountToken' -C3 .

# Confirm its return type and usage sites in deployment.go
rg -nP --type=go 'determineAutomountServiceAccountToken' -C3 cluster/kube/builder/deployment.go

Length of output: 100


🏁 Script executed:

#!/bin/bash
set -e

# Search for the method across the entire repository without type filters
rg -n 'determineAutomountServiceAccountToken' -C3 .

# Search for any references to AutomountServiceAccountToken to find context
rg -n 'AutomountServiceAccountToken' -C3 .

# List all Go files in cluster/kube/builder to locate definitions
find cluster/kube/builder -maxdepth 1 -type f -name '*.go'

Length of output: 5822


Preserve k8s default AutomountServiceAccountToken

  • In cluster/kube/builder/deployment.go (lines 41–44, 73–75 & 99–102), only set .AutomountServiceAccountToken when determineAutomountServiceAccountToken(image) returns true (i.e. point to true); leave it nil otherwise so Kubernetes uses its default.
  • Update cluster/kube/builder/deployment_test.go (lines 143–145) to expect a nil pointer when automount should be disabled instead of always requiring a non-nil pointer and boolean comparison.
  • No change needed in statefulset.go – it intentionally disables automount for all StatefulSets.
🤖 Prompt for AI Agents
In cluster/kube/builder/deployment.go around lines 41–44, 73–75 and 99–102, the
code unconditionally sets PodSpec.AutomountServiceAccountToken to a pointer
value derived from determineAutomountServiceAccountToken(image); change this so
you only set the AutomountServiceAccountToken field when
determineAutomountServiceAccountToken(image) returns true (i.e., set to pointer
to true), and leave the field nil when it would be false so Kubernetes uses its
default. Also update cluster/kube/builder/deployment_test.go around lines
143–145 to expect a nil pointer when automount should be disabled instead of
asserting a non-nil boolean value.

kdeployment := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: b.Name(),
Expand Down Expand Up @@ -65,8 +70,8 @@ func (b *deployment) Create() (*appsv1.Deployment, error) { // nolint:golint,unp
SecurityContext: &corev1.PodSecurityContext{
RunAsNonRoot: &falseValue,
},
AutomountServiceAccountToken: &falseValue,
Containers: []corev1.Container{b.container()},
AutomountServiceAccountToken: &automountServiceAccountToken,
Containers: []corev1.Container{container},
ImagePullSecrets: b.secretsRefs,
Volumes: b.volumesObjs,
},
Expand All @@ -79,16 +84,51 @@ func (b *deployment) Create() (*appsv1.Deployment, error) { // nolint:golint,unp

func (b *deployment) Update(obj *appsv1.Deployment) (*appsv1.Deployment, error) { // nolint:golint,unparam
uobj := obj.DeepCopy()
container := b.container()

uobj.Labels = updateAkashLabels(obj.Labels, b.labels())
uobj.Spec.Selector.MatchLabels = b.selectorLabels()
uobj.Spec.Replicas = b.replicas()
uobj.Spec.Template.Labels = b.labels()
uobj.Spec.Template.Spec.Affinity = b.affinity()
uobj.Spec.Template.Spec.RuntimeClassName = b.runtimeClass()
uobj.Spec.Template.Spec.Containers = []corev1.Container{b.container()}
uobj.Spec.Template.Spec.Containers = []corev1.Container{container}
uobj.Spec.Template.Spec.ImagePullSecrets = b.secretsRefs
uobj.Spec.Template.Spec.Volumes = b.volumesObjs

image := container.Image
automountServiceAccountToken := b.determineAutomountServiceAccountToken(image)
uobj.Spec.Template.Spec.AutomountServiceAccountToken = &automountServiceAccountToken

return uobj, nil
}

func (b *deployment) determineAutomountServiceAccountToken(image string) bool {
automountImages := []string{
"ghcr.io/akash-network/log-collector",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be configurable as any change to the image location would require recompilation.

}

imageName := extractImageName(image)

for _, automountImage := range automountImages {
if strings.EqualFold(imageName, automountImage) {
return true
}
}

return false
}

func extractImageName(image string) string {
if idxAt := strings.LastIndex(image, "@"); idxAt != -1 {
return image[:idxAt]
}

if idx := strings.LastIndex(image, ":"); idx != -1 {
if lastSlash := strings.LastIndex(image, "/"); lastSlash == -1 || idx > lastSlash {
return image[:idx]
}
}

return image
}
128 changes: 128 additions & 0 deletions cluster/kube/builder/deployment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,131 @@ func TestDeploySetsEnvironmentVariables(t *testing.T) {
require.True(t, ok)
require.Equal(t, lid.Provider, value)
}

func TestDeploymentAutomountServiceAccountToken(t *testing.T) {
log := testutil.Logger(t)
const fakeHostname = "ahostname.dev"
settings := Settings{
ClusterPublicHostname: fakeHostname,
}
lid := testutil.LeaseID(t)

testCases := []struct {
name string
sdlFile string
expectedResult bool
}{
{
name: "should enable automount for log-collector image",
sdlFile: "../../../testdata/deployment/deployment-log-collector.yaml",
expectedResult: true,
},
{
name: "should disable automount for regular image",
sdlFile: "../../../testdata/deployment/deployment.yaml",
expectedResult: false,
},
}

for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
sdl, err := sdl.ReadFile(testCase.sdlFile)
require.NoError(t, err)

manifest, err := sdl.Manifest()
require.NoError(t, err)

schedulerParams := make([]*crd.SchedulerParams, len(manifest.GetGroups()[0].Services))

clusterManifest, err := crd.NewManifest("lease", lid, &manifest.GetGroups()[0], crd.ClusterSettings{SchedulerParams: schedulerParams})
require.NoError(t, err)

group, schedulerParams, err := clusterManifest.Spec.Group.FromCRD()
require.NoError(t, err)

clusterDeployment := &ClusterDeployment{
Lid: lid,
Group: &group,
Sparams: crd.ClusterSettings{SchedulerParams: schedulerParams},
}

workload, err := NewWorkloadBuilder(log, settings, clusterDeployment, clusterManifest, 0)
require.NoError(t, err)

deploymentBuilder := NewDeployment(workload)

require.NotNil(t, deploymentBuilder)

deploymentInstance := deploymentBuilder.(*deployment)

deployment, err := deploymentInstance.Create()
require.NoError(t, err)
require.NotNil(t, deployment)

automountValue := deployment.Spec.Template.Spec.AutomountServiceAccountToken
require.NotNil(t, automountValue)
require.Equal(t, testCase.expectedResult, *automountValue)
})
}
}

func TestExtractImageName(t *testing.T) {
testCases := []struct {
name string
image string
expected string
}{
{
name: "image with tag",
image: "ghcr.io/akash-network/log-collector:v1.2.3",
expected: "ghcr.io/akash-network/log-collector",
},
{
name: "image without tag",
image: "ghcr.io/akash-network/log-collector",
expected: "ghcr.io/akash-network/log-collector",
},
{
name: "image with latest tag",
image: "nginx:latest",
expected: "nginx",
},
{
name: "image with port and tag",
image: "registry.example.com:5000/myapp:v1.0",
expected: "registry.example.com:5000/myapp",
},
{
name: "simple image name",
image: "postgres",
expected: "postgres",
},
{
name: "image with digest",
image: "ghcr.io/akash-network/log-collector@sha256:abc123def456",
expected: "ghcr.io/akash-network/log-collector",
},
{
name: "image with tag and digest",
image: "ghcr.io/akash-network/log-collector:v1.2.3@sha256:abc123def456",
expected: "ghcr.io/akash-network/log-collector:v1.2.3",
},
{
name: "registry with port",
image: "registry.example.com:5000/myapp:latest",
expected: "registry.example.com:5000/myapp",
},
{
name: "registry with port no tag",
image: "registry.example.com:5000/myapp",
expected: "registry.example.com:5000/myapp",
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
result := extractImageName(tc.image)
require.Equal(t, tc.expected, result)
})
}
}
41 changes: 41 additions & 0 deletions testdata/deployment/deployment-log-collector.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
version: "2.0"

services:
log-collector:
image: ghcr.io/akash-network/log-collector:1.7.0
expose:
- port: 8080
to:
- global: true
accept:
- log-collector.localhost
env:
- PROVIDER=DATADOG
- POD_LABEL_SELECTOR="akash.network/manifest-service=target"
- DD_API_KEY=some-secret-value
- DD_SITE=datadoghq.eu

profiles:
compute:
log-collector:
resources:
cpu:
units: "0.01"
memory:
size: "128Mi"
storage:
size: "512Mi"

placement:
global:
pricing:
log-collector:
denom: uakt
amount: 30

deployment:
log-collector:
global:
profile: log-collector
count: 1
Loading