Create PVC object for docker-compose volumes

Instead of creating emptydir, create PersistentVolumeClaim
for docker-compose volumes by default

Fixes #150
This commit is contained in:
Suraj Deshmukh 2016-10-05 09:19:00 +05:30
parent 676fead88e
commit e879164c2e
8 changed files with 358 additions and 22 deletions

View File

@ -182,6 +182,8 @@ func PrintList(objects []runtime.Object, opt kobject.ConvertOptions) error {
file = transformer.Print(t.Name, strings.ToLower(t.Kind), data, opt.ToStdout, opt.GenerateYaml, f) file = transformer.Print(t.Name, strings.ToLower(t.Kind), data, opt.ToStdout, opt.GenerateYaml, f)
case *api.Service: case *api.Service:
file = transformer.Print(t.Name, strings.ToLower(t.Kind), data, opt.ToStdout, opt.GenerateYaml, f) file = transformer.Print(t.Name, strings.ToLower(t.Kind), data, opt.ToStdout, opt.GenerateYaml, f)
case *api.PersistentVolumeClaim:
file = transformer.Print(t.Name, strings.ToLower(t.Kind), data, opt.ToStdout, opt.GenerateYaml, f)
} }
files = append(files, file) files = append(files, file)
} }
@ -250,12 +252,20 @@ func CreateService(name string, service kobject.ServiceConfig, objects []runtime
} }
// load configurations to k8s objects // load configurations to k8s objects
func UpdateKubernetesObjects(name string, service kobject.ServiceConfig, objects []runtime.Object) { func UpdateKubernetesObjects(name string, service kobject.ServiceConfig, objects *[]runtime.Object) {
// Configure the environment variables. // Configure the environment variables.
envs := ConfigEnvs(name, service) envs := ConfigEnvs(name, service)
// Configure the container volumes. // Configure the container volumes.
volumesMount, volumes := ConfigVolumes(service) volumesMount, volumes, pvc := ConfigVolumes(name, service)
if pvc != nil {
// Looping on the slice pvc instead of `*objects = append(*objects, pvc...)`
// because the type of objects and pvc is different, but when doing append
// one element at a time it gets converted to runtime.Object for objects slice
for _, p := range pvc {
*objects = append(*objects, p)
}
}
// Configure the container ports. // Configure the container ports.
ports := ConfigPorts(name, service) ports := ConfigPorts(name, service)
@ -301,7 +311,7 @@ func UpdateKubernetesObjects(name string, service kobject.ServiceConfig, objects
} }
// update supported controller // update supported controller
for _, obj := range objects { for _, obj := range *objects {
UpdateController(obj, fillTemplate, fillObjectMeta) UpdateController(obj, fillTemplate, fillObjectMeta)
} }
} }

View File

