diff --git a/pkg/transformer/kubernetes/kubernetes.go b/pkg/transformer/kubernetes/kubernetes.go index ddf8b13d..f4599c3f 100644 --- a/pkg/transformer/kubernetes/kubernetes.go +++ b/pkg/transformer/kubernetes/kubernetes.go @@ -45,14 +45,12 @@ func InitRC(name string, service kobject.ServiceConfig, replicas int) *api.Repli }, ObjectMeta: api.ObjectMeta{ Name: name, - //Labels: map[string]string{"service": name}, }, Spec: api.ReplicationControllerSpec{ - Selector: map[string]string{"service": name}, Replicas: int32(replicas), Template: &api.PodTemplateSpec{ ObjectMeta: api.ObjectMeta{ - //Labels: map[string]string{"service": name}, + Labels: transformer.ConfigLabels(name), }, Spec: api.PodSpec{ Containers: []api.Container{ @@ -68,45 +66,37 @@ func InitRC(name string, service kobject.ServiceConfig, replicas int) *api.Repli return rc } -// Init SC object -func InitSC(name string, service kobject.ServiceConfig) *api.Service { - sc := &api.Service{ +// Init Svc object +func InitSvc(name string, service kobject.ServiceConfig) *api.Service { + svc := &api.Service{ TypeMeta: unversioned.TypeMeta{ Kind: "Service", APIVersion: "v1", }, ObjectMeta: api.ObjectMeta{ - Name: name, - //Labels: map[string]string{"service": name}, + Name: name, + Labels: transformer.ConfigLabels(name), }, Spec: api.ServiceSpec{ - Selector: map[string]string{"service": name}, + Selector: transformer.ConfigLabels(name), }, } - return sc + return svc } -// Init DC object -func InitDC(name string, service kobject.ServiceConfig, replicas int) *extensions.Deployment { +// Init Deployment +func InitD(name string, service kobject.ServiceConfig, replicas int) *extensions.Deployment { dc := &extensions.Deployment{ TypeMeta: unversioned.TypeMeta{ Kind: "Deployment", APIVersion: "extensions/v1beta1", }, ObjectMeta: api.ObjectMeta{ - Name: name, - Labels: map[string]string{"service": name}, + Name: name, }, Spec: extensions.DeploymentSpec{ Replicas: int32(replicas), - Selector: &unversioned.LabelSelector{ - MatchLabels: map[string]string{"service": name}, - }, - //UniqueLabelKey: p.Name, Template: api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{"service": name}, - }, Spec: api.PodSpec{ Containers: []api.Container{ { @@ -133,9 +123,6 @@ func InitDS(name string, service kobject.ServiceConfig) *extensions.DaemonSet { }, Spec: extensions.DaemonSetSpec{ Template: api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Name: name, - }, Spec: api.PodSpec{ Containers: []api.Container{ { @@ -238,10 +225,10 @@ func (k *Kubernetes) Transform(komposeObject kobject.KomposeObject, opt kobject. var objects []runtime.Object svcnames = append(svcnames, name) - sc := InitSC(name, service) + svc := InitSvc(name, service) if opt.CreateD { - objects = append(objects, InitDC(name, service, opt.Replicas)) + objects = append(objects, InitD(name, service, opt.Replicas)) } if opt.CreateDS { objects = append(objects, InitDS(name, service)) @@ -264,15 +251,11 @@ func (k *Kubernetes) Transform(komposeObject kobject.KomposeObject, opt kobject. // Configure the service ports. servicePorts := ConfigServicePorts(name, service) - sc.Spec.Ports = servicePorts - - // Configure label - labels := transformer.ConfigLabels(name) - sc.ObjectMeta.Labels = labels + svc.Spec.Ports = servicePorts // Configure annotations annotations := transformer.ConfigAnnotations(service) - sc.ObjectMeta.Annotations = annotations + svc.ObjectMeta.Annotations = annotations // fillTemplate fills the pod template with the value calculated from config fillTemplate := func(template *api.PodTemplateSpec) { @@ -288,7 +271,7 @@ func (k *Kubernetes) Transform(komposeObject kobject.KomposeObject, opt kobject. } } template.Spec.Containers[0].Ports = ports - template.ObjectMeta.Labels = labels + template.ObjectMeta.Labels = transformer.ConfigLabels(name) // Configure the container restart policy. switch service.Restart { case "", "always": @@ -304,7 +287,6 @@ func (k *Kubernetes) Transform(komposeObject kobject.KomposeObject, opt kobject. // fillObjectMeta fills the metadata with the value calculated from config fillObjectMeta := func(meta *api.ObjectMeta) { - meta.Labels = labels meta.Annotations = annotations } @@ -315,7 +297,7 @@ func (k *Kubernetes) Transform(komposeObject kobject.KomposeObject, opt kobject. // If ports not provided in configuration we will not make service if PortsExist(name, service) { - objects = append(objects, sc) + objects = append(objects, svc) } allobjects = append(allobjects, objects...) diff --git a/pkg/transformer/kubernetes/kubernetes_test.go b/pkg/transformer/kubernetes/kubernetes_test.go index 106d7f40..b9a3fbcf 100644 --- a/pkg/transformer/kubernetes/kubernetes_test.go +++ b/pkg/transformer/kubernetes/kubernetes_test.go @@ -23,6 +23,7 @@ import ( deployapi "github.com/openshift/origin/pkg/deploy/api" "github.com/skippbox/kompose/pkg/kobject" + "github.com/skippbox/kompose/pkg/transformer" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apis/extensions" @@ -38,19 +39,19 @@ func newServiceConfig() kobject.ServiceConfig { WorkingDir: "dir", Args: []string{"arg1", "arg2"}, Volumes: []string{"/tmp/volume"}, - Network: []string{"network1", "network2"}, - Labels: map[string]string{"service": "app"}, + Network: []string{"network1", "network2"}, // not supported + Labels: nil, Annotations: map[string]string{"abc": "def"}, - CPUSet: "cpu_set", - CPUShares: 1, - CPUQuota: 1, - CapAdd: []string{"cap_add"}, - CapDrop: []string{"cap_drop"}, - Entrypoint: []string{"entrypoint"}, - Expose: []string{"expose"}, + CPUSet: "cpu_set", // not supported + CPUShares: 1, // not supported + CPUQuota: 1, // not supported + CapAdd: []string{"cap_add"}, // not supported + CapDrop: []string{"cap_drop"}, // not supported + Entrypoint: []string{"entrypoint"}, // not supported + Expose: []string{"expose"}, // not supported Privileged: true, Restart: "always", - User: "user", + User: "user", // not supported } } @@ -114,7 +115,22 @@ func equalPorts(kPorts []kobject.Ports, k8sPorts []api.ContainerPort) bool { return true } -func checkPodTemplate(config kobject.ServiceConfig, template api.PodTemplateSpec) error { +func equalStringMaps(map1 map[string]string, map2 map[string]string) bool { + if len(map1) != len(map2) { + return false + } + for k, v := range map1 { + if map2[k] != v { + return false + } + } + return true +} + +func checkPodTemplate(config kobject.ServiceConfig, template api.PodTemplateSpec, expectedLabels map[string]string) error { + if len(template.Spec.Containers) == 0 { + return fmt.Errorf("Failed to set container: %#v vs. %#v", config, template) + } container := template.Spec.Containers[0] // FIXME: we should support container name and uncomment this test //if config.ContainerName != container.Name { @@ -143,19 +159,43 @@ func checkPodTemplate(config kobject.ServiceConfig, template api.PodTemplateSpec (template.Spec.Volumes[0].VolumeSource.HostPath == nil && template.Spec.Volumes[0].VolumeSource.EmptyDir == nil) { 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 + if !equalStringMaps(expectedLabels, template.Labels) { + return fmt.Errorf("Found different template labels: %#v vs. %#v", expectedLabels, template.Labels) + } restartPolicyMapping := map[string]api.RestartPolicy{"always": api.RestartPolicyAlways} if restartPolicyMapping[config.Restart] != template.Spec.RestartPolicy { return fmt.Errorf("Found incorrect restart policy: %v vs. %v", config.Restart, template.Spec.RestartPolicy) } - if len(template.Labels) != 1 || len(template.Labels["service"]) == 0 { - return fmt.Errorf("Found incorrect labels: %#v", template.Labels) + if config.Privileged == privilegedNilOrFalse(template) { + return fmt.Errorf("Found different template privileged: %#v vs. %#v", config.Privileged, template.Spec.Containers[0].SecurityContext) + } + return nil +} + +func privilegedNilOrFalse(template api.PodTemplateSpec) bool { + return len(template.Spec.Containers) == 0 || template.Spec.Containers[0].SecurityContext == nil || + template.Spec.Containers[0].SecurityContext.Privileged == nil || *template.Spec.Containers[0].SecurityContext.Privileged == false +} + +func checkService(config kobject.ServiceConfig, svc *api.Service, expectedLabels map[string]string) error { + if !equalStringMaps(expectedLabels, svc.Spec.Selector) { + return fmt.Errorf("Found unexpected selector: %#v vs. %#v", expectedLabels, svc.Spec.Selector) } // TODO: finish this return nil } -func checkService(config kobject.ServiceConfig, svc *api.Service) error { - // TODO: finish this +func checkMeta(config kobject.ServiceConfig, meta api.ObjectMeta, expectedName string, shouldSetLabels bool) error { + if expectedName != meta.Name { + return fmt.Errorf("Found unexpected name: %s vs. %s", expectedName, meta.Name) + } + if !equalStringMaps(config.Annotations, meta.Annotations) { + return fmt.Errorf("Found different annotations: %#v vs. %#v", config.Annotations, meta.Annotations) + } + if shouldSetLabels != (len(meta.Labels) > 0) { + return fmt.Errorf("Unexpected labels: %#v", meta.Labels) + } return nil } @@ -166,62 +206,100 @@ func TestKomposeConvert(t *testing.T) { opt kobject.ConvertOptions expectedNumObjs int }{ - "Convert to Deployments": {newKomposeObject(), kobject.ConvertOptions{CreateD: true, Replicas: replicas}, 2}, + "Convert to Deployments (D)": {newKomposeObject(), kobject.ConvertOptions{CreateD: true, Replicas: replicas}, 2}, + "Convert to DaemonSets (DS)": {newKomposeObject(), kobject.ConvertOptions{CreateDS: true}, 2}, + "Convert to ReplicationController (RC)": {newKomposeObject(), kobject.ConvertOptions{CreateRC: true, Replicas: replicas}, 2}, + "Convert to D, DS, and RC": {newKomposeObject(), kobject.ConvertOptions{CreateD: true, CreateDS: true, CreateRC: true, Replicas: replicas}, 4}, // TODO: add more tests } + for name, test := range testCases { t.Log("Test case:", name) k := Kubernetes{} + // Run Transform objs := k.Transform(test.komposeObject, test.opt) if len(objs) != test.expectedNumObjs { t.Errorf("Expected %d objects returned, got %d", test.expectedNumObjs, len(objs)) } + var foundSVC, foundD, foundDS, foundRC, foundDC bool + name := "app" + labels := transformer.ConfigLabels(name) + config := test.komposeObject.ServiceConfigs[name] + // Check results for _, obj := range objs { if svc, ok := obj.(*api.Service); ok { - if err := checkService(test.komposeObject.ServiceConfigs["app"], svc); err != nil { + if err := checkService(config, svc, labels); err != nil { + t.Errorf("%v", err) + } + if err := checkMeta(config, svc.ObjectMeta, name, true); err != nil { t.Errorf("%v", err) } foundSVC = true } if test.opt.CreateD { if d, ok := obj.(*extensions.Deployment); ok { - if err := checkPodTemplate(test.komposeObject.ServiceConfigs["app"], d.Spec.Template); err != nil { + if err := checkPodTemplate(config, d.Spec.Template, labels); err != nil { + t.Errorf("%v", err) + } + if err := checkMeta(config, d.ObjectMeta, name, false); err != nil { t.Errorf("%v", err) } if (int)(d.Spec.Replicas) != replicas { t.Errorf("Expected %d replicas, got %d", replicas, d.Spec.Replicas) } + if d.Spec.Selector != nil && len(d.Spec.Selector.MatchLabels) > 0 { + t.Errorf("Expect selector be unset, got: %#v", d.Spec.Selector) + } foundD = true } } if test.opt.CreateDS { if ds, ok := obj.(*extensions.DaemonSet); ok { - if err := checkPodTemplate(test.komposeObject.ServiceConfigs["app"], ds.Spec.Template); err != nil { + if err := checkPodTemplate(config, ds.Spec.Template, labels); err != nil { t.Errorf("%v", err) } + if err := checkMeta(config, ds.ObjectMeta, name, false); err != nil { + t.Errorf("%v", err) + } + if ds.Spec.Selector != nil && len(ds.Spec.Selector.MatchLabels) > 0 { + t.Errorf("Expect selector be unset, got: %#v", ds.Spec.Selector) + } foundDS = true } } if test.opt.CreateRC { if rc, ok := obj.(*api.ReplicationController); ok { - if err := checkPodTemplate(test.komposeObject.ServiceConfigs["app"], *rc.Spec.Template); err != nil { + if err := checkPodTemplate(config, *rc.Spec.Template, labels); err != nil { + t.Errorf("%v", err) + } + if err := checkMeta(config, rc.ObjectMeta, name, false); err != nil { t.Errorf("%v", err) } if (int)(rc.Spec.Replicas) != replicas { t.Errorf("Expected %d replicas, got %d", replicas, rc.Spec.Replicas) } + if len(rc.Spec.Selector) > 0 { + t.Errorf("Expect selector be unset, got: %#v", rc.Spec.Selector) + } foundRC = true } } + // TODO: k8s & openshift transformer is now separated; either separate the test or combine the transformer if test.opt.CreateDeploymentConfig { if dc, ok := obj.(*deployapi.DeploymentConfig); ok { - if err := checkPodTemplate(test.komposeObject.ServiceConfigs["app"], *dc.Spec.Template); err != nil { + if err := checkPodTemplate(config, *dc.Spec.Template, labels); err != nil { + t.Errorf("%v", err) + } + if err := checkMeta(config, dc.ObjectMeta, name, false); err != nil { t.Errorf("%v", err) } if (int)(dc.Spec.Replicas) != replicas { t.Errorf("Expected %d replicas, got %d", replicas, dc.Spec.Replicas) } + if len(dc.Spec.Selector) > 0 { + t.Errorf("Expect selector be unset, got: %#v", dc.Spec.Selector) + } foundDC = true } } diff --git a/pkg/transformer/openshift/openshift.go b/pkg/transformer/openshift/openshift.go index ec805beb..8f48dfb1 100644 --- a/pkg/transformer/openshift/openshift.go +++ b/pkg/transformer/openshift/openshift.go @@ -74,10 +74,10 @@ func (k *OpenShift) Transform(komposeObject kobject.KomposeObject, opt kobject.C var objects []runtime.Object svcnames = append(svcnames, name) - sc := kubernetes.InitSC(name, service) + svc := kubernetes.InitSvc(name, service) if opt.CreateD { - objects = append(objects, kubernetes.InitDC(name, service, opt.Replicas)) + objects = append(objects, kubernetes.InitD(name, service, opt.Replicas)) } if opt.CreateDS { objects = append(objects, kubernetes.InitDS(name, service)) @@ -103,15 +103,15 @@ func (k *OpenShift) Transform(komposeObject kobject.KomposeObject, opt kobject.C // Configure the service ports. servicePorts := kubernetes.ConfigServicePorts(name, service) - sc.Spec.Ports = servicePorts + svc.Spec.Ports = servicePorts // Configure label labels := transformer.ConfigLabels(name) - sc.ObjectMeta.Labels = labels + svc.ObjectMeta.Labels = labels // Configure annotations annotations := transformer.ConfigAnnotations(service) - sc.ObjectMeta.Annotations = annotations + svc.ObjectMeta.Annotations = annotations // fillTemplate fills the pod template with the value calculated from config fillTemplate := func(template *api.PodTemplateSpec) { @@ -154,7 +154,7 @@ func (k *OpenShift) Transform(komposeObject kobject.KomposeObject, opt kobject.C // If ports not provided in configuration we will not make service if kubernetes.PortsExist(name, service) { - objects = append(objects, sc) + objects = append(objects, svc) } allobjects = append(allobjects, objects...) } diff --git a/script/test/fixtures/etherpad/output-k8s.json b/script/test/fixtures/etherpad/output-k8s.json index 43ec3277..f2739620 100644 --- a/script/test/fixtures/etherpad/output-k8s.json +++ b/script/test/fixtures/etherpad/output-k8s.json @@ -9,17 +9,9 @@ "metadata": { "name": "etherpad", "creationTimestamp": null, - "labels": { - "service": "etherpad" - } }, "spec": { "replicas": 1, - "selector": { - "matchLabels": { - "service": "etherpad" - } - }, "template": { "metadata": { "creationTimestamp": null, @@ -103,17 +95,9 @@ "metadata": { "name": "mariadb", "creationTimestamp": null, - "labels": { - "service": "mariadb" - } }, "spec": { "replicas": 1, - "selector": { - "matchLabels": { - "service": "mariadb" - } - }, "template": { "metadata": { "creationTimestamp": null, diff --git a/script/test/fixtures/flask-redis/output-k8s.json b/script/test/fixtures/flask-redis/output-k8s.json index ba6244cd..10c86aec 100644 --- a/script/test/fixtures/flask-redis/output-k8s.json +++ b/script/test/fixtures/flask-redis/output-k8s.json @@ -9,17 +9,9 @@ "metadata": { "name": "flask", "creationTimestamp": null, - "labels": { - "service": "flask" - } }, "spec": { "replicas": 1, - "selector": { - "matchLabels": { - "service": "flask" - } - }, "template": { "metadata": { "creationTimestamp": null, @@ -91,17 +83,9 @@ "metadata": { "name": "redis", "creationTimestamp": null, - "labels": { - "service": "redis" - } }, "spec": { "replicas": 1, - "selector": { - "matchLabels": { - "service": "redis" - } - }, "template": { "metadata": { "creationTimestamp": null, diff --git a/script/test/fixtures/gitlab/output-k8s.json b/script/test/fixtures/gitlab/output-k8s.json index bf36071e..417b1f5a 100644 --- a/script/test/fixtures/gitlab/output-k8s.json +++ b/script/test/fixtures/gitlab/output-k8s.json @@ -9,17 +9,9 @@ "metadata": { "name": "gitlab", "creationTimestamp": null, - "labels": { - "service": "gitlab" - } }, "spec": { "replicas": 1, - "selector": { - "matchLabels": { - "service": "gitlab" - } - }, "template": { "metadata": { "creationTimestamp": null, @@ -135,17 +127,9 @@ "metadata": { "name": "postgresql", "creationTimestamp": null, - "labels": { - "service": "postgresql" - } }, "spec": { "replicas": 1, - "selector": { - "matchLabels": { - "service": "postgresql" - } - }, "template": { "metadata": { "creationTimestamp": null, @@ -221,17 +205,9 @@ "metadata": { "name": "redis", "creationTimestamp": null, - "labels": { - "service": "redis" - } }, "spec": { "replicas": 1, - "selector": { - "matchLabels": { - "service": "redis" - } - }, "template": { "metadata": { "creationTimestamp": null, diff --git a/script/test/fixtures/ngnix-node-redis/output-k8s.json b/script/test/fixtures/ngnix-node-redis/output-k8s.json index e53d27d3..a8aa4e03 100644 --- a/script/test/fixtures/ngnix-node-redis/output-k8s.json +++ b/script/test/fixtures/ngnix-node-redis/output-k8s.json @@ -9,17 +9,9 @@ "metadata": { "name": "node2", "creationTimestamp": null, - "labels": { - "service": "node2" - } }, "spec": { "replicas": 1, - "selector": { - "matchLabels": { - "service": "node2" - } - }, "template": { "metadata": { "creationTimestamp": null, @@ -80,17 +72,9 @@ "metadata": { "name": "node3", "creationTimestamp": null, - "labels": { - "service": "node3" - } }, "spec": { "replicas": 1, - "selector": { - "matchLabels": { - "service": "node3" - } - }, "template": { "metadata": { "creationTimestamp": null, @@ -151,17 +135,9 @@ "metadata": { "name": "redis", "creationTimestamp": null, - "labels": { - "service": "redis" - } }, "spec": { "replicas": 1, - "selector": { - "matchLabels": { - "service": "redis" - } - }, "template": { "metadata": { "creationTimestamp": null, @@ -223,17 +199,9 @@ "metadata": { "name": "nginx", "creationTimestamp": null, - "labels": { - "service": "nginx" - } }, "spec": { "replicas": 1, - "selector": { - "matchLabels": { - "service": "nginx" - } - }, "template": { "metadata": { "creationTimestamp": null, @@ -294,17 +262,9 @@ "metadata": { "name": "node1", "creationTimestamp": null, - "labels": { - "service": "node1" - } }, "spec": { "replicas": 1, - "selector": { - "matchLabels": { - "service": "node1" - } - }, "template": { "metadata": { "creationTimestamp": null, diff --git a/script/test/fixtures/wordpress/output-k8s.json b/script/test/fixtures/wordpress/output-k8s.json index 1a09a00e..88579c6c 100644 --- a/script/test/fixtures/wordpress/output-k8s.json +++ b/script/test/fixtures/wordpress/output-k8s.json @@ -9,17 +9,9 @@ "metadata": { "name": "mariadb", "creationTimestamp": null, - "labels": { - "service": "mariadb" - } }, "spec": { "replicas": 1, - "selector": { - "matchLabels": { - "service": "mariadb" - } - }, "template": { "metadata": { "creationTimestamp": null, @@ -99,17 +91,9 @@ "metadata": { "name": "wordpress", "creationTimestamp": null, - "labels": { - "service": "wordpress" - } }, "spec": { "replicas": 1, - "selector": { - "matchLabels": { - "service": "wordpress" - } - }, "template": { "metadata": { "creationTimestamp": null, diff --git a/script/test/fixtures/wordpress/output-os.json b/script/test/fixtures/wordpress/output-os.json index 50c2a735..33a252fc 100644 --- a/script/test/fixtures/wordpress/output-os.json +++ b/script/test/fixtures/wordpress/output-os.json @@ -9,17 +9,9 @@ "metadata": { "name": "mariadb", "creationTimestamp": null, - "labels": { - "service": "mariadb" - } }, "spec": { "replicas": 1, - "selector": { - "matchLabels": { - "service": "mariadb" - } - }, "template": { "metadata": { "creationTimestamp": null, @@ -99,17 +91,9 @@ "metadata": { "name": "wordpress", "creationTimestamp": null, - "labels": { - "service": "wordpress" - } }, "spec": { "replicas": 1, - "selector": { - "matchLabels": { - "service": "wordpress" - } - }, "template": { "metadata": { "creationTimestamp": null,