/
service_options.go
140 lines (128 loc) · 3.74 KB
/
service_options.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package container
import (
"os"
"strconv"
"strings"
"time"
"github.com/docker/docker/api/types/mount"
"github.com/docker/docker/api/types/swarm"
)
// ServiceOptions is a simplify version of swarm.ServiceSpec.
type ServiceOptions struct {
Image string
Namespace []string
Ports []Port
Mounts []Mount
Env []string // TODO: should be transform to map[string]string and use the func mapToEnv
Args []string
Command string
Networks []Network
Labels map[string]string
StopGracePeriod *time.Duration
}
// Network keeps the network info for service.
type Network struct {
// ID of the docker network.
ID string
// Alias is an optional attribute to name this service in the
// network and be able to access to it using this name.
Alias string
}
// Port is a simplify version of swarm.PortConfig.
type Port struct {
Target uint32
Published uint32
}
// Mount is a simplify version of mount.Mount.
type Mount struct {
Source string
Target string
Bind bool
}
func (options *ServiceOptions) toSwarmServiceSpec(c *DockerContainer) swarm.ServiceSpec {
namespace := c.Namespace(options.Namespace)
return swarm.ServiceSpec{
Annotations: swarm.Annotations{
Name: namespace,
Labels: mergeLabels(options.Labels, map[string]string{
"com.docker.stack.namespace": namespace,
"com.docker.stack.image": options.Image,
}),
},
TaskTemplate: swarm.TaskSpec{
ContainerSpec: &swarm.ContainerSpec{
Image: options.Image,
Labels: map[string]string{
"com.docker.stack.namespace": namespace,
},
Env: options.Env,
Args: options.Args,
Command: strings.Fields(options.Command),
Mounts: options.swarmMounts(false),
StopGracePeriod: options.StopGracePeriod,
},
Networks: options.swarmNetworks(),
},
EndpointSpec: &swarm.EndpointSpec{
Ports: options.swarmPorts(),
},
}
}
func (options *ServiceOptions) swarmPorts() []swarm.PortConfig {
ports := make([]swarm.PortConfig, len(options.Ports))
for i, p := range options.Ports {
ports[i] = swarm.PortConfig{
Protocol: swarm.PortConfigProtocolTCP,
PublishMode: swarm.PortConfigPublishModeIngress,
TargetPort: p.Target,
PublishedPort: p.Published,
}
}
return ports
}
func (options *ServiceOptions) swarmMounts(force bool) []mount.Mount {
// TOFIX: hack to prevent mount when in CircleCI (Mount in CircleCI doesn't work). Should use CircleCi with machine to fix this.
circleCI, errCircle := strconv.ParseBool(os.Getenv("CIRCLECI"))
if !force && errCircle == nil && circleCI {
return nil
}
mounts := make([]mount.Mount, len(options.Mounts))
for i, m := range options.Mounts {
mountType := mount.TypeVolume
if m.Bind {
mountType = mount.TypeBind
}
mounts[i] = mount.Mount{
Source: m.Source,
Target: m.Target,
Type: mountType,
}
}
return mounts
}
// swarmNetworks creates all necessary network attachment configurations for service.
// each network will be attached based on their networkID and an alias can be used to
// identify service in the network.
// aliases will make services accessible from other containers inside the same network.
func (options *ServiceOptions) swarmNetworks() (networks []swarm.NetworkAttachmentConfig) {
networks = make([]swarm.NetworkAttachmentConfig, len(options.Networks))
for i, network := range options.Networks {
cfg := swarm.NetworkAttachmentConfig{
Target: network.ID,
}
if network.Alias != "" {
cfg.Aliases = []string{network.Alias}
}
networks[i] = cfg
}
return networks
}
func mergeLabels(l1 map[string]string, l2 map[string]string) map[string]string {
if l1 == nil {
l1 = make(map[string]string)
}
for k, v := range l2 {
l1[k] = v
}
return l1
}