Skip to content

Commit

Permalink
feat: add annotation to select container for version extraction (#2471)
Browse files Browse the repository at this point in the history
Signed-off-by: Norman <[email protected]>
  • Loading branch information
norman-zon authored Nov 15, 2023
1 parent 14dcd27 commit d093860
Show file tree
Hide file tree
Showing 7 changed files with 397 additions and 36 deletions.
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) {
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 {
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 {
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)
}

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
17 changes: 15 additions & 2 deletions lifecycle-operator/webhooks/pod_mutator/handlers/pod_annotation.go
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 {
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

0 comments on commit d093860

Please sign in to comment.