Skip to content
This repository was archived by the owner on Nov 27, 2023. It is now read-only.

Commit 6215445

Browse files
authored
Merge pull request #1196 from gtardif/kube_expose_LoadBalancer
Support exposing ports and cross-service communication
2 parents ef2e82b + 74c3941 commit 6215445

File tree

5 files changed

+143
-46
lines changed

5 files changed

+143
-46
lines changed

.github/labeler.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ ecs:
77
local:
88
- local/**/*
99

10+
kube:
11+
- kube/**/*
12+
1013
cli:
1114
- cli/**/*
1215

kube/charts/kubernetes/kube.go

Lines changed: 35 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"time"
2626

2727
"github.com/compose-spec/compose-go/types"
28+
"github.com/docker/compose-cli/api/compose"
2829
apps "k8s.io/api/apps/v1"
2930
core "k8s.io/api/core/v1"
3031
resource "k8s.io/apimachinery/pkg/api/resource"
@@ -33,6 +34,10 @@ import (
3334
"k8s.io/apimachinery/pkg/util/intstr"
3435
)
3536

37+
const (
38+
clusterIPHeadless = "None"
39+
)
40+
3641
//MapToKubernetesObjects maps compose project to Kubernetes objects
3742
func MapToKubernetesObjects(project *types.Project) (map[string]runtime.Object, error) {
3843
objects := map[string]runtime.Object{}
@@ -46,13 +51,13 @@ func MapToKubernetesObjects(project *types.Project) (map[string]runtime.Object,
4651
}
4752

4853
if service.Deploy != nil && service.Deploy.Mode == "global" {
49-
daemonset, err := mapToDaemonset(project, service, project.Name)
54+
daemonset, err := mapToDaemonset(project, service)
5055
if err != nil {
5156
return nil, err
5257
}
5358
objects[fmt.Sprintf("%s-daemonset.yaml", service.Name)] = daemonset
5459
} else {
55-
deployment, err := mapToDeployment(project, service, project.Name)
60+
deployment, err := mapToDeployment(project, service)
5661
if err != nil {
5762
return nil, err
5863
}
@@ -61,7 +66,7 @@ func MapToKubernetesObjects(project *types.Project) (map[string]runtime.Object,
6166
for _, vol := range service.Volumes {
6267
if vol.Type == "volume" {
6368
vol.Source = strings.ReplaceAll(vol.Source, "_", "-")
64-
objects[fmt.Sprintf("%s-persistentvolumeclaim.yaml", vol.Source)] = mapToPVC(service, vol)
69+
objects[fmt.Sprintf("%s-persistentvolumeclaim.yaml", vol.Source)] = mapToPVC(project, service, vol)
6570
}
6671
}
6772
}
@@ -70,7 +75,12 @@ func MapToKubernetesObjects(project *types.Project) (map[string]runtime.Object,
7075

7176
func mapToService(project *types.Project, service types.ServiceConfig) *core.Service {
7277
ports := []core.ServicePort{}
78+
serviceType := core.ServiceTypeClusterIP
79+
clusterIP := ""
7380
for _, p := range service.Ports {
81+
if p.Published != 0 {
82+
serviceType = core.ServiceTypeLoadBalancer
83+
}
7484
ports = append(ports,
7585
core.ServicePort{
7686
Name: fmt.Sprintf("%d-%s", p.Target, strings.ToLower(p.Protocol)),
@@ -79,8 +89,8 @@ func mapToService(project *types.Project, service types.ServiceConfig) *core.Ser
7989
Protocol: toProtocol(p.Protocol),
8090
})
8191
}
82-
if len(ports) == 0 {
83-
return nil
92+
if len(ports) == 0 { // headless service
93+
clusterIP = clusterIPHeadless
8494
}
8595
return &core.Service{
8696
TypeMeta: meta.TypeMeta{
@@ -91,46 +101,25 @@ func mapToService(project *types.Project, service types.ServiceConfig) *core.Ser
91101
Name: service.Name,
92102
},
93103
Spec: core.ServiceSpec{
94-
Selector: map[string]string{"com.docker.compose.service": service.Name},
95-
Ports: ports,
96-
Type: mapServiceToServiceType(project, service),
104+
ClusterIP: clusterIP,
105+
Selector: selectorLabels(project.Name, service.Name),
106+
Ports: ports,
107+
Type: serviceType,
97108
},
98109
}
99110
}
100111

101-
func mapServiceToServiceType(project *types.Project, service types.ServiceConfig) core.ServiceType {
102-
serviceType := core.ServiceTypeClusterIP
103-
if len(service.Networks) == 0 {
104-
// service is implicitly attached to "default" network
105-
serviceType = core.ServiceTypeLoadBalancer
106-
}
107-
for name := range service.Networks {
108-
if !project.Networks[name].Internal {
109-
serviceType = core.ServiceTypeLoadBalancer
110-
}
111-
}
112-
for _, port := range service.Ports {
113-
if port.Published != 0 {
114-
serviceType = core.ServiceTypeNodePort
115-
}
116-
}
117-
return serviceType
118-
}
119-
120-
func mapToDeployment(project *types.Project, service types.ServiceConfig, name string) (*apps.Deployment, error) {
121-
labels := map[string]string{
122-
"com.docker.compose.service": service.Name,
123-
"com.docker.compose.project": name,
124-
}
125-
podTemplate, err := toPodTemplate(project, service, labels)
126-
if err != nil {
127-
return nil, err
128-
}
112+
func mapToDeployment(project *types.Project, service types.ServiceConfig) (*apps.Deployment, error) {
113+
labels := selectorLabels(project.Name, service.Name)
129114
selector := new(meta.LabelSelector)
130115
selector.MatchLabels = make(map[string]string)
131116
for key, val := range labels {
132117
selector.MatchLabels[key] = val
133118
}
119+
podTemplate, err := toPodTemplate(project, service, labels)
120+
if err != nil {
121+
return nil, err
122+
}
134123
return &apps.Deployment{
135124
TypeMeta: meta.TypeMeta{
136125
Kind: "Deployment",
@@ -149,11 +138,15 @@ func mapToDeployment(project *types.Project, service types.ServiceConfig, name s
149138
}, nil
150139
}
151140

152-
func mapToDaemonset(project *types.Project, service types.ServiceConfig, name string) (*apps.DaemonSet, error) {
153-
labels := map[string]string{
154-
"com.docker.compose.service": service.Name,
155-
"com.docker.compose.project": name,
141+
func selectorLabels(projectName string, serviceName string) map[string]string {
142+
return map[string]string{
143+
compose.ProjectTag: projectName,
144+
compose.ServiceTag: serviceName,
156145
}
146+
}
147+
148+
func mapToDaemonset(project *types.Project, service types.ServiceConfig) (*apps.DaemonSet, error) {
149+
labels := selectorLabels(project.Name, service.Name)
157150
podTemplate, err := toPodTemplate(project, service, labels)
158151
if err != nil {
159152
return nil, err
@@ -196,7 +189,7 @@ func toDeploymentStrategy(deploy *types.DeployConfig) apps.DeploymentStrategy {
196189
}
197190
}
198191

199-
func mapToPVC(service types.ServiceConfig, vol types.ServiceVolumeConfig) runtime.Object {
192+
func mapToPVC(project *types.Project, service types.ServiceConfig, vol types.ServiceVolumeConfig) runtime.Object {
200193
rwaccess := core.ReadWriteOnce
201194
if vol.ReadOnly {
202195
rwaccess = core.ReadOnlyMany
@@ -208,7 +201,7 @@ func mapToPVC(service types.ServiceConfig, vol types.ServiceVolumeConfig) runtim
208201
},
209202
ObjectMeta: meta.ObjectMeta{
210203
Name: vol.Source,
211-
Labels: map[string]string{"com.docker.compose.service": service.Name},
204+
Labels: selectorLabels(project.Name, service.Name),
212205
},
213206
Spec: core.PersistentVolumeClaimSpec{
214207
VolumeName: vol.Source,

kube/charts/kubernetes/kube_test.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// +build kube
2+
3+
/*
4+
Copyright 2020 Docker Compose CLI authors
5+
6+
Licensed under the Apache License, Version 2.0 (the "License");
7+
you may not use this file except in compliance with the License.
8+
You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
*/
18+
19+
package kubernetes
20+
21+
import (
22+
"testing"
23+
24+
"gotest.tools/v3/assert"
25+
26+
core "k8s.io/api/core/v1"
27+
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
28+
"k8s.io/apimachinery/pkg/util/intstr"
29+
)
30+
31+
func TestServiceWithExposedPort(t *testing.T) {
32+
model, err := loadYAML(`
33+
services:
34+
nginx:
35+
image: nginx
36+
ports:
37+
- "80:80"
38+
`)
39+
assert.NilError(t, err)
40+
41+
service := mapToService(model, model.Services[0])
42+
assert.DeepEqual(t, *service, core.Service{
43+
TypeMeta: meta.TypeMeta{
44+
Kind: "Service",
45+
APIVersion: "v1",
46+
},
47+
ObjectMeta: meta.ObjectMeta{
48+
Name: "nginx",
49+
},
50+
Spec: core.ServiceSpec{
51+
Selector: map[string]string{"com.docker.compose.service": "nginx", "com.docker.compose.project": ""},
52+
Ports: []core.ServicePort{
53+
{
54+
Name: "80-tcp",
55+
Port: int32(80),
56+
TargetPort: intstr.FromInt(int(80)),
57+
Protocol: core.ProtocolTCP,
58+
},
59+
},
60+
Type: core.ServiceTypeLoadBalancer,
61+
}})
62+
}
63+
64+
func TestServiceWithoutExposedPort(t *testing.T) {
65+
model, err := loadYAML(`
66+
services:
67+
nginx:
68+
image: nginx
69+
`)
70+
assert.NilError(t, err)
71+
72+
service := mapToService(model, model.Services[0])
73+
assert.DeepEqual(t, *service, core.Service{
74+
TypeMeta: meta.TypeMeta{
75+
Kind: "Service",
76+
APIVersion: "v1",
77+
},
78+
ObjectMeta: meta.ObjectMeta{
79+
Name: "nginx",
80+
},
81+
Spec: core.ServiceSpec{
82+
Selector: map[string]string{"com.docker.compose.service": "nginx", "com.docker.compose.project": ""},
83+
ClusterIP: "None",
84+
Ports: []core.ServicePort{},
85+
Type: core.ServiceTypeClusterIP,
86+
}})
87+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
services:
2+
db:
3+
build: aci-demo/db
4+
image: gtardif/sentences-db
5+
6+
words:
7+
build: aci-demo/words
8+
image: gtardif/sentences-api
9+
web:
10+
build: aci-demo/web
11+
image: gtardif/sentences-web
12+
ports:
13+
- "80:80"

local/compose/labels.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,22 @@ package compose
1919
import (
2020
"fmt"
2121

22+
"github.com/docker/compose-cli/api/compose"
2223
"github.com/docker/docker/api/types/filters"
2324
)
2425

2526
const (
2627
containerNumberLabel = "com.docker.compose.container-number"
2728
oneoffLabel = "com.docker.compose.oneoff"
2829
slugLabel = "com.docker.compose.slug"
29-
projectLabel = "com.docker.compose.project"
30-
volumeLabel = "com.docker.compose.volume"
30+
projectLabel = compose.ProjectTag
31+
volumeLabel = compose.VolumeTag
3132
workingDirLabel = "com.docker.compose.project.working_dir"
3233
configFilesLabel = "com.docker.compose.project.config_files"
33-
serviceLabel = "com.docker.compose.service"
34+
serviceLabel = compose.ServiceTag
3435
versionLabel = "com.docker.compose.version"
3536
configHashLabel = "com.docker.compose.config-hash"
36-
networkLabel = "com.docker.compose.network"
37+
networkLabel = compose.NetworkTag
3738

3839
//ComposeVersion Compose version
3940
ComposeVersion = "1.0-alpha"

0 commit comments

Comments
 (0)