Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add annotation to select container for version extraction #2471

Merged
merged 5 commits into from
Nov 15, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const CreateAppEvalSpanName = "create_%s_app_evaluation"
const CreateWorkloadEvalSpanName = "create_%s_deployment_evaluation"
const AppTypeAnnotation = "keptn.sh/app-type"
const KeptnGate = "keptn-prechecks-gate"
const ContainerNameAnnotation = "keptn.sh/container"

const MinKeptnNameLen = 80
const MaxK8sObjectLength = 253
Expand Down
32 changes: 26 additions & 6 deletions lifecycle-operator/webhooks/pod_mutator/handlers/objectmeta.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,24 +94,44 @@ func initEmptyAnnotations(meta *metav1.ObjectMeta, size int) {
}
}

func calculateVersion(pod *corev1.Pod) string {
func getImageVersion(image string) (string, error) {
norman-zon marked this conversation as resolved.
Show resolved Hide resolved
splitImage := strings.Split(image, ":")
lenImg := len(splitImage) - 1
if lenImg >= 1 && splitImage[lenImg] != "" && splitImage[lenImg] != "latest" {
return splitImage[lenImg], nil
}
return "", fmt.Errorf("Invalid image version")
}

func calculateVersion(pod *corev1.Pod, containerName string) (string, error) {
if len(pod.Spec.Containers) == 1 {
image := strings.Split(pod.Spec.Containers[0].Image, ":")
lenImg := len(image) - 1
if lenImg >= 1 && image[lenImg] != "" && image[lenImg] != "latest" {
return image[lenImg]
if containerName != "" && pod.Spec.Containers[0].Name != containerName {
norman-zon marked this conversation as resolved.
Show resolved Hide resolved
return "", fmt.Errorf("The container name '%s' specified in %s does not match the name of the container in the pod", containerName, apicommon.ContainerNameAnnotation)
}
return getImageVersion(pod.Spec.Containers[0].Image)
}

name := ""
containerFound := false
for _, item := range pod.Spec.Containers {
if item.Name == containerName {
odubajDT marked this conversation as resolved.
Show resolved Hide resolved
containerFound = true
version, err := getImageVersion(item.Image)
if err == nil {
return version, nil
}
}
name = name + item.Name + item.Image
for _, e := range item.Env {
name = name + e.Name + e.Value
}
}

if containerName != "" && !containerFound {
return "", fmt.Errorf("The container name '%s' specified in %s does not match any containers in the pod", containerName, apicommon.ContainerNameAnnotation)
}
odubajDT marked this conversation as resolved.
Show resolved Hide resolved

h := fnv.New32a()
h.Write([]byte(name))
return fmt.Sprint(h.Sum32())
return fmt.Sprint(h.Sum32()), nil
}
140 changes: 132 additions & 8 deletions lifecycle-operator/webhooks/pod_mutator/handlers/objectmeta_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,12 +187,59 @@ func TestGetLabelOrAnnotation(t *testing.T) {
}
}

func TestCalculateVersion(t *testing.T) {
func TestGetImageVersion(t *testing.T) {
tests := []struct {
name string
image string
want string
wantErr bool
}{
{
name: "Return image version when version is present",
image: "my-image:1.0.0",
want: "1.0.0",
wantErr: false,
},
{
name: "Return error when image version is not present",
image: "my-image",
want: "",
wantErr: true,
},
{
name: "Return error when image version is empty",
image: "my-image:",
want: "",
wantErr: true,
},
{
name: "Return error when image version is latest",
image: "my-image:latest",
want: "",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := getImageVersion(tt.image)
if (err != nil) != tt.wantErr {
t.Errorf("getImageVersion() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("getImageVersion() = %v, want %v", got, tt.want)
}
})
}
}

func Test_calculateVersion(t *testing.T) {
tests := []struct {
name string
pod *corev1.Pod
want string
name string
pod *corev1.Pod
containerName string
want string
wantErr bool
}{
{
name: "simple tag",
Expand All @@ -202,7 +249,9 @@ func TestCalculateVersion(t *testing.T) {
{Image: "ciao:1.0.0"},
},
}},
want: "1.0.0",
containerName: "",
want: "1.0.0",
wantErr: false,
}, {
name: "local registry",
pod: &corev1.Pod{
Expand All @@ -211,7 +260,28 @@ func TestCalculateVersion(t *testing.T) {
{Image: "localhost:5000/node-web-app:1.0.0"},
},
}},
want: "1.0.0",
containerName: "",
want: "1.0.0",
wantErr: false,
},
{
name: "single container with annotation mismatch",
pod: &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "pod-name",
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "container-name",
Image: "image:tag1",
},
},
},
},
containerName: "not-container-name",
want: "",
wantErr: true,
},
{
name: "multiple containers",
Expand All @@ -230,12 +300,66 @@ func TestCalculateVersion(t *testing.T) {
}},
},
}},
want: "1253120182", //the hash of ciaopeppetest12
containerName: "",
want: "1253120182", //the hash of ciaopeppetest12
wantErr: false,
},
{
name: "multiple containers with annotation",
pod: &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "pod-name",
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "container-name",
Image: "image:tag1",
},
{
Name: "container-name2",
Image: "image:tag2",
},
},
},
},
containerName: "container-name2",
want: "tag2",
wantErr: false,
},
{
name: "multiple containers with annotation mismatch",
pod: &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "pod-name",
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "container-name",
Image: "image:tag1",
},
{
Name: "container-name2",
Image: "image:tag2",
},
},
},
},
containerName: "not-container-name",
want: "",
wantErr: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := calculateVersion(tt.pod); got != tt.want {
got, err := calculateVersion(tt.pod, tt.containerName)
if (err != nil) != tt.wantErr {
t.Errorf("calculateVersion() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("calculateVersion() = %v, want %v", got, tt.want)
}
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package handlers

import (
"context"
"log"

argov1alpha1 "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1"
"github.com/go-logr/logr"
Expand Down Expand Up @@ -94,12 +95,18 @@ func copyResourceLabelsIfPresent(sourceResource *metav1.ObjectMeta, targetPod *c
postDeploymentChecks, _ = GetLabelOrAnnotation(sourceResource, apicommon.PostDeploymentTaskAnnotation, "")
preEvaluationChecks, _ = GetLabelOrAnnotation(sourceResource, apicommon.PreDeploymentEvaluationAnnotation, "")
postEvaluationChecks, _ = GetLabelOrAnnotation(sourceResource, apicommon.PostDeploymentEvaluationAnnotation, "")
containerName, _ := GetLabelOrAnnotation(sourceResource, apicommon.ContainerNameAnnotation, "")

if gotWorkloadName {
setMapKey(targetPod.Annotations, apicommon.WorkloadAnnotation, workloadName)

if !gotVersion {
setMapKey(targetPod.Annotations, apicommon.VersionAnnotation, calculateVersion(targetPod))
version, err := calculateVersion(targetPod, containerName)
if err != nil {
norman-zon marked this conversation as resolved.
Show resolved Hide resolved
log.Println(err)
return false
}
setMapKey(targetPod.Annotations, apicommon.VersionAnnotation, version)
} else {
setMapKey(targetPod.Annotations, apicommon.VersionAnnotation, version)
}
Expand All @@ -116,13 +123,19 @@ func copyResourceLabelsIfPresent(sourceResource *metav1.ObjectMeta, targetPod *c
}

func isPodAnnotated(pod *corev1.Pod) bool {
containerName, _ := GetLabelOrAnnotation(&pod.ObjectMeta, apicommon.ContainerNameAnnotation, "")
_, gotWorkloadAnnotation := GetLabelOrAnnotation(&pod.ObjectMeta, apicommon.WorkloadAnnotation, apicommon.K8sRecommendedWorkloadAnnotations)
_, gotVersionAnnotation := GetLabelOrAnnotation(&pod.ObjectMeta, apicommon.VersionAnnotation, apicommon.K8sRecommendedVersionAnnotations)

if gotWorkloadAnnotation {
if !gotVersionAnnotation {
initEmptyAnnotations(&pod.ObjectMeta, 1)
pod.Annotations[apicommon.VersionAnnotation] = calculateVersion(pod)
version, err := calculateVersion(pod, containerName)
if err != nil {
log.Println(err)
} else {
pod.Annotations[apicommon.VersionAnnotation] = version
}
}
return true
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,49 @@ func TestIsPodAnnotated(t *testing.T) {
},
want: false,
},
{
name: "Test return false when container annotation does not match container name",
args: args{
pod: &corev1.Pod{
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "container-name",
},
},
},
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
apicommon.ContainerNameAnnotation: "not-container-name",
},
},
},
},
want: false,
},
{
name: "Test return false when container annotation does not match any container name",
args: args{
pod: &corev1.Pod{
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "container-name",
},
{
Name: "container-name2",
},
},
},
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
apicommon.ContainerNameAnnotation: "not-container-name",
},
},
},
},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
Loading
Loading