From 66ac4aff4fb6125dd6f4c64e64e6bfe3e631b15a Mon Sep 17 00:00:00 2001 From: Kamil Kieliszczyk Date: Thu, 7 Feb 2019 15:50:53 +0100 Subject: [PATCH] Add kompose.image-pull-secret and kompose.image-pull-policy (#1091) * Add kompose.image-pull-secret * Add kompose.image-pull-secret tests * Add kompose.image-pull-policy * Add kompose.image-pull-policy tests * ignore .coverprofile * Fix typo --- .gitignore | 3 + docs/user-guide.md | 28 ++++ pkg/kobject/kobject.go | 2 + pkg/loader/compose/utils.go | 4 + pkg/loader/compose/v1v2.go | 4 + pkg/loader/compose/v3.go | 4 + pkg/transformer/kubernetes/k8sutils.go | 13 ++ pkg/transformer/kubernetes/kubernetes.go | 17 ++- pkg/transformer/kubernetes/kubernetes_test.go | 2 +- pkg/transformer/openshift/openshift.go | 2 +- script/test/cmd/tests.sh | 19 +++ .../v12-fail-image-pull-policy.yml | 6 + .../compose-files/v12-image-pull-policy.yml | 7 + .../compose-files/v3-image-pull-policy.yml | 14 ++ .../kubernetes-v12-image-pull-policy.json | 47 +++++++ .../kubernetes-v3-image-pull-policy.json | 127 ++++++++++++++++++ .../docker-compose-image-pull-secret.yml | 6 + .../kubernetes-image-pull-secret.json | 89 ++++++++++++ 18 files changed, 387 insertions(+), 7 deletions(-) create mode 100644 script/test/fixtures/image-pull-policy/compose-files/v12-fail-image-pull-policy.yml create mode 100644 script/test/fixtures/image-pull-policy/compose-files/v12-image-pull-policy.yml create mode 100644 script/test/fixtures/image-pull-policy/compose-files/v3-image-pull-policy.yml create mode 100644 script/test/fixtures/image-pull-policy/provider-files/kubernetes-v12-image-pull-policy.json create mode 100644 script/test/fixtures/image-pull-policy/provider-files/kubernetes-v3-image-pull-policy.json create mode 100644 script/test/fixtures/image-pull-secret/compose-files/docker-compose-image-pull-secret.yml create mode 100644 script/test/fixtures/image-pull-secret/provider-files/kubernetes-image-pull-secret.json diff --git a/.gitignore b/.gitignore index 791d6bed..8d119c79 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,9 @@ _testmain.go # Output of the go coverage tool, specifically when used with LiteIDE *.out +# Coveralls files +.coverprofile + # # VIM SPECIFIC # diff --git a/docs/user-guide.md b/docs/user-guide.md index f5908a2b..d829954c 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -302,6 +302,8 @@ The currently supported options are: | kompose.service.expose.tls-secret | secret name | | kompose.volume.size | kubernetes supported volume size | | kompose.controller.type | deployment / daemonset / replicationcontroller | +| kompose.image-pull-policy | kubernetes pods imagePullPolicy | +| kompose.image-pull-secret | kubernetes secret name for imagePullSecrets | **Note**: `kompose.service.type` label should be defined with `ports` only (except for headless service), otherwise `kompose` will fail. @@ -350,6 +352,19 @@ services: - "6379" ``` +- `kompose.image-pull-secret` defines a kubernetes secret name for imagePullSecrets podspec field. +This secret will be used for pulling private images. +For example: + +```yaml +version: '2' +services: + tm-service: + image: premium/private-image + labels: + kompose.image-pull-secret: "example-kubernetes-secret" +``` + - `kompose.volume.size` defines the requests storage's size in the PersistentVolumeClaim For example: @@ -399,6 +414,19 @@ db: Service `web` will be converted to `Deployment` as default, service `db` will be converted to `DaemonSet` because of `kompose.controller.type` label. +- `kompose.image-pull-policy` defines Kubernetes PodSpec imagePullPolicy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. + +For example: + +```yaml +version: '2' +services: + example-service: + image: example-image + labels: + kompose.image-pull-policy: "Never" +``` + ## Restart If you want to create normal pods without controller you can use `restart` construct of docker-compose to define that. Follow table below to see what happens on the `restart` value. diff --git a/pkg/kobject/kobject.go b/pkg/kobject/kobject.go index 540c8b2b..41344baa 100644 --- a/pkg/kobject/kobject.go +++ b/pkg/kobject/kobject.go @@ -85,6 +85,7 @@ type ServiceConfig struct { CapAdd []string `compose:"cap_add"` CapDrop []string `compose:"cap_drop"` Expose []string `compose:"expose"` + ImagePullPolicy string `compose:"kompose.image-pull-policy"` Pid string `compose:"pid"` Privileged bool `compose:"privileged"` Restart string `compose:"restart"` @@ -97,6 +98,7 @@ type ServiceConfig struct { ExposeService string `compose:"kompose.service.expose"` BuildLabels map[string]string `compose:"build-labels"` ExposeServiceTLS string `compose:"kompose.service.expose.tls-secret"` + ImagePullSecret string `compose:"kompose.image-pull-secret"` Stdin bool `compose:"stdin_open"` Tty bool `compose:"tty"` MemLimit yaml.MemStringorInt `compose:"mem_limit"` diff --git a/pkg/loader/compose/utils.go b/pkg/loader/compose/utils.go index 99254689..aaf90529 100644 --- a/pkg/loader/compose/utils.go +++ b/pkg/loader/compose/utils.go @@ -37,6 +37,10 @@ const ( LabelServiceExposeTLSSecret = "kompose.service.expose.tls-secret" // LabelControllerType defines the type of controller to be created LabelControllerType = "kompose.controller.type" + // LabelImagePullSecret defines a secret name for kubernetes ImagePullSecrets + LabelImagePullSecret = "kompose.image-pull-secret" + // LabelImagePullPolicy defines Kubernetes PodSpec imagePullPolicy. + LabelImagePullPolicy = "kompose.image-pull-policy" // ServiceTypeHeadless ... ServiceTypeHeadless = "Headless" diff --git a/pkg/loader/compose/v1v2.go b/pkg/loader/compose/v1v2.go index 9f15b8a7..730bb81c 100644 --- a/pkg/loader/compose/v1v2.go +++ b/pkg/loader/compose/v1v2.go @@ -246,6 +246,10 @@ func libComposeToKomposeMapping(composeObject *project.Project) (kobject.Kompose serviceConfig.ExposeService = strings.Trim(strings.ToLower(value), " ,") case LabelServiceExposeTLSSecret: serviceConfig.ExposeServiceTLS = value + case LabelImagePullSecret: + serviceConfig.ImagePullSecret = value + case LabelImagePullPolicy: + serviceConfig.ImagePullPolicy = value default: serviceConfig.Labels[key] = value } diff --git a/pkg/loader/compose/v3.go b/pkg/loader/compose/v3.go index 1e7083a3..7c513df7 100644 --- a/pkg/loader/compose/v3.go +++ b/pkg/loader/compose/v3.go @@ -401,6 +401,10 @@ func dockerComposeToKomposeMapping(composeObject *types.Config) (kobject.Kompose serviceConfig.ExposeService = strings.Trim(strings.ToLower(value), " ,") case LabelServiceExposeTLSSecret: serviceConfig.ExposeServiceTLS = value + case LabelImagePullSecret: + serviceConfig.ImagePullSecret = value + case LabelImagePullPolicy: + serviceConfig.ImagePullPolicy = value } } diff --git a/pkg/transformer/kubernetes/k8sutils.go b/pkg/transformer/kubernetes/k8sutils.go index 4223b70f..ae88f1de 100644 --- a/pkg/transformer/kubernetes/k8sutils.go +++ b/pkg/transformer/kubernetes/k8sutils.go @@ -503,6 +503,19 @@ func (k *Kubernetes) UpdateKubernetesObjects(name string, service kobject.Servic template.Spec.Containers[0].Ports = ports template.ObjectMeta.Labels = transformer.ConfigLabels(name) + // Configure the image pull policy + switch service.ImagePullPolicy { + case "": + case "Always": + template.Spec.Containers[0].ImagePullPolicy = api.PullAlways + case "Never": + template.Spec.Containers[0].ImagePullPolicy = api.PullNever + case "IfNotPresent": + template.Spec.Containers[0].ImagePullPolicy = api.PullIfNotPresent + default: + return errors.New("Unknown image-pull-policy " + service.ImagePullPolicy + " for service " + name) + } + // Configure the container restart policy. switch service.Restart { case "", "always", "any": diff --git a/pkg/transformer/kubernetes/kubernetes.go b/pkg/transformer/kubernetes/kubernetes.go index d4cc5eb6..76692498 100644 --- a/pkg/transformer/kubernetes/kubernetes.go +++ b/pkg/transformer/kubernetes/kubernetes.go @@ -116,7 +116,7 @@ func (k *Kubernetes) CheckUnsupportedKey(komposeObject *kobject.KomposeObject, u } // InitPodSpec creates the pod specification -func (k *Kubernetes) InitPodSpec(name string, image string) api.PodSpec { +func (k *Kubernetes) InitPodSpec(name string, image string, pullSecret string) api.PodSpec { pod := api.PodSpec{ Containers: []api.Container{ { @@ -125,6 +125,13 @@ func (k *Kubernetes) InitPodSpec(name string, image string) api.PodSpec { }, }, } + if pullSecret != "" { + pod.ImagePullSecrets = []api.LocalObjectReference{ + { + Name: pullSecret, + }, + } + } return pod } @@ -191,7 +198,7 @@ func (k *Kubernetes) InitRC(name string, service kobject.ServiceConfig, replicas ObjectMeta: api.ObjectMeta{ Labels: transformer.ConfigLabels(name), }, - Spec: k.InitPodSpec(name, service.Image), + Spec: k.InitPodSpec(name, service.Image, service.ImagePullSecret), }, }, } @@ -281,7 +288,7 @@ func (k *Kubernetes) InitD(name string, service kobject.ServiceConfig, replicas if len(service.Configs) > 0 { podSpec = k.InitPodSpecWithConfigMap(name, service.Image, service) } else { - podSpec = k.InitPodSpec(name, service.Image) + podSpec = k.InitPodSpec(name, service.Image, service.ImagePullSecret) } dc := &extensions.Deployment{ @@ -316,7 +323,7 @@ func (k *Kubernetes) InitDS(name string, service kobject.ServiceConfig) *extensi }, Spec: extensions.DaemonSetSpec{ Template: api.PodTemplateSpec{ - Spec: k.InitPodSpec(name, service.Image), + Spec: k.InitPodSpec(name, service.Image, service.ImagePullSecret), }, }, } @@ -807,7 +814,7 @@ func (k *Kubernetes) InitPod(name string, service kobject.ServiceConfig) *api.Po Name: name, Labels: transformer.ConfigLabels(name), }, - Spec: k.InitPodSpec(name, service.Image), + Spec: k.InitPodSpec(name, service.Image, service.ImagePullSecret), } return &pod } diff --git a/pkg/transformer/kubernetes/kubernetes_test.go b/pkg/transformer/kubernetes/kubernetes_test.go index 20a8b982..908871fb 100644 --- a/pkg/transformer/kubernetes/kubernetes_test.go +++ b/pkg/transformer/kubernetes/kubernetes_test.go @@ -489,7 +489,7 @@ func TestRestartOnFailure(t *testing.T) { func TestInitPodSpec(t *testing.T) { name := "foo" k := Kubernetes{} - result := k.InitPodSpec(name, newServiceConfig().Image) + result := k.InitPodSpec(name, newServiceConfig().Image, "") if result.Containers[0].Name != "foo" && result.Containers[0].Image != "image" { t.Fatalf("Pod object not found") } diff --git a/pkg/transformer/openshift/openshift.go b/pkg/transformer/openshift/openshift.go index c7fe4563..54ef4668 100644 --- a/pkg/transformer/openshift/openshift.go +++ b/pkg/transformer/openshift/openshift.go @@ -187,7 +187,7 @@ func (o *OpenShift) initDeploymentConfig(name string, service kobject.ServiceCon if len(service.Configs) > 0 { podSpec = o.InitPodSpecWithConfigMap(name, " ", service) } else { - podSpec = o.InitPodSpec(name, " ") + podSpec = o.InitPodSpec(name, " ", "") } dc := &deployapi.DeploymentConfig{ diff --git a/script/test/cmd/tests.sh b/script/test/cmd/tests.sh index cf0d870e..37ae49d6 100755 --- a/script/test/cmd/tests.sh +++ b/script/test/cmd/tests.sh @@ -415,6 +415,25 @@ cmd="kompose --provider openshift -f $KOMPOSE_ROOT/script/test/fixtures/expose-s sed -e "s;%VERSION%;$version;g" -e "s;%CMD%;$cmd;g" $KOMPOSE_ROOT/script/test/fixtures/expose-service/provider-files/openshift-expose-hostname-multiple-ports.json > /tmp/output-os.json convert::expect_success "kompose --provider openshift -f $KOMPOSE_ROOT/script/test/fixtures/expose-service/compose-files/docker-compose-expose-hostname-multiple-ports.yml convert --stdout -j" "/tmp/output-os.json" +# Test related to kompose.image-pull-secret label in docker compose file to ensure imagePullSecrets are populated properly. +# Kubernetes Test +cmd="kompose -f $KOMPOSE_ROOT/script/test/fixtures/image-pull-secret/compose-files/docker-compose-image-pull-secret.yml convert --stdout -j" +sed -e "s;%VERSION%;$version;g" -e "s;%CMD%;$cmd;g" $KOMPOSE_ROOT/script/test/fixtures/image-pull-secret/provider-files/kubernetes-image-pull-secret.json > /tmp/output-k8s.json +convert::expect_success "$cmd" "/tmp/output-k8s.json" + +# Test related to kompose.image-pull-secrets label in docker compose file. +# Kubernetes Tests +# when kompose.image-pull-secrets is invalid +convert::expect_failure "kompose -f $KOMPOSE_ROOT/script/test/fixtures/image-pull-policy/compose-files/v12-fail-image-pull-policy.yml convert --stdout" +# when kompose.image-pull-secrets in v1 and 2 +cmd="kompose -f $KOMPOSE_ROOT/script/test/fixtures/image-pull-policy/compose-files/v12-image-pull-policy.yml convert --stdout -j" +sed -e "s;%VERSION%;$version;g" -e "s;%CMD%;$cmd;g" $KOMPOSE_ROOT/script/test/fixtures/image-pull-policy/provider-files/kubernetes-v12-image-pull-policy.json > /tmp/output-k8s.json +convert::expect_success "$cmd" "/tmp/output-k8s.json" +# when kompose.image-pull-secrets in v3 +cmd="kompose -f $KOMPOSE_ROOT/script/test/fixtures/image-pull-policy/compose-files/v3-image-pull-policy.yml convert --stdout -j" +sed -e "s;%VERSION%;$version;g" -e "s;%CMD%;$cmd;g" $KOMPOSE_ROOT/script/test/fixtures/image-pull-policy/provider-files/kubernetes-v3-image-pull-policy.json > /tmp/output-k8s.json +convert::expect_success "$cmd" "/tmp/output-k8s.json" + # Test the change in the service name # Kubernetes Test diff --git a/script/test/fixtures/image-pull-policy/compose-files/v12-fail-image-pull-policy.yml b/script/test/fixtures/image-pull-policy/compose-files/v12-fail-image-pull-policy.yml new file mode 100644 index 00000000..4e43acd3 --- /dev/null +++ b/script/test/fixtures/image-pull-policy/compose-files/v12-fail-image-pull-policy.yml @@ -0,0 +1,6 @@ +version: "2" +services: + nginx0: + image: nginx + labels: + kompose.image-pull-policy: "Fail" diff --git a/script/test/fixtures/image-pull-policy/compose-files/v12-image-pull-policy.yml b/script/test/fixtures/image-pull-policy/compose-files/v12-image-pull-policy.yml new file mode 100644 index 00000000..90cc80eb --- /dev/null +++ b/script/test/fixtures/image-pull-policy/compose-files/v12-image-pull-policy.yml @@ -0,0 +1,7 @@ +version: "2" +services: + nginx0: + image: nginx + labels: + kompose.image-pull-policy: "Always" + diff --git a/script/test/fixtures/image-pull-policy/compose-files/v3-image-pull-policy.yml b/script/test/fixtures/image-pull-policy/compose-files/v3-image-pull-policy.yml new file mode 100644 index 00000000..cd4ef0d6 --- /dev/null +++ b/script/test/fixtures/image-pull-policy/compose-files/v3-image-pull-policy.yml @@ -0,0 +1,14 @@ +version: "3" +services: + nginx0: + image: nginx + labels: + kompose.image-pull-policy: "Always" + nginx1: + image: nginx + labels: + kompose.image-pull-policy: "IfNotPresent" + nginx2: + image: nginx + labels: + kompose.image-pull-policy: "Never" diff --git a/script/test/fixtures/image-pull-policy/provider-files/kubernetes-v12-image-pull-policy.json b/script/test/fixtures/image-pull-policy/provider-files/kubernetes-v12-image-pull-policy.json new file mode 100644 index 00000000..b55529dc --- /dev/null +++ b/script/test/fixtures/image-pull-policy/provider-files/kubernetes-v12-image-pull-policy.json @@ -0,0 +1,47 @@ +{ + "kind": "List", + "apiVersion": "v1", + "metadata": {}, + "items": [ + { + "kind": "Deployment", + "apiVersion": "extensions/v1beta1", + "metadata": { + "name": "nginx0", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "nginx0" + }, + "annotations": { + "kompose.cmd": "%CMD%", + "kompose.image-pull-policy": "Always", + "kompose.version": "%VERSION%" + } + }, + "spec": { + "replicas": 1, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "io.kompose.service": "nginx0" + } + }, + "spec": { + "containers": [ + { + "name": "nginx0", + "image": "nginx", + "resources": {}, + "imagePullPolicy": "Always" + } + ], + "restartPolicy": "Always" + } + }, + "strategy": {} + }, + "status": {} + } + ] +} \ No newline at end of file diff --git a/script/test/fixtures/image-pull-policy/provider-files/kubernetes-v3-image-pull-policy.json b/script/test/fixtures/image-pull-policy/provider-files/kubernetes-v3-image-pull-policy.json new file mode 100644 index 00000000..74da0a42 --- /dev/null +++ b/script/test/fixtures/image-pull-policy/provider-files/kubernetes-v3-image-pull-policy.json @@ -0,0 +1,127 @@ +{ + "kind": "List", + "apiVersion": "v1", + "metadata": {}, + "items": [ + { + "kind": "Deployment", + "apiVersion": "extensions/v1beta1", + "metadata": { + "name": "nginx0", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "nginx0" + }, + "annotations": { + "kompose.cmd": "%CMD%", + "kompose.image-pull-policy": "Always", + "kompose.version": "%VERSION%" + } + }, + "spec": { + "replicas": 1, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "io.kompose.service": "nginx0" + } + }, + "spec": { + "containers": [ + { + "name": "nginx0", + "image": "nginx", + "resources": {}, + "imagePullPolicy": "Always" + } + ], + "restartPolicy": "Always" + } + }, + "strategy": {} + }, + "status": {} + }, + { + "kind": "Deployment", + "apiVersion": "extensions/v1beta1", + "metadata": { + "name": "nginx1", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "nginx1" + }, + "annotations": { + "kompose.cmd": "%CMD%", + "kompose.image-pull-policy": "IfNotPresent", + "kompose.version": "%VERSION%" + } + }, + "spec": { + "replicas": 1, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "io.kompose.service": "nginx1" + } + }, + "spec": { + "containers": [ + { + "name": "nginx1", + "image": "nginx", + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "restartPolicy": "Always" + } + }, + "strategy": {} + }, + "status": {} + }, + { + "kind": "Deployment", + "apiVersion": "extensions/v1beta1", + "metadata": { + "name": "nginx2", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "nginx2" + }, + "annotations": { + "kompose.cmd": "%CMD%", + "kompose.image-pull-policy": "Never", + "kompose.version": "%VERSION%" + } + }, + "spec": { + "replicas": 1, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "io.kompose.service": "nginx2" + } + }, + "spec": { + "containers": [ + { + "name": "nginx2", + "image": "nginx", + "resources": {}, + "imagePullPolicy": "Never" + } + ], + "restartPolicy": "Always" + } + }, + "strategy": {} + }, + "status": {} + } + ] +} \ No newline at end of file diff --git a/script/test/fixtures/image-pull-secret/compose-files/docker-compose-image-pull-secret.yml b/script/test/fixtures/image-pull-secret/compose-files/docker-compose-image-pull-secret.yml new file mode 100644 index 00000000..6fc85da3 --- /dev/null +++ b/script/test/fixtures/image-pull-secret/compose-files/docker-compose-image-pull-secret.yml @@ -0,0 +1,6 @@ +tm-image-service: + image: premium/private-image + labels: + kompose.image-pull-secret: "sample-k8s-secret-name" +open-image-service: + image: nginx-alpine diff --git a/script/test/fixtures/image-pull-secret/provider-files/kubernetes-image-pull-secret.json b/script/test/fixtures/image-pull-secret/provider-files/kubernetes-image-pull-secret.json new file mode 100644 index 00000000..433756db --- /dev/null +++ b/script/test/fixtures/image-pull-secret/provider-files/kubernetes-image-pull-secret.json @@ -0,0 +1,89 @@ +{ + "kind": "List", + "apiVersion": "v1", + "metadata": {}, + "items": [ + { + "kind": "Deployment", + "apiVersion": "extensions/v1beta1", + "metadata": { + "name": "open-image-service", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "open-image-service" + }, + "annotations": { + "kompose.cmd": "%CMD%", + "kompose.version": "%VERSION%" + } + }, + "spec": { + "replicas": 1, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "io.kompose.service": "open-image-service" + } + }, + "spec": { + "containers": [ + { + "name": "open-image-service", + "image": "nginx-alpine", + "resources": {} + } + ], + "restartPolicy": "Always" + } + }, + "strategy": {} + }, + "status": {} + }, + { + "kind": "Deployment", + "apiVersion": "extensions/v1beta1", + "metadata": { + "name": "tm-image-service", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "tm-image-service" + }, + "annotations": { + "kompose.cmd": "%CMD%", + "kompose.image-pull-secret": "sample-k8s-secret-name", + "kompose.version": "%VERSION%" + } + }, + "spec": { + "replicas": 1, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "io.kompose.service": "tm-image-service" + } + }, + "spec": { + "containers": [ + { + "name": "tm-image-service", + "image": "premium/private-image", + "resources": {} + } + ], + "restartPolicy": "Always", + "imagePullSecrets": [ + { + "name": "sample-k8s-secret-name" + } + ] + } + }, + "strategy": {} + }, + "status": {} + } + ] +}