@ -28,6 +28,7 @@ import (
// install kubernetes api // install kubernetes api
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
_ "k8s.io/kubernetes/pkg/api/install" _ "k8s.io/kubernetes/pkg/api/install"
"k8s.io/kubernetes/pkg/api/resource"
"k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apis/extensions"
_ "k8s.io/kubernetes/pkg/apis/extensions/install" _ "k8s.io/kubernetes/pkg/apis/extensions/install"
@ -146,6 +147,38 @@ func InitDS(name string, service kobject.ServiceConfig) *extensions.DaemonSet {
return ds return ds
} }
// Initialize PersistentVolumeClaim
func CreatePVC(name string, mode string) *api.PersistentVolumeClaim {
size, err := resource.ParseQuantity("100Mi")
if err != nil {
logrus.Fatalf("Error parsing size")
}
pvc := &api.PersistentVolumeClaim{
TypeMeta: unversioned.TypeMeta{
Kind: "PersistentVolumeClaim",
APIVersion: "v1",
},
ObjectMeta: api.ObjectMeta{
Name: name,
},
Spec: api.PersistentVolumeClaimSpec{
Resources: api.ResourceRequirements{
Requests: api.ResourceList{
api.ResourceStorage: size,
},
},
},
}
if mode == "ro" {
pvc.Spec.AccessModes = []api.PersistentVolumeAccessMode{"ReadWriteOnce"}
} else {
pvc.Spec.AccessModes = []api.PersistentVolumeAccessMode{"ReadWriteOnce"}
}
return pvc
}
// Configure the container ports. // Configure the container ports.
func ConfigPorts(name string, service kobject.ServiceConfig) []api.ContainerPort { func ConfigPorts(name string, service kobject.ServiceConfig) []api.ContainerPort {
ports := []api.ContainerPort{} ports := []api.ContainerPort{}
@ -180,34 +213,49 @@ func ConfigServicePorts(name string, service kobject.ServiceConfig) []api.Servic
} }
// Configure the container volumes. // Configure the container volumes.
func ConfigVolumes(service kobject.ServiceConfig) ([]api.VolumeMount, []api.Volume) { func ConfigVolumes(name string, service kobject.ServiceConfig) ([]api.VolumeMount, []api.Volume, []*api.PersistentVolumeClaim) {
volumesMount := []api.VolumeMount{} volumesMount := []api.VolumeMount{}
volumes := []api.Volume{} volumes := []api.Volume{}
volumeSource := api.VolumeSource{} var pvc []*api.PersistentVolumeClaim
var count int
for _, volume := range service.Volumes { for _, volume := range service.Volumes {
name, host, container, mode, err := transformer.ParseVolume(volume) volumeName, host, container, mode, err := transformer.ParseVolume(volume)
if err != nil { if err != nil {
logrus.Warningf("Failed to configure container volume: %v", err) logrus.Warningf("Failed to configure container volume: %v", err)
continue continue
} }
if volumeName == "" {
// if volume name isn't specified, set it to a random string of 20 chars volumeName = fmt.Sprintf("%s-claim%d", name, count)
if len(name) == 0 { count++
name = transformer.RandStringBytes(20)
} }
// check if ro/rw mode is defined, default rw // check if ro/rw mode is defined, default rw
readonly := len(mode) > 0 && mode == "ro" readonly := len(mode) > 0 && mode == "ro"
volumesMount = append(volumesMount, api.VolumeMount{Name: name, ReadOnly: readonly, MountPath: container}) volmount := api.VolumeMount{
Name: volumeName,
ReadOnly: readonly,
MountPath: container,
}
volumesMount = append(volumesMount, volmount)
vol := api.Volume{
Name: volumeName,
VolumeSource: api.VolumeSource{
PersistentVolumeClaim: &api.PersistentVolumeClaimVolumeSource{
ClaimName: volumeName,
ReadOnly: readonly,
},
},
}
volumes = append(volumes, vol)
if len(host) > 0 { if len(host) > 0 {
logrus.Warningf("Volume mount on the host %q isn't supported - ignoring path on the host", host) logrus.Warningf("Volume mount on the host %q isn't supported - ignoring path on the host", host)
} }
volumeSource = api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}} pvc = append(pvc, CreatePVC(volumeName, mode))
volumes = append(volumes, api.Volume{Name: name, VolumeSource: volumeSource})
} }
return volumesMount, volumes return volumesMount, volumes, pvc
} }
// Configure the environment variables. // Configure the environment variables.
@ -255,7 +303,7 @@ func (k *Kubernetes) Transform(komposeObject kobject.KomposeObject, opt kobject.
objects = append(objects, svc) objects = append(objects, svc)
} }
UpdateKubernetesObjects(name, service, objects) UpdateKubernetesObjects(name, service, &objects)
allobjects = append(allobjects, objects...) allobjects = append(allobjects, objects...)
} }

View File

