1/* 2 Copyright 2020 The Compose Specification Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15*/ 16 17package types 18 19import ( 20 "encoding/json" 21 "fmt" 22 "sort" 23 "strings" 24 "time" 25 26 "github.com/docker/go-connections/nat" 27) 28 29// Duration is a thin wrapper around time.Duration with improved JSON marshalling 30type Duration time.Duration 31 32func (d Duration) String() string { 33 return time.Duration(d).String() 34} 35 36// ConvertDurationPtr converts a typedefined Duration pointer to a time.Duration pointer with the same value. 37func ConvertDurationPtr(d *Duration) *time.Duration { 38 if d == nil { 39 return nil 40 } 41 res := time.Duration(*d) 42 return &res 43} 44 45// MarshalJSON makes Duration implement json.Marshaler 46func (d Duration) MarshalJSON() ([]byte, error) { 47 return json.Marshal(d.String()) 48} 49 50// MarshalYAML makes Duration implement yaml.Marshaler 51func (d Duration) MarshalYAML() (interface{}, error) { 52 return d.String(), nil 53} 54 55func (d *Duration) UnmarshalJSON(b []byte) error { 56 s := strings.Trim(string(b), "\"") 57 timeDuration, err := time.ParseDuration(s) 58 if err != nil { 59 return err 60 } 61 *d = Duration(timeDuration) 62 return nil 63} 64 65// Services is a list of ServiceConfig 66type Services []ServiceConfig 67 68// MarshalYAML makes Services implement yaml.Marshaller 69func (s Services) MarshalYAML() (interface{}, error) { 70 services := map[string]ServiceConfig{} 71 for _, service := range s { 72 services[service.Name] = service 73 } 74 return services, nil 75} 76 77// MarshalJSON makes Services implement json.Marshaler 78func (s Services) MarshalJSON() ([]byte, error) { 79 data, err := s.MarshalYAML() 80 if err != nil { 81 return nil, err 82 } 83 return json.MarshalIndent(data, "", " ") 84} 85 86// ServiceConfig is the configuration of one service 87type ServiceConfig struct { 88 Name string `yaml:"-" json:"-"` 89 Profiles []string `mapstructure:"profiles" yaml:"profiles,omitempty" json:"profiles,omitempty"` 90 91 Build *BuildConfig `yaml:",omitempty" json:"build,omitempty"` 92 BlkioConfig *BlkioConfig `yaml:",omitempty" json:"blkio_config,omitempty"` 93 CapAdd []string `mapstructure:"cap_add" yaml:"cap_add,omitempty" json:"cap_add,omitempty"` 94 CapDrop []string `mapstructure:"cap_drop" yaml:"cap_drop,omitempty" json:"cap_drop,omitempty"` 95 CgroupParent string `mapstructure:"cgroup_parent" yaml:"cgroup_parent,omitempty" json:"cgroup_parent,omitempty"` 96 CPUCount int64 `mapstructure:"cpu_count" yaml:"cpu_count,omitempty" json:"cpu_count,omitempty"` 97 CPUPercent float32 `mapstructure:"cpu_percent" yaml:"cpu_percent,omitempty" json:"cpu_percent,omitempty"` 98 CPUPeriod int64 `mapstructure:"cpu_period" yaml:"cpu_period,omitempty" json:"cpu_period,omitempty"` 99 CPUQuota int64 `mapstructure:"cpu_quota" yaml:"cpu_quota,omitempty" json:"cpu_quota,omitempty"` 100 CPURTPeriod int64 `mapstructure:"cpu_rt_period" yaml:"cpu_rt_period,omitempty" json:"cpu_rt_period,omitempty"` 101 CPURTRuntime int64 `mapstructure:"cpu_rt_runtime" yaml:"cpu_rt_runtime,omitempty" json:"cpu_rt_runtime,omitempty"` 102 CPUS float32 `mapstructure:"cpus" yaml:"cpus,omitempty" json:"cpus,omitempty"` 103 CPUSet string `mapstructure:"cpuset" yaml:"cpuset,omitempty" json:"cpuset,omitempty"` 104 CPUShares int64 `mapstructure:"cpu_shares" yaml:"cpu_shares,omitempty" json:"cpu_shares,omitempty"` 105 Command ShellCommand `yaml:",omitempty" json:"command,omitempty"` 106 Configs []ServiceConfigObjConfig `yaml:",omitempty" json:"configs,omitempty"` 107 ContainerName string `mapstructure:"container_name" yaml:"container_name,omitempty" json:"container_name,omitempty"` 108 CredentialSpec *CredentialSpecConfig `mapstructure:"credential_spec" yaml:"credential_spec,omitempty" json:"credential_spec,omitempty"` 109 DependsOn DependsOnConfig `mapstructure:"depends_on" yaml:"depends_on,omitempty" json:"depends_on,omitempty"` 110 Deploy *DeployConfig `yaml:",omitempty" json:"deploy,omitempty"` 111 Devices []string `yaml:",omitempty" json:"devices,omitempty"` 112 DNS StringList `yaml:",omitempty" json:"dns,omitempty"` 113 DNSOpts []string `mapstructure:"dns_opt" yaml:"dns_opt,omitempty" json:"dns_opt,omitempty"` 114 DNSSearch StringList `mapstructure:"dns_search" yaml:"dns_search,omitempty" json:"dns_search,omitempty"` 115 Dockerfile string `yaml:"dockerfile,omitempty" json:"dockerfile,omitempty"` 116 DomainName string `mapstructure:"domainname" yaml:"domainname,omitempty" json:"domainname,omitempty"` 117 Entrypoint ShellCommand `yaml:",omitempty" json:"entrypoint,omitempty"` 118 Environment MappingWithEquals `yaml:",omitempty" json:"environment,omitempty"` 119 EnvFile StringList `mapstructure:"env_file" yaml:"env_file,omitempty" json:"env_file,omitempty"` 120 Expose StringOrNumberList `yaml:",omitempty" json:"expose,omitempty"` 121 Extends ExtendsConfig `yaml:"extends,omitempty" json:"extends,omitempty"` 122 ExternalLinks []string `mapstructure:"external_links" yaml:"external_links,omitempty" json:"external_links,omitempty"` 123 ExtraHosts HostsList `mapstructure:"extra_hosts" yaml:"extra_hosts,omitempty" json:"extra_hosts,omitempty"` 124 GroupAdd []string `mapstructure:"group_app" yaml:"group_add,omitempty" json:"group_add,omitempty"` 125 Hostname string `yaml:",omitempty" json:"hostname,omitempty"` 126 HealthCheck *HealthCheckConfig `yaml:",omitempty" json:"healthcheck,omitempty"` 127 Image string `yaml:",omitempty" json:"image,omitempty"` 128 Init *bool `yaml:",omitempty" json:"init,omitempty"` 129 Ipc string `yaml:",omitempty" json:"ipc,omitempty"` 130 Isolation string `mapstructure:"isolation" yaml:"isolation,omitempty" json:"isolation,omitempty"` 131 Labels Labels `yaml:",omitempty" json:"labels,omitempty"` 132 Links []string `yaml:",omitempty" json:"links,omitempty"` 133 Logging *LoggingConfig `yaml:",omitempty" json:"logging,omitempty"` 134 LogDriver string `mapstructure:"log_driver" yaml:"log_driver,omitempty" json:"log_driver,omitempty"` 135 LogOpt map[string]string `mapstructure:"log_opt" yaml:"log_opt,omitempty" json:"log_opt,omitempty"` 136 MemLimit UnitBytes `mapstructure:"mem_limit" yaml:"mem_limit,omitempty" json:"mem_limit,omitempty"` 137 MemReservation UnitBytes `mapstructure:"mem_reservation" yaml:"mem_reservation,omitempty" json:"mem_reservation,omitempty"` 138 MemSwapLimit UnitBytes `mapstructure:"memswap_limit" yaml:"memswap_limit,omitempty" json:"memswap_limit,omitempty"` 139 MemSwappiness UnitBytes `mapstructure:"mem_swappiness" yaml:"mem_swappiness,omitempty" json:"mem_swappiness,omitempty"` 140 MacAddress string `mapstructure:"mac_address" yaml:"mac_address,omitempty" json:"mac_address,omitempty"` 141 Net string `yaml:"net,omitempty" json:"net,omitempty"` 142 NetworkMode string `mapstructure:"network_mode" yaml:"network_mode,omitempty" json:"network_mode,omitempty"` 143 Networks map[string]*ServiceNetworkConfig `yaml:",omitempty" json:"networks,omitempty"` 144 OomKillDisable bool `mapstructure:"oom_kill_disable" yaml:"oom_kill_disable,omitempty" json:"oom_kill_disable,omitempty"` 145 OomScoreAdj int64 `mapstructure:"oom_score_adj" yaml:"oom_score_adj,omitempty" json:"oom_score_adj,omitempty"` 146 Pid string `yaml:",omitempty" json:"pid,omitempty"` 147 PidsLimit int64 `mapstructure:"pids_limit" yaml:"pids_limit,omitempty" json:"pids_limit,omitempty"` 148 Platform string `yaml:",omitempty" json:"platform,omitempty"` 149 Ports []ServicePortConfig `yaml:",omitempty" json:"ports,omitempty"` 150 Privileged bool `yaml:",omitempty" json:"privileged,omitempty"` 151 PullPolicy string `mapstructure:"pull_policy" yaml:"pull_policy,omitempty" json:"pull_policy,omitempty"` 152 ReadOnly bool `mapstructure:"read_only" yaml:"read_only,omitempty" json:"read_only,omitempty"` 153 Restart string `yaml:",omitempty" json:"restart,omitempty"` 154 Runtime string `yaml:",omitempty" json:"runtime,omitempty"` 155 Scale int `yaml:"-" json:"-"` 156 Secrets []ServiceSecretConfig `yaml:",omitempty" json:"secrets,omitempty"` 157 SecurityOpt []string `mapstructure:"security_opt" yaml:"security_opt,omitempty" json:"security_opt,omitempty"` 158 ShmSize UnitBytes `mapstructure:"shm_size" yaml:"shm_size,omitempty" json:"shm_size,omitempty"` 159 StdinOpen bool `mapstructure:"stdin_open" yaml:"stdin_open,omitempty" json:"stdin_open,omitempty"` 160 StopGracePeriod *Duration `mapstructure:"stop_grace_period" yaml:"stop_grace_period,omitempty" json:"stop_grace_period,omitempty"` 161 StopSignal string `mapstructure:"stop_signal" yaml:"stop_signal,omitempty" json:"stop_signal,omitempty"` 162 Sysctls Mapping `yaml:",omitempty" json:"sysctls,omitempty"` 163 Tmpfs StringList `yaml:",omitempty" json:"tmpfs,omitempty"` 164 Tty bool `mapstructure:"tty" yaml:"tty,omitempty" json:"tty,omitempty"` 165 Ulimits map[string]*UlimitsConfig `yaml:",omitempty" json:"ulimits,omitempty"` 166 User string `yaml:",omitempty" json:"user,omitempty"` 167 UserNSMode string `mapstructure:"userns_mode" yaml:"userns_mode,omitempty" json:"userns_mode,omitempty"` 168 Uts string `yaml:"uts,omitempty" json:"uts,omitempty"` 169 VolumeDriver string `mapstructure:"volume_driver" yaml:"volume_driver,omitempty" json:"volume_driver,omitempty"` 170 Volumes []ServiceVolumeConfig `yaml:",omitempty" json:"volumes,omitempty"` 171 VolumesFrom []string `mapstructure:"volumes_from" yaml:"volumes_from,omitempty" json:"volumes_from,omitempty"` 172 WorkingDir string `mapstructure:"working_dir" yaml:"working_dir,omitempty" json:"working_dir,omitempty"` 173 174 Extensions map[string]interface{} `yaml:",inline" json:"-"` 175} 176 177// NetworksByPriority return the service networks IDs sorted according to Priority 178func (s *ServiceConfig) NetworksByPriority() []string { 179 type key struct { 180 name string 181 priority int 182 } 183 var keys []key 184 for k, v := range s.Networks { 185 priority := 0 186 if v != nil { 187 priority = v.Priority 188 } 189 keys = append(keys, key{ 190 name: k, 191 priority: priority, 192 }) 193 } 194 sort.Slice(keys, func(i, j int) bool { 195 return keys[i].priority > keys[j].priority 196 }) 197 var sorted []string 198 for _, k := range keys { 199 sorted = append(sorted, k.name) 200 } 201 return sorted 202} 203 204const ( 205 //PullPolicyAlways always pull images 206 PullPolicyAlways = "always" 207 //PullPolicyNever never pull images 208 PullPolicyNever = "never" 209 //PullPolicyIfNotPresent pull missing images 210 PullPolicyIfNotPresent = "if_not_present" 211 //PullPolicyIfNotPresent pull missing images 212 PullPolicyMissing = "missing" 213 //PullPolicyBuild force building images 214 PullPolicyBuild = "build" 215) 216 217const ( 218 //RestartPolicyAlways always restart the container if it stops 219 RestartPolicyAlways = "always" 220 //RestartPolicyOnFailure restart the container if it exits due to an error 221 RestartPolicyOnFailure = "on-failure" 222 //RestartPolicyNo do not automatically restart the container 223 RestartPolicyNo = "no" 224 //RestartPolicyUnlessStopped always restart the container unless the container is stopped (manually or otherwise) 225 RestartPolicyUnlessStopped = "unless-stopped" 226) 227 228const ( 229 // ServicePrefix is the prefix for references pointing to a service 230 ServicePrefix = "service:" 231 // ContainerPrefix is the prefix for references pointing to a container 232 ContainerPrefix = "container:" 233 234 // NetworkModeServicePrefix is the prefix for network_mode pointing to a service 235 // Deprecated prefer ServicePrefix 236 NetworkModeServicePrefix = ServicePrefix 237 // NetworkModeContainerPrefix is the prefix for network_mode pointing to a container 238 // Deprecated prefer ContainerPrefix 239 NetworkModeContainerPrefix = ContainerPrefix 240) 241 242// GetDependencies retrieve all services this service depends on 243func (s ServiceConfig) GetDependencies() []string { 244 dependencies := make(set) 245 for dependency := range s.DependsOn { 246 dependencies.append(dependency) 247 } 248 for _, link := range s.Links { 249 parts := strings.Split(link, ":") 250 if len(parts) == 2 { 251 dependencies.append(parts[0]) 252 } else { 253 dependencies.append(link) 254 } 255 } 256 if strings.HasPrefix(s.NetworkMode, ServicePrefix) { 257 dependencies.append(s.NetworkMode[len(ServicePrefix):]) 258 } 259 if strings.HasPrefix(s.Ipc, ServicePrefix) { 260 dependencies.append(s.Ipc[len(ServicePrefix):]) 261 } 262 if strings.HasPrefix(s.Pid, ServicePrefix) { 263 dependencies.append(s.Pid[len(ServicePrefix):]) 264 } 265 for _, vol := range s.VolumesFrom { 266 if !strings.HasPrefix(s.Pid, ContainerPrefix) { 267 dependencies.append(vol) 268 } 269 } 270 271 return dependencies.toSlice() 272} 273 274type set map[string]struct{} 275 276func (s set) append(strings ...string) { 277 for _, str := range strings { 278 s[str] = struct{}{} 279 } 280} 281 282func (s set) toSlice() []string { 283 slice := make([]string, 0, len(s)) 284 for v := range s { 285 slice = append(slice, v) 286 } 287 return slice 288} 289 290// BuildConfig is a type for build 291type BuildConfig struct { 292 Context string `yaml:",omitempty" json:"context,omitempty"` 293 Dockerfile string `yaml:",omitempty" json:"dockerfile,omitempty"` 294 Args MappingWithEquals `yaml:",omitempty" json:"args,omitempty"` 295 Labels Labels `yaml:",omitempty" json:"labels,omitempty"` 296 CacheFrom StringList `mapstructure:"cache_from" yaml:"cache_from,omitempty" json:"cache_from,omitempty"` 297 ExtraHosts HostsList `mapstructure:"extra_hosts" yaml:"extra_hosts,omitempty" json:"extra_hosts,omitempty"` 298 Isolation string `yaml:",omitempty" json:"isolation,omitempty"` 299 Network string `yaml:",omitempty" json:"network,omitempty"` 300 Target string `yaml:",omitempty" json:"target,omitempty"` 301 302 Extensions map[string]interface{} `yaml:",inline" json:"-"` 303} 304 305// BlkioConfig define blkio config 306type BlkioConfig struct { 307 Weight uint16 `yaml:",omitempty" json:"weight,omitempty"` 308 WeightDevice []WeightDevice `yaml:",omitempty" json:"weight_device,omitempty"` 309 DeviceReadBps []ThrottleDevice `yaml:",omitempty" json:"device_read_bps,omitempty"` 310 DeviceReadIOps []ThrottleDevice `yaml:",omitempty" json:"device_read_iops,omitempty"` 311 DeviceWriteBps []ThrottleDevice `yaml:",omitempty" json:"device_write_bps,omitempty"` 312 DeviceWriteIOps []ThrottleDevice `yaml:",omitempty" json:"device_write_iops,omitempty"` 313 314 Extensions map[string]interface{} `yaml:",inline" json:"-"` 315} 316 317// WeightDevice is a structure that holds device:weight pair 318type WeightDevice struct { 319 Path string 320 Weight uint16 321 322 Extensions map[string]interface{} `yaml:",inline" json:"-"` 323} 324 325// ThrottleDevice is a structure that holds device:rate_per_second pair 326type ThrottleDevice struct { 327 Path string 328 Rate uint64 329 330 Extensions map[string]interface{} `yaml:",inline" json:"-"` 331} 332 333// ShellCommand is a string or list of string args 334type ShellCommand []string 335 336// StringList is a type for fields that can be a string or list of strings 337type StringList []string 338 339// StringOrNumberList is a type for fields that can be a list of strings or 340// numbers 341type StringOrNumberList []string 342 343// MappingWithEquals is a mapping type that can be converted from a list of 344// key[=value] strings. 345// For the key with an empty value (`key=`), the mapped value is set to a pointer to `""`. 346// For the key without value (`key`), the mapped value is set to nil. 347type MappingWithEquals map[string]*string 348 349// NewMappingWithEquals build a new Mapping from a set of KEY=VALUE strings 350func NewMappingWithEquals(values []string) MappingWithEquals { 351 mapping := MappingWithEquals{} 352 for _, env := range values { 353 tokens := strings.SplitN(env, "=", 2) 354 if len(tokens) > 1 { 355 mapping[tokens[0]] = &tokens[1] 356 } else { 357 mapping[env] = nil 358 } 359 } 360 return mapping 361} 362 363// OverrideBy update MappingWithEquals with values from another MappingWithEquals 364func (e MappingWithEquals) OverrideBy(other MappingWithEquals) MappingWithEquals { 365 for k, v := range other { 366 e[k] = v 367 } 368 return e 369} 370 371// Resolve update a MappingWithEquals for keys without value (`key`, but not `key=`) 372func (e MappingWithEquals) Resolve(lookupFn func(string) (string, bool)) MappingWithEquals { 373 for k, v := range e { 374 if v == nil { 375 if value, ok := lookupFn(k); ok { 376 e[k] = &value 377 } 378 } 379 } 380 return e 381} 382 383// RemoveEmpty excludes keys that are not associated with a value 384func (e MappingWithEquals) RemoveEmpty() MappingWithEquals { 385 for k, v := range e { 386 if v == nil { 387 delete(e, k) 388 } 389 } 390 return e 391} 392 393// Mapping is a mapping type that can be converted from a list of 394// key[=value] strings. 395// For the key with an empty value (`key=`), or key without value (`key`), the 396// mapped value is set to an empty string `""`. 397type Mapping map[string]string 398 399// NewMapping build a new Mapping from a set of KEY=VALUE strings 400func NewMapping(values []string) Mapping { 401 mapping := Mapping{} 402 for _, value := range values { 403 parts := strings.SplitN(value, "=", 2) 404 key := parts[0] 405 switch { 406 case len(parts) == 1: 407 mapping[key] = "" 408 default: 409 mapping[key] = parts[1] 410 } 411 } 412 return mapping 413} 414 415// Labels is a mapping type for labels 416type Labels map[string]string 417 418func (l Labels) Add(key, value string) Labels { 419 if l == nil { 420 l = Labels{} 421 } 422 l[key] = value 423 return l 424} 425 426// MappingWithColon is a mapping type that can be converted from a list of 427// 'key: value' strings 428type MappingWithColon map[string]string 429 430// HostsList is a list of colon-separated host-ip mappings 431type HostsList []string 432 433// LoggingConfig the logging configuration for a service 434type LoggingConfig struct { 435 Driver string `yaml:",omitempty" json:"driver,omitempty"` 436 Options map[string]string `yaml:",omitempty" json:"options,omitempty"` 437 438 Extensions map[string]interface{} `yaml:",inline" json:"-"` 439} 440 441// DeployConfig the deployment configuration for a service 442type DeployConfig struct { 443 Mode string `yaml:",omitempty" json:"mode,omitempty"` 444 Replicas *uint64 `yaml:",omitempty" json:"replicas,omitempty"` 445 Labels Labels `yaml:",omitempty" json:"labels,omitempty"` 446 UpdateConfig *UpdateConfig `mapstructure:"update_config" yaml:"update_config,omitempty" json:"update_config,omitempty"` 447 RollbackConfig *UpdateConfig `mapstructure:"rollback_config" yaml:"rollback_config,omitempty" json:"rollback_config,omitempty"` 448 Resources Resources `yaml:",omitempty" json:"resources,omitempty"` 449 RestartPolicy *RestartPolicy `mapstructure:"restart_policy" yaml:"restart_policy,omitempty" json:"restart_policy,omitempty"` 450 Placement Placement `yaml:",omitempty" json:"placement,omitempty"` 451 EndpointMode string `mapstructure:"endpoint_mode" yaml:"endpoint_mode,omitempty" json:"endpoint_mode,omitempty"` 452 453 Extensions map[string]interface{} `yaml:",inline" json:"-"` 454} 455 456// HealthCheckConfig the healthcheck configuration for a service 457type HealthCheckConfig struct { 458 Test HealthCheckTest `yaml:",omitempty" json:"test,omitempty"` 459 Timeout *Duration `yaml:",omitempty" json:"timeout,omitempty"` 460 Interval *Duration `yaml:",omitempty" json:"interval,omitempty"` 461 Retries *uint64 `yaml:",omitempty" json:"retries,omitempty"` 462 StartPeriod *Duration `mapstructure:"start_period" yaml:"start_period,omitempty" json:"start_period,omitempty"` 463 Disable bool `yaml:",omitempty" json:"disable,omitempty"` 464 465 Extensions map[string]interface{} `yaml:",inline" json:"-"` 466} 467 468// HealthCheckTest is the command run to test the health of a service 469type HealthCheckTest []string 470 471// UpdateConfig the service update configuration 472type UpdateConfig struct { 473 Parallelism *uint64 `yaml:",omitempty" json:"parallelism,omitempty"` 474 Delay Duration `yaml:",omitempty" json:"delay,omitempty"` 475 FailureAction string `mapstructure:"failure_action" yaml:"failure_action,omitempty" json:"failure_action,omitempty"` 476 Monitor Duration `yaml:",omitempty" json:"monitor,omitempty"` 477 MaxFailureRatio float32 `mapstructure:"max_failure_ratio" yaml:"max_failure_ratio,omitempty" json:"max_failure_ratio,omitempty"` 478 Order string `yaml:",omitempty" json:"order,omitempty"` 479 480 Extensions map[string]interface{} `yaml:",inline" json:"-"` 481} 482 483// Resources the resource limits and reservations 484type Resources struct { 485 Limits *Resource `yaml:",omitempty" json:"limits,omitempty"` 486 Reservations *Resource `yaml:",omitempty" json:"reservations,omitempty"` 487 488 Extensions map[string]interface{} `yaml:",inline" json:"-"` 489} 490 491// Resource is a resource to be limited or reserved 492type Resource struct { 493 // TODO: types to convert from units and ratios 494 NanoCPUs string `mapstructure:"cpus" yaml:"cpus,omitempty" json:"cpus,omitempty"` 495 MemoryBytes UnitBytes `mapstructure:"memory" yaml:"memory,omitempty" json:"memory,omitempty"` 496 Devices []DeviceRequest `mapstructure:"devices" yaml:"devices,omitempty" json:"devices,omitempty"` 497 GenericResources []GenericResource `mapstructure:"generic_resources" yaml:"generic_resources,omitempty" json:"generic_resources,omitempty"` 498 499 Extensions map[string]interface{} `yaml:",inline" json:"-"` 500} 501 502type DeviceRequest struct { 503 Capabilities []string `mapstructure:"capabilities" yaml:"capabilities,omitempty" json:"capabilities,omitempty"` 504 Driver string `mapstructure:"driver" yaml:"driver,omitempty" json:"driver,omitempty"` 505 Count int64 `mapstructure:"count" yaml:"count,omitempty" json:"count,omitempty"` 506 IDs []string `mapstructure:"device_ids" yaml:"device_ids,omitempty" json:"device_ids,omitempty"` 507} 508 509// GenericResource represents a "user defined" resource which can 510// only be an integer (e.g: SSD=3) for a service 511type GenericResource struct { 512 DiscreteResourceSpec *DiscreteGenericResource `mapstructure:"discrete_resource_spec" yaml:"discrete_resource_spec,omitempty" json:"discrete_resource_spec,omitempty"` 513 514 Extensions map[string]interface{} `yaml:",inline" json:"-"` 515} 516 517// DiscreteGenericResource represents a "user defined" resource which is defined 518// as an integer 519// "Kind" is used to describe the Kind of a resource (e.g: "GPU", "FPGA", "SSD", ...) 520// Value is used to count the resource (SSD=5, HDD=3, ...) 521type DiscreteGenericResource struct { 522 Kind string `json:"kind"` 523 Value int64 `json:"value"` 524 525 Extensions map[string]interface{} `yaml:",inline" json:"-"` 526} 527 528// UnitBytes is the bytes type 529type UnitBytes int64 530 531// MarshalYAML makes UnitBytes implement yaml.Marshaller 532func (u UnitBytes) MarshalYAML() (interface{}, error) { 533 return fmt.Sprintf("%d", u), nil 534} 535 536// MarshalJSON makes UnitBytes implement json.Marshaler 537func (u UnitBytes) MarshalJSON() ([]byte, error) { 538 return []byte(fmt.Sprintf(`"%d"`, u)), nil 539} 540 541// RestartPolicy the service restart policy 542type RestartPolicy struct { 543 Condition string `yaml:",omitempty" json:"condition,omitempty"` 544 Delay *Duration `yaml:",omitempty" json:"delay,omitempty"` 545 MaxAttempts *uint64 `mapstructure:"max_attempts" yaml:"max_attempts,omitempty" json:"max_attempts,omitempty"` 546 Window *Duration `yaml:",omitempty" json:"window,omitempty"` 547 548 Extensions map[string]interface{} `yaml:",inline" json:"-"` 549} 550 551// Placement constraints for the service 552type Placement struct { 553 Constraints []string `yaml:",omitempty" json:"constraints,omitempty"` 554 Preferences []PlacementPreferences `yaml:",omitempty" json:"preferences,omitempty"` 555 MaxReplicas uint64 `mapstructure:"max_replicas_per_node" yaml:"max_replicas_per_node,omitempty" json:"max_replicas_per_node,omitempty"` 556 557 Extensions map[string]interface{} `yaml:",inline" json:"-"` 558} 559 560// PlacementPreferences is the preferences for a service placement 561type PlacementPreferences struct { 562 Spread string `yaml:",omitempty" json:"spread,omitempty"` 563 564 Extensions map[string]interface{} `yaml:",inline" json:"-"` 565} 566 567// ServiceNetworkConfig is the network configuration for a service 568type ServiceNetworkConfig struct { 569 Priority int `yaml:",omitempty" json:"priotirt,omitempty"` 570 Aliases []string `yaml:",omitempty" json:"aliases,omitempty"` 571 Ipv4Address string `mapstructure:"ipv4_address" yaml:"ipv4_address,omitempty" json:"ipv4_address,omitempty"` 572 Ipv6Address string `mapstructure:"ipv6_address" yaml:"ipv6_address,omitempty" json:"ipv6_address,omitempty"` 573 574 Extensions map[string]interface{} `yaml:",inline" json:"-"` 575} 576 577// ServicePortConfig is the port configuration for a service 578type ServicePortConfig struct { 579 Mode string `yaml:",omitempty" json:"mode,omitempty"` 580 HostIP string `mapstructure:"host_ip" yaml:"host_ip,omitempty" json:"host_ip,omitempty"` 581 Target uint32 `yaml:",omitempty" json:"target,omitempty"` 582 Published uint32 `yaml:",omitempty" json:"published,omitempty"` 583 Protocol string `yaml:",omitempty" json:"protocol,omitempty"` 584 585 Extensions map[string]interface{} `yaml:",inline" json:"-"` 586} 587 588// ParsePortConfig parse short syntax for service port configuration 589func ParsePortConfig(value string) ([]ServicePortConfig, error) { 590 var portConfigs []ServicePortConfig 591 ports, portBindings, err := nat.ParsePortSpecs([]string{value}) 592 if err != nil { 593 return nil, err 594 } 595 // We need to sort the key of the ports to make sure it is consistent 596 keys := []string{} 597 for port := range ports { 598 keys = append(keys, string(port)) 599 } 600 sort.Strings(keys) 601 602 for _, key := range keys { 603 port := nat.Port(key) 604 converted, err := convertPortToPortConfig(port, portBindings) 605 if err != nil { 606 return nil, err 607 } 608 portConfigs = append(portConfigs, converted...) 609 } 610 return portConfigs, nil 611} 612 613func convertPortToPortConfig(port nat.Port, portBindings map[nat.Port][]nat.PortBinding) ([]ServicePortConfig, error) { 614 portConfigs := []ServicePortConfig{} 615 for _, binding := range portBindings[port] { 616 startHostPort, endHostPort, err := nat.ParsePortRange(binding.HostPort) 617 618 if err != nil && binding.HostPort != "" { 619 return nil, fmt.Errorf("invalid hostport binding (%s) for port (%s)", binding.HostPort, port.Port()) 620 } 621 622 for i := startHostPort; i <= endHostPort; i++ { 623 portConfigs = append(portConfigs, ServicePortConfig{ 624 HostIP: binding.HostIP, 625 Protocol: strings.ToLower(port.Proto()), 626 Target: uint32(port.Int()), 627 Published: uint32(i), 628 Mode: "ingress", 629 }) 630 } 631 } 632 return portConfigs, nil 633} 634 635// ServiceVolumeConfig are references to a volume used by a service 636type ServiceVolumeConfig struct { 637 Type string `yaml:",omitempty" json:"type,omitempty"` 638 Source string `yaml:",omitempty" json:"source,omitempty"` 639 Target string `yaml:",omitempty" json:"target,omitempty"` 640 ReadOnly bool `mapstructure:"read_only" yaml:"read_only,omitempty" json:"read_only,omitempty"` 641 Consistency string `yaml:",omitempty" json:"consistency,omitempty"` 642 Bind *ServiceVolumeBind `yaml:",omitempty" json:"bind,omitempty"` 643 Volume *ServiceVolumeVolume `yaml:",omitempty" json:"volume,omitempty"` 644 Tmpfs *ServiceVolumeTmpfs `yaml:",omitempty" json:"tmpfs,omitempty"` 645 646 Extensions map[string]interface{} `yaml:",inline" json:"-"` 647} 648 649const ( 650 // TypeBind is the type for mounting host dir 651 VolumeTypeBind = "bind" 652 // TypeVolume is the type for remote storage volumes 653 VolumeTypeVolume = "volume" 654 // TypeTmpfs is the type for mounting tmpfs 655 VolumeTypeTmpfs = "tmpfs" 656 // TypeNamedPipe is the type for mounting Windows named pipes 657 VolumeTypeNamedPipe = "npipe" 658) 659 660// ServiceVolumeBind are options for a service volume of type bind 661type ServiceVolumeBind struct { 662 Propagation string `yaml:",omitempty" json:"propagation,omitempty"` 663 CreateHostPath bool `mapstructure:"create_host_path" yaml:"create_host_path,omitempty" json:"create_host_path,omitempty"` 664 665 Extensions map[string]interface{} `yaml:",inline" json:"-"` 666} 667 668// Propagation represents the propagation of a mount. 669const ( 670 // PropagationRPrivate RPRIVATE 671 PropagationRPrivate string = "rprivate" 672 // PropagationPrivate PRIVATE 673 PropagationPrivate string = "private" 674 // PropagationRShared RSHARED 675 PropagationRShared string = "rshared" 676 // PropagationShared SHARED 677 PropagationShared string = "shared" 678 // PropagationRSlave RSLAVE 679 PropagationRSlave string = "rslave" 680 // PropagationSlave SLAVE 681 PropagationSlave string = "slave" 682) 683 684// ServiceVolumeVolume are options for a service volume of type volume 685type ServiceVolumeVolume struct { 686 NoCopy bool `mapstructure:"nocopy" yaml:"nocopy,omitempty" json:"nocopy,omitempty"` 687 688 Extensions map[string]interface{} `yaml:",inline" json:"-"` 689} 690 691// ServiceVolumeTmpfs are options for a service volume of type tmpfs 692type ServiceVolumeTmpfs struct { 693 Size UnitBytes `yaml:",omitempty" json:"size,omitempty"` 694 695 Extensions map[string]interface{} `yaml:",inline" json:"-"` 696} 697 698// FileReferenceConfig for a reference to a swarm file object 699type FileReferenceConfig struct { 700 Source string `yaml:",omitempty" json:"source,omitempty"` 701 Target string `yaml:",omitempty" json:"target,omitempty"` 702 UID string `yaml:",omitempty" json:"uid,omitempty"` 703 GID string `yaml:",omitempty" json:"gid,omitempty"` 704 Mode *uint32 `yaml:",omitempty" json:"mode,omitempty"` 705 706 Extensions map[string]interface{} `yaml:",inline" json:"-"` 707} 708 709// ServiceConfigObjConfig is the config obj configuration for a service 710type ServiceConfigObjConfig FileReferenceConfig 711 712// ServiceSecretConfig is the secret configuration for a service 713type ServiceSecretConfig FileReferenceConfig 714 715// UlimitsConfig the ulimit configuration 716type UlimitsConfig struct { 717 Single int `yaml:",omitempty" json:"single,omitempty"` 718 Soft int `yaml:",omitempty" json:"soft,omitempty"` 719 Hard int `yaml:",omitempty" json:"hard,omitempty"` 720 721 Extensions map[string]interface{} `yaml:",inline" json:"-"` 722} 723 724// MarshalYAML makes UlimitsConfig implement yaml.Marshaller 725func (u *UlimitsConfig) MarshalYAML() (interface{}, error) { 726 if u.Single != 0 { 727 return u.Single, nil 728 } 729 return u, nil 730} 731 732// MarshalJSON makes UlimitsConfig implement json.Marshaller 733func (u *UlimitsConfig) MarshalJSON() ([]byte, error) { 734 if u.Single != 0 { 735 return json.Marshal(u.Single) 736 } 737 // Pass as a value to avoid re-entering this method and use the default implementation 738 return json.Marshal(*u) 739} 740 741// NetworkConfig for a network 742type NetworkConfig struct { 743 Name string `yaml:",omitempty" json:"name,omitempty"` 744 Driver string `yaml:",omitempty" json:"driver,omitempty"` 745 DriverOpts map[string]string `mapstructure:"driver_opts" yaml:"driver_opts,omitempty" json:"driver_opts,omitempty"` 746 Ipam IPAMConfig `yaml:",omitempty" json:"ipam,omitempty"` 747 External External `yaml:",omitempty" json:"external,omitempty"` 748 Internal bool `yaml:",omitempty" json:"internal,omitempty"` 749 Attachable bool `yaml:",omitempty" json:"attachable,omitempty"` 750 Labels Labels `yaml:",omitempty" json:"labels,omitempty"` 751 Extensions map[string]interface{} `yaml:",inline" json:"-"` 752} 753 754// IPAMConfig for a network 755type IPAMConfig struct { 756 Driver string `yaml:",omitempty" json:"driver,omitempty"` 757 Config []*IPAMPool `yaml:",omitempty" json:"config,omitempty"` 758 Extensions map[string]interface{} `yaml:",inline" json:"-"` 759} 760 761// IPAMPool for a network 762type IPAMPool struct { 763 Subnet string `yaml:",omitempty" json:"subnet,omitempty"` 764 Gateway string `yaml:",omitempty" json:"gateway,omitempty"` 765 IPRange string `mapstructure:"ip_range" yaml:"ip_range,omitempty" json:"ip_range,omitempty"` 766 AuxiliaryAddresses map[string]string `mapstructure:"aux_addresses" yaml:"aux_addresses,omitempty" json:"aux_addresses,omitempty"` 767 Extensions map[string]interface{} `yaml:",inline" json:"-"` 768} 769 770// VolumeConfig for a volume 771type VolumeConfig struct { 772 Name string `yaml:",omitempty" json:"name,omitempty"` 773 Driver string `yaml:",omitempty" json:"driver,omitempty"` 774 DriverOpts map[string]string `mapstructure:"driver_opts" yaml:"driver_opts,omitempty" json:"driver_opts,omitempty"` 775 External External `yaml:",omitempty" json:"external,omitempty"` 776 Labels Labels `yaml:",omitempty" json:"labels,omitempty"` 777 Extensions map[string]interface{} `yaml:",inline" json:"-"` 778} 779 780// External identifies a Volume or Network as a reference to a resource that is 781// not managed, and should already exist. 782// External.name is deprecated and replaced by Volume.name 783type External struct { 784 Name string `yaml:",omitempty" json:"name,omitempty"` 785 External bool `yaml:",omitempty" json:"external,omitempty"` 786 Extensions map[string]interface{} `yaml:",inline" json:"-"` 787} 788 789// MarshalYAML makes External implement yaml.Marshaller 790func (e External) MarshalYAML() (interface{}, error) { 791 if e.Name == "" { 792 return e.External, nil 793 } 794 return External{Name: e.Name}, nil 795} 796 797// MarshalJSON makes External implement json.Marshaller 798func (e External) MarshalJSON() ([]byte, error) { 799 if e.Name == "" { 800 return []byte(fmt.Sprintf("%v", e.External)), nil 801 } 802 return []byte(fmt.Sprintf(`{"name": %q}`, e.Name)), nil 803} 804 805// CredentialSpecConfig for credential spec on Windows 806type CredentialSpecConfig struct { 807 Config string `yaml:",omitempty" json:"config,omitempty"` // Config was added in API v1.40 808 File string `yaml:",omitempty" json:"file,omitempty"` 809 Registry string `yaml:",omitempty" json:"registry,omitempty"` 810 Extensions map[string]interface{} `yaml:",inline" json:"-"` 811} 812 813// FileObjectConfig is a config type for a file used by a service 814type FileObjectConfig struct { 815 Name string `yaml:",omitempty" json:"name,omitempty"` 816 File string `yaml:",omitempty" json:"file,omitempty"` 817 External External `yaml:",omitempty" json:"external,omitempty"` 818 Labels Labels `yaml:",omitempty" json:"labels,omitempty"` 819 Driver string `yaml:",omitempty" json:"driver,omitempty"` 820 DriverOpts map[string]string `mapstructure:"driver_opts" yaml:"driver_opts,omitempty" json:"driver_opts,omitempty"` 821 TemplateDriver string `mapstructure:"template_driver" yaml:"template_driver,omitempty" json:"template_driver,omitempty"` 822 Extensions map[string]interface{} `yaml:",inline" json:"-"` 823} 824 825const ( 826 // ServiceConditionCompletedSuccessfully is the type for waiting until a service has completed successfully (exit code 0). 827 ServiceConditionCompletedSuccessfully = "service_completed_successfully" 828 829 // ServiceConditionHealthy is the type for waiting until a service is healthy. 830 ServiceConditionHealthy = "service_healthy" 831 832 // ServiceConditionStarted is the type for waiting until a service has started (default). 833 ServiceConditionStarted = "service_started" 834) 835 836type DependsOnConfig map[string]ServiceDependency 837 838type ServiceDependency struct { 839 Condition string `yaml:",omitempty" json:"condition,omitempty"` 840 Extensions map[string]interface{} `yaml:",inline" json:"-"` 841} 842 843type ExtendsConfig MappingWithEquals 844 845// SecretConfig for a secret 846type SecretConfig FileObjectConfig 847 848// ConfigObjConfig is the config for the swarm "Config" object 849type ConfigObjConfig FileObjectConfig 850