From e6ae470830054d83086053db92e02f41511267e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=2E=C3=87a=C4=9Flar=20Onur?= Date: Sat, 6 Jan 2018 16:33:32 -0800 Subject: [PATCH] Allow services to use same port with different protocols kompose fails if compose file declares different protocols for the same port. eg; ... ports: - 666:666/udp - 666:666/tcp ... This PR adds the port to the output and also makes sure that names are unique for each port/protocol pair. This is supported with LoadBalancer (kubernetes/kubernetes#2995) so trying to use this config with LB panics. --- pkg/transformer/kubernetes/kubernetes.go | 38 ++- script/test/cmd/tests.sh | 15 +- .../output-k8s-template.json | 2 +- .../output-os-template.json | 2 +- .../docker-compose.yml | 21 ++ .../output-k8s-template.json | 174 +++++++++++ .../output-os-template.json | 277 ++++++++++++++++++ 7 files changed, 512 insertions(+), 17 deletions(-) create mode 100644 script/test/fixtures/same-port-different-proto/docker-compose.yml create mode 100644 script/test/fixtures/same-port-different-proto/output-k8s-template.json create mode 100644 script/test/fixtures/same-port-different-proto/output-os-template.json diff --git a/pkg/transformer/kubernetes/kubernetes.go b/pkg/transformer/kubernetes/kubernetes.go index e10627ae..ba23ab5a 100644 --- a/pkg/transformer/kubernetes/kubernetes.go +++ b/pkg/transformer/kubernetes/kubernetes.go @@ -337,6 +337,9 @@ func (k *Kubernetes) ConfigPorts(name string, service kobject.ServiceConfig) []a // ConfigServicePorts configure the container service ports. func (k *Kubernetes) ConfigServicePorts(name string, service kobject.ServiceConfig) []api.ServicePort { servicePorts := []api.ServicePort{} + seenPorts := make(map[int]struct{}, len(service.Port)) + + var servicePort api.ServicePort for _, port := range service.Port { if port.HostPort == 0 { port.HostPort = port.ContainerPort @@ -346,21 +349,28 @@ func (k *Kubernetes) ConfigServicePorts(name string, service kobject.ServiceConf targetPort.IntVal = port.ContainerPort targetPort.StrVal = strconv.Itoa(int(port.ContainerPort)) - // If the default is already TCP, no need to include it. - if port.Protocol == api.ProtocolTCP { - servicePorts = append(servicePorts, api.ServicePort{ - Name: strconv.Itoa(int(port.HostPort)), - Port: port.HostPort, - TargetPort: targetPort, - }) - } else { - servicePorts = append(servicePorts, api.ServicePort{ - Name: strconv.Itoa(int(port.HostPort)), - Protocol: port.Protocol, - Port: port.HostPort, - TargetPort: targetPort, - }) + // decide the name based on whether we saw this port before + name := strconv.Itoa(int(port.HostPort)) + if _, ok := seenPorts[int(port.HostPort)]; ok { + // https://github.com/kubernetes/kubernetes/issues/2995 + if service.ServiceType == string(api.ServiceTypeLoadBalancer) { + log.Fatalf("Service %s of type LoadBalancer cannot use TCP and UDP for the same port", name) + } + name = fmt.Sprintf("%s-%s", name, port.Protocol) } + + servicePort = api.ServicePort{ + Name: name, + Port: port.HostPort, + TargetPort: targetPort, + } + // If the default is already TCP, no need to include it. + if port.Protocol != api.ProtocolTCP { + servicePort.Protocol = port.Protocol + } + + servicePorts = append(servicePorts, servicePort) + seenPorts[int(port.HostPort)] = struct{}{} } return servicePorts } diff --git a/script/test/cmd/tests.sh b/script/test/cmd/tests.sh index f6cb3f24..7768e43e 100755 --- a/script/test/cmd/tests.sh +++ b/script/test/cmd/tests.sh @@ -137,6 +137,19 @@ sed -e "s;%VERSION%;$version;g" -e "s;%CMD%;$cmd;g" $KOMPOSE_ROOT/script/test/f convert::expect_success "kompose --provider=openshift -f $KOMPOSE_ROOT/script/test/fixtures/ports-with-proto/docker-compose.yml convert --stdout -j" "/tmp/output-os.json" +###### +# Tests related to docker-compose file in /script/test/fixtures/same-port-different-proto +# kubernetes test +cmd="kompose -f $KOMPOSE_ROOT/script/test/fixtures/same-port-different-proto/docker-compose.yml convert --stdout -j" +sed -e "s;%VERSION%;$version;g" -e "s;%CMD%;$cmd;g" $KOMPOSE_ROOT/script/test/fixtures/same-port-different-proto/output-k8s-template.json > /tmp/output-k8s.json +convert::expect_success "kompose -f $KOMPOSE_ROOT/script/test/fixtures/same-port-different-proto/docker-compose.yml convert --stdout -j" "/tmp/output-k8s.json" + +# openshift test +cmd="kompose --provider=openshift -f $KOMPOSE_ROOT/script/test/fixtures/same-port-different-proto/docker-compose.yml convert --stdout -j" +sed -e "s;%VERSION%;$version;g" -e "s;%CMD%;$cmd;g" $KOMPOSE_ROOT/script/test/fixtures/same-port-different-proto/output-os-template.json > /tmp/output-os.json +convert::expect_success "kompose --provider=openshift -f $KOMPOSE_ROOT/script/test/fixtures/same-port-different-proto/docker-compose.yml convert --stdout -j" "/tmp/output-os.json" + + ###### # Tests related to docker-compose file in /script/test/fixtures/volume-mounts/simple-vol-mounts # kubernetes test @@ -172,7 +185,7 @@ convert::expect_success "kompose -f $KOMPOSE_ROOT/script/test/fixtures/volume-mo # openshift test cmd="kompose --provider=openshift -f $KOMPOSE_ROOT/script/test/fixtures/volume-mounts/tmpfs/docker-compose.yml convert --stdout -j" sed -e "s;%VERSION%;$version;g" -e "s;%CMD%;$cmd;g" $KOMPOSE_ROOT/script/test/fixtures/volume-mounts/tmpfs/output-os-template.json > /tmp/output-os.json -convert::expect_success "kompose --provider=openshift -f $KOMPOSE_ROOT/script/test/fixtures/volume-mounts/tmpfs/docker-compose.yml convert --stdout -j" "/tmp/output-os.json" +convert::expect_success "kompose --provider=openshift -f $KOMPOSE_ROOT/script/test/fixtures/volume-mounts/tmpfs/docker-compose.yml convert --stdout -j" "/tmp/output-os.json" ###### # Tests related to docker-compose file in /script/test/fixtures/envvars-separators diff --git a/script/test/fixtures/multiple-compose-files/output-k8s-template.json b/script/test/fixtures/multiple-compose-files/output-k8s-template.json index ac88fee8..d782c95f 100644 --- a/script/test/fixtures/multiple-compose-files/output-k8s-template.json +++ b/script/test/fixtures/multiple-compose-files/output-k8s-template.json @@ -25,7 +25,7 @@ "targetPort": 9001 }, { - "name": "80", + "name": "80-TCP", "port": 80, "targetPort": 9001 } diff --git a/script/test/fixtures/multiple-compose-files/output-os-template.json b/script/test/fixtures/multiple-compose-files/output-os-template.json index 342df979..206fb4af 100644 --- a/script/test/fixtures/multiple-compose-files/output-os-template.json +++ b/script/test/fixtures/multiple-compose-files/output-os-template.json @@ -25,7 +25,7 @@ "targetPort": 9001 }, { - "name": "80", + "name": "80-TCP", "port": 80, "targetPort": 9001 } diff --git a/script/test/fixtures/same-port-different-proto/docker-compose.yml b/script/test/fixtures/same-port-different-proto/docker-compose.yml new file mode 100644 index 00000000..f33438ed --- /dev/null +++ b/script/test/fixtures/same-port-different-proto/docker-compose.yml @@ -0,0 +1,21 @@ +version: "2" + +services: + web: + image: tuna/docker-counter23 + ports: + - "5000:5000/tcp" + links: + - redis + networks: + - default + + redis: + image: redis:3.0 + networks: + - default + ports: + - "6379/tcp" + - "6379/udp" + - "1234:1235/udp" + diff --git a/script/test/fixtures/same-port-different-proto/output-k8s-template.json b/script/test/fixtures/same-port-different-proto/output-k8s-template.json new file mode 100644 index 00000000..31afe35c --- /dev/null +++ b/script/test/fixtures/same-port-different-proto/output-k8s-template.json @@ -0,0 +1,174 @@ +{ + "kind": "List", + "apiVersion": "v1", + "metadata": {}, + "items": [ + { + "kind": "Service", + "apiVersion": "v1", + "metadata": { + "name": "redis", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "redis" + }, + "annotations": { + "kompose.cmd": "%CMD%", + "kompose.version": "%VERSION%" + } + }, + "spec": { + "ports": [ + { + "name": "6379", + "port": 6379, + "targetPort": 6379 + }, + { + "name": "6379-UDP", + "protocol": "UDP", + "port": 6379, + "targetPort": 6379 + }, + + { + "name": "1234", + "protocol": "UDP", + "port": 1234, + "targetPort": 1235 + } + ], + "selector": { + "io.kompose.service": "redis" + } + }, + "status": { + "loadBalancer": {} + } + }, + { + "kind": "Service", + "apiVersion": "v1", + "metadata": { + "name": "web", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "web" + }, + "annotations": { + "kompose.cmd": "%CMD%", + "kompose.version": "%VERSION%" + } + }, + "spec": { + "ports": [ + { + "name": "5000", + "port": 5000, + "targetPort": 5000 + } + ], + "selector": { + "io.kompose.service": "web" + } + }, + "status": { + "loadBalancer": {} + } + }, + { + "kind": "Deployment", + "apiVersion": "extensions/v1beta1", + "metadata": { + "name": "redis", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "redis" + }, + "annotations": { + "kompose.cmd": "%CMD%", + "kompose.version": "%VERSION%" + } + }, + "spec": { + "replicas": 1, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "io.kompose.service": "redis" + } + }, + "spec": { + "containers": [ + { + "name": "redis", + "image": "redis:3.0", + "ports": [ + { + "containerPort": 6379 + }, + { + "containerPort": 6379, + "protocol": "UDP" + }, + { + "containerPort": 1235, + "protocol": "UDP" + } + ], + "resources": {} + } + ], + "restartPolicy": "Always" + } + }, + "strategy": {} + }, + "status": {} + }, + { + "kind": "Deployment", + "apiVersion": "extensions/v1beta1", + "metadata": { + "name": "web", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "web" + }, + "annotations": { + "kompose.cmd": "%CMD%", + "kompose.version": "%VERSION%" + } + }, + "spec": { + "replicas": 1, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "io.kompose.service": "web" + } + }, + "spec": { + "containers": [ + { + "name": "web", + "image": "tuna/docker-counter23", + "ports": [ + { + "containerPort": 5000 + } + ], + "resources": {} + } + ], + "restartPolicy": "Always" + } + }, + "strategy": {} + }, + "status": {} + } + ] +} diff --git a/script/test/fixtures/same-port-different-proto/output-os-template.json b/script/test/fixtures/same-port-different-proto/output-os-template.json new file mode 100644 index 00000000..ef98a1c2 --- /dev/null +++ b/script/test/fixtures/same-port-different-proto/output-os-template.json @@ -0,0 +1,277 @@ +{ + "kind": "List", + "apiVersion": "v1", + "metadata": {}, + "items": [ + { + "kind": "Service", + "apiVersion": "v1", + "metadata": { + "name": "redis", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "redis" + }, + "annotations": { + "kompose.cmd": "%CMD%", + "kompose.version": "%VERSION%" + } + }, + "spec": { + "ports": [ + { + "name": "6379", + "port": 6379, + "targetPort": 6379 + }, + { + "name": "6379-UDP", + "protocol": "UDP", + "port": 6379, + "targetPort": 6379 + }, + { + "name": "1234", + "protocol": "UDP", + "port": 1234, + "targetPort": 1235 + } + ], + "selector": { + "io.kompose.service": "redis" + } + }, + "status": { + "loadBalancer": {} + } + }, + { + "kind": "Service", + "apiVersion": "v1", + "metadata": { + "name": "web", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "web" + }, + "annotations": { + "kompose.cmd": "%CMD%", + "kompose.version": "%VERSION%" + } + }, + "spec": { + "ports": [ + { + "name": "5000", + "port": 5000, + "targetPort": 5000 + } + ], + "selector": { + "io.kompose.service": "web" + } + }, + "status": { + "loadBalancer": {} + } + }, + { + "kind": "DeploymentConfig", + "apiVersion": "v1", + "metadata": { + "name": "redis", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "redis" + }, + "annotations": { + "kompose.cmd": "%CMD%", + "kompose.version": "%VERSION%" + } + }, + "spec": { + "strategy": { + "resources": {} + }, + "triggers": [ + { + "type": "ConfigChange" + }, + { + "type": "ImageChange", + "imageChangeParams": { + "automatic": true, + "containerNames": [ + "redis" + ], + "from": { + "kind": "ImageStreamTag", + "name": "redis:3.0" + } + } + } + ], + "replicas": 1, + "test": false, + "selector": { + "io.kompose.service": "redis" + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "io.kompose.service": "redis" + } + }, + "spec": { + "containers": [ + { + "name": "redis", + "image": " ", + "ports": [ + { + "containerPort": 6379 + }, + { + "containerPort": 6379, + "protocol": "UDP" + }, + { + "containerPort": 1235, + "protocol": "UDP" + } + ], + "resources": {} + } + ], + "restartPolicy": "Always" + } + } + }, + "status": {} + }, + { + "kind": "ImageStream", + "apiVersion": "v1", + "metadata": { + "name": "redis", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "redis" + } + }, + "spec": { + "tags": [ + { + "name": "3.0", + "annotations": null, + "from": { + "kind": "DockerImage", + "name": "redis:3.0" + }, + "generation": null, + "importPolicy": {} + } + ] + }, + "status": { + "dockerImageRepository": "" + } + }, + { + "kind": "DeploymentConfig", + "apiVersion": "v1", + "metadata": { + "name": "web", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "web" + }, + "annotations": { + "kompose.cmd": "%CMD%", + "kompose.version": "%VERSION%" + } + }, + "spec": { + "strategy": { + "resources": {} + }, + "triggers": [ + { + "type": "ConfigChange" + }, + { + "type": "ImageChange", + "imageChangeParams": { + "automatic": true, + "containerNames": [ + "web" + ], + "from": { + "kind": "ImageStreamTag", + "name": "web:latest" + } + } + } + ], + "replicas": 1, + "test": false, + "selector": { + "io.kompose.service": "web" + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "io.kompose.service": "web" + } + }, + "spec": { + "containers": [ + { + "name": "web", + "image": " ", + "ports": [ + { + "containerPort": 5000 + } + ], + "resources": {} + } + ], + "restartPolicy": "Always" + } + } + }, + "status": {} + }, + { + "kind": "ImageStream", + "apiVersion": "v1", + "metadata": { + "name": "web", + "creationTimestamp": null, + "labels": { + "io.kompose.service": "web" + } + }, + "spec": { + "tags": [ + { + "name": "latest", + "annotations": null, + "from": { + "kind": "DockerImage", + "name": "tuna/docker-counter23" + }, + "generation": null, + "importPolicy": {} + } + ] + }, + "status": { + "dockerImageRepository": "" + } + } + ] +}