From fd09efed43d3fa23c8f04b64d2d54af63ea7913c Mon Sep 17 00:00:00 2001 From: Suraj Deshmukh Date: Fri, 7 Oct 2016 16:21:57 +0530 Subject: [PATCH] support for volumes_from docker-compose construct Now a user can provide volumes_from to share volumes from other service so here the PVC created for that service will be shared by service calling volumes_from --- pkg/kobject/kobject.go | 2 +- pkg/loader/compose/compose.go | 1 + pkg/transformer/kubernetes/k8sutils.go | 50 +++ pkg/transformer/kubernetes/kubernetes.go | 2 + pkg/transformer/openshift/openshift.go | 2 + script/test/cmd/tests.sh | 11 +- .../volumes-from/docker-compose.yml | 20 ++ .../volumes-from/output-k8s.json | 216 ++++++++++++ .../volume-mounts/volumes-from/output-os.json | 320 ++++++++++++++++++ 9 files changed, 622 insertions(+), 2 deletions(-) create mode 100644 script/test/fixtures/volume-mounts/volumes-from/docker-compose.yml create mode 100644 script/test/fixtures/volume-mounts/volumes-from/output-k8s.json create mode 100644 script/test/fixtures/volume-mounts/volumes-from/output-os.json diff --git a/pkg/kobject/kobject.go b/pkg/kobject/kobject.go index 228df9ed5..9f729e609 100644 --- a/pkg/kobject/kobject.go +++ b/pkg/kobject/kobject.go @@ -53,7 +53,6 @@ var unsupportedKey = map[string]int{ "ShmSize": 0, "StopSignal": 0, "VolumeDriver": 0, - "VolumesFrom": 0, "Uts": 0, "ReadOnly": 0, "StdinOpen": 0, @@ -150,6 +149,7 @@ type ServiceConfig struct { Privileged bool Restart string User string + VolumesFrom []string } // EnvVar holds the environment variable struct of a container diff --git a/pkg/loader/compose/compose.go b/pkg/loader/compose/compose.go index 68bbbc295..baaf165e1 100644 --- a/pkg/loader/compose/compose.go +++ b/pkg/loader/compose/compose.go @@ -193,6 +193,7 @@ func (c *Compose) LoadFile(file string) kobject.KomposeObject { serviceConfig.Privileged = composeServiceConfig.Privileged serviceConfig.Restart = composeServiceConfig.Restart serviceConfig.User = composeServiceConfig.User + serviceConfig.VolumesFrom = composeServiceConfig.VolumesFrom komposeObject.ServiceConfigs[name] = serviceConfig } diff --git a/pkg/transformer/kubernetes/k8sutils.go b/pkg/transformer/kubernetes/k8sutils.go index 50be67af1..c75d663a9 100644 --- a/pkg/transformer/kubernetes/k8sutils.go +++ b/pkg/transformer/kubernetes/k8sutils.go @@ -333,3 +333,53 @@ func SortServicesFirst(objs *[]runtime.Object) { ret = append(ret, others...) *objs = ret } + +func findDependentVolumes(svcname string, komposeObject kobject.KomposeObject) (volumes []api.Volume, volumeMounts []api.VolumeMount) { + // Get all the volumes and volumemounts this particular service is dependent on + for _, dependentSvc := range komposeObject.ServiceConfigs[svcname].VolumesFrom { + vols, volMounts := findDependentVolumes(dependentSvc, komposeObject) + volumes = append(volumes, vols...) + volumeMounts = append(volumeMounts, volMounts...) + } + // add the volumes info of this service + volMounts, vols, _ := ConfigVolumes(svcname, komposeObject.ServiceConfigs[svcname]) + volumes = append(volumes, vols...) + volumeMounts = append(volumeMounts, volMounts...) + return +} + +func VolumesFrom(objects *[]runtime.Object, komposeObject kobject.KomposeObject) { + + for _, obj := range *objects { + switch t := obj.(type) { + case *api.ReplicationController: + svcName := t.ObjectMeta.Name + for _, dependentSvc := range komposeObject.ServiceConfigs[svcName].VolumesFrom { + volumes, volumeMounts := findDependentVolumes(dependentSvc, komposeObject) + t.Spec.Template.Spec.Volumes = append(t.Spec.Template.Spec.Volumes, volumes...) + t.Spec.Template.Spec.Containers[0].VolumeMounts = append(t.Spec.Template.Spec.Containers[0].VolumeMounts, volumeMounts...) + } + case *extensions.Deployment: + svcName := t.ObjectMeta.Name + for _, dependentSvc := range komposeObject.ServiceConfigs[svcName].VolumesFrom { + volumes, volumeMounts := findDependentVolumes(dependentSvc, komposeObject) + t.Spec.Template.Spec.Volumes = append(t.Spec.Template.Spec.Volumes, volumes...) + t.Spec.Template.Spec.Containers[0].VolumeMounts = append(t.Spec.Template.Spec.Containers[0].VolumeMounts, volumeMounts...) + } + case *extensions.DaemonSet: + svcName := t.ObjectMeta.Name + for _, dependentSvc := range komposeObject.ServiceConfigs[svcName].VolumesFrom { + volumes, volumeMounts := findDependentVolumes(dependentSvc, komposeObject) + t.Spec.Template.Spec.Volumes = append(t.Spec.Template.Spec.Volumes, volumes...) + t.Spec.Template.Spec.Containers[0].VolumeMounts = append(t.Spec.Template.Spec.Containers[0].VolumeMounts, volumeMounts...) + } + case *deployapi.DeploymentConfig: + svcName := t.ObjectMeta.Name + for _, dependentSvc := range komposeObject.ServiceConfigs[svcName].VolumesFrom { + volumes, volumeMounts := findDependentVolumes(dependentSvc, komposeObject) + t.Spec.Template.Spec.Volumes = append(t.Spec.Template.Spec.Volumes, volumes...) + t.Spec.Template.Spec.Containers[0].VolumeMounts = append(t.Spec.Template.Spec.Containers[0].VolumeMounts, volumeMounts...) + } + } + } +} diff --git a/pkg/transformer/kubernetes/kubernetes.go b/pkg/transformer/kubernetes/kubernetes.go index bb66ef97f..b9be95471 100644 --- a/pkg/transformer/kubernetes/kubernetes.go +++ b/pkg/transformer/kubernetes/kubernetes.go @@ -307,6 +307,8 @@ func (k *Kubernetes) Transform(komposeObject kobject.KomposeObject, opt kobject. allobjects = append(allobjects, objects...) } + // If docker-compose has a volumes_from directive it will be handled here + VolumesFrom(&allobjects, komposeObject) // sort all object so Services are first SortServicesFirst(&allobjects) return allobjects diff --git a/pkg/transformer/openshift/openshift.go b/pkg/transformer/openshift/openshift.go index e921e2c03..e886eb9bf 100644 --- a/pkg/transformer/openshift/openshift.go +++ b/pkg/transformer/openshift/openshift.go @@ -160,6 +160,8 @@ func (k *OpenShift) Transform(komposeObject kobject.KomposeObject, opt kobject.C allobjects = append(allobjects, objects...) } + // If docker-compose has a volumes_from directive it will be handled here + kubernetes.VolumesFrom(&allobjects, komposeObject) // sort all object so Services are first kubernetes.SortServicesFirst(&allobjects) return allobjects diff --git a/script/test/cmd/tests.sh b/script/test/cmd/tests.sh index 695a1f676..a9cbf0048 100755 --- a/script/test/cmd/tests.sh +++ b/script/test/cmd/tests.sh @@ -56,6 +56,15 @@ convert::expect_success "kompose --provider=openshift -f $KOMPOSE_ROOT/script/te # kubernetes test convert::expect_success "kompose -f $KOMPOSE_ROOT/script/test/fixtures/volume-mounts/simple-vol-mounts/docker-compose.yml convert --stdout" "$KOMPOSE_ROOT/script/test/fixtures/volume-mounts/simple-vol-mounts/output-k8s.json" # openshift test -convert::expect_success "kompose --provider=openshift -f $KOMPOSE_ROOT/script/test/fixtures/volume-mounts/simple-vol-mounts/docker-compose.yml convert --stdout --dc" "$KOMPOSE_ROOT/script/test/fixtures/volume-mounts/simple-vol-mounts/output-os.json" +convert::expect_success "kompose --provider=openshift -f $KOMPOSE_ROOT/script/test/fixtures/volume-mounts/simple-vol-mounts/docker-compose.yml convert --stdout" "$KOMPOSE_ROOT/script/test/fixtures/volume-mounts/simple-vol-mounts/output-os.json" + + +###### +# Tests related to docker-compose file in /script/test/fixtures/volume-mounts/volumes-from +# kubernetes test +convert::expect_success_and_warning "kompose -f $KOMPOSE_ROOT/script/test/fixtures/volume-mounts/volumes-from/docker-compose.yml convert --stdout" "$KOMPOSE_ROOT/script/test/fixtures/volume-mounts/volumes-from/output-k8s.json" "ignoring path on the host" +# openshift test +convert::expect_success_and_warning "kompose --provider=openshift -f $KOMPOSE_ROOT/script/test/fixtures/volume-mounts/volumes-from/docker-compose.yml convert --stdout" "$KOMPOSE_ROOT/script/test/fixtures/volume-mounts/volumes-from/output-os.json" "ignoring path on the host" + exit $EXIT_STATUS diff --git a/script/test/fixtures/volume-mounts/volumes-from/docker-compose.yml b/script/test/fixtures/volume-mounts/volumes-from/docker-compose.yml new file mode 100644 index 000000000..92dd80eaf --- /dev/null +++ b/script/test/fixtures/volume-mounts/volumes-from/docker-compose.yml @@ -0,0 +1,20 @@ +version: "2" + +services: + web: + image: centos/httpd + volumes: + - "./app:/src/app" + ports: + - "3030:3000" + command: nodemon -L app/bin/www + + nginx: + restart: always + image: nginx + ports: + - "80:80" + volumes: + - /www/public + volumes_from: + - web diff --git a/script/test/fixtures/volume-mounts/volumes-from/output-k8s.json b/script/test/fixtures/volume-mounts/volumes-from/output-k8s.json new file mode 100644 index 000000000..2a241d5e5 --- /dev/null +++ b/script/test/fixtures/volume-mounts/volumes-from/output-k8s.json @@ -0,0 +1,216 @@ +{ + "kind": "List", + "apiVersion": "v1", + "metadata": {}, + "items": [ + { + "kind": "Service", + "apiVersion": "v1", + "metadata": { + "name": "web", + "creationTimestamp": null, + "labels": { + "service": "web" + } + }, + "spec": { + "ports": [ + { + "name": "3030", + "protocol": "TCP", + "port": 3030, + "targetPort": 3000 + } + ], + "selector": { + "service": "web" + } + }, + "status": { + "loadBalancer": {} + } + }, + { + "kind": "Service", + "apiVersion": "v1", + "metadata": { + "name": "nginx", + "creationTimestamp": null, + "labels": { + "service": "nginx" + } + }, + "spec": { + "ports": [ + { + "name": "80", + "protocol": "TCP", + "port": 80, + "targetPort": 80 + } + ], + "selector": { + "service": "nginx" + } + }, + "status": { + "loadBalancer": {} + } + }, + { + "kind": "Deployment", + "apiVersion": "extensions/v1beta1", + "metadata": { + "name": "web", + "creationTimestamp": null + }, + "spec": { + "replicas": 1, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "service": "web" + } + }, + "spec": { + "volumes": [ + { + "name": "web-claim0", + "persistentVolumeClaim": { + "claimName": "web-claim0" + } + } + ], + "containers": [ + { + "name": "web", + "image": "centos/httpd", + "args": [ + "nodemon", + "-L", + "app/bin/www" + ], + "ports": [ + { + "containerPort": 3000, + "protocol": "TCP" + } + ], + "resources": {}, + "volumeMounts": [ + { + "name": "web-claim0", + "mountPath": "/src/app" + } + ] + } + ], + "restartPolicy": "Always" + } + }, + "strategy": {} + }, + "status": {} + }, + { + "kind": "PersistentVolumeClaim", + "apiVersion": "v1", + "metadata": { + "name": "web-claim0", + "creationTimestamp": null + }, + "spec": { + "accessModes": [ + "ReadWriteOnce" + ], + "resources": { + "requests": { + "storage": "100Mi" + } + } + }, + "status": {} + }, + { + "kind": "Deployment", + "apiVersion": "extensions/v1beta1", + "metadata": { + "name": "nginx", + "creationTimestamp": null + }, + "spec": { + "replicas": 1, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "service": "nginx" + } + }, + "spec": { + "volumes": [ + { + "name": "nginx-claim0", + "persistentVolumeClaim": { + "claimName": "nginx-claim0" + } + }, + { + "name": "web-claim0", + "persistentVolumeClaim": { + "claimName": "web-claim0" + } + } + ], + "containers": [ + { + "name": "nginx", + "image": "nginx", + "ports": [ + { + "containerPort": 80, + "protocol": "TCP" + } + ], + "resources": {}, + "volumeMounts": [ + { + "name": "nginx-claim0", + "mountPath": "/www/public" + }, + { + "name": "web-claim0", + "mountPath": "/src/app" + } + ] + } + ], + "restartPolicy": "Always" + } + }, + "strategy": {} + }, + "status": {} + }, + { + "kind": "PersistentVolumeClaim", + "apiVersion": "v1", + "metadata": { + "name": "nginx-claim0", + "creationTimestamp": null + }, + "spec": { + "accessModes": [ + "ReadWriteOnce" + ], + "resources": { + "requests": { + "storage": "100Mi" + } + } + }, + "status": {} + } + ] +} diff --git a/script/test/fixtures/volume-mounts/volumes-from/output-os.json b/script/test/fixtures/volume-mounts/volumes-from/output-os.json new file mode 100644 index 000000000..6165cfb41 --- /dev/null +++ b/script/test/fixtures/volume-mounts/volumes-from/output-os.json @@ -0,0 +1,320 @@ +{ + "kind": "List", + "apiVersion": "v1", + "metadata": {}, + "items": [ + { + "kind": "Service", + "apiVersion": "v1", + "metadata": { + "name": "nginx", + "creationTimestamp": null, + "labels": { + "service": "nginx" + } + }, + "spec": { + "ports": [ + { + "name": "80", + "protocol": "TCP", + "port": 80, + "targetPort": 80 + } + ], + "selector": { + "service": "nginx" + } + }, + "status": { + "loadBalancer": {} + } + }, + { + "kind": "Service", + "apiVersion": "v1", + "metadata": { + "name": "web", + "creationTimestamp": null, + "labels": { + "service": "web" + } + }, + "spec": { + "ports": [ + { + "name": "3030", + "protocol": "TCP", + "port": 3030, + "targetPort": 3000 + } + ], + "selector": { + "service": "web" + } + }, + "status": { + "loadBalancer": {} + } + }, + { + "kind": "DeploymentConfig", + "apiVersion": "v1", + "metadata": { + "name": "nginx", + "creationTimestamp": null, + "labels": { + "service": "nginx" + } + }, + "spec": { + "strategy": { + "resources": {} + }, + "triggers": [ + { + "type": "ConfigChange" + }, + { + "type": "ImageChange", + "imageChangeParams": { + "automatic": true, + "containerNames": [ + "nginx" + ], + "from": { + "kind": "ImageStreamTag", + "name": "nginx:latest" + } + } + } + ], + "replicas": 1, + "test": false, + "selector": { + "service": "nginx" + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "service": "nginx" + } + }, + "spec": { + "volumes": [ + { + "name": "nginx-claim0", + "persistentVolumeClaim": { + "claimName": "nginx-claim0" + } + }, + { + "name": "web-claim0", + "persistentVolumeClaim": { + "claimName": "web-claim0" + } + } + ], + "containers": [ + { + "name": "nginx", + "image": " ", + "ports": [ + { + "containerPort": 80, + "protocol": "TCP" + } + ], + "resources": {}, + "volumeMounts": [ + { + "name": "nginx-claim0", + "mountPath": "/www/public" + }, + { + "name": "web-claim0", + "mountPath": "/src/app" + } + ] + } + ], + "restartPolicy": "Always" + } + } + }, + "status": {} + }, + { + "kind": "ImageStream", + "apiVersion": "v1", + "metadata": { + "name": "nginx", + "creationTimestamp": null + }, + "spec": { + "tags": [ + { + "name": "latest", + "annotations": null, + "from": { + "kind": "DockerImage", + "name": "nginx" + }, + "generation": null, + "importPolicy": {} + } + ] + }, + "status": { + "dockerImageRepository": "" + } + }, + { + "kind": "PersistentVolumeClaim", + "apiVersion": "v1", + "metadata": { + "name": "nginx-claim0", + "creationTimestamp": null + }, + "spec": { + "accessModes": [ + "ReadWriteOnce" + ], + "resources": { + "requests": { + "storage": "100Mi" + } + } + }, + "status": {} + }, + { + "kind": "DeploymentConfig", + "apiVersion": "v1", + "metadata": { + "name": "web", + "creationTimestamp": null, + "labels": { + "service": "web" + } + }, + "spec": { + "strategy": { + "resources": {} + }, + "triggers": [ + { + "type": "ConfigChange" + }, + { + "type": "ImageChange", + "imageChangeParams": { + "automatic": true, + "containerNames": [ + "web" + ], + "from": { + "kind": "ImageStreamTag", + "name": "web:latest" + } + } + } + ], + "replicas": 1, + "test": false, + "selector": { + "service": "web" + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "service": "web" + } + }, + "spec": { + "volumes": [ + { + "name": "web-claim0", + "persistentVolumeClaim": { + "claimName": "web-claim0" + } + } + ], + "containers": [ + { + "name": "web", + "image": " ", + "args": [ + "nodemon", + "-L", + "app/bin/www" + ], + "ports": [ + { + "containerPort": 3000, + "protocol": "TCP" + } + ], + "resources": {}, + "volumeMounts": [ + { + "name": "web-claim0", + "mountPath": "/src/app" + } + ] + } + ], + "restartPolicy": "Always" + } + } + }, + "status": {} + }, + { + "kind": "ImageStream", + "apiVersion": "v1", + "metadata": { + "name": "web", + "creationTimestamp": null + }, + "spec": { + "tags": [ + { + "name": "latest", + "annotations": null, + "from": { + "kind": "DockerImage", + "name": "centos/httpd" + }, + "generation": null, + "importPolicy": {} + } + ] + }, + "status": { + "dockerImageRepository": "" + } + }, + { + "kind": "PersistentVolumeClaim", + "apiVersion": "v1", + "metadata": { + "name": "web-claim0", + "creationTimestamp": null + }, + "spec": { + "accessModes": [ + "ReadWriteOnce" + ], + "resources": { + "requests": { + "storage": "100Mi" + } + } + }, + "status": {} + } + ] +}