Skip to content

Commit

Permalink
feat: ability to ignore images (#6)
Browse files Browse the repository at this point in the history
  • Loading branch information
AverageMarcus committed Aug 12, 2020
1 parent 85cf74e commit 543a25c
Show file tree
Hide file tree
Showing 5 changed files with 293 additions and 15 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ format:

.PHONY: run-tests # Runs all tests
run-tests:
@go test
@go test ./...

.PHONY: fetch-deps # Fetch all project dependencies
fetch-deps:
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

* Pull all images used by deployments in the cluster on all nodes
* Watch for new, changed or removed deployments and pre-fetch images on all nodes
* Ignore deployments with annotation `kube-image-prefetch/ignore: "true"`
* Ignore specific containers with annotation `kube-image-prefetch/ignore-containers: "container-name"`. Multiple containers within a pod can be specified as a comma separated list.

## Install

Expand Down
36 changes: 36 additions & 0 deletions internal/controller/images.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package controller

import (
"strings"

appsv1 "k8s.io/api/apps/v1"
)

func getImages(dp appsv1.Deployment) []string {
images := []string{}

ignoreDP := dp.ObjectMeta.Annotations["kube-image-prefetch/ignore"]
if ignoreDP == "true" {
return images
}

ignoreContainersStr := dp.ObjectMeta.Annotations["kube-image-prefetch/ignore-containers"]
ignoreContainers := strings.Split(ignoreContainersStr, ",")

for _, container := range append(dp.Spec.Template.Spec.InitContainers, dp.Spec.Template.Spec.Containers...) {
if !contains(ignoreContainers, container.Name) {
images = append(images, container.Image)
}
}

return images
}

func contains(arr []string, str string) bool {
for _, c := range arr {
if strings.TrimSpace(strings.ToLower(str)) == strings.TrimSpace(strings.ToLower(c)) {
return true
}
}
return false
}
245 changes: 245 additions & 0 deletions internal/controller/images_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
package controller

import (
"testing"
"reflect"

appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TestGetImages_SingleImage(t *testing.T) {
dp := appsv1.Deployment{
ObjectMeta: v1.ObjectMeta{
Name: "test-deployment",
},
Spec: appsv1.DeploymentSpec{
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
Containers: []corev1.Container{
corev1.Container{
Name: "container-1",
Image: "image:1",
},
},
},
},
},
}

expected := []string{"image:1"}
actual := getImages(dp)

if ! reflect.DeepEqual(expected, actual) {
t.Errorf("Unexpected images returned - %v", actual)
}
}

func TestGetImages_MultipleImage(t *testing.T) {
dp := appsv1.Deployment{
ObjectMeta: v1.ObjectMeta{
Name: "test-deployment",
},
Spec: appsv1.DeploymentSpec{
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
Containers: []corev1.Container{
corev1.Container{
Name: "container-1",
Image: "image:1",
},
corev1.Container{
Name: "container-2",
Image: "image:2",
},
},
},
},
},
}

expected := []string{"image:1","image:2"}
actual := getImages(dp)

if ! reflect.DeepEqual(expected, actual) {
t.Errorf("Unexpected images returned - %v", actual)
}
}

func TestGetImages_InitContainers(t *testing.T) {
dp := appsv1.Deployment{
ObjectMeta: v1.ObjectMeta{
Name: "test-deployment",
},
Spec: appsv1.DeploymentSpec{
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
InitContainers: []corev1.Container{
corev1.Container{
Name: "initcontainer-1",
Image: "initimage:1",
},
},
Containers: []corev1.Container{
corev1.Container{
Name: "container-1",
Image: "image:1",
},
},
},
},
},
}

expected := []string{"initimage:1","image:1"}
actual := getImages(dp)

if ! reflect.DeepEqual(expected, actual) {
t.Errorf("Unexpected images returned - %v", actual)
}
}

