mirror of
https://gitea.com/Lydanne/buildx.git
synced 2025-07-10 05:27:07 +08:00
Use compose-spec parser
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
This commit is contained in:
8
vendor/github.com/compose-spec/compose-go/loader/example1.env
generated
vendored
Normal file
8
vendor/github.com/compose-spec/compose-go/loader/example1.env
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
# passed through
|
||||
FOO=foo_from_env_file
|
||||
|
||||
# overridden in example2.env
|
||||
BAR=bar_from_env_file
|
||||
|
||||
# overridden in full-example.yml
|
||||
BAZ=baz_from_env_file
|
4
vendor/github.com/compose-spec/compose-go/loader/example2.env
generated
vendored
Normal file
4
vendor/github.com/compose-spec/compose-go/loader/example2.env
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
BAR=bar_from_env_file_2
|
||||
|
||||
# overridden in configDetails.Environment
|
||||
QUX=quz_from_env_file_2
|
412
vendor/github.com/compose-spec/compose-go/loader/full-example.yml
generated
vendored
Normal file
412
vendor/github.com/compose-spec/compose-go/loader/full-example.yml
generated
vendored
Normal file
@ -0,0 +1,412 @@
|
||||
services:
|
||||
foo:
|
||||
|
||||
build:
|
||||
context: ./dir
|
||||
dockerfile: Dockerfile
|
||||
args:
|
||||
foo: bar
|
||||
target: foo
|
||||
network: foo
|
||||
cache_from:
|
||||
- foo
|
||||
- bar
|
||||
labels: [FOO=BAR]
|
||||
|
||||
|
||||
cap_add:
|
||||
- ALL
|
||||
|
||||
cap_drop:
|
||||
- NET_ADMIN
|
||||
- SYS_ADMIN
|
||||
|
||||
cgroup_parent: m-executor-abcd
|
||||
|
||||
# String or list
|
||||
command: bundle exec thin -p 3000
|
||||
# command: ["bundle", "exec", "thin", "-p", "3000"]
|
||||
|
||||
configs:
|
||||
- config1
|
||||
- source: config2
|
||||
target: /my_config
|
||||
uid: '103'
|
||||
gid: '103'
|
||||
mode: 0440
|
||||
|
||||
container_name: my-web-container
|
||||
|
||||
depends_on:
|
||||
- db
|
||||
- redis
|
||||
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 6
|
||||
labels: [FOO=BAR]
|
||||
rollback_config:
|
||||
parallelism: 3
|
||||
delay: 10s
|
||||
failure_action: continue
|
||||
monitor: 60s
|
||||
max_failure_ratio: 0.3
|
||||
order: start-first
|
||||
update_config:
|
||||
parallelism: 3
|
||||
delay: 10s
|
||||
failure_action: continue
|
||||
monitor: 60s
|
||||
max_failure_ratio: 0.3
|
||||
order: start-first
|
||||
resources:
|
||||
limits:
|
||||
cpus: '0.001'
|
||||
memory: 50M
|
||||
reservations:
|
||||
cpus: '0.0001'
|
||||
memory: 20M
|
||||
generic_resources:
|
||||
- discrete_resource_spec:
|
||||
kind: 'gpu'
|
||||
value: 2
|
||||
- discrete_resource_spec:
|
||||
kind: 'ssd'
|
||||
value: 1
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
delay: 5s
|
||||
max_attempts: 3
|
||||
window: 120s
|
||||
placement:
|
||||
constraints: [node=foo]
|
||||
max_replicas_per_node: 5
|
||||
preferences:
|
||||
- spread: node.labels.az
|
||||
endpoint_mode: dnsrr
|
||||
|
||||
devices:
|
||||
- "/dev/ttyUSB0:/dev/ttyUSB0"
|
||||
|
||||
# String or list
|
||||
# dns: 8.8.8.8
|
||||
dns:
|
||||
- 8.8.8.8
|
||||
- 9.9.9.9
|
||||
|
||||
# String or list
|
||||
# dns_search: example.com
|
||||
dns_search:
|
||||
- dc1.example.com
|
||||
- dc2.example.com
|
||||
|
||||
domainname: foo.com
|
||||
|
||||
# String or list
|
||||
# entrypoint: /code/entrypoint.sh -p 3000
|
||||
entrypoint: ["/code/entrypoint.sh", "-p", "3000"]
|
||||
|
||||
# String or list
|
||||
# env_file: .env
|
||||
env_file:
|
||||
- ./example1.env
|
||||
- ./example2.env
|
||||
|
||||
# Mapping or list
|
||||
# Mapping values can be strings, numbers or null
|
||||
# Booleans are not allowed - must be quoted
|
||||
environment:
|
||||
BAZ: baz_from_service_def
|
||||
QUX:
|
||||
# environment:
|
||||
# - RACK_ENV=development
|
||||
# - SHOW=true
|
||||
# - SESSION_SECRET
|
||||
|
||||
# Items can be strings or numbers
|
||||
expose:
|
||||
- "3000"
|
||||
- 8000
|
||||
|
||||
external_links:
|
||||
- redis_1
|
||||
- project_db_1:mysql
|
||||
- project_db_1:postgresql
|
||||
|
||||
# Mapping or list
|
||||
# Mapping values must be strings
|
||||
# extra_hosts:
|
||||
# somehost: "162.242.195.82"
|
||||
# otherhost: "50.31.209.229"
|
||||
extra_hosts:
|
||||
- "somehost:162.242.195.82"
|
||||
- "otherhost:50.31.209.229"
|
||||
|
||||
hostname: foo
|
||||
|
||||
healthcheck:
|
||||
test: echo "hello world"
|
||||
interval: 10s
|
||||
timeout: 1s
|
||||
retries: 5
|
||||
start_period: 15s
|
||||
|
||||
# Any valid image reference - repo, tag, id, sha
|
||||
image: redis
|
||||
# image: ubuntu:14.04
|
||||
# image: tutum/influxdb
|
||||
# image: example-registry.com:4000/postgresql
|
||||
# image: a4bc65fd
|
||||
# image: busybox@sha256:38a203e1986cf79639cfb9b2e1d6e773de84002feea2d4eb006b52004ee8502d
|
||||
|
||||
ipc: host
|
||||
|
||||
# Mapping or list
|
||||
# Mapping values can be strings, numbers or null
|
||||
labels:
|
||||
com.example.description: "Accounting webapp"
|
||||
com.example.number: 42
|
||||
com.example.empty-label:
|
||||
# labels:
|
||||
# - "com.example.description=Accounting webapp"
|
||||
# - "com.example.number=42"
|
||||
# - "com.example.empty-label"
|
||||
|
||||
links:
|
||||
- db
|
||||
- db:database
|
||||
- redis
|
||||
|
||||
logging:
|
||||
driver: syslog
|
||||
options:
|
||||
syslog-address: "tcp://192.168.0.42:123"
|
||||
|
||||
mac_address: 02:42:ac:11:65:43
|
||||
|
||||
# network_mode: "bridge"
|
||||
# network_mode: "host"
|
||||
# network_mode: "none"
|
||||
# Use the network mode of an arbitrary container from another service
|
||||
# network_mode: "service:db"
|
||||
# Use the network mode of another container, specified by name or id
|
||||
# network_mode: "container:some-container"
|
||||
network_mode: "container:0cfeab0f748b9a743dc3da582046357c6ef497631c1a016d28d2bf9b4f899f7b"
|
||||
|
||||
networks:
|
||||
some-network:
|
||||
aliases:
|
||||
- alias1
|
||||
- alias3
|
||||
other-network:
|
||||
ipv4_address: 172.16.238.10
|
||||
ipv6_address: 2001:3984:3989::10
|
||||
other-other-network:
|
||||
|
||||
pid: "host"
|
||||
|
||||
ports:
|
||||
- 3000
|
||||
- "3001-3005"
|
||||
- "8000:8000"
|
||||
- "9090-9091:8080-8081"
|
||||
- "49100:22"
|
||||
- "127.0.0.1:8001:8001"
|
||||
- "127.0.0.1:5000-5010:5000-5010"
|
||||
|
||||
privileged: true
|
||||
|
||||
read_only: true
|
||||
|
||||
restart: always
|
||||
|
||||
secrets:
|
||||
- secret1
|
||||
- source: secret2
|
||||
target: my_secret
|
||||
uid: '103'
|
||||
gid: '103'
|
||||
mode: 0440
|
||||
|
||||
security_opt:
|
||||
- label=level:s0:c100,c200
|
||||
- label=type:svirt_apache_t
|
||||
|
||||
stdin_open: true
|
||||
|
||||
stop_grace_period: 20s
|
||||
|
||||
stop_signal: SIGUSR1
|
||||
|
||||
sysctls:
|
||||
net.core.somaxconn: 1024
|
||||
net.ipv4.tcp_syncookies: 0
|
||||
|
||||
# String or list
|
||||
# tmpfs: /run
|
||||
tmpfs:
|
||||
- /run
|
||||
- /tmp
|
||||
|
||||
tty: true
|
||||
|
||||
ulimits:
|
||||
# Single number or mapping with soft + hard limits
|
||||
nproc: 65535
|
||||
nofile:
|
||||
soft: 20000
|
||||
hard: 40000
|
||||
|
||||
user: someone
|
||||
|
||||
volumes:
|
||||
# Just specify a path and let the Engine create a volume
|
||||
- /var/lib/mysql
|
||||
# Specify an absolute path mapping
|
||||
- /opt/data:/var/lib/mysql
|
||||
# Path on the host, relative to the Compose file
|
||||
- .:/code
|
||||
- ./static:/var/www/html
|
||||
# User-relative path
|
||||
- ~/configs:/etc/configs/:ro
|
||||
# Named volume
|
||||
- datavolume:/var/lib/mysql
|
||||
- type: bind
|
||||
source: ./opt
|
||||
target: /opt
|
||||
consistency: cached
|
||||
- type: tmpfs
|
||||
target: /opt
|
||||
tmpfs:
|
||||
size: 10000
|
||||
|
||||
working_dir: /code
|
||||
x-bar: baz
|
||||
x-foo: bar
|
||||
|
||||
networks:
|
||||
# Entries can be null, which specifies simply that a network
|
||||
# called "{project name}_some-network" should be created and
|
||||
# use the default driver
|
||||
some-network:
|
||||
|
||||
other-network:
|
||||
driver: overlay
|
||||
|
||||
driver_opts:
|
||||
# Values can be strings or numbers
|
||||
foo: "bar"
|
||||
baz: 1
|
||||
|
||||
ipam:
|
||||
driver: overlay
|
||||
# driver_opts:
|
||||
# # Values can be strings or numbers
|
||||
# com.docker.network.enable_ipv6: "true"
|
||||
# com.docker.network.numeric_value: 1
|
||||
config:
|
||||
- subnet: 172.28.0.0/16
|
||||
ip_range: 172.28.5.0/24
|
||||
gateway: 172.28.5.254
|
||||
aux_addresses:
|
||||
host1: 172.28.1.5
|
||||
host2: 172.28.1.6
|
||||
host3: 172.28.1.7
|
||||
- subnet: 2001:3984:3989::/64
|
||||
gateway: 2001:3984:3989::1
|
||||
|
||||
labels:
|
||||
foo: bar
|
||||
|
||||
external-network:
|
||||
# Specifies that a pre-existing network called "external-network"
|
||||
# can be referred to within this file as "external-network"
|
||||
external: true
|
||||
|
||||
other-external-network:
|
||||
# Specifies that a pre-existing network called "my-cool-network"
|
||||
# can be referred to within this file as "other-external-network"
|
||||
external:
|
||||
name: my-cool-network
|
||||
x-bar: baz
|
||||
x-foo: bar
|
||||
|
||||
volumes:
|
||||
# Entries can be null, which specifies simply that a volume
|
||||
# called "{project name}_some-volume" should be created and
|
||||
# use the default driver
|
||||
some-volume:
|
||||
|
||||
other-volume:
|
||||
driver: flocker
|
||||
|
||||
driver_opts:
|
||||
# Values can be strings or numbers
|
||||
foo: "bar"
|
||||
baz: 1
|
||||
labels:
|
||||
foo: bar
|
||||
|
||||
another-volume:
|
||||
name: "user_specified_name"
|
||||
driver: vsphere
|
||||
|
||||
driver_opts:
|
||||
# Values can be strings or numbers
|
||||
foo: "bar"
|
||||
baz: 1
|
||||
|
||||
external-volume:
|
||||
# Specifies that a pre-existing volume called "external-volume"
|
||||
# can be referred to within this file as "external-volume"
|
||||
external: true
|
||||
|
||||
other-external-volume:
|
||||
# Specifies that a pre-existing volume called "my-cool-volume"
|
||||
# can be referred to within this file as "other-external-volume"
|
||||
# This example uses the deprecated "volume.external.name" (replaced by "volume.name")
|
||||
external:
|
||||
name: my-cool-volume
|
||||
|
||||
external-volume3:
|
||||
# Specifies that a pre-existing volume called "this-is-volume3"
|
||||
# can be referred to within this file as "external-volume3"
|
||||
name: this-is-volume3
|
||||
external: true
|
||||
x-bar: baz
|
||||
x-foo: bar
|
||||
|
||||
configs:
|
||||
config1:
|
||||
file: ./config_data
|
||||
labels:
|
||||
foo: bar
|
||||
config2:
|
||||
external:
|
||||
name: my_config
|
||||
config3:
|
||||
external: true
|
||||
config4:
|
||||
name: foo
|
||||
x-bar: baz
|
||||
x-foo: bar
|
||||
|
||||
secrets:
|
||||
secret1:
|
||||
file: ./secret_data
|
||||
labels:
|
||||
foo: bar
|
||||
secret2:
|
||||
external:
|
||||
name: my_secret
|
||||
secret3:
|
||||
external: true
|
||||
secret4:
|
||||
name: bar
|
||||
x-bar: baz
|
||||
x-foo: bar
|
||||
x-bar: baz
|
||||
x-foo: bar
|
||||
x-nested:
|
||||
bar: baz
|
||||
foo: bar
|
84
vendor/github.com/compose-spec/compose-go/loader/interpolate.go
generated
vendored
Normal file
84
vendor/github.com/compose-spec/compose-go/loader/interpolate.go
generated
vendored
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification 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 loader
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
interp "github.com/compose-spec/compose-go/interpolation"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var interpolateTypeCastMapping = map[interp.Path]interp.Cast{
|
||||
servicePath("configs", interp.PathMatchList, "mode"): toInt,
|
||||
servicePath("secrets", interp.PathMatchList, "mode"): toInt,
|
||||
servicePath("healthcheck", "retries"): toInt,
|
||||
servicePath("healthcheck", "disable"): toBoolean,
|
||||
servicePath("deploy", "replicas"): toInt,
|
||||
servicePath("deploy", "update_config", "parallelism"): toInt,
|
||||
servicePath("deploy", "update_config", "max_failure_ratio"): toFloat,
|
||||
servicePath("deploy", "rollback_config", "parallelism"): toInt,
|
||||
servicePath("deploy", "rollback_config", "max_failure_ratio"): toFloat,
|
||||
servicePath("deploy", "restart_policy", "max_attempts"): toInt,
|
||||
servicePath("deploy", "placement", "max_replicas_per_node"): toInt,
|
||||
servicePath("ports", interp.PathMatchList, "target"): toInt,
|
||||
servicePath("ports", interp.PathMatchList, "published"): toInt,
|
||||
servicePath("ulimits", interp.PathMatchAll): toInt,
|
||||
servicePath("ulimits", interp.PathMatchAll, "hard"): toInt,
|
||||
servicePath("ulimits", interp.PathMatchAll, "soft"): toInt,
|
||||
servicePath("privileged"): toBoolean,
|
||||
servicePath("read_only"): toBoolean,
|
||||
servicePath("stdin_open"): toBoolean,
|
||||
servicePath("tty"): toBoolean,
|
||||
servicePath("volumes", interp.PathMatchList, "read_only"): toBoolean,
|
||||
servicePath("volumes", interp.PathMatchList, "volume", "nocopy"): toBoolean,
|
||||
iPath("networks", interp.PathMatchAll, "external"): toBoolean,
|
||||
iPath("networks", interp.PathMatchAll, "internal"): toBoolean,
|
||||
iPath("networks", interp.PathMatchAll, "attachable"): toBoolean,
|
||||
iPath("volumes", interp.PathMatchAll, "external"): toBoolean,
|
||||
iPath("secrets", interp.PathMatchAll, "external"): toBoolean,
|
||||
iPath("configs", interp.PathMatchAll, "external"): toBoolean,
|
||||
}
|
||||
|
||||
func iPath(parts ...string) interp.Path {
|
||||
return interp.NewPath(parts...)
|
||||
}
|
||||
|
||||
func servicePath(parts ...string) interp.Path {
|
||||
return iPath(append([]string{"services", interp.PathMatchAll}, parts...)...)
|
||||
}
|
||||
|
||||
func toInt(value string) (interface{}, error) {
|
||||
return strconv.Atoi(value)
|
||||
}
|
||||
|
||||
func toFloat(value string) (interface{}, error) {
|
||||
return strconv.ParseFloat(value, 64)
|
||||
}
|
||||
|
||||
// should match http://yaml.org/type/bool.html
|
||||
func toBoolean(value string) (interface{}, error) {
|
||||
switch strings.ToLower(value) {
|
||||
case "y", "yes", "true", "on":
|
||||
return true, nil
|
||||
case "n", "no", "false", "off":
|
||||
return false, nil
|
||||
default:
|
||||
return nil, errors.Errorf("invalid boolean: %s", value)
|
||||
}
|
||||
}
|
1050
vendor/github.com/compose-spec/compose-go/loader/loader.go
generated
vendored
Normal file
1050
vendor/github.com/compose-spec/compose-go/loader/loader.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
291
vendor/github.com/compose-spec/compose-go/loader/merge.go
generated
vendored
Normal file
291
vendor/github.com/compose-spec/compose-go/loader/merge.go
generated
vendored
Normal file
@ -0,0 +1,291 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification 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 loader
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/imdario/mergo"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type specials struct {
|
||||
m map[reflect.Type]func(dst, src reflect.Value) error
|
||||
}
|
||||
|
||||
var serviceSpecials = &specials{
|
||||
m: map[reflect.Type]func(dst, src reflect.Value) error{
|
||||
reflect.TypeOf(&types.LoggingConfig{}): safelyMerge(mergeLoggingConfig),
|
||||
reflect.TypeOf(&types.UlimitsConfig{}): safelyMerge(mergeUlimitsConfig),
|
||||
reflect.TypeOf([]types.ServicePortConfig{}): mergeSlice(toServicePortConfigsMap, toServicePortConfigsSlice),
|
||||
reflect.TypeOf([]types.ServiceSecretConfig{}): mergeSlice(toServiceSecretConfigsMap, toServiceSecretConfigsSlice),
|
||||
reflect.TypeOf([]types.ServiceConfigObjConfig{}): mergeSlice(toServiceConfigObjConfigsMap, toSServiceConfigObjConfigsSlice),
|
||||
reflect.TypeOf(&types.UlimitsConfig{}): mergeUlimitsConfig,
|
||||
reflect.TypeOf(&types.ServiceNetworkConfig{}): mergeServiceNetworkConfig,
|
||||
},
|
||||
}
|
||||
|
||||
func (s *specials) Transformer(t reflect.Type) func(dst, src reflect.Value) error {
|
||||
if fn, ok := s.m[t]; ok {
|
||||
return fn
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func merge(configs []*types.Config) (*types.Config, error) {
|
||||
base := configs[0]
|
||||
for _, override := range configs[1:] {
|
||||
var err error
|
||||
base.Services, err = mergeServices(base.Services, override.Services)
|
||||
if err != nil {
|
||||
return base, errors.Wrapf(err, "cannot merge services from %s", override.Filename)
|
||||
}
|
||||
base.Volumes, err = mergeVolumes(base.Volumes, override.Volumes)
|
||||
if err != nil {
|
||||
return base, errors.Wrapf(err, "cannot merge volumes from %s", override.Filename)
|
||||
}
|
||||
base.Networks, err = mergeNetworks(base.Networks, override.Networks)
|
||||
if err != nil {
|
||||
return base, errors.Wrapf(err, "cannot merge networks from %s", override.Filename)
|
||||
}
|
||||
base.Secrets, err = mergeSecrets(base.Secrets, override.Secrets)
|
||||
if err != nil {
|
||||
return base, errors.Wrapf(err, "cannot merge secrets from %s", override.Filename)
|
||||
}
|
||||
base.Configs, err = mergeConfigs(base.Configs, override.Configs)
|
||||
if err != nil {
|
||||
return base, errors.Wrapf(err, "cannot merge configs from %s", override.Filename)
|
||||
}
|
||||
base.Extensions, err = mergeExtensions(base.Extensions, override.Extensions)
|
||||
if err != nil {
|
||||
return base, errors.Wrapf(err, "cannot merge extensions from %s", override.Filename)
|
||||
}
|
||||
}
|
||||
return base, nil
|
||||
}
|
||||
|
||||
func mergeServices(base, override []types.ServiceConfig) ([]types.ServiceConfig, error) {
|
||||
baseServices := mapByName(base)
|
||||
overrideServices := mapByName(override)
|
||||
for name, overrideService := range overrideServices {
|
||||
overrideService := overrideService
|
||||
if baseService, ok := baseServices[name]; ok {
|
||||
if err := mergo.Merge(&baseService, &overrideService, mergo.WithAppendSlice, mergo.WithOverride, mergo.WithTransformers(serviceSpecials)); err != nil {
|
||||
return base, errors.Wrapf(err, "cannot merge service %s", name)
|
||||
}
|
||||
if len(overrideService.Command) > 0 {
|
||||
baseService.Command = overrideService.Command
|
||||
}
|
||||
baseServices[name] = baseService
|
||||
continue
|
||||
}
|
||||
baseServices[name] = overrideService
|
||||
}
|
||||
services := []types.ServiceConfig{}
|
||||
for _, baseService := range baseServices {
|
||||
services = append(services, baseService)
|
||||
}
|
||||
sort.Slice(services, func(i, j int) bool { return services[i].Name < services[j].Name })
|
||||
return services, nil
|
||||
}
|
||||
|
||||
func toServiceSecretConfigsMap(s interface{}) (map[interface{}]interface{}, error) {
|
||||
secrets, ok := s.([]types.ServiceSecretConfig)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("not a serviceSecretConfig: %v", s)
|
||||
}
|
||||
m := map[interface{}]interface{}{}
|
||||
for _, secret := range secrets {
|
||||
m[secret.Source] = secret
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func toServiceConfigObjConfigsMap(s interface{}) (map[interface{}]interface{}, error) {
|
||||
secrets, ok := s.([]types.ServiceConfigObjConfig)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("not a serviceSecretConfig: %v", s)
|
||||
}
|
||||
m := map[interface{}]interface{}{}
|
||||
for _, secret := range secrets {
|
||||
m[secret.Source] = secret
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func toServicePortConfigsMap(s interface{}) (map[interface{}]interface{}, error) {
|
||||
ports, ok := s.([]types.ServicePortConfig)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("not a servicePortConfig slice: %v", s)
|
||||
}
|
||||
m := map[interface{}]interface{}{}
|
||||
for _, p := range ports {
|
||||
m[p.Published] = p
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func toServiceSecretConfigsSlice(dst reflect.Value, m map[interface{}]interface{}) error {
|
||||
s := []types.ServiceSecretConfig{}
|
||||
for _, v := range m {
|
||||
s = append(s, v.(types.ServiceSecretConfig))
|
||||
}
|
||||
sort.Slice(s, func(i, j int) bool { return s[i].Source < s[j].Source })
|
||||
dst.Set(reflect.ValueOf(s))
|
||||
return nil
|
||||
}
|
||||
|
||||
func toSServiceConfigObjConfigsSlice(dst reflect.Value, m map[interface{}]interface{}) error {
|
||||
s := []types.ServiceConfigObjConfig{}
|
||||
for _, v := range m {
|
||||
s = append(s, v.(types.ServiceConfigObjConfig))
|
||||
}
|
||||
sort.Slice(s, func(i, j int) bool { return s[i].Source < s[j].Source })
|
||||
dst.Set(reflect.ValueOf(s))
|
||||
return nil
|
||||
}
|
||||
|
||||
func toServicePortConfigsSlice(dst reflect.Value, m map[interface{}]interface{}) error {
|
||||
s := []types.ServicePortConfig{}
|
||||
for _, v := range m {
|
||||
s = append(s, v.(types.ServicePortConfig))
|
||||
}
|
||||
sort.Slice(s, func(i, j int) bool { return s[i].Published < s[j].Published })
|
||||
dst.Set(reflect.ValueOf(s))
|
||||
return nil
|
||||
}
|
||||
|
||||
type tomapFn func(s interface{}) (map[interface{}]interface{}, error)
|
||||
type writeValueFromMapFn func(reflect.Value, map[interface{}]interface{}) error
|
||||
|
||||
func safelyMerge(mergeFn func(dst, src reflect.Value) error) func(dst, src reflect.Value) error {
|
||||
return func(dst, src reflect.Value) error {
|
||||
if src.IsNil() {
|
||||
return nil
|
||||
}
|
||||
if dst.IsNil() {
|
||||
dst.Set(src)
|
||||
return nil
|
||||
}
|
||||
return mergeFn(dst, src)
|
||||
}
|
||||
}
|
||||
|
||||
func mergeSlice(tomap tomapFn, writeValue writeValueFromMapFn) func(dst, src reflect.Value) error {
|
||||
return func(dst, src reflect.Value) error {
|
||||
dstMap, err := sliceToMap(tomap, dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
srcMap, err := sliceToMap(tomap, src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := mergo.Map(&dstMap, srcMap, mergo.WithOverride); err != nil {
|
||||
return err
|
||||
}
|
||||
return writeValue(dst, dstMap)
|
||||
}
|
||||
}
|
||||
|
||||
func sliceToMap(tomap tomapFn, v reflect.Value) (map[interface{}]interface{}, error) {
|
||||
// check if valid
|
||||
if !v.IsValid() {
|
||||
return nil, errors.Errorf("invalid value : %+v", v)
|
||||
}
|
||||
return tomap(v.Interface())
|
||||
}
|
||||
|
||||
func mergeLoggingConfig(dst, src reflect.Value) error {
|
||||
// Same driver, merging options
|
||||
if getLoggingDriver(dst.Elem()) == getLoggingDriver(src.Elem()) ||
|
||||
getLoggingDriver(dst.Elem()) == "" || getLoggingDriver(src.Elem()) == "" {
|
||||
if getLoggingDriver(dst.Elem()) == "" {
|
||||
dst.Elem().FieldByName("Driver").SetString(getLoggingDriver(src.Elem()))
|
||||
}
|
||||
dstOptions := dst.Elem().FieldByName("Options").Interface().(map[string]string)
|
||||
srcOptions := src.Elem().FieldByName("Options").Interface().(map[string]string)
|
||||
return mergo.Merge(&dstOptions, srcOptions, mergo.WithOverride)
|
||||
}
|
||||
// Different driver, override with src
|
||||
dst.Set(src)
|
||||
return nil
|
||||
}
|
||||
|
||||
//nolint: unparam
|
||||
func mergeUlimitsConfig(dst, src reflect.Value) error {
|
||||
if src.Interface() != reflect.Zero(reflect.TypeOf(src.Interface())).Interface() {
|
||||
dst.Elem().Set(src.Elem())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//nolint: unparam
|
||||
func mergeServiceNetworkConfig(dst, src reflect.Value) error {
|
||||
if src.Interface() != reflect.Zero(reflect.TypeOf(src.Interface())).Interface() {
|
||||
dst.Elem().FieldByName("Aliases").Set(src.Elem().FieldByName("Aliases"))
|
||||
if ipv4 := src.Elem().FieldByName("Ipv4Address").Interface().(string); ipv4 != "" {
|
||||
dst.Elem().FieldByName("Ipv4Address").SetString(ipv4)
|
||||
}
|
||||
if ipv6 := src.Elem().FieldByName("Ipv6Address").Interface().(string); ipv6 != "" {
|
||||
dst.Elem().FieldByName("Ipv6Address").SetString(ipv6)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getLoggingDriver(v reflect.Value) string {
|
||||
return v.FieldByName("Driver").String()
|
||||
}
|
||||
|
||||
func mapByName(services []types.ServiceConfig) map[string]types.ServiceConfig {
|
||||
m := map[string]types.ServiceConfig{}
|
||||
for _, service := range services {
|
||||
m[service.Name] = service
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func mergeVolumes(base, override map[string]types.VolumeConfig) (map[string]types.VolumeConfig, error) {
|
||||
err := mergo.Map(&base, &override, mergo.WithOverride)
|
||||
return base, err
|
||||
}
|
||||
|
||||
func mergeNetworks(base, override map[string]types.NetworkConfig) (map[string]types.NetworkConfig, error) {
|
||||
err := mergo.Map(&base, &override, mergo.WithOverride)
|
||||
return base, err
|
||||
}
|
||||
|
||||
func mergeSecrets(base, override map[string]types.SecretConfig) (map[string]types.SecretConfig, error) {
|
||||
err := mergo.Map(&base, &override, mergo.WithOverride)
|
||||
return base, err
|
||||
}
|
||||
|
||||
func mergeConfigs(base, override map[string]types.ConfigObjConfig) (map[string]types.ConfigObjConfig, error) {
|
||||
err := mergo.Map(&base, &override, mergo.WithOverride)
|
||||
return base, err
|
||||
}
|
||||
|
||||
func mergeExtensions(base, override map[string]interface{}) (map[string]interface{}, error) {
|
||||
if base == nil {
|
||||
base = map[string]interface{}{}
|
||||
}
|
||||
err := mergo.Map(&base, &override, mergo.WithOverride)
|
||||
return base, err
|
||||
}
|
239
vendor/github.com/compose-spec/compose-go/loader/normalize.go
generated
vendored
Normal file
239
vendor/github.com/compose-spec/compose-go/loader/normalize.go
generated
vendored
Normal file
@ -0,0 +1,239 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification 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 loader
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/compose-spec/compose-go/errdefs"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// normalize compose project by moving deprecated attributes to their canonical position and injecting implicit defaults
|
||||
func normalize(project *types.Project) error {
|
||||
absWorkingDir, err := filepath.Abs(project.WorkingDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
project.WorkingDir = absWorkingDir
|
||||
|
||||
absComposeFiles, err := absComposeFiles(project.ComposeFiles)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
project.ComposeFiles = absComposeFiles
|
||||
|
||||
// If not declared explicitly, Compose model involves an implicit "default" network
|
||||
if _, ok := project.Networks["default"]; !ok {
|
||||
project.Networks["default"] = types.NetworkConfig{}
|
||||
}
|
||||
|
||||
err = relocateExternalName(project)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i, s := range project.Services {
|
||||
if len(s.Networks) == 0 && s.NetworkMode == "" {
|
||||
// Service without explicit network attachment are implicitly exposed on default network
|
||||
s.Networks = map[string]*types.ServiceNetworkConfig{"default": nil}
|
||||
}
|
||||
|
||||
if s.PullPolicy == types.PullPolicyIfNotPresent {
|
||||
s.PullPolicy = types.PullPolicyMissing
|
||||
}
|
||||
|
||||
fn := func(s string) (string, bool) {
|
||||
v, ok := project.Environment[s]
|
||||
return v, ok
|
||||
}
|
||||
|
||||
if s.Build != nil {
|
||||
if s.Build.Dockerfile == "" {
|
||||
s.Build.Dockerfile = "Dockerfile"
|
||||
}
|
||||
localContext := absPath(project.WorkingDir, s.Build.Context)
|
||||
if _, err := os.Stat(localContext); err == nil {
|
||||
s.Build.Context = localContext
|
||||
s.Build.Dockerfile = absPath(localContext, s.Build.Dockerfile)
|
||||
} else {
|
||||
// might be a remote http/git context. Unfortunately supported "remote" syntax is highly ambiguous
|
||||
// in moby/moby and not defined by compose-spec, so let's assume runtime will check
|
||||
}
|
||||
s.Build.Args = s.Build.Args.Resolve(fn)
|
||||
}
|
||||
s.Environment = s.Environment.Resolve(fn)
|
||||
|
||||
err := relocateLogDriver(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = relocateLogOpt(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = relocateDockerfile(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
project.Services[i] = s
|
||||
}
|
||||
|
||||
setNameFromKey(project)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func absComposeFiles(composeFiles []string) ([]string, error) {
|
||||
absComposeFiles := make([]string, len(composeFiles))
|
||||
for i, composeFile := range composeFiles {
|
||||
absComposefile, err := filepath.Abs(composeFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
absComposeFiles[i] = absComposefile
|
||||
}
|
||||
return absComposeFiles, nil
|
||||
}
|
||||
|
||||
// Resources with no explicit name are actually named by their key in map
|
||||
func setNameFromKey(project *types.Project) {
|
||||
for i, n := range project.Networks {
|
||||
if n.Name == "" {
|
||||
n.Name = fmt.Sprintf("%s_%s", project.Name, i)
|
||||
project.Networks[i] = n
|
||||
}
|
||||
}
|
||||
|
||||
for i, v := range project.Volumes {
|
||||
if v.Name == "" {
|
||||
v.Name = fmt.Sprintf("%s_%s", project.Name, i)
|
||||
project.Volumes[i] = v
|
||||
}
|
||||
}
|
||||
|
||||
for i, c := range project.Configs {
|
||||
if c.Name == "" {
|
||||
c.Name = fmt.Sprintf("%s_%s", project.Name, i)
|
||||
project.Configs[i] = c
|
||||
}
|
||||
}
|
||||
|
||||
for i, s := range project.Secrets {
|
||||
if s.Name == "" {
|
||||
s.Name = fmt.Sprintf("%s_%s", project.Name, i)
|
||||
project.Secrets[i] = s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func relocateExternalName(project *types.Project) error {
|
||||
for i, n := range project.Networks {
|
||||
if n.External.Name != "" {
|
||||
if n.Name != "" {
|
||||
return errors.Wrap(errdefs.ErrInvalid, "can't use both 'networks.external.name' (deprecated) and 'networks.name'")
|
||||
}
|
||||
n.Name = n.External.Name
|
||||
}
|
||||
project.Networks[i] = n
|
||||
}
|
||||
|
||||
for i, v := range project.Volumes {
|
||||
if v.External.Name != "" {
|
||||
if v.Name != "" {
|
||||
return errors.Wrap(errdefs.ErrInvalid, "can't use both 'volumes.external.name' (deprecated) and 'volumes.name'")
|
||||
}
|
||||
v.Name = v.External.Name
|
||||
}
|
||||
project.Volumes[i] = v
|
||||
}
|
||||
|
||||
for i, s := range project.Secrets {
|
||||
if s.External.Name != "" {
|
||||
if s.Name != "" {
|
||||
return errors.Wrap(errdefs.ErrInvalid, "can't use both 'secrets.external.name' (deprecated) and 'secrets.name'")
|
||||
}
|
||||
s.Name = s.External.Name
|
||||
}
|
||||
project.Secrets[i] = s
|
||||
}
|
||||
|
||||
for i, c := range project.Configs {
|
||||
if c.External.Name != "" {
|
||||
if c.Name != "" {
|
||||
return errors.Wrap(errdefs.ErrInvalid, "can't use both 'configs.external.name' (deprecated) and 'configs.name'")
|
||||
}
|
||||
c.Name = c.External.Name
|
||||
}
|
||||
project.Configs[i] = c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func relocateLogOpt(s types.ServiceConfig) error {
|
||||
if len(s.LogOpt) != 0 {
|
||||
logrus.Warn("`log_opts` is deprecated. Use the `logging` element")
|
||||
if s.Logging == nil {
|
||||
s.Logging = &types.LoggingConfig{}
|
||||
}
|
||||
for k, v := range s.LogOpt {
|
||||
if _, ok := s.Logging.Options[k]; !ok {
|
||||
s.Logging.Options[k] = v
|
||||
} else {
|
||||
return errors.Wrap(errdefs.ErrInvalid, "can't use both 'log_opt' (deprecated) and 'logging.options'")
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func relocateLogDriver(s types.ServiceConfig) error {
|
||||
if s.LogDriver != "" {
|
||||
logrus.Warn("`log_driver` is deprecated. Use the `logging` element")
|
||||
if s.Logging == nil {
|
||||
s.Logging = &types.LoggingConfig{}
|
||||
}
|
||||
if s.Logging.Driver == "" {
|
||||
s.Logging.Driver = s.LogDriver
|
||||
} else {
|
||||
return errors.Wrap(errdefs.ErrInvalid, "can't use both 'log_driver' (deprecated) and 'logging.driver'")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func relocateDockerfile(s types.ServiceConfig) error {
|
||||
if s.Dockerfile != "" {
|
||||
logrus.Warn("`dockerfile` is deprecated. Use the `build` element")
|
||||
if s.Build == nil {
|
||||
s.Build = &types.BuildConfig{}
|
||||
}
|
||||
if s.Dockerfile == "" {
|
||||
s.Build.Dockerfile = s.Dockerfile
|
||||
} else {
|
||||
return errors.Wrap(errdefs.ErrInvalid, "can't use both 'dockerfile' (deprecated) and 'build.dockerfile'")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
77
vendor/github.com/compose-spec/compose-go/loader/validate.go
generated
vendored
Normal file
77
vendor/github.com/compose-spec/compose-go/loader/validate.go
generated
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification 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 loader
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/compose-spec/compose-go/errdefs"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// checkConsistency validate a compose model is consistent
|
||||
func checkConsistency(project *types.Project) error {
|
||||
for _, s := range project.Services {
|
||||
if s.Build == nil && s.Image == "" {
|
||||
return errors.Wrapf(errdefs.ErrInvalid, "service %q has neither an image nor a build context specified", s.Name)
|
||||
}
|
||||
|
||||
for network := range s.Networks {
|
||||
if _, ok := project.Networks[network]; !ok {
|
||||
return errors.Wrap(errdefs.ErrInvalid, fmt.Sprintf("service %q refers to undefined network %s", s.Name, network))
|
||||
}
|
||||
}
|
||||
|
||||
if strings.HasPrefix(s.NetworkMode, types.NetworkModeServicePrefix) {
|
||||
serviceName := s.NetworkMode[len(types.NetworkModeServicePrefix):]
|
||||
if _, err := project.GetServices(serviceName); err != nil {
|
||||
return fmt.Errorf("service %q not found for network_mode 'service:%s'", serviceName, serviceName)
|
||||
}
|
||||
}
|
||||
|
||||
if strings.HasPrefix(s.NetworkMode, types.NetworkModeContainerPrefix) {
|
||||
containerName := s.NetworkMode[len(types.NetworkModeContainerPrefix):]
|
||||
if _, err := project.GetByContainerName(containerName); err != nil {
|
||||
return fmt.Errorf("service with container_name %q not found for network_mode 'container:%s'", containerName, containerName)
|
||||
}
|
||||
}
|
||||
|
||||
for _, volume := range s.Volumes {
|
||||
switch volume.Type {
|
||||
case types.VolumeTypeVolume:
|
||||
if volume.Source != "" { // non anonymous volumes
|
||||
if _, ok := project.Volumes[volume.Source]; !ok {
|
||||
return errors.Wrap(errdefs.ErrInvalid, fmt.Sprintf("service %q refers to undefined volume %s", s.Name, volume.Source))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, secret := range s.Secrets {
|
||||
if _, ok := project.Secrets[secret.Source]; !ok {
|
||||
return errors.Wrap(errdefs.ErrInvalid, fmt.Sprintf("service %q refers to undefined secret %s", s.Name, secret.Source))
|
||||
}
|
||||
}
|
||||
for _, config := range s.Configs {
|
||||
if _, ok := project.Configs[config.Source]; !ok {
|
||||
return errors.Wrap(errdefs.ErrInvalid, fmt.Sprintf("service %q refers to undefined config %s", s.Name, config.Source))
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
154
vendor/github.com/compose-spec/compose-go/loader/volume.go
generated
vendored
Normal file
154
vendor/github.com/compose-spec/compose-go/loader/volume.go
generated
vendored
Normal file
@ -0,0 +1,154 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification 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 loader
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const endOfSpec = rune(0)
|
||||
|
||||
// ParseVolume parses a volume spec without any knowledge of the target platform
|
||||
func ParseVolume(spec string) (types.ServiceVolumeConfig, error) {
|
||||
volume := types.ServiceVolumeConfig{}
|
||||
|
||||
switch len(spec) {
|
||||
case 0:
|
||||
return volume, errors.New("invalid empty volume spec")
|
||||
case 1, 2:
|
||||
volume.Target = spec
|
||||
volume.Type = string(types.VolumeTypeVolume)
|
||||
return volume, nil
|
||||
}
|
||||
|
||||
buffer := []rune{}
|
||||
for _, char := range spec + string(endOfSpec) {
|
||||
switch {
|
||||
case isWindowsDrive(buffer, char):
|
||||
buffer = append(buffer, char)
|
||||
case char == ':' || char == endOfSpec:
|
||||
if err := populateFieldFromBuffer(char, buffer, &volume); err != nil {
|
||||
populateType(&volume)
|
||||
return volume, errors.Wrapf(err, "invalid spec: %s", spec)
|
||||
}
|
||||
buffer = []rune{}
|
||||
default:
|
||||
buffer = append(buffer, char)
|
||||
}
|
||||
}
|
||||
|
||||
populateType(&volume)
|
||||
return volume, nil
|
||||
}
|
||||
|
||||
func isWindowsDrive(buffer []rune, char rune) bool {
|
||||
return char == ':' && len(buffer) == 1 && unicode.IsLetter(buffer[0])
|
||||
}
|
||||
|
||||
func populateFieldFromBuffer(char rune, buffer []rune, volume *types.ServiceVolumeConfig) error {
|
||||
strBuffer := string(buffer)
|
||||
switch {
|
||||
case len(buffer) == 0:
|
||||
return errors.New("empty section between colons")
|
||||
// Anonymous volume
|
||||
case volume.Source == "" && char == endOfSpec:
|
||||
volume.Target = strBuffer
|
||||
return nil
|
||||
case volume.Source == "":
|
||||
volume.Source = strBuffer
|
||||
return nil
|
||||
case volume.Target == "":
|
||||
volume.Target = strBuffer
|
||||
return nil
|
||||
case char == ':':
|
||||
return errors.New("too many colons")
|
||||
}
|
||||
for _, option := range strings.Split(strBuffer, ",") {
|
||||
switch option {
|
||||
case "ro":
|
||||
volume.ReadOnly = true
|
||||
case "rw":
|
||||
volume.ReadOnly = false
|
||||
case "nocopy":
|
||||
volume.Volume = &types.ServiceVolumeVolume{NoCopy: true}
|
||||
default:
|
||||
if isBindOption(option) {
|
||||
volume.Bind = &types.ServiceVolumeBind{Propagation: option}
|
||||
}
|
||||
// ignore unknown options
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var Propagations = []string{
|
||||
types.PropagationRPrivate,
|
||||
types.PropagationPrivate,
|
||||
types.PropagationRShared,
|
||||
types.PropagationShared,
|
||||
types.PropagationRSlave,
|
||||
types.PropagationSlave,
|
||||
}
|
||||
|
||||
func isBindOption(option string) bool {
|
||||
for _, propagation := range Propagations {
|
||||
if option == propagation {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func populateType(volume *types.ServiceVolumeConfig) {
|
||||
if isFilePath(volume.Source) {
|
||||
volume.Type = types.VolumeTypeBind
|
||||
if volume.Bind == nil {
|
||||
volume.Bind = &types.ServiceVolumeBind{}
|
||||
}
|
||||
// For backward compatibility with docker-compose legacy, using short notation involves
|
||||
// bind will create missing host path
|
||||
volume.Bind.CreateHostPath = true
|
||||
} else {
|
||||
volume.Type = types.VolumeTypeVolume
|
||||
if volume.Volume == nil {
|
||||
volume.Volume = &types.ServiceVolumeVolume{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func isFilePath(source string) bool {
|
||||
if source == "" {
|
||||
return false
|
||||
}
|
||||
switch source[0] {
|
||||
case '.', '/', '~':
|
||||
return true
|
||||
}
|
||||
|
||||
// windows named pipes
|
||||
if strings.HasPrefix(source, `\\`) {
|
||||
return true
|
||||
}
|
||||
|
||||
first, nextIndex := utf8.DecodeRuneInString(source)
|
||||
return isWindowsDrive([]rune{first}, rune(source[nextIndex]))
|
||||
}
|
82
vendor/github.com/compose-spec/compose-go/loader/windows_path.go
generated
vendored
Normal file
82
vendor/github.com/compose-spec/compose-go/loader/windows_path.go
generated
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
Copyright 2020 The Compose Specification 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 loader
|
||||
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// https://github.com/golang/go/blob/master/LICENSE
|
||||
|
||||
// This file contains utilities to check for Windows absolute paths on Linux.
|
||||
// The code in this file was largely copied from the Golang filepath package
|
||||
// https://github.com/golang/go/blob/1d0e94b1e13d5e8a323a63cd1cc1ef95290c9c36/src/path/filepath/path_windows.go#L12-L65
|
||||
|
||||
func isSlash(c uint8) bool {
|
||||
return c == '\\' || c == '/'
|
||||
}
|
||||
|
||||
// isAbs reports whether the path is a Windows absolute path.
|
||||
func isAbs(path string) (b bool) {
|
||||
l := volumeNameLen(path)
|
||||
if l == 0 {
|
||||
return false
|
||||
}
|
||||
path = path[l:]
|
||||
if path == "" {
|
||||
return false
|
||||
}
|
||||
return isSlash(path[0])
|
||||
}
|
||||
|
||||
// volumeNameLen returns length of the leading volume name on Windows.
|
||||
// It returns 0 elsewhere.
|
||||
// nolint: gocyclo
|
||||
func volumeNameLen(path string) int {
|
||||
if len(path) < 2 {
|
||||
return 0
|
||||
}
|
||||
// with drive letter
|
||||
c := path[0]
|
||||
if path[1] == ':' && ('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z') {
|
||||
return 2
|
||||
}
|
||||
// is it UNC? https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
|
||||
if l := len(path); l >= 5 && isSlash(path[0]) && isSlash(path[1]) &&
|
||||
!isSlash(path[2]) && path[2] != '.' {
|
||||
// first, leading `\\` and next shouldn't be `\`. its server name.
|
||||
for n := 3; n < l-1; n++ {
|
||||
// second, next '\' shouldn't be repeated.
|
||||
if isSlash(path[n]) {
|
||||
n++
|
||||
// third, following something characters. its share name.
|
||||
if !isSlash(path[n]) {
|
||||
if path[n] == '.' {
|
||||
break
|
||||
}
|
||||
for ; n < l; n++ {
|
||||
if isSlash(path[n]) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
Reference in New Issue
Block a user