@ -152,7 +152,7 @@ func checkPodTemplate(config kobject.ServiceConfig, template api.PodTemplateSpec
if !equalStringSlice(config.Args, container.Args) { if !equalStringSlice(config.Args, container.Args) {
return fmt.Errorf("Found different container args: %#v vs. %#v", config.Args, container.Args) return fmt.Errorf("Found different container args: %#v vs. %#v", config.Args, container.Args)
} }
if len(template.Spec.Volumes) == 0 || len(template.Spec.Volumes[0].Name) == 0 || template.Spec.Volumes[0].VolumeSource.EmptyDir == nil { if len(template.Spec.Volumes) == 0 || len(template.Spec.Volumes[0].Name) == 0 || template.Spec.Volumes[0].VolumeSource.PersistentVolumeClaim == nil {
return fmt.Errorf("Found incorrect volumes: %v vs. %#v", config.Volumes, template.Spec.Volumes) return fmt.Errorf("Found incorrect volumes: %v vs. %#v", config.Volumes, template.Spec.Volumes)
} }
// We only set controller labels here and k8s server will take care of other defaults, such as selectors // We only set controller labels here and k8s server will take care of other defaults, such as selectors
@ -202,10 +202,12 @@ func TestKomposeConvert(t *testing.T) {
opt kobject.ConvertOptions opt kobject.ConvertOptions
expectedNumObjs int expectedNumObjs int
}{ }{
"Convert to Deployments (D)": {newKomposeObject(), kobject.ConvertOptions{CreateD: true, Replicas: replicas}, 2}, // objects generated are deployment, service and pvc
"Convert to DaemonSets (DS)": {newKomposeObject(), kobject.ConvertOptions{CreateDS: true}, 2}, "Convert to Deployments (D)": {newKomposeObject(), kobject.ConvertOptions{CreateD: true, Replicas: replicas}, 3},
"Convert to ReplicationController (RC)": {newKomposeObject(), kobject.ConvertOptions{CreateRC: true, Replicas: replicas}, 2}, "Convert to DaemonSets (DS)": {newKomposeObject(), kobject.ConvertOptions{CreateDS: true}, 3},
"Convert to D, DS, and RC": {newKomposeObject(), kobject.ConvertOptions{CreateD: true, CreateDS: true, CreateRC: true, Replicas: replicas}, 4}, "Convert to ReplicationController (RC)": {newKomposeObject(), kobject.ConvertOptions{CreateRC: true, Replicas: replicas}, 3},
// objects generated are deployment, daemonset, ReplicationController, service and pvc
"Convert to D, DS, and RC": {newKomposeObject(), kobject.ConvertOptions{CreateD: true, CreateDS: true, CreateRC: true, Replicas: replicas}, 5},
// TODO: add more tests // TODO: add more tests
} }

View File

@ -147,7 +147,7 @@ func (k *OpenShift) Transform(komposeObject kobject.KomposeObject, opt kobject.C
objects = append(objects, svc) objects = append(objects, svc)
} }
kubernetes.UpdateKubernetesObjects(name, service, objects) kubernetes.UpdateKubernetesObjects(name, service, &objects)
allobjects = append(allobjects, objects...) allobjects = append(allobjects, objects...)
} }

View File

@ -51,5 +51,12 @@ convert::expect_success "kompose -f $KOMPOSE_ROOT/script/test/fixtures/ports-wit
convert::expect_success "kompose -f $KOMPOSE_ROOT/script/test/fixtures/ports-with-proto/docker-compose.yml convert --stdout --dc" "$KOMPOSE_ROOT/script/test/fixtures/ports-with-proto/output-os.json" convert::expect_success "kompose -f $KOMPOSE_ROOT/script/test/fixtures/ports-with-proto/docker-compose.yml convert --stdout --dc" "$KOMPOSE_ROOT/script/test/fixtures/ports-with-proto/output-os.json"
######
# Tests related to docker-compose file in /script/test/fixtures/volume-mounts/simple-vol-mounts
# 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 -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"
exit $EXIT_STATUS exit $EXIT_STATUS

View File

@ -0,0 +1,9 @@
version: "2"
services:
httpd:
image: docker.io/fedora/apache
ports:
- "80"
volumes:
- /var/www/html

View File