func TestGetImages_IgnoreDeployment(t *testing.T) {
dp := appsv1.Deployment{
ObjectMeta: v1.ObjectMeta{
Name: "test-deployment",
Annotations: map[string]string{
"kube-image-prefetch/ignore": "true",
},
},
Spec: appsv1.DeploymentSpec{
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
InitContainers: []corev1.Container{
corev1.Container{
Name: "initcontainer-1",
Image: "initimage:1",
},
},
Containers: []corev1.Container{
corev1.Container{
Name: "container-1",
Image: "image:1",
},
},
},
},
},
}

expected := []string{}
actual := getImages(dp)

if ! reflect.DeepEqual(expected, actual) {
t.Errorf("Unexpected images returned - %v", actual)
}
}

func TestGetImages_IgnoreContainer(t *testing.T) {
dp := appsv1.Deployment{
ObjectMeta: v1.ObjectMeta{
Name: "test-deployment",
Annotations: map[string]string{
"kube-image-prefetch/ignore-containers": "initcontainer-1",
},
},
Spec: appsv1.DeploymentSpec{
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
InitContainers: []corev1.Container{
corev1.Container{
Name: "initcontainer-1",
Image: "initimage:1",
},
},
Containers: []corev1.Container{
corev1.Container{
Name: "container-1",
Image: "image:1",
},
},
},
},
},
}

expected := []string{"image:1"}
actual := getImages(dp)

if ! reflect.DeepEqual(expected, actual) {
t.Errorf("Unexpected images returned - %v", actual)
}
}

func TestGetImages_IgnoreMultipleContainer(t *testing.T) {
dp := appsv1.Deployment{
ObjectMeta: v1.ObjectMeta{
Name: "test-deployment",
Annotations: map[string]string{
"kube-image-prefetch/ignore-containers": "initcontainer-1,container-1",
},
},
Spec: appsv1.DeploymentSpec{
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
InitContainers: []corev1.Container{
corev1.Container{
Name: "initcontainer-1",
Image: "initimage:1",
},
},
Containers: []corev1.Container{
corev1.Container{
Name: "container-1",
Image: "image:1",
},
},
},
},
},
}

expected := []string{}
actual := getImages(dp)

if ! reflect.DeepEqual(expected, actual) {
t.Errorf("Unexpected images returned - %v", actual)
}
}

func TestGetImages_IgnoreContainerSpaces(t *testing.T) {
dp := appsv1.Deployment{
ObjectMeta: v1.ObjectMeta{
Name: "test-deployment",
Annotations: map[string]string{
"kube-image-prefetch/ignore-containers": "initcontainer-1, container-1",
},
},
Spec: appsv1.DeploymentSpec{
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
InitContainers: []corev1.Container{
corev1.Container{
Name: "initcontainer-1",
Image: "initimage:1",
},
},
Containers: []corev1.Container{
corev1.Container{
Name: "container-1",
Image: "image:1",
},
},
},
},
},
}

expected := []string{}
actual := getImages(dp)

if ! reflect.DeepEqual(expected, actual) {
t.Errorf("Unexpected images returned - %v", actual)
}
}
23 changes: 9 additions & 14 deletions internal/controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,11 +124,16 @@ func (w *Worker) processItem(newEvent Event) error {
fallthrough
case updatedAction:
dp := obj.(*appsv1.Deployment)
w.imageChan <- Images{
ID: newEvent.Key,
Images: getImages(*dp),
PullSecrets: dp.Spec.Template.Spec.ImagePullSecrets,
images := getImages(*dp)
if len(images) > 0 {
w.imageChan <- Images{
ID: newEvent.Key,
Images: images,
PullSecrets: dp.Spec.Template.Spec.ImagePullSecrets,
}
return nil
}
fallthrough
case deletedAction:
w.imageChan <- Images{
ID: newEvent.Key,
Expand All @@ -138,13 +143,3 @@ func (w *Worker) processItem(newEvent Event) error {
}
return nil
}

func getImages(dp appsv1.Deployment) []string {
images := []string{}

for _, container := range append(dp.Spec.Template.Spec.InitContainers, dp.Spec.Template.Spec.Containers...) {
images = append(images, container.Image)
}

return images
}

0 comments on commit 543a25c

Please sign in to comment.