mirror of https://github.com/docker/buildx.git
Merge pull request #2609 from glours/bump-compose-go-v2.1.4
bump compose-go to v2.1.4
This commit is contained in:
commit
12d431d1b4
2
go.mod
2
go.mod
|
@ -6,7 +6,7 @@ require (
|
||||||
github.com/Masterminds/semver/v3 v3.2.1
|
github.com/Masterminds/semver/v3 v3.2.1
|
||||||
github.com/Microsoft/go-winio v0.6.2
|
github.com/Microsoft/go-winio v0.6.2
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.26.6
|
github.com/aws/aws-sdk-go-v2/config v1.26.6
|
||||||
github.com/compose-spec/compose-go/v2 v2.1.3
|
github.com/compose-spec/compose-go/v2 v2.1.4
|
||||||
github.com/containerd/console v1.0.4
|
github.com/containerd/console v1.0.4
|
||||||
github.com/containerd/containerd v1.7.19
|
github.com/containerd/containerd v1.7.19
|
||||||
github.com/containerd/continuity v0.4.3
|
github.com/containerd/continuity v0.4.3
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -84,8 +84,8 @@ github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+g
|
||||||
github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||||
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE=
|
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE=
|
||||||
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=
|
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=
|
||||||
github.com/compose-spec/compose-go/v2 v2.1.3 h1:bD67uqLuL/XgkAK6ir3xZvNLFPxPScEi1KW7R5esrLE=
|
github.com/compose-spec/compose-go/v2 v2.1.4 h1:+1UKMvbBJo22Bpulgb9KAeZwRT99hANf3tDQVeG6ZJo=
|
||||||
github.com/compose-spec/compose-go/v2 v2.1.3/go.mod h1:lFN0DrMxIncJGYAXTfWuajfwj5haBJqrBkarHcnjJKc=
|
github.com/compose-spec/compose-go/v2 v2.1.4/go.mod h1:lFN0DrMxIncJGYAXTfWuajfwj5haBJqrBkarHcnjJKc=
|
||||||
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
|
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
|
||||||
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
|
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
|
||||||
github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro=
|
github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro=
|
||||||
|
|
|
@ -22,9 +22,14 @@ import (
|
||||||
"github.com/compose-spec/compose-go/v2/types"
|
"github.com/compose-spec/compose-go/v2/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Will update the environment variables for the format {- VAR} (without interpolation)
|
// ResolveEnvironment update the environment variables for the format {- VAR} (without interpolation)
|
||||||
// This function should resolve context environment vars for include (passed in env_file)
|
func ResolveEnvironment(dict map[string]any, environment types.Mapping) {
|
||||||
func resolveServicesEnvironment(dict map[string]any, config types.ConfigDetails) {
|
resolveServicesEnvironment(dict, environment)
|
||||||
|
resolveSecretsEnvironment(dict, environment)
|
||||||
|
resolveConfigsEnvironment(dict, environment)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolveServicesEnvironment(dict map[string]any, environment types.Mapping) {
|
||||||
services, ok := dict["services"].(map[string]any)
|
services, ok := dict["services"].(map[string]any)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
|
@ -45,7 +50,7 @@ func resolveServicesEnvironment(dict map[string]any, config types.ConfigDetails)
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if found, ok := config.Environment[varEnv]; ok {
|
if found, ok := environment[varEnv]; ok {
|
||||||
envs = append(envs, fmt.Sprintf("%s=%s", varEnv, found))
|
envs = append(envs, fmt.Sprintf("%s=%s", varEnv, found))
|
||||||
} else {
|
} else {
|
||||||
// either does not exist or it was already resolved in interpolation
|
// either does not exist or it was already resolved in interpolation
|
||||||
|
@ -57,3 +62,49 @@ func resolveServicesEnvironment(dict map[string]any, config types.ConfigDetails)
|
||||||
}
|
}
|
||||||
dict["services"] = services
|
dict["services"] = services
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func resolveSecretsEnvironment(dict map[string]any, environment types.Mapping) {
|
||||||
|
secrets, ok := dict["secrets"].(map[string]any)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, cfg := range secrets {
|
||||||
|
secret, ok := cfg.(map[string]any)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
env, ok := secret["environment"].(string)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if found, ok := environment[env]; ok {
|
||||||
|
secret["content"] = found
|
||||||
|
}
|
||||||
|
secrets[name] = secret
|
||||||
|
}
|
||||||
|
dict["secrets"] = secrets
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolveConfigsEnvironment(dict map[string]any, environment types.Mapping) {
|
||||||
|
configs, ok := dict["configs"].(map[string]any)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, cfg := range configs {
|
||||||
|
config, ok := cfg.(map[string]any)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
env, ok := config["environment"].(string)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if found, ok := environment[env]; ok {
|
||||||
|
config["content"] = found
|
||||||
|
}
|
||||||
|
configs[name] = config
|
||||||
|
}
|
||||||
|
dict["configs"] = configs
|
||||||
|
}
|
||||||
|
|
|
@ -22,7 +22,11 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/compose-spec/compose-go/v2/consts"
|
"github.com/compose-spec/compose-go/v2/consts"
|
||||||
|
"github.com/compose-spec/compose-go/v2/interpolation"
|
||||||
"github.com/compose-spec/compose-go/v2/override"
|
"github.com/compose-spec/compose-go/v2/override"
|
||||||
|
"github.com/compose-spec/compose-go/v2/paths"
|
||||||
|
"github.com/compose-spec/compose-go/v2/template"
|
||||||
|
"github.com/compose-spec/compose-go/v2/transform"
|
||||||
"github.com/compose-spec/compose-go/v2/types"
|
"github.com/compose-spec/compose-go/v2/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -67,25 +71,43 @@ func applyServiceExtends(ctx context.Context, name string, services map[string]a
|
||||||
)
|
)
|
||||||
switch v := extends.(type) {
|
switch v := extends.(type) {
|
||||||
case map[string]any:
|
case map[string]any:
|
||||||
|
if opts.Interpolate != nil {
|
||||||
|
v, err = interpolation.Interpolate(v, *opts.Interpolate)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
ref = v["service"].(string)
|
ref = v["service"].(string)
|
||||||
file = v["file"]
|
file = v["file"]
|
||||||
opts.ProcessEvent("extends", v)
|
opts.ProcessEvent("extends", v)
|
||||||
case string:
|
case string:
|
||||||
|
if opts.Interpolate != nil {
|
||||||
|
v, err = opts.Interpolate.Substitute(v, template.Mapping(opts.Interpolate.LookupValue))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
ref = v
|
ref = v
|
||||||
opts.ProcessEvent("extends", map[string]any{"service": ref})
|
opts.ProcessEvent("extends", map[string]any{"service": ref})
|
||||||
}
|
}
|
||||||
|
|
||||||
var base any
|
var (
|
||||||
|
base any
|
||||||
|
processor PostProcessor
|
||||||
|
)
|
||||||
|
|
||||||
if file != nil {
|
if file != nil {
|
||||||
filename = file.(string)
|
refFilename := file.(string)
|
||||||
services, err = getExtendsBaseFromFile(ctx, ref, filename, opts, tracker)
|
services, processor, err = getExtendsBaseFromFile(ctx, name, ref, filename, refFilename, opts, tracker)
|
||||||
|
post = append(post, processor)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
filename = refFilename
|
||||||
} else {
|
} else {
|
||||||
_, ok := services[ref]
|
_, ok := services[ref]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("cannot extend service %q in %s: service not found", name, filename)
|
return nil, fmt.Errorf("cannot extend service %q in %s: service %q not found", name, filename, ref)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,47 +143,71 @@ func applyServiceExtends(ctx context.Context, name string, services map[string]a
|
||||||
return merged, nil
|
return merged, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getExtendsBaseFromFile(ctx context.Context, name string, path string, opts *Options, ct *cycleTracker) (map[string]any, error) {
|
func getExtendsBaseFromFile(
|
||||||
|
ctx context.Context,
|
||||||
|
name, ref string,
|
||||||
|
path, refPath string,
|
||||||
|
opts *Options,
|
||||||
|
ct *cycleTracker,
|
||||||
|
) (map[string]any, PostProcessor, error) {
|
||||||
for _, loader := range opts.ResourceLoaders {
|
for _, loader := range opts.ResourceLoaders {
|
||||||
if !loader.Accept(path) {
|
if !loader.Accept(refPath) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
local, err := loader.Load(ctx, path)
|
local, err := loader.Load(ctx, refPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
localdir := filepath.Dir(local)
|
localdir := filepath.Dir(local)
|
||||||
relworkingdir := loader.Dir(path)
|
relworkingdir := loader.Dir(refPath)
|
||||||
|
|
||||||
extendsOpts := opts.clone()
|
extendsOpts := opts.clone()
|
||||||
// replace localResourceLoader with a new flavour, using extended file base path
|
// replace localResourceLoader with a new flavour, using extended file base path
|
||||||
extendsOpts.ResourceLoaders = append(opts.RemoteResourceLoaders(), localResourceLoader{
|
extendsOpts.ResourceLoaders = append(opts.RemoteResourceLoaders(), localResourceLoader{
|
||||||
WorkingDir: localdir,
|
WorkingDir: localdir,
|
||||||
})
|
})
|
||||||
extendsOpts.ResolvePaths = true
|
extendsOpts.ResolvePaths = false // we do relative path resolution after file has been loaded
|
||||||
extendsOpts.SkipNormalization = true
|
extendsOpts.SkipNormalization = true
|
||||||
extendsOpts.SkipConsistencyCheck = true
|
extendsOpts.SkipConsistencyCheck = true
|
||||||
extendsOpts.SkipInclude = true
|
extendsOpts.SkipInclude = true
|
||||||
extendsOpts.SkipExtends = true // we manage extends recursively based on raw service definition
|
extendsOpts.SkipExtends = true // we manage extends recursively based on raw service definition
|
||||||
extendsOpts.SkipValidation = true // we validate the merge result
|
extendsOpts.SkipValidation = true // we validate the merge result
|
||||||
extendsOpts.SkipDefaultValues = true
|
extendsOpts.SkipDefaultValues = true
|
||||||
source, err := loadYamlModel(ctx, types.ConfigDetails{
|
source, processor, err := loadYamlFile(ctx, types.ConfigFile{Filename: local},
|
||||||
WorkingDir: relworkingdir,
|
extendsOpts, relworkingdir, nil, ct, map[string]any{}, nil)
|
||||||
ConfigFiles: []types.ConfigFile{
|
|
||||||
{Filename: local},
|
|
||||||
},
|
|
||||||
}, extendsOpts, ct, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
services := source["services"].(map[string]any)
|
services := source["services"].(map[string]any)
|
||||||
_, ok := services[name]
|
_, ok := services[ref]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("cannot extend service %q in %s: service not found", name, path)
|
return nil, nil, fmt.Errorf(
|
||||||
|
"cannot extend service %q in %s: service %q not found in %s",
|
||||||
|
name,
|
||||||
|
path,
|
||||||
|
ref,
|
||||||
|
refPath,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return services, nil
|
|
||||||
|
// Attempt to make a canonical model so ResolveRelativePaths can operate on source:target short syntaxes
|
||||||
|
source, err = transform.Canonical(source, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var remotes []paths.RemoteResource
|
||||||
|
for _, loader := range opts.RemoteResourceLoaders() {
|
||||||
|
remotes = append(remotes, loader.Accept)
|
||||||
|
}
|
||||||
|
err = paths.ResolveRelativePaths(source, relworkingdir, remotes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return services, processor, nil
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("cannot read %s", path)
|
return nil, nil, fmt.Errorf("cannot read %s", refPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func deepClone(value any) any {
|
func deepClone(value any) any {
|
||||||
|
|
|
@ -30,7 +30,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// loadIncludeConfig parse the required config from raw yaml
|
// loadIncludeConfig parse the required config from raw yaml
|
||||||
func loadIncludeConfig(source any) ([]types.IncludeConfig, error) {
|
func loadIncludeConfig(source any, options *Options) ([]types.IncludeConfig, error) {
|
||||||
if source == nil {
|
if source == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
@ -45,21 +45,32 @@ func loadIncludeConfig(source any) ([]types.IncludeConfig, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if options.Interpolate != nil {
|
||||||
|
for i, config := range configs {
|
||||||
|
interpolated, err := interp.Interpolate(config.(map[string]any), *options.Interpolate)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
configs[i] = interpolated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var requires []types.IncludeConfig
|
var requires []types.IncludeConfig
|
||||||
err := Transform(source, &requires)
|
err := Transform(source, &requires)
|
||||||
return requires, err
|
return requires, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func ApplyInclude(ctx context.Context, configDetails types.ConfigDetails, model map[string]any, options *Options, included []string) error {
|
func ApplyInclude(ctx context.Context, workingDir string, environment types.Mapping, model map[string]any, options *Options, included []string) error {
|
||||||
includeConfig, err := loadIncludeConfig(model["include"])
|
includeConfig, err := loadIncludeConfig(model["include"], options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, r := range includeConfig {
|
for _, r := range includeConfig {
|
||||||
for _, listener := range options.Listeners {
|
for _, listener := range options.Listeners {
|
||||||
listener("include", map[string]any{
|
listener("include", map[string]any{
|
||||||
"path": r.Path,
|
"path": r.Path,
|
||||||
"workingdir": configDetails.WorkingDir,
|
"workingdir": workingDir,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +94,7 @@ func ApplyInclude(ctx context.Context, configDetails types.ConfigDetails, model
|
||||||
r.ProjectDirectory = filepath.Dir(path)
|
r.ProjectDirectory = filepath.Dir(path)
|
||||||
case !filepath.IsAbs(r.ProjectDirectory):
|
case !filepath.IsAbs(r.ProjectDirectory):
|
||||||
relworkingdir = loader.Dir(r.ProjectDirectory)
|
relworkingdir = loader.Dir(r.ProjectDirectory)
|
||||||
r.ProjectDirectory = filepath.Join(configDetails.WorkingDir, r.ProjectDirectory)
|
r.ProjectDirectory = filepath.Join(workingDir, r.ProjectDirectory)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
relworkingdir = r.ProjectDirectory
|
relworkingdir = r.ProjectDirectory
|
||||||
|
@ -117,7 +128,7 @@ func ApplyInclude(ctx context.Context, configDetails types.ConfigDetails, model
|
||||||
envFile := []string{}
|
envFile := []string{}
|
||||||
for _, f := range r.EnvFile {
|
for _, f := range r.EnvFile {
|
||||||
if !filepath.IsAbs(f) {
|
if !filepath.IsAbs(f) {
|
||||||
f = filepath.Join(configDetails.WorkingDir, f)
|
f = filepath.Join(workingDir, f)
|
||||||
s, err := os.Stat(f)
|
s, err := os.Stat(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -131,7 +142,7 @@ func ApplyInclude(ctx context.Context, configDetails types.ConfigDetails, model
|
||||||
r.EnvFile = envFile
|
r.EnvFile = envFile
|
||||||
}
|
}
|
||||||
|
|
||||||
envFromFile, err := dotenv.GetEnvFromFile(configDetails.Environment, r.EnvFile)
|
envFromFile, err := dotenv.GetEnvFromFile(environment, r.EnvFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -139,7 +150,7 @@ func ApplyInclude(ctx context.Context, configDetails types.ConfigDetails, model
|
||||||
config := types.ConfigDetails{
|
config := types.ConfigDetails{
|
||||||
WorkingDir: relworkingdir,
|
WorkingDir: relworkingdir,
|
||||||
ConfigFiles: types.ToConfigFiles(r.Path),
|
ConfigFiles: types.ToConfigFiles(r.Path),
|
||||||
Environment: configDetails.Environment.Clone().Merge(envFromFile),
|
Environment: environment.Clone().Merge(envFromFile),
|
||||||
}
|
}
|
||||||
loadOptions.Interpolate = &interp.Options{
|
loadOptions.Interpolate = &interp.Options{
|
||||||
Substitute: options.Interpolate.Substitute,
|
Substitute: options.Interpolate.Substitute,
|
||||||
|
|
|
@ -89,7 +89,7 @@ var versionWarning []string
|
||||||
|
|
||||||
func (o *Options) warnObsoleteVersion(file string) {
|
func (o *Options) warnObsoleteVersion(file string) {
|
||||||
if !slices.Contains(versionWarning, file) {
|
if !slices.Contains(versionWarning, file) {
|
||||||
logrus.Warning(fmt.Sprintf("%s: `version` is obsolete", file))
|
logrus.Warning(fmt.Sprintf("%s: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion", file))
|
||||||
}
|
}
|
||||||
versionWarning = append(versionWarning, file)
|
versionWarning = append(versionWarning, file)
|
||||||
}
|
}
|
||||||
|
@ -358,100 +358,19 @@ func loadYamlModel(ctx context.Context, config types.ConfigDetails, opts *Option
|
||||||
dict = map[string]interface{}{}
|
dict = map[string]interface{}{}
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
workingDir, environment := config.WorkingDir, config.Environment
|
||||||
|
|
||||||
for _, file := range config.ConfigFiles {
|
for _, file := range config.ConfigFiles {
|
||||||
fctx := context.WithValue(ctx, consts.ComposeFileKey{}, file.Filename)
|
dict, _, err = loadYamlFile(ctx, file, opts, workingDir, environment, ct, dict, included)
|
||||||
if file.Content == nil && file.Config == nil {
|
if err != nil {
|
||||||
content, err := os.ReadFile(file.Filename)
|
return nil, err
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
file.Content = content
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
processRawYaml := func(raw interface{}, processors ...PostProcessor) error {
|
if opts.Interpolate != nil && !opts.SkipInterpolation {
|
||||||
converted, err := convertToStringKeysRecursive(raw, "")
|
dict, err = interp.Interpolate(dict, *opts.Interpolate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
|
||||||
cfg, ok := converted.(map[string]interface{})
|
|
||||||
if !ok {
|
|
||||||
return errors.New("Top-level object must be a mapping")
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.Interpolate != nil && !opts.SkipInterpolation {
|
|
||||||
cfg, err = interp.Interpolate(cfg, *opts.Interpolate)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fixEmptyNotNull(cfg)
|
|
||||||
|
|
||||||
if !opts.SkipExtends {
|
|
||||||
err = ApplyExtends(fctx, cfg, opts, ct, processors...)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, processor := range processors {
|
|
||||||
if err := processor.Apply(dict); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !opts.SkipInclude {
|
|
||||||
included = append(included, config.ConfigFiles[0].Filename)
|
|
||||||
err = ApplyInclude(ctx, config, cfg, opts, included)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dict, err = override.Merge(dict, cfg)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
dict, err = override.EnforceUnicity(dict)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !opts.SkipValidation {
|
|
||||||
if err := schema.Validate(dict); err != nil {
|
|
||||||
return fmt.Errorf("validating %s: %w", file.Filename, err)
|
|
||||||
}
|
|
||||||
if _, ok := dict["version"]; ok {
|
|
||||||
opts.warnObsoleteVersion(file.Filename)
|
|
||||||
delete(dict, "version")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if file.Config == nil {
|
|
||||||
r := bytes.NewReader(file.Content)
|
|
||||||
decoder := yaml.NewDecoder(r)
|
|
||||||
for {
|
|
||||||
var raw interface{}
|
|
||||||
processor := &ResetProcessor{target: &raw}
|
|
||||||
err := decoder.Decode(processor)
|
|
||||||
if err != nil && errors.Is(err, io.EOF) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := processRawYaml(raw, processor); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err := processRawYaml(file.Config); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -460,7 +379,6 @@ func loadYamlModel(ctx context.Context, config types.ConfigDetails, opts *Option
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Canonical transformation can reveal duplicates, typically as ports can be a range and conflict with an override
|
|
||||||
dict, err = override.EnforceUnicity(dict)
|
dict, err = override.EnforceUnicity(dict)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -489,11 +407,98 @@ func loadYamlModel(ctx context.Context, config types.ConfigDetails, opts *Option
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
resolveServicesEnvironment(dict, config)
|
ResolveEnvironment(dict, config.Environment)
|
||||||
|
|
||||||
return dict, nil
|
return dict, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func loadYamlFile(ctx context.Context, file types.ConfigFile, opts *Options, workingDir string, environment types.Mapping, ct *cycleTracker, dict map[string]interface{}, included []string) (map[string]interface{}, PostProcessor, error) {
|
||||||
|
ctx = context.WithValue(ctx, consts.ComposeFileKey{}, file.Filename)
|
||||||
|
if file.Content == nil && file.Config == nil {
|
||||||
|
content, err := os.ReadFile(file.Filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
file.Content = content
|
||||||
|
}
|
||||||
|
|
||||||
|
processRawYaml := func(raw interface{}, processors ...PostProcessor) error {
|
||||||
|
converted, err := convertToStringKeysRecursive(raw, "")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cfg, ok := converted.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return errors.New("Top-level object must be a mapping")
|
||||||
|
}
|
||||||
|
|
||||||
|
fixEmptyNotNull(cfg)
|
||||||
|
|
||||||
|
if !opts.SkipExtends {
|
||||||
|
err = ApplyExtends(ctx, cfg, opts, ct, processors...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, processor := range processors {
|
||||||
|
if err := processor.Apply(dict); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !opts.SkipInclude {
|
||||||
|
included = append(included, file.Filename)
|
||||||
|
err = ApplyInclude(ctx, workingDir, environment, cfg, opts, included)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dict, err = override.Merge(dict, cfg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !opts.SkipValidation {
|
||||||
|
if err := schema.Validate(dict); err != nil {
|
||||||
|
return fmt.Errorf("validating %s: %w", file.Filename, err)
|
||||||
|
}
|
||||||
|
if _, ok := dict["version"]; ok {
|
||||||
|
opts.warnObsoleteVersion(file.Filename)
|
||||||
|
delete(dict, "version")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var processor PostProcessor
|
||||||
|
if file.Config == nil {
|
||||||
|
r := bytes.NewReader(file.Content)
|
||||||
|
decoder := yaml.NewDecoder(r)
|
||||||
|
for {
|
||||||
|
var raw interface{}
|
||||||
|
reset := &ResetProcessor{target: &raw}
|
||||||
|
err := decoder.Decode(reset)
|
||||||
|
if err != nil && errors.Is(err, io.EOF) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
processor = reset
|
||||||
|
if err := processRawYaml(raw, processor); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := processRawYaml(file.Config); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dict, processor, nil
|
||||||
|
}
|
||||||
|
|
||||||
func load(ctx context.Context, configDetails types.ConfigDetails, opts *Options, loaded []string) (map[string]interface{}, error) {
|
func load(ctx context.Context, configDetails types.ConfigDetails, opts *Options, loaded []string) (map[string]interface{}, error) {
|
||||||
mainFile := configDetails.ConfigFiles[0].Filename
|
mainFile := configDetails.ConfigFiles[0].Filename
|
||||||
for _, f := range loaded {
|
for _, f := range loaded {
|
||||||
|
|
|
@ -52,14 +52,14 @@ func Normalize(dict map[string]any, env types.Mapping) (map[string]any, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if a, ok := build["args"]; ok {
|
if a, ok := build["args"]; ok {
|
||||||
build["args"], _ = resolve(a, fn)
|
build["args"], _ = resolve(a, fn, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
service["build"] = build
|
service["build"] = build
|
||||||
}
|
}
|
||||||
|
|
||||||
if e, ok := service["environment"]; ok {
|
if e, ok := service["environment"]; ok {
|
||||||
service["environment"], _ = resolve(e, fn)
|
service["environment"], _ = resolve(e, fn, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
var dependsOn map[string]any
|
var dependsOn map[string]any
|
||||||
|
@ -178,12 +178,12 @@ func normalizeNetworks(dict map[string]any) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolve(a any, fn func(s string) (string, bool)) (any, bool) {
|
func resolve(a any, fn func(s string) (string, bool), keepEmpty bool) (any, bool) {
|
||||||
switch v := a.(type) {
|
switch v := a.(type) {
|
||||||
case []any:
|
case []any:
|
||||||
var resolved []any
|
var resolved []any
|
||||||
for _, val := range v {
|
for _, val := range v {
|
||||||
if r, ok := resolve(val, fn); ok {
|
if r, ok := resolve(val, fn, keepEmpty); ok {
|
||||||
resolved = append(resolved, r)
|
resolved = append(resolved, r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -197,6 +197,8 @@ func resolve(a any, fn func(s string) (string, bool)) (any, bool) {
|
||||||
}
|
}
|
||||||
if s, ok := fn(key); ok {
|
if s, ok := fn(key); ok {
|
||||||
resolved[key] = s
|
resolved[key] = s
|
||||||
|
} else if keepEmpty {
|
||||||
|
resolved[key] = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return resolved, true
|
return resolved, true
|
||||||
|
@ -205,6 +207,9 @@ func resolve(a any, fn func(s string) (string, bool)) (any, bool) {
|
||||||
if val, ok := fn(v); ok {
|
if val, ok := fn(v); ok {
|
||||||
return fmt.Sprintf("%s=%s", v, val), true
|
return fmt.Sprintf("%s=%s", v, val), true
|
||||||
}
|
}
|
||||||
|
if keepEmpty {
|
||||||
|
return v, true
|
||||||
|
}
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
return v, true
|
return v, true
|
||||||
|
|
|
@ -38,7 +38,7 @@ func ResolveRelativePaths(project map[string]any, base string, remotes []RemoteR
|
||||||
"services.*.build.additional_contexts.*": r.absContextPath,
|
"services.*.build.additional_contexts.*": r.absContextPath,
|
||||||
"services.*.env_file.*.path": r.absPath,
|
"services.*.env_file.*.path": r.absPath,
|
||||||
"services.*.extends.file": r.absExtendsPath,
|
"services.*.extends.file": r.absExtendsPath,
|
||||||
"services.*.develop.watch.*.path": r.absPath,
|
"services.*.develop.watch.*.path": r.absSymbolicLink,
|
||||||
"services.*.volumes.*": r.absVolumeMount,
|
"services.*.volumes.*": r.absVolumeMount,
|
||||||
"configs.*.file": r.maybeUnixPath,
|
"configs.*.file": r.maybeUnixPath,
|
||||||
"secrets.*.file": r.maybeUnixPath,
|
"secrets.*.file": r.maybeUnixPath,
|
||||||
|
|
|
@ -19,6 +19,8 @@ package paths
|
||||||
import (
|
import (
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/compose-spec/compose-go/v2/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *relativePathsResolver) maybeUnixPath(a any) (any, error) {
|
func (r *relativePathsResolver) maybeUnixPath(a any) (any, error) {
|
||||||
|
@ -38,3 +40,15 @@ func (r *relativePathsResolver) maybeUnixPath(a any) (any, error) {
|
||||||
}
|
}
|
||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *relativePathsResolver) absSymbolicLink(value any) (any, error) {
|
||||||
|
abs, err := r.absPath(value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
str, ok := abs.(string)
|
||||||
|
if !ok {
|
||||||
|
return abs, nil
|
||||||
|
}
|
||||||
|
return utils.ResolveSymbolicLink(str)
|
||||||
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
|
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"pattern": "^[a-z0-9][a-z0-9_-]*$",
|
|
||||||
"description": "define the Compose project name, until user defines one explicitly."
|
"description": "define the Compose project name, until user defines one explicitly."
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -94,7 +93,7 @@
|
||||||
"develop": {"$ref": "#/definitions/development"},
|
"develop": {"$ref": "#/definitions/development"},
|
||||||
"deploy": {"$ref": "#/definitions/deployment"},
|
"deploy": {"$ref": "#/definitions/deployment"},
|
||||||
"annotations": {"$ref": "#/definitions/list_or_dict"},
|
"annotations": {"$ref": "#/definitions/list_or_dict"},
|
||||||
"attach": {"type": "boolean"},
|
"attach": {"type": ["boolean", "string"]},
|
||||||
"build": {
|
"build": {
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{"type": "string"},
|
{"type": "string"},
|
||||||
|
@ -110,15 +109,15 @@
|
||||||
"labels": {"$ref": "#/definitions/list_or_dict"},
|
"labels": {"$ref": "#/definitions/list_or_dict"},
|
||||||
"cache_from": {"type": "array", "items": {"type": "string"}},
|
"cache_from": {"type": "array", "items": {"type": "string"}},
|
||||||
"cache_to": {"type": "array", "items": {"type": "string"}},
|
"cache_to": {"type": "array", "items": {"type": "string"}},
|
||||||
"no_cache": {"type": "boolean"},
|
"no_cache": {"type": ["boolean", "string"]},
|
||||||
"additional_contexts": {"$ref": "#/definitions/list_or_dict"},
|
"additional_contexts": {"$ref": "#/definitions/list_or_dict"},
|
||||||
"network": {"type": "string"},
|
"network": {"type": "string"},
|
||||||
"pull": {"type": "boolean"},
|
"pull": {"type": ["boolean", "string"]},
|
||||||
"target": {"type": "string"},
|
"target": {"type": "string"},
|
||||||
"shm_size": {"type": ["integer", "string"]},
|
"shm_size": {"type": ["integer", "string"]},
|
||||||
"extra_hosts": {"$ref": "#/definitions/list_or_dict"},
|
"extra_hosts": {"$ref": "#/definitions/list_or_dict"},
|
||||||
"isolation": {"type": "string"},
|
"isolation": {"type": "string"},
|
||||||
"privileged": {"type": "boolean"},
|
"privileged": {"type": ["boolean", "string"]},
|
||||||
"secrets": {"$ref": "#/definitions/service_config_or_secret"},
|
"secrets": {"$ref": "#/definitions/service_config_or_secret"},
|
||||||
"tags": {"type": "array", "items": {"type": "string"}},
|
"tags": {"type": "array", "items": {"type": "string"}},
|
||||||
"ulimits": {"$ref": "#/definitions/ulimits"},
|
"ulimits": {"$ref": "#/definitions/ulimits"},
|
||||||
|
@ -148,7 +147,7 @@
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {"$ref": "#/definitions/blkio_limit"}
|
"items": {"$ref": "#/definitions/blkio_limit"}
|
||||||
},
|
},
|
||||||
"weight": {"type": "integer"},
|
"weight": {"type": ["integer", "string"]},
|
||||||
"weight_device": {
|
"weight_device": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {"$ref": "#/definitions/blkio_weight"}
|
"items": {"$ref": "#/definitions/blkio_weight"}
|
||||||
|
@ -156,15 +155,21 @@
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
"cap_add": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
|
"cap_add": {"type": "array", "items": {"type": "string"}},
|
||||||
"cap_drop": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
|
"cap_drop": {"type": "array", "items": {"type": "string"}},
|
||||||
"cgroup": {"type": "string", "enum": ["host", "private"]},
|
"cgroup": {"type": "string", "enum": ["host", "private"]},
|
||||||
"cgroup_parent": {"type": "string"},
|
"cgroup_parent": {"type": "string"},
|
||||||
"command": {"$ref": "#/definitions/command"},
|
"command": {"$ref": "#/definitions/command"},
|
||||||
"configs": {"$ref": "#/definitions/service_config_or_secret"},
|
"configs": {"$ref": "#/definitions/service_config_or_secret"},
|
||||||
"container_name": {"type": "string"},
|
"container_name": {"type": "string"},
|
||||||
"cpu_count": {"type": "integer", "minimum": 0},
|
"cpu_count": {"oneOf": [
|
||||||
"cpu_percent": {"type": "integer", "minimum": 0, "maximum": 100},
|
{"type": "string"},
|
||||||
|
{"type": "integer", "minimum": 0}
|
||||||
|
]},
|
||||||
|
"cpu_percent": {"oneOf": [
|
||||||
|
{"type": "string"},
|
||||||
|
{"type": "integer", "minimum": 0, "maximum": 100}
|
||||||
|
]},
|
||||||
"cpu_shares": {"type": ["number", "string"]},
|
"cpu_shares": {"type": ["number", "string"]},
|
||||||
"cpu_quota": {"type": ["number", "string"]},
|
"cpu_quota": {"type": ["number", "string"]},
|
||||||
"cpu_period": {"type": ["number", "string"]},
|
"cpu_period": {"type": ["number", "string"]},
|
||||||
|
@ -192,8 +197,9 @@
|
||||||
"^[a-zA-Z0-9._-]+$": {
|
"^[a-zA-Z0-9._-]+$": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
|
"patternProperties": {"^x-": {}},
|
||||||
"properties": {
|
"properties": {
|
||||||
"restart": {"type": "boolean"},
|
"restart": {"type": ["boolean", "string"]},
|
||||||
"required": {
|
"required": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": true
|
"default": true
|
||||||
|
@ -210,9 +216,9 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"device_cgroup_rules": {"$ref": "#/definitions/list_of_strings"},
|
"device_cgroup_rules": {"$ref": "#/definitions/list_of_strings"},
|
||||||
"devices": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
|
"devices": {"type": "array", "items": {"type": "string"}},
|
||||||
"dns": {"$ref": "#/definitions/string_or_list"},
|
"dns": {"$ref": "#/definitions/string_or_list"},
|
||||||
"dns_opt": {"type": "array","items": {"type": "string"}, "uniqueItems": true},
|
"dns_opt": {"type": "array","items": {"type": "string"}},
|
||||||
"dns_search": {"$ref": "#/definitions/string_or_list"},
|
"dns_search": {"$ref": "#/definitions/string_or_list"},
|
||||||
"domainname": {"type": "string"},
|
"domainname": {"type": "string"},
|
||||||
"entrypoint": {"$ref": "#/definitions/command"},
|
"entrypoint": {"$ref": "#/definitions/command"},
|
||||||
|
@ -224,8 +230,7 @@
|
||||||
"items": {
|
"items": {
|
||||||
"type": ["string", "number"],
|
"type": ["string", "number"],
|
||||||
"format": "expose"
|
"format": "expose"
|
||||||
},
|
}
|
||||||
"uniqueItems": true
|
|
||||||
},
|
},
|
||||||
"extends": {
|
"extends": {
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
|
@ -242,23 +247,22 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"external_links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
|
"external_links": {"type": "array", "items": {"type": "string"}},
|
||||||
"extra_hosts": {"$ref": "#/definitions/list_or_dict"},
|
"extra_hosts": {"$ref": "#/definitions/list_or_dict"},
|
||||||
"group_add": {
|
"group_add": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"type": ["string", "number"]
|
"type": ["string", "number"]
|
||||||
},
|
}
|
||||||
"uniqueItems": true
|
|
||||||
},
|
},
|
||||||
"healthcheck": {"$ref": "#/definitions/healthcheck"},
|
"healthcheck": {"$ref": "#/definitions/healthcheck"},
|
||||||
"hostname": {"type": "string"},
|
"hostname": {"type": "string"},
|
||||||
"image": {"type": "string"},
|
"image": {"type": "string"},
|
||||||
"init": {"type": "boolean"},
|
"init": {"type": ["boolean", "string"]},
|
||||||
"ipc": {"type": "string"},
|
"ipc": {"type": "string"},
|
||||||
"isolation": {"type": "string"},
|
"isolation": {"type": "string"},
|
||||||
"labels": {"$ref": "#/definitions/list_or_dict"},
|
"labels": {"$ref": "#/definitions/list_or_dict"},
|
||||||
"links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
|
"links": {"type": "array", "items": {"type": "string"}},
|
||||||
"logging": {
|
"logging": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
|
||||||
|
@ -277,7 +281,7 @@
|
||||||
"mac_address": {"type": "string"},
|
"mac_address": {"type": "string"},
|
||||||
"mem_limit": {"type": ["number", "string"]},
|
"mem_limit": {"type": ["number", "string"]},
|
||||||
"mem_reservation": {"type": ["string", "integer"]},
|
"mem_reservation": {"type": ["string", "integer"]},
|
||||||
"mem_swappiness": {"type": "integer"},
|
"mem_swappiness": {"type": ["integer", "string"]},
|
||||||
"memswap_limit": {"type": ["number", "string"]},
|
"memswap_limit": {"type": ["number", "string"]},
|
||||||
"network_mode": {"type": "string"},
|
"network_mode": {"type": "string"},
|
||||||
"networks": {
|
"networks": {
|
||||||
|
@ -315,8 +319,11 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"oom_kill_disable": {"type": "boolean"},
|
"oom_kill_disable": {"type": ["boolean", "string"]},
|
||||||
"oom_score_adj": {"type": "integer", "minimum": -1000, "maximum": 1000},
|
"oom_score_adj": {"oneOf": [
|
||||||
|
{"type": "string"},
|
||||||
|
{"type": "integer", "minimum": -1000, "maximum": 1000}
|
||||||
|
]},
|
||||||
"pid": {"type": ["string", "null"]},
|
"pid": {"type": ["string", "null"]},
|
||||||
"pids_limit": {"type": ["number", "string"]},
|
"pids_limit": {"type": ["number", "string"]},
|
||||||
"platform": {"type": "string"},
|
"platform": {"type": "string"},
|
||||||
|
@ -324,15 +331,15 @@
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{"type": "number", "format": "ports"},
|
{"type": "number"},
|
||||||
{"type": "string", "format": "ports"},
|
{"type": "string"},
|
||||||
{
|
{
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {"type": "string"},
|
"name": {"type": "string"},
|
||||||
"mode": {"type": "string"},
|
"mode": {"type": "string"},
|
||||||
"host_ip": {"type": "string"},
|
"host_ip": {"type": "string"},
|
||||||
"target": {"type": "integer"},
|
"target": {"type": ["integer", "string"]},
|
||||||
"published": {"type": ["string", "integer"]},
|
"published": {"type": ["string", "integer"]},
|
||||||
"protocol": {"type": "string"},
|
"protocol": {"type": "string"},
|
||||||
"app_protocol": {"type": "string"}
|
"app_protocol": {"type": "string"}
|
||||||
|
@ -341,32 +348,31 @@
|
||||||
"patternProperties": {"^x-": {}}
|
"patternProperties": {"^x-": {}}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
}
|
||||||
"uniqueItems": true
|
|
||||||
},
|
},
|
||||||
"privileged": {"type": "boolean"},
|
"privileged": {"type": ["boolean", "string"]},
|
||||||
"profiles": {"$ref": "#/definitions/list_of_strings"},
|
"profiles": {"$ref": "#/definitions/list_of_strings"},
|
||||||
"pull_policy": {"type": "string", "enum": [
|
"pull_policy": {"type": "string", "enum": [
|
||||||
"always", "never", "if_not_present", "build", "missing"
|
"always", "never", "if_not_present", "build", "missing"
|
||||||
]},
|
]},
|
||||||
"read_only": {"type": "boolean"},
|
"read_only": {"type": ["boolean", "string"]},
|
||||||
"restart": {"type": "string"},
|
"restart": {"type": "string"},
|
||||||
"runtime": {
|
"runtime": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"scale": {
|
"scale": {
|
||||||
"type": "integer"
|
"type": ["integer", "string"]
|
||||||
},
|
},
|
||||||
"security_opt": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
|
"security_opt": {"type": "array", "items": {"type": "string"}},
|
||||||
"shm_size": {"type": ["number", "string"]},
|
"shm_size": {"type": ["number", "string"]},
|
||||||
"secrets": {"$ref": "#/definitions/service_config_or_secret"},
|
"secrets": {"$ref": "#/definitions/service_config_or_secret"},
|
||||||
"sysctls": {"$ref": "#/definitions/list_or_dict"},
|
"sysctls": {"$ref": "#/definitions/list_or_dict"},
|
||||||
"stdin_open": {"type": "boolean"},
|
"stdin_open": {"type": ["boolean", "string"]},
|
||||||
"stop_grace_period": {"type": "string", "format": "duration"},
|
"stop_grace_period": {"type": "string"},
|
||||||
"stop_signal": {"type": "string"},
|
"stop_signal": {"type": "string"},
|
||||||
"storage_opt": {"type": "object"},
|
"storage_opt": {"type": "object"},
|
||||||
"tmpfs": {"$ref": "#/definitions/string_or_list"},
|
"tmpfs": {"$ref": "#/definitions/string_or_list"},
|
||||||
"tty": {"type": "boolean"},
|
"tty": {"type": ["boolean", "string"]},
|
||||||
"ulimits": {"$ref": "#/definitions/ulimits"},
|
"ulimits": {"$ref": "#/definitions/ulimits"},
|
||||||
"user": {"type": "string"},
|
"user": {"type": "string"},
|
||||||
"uts": {"type": "string"},
|
"uts": {"type": "string"},
|
||||||
|
@ -383,13 +389,13 @@
|
||||||
"type": {"type": "string"},
|
"type": {"type": "string"},
|
||||||
"source": {"type": "string"},
|
"source": {"type": "string"},
|
||||||
"target": {"type": "string"},
|
"target": {"type": "string"},
|
||||||
"read_only": {"type": "boolean"},
|
"read_only": {"type": ["boolean", "string"]},
|
||||||
"consistency": {"type": "string"},
|
"consistency": {"type": "string"},
|
||||||
"bind": {
|
"bind": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"propagation": {"type": "string"},
|
"propagation": {"type": "string"},
|
||||||
"create_host_path": {"type": "boolean"},
|
"create_host_path": {"type": ["boolean", "string"]},
|
||||||
"selinux": {"type": "string", "enum": ["z", "Z"]}
|
"selinux": {"type": "string", "enum": ["z", "Z"]}
|
||||||
},
|
},
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
|
@ -398,7 +404,7 @@
|
||||||
"volume": {
|
"volume": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"nocopy": {"type": "boolean"},
|
"nocopy": {"type": ["boolean", "string"]},
|
||||||
"subpath": {"type": "string"}
|
"subpath": {"type": "string"}
|
||||||
},
|
},
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
|
@ -413,7 +419,7 @@
|
||||||
{"type": "string"}
|
{"type": "string"}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"mode": {"type": "number"}
|
"mode": {"type": ["number", "string"]}
|
||||||
},
|
},
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"patternProperties": {"^x-": {}}
|
"patternProperties": {"^x-": {}}
|
||||||
|
@ -423,13 +429,11 @@
|
||||||
"patternProperties": {"^x-": {}}
|
"patternProperties": {"^x-": {}}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
}
|
||||||
"uniqueItems": true
|
|
||||||
},
|
},
|
||||||
"volumes_from": {
|
"volumes_from": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {"type": "string"},
|
"items": {"type": "string"}
|
||||||
"uniqueItems": true
|
|
||||||
},
|
},
|
||||||
"working_dir": {"type": "string"}
|
"working_dir": {"type": "string"}
|
||||||
},
|
},
|
||||||
|
@ -441,18 +445,18 @@
|
||||||
"id": "#/definitions/healthcheck",
|
"id": "#/definitions/healthcheck",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"disable": {"type": "boolean"},
|
"disable": {"type": ["boolean", "string"]},
|
||||||
"interval": {"type": "string", "format": "duration"},
|
"interval": {"type": "string"},
|
||||||
"retries": {"type": "number"},
|
"retries": {"type": ["number", "string"]},
|
||||||
"test": {
|
"test": {
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{"type": "string"},
|
{"type": "string"},
|
||||||
{"type": "array", "items": {"type": "string"}}
|
{"type": "array", "items": {"type": "string"}}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"timeout": {"type": "string", "format": "duration"},
|
"timeout": {"type": "string"},
|
||||||
"start_period": {"type": "string", "format": "duration"},
|
"start_period": {"type": "string"},
|
||||||
"start_interval": {"type": "string", "format": "duration"}
|
"start_interval": {"type": "string"}
|
||||||
},
|
},
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"patternProperties": {"^x-": {}}
|
"patternProperties": {"^x-": {}}
|
||||||
|
@ -484,16 +488,16 @@
|
||||||
"properties": {
|
"properties": {
|
||||||
"mode": {"type": "string"},
|
"mode": {"type": "string"},
|
||||||
"endpoint_mode": {"type": "string"},
|
"endpoint_mode": {"type": "string"},
|
||||||
"replicas": {"type": "integer"},
|
"replicas": {"type": ["integer", "string"]},
|
||||||
"labels": {"$ref": "#/definitions/list_or_dict"},
|
"labels": {"$ref": "#/definitions/list_or_dict"},
|
||||||
"rollback_config": {
|
"rollback_config": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"parallelism": {"type": "integer"},
|
"parallelism": {"type": ["integer", "string"]},
|
||||||
"delay": {"type": "string", "format": "duration"},
|
"delay": {"type": "string"},
|
||||||
"failure_action": {"type": "string"},
|
"failure_action": {"type": "string"},
|
||||||
"monitor": {"type": "string", "format": "duration"},
|
"monitor": {"type": "string"},
|
||||||
"max_failure_ratio": {"type": "number"},
|
"max_failure_ratio": {"type": ["number", "string"]},
|
||||||
"order": {"type": "string", "enum": [
|
"order": {"type": "string", "enum": [
|
||||||
"start-first", "stop-first"
|
"start-first", "stop-first"
|
||||||
]}
|
]}
|
||||||
|
@ -504,11 +508,11 @@
|
||||||
"update_config": {
|
"update_config": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"parallelism": {"type": "integer"},
|
"parallelism": {"type": ["integer", "string"]},
|
||||||
"delay": {"type": "string", "format": "duration"},
|
"delay": {"type": "string"},
|
||||||
"failure_action": {"type": "string"},
|
"failure_action": {"type": "string"},
|
||||||
"monitor": {"type": "string", "format": "duration"},
|
"monitor": {"type": "string"},
|
||||||
"max_failure_ratio": {"type": "number"},
|
"max_failure_ratio": {"type": ["number", "string"]},
|
||||||
"order": {"type": "string", "enum": [
|
"order": {"type": "string", "enum": [
|
||||||
"start-first", "stop-first"
|
"start-first", "stop-first"
|
||||||
]}
|
]}
|
||||||
|
@ -524,7 +528,7 @@
|
||||||
"properties": {
|
"properties": {
|
||||||
"cpus": {"type": ["number", "string"]},
|
"cpus": {"type": ["number", "string"]},
|
||||||
"memory": {"type": "string"},
|
"memory": {"type": "string"},
|
||||||
"pids": {"type": "integer"}
|
"pids": {"type": ["integer", "string"]}
|
||||||
},
|
},
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"patternProperties": {"^x-": {}}
|
"patternProperties": {"^x-": {}}
|
||||||
|
@ -548,9 +552,9 @@
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"condition": {"type": "string"},
|
"condition": {"type": "string"},
|
||||||
"delay": {"type": "string", "format": "duration"},
|
"delay": {"type": "string"},
|
||||||
"max_attempts": {"type": "integer"},
|
"max_attempts": {"type": ["integer", "string"]},
|
||||||
"window": {"type": "string", "format": "duration"}
|
"window": {"type": "string"}
|
||||||
},
|
},
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"patternProperties": {"^x-": {}}
|
"patternProperties": {"^x-": {}}
|
||||||
|
@ -570,7 +574,7 @@
|
||||||
"patternProperties": {"^x-": {}}
|
"patternProperties": {"^x-": {}}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"max_replicas_per_node": {"type": "integer"}
|
"max_replicas_per_node": {"type": ["integer", "string"]}
|
||||||
},
|
},
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"patternProperties": {"^x-": {}}
|
"patternProperties": {"^x-": {}}
|
||||||
|
@ -590,7 +594,7 @@
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"kind": {"type": "string"},
|
"kind": {"type": "string"},
|
||||||
"value": {"type": "number"}
|
"value": {"type": ["number", "string"]}
|
||||||
},
|
},
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"patternProperties": {"^x-": {}}
|
"patternProperties": {"^x-": {}}
|
||||||
|
@ -655,7 +659,7 @@
|
||||||
"items": {
|
"items": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"subnet": {"type": "string", "format": "subnet_ip_address"},
|
"subnet": {"type": "string"},
|
||||||
"ip_range": {"type": "string"},
|
"ip_range": {"type": "string"},
|
||||||
"gateway": {"type": "string"},
|
"gateway": {"type": "string"},
|
||||||
"aux_addresses": {
|
"aux_addresses": {
|
||||||
|
@ -678,7 +682,7 @@
|
||||||
"patternProperties": {"^x-": {}}
|
"patternProperties": {"^x-": {}}
|
||||||
},
|
},
|
||||||
"external": {
|
"external": {
|
||||||
"type": ["boolean", "object"],
|
"type": ["boolean", "string", "object"],
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {
|
"name": {
|
||||||
"deprecated": true,
|
"deprecated": true,
|
||||||
|
@ -688,9 +692,9 @@
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"patternProperties": {"^x-": {}}
|
"patternProperties": {"^x-": {}}
|
||||||
},
|
},
|
||||||
"internal": {"type": "boolean"},
|
"internal": {"type": ["boolean", "string"]},
|
||||||
"enable_ipv6": {"type": "boolean"},
|
"enable_ipv6": {"type": ["boolean", "string"]},
|
||||||
"attachable": {"type": "boolean"},
|
"attachable": {"type": ["boolean", "string"]},
|
||||||
"labels": {"$ref": "#/definitions/list_or_dict"}
|
"labels": {"$ref": "#/definitions/list_or_dict"}
|
||||||
},
|
},
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
|
@ -710,7 +714,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"external": {
|
"external": {
|
||||||
"type": ["boolean", "object"],
|
"type": ["boolean", "string", "object"],
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {
|
"name": {
|
||||||
"deprecated": true,
|
"deprecated": true,
|
||||||
|
@ -734,7 +738,7 @@
|
||||||
"environment": {"type": "string"},
|
"environment": {"type": "string"},
|
||||||
"file": {"type": "string"},
|
"file": {"type": "string"},
|
||||||
"external": {
|
"external": {
|
||||||
"type": ["boolean", "object"],
|
"type": ["boolean", "string", "object"],
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {"type": "string"}
|
"name": {"type": "string"}
|
||||||
}
|
}
|
||||||
|
@ -762,7 +766,7 @@
|
||||||
"environment": {"type": "string"},
|
"environment": {"type": "string"},
|
||||||
"file": {"type": "string"},
|
"file": {"type": "string"},
|
||||||
"external": {
|
"external": {
|
||||||
"type": ["boolean", "object"],
|
"type": ["boolean", "string", "object"],
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {
|
"name": {
|
||||||
"deprecated": true,
|
"deprecated": true,
|
||||||
|
@ -801,7 +805,7 @@
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"required": {
|
"required": {
|
||||||
"type": "boolean",
|
"type": ["boolean", "string"],
|
||||||
"default": true
|
"default": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -824,8 +828,7 @@
|
||||||
|
|
||||||
"list_of_strings": {
|
"list_of_strings": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {"type": "string"},
|
"items": {"type": "string"}
|
||||||
"uniqueItems": true
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"list_or_dict": {
|
"list_or_dict": {
|
||||||
|
@ -839,7 +842,7 @@
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
{"type": "array", "items": {"type": "string"}, "uniqueItems": true}
|
{"type": "array", "items": {"type": "string"}}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -855,7 +858,7 @@
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"path": {"type": "string"},
|
"path": {"type": "string"},
|
||||||
"weight": {"type": "integer"}
|
"weight": {"type": ["integer", "string"]}
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
|
@ -871,7 +874,7 @@
|
||||||
"target": {"type": "string"},
|
"target": {"type": "string"},
|
||||||
"uid": {"type": "string"},
|
"uid": {"type": "string"},
|
||||||
"gid": {"type": "string"},
|
"gid": {"type": "string"},
|
||||||
"mode": {"type": "number"}
|
"mode": {"type": ["number", "string"]}
|
||||||
},
|
},
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"patternProperties": {"^x-": {}}
|
"patternProperties": {"^x-": {}}
|
||||||
|
@ -884,12 +887,12 @@
|
||||||
"patternProperties": {
|
"patternProperties": {
|
||||||
"^[a-z]+$": {
|
"^[a-z]+$": {
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{"type": "integer"},
|
{"type": ["integer", "string"]},
|
||||||
{
|
{
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"hard": {"type": "integer"},
|
"hard": {"type": ["integer", "string"]},
|
||||||
"soft": {"type": "integer"}
|
"soft": {"type": ["integer", "string"]}
|
||||||
},
|
},
|
||||||
"required": ["soft", "hard"],
|
"required": ["soft", "hard"],
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
|
|
123
vendor/github.com/compose-spec/compose-go/v2/schema/using-variables.yaml
generated
vendored
Normal file
123
vendor/github.com/compose-spec/compose-go/v2/schema/using-variables.yaml
generated
vendored
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
name: ${VARIABLE}
|
||||||
|
services:
|
||||||
|
foo:
|
||||||
|
deploy:
|
||||||
|
mode: ${VARIABLE}
|
||||||
|
replicas: ${VARIABLE}
|
||||||
|
rollback_config:
|
||||||
|
parallelism: ${VARIABLE}
|
||||||
|
delay: ${VARIABLE}
|
||||||
|
failure_action: ${VARIABLE}
|
||||||
|
monitor: ${VARIABLE}
|
||||||
|
max_failure_ratio: ${VARIABLE}
|
||||||
|
update_config:
|
||||||
|
parallelism: ${VARIABLE}
|
||||||
|
delay: ${VARIABLE}
|
||||||
|
failure_action: ${VARIABLE}
|
||||||
|
monitor: ${VARIABLE}
|
||||||
|
max_failure_ratio: ${VARIABLE}
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: ${VARIABLE}
|
||||||
|
reservations:
|
||||||
|
memory: ${VARIABLE}
|
||||||
|
generic_resources:
|
||||||
|
- discrete_resource_spec:
|
||||||
|
kind: ${VARIABLE}
|
||||||
|
value: ${VARIABLE}
|
||||||
|
- discrete_resource_spec:
|
||||||
|
kind: ${VARIABLE}
|
||||||
|
value: ${VARIABLE}
|
||||||
|
restart_policy:
|
||||||
|
condition: ${VARIABLE}
|
||||||
|
delay: ${VARIABLE}
|
||||||
|
max_attempts: ${VARIABLE}
|
||||||
|
window: ${VARIABLE}
|
||||||
|
placement:
|
||||||
|
max_replicas_per_node: ${VARIABLE}
|
||||||
|
preferences:
|
||||||
|
- spread: ${VARIABLE}
|
||||||
|
endpoint_mode: ${VARIABLE}
|
||||||
|
expose:
|
||||||
|
- ${VARIABLE}
|
||||||
|
external_links:
|
||||||
|
- ${VARIABLE}
|
||||||
|
extra_hosts:
|
||||||
|
- ${VARIABLE}
|
||||||
|
hostname: ${VARIABLE}
|
||||||
|
|
||||||
|
healthcheck:
|
||||||
|
test: ${VARIABLE}
|
||||||
|
interval: ${VARIABLE}
|
||||||
|
timeout: ${VARIABLE}
|
||||||
|
retries: ${VARIABLE}
|
||||||
|
start_period: ${VARIABLE}
|
||||||
|
start_interval: ${VARIABLE}
|
||||||
|
image: ${VARIABLE}
|
||||||
|
mac_address: ${VARIABLE}
|
||||||
|
networks:
|
||||||
|
some-network:
|
||||||
|
aliases:
|
||||||
|
- ${VARIABLE}
|
||||||
|
other-network:
|
||||||
|
ipv4_address: ${VARIABLE}
|
||||||
|
ipv6_address: ${VARIABLE}
|
||||||
|
mac_address: ${VARIABLE}
|
||||||
|
ports:
|
||||||
|
- ${VARIABLE}
|
||||||
|
privileged: ${VARIABLE}
|
||||||
|
read_only: ${VARIABLE}
|
||||||
|
restart: ${VARIABLE}
|
||||||
|
secrets:
|
||||||
|
- source: ${VARIABLE}
|
||||||
|
target: ${VARIABLE}
|
||||||
|
uid: ${VARIABLE}
|
||||||
|
gid: ${VARIABLE}
|
||||||
|
mode: ${VARIABLE}
|
||||||
|
stdin_open: ${VARIABLE}
|
||||||
|
stop_grace_period: ${VARIABLE}
|
||||||
|
stop_signal: ${VARIABLE}
|
||||||
|
storage_opt:
|
||||||
|
size: ${VARIABLE}
|
||||||
|
sysctls:
|
||||||
|
net.core.somaxconn: ${VARIABLE}
|
||||||
|
tmpfs:
|
||||||
|
- ${VARIABLE}
|
||||||
|
tty: ${VARIABLE}
|
||||||
|
ulimits:
|
||||||
|
nproc: ${VARIABLE}
|
||||||
|
nofile:
|
||||||
|
soft: ${VARIABLE}
|
||||||
|
hard: ${VARIABLE}
|
||||||
|
user: ${VARIABLE}
|
||||||
|
volumes:
|
||||||
|
- ${VARIABLE}:${VARIABLE}
|
||||||
|
- type: tmpfs
|
||||||
|
target: ${VARIABLE}
|
||||||
|
tmpfs:
|
||||||
|
size: ${VARIABLE}
|
||||||
|
|
||||||
|
networks:
|
||||||
|
network:
|
||||||
|
ipam:
|
||||||
|
driver: ${VARIABLE}
|
||||||
|
config:
|
||||||
|
- subnet: ${VARIABLE}
|
||||||
|
ip_range: ${VARIABLE}
|
||||||
|
gateway: ${VARIABLE}
|
||||||
|
aux_addresses:
|
||||||
|
host1: ${VARIABLE}
|
||||||
|
external-network:
|
||||||
|
external: ${VARIABLE}
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
external-volume:
|
||||||
|
external: ${VARIABLE}
|
||||||
|
|
||||||
|
configs:
|
||||||
|
config1:
|
||||||
|
external: ${VARIABLE}
|
||||||
|
|
||||||
|
secrets:
|
||||||
|
secret1:
|
||||||
|
external: ${VARIABLE}
|
|
@ -25,6 +25,7 @@ var defaultValues = map[tree.Path]transformFunc{}
|
||||||
func init() {
|
func init() {
|
||||||
defaultValues["services.*.build"] = defaultBuildContext
|
defaultValues["services.*.build"] = defaultBuildContext
|
||||||
defaultValues["services.*.secrets.*"] = defaultSecretMount
|
defaultValues["services.*.secrets.*"] = defaultSecretMount
|
||||||
|
defaultValues["services.*.ports.*"] = portDefaults
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetDefaultValues transforms a compose model to set default values to missing attributes
|
// SetDefaultValues transforms a compose model to set default values to missing attributes
|
||||||
|
|
|
@ -87,3 +87,18 @@ func encode(v any) (map[string]any, error) {
|
||||||
err = decoder.Decode(v)
|
err = decoder.Decode(v)
|
||||||
return m, err
|
return m, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func portDefaults(data any, _ tree.Path, _ bool) (any, error) {
|
||||||
|
switch v := data.(type) {
|
||||||
|
case map[string]any:
|
||||||
|
if _, ok := v["protocol"]; !ok {
|
||||||
|
v["protocol"] = "tcp"
|
||||||
|
}
|
||||||
|
if _, ok := v["mode"]; !ok {
|
||||||
|
v["mode"] = "ingress"
|
||||||
|
}
|
||||||
|
return v, nil
|
||||||
|
default:
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1341,7 +1341,12 @@ func deriveDeepCopy_21(dst, src *NetworkConfig) {
|
||||||
} else {
|
} else {
|
||||||
dst.Labels = nil
|
dst.Labels = nil
|
||||||
}
|
}
|
||||||
dst.EnableIPv6 = src.EnableIPv6
|
if src.EnableIPv6 == nil {
|
||||||
|
dst.EnableIPv6 = nil
|
||||||
|
} else {
|
||||||
|
dst.EnableIPv6 = new(bool)
|
||||||
|
*dst.EnableIPv6 = *src.EnableIPv6
|
||||||
|
}
|
||||||
if src.Extensions != nil {
|
if src.Extensions != nil {
|
||||||
dst.Extensions = make(map[string]any, len(src.Extensions))
|
dst.Extensions = make(map[string]any, len(src.Extensions))
|
||||||
src.Extensions.DeepCopy(dst.Extensions)
|
src.Extensions.DeepCopy(dst.Extensions)
|
||||||
|
|
|
@ -782,9 +782,41 @@ type ExtendsConfig struct {
|
||||||
// SecretConfig for a secret
|
// SecretConfig for a secret
|
||||||
type SecretConfig FileObjectConfig
|
type SecretConfig FileObjectConfig
|
||||||
|
|
||||||
|
// MarshalYAML makes SecretConfig implement yaml.Marshaller
|
||||||
|
func (s SecretConfig) MarshalYAML() (interface{}, error) {
|
||||||
|
// secret content is set while loading model. Never marshall it
|
||||||
|
s.Content = ""
|
||||||
|
return FileObjectConfig(s), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON makes SecretConfig implement json.Marshaller
|
||||||
|
func (s SecretConfig) MarshalJSON() ([]byte, error) {
|
||||||
|
// secret content is set while loading model. Never marshall it
|
||||||
|
s.Content = ""
|
||||||
|
return json.Marshal(FileObjectConfig(s))
|
||||||
|
}
|
||||||
|
|
||||||
// ConfigObjConfig is the config for the swarm "Config" object
|
// ConfigObjConfig is the config for the swarm "Config" object
|
||||||
type ConfigObjConfig FileObjectConfig
|
type ConfigObjConfig FileObjectConfig
|
||||||
|
|
||||||
|
// MarshalYAML makes ConfigObjConfig implement yaml.Marshaller
|
||||||
|
func (s ConfigObjConfig) MarshalYAML() (interface{}, error) {
|
||||||
|
// config content may have been set from environment while loading model. Marshall actual source
|
||||||
|
if s.Environment != "" {
|
||||||
|
s.Content = ""
|
||||||
|
}
|
||||||
|
return FileObjectConfig(s), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON makes ConfigObjConfig implement json.Marshaller
|
||||||
|
func (s ConfigObjConfig) MarshalJSON() ([]byte, error) {
|
||||||
|
// config content may have been set from environment while loading model. Marshall actual source
|
||||||
|
if s.Environment != "" {
|
||||||
|
s.Content = ""
|
||||||
|
}
|
||||||
|
return json.Marshal(FileObjectConfig(s))
|
||||||
|
}
|
||||||
|
|
||||||
type IncludeConfig struct {
|
type IncludeConfig struct {
|
||||||
Path StringList `yaml:"path,omitempty" json:"path,omitempty"`
|
Path StringList `yaml:"path,omitempty" json:"path,omitempty"`
|
||||||
ProjectDirectory string `yaml:"project_directory,omitempty" json:"project_directory,omitempty"`
|
ProjectDirectory string `yaml:"project_directory,omitempty" json:"project_directory,omitempty"`
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
/*
|
||||||
|
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 utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ResolveSymbolicLink converts the section of an absolute path if it is a
|
||||||
|
// symbolic link
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - path: an absolute path
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - converted path if it has a symbolic link or the same path if there is
|
||||||
|
// no symbolic link
|
||||||
|
func ResolveSymbolicLink(path string) (string, error) {
|
||||||
|
sym, part, err := getSymbolinkLink(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if sym == "" && part == "" {
|
||||||
|
// no symbolic link detected
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
return strings.Replace(path, part, sym, 1), nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// getSymbolinkLink parses all parts of the path and returns the
|
||||||
|
// the symbolic link part as well as the correspondent original part
|
||||||
|
// Parameters:
|
||||||
|
// - path: an absolute path
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - string section of the path that is a symbolic link
|
||||||
|
// - string correspondent path section of the symbolic link
|
||||||
|
// - An error
|
||||||
|
func getSymbolinkLink(path string) (string, string, error) {
|
||||||
|
parts := strings.Split(path, string(os.PathSeparator))
|
||||||
|
|
||||||
|
// Reconstruct the path step by step, checking each component
|
||||||
|
var currentPath string
|
||||||
|
if filepath.IsAbs(path) {
|
||||||
|
currentPath = string(os.PathSeparator)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, part := range parts {
|
||||||
|
if part == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
currentPath = filepath.Join(currentPath, part)
|
||||||
|
|
||||||
|
if isSymLink := isSymbolicLink(currentPath); isSymLink {
|
||||||
|
// return symbolic link, and correspondent part
|
||||||
|
target, err := filepath.EvalSymlinks(currentPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
return target, currentPath, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", "", nil // no symbolic link
|
||||||
|
}
|
||||||
|
|
||||||
|
// isSymbolicLink validates if the path is a symbolic link
|
||||||
|
func isSymbolicLink(path string) bool {
|
||||||
|
info, err := os.Lstat(path)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the file mode indicates a symbolic link
|
||||||
|
return info.Mode()&os.ModeSymlink != 0
|
||||||
|
}
|
|
@ -32,8 +32,10 @@ func StringToBool(s string) bool {
|
||||||
func GetAsEqualsMap(em []string) map[string]string {
|
func GetAsEqualsMap(em []string) map[string]string {
|
||||||
m := make(map[string]string)
|
m := make(map[string]string)
|
||||||
for _, v := range em {
|
for _, v := range em {
|
||||||
kv := strings.SplitN(v, "=", 2)
|
key, val, found := strings.Cut(v, "=")
|
||||||
m[kv[0]] = kv[1]
|
if found {
|
||||||
|
m[key] = val
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,7 +128,7 @@ github.com/cenkalti/backoff/v4
|
||||||
# github.com/cespare/xxhash/v2 v2.2.0
|
# github.com/cespare/xxhash/v2 v2.2.0
|
||||||
## explicit; go 1.11
|
## explicit; go 1.11
|
||||||
github.com/cespare/xxhash/v2
|
github.com/cespare/xxhash/v2
|
||||||
# github.com/compose-spec/compose-go/v2 v2.1.3
|
# github.com/compose-spec/compose-go/v2 v2.1.4
|
||||||
## explicit; go 1.21
|
## explicit; go 1.21
|
||||||
github.com/compose-spec/compose-go/v2/cli
|
github.com/compose-spec/compose-go/v2/cli
|
||||||
github.com/compose-spec/compose-go/v2/consts
|
github.com/compose-spec/compose-go/v2/consts
|
||||||
|
|
Loading…
Reference in New Issue