@ -0,0 +1,104 @@
{
"kind": "List",
"apiVersion": "v1",
"metadata": {},
"items": [
{
"kind": "Service",
"apiVersion": "v1",
"metadata": {
"name": "httpd",
"creationTimestamp": null,
"labels": {
"service": "httpd"
}
},
"spec": {
"ports": [
{
"name": "80",
"protocol": "TCP",
"port": 80,
"targetPort": 80
}
],
"selector": {
"service": "httpd"
}
},
"status": {
"loadBalancer": {}
}
},
{
"kind": "Deployment",
"apiVersion": "extensions/v1beta1",
"metadata": {
"name": "httpd",
"creationTimestamp": null
},
"spec": {
"replicas": 1,
"template": {
"metadata": {
"creationTimestamp": null,
"labels": {
"service": "httpd"
}
},
"spec": {
"volumes": [
{
"name": "httpd-claim0",
"persistentVolumeClaim": {
"claimName": "httpd-claim0"
}
}
],
"containers": [
{
"name": "httpd",
"image": "docker.io/fedora/apache",
"ports": [
{
"containerPort": 80,
"protocol": "TCP"
}
],
"resources": {},
"volumeMounts": [
{
"name": "httpd-claim0",
"mountPath": "/var/www/html"
}
]
}
],
"restartPolicy": "Always"
}
},
"strategy": {}
},
"status": {}
},
{
"kind": "PersistentVolumeClaim",
"apiVersion": "v1",
"metadata": {
"name": "httpd-claim0",
"creationTimestamp": null
},
"spec": {
"accessModes": [
"ReadWriteOnce"
],
"resources": {
"requests": {
"storage": "100Mi"
}
}
},
"status": {}
}
]
}

View File

@ -0,0 +1,156 @@
{
"kind": "List",
"apiVersion": "v1",
"metadata": {},
"items": [
{
"kind": "Service",
"apiVersion": "v1",
"metadata": {
"name": "httpd",
"creationTimestamp": null,
"labels": {
"service": "httpd"
}
},
"spec": {
"ports": [
{
"name": "80",
"protocol": "TCP",
"port": 80,
"targetPort": 80
}
],
"selector": {
"service": "httpd"
}
},
"status": {
"loadBalancer": {}
}
},
{
"kind": "DeploymentConfig",
"apiVersion": "v1",
"metadata": {
"name": "httpd",
"creationTimestamp": null,
"labels": {
"service": "httpd"
}
},
"spec": {
"strategy": {
"resources": {}
},
"triggers": [
{
"type": "ConfigChange"
},
{
"type": "ImageChange",
"imageChangeParams": {
"automatic": true,
"containerNames": [
"httpd"
],
"from": {
"kind": "ImageStreamTag",
"name": "httpd:latest"
}
}
}
],
"replicas": 1,
"test": false,
"selector": {
"service": "httpd"
},
"template": {
"metadata": {
"creationTimestamp": null,
"labels": {
"service": "httpd"
}
},
"spec": {
"volumes": [
{
"name": "httpd-claim0",
"persistentVolumeClaim": {
"claimName": "httpd-claim0"
}
}
],
"containers": [
{
"name": "httpd",
"image": " ",
"ports": [
{
"containerPort": 80,
"protocol": "TCP"
}
],
"resources": {},
"volumeMounts": [
{
"name": "httpd-claim0",
"mountPath": "/var/www/html"
}
]
}
],
"restartPolicy": "Always"
}
}
},
"status": {}
},
{
"kind": "ImageStream",
"apiVersion": "v1",
"metadata": {
"name": "httpd",
"creationTimestamp": null
},
"spec": {
"tags": [
{
"name": "latest",
"annotations": null,
"from": {
"kind": "DockerImage",
"name": "docker.io/fedora/apache"
},
"generation": null,
"importPolicy": {}
}
]
},
"status": {
"dockerImageRepository": ""
}
},
{
"kind": "PersistentVolumeClaim",
"apiVersion": "v1",
"metadata": {
"name": "httpd-claim0",
"creationTimestamp": null
},
"spec": {
"accessModes": [
"ReadWriteOnce"
],
"resources": {
"requests": {
"storage": "100Mi"
}
}
},
"status": {}
}
]
}