Browse Source

Support exposing ports and cross-service communication

Signed-off-by: Guillaume Tardif <[email protected]>
Guillaume Tardif 5 years ago
parent
commit
e159e82ecc

+ 23 - 24
kube/charts/kubernetes/kube.go

@@ -33,6 +33,12 @@ import (
 	"k8s.io/apimachinery/pkg/util/intstr"
 )
 
+const (
+	headlessPort      = 55555
+	headlessName      = "headless"
+	clusterIPHeadless = "None"
+)
+
 //MapToKubernetesObjects maps compose project to Kubernetes objects
 func MapToKubernetesObjects(project *types.Project) (map[string]runtime.Object, error) {
 	objects := map[string]runtime.Object{}
@@ -70,7 +76,12 @@ func MapToKubernetesObjects(project *types.Project) (map[string]runtime.Object,
 
 func mapToService(project *types.Project, service types.ServiceConfig) *core.Service {
 	ports := []core.ServicePort{}
+	serviceType := core.ServiceTypeClusterIP
+	clusterIP := ""
 	for _, p := range service.Ports {
+		if p.Published != 0 {
+			serviceType = core.ServiceTypeLoadBalancer
+		}
 		ports = append(ports,
 			core.ServicePort{
 				Name:       fmt.Sprintf("%d-%s", p.Target, strings.ToLower(p.Protocol)),
@@ -79,8 +90,14 @@ func mapToService(project *types.Project, service types.ServiceConfig) *core.Ser
 				Protocol:   toProtocol(p.Protocol),
 			})
 	}
-	if len(ports) == 0 {
-		return nil
+	if len(ports) == 0 { // headless service
+		clusterIP = clusterIPHeadless
+		ports = append(ports, core.ServicePort{
+			Name:       headlessName,
+			Port:       headlessPort,
+			TargetPort: intstr.FromInt(headlessPort),
+			Protocol:   core.ProtocolTCP,
+		})
 	}
 	return &core.Service{
 		TypeMeta: meta.TypeMeta{
@@ -91,32 +108,14 @@ func mapToService(project *types.Project, service types.ServiceConfig) *core.Ser
 			Name: service.Name,
 		},
 		Spec: core.ServiceSpec{
-			Selector: map[string]string{"com.docker.compose.service": service.Name},
-			Ports:    ports,
-			Type:     mapServiceToServiceType(project, service),
+			ClusterIP: clusterIP,
+			Selector:  map[string]string{"com.docker.compose.service": service.Name},
+			Ports:     ports,
+			Type:      serviceType,
 		},
 	}
 }
 
-func mapServiceToServiceType(project *types.Project, service types.ServiceConfig) core.ServiceType {
-	serviceType := core.ServiceTypeClusterIP
-	if len(service.Networks) == 0 {
-		// service is implicitly attached to "default" network
-		serviceType = core.ServiceTypeLoadBalancer
-	}
-	for name := range service.Networks {
-		if !project.Networks[name].Internal {
-			serviceType = core.ServiceTypeLoadBalancer
-		}
-	}
-	for _, port := range service.Ports {
-		if port.Published != 0 {
-			serviceType = core.ServiceTypeNodePort
-		}
-	}
-	return serviceType
-}
-
 func mapToDeployment(project *types.Project, service types.ServiceConfig, name string) (*apps.Deployment, error) {
 	labels := map[string]string{
 		"com.docker.compose.service": service.Name,

+ 94 - 0
kube/charts/kubernetes/kube_test.go

@@ -0,0 +1,94 @@
+// +build kube
+
+/*
+   Copyright 2020 Docker Compose CLI authors
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package kubernetes
+
+import (
+	"testing"
+
+	"gotest.tools/v3/assert"
+
+	core "k8s.io/api/core/v1"
+	meta "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/util/intstr"
+)
+
+func TestServiceWithExposedPort(t *testing.T) {
+	model, err := loadYAML(`
+services:
+  nginx:
+    image: nginx
+    ports:
+      - "80:80"
+`)
+	assert.NilError(t, err)
+
+	service := mapToService(model, model.Services[0])
+	assert.DeepEqual(t, *service, core.Service{
+		TypeMeta: meta.TypeMeta{
+			Kind:       "Service",
+			APIVersion: "v1",
+		},
+		ObjectMeta: meta.ObjectMeta{
+			Name: "nginx",
+		},
+		Spec: core.ServiceSpec{
+			Selector: map[string]string{"com.docker.compose.service": "nginx"},
+			Ports: []core.ServicePort{
+				{
+					Name:       "80-tcp",
+					Port:       int32(80),
+					TargetPort: intstr.FromInt(int(80)),
+					Protocol:   core.ProtocolTCP,
+				},
+			},
+			Type: core.ServiceTypeLoadBalancer,
+		}})
+}
+
+func TestServiceWithoutExposedPort(t *testing.T) {
+	model, err := loadYAML(`
+services:
+  nginx:
+    image: nginx
+`)
+	assert.NilError(t, err)
+
+	service := mapToService(model, model.Services[0])
+	assert.DeepEqual(t, *service, core.Service{
+		TypeMeta: meta.TypeMeta{
+			Kind:       "Service",
+			APIVersion: "v1",
+		},
+		ObjectMeta: meta.ObjectMeta{
+			Name: "nginx",
+		},
+		Spec: core.ServiceSpec{
+			Selector:  map[string]string{"com.docker.compose.service": "nginx"},
+			ClusterIP: "None",
+			Ports: []core.ServicePort{
+				{
+					Name:       headlessName,
+					Protocol:   core.ProtocolTCP,
+					Port:       headlessPort,
+					TargetPort: intstr.IntOrString{IntVal: 55555},
+				},
+			},
+			Type: core.ServiceTypeClusterIP,
+		}})
+}

+ 13 - 0
kube/e2e/kube-simple-demo/demo_sentences.yaml

@@ -0,0 +1,13 @@
+services:
+  db:
+    build: aci-demo/db
+    image: gtardif/sentences-db
+
+  words:
+    build: aci-demo/words
+    image: gtardif/sentences-api
+  web:
+    build: aci-demo/web
+    image: gtardif/sentences-web
+    ports:
+      - "80:80"