diff --git a/cluster/kube/builder/deployment.go b/cluster/kube/builder/deployment.go index 3a820b199..77b0b1e06 100644 --- a/cluster/kube/builder/deployment.go +++ b/cluster/kube/builder/deployment.go @@ -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 { @@ -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) + kdeployment := &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: b.Name(), @@ -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, }, @@ -79,6 +84,7 @@ 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() @@ -86,9 +92,43 @@ func (b *deployment) Update(obj *appsv1.Deployment) (*appsv1.Deployment, error) 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", + } + + 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 +} diff --git a/cluster/kube/builder/deployment_test.go b/cluster/kube/builder/deployment_test.go index d7bfdfac3..367c4062a 100644 --- a/cluster/kube/builder/deployment_test.go +++ b/cluster/kube/builder/deployment_test.go @@ -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) + }) + } +} diff --git a/testdata/deployment/deployment-log-collector.yaml b/testdata/deployment/deployment-log-collector.yaml new file mode 100644 index 000000000..b063707b0 --- /dev/null +++ b/testdata/deployment/deployment-log-collector.yaml @@ -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