github.com/ijc/docker-app@v0.6.1-0.20181012090447-c7ca8bc483fb/internal/helm/templateconversion/convert.go (about) 1 package templateconversion 2 3 import ( 4 "regexp" 5 "sort" 6 "strconv" 7 "strings" 8 9 "github.com/docker/app/internal/helm/templatetypes" 10 v1beta2 "github.com/docker/app/internal/helm/templatev1beta2" 11 "github.com/docker/cli/cli/compose/types" 12 ) 13 14 // FromComposeConfig converts a compose.Config into a StackSpec 15 func FromComposeConfig(c *templatetypes.Config) *v1beta2.StackSpec { 16 if c == nil { 17 return nil 18 } 19 serviceConfigs := make([]v1beta2.ServiceConfig, len(c.Services)) 20 for i, s := range c.Services { 21 serviceConfigs[i] = fromComposeServiceConfig(s) 22 } 23 sort.Slice(serviceConfigs, func(i, j int) bool { 24 return strings.Compare(serviceConfigs[i].Name, serviceConfigs[j].Name) < 0 25 }) 26 return &v1beta2.StackSpec{ 27 Services: serviceConfigs, 28 Secrets: fromComposeSecrets(c.Secrets), 29 Configs: fromComposeConfigs(c.Configs), 30 } 31 } 32 33 func fromComposeSecrets(s map[string]types.SecretConfig) map[string]v1beta2.SecretConfig { 34 if s == nil { 35 return nil 36 } 37 m := map[string]v1beta2.SecretConfig{} 38 for key, value := range s { 39 m[key] = v1beta2.SecretConfig{ 40 Name: templatetypes.StringTemplate{Value: value.Name}, 41 File: templatetypes.StringTemplate{Value: value.File}, 42 External: v1beta2.External{ 43 Name: value.External.Name, 44 External: value.External.External, 45 }, 46 Labels: value.Labels, 47 } 48 } 49 return m 50 } 51 52 func fromComposeConfigs(s map[string]types.ConfigObjConfig) map[string]v1beta2.ConfigObjConfig { 53 if s == nil { 54 return nil 55 } 56 m := map[string]v1beta2.ConfigObjConfig{} 57 for key, value := range s { 58 m[key] = v1beta2.ConfigObjConfig{ 59 Name: templatetypes.StringTemplate{Value: value.Name}, 60 File: templatetypes.StringTemplate{Value: value.File}, 61 External: v1beta2.External{ 62 Name: value.External.Name, 63 External: value.External.External, 64 }, 65 Labels: value.Labels, 66 } 67 } 68 return m 69 } 70 71 func fromComposeServiceConfig(s templatetypes.ServiceConfig) v1beta2.ServiceConfig { 72 var userID *int64 73 if s.User.Value != "" { 74 numerical, err := strconv.Atoi(s.User.Value) 75 if err == nil { 76 unixUserID := int64(numerical) 77 userID = &unixUserID 78 } 79 } 80 return v1beta2.ServiceConfig{ 81 Name: s.Name, 82 CapAdd: s.CapAdd, 83 CapDrop: s.CapDrop, 84 Command: s.Command, 85 Configs: fromComposeServiceConfigs(s.Configs), 86 Deploy: v1beta2.DeployConfig{ 87 Mode: s.Deploy.Mode, 88 Replicas: s.Deploy.Replicas, 89 Labels: s.Deploy.Labels, 90 UpdateConfig: fromComposeUpdateConfig(s.Deploy.UpdateConfig), 91 Resources: fromComposeResources(s.Deploy.Resources), 92 RestartPolicy: fromComposeRestartPolicy(s.Deploy.RestartPolicy), 93 Placement: fromComposePlacement(s.Deploy.Placement), 94 }, 95 Entrypoint: s.Entrypoint, 96 Environment: s.Environment, 97 ExtraHosts: s.ExtraHosts, 98 Hostname: s.Hostname, 99 HealthCheck: fromComposeHealthcheck(s.HealthCheck), 100 Image: s.Image, 101 Ipc: s.Ipc, 102 Labels: s.Labels, 103 Pid: s.Pid, 104 Ports: fromComposePorts(s.Ports), 105 Privileged: s.Privileged, 106 ReadOnly: s.ReadOnly, 107 Secrets: fromComposeServiceSecrets(s.Secrets), 108 StdinOpen: s.StdinOpen, 109 StopGracePeriod: s.StopGracePeriod, 110 Tmpfs: s.Tmpfs, 111 Tty: s.Tty, 112 User: userID, 113 Volumes: fromComposeServiceVolumeConfig(s.Volumes), 114 WorkingDir: s.WorkingDir, 115 } 116 } 117 118 func fromComposePorts(ports []templatetypes.ServicePortConfig) []v1beta2.ServicePortConfig { 119 if ports == nil { 120 return nil 121 } 122 p := make([]v1beta2.ServicePortConfig, len(ports)) 123 for i, port := range ports { 124 p[i] = v1beta2.ServicePortConfig{ 125 Mode: port.Mode, 126 Target: port.Target, 127 Published: port.Published, 128 Protocol: port.Protocol, 129 } 130 } 131 return p 132 } 133 134 func fromComposeServiceSecrets(secrets []templatetypes.ServiceSecretConfig) []v1beta2.ServiceSecretConfig { 135 if secrets == nil { 136 return nil 137 } 138 c := make([]v1beta2.ServiceSecretConfig, len(secrets)) 139 for i, secret := range secrets { 140 c[i] = v1beta2.ServiceSecretConfig{ 141 Source: secret.Source, 142 Target: secret.Target, 143 UID: secret.UID, 144 Mode: secret.Mode, 145 } 146 } 147 return c 148 } 149 150 func fromComposeServiceConfigs(configs []templatetypes.ServiceConfigObjConfig) []v1beta2.ServiceConfigObjConfig { 151 if configs == nil { 152 return nil 153 } 154 c := make([]v1beta2.ServiceConfigObjConfig, len(configs)) 155 for i, config := range configs { 156 c[i] = v1beta2.ServiceConfigObjConfig{ 157 Source: config.Source, 158 Target: config.Target, 159 UID: config.UID, 160 Mode: config.Mode, 161 } 162 } 163 return c 164 } 165 166 func fromComposeHealthcheck(h *templatetypes.HealthCheckConfig) *v1beta2.HealthCheckConfig { 167 if h == nil { 168 return nil 169 } 170 return &v1beta2.HealthCheckConfig{ 171 Test: h.Test, 172 Timeout: h.Timeout, 173 Interval: h.Interval, 174 Retries: h.Retries, 175 } 176 } 177 178 func fromComposePlacement(p types.Placement) v1beta2.Placement { 179 return v1beta2.Placement{ 180 Constraints: fromComposeConstraints(p.Constraints), 181 } 182 } 183 184 var constraintEquals = regexp.MustCompile(`([\w\.]*)\W*(==|!=)\W*([\w\.]*)`) 185 186 const ( 187 swarmOs = "node.platform.os" 188 swarmArch = "node.platform.arch" 189 swarmHostname = "node.hostname" 190 swarmLabelPrefix = "node.labels." 191 ) 192 193 func fromComposeConstraints(s []string) *v1beta2.Constraints { 194 if len(s) == 0 { 195 return nil 196 } 197 constraints := &v1beta2.Constraints{} 198 for _, constraint := range s { 199 matches := constraintEquals.FindStringSubmatch(constraint) 200 if len(matches) == 4 { 201 key := matches[1] 202 operator := matches[2] 203 value := matches[3] 204 constraint := &v1beta2.Constraint{ 205 Operator: operator, 206 Value: value, 207 } 208 switch { 209 case key == swarmOs: 210 constraints.OperatingSystem = constraint 211 case key == swarmArch: 212 constraints.Architecture = constraint 213 case key == swarmHostname: 214 constraints.Hostname = constraint 215 case strings.HasPrefix(key, swarmLabelPrefix): 216 if constraints.MatchLabels == nil { 217 constraints.MatchLabels = map[string]v1beta2.Constraint{} 218 } 219 constraints.MatchLabels[strings.TrimPrefix(key, swarmLabelPrefix)] = *constraint 220 } 221 } 222 } 223 return constraints 224 } 225 226 func fromComposeResources(r templatetypes.Resources) v1beta2.Resources { 227 return v1beta2.Resources{ 228 Limits: fromComposeResourcesResource(r.Limits), 229 Reservations: fromComposeResourcesResource(r.Reservations), 230 } 231 } 232 233 func fromComposeResourcesResource(r *templatetypes.Resource) *v1beta2.Resource { 234 if r == nil { 235 return nil 236 } 237 return &v1beta2.Resource{ 238 MemoryBytes: r.MemoryBytes, 239 NanoCPUs: r.NanoCPUs, 240 } 241 } 242 243 func fromComposeUpdateConfig(u *templatetypes.UpdateConfig) *v1beta2.UpdateConfig { 244 if u == nil { 245 return nil 246 } 247 return &v1beta2.UpdateConfig{ 248 Parallelism: u.Parallelism, 249 } 250 } 251 252 func fromComposeRestartPolicy(r *types.RestartPolicy) *v1beta2.RestartPolicy { 253 if r == nil { 254 return nil 255 } 256 return &v1beta2.RestartPolicy{ 257 Condition: r.Condition, 258 } 259 } 260 261 func fromComposeServiceVolumeConfig(vs []templatetypes.ServiceVolumeConfig) []v1beta2.ServiceVolumeConfig { 262 if vs == nil { 263 return nil 264 } 265 volumes := []v1beta2.ServiceVolumeConfig{} 266 for _, v := range vs { 267 volumes = append(volumes, v1beta2.ServiceVolumeConfig{ 268 Type: v.Type, 269 Source: v.Source, 270 Target: v.Target, 271 ReadOnly: v.ReadOnly, 272 }) 273 } 274 return volumes 275 }