mirror of https://github.com/docker/buildx.git
bump compose-go to v2.1.4
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
This commit is contained in:
parent
3005743f7c
commit
ca452c47d8
2
go.mod
2
go.mod
|
@ -6,7 +6,7 @@ require (
|
|||
github.com/Masterminds/semver/v3 v3.2.1
|
||||
github.com/Microsoft/go-winio v0.6.2
|
||||
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/containerd v1.7.19
|
||||
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/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/compose-spec/compose-go/v2 v2.1.3 h1:bD67uqLuL/XgkAK6ir3xZvNLFPxPScEi1KW7R5esrLE=
|
||||
github.com/compose-spec/compose-go/v2 v2.1.3/go.mod h1:lFN0DrMxIncJGYAXTfWuajfwj5haBJqrBkarHcnjJKc=
|
||||
github.com/compose-spec/compose-go/v2 v2.1.4 h1:+1UKMvbBJo22Bpulgb9KAeZwRT99hANf3tDQVeG6ZJo=
|
||||
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/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
|
||||
github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro=
|
||||
|
|
|
@ -22,9 +22,14 @@ import (
|
|||
"github.com/compose-spec/compose-go/v2/types"
|
||||
)
|
||||
|
||||
// Will update the environment variables for the format {- VAR} (without interpolation)
|
||||
// This function should resolve context environment vars for include (passed in env_file)
|
||||
func resolveServicesEnvironment(dict map[string]any, config types.ConfigDetails) {
|
||||
// ResolveEnvironment update the environment variables for the format {- VAR} (without interpolation)
|
||||
func ResolveEnvironment(dict map[string]any, environment types.Mapping) {
|
||||
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)
|
||||
if !ok {
|
||||
return
|
||||
|
@ -45,7 +50,7 @@ func resolveServicesEnvironment(dict map[string]any, config types.ConfigDetails)
|
|||
if !ok {
|
||||
continue
|
||||
}
|
||||
if found, ok := config.Environment[varEnv]; ok {
|
||||
if found, ok := environment[varEnv]; ok {
|
||||
envs = append(envs, fmt.Sprintf("%s=%s", varEnv, found))
|
||||
} else {
|
||||
// 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
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
"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/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"
|
||||
)
|
||||
|
||||
|
@ -67,25 +71,43 @@ func applyServiceExtends(ctx context.Context, name string, services map[string]a
|
|||
)
|
||||
switch v := extends.(type) {
|
||||
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)
|
||||
file = v["file"]
|
||||
opts.ProcessEvent("extends", v)
|
||||
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
|
||||
opts.ProcessEvent("extends", map[string]any{"service": ref})
|
||||
}
|
||||
|
||||
var base any
|
||||
var (
|
||||
base any
|
||||
processor PostProcessor
|
||||
)
|
||||
|
||||
if file != nil {
|
||||
filename = file.(string)
|
||||
services, err = getExtendsBaseFromFile(ctx, ref, filename, opts, tracker)
|
||||
refFilename := file.(string)
|
||||
services, processor, err = getExtendsBaseFromFile(ctx, name, ref, filename, refFilename, opts, tracker)
|
||||
post = append(post, processor)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
filename = refFilename
|
||||
} else {
|
||||
_, ok := services[ref]
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
if !loader.Accept(path) {
|
||||
if !loader.Accept(refPath) {
|
||||
continue
|
||||
}
|
||||
local, err := loader.Load(ctx, path)
|
||||
local, err := loader.Load(ctx, refPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
localdir := filepath.Dir(local)
|
||||
relworkingdir := loader.Dir(path)
|
||||
relworkingdir := loader.Dir(refPath)
|
||||
|
||||
extendsOpts := opts.clone()
|
||||
// replace localResourceLoader with a new flavour, using extended file base path
|
||||
extendsOpts.ResourceLoaders = append(opts.RemoteResourceLoaders(), localResourceLoader{
|
||||
WorkingDir: localdir,
|
||||
})
|
||||
extendsOpts.ResolvePaths = true
|
||||
extendsOpts.ResolvePaths = false // we do relative path resolution after file has been loaded
|
||||
extendsOpts.SkipNormalization = true
|
||||
extendsOpts.SkipConsistencyCheck = true
|
||||
extendsOpts.SkipInclude = true
|
||||
extendsOpts.SkipExtends = true // we manage extends recursively based on raw service definition
|
||||
extendsOpts.SkipValidation = true // we validate the merge result
|
||||
extendsOpts.SkipDefaultValues = true
|
||||
source, err := loadYamlModel(ctx, types.ConfigDetails{
|
||||
WorkingDir: relworkingdir,
|
||||
ConfigFiles: []types.ConfigFile{
|
||||
{Filename: local},
|
||||
},
|
||||
}, extendsOpts, ct, nil)
|
||||
source, processor, err := loadYamlFile(ctx, types.ConfigFile{Filename: local},
|
||||
extendsOpts, relworkingdir, nil, ct, map[string]any{}, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
services := source["services"].(map[string]any)
|
||||
_, ok := services[name]
|
||||
_, ok := services[ref]
|
||||
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 {
|
||||
|
|
|
@ -30,7 +30,7 @@ import (
|
|||
)
|
||||
|
||||
// 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 {
|
||||
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
|
||||
err := Transform(source, &requires)
|
||||
return requires, err
|
||||
}
|
||||
|
||||
func ApplyInclude(ctx context.Context, configDetails types.ConfigDetails, model map[string]any, options *Options, included []string) error {
|
||||
includeConfig, err := loadIncludeConfig(model["include"])
|
||||
func ApplyInclude(ctx context.Context, workingDir string, environment types.Mapping, model map[string]any, options *Options, included []string) error {
|
||||
includeConfig, err := loadIncludeConfig(model["include"], options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, r := range includeConfig {
|
||||
for _, listener := range options.Listeners {
|
||||
listener("include", map[string]any{
|
||||
"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)
|
||||
case !filepath.IsAbs(r.ProjectDirectory):
|
||||
relworkingdir = loader.Dir(r.ProjectDirectory)
|
||||
r.ProjectDirectory = filepath.Join(configDetails.WorkingDir, r.ProjectDirectory)
|
||||
r.ProjectDirectory = filepath.Join(workingDir, r.ProjectDirectory)
|
||||
|
||||
default:
|
||||
relworkingdir = r.ProjectDirectory
|
||||
|
@ -117,7 +128,7 @@ func ApplyInclude(ctx context.Context, configDetails types.ConfigDetails, model
|
|||
envFile := []string{}
|
||||
for _, f := range r.EnvFile {
|
||||
if !filepath.IsAbs(f) {
|
||||
f = filepath.Join(configDetails.WorkingDir, f)
|
||||
f = filepath.Join(workingDir, f)
|
||||
s, err := os.Stat(f)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -131,7 +142,7 @@ func ApplyInclude(ctx context.Context, configDetails types.ConfigDetails, model
|
|||
r.EnvFile = envFile
|
||||
}
|
||||
|
||||
envFromFile, err := dotenv.GetEnvFromFile(configDetails.Environment, r.EnvFile)
|
||||
envFromFile, err := dotenv.GetEnvFromFile(environment, r.EnvFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -139,7 +150,7 @@ func ApplyInclude(ctx context.Context, configDetails types.ConfigDetails, model
|
|||
config := types.ConfigDetails{
|
||||
WorkingDir: relworkingdir,
|
||||
ConfigFiles: types.ToConfigFiles(r.Path),
|
||||
Environment: configDetails.Environment.Clone().Merge(envFromFile),
|
||||
Environment: environment.Clone().Merge(envFromFile),
|
||||
}
|
||||
loadOptions.Interpolate = &interp.Options{
|
||||
Substitute: options.Interpolate.Substitute,
|
||||
|
|
|
@ -89,7 +89,7 @@ var versionWarning []string
|
|||
|
||||
func (o *Options) warnObsoleteVersion(file string) {
|
||||
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)
|
||||
}
|
||||
|
@ -358,100 +358,19 @@ func loadYamlModel(ctx context.Context, config types.ConfigDetails, opts *Option
|
|||
dict = map[string]interface{}{}
|
||||
err error
|
||||
)
|
||||
workingDir, environment := config.WorkingDir, config.Environment
|
||||
|
||||
for _, file := range config.ConfigFiles {
|
||||
fctx := 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, err
|
||||
}
|
||||
file.Content = content
|
||||
dict, _, err = loadYamlFile(ctx, file, opts, workingDir, environment, ct, dict, included)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
if opts.Interpolate != nil && !opts.SkipInterpolation {
|
||||
dict, err = interp.Interpolate(dict, *opts.Interpolate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -460,7 +379,6 @@ func loadYamlModel(ctx context.Context, config types.ConfigDetails, opts *Option
|
|||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -489,11 +407,98 @@ func loadYamlModel(ctx context.Context, config types.ConfigDetails, opts *Option
|
|||
return nil, err
|
||||
}
|
||||
}
|
||||
resolveServicesEnvironment(dict, config)
|
||||
ResolveEnvironment(dict, config.Environment)
|
||||
|
||||
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) {
|
||||
mainFile := configDetails.ConfigFiles[0].Filename
|
||||
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 {
|
||||
build["args"], _ = resolve(a, fn)
|
||||
build["args"], _ = resolve(a, fn, false)
|
||||
}
|
||||
|
||||
service["build"] = build
|
||||
}
|
||||
|
||||
if e, ok := service["environment"]; ok {
|
||||
service["environment"], _ = resolve(e, fn)
|
||||
service["environment"], _ = resolve(e, fn, true)
|
||||
}
|
||||
|
||||
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) {
|
||||
case []any:
|
||||
var resolved []any
|
||||
for _, val := range v {
|
||||
if r, ok := resolve(val, fn); ok {
|
||||
if r, ok := resolve(val, fn, keepEmpty); ok {
|
||||
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 {
|
||||
resolved[key] = s
|
||||
} else if keepEmpty {
|
||||
resolved[key] = nil
|
||||
}
|
||||
}
|
||||
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 {
|
||||
return fmt.Sprintf("%s=%s", v, val), true
|
||||
}
|
||||
if keepEmpty {
|
||||
return v, true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
return v, true
|
||||
|
|
|
@ -38,7 +38,7 @@ func ResolveRelativePaths(project map[string]any, base string, remotes []RemoteR
|
|||
"services.*.build.additional_contexts.*": r.absContextPath,
|
||||
"services.*.env_file.*.path": r.absPath,
|
||||
"services.*.extends.file": r.absExtendsPath,
|
||||
"services.*.develop.watch.*.path": r.absPath,
|
||||
"services.*.develop.watch.*.path": r.absSymbolicLink,
|
||||
"services.*.volumes.*": r.absVolumeMount,
|
||||
"configs.*.file": r.maybeUnixPath,
|
||||
"secrets.*.file": r.maybeUnixPath,
|
||||
|
|
|
@ -19,6 +19,8 @@ package paths
|
|||
import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/utils"
|
||||
)
|
||||
|
||||
func (r *relativePathsResolver) maybeUnixPath(a any) (any, error) {
|
||||
|
@ -38,3 +40,15 @@ func (r *relativePathsResolver) maybeUnixPath(a any) (any, error) {
|
|||
}
|
||||
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": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z0-9][a-z0-9_-]*$",
|
||||
"description": "define the Compose project name, until user defines one explicitly."
|
||||
},
|
||||
|
||||
|
@ -94,7 +93,7 @@
|
|||
"develop": {"$ref": "#/definitions/development"},
|
||||
"deploy": {"$ref": "#/definitions/deployment"},
|
||||
"annotations": {"$ref": "#/definitions/list_or_dict"},
|
||||
"attach": {"type": "boolean"},
|
||||
"attach": {"type": ["boolean", "string"]},
|
||||
"build": {
|
||||
"oneOf": [
|
||||
{"type": "string"},
|
||||
|
@ -110,15 +109,15 @@
|
|||
"labels": {"$ref": "#/definitions/list_or_dict"},
|
||||
"cache_from": {"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"},
|
||||
"network": {"type": "string"},
|
||||
"pull": {"type": "boolean"},
|
||||
"pull": {"type": ["boolean", "string"]},
|
||||
"target": {"type": "string"},
|
||||
"shm_size": {"type": ["integer", "string"]},
|
||||
"extra_hosts": {"$ref": "#/definitions/list_or_dict"},
|
||||
"isolation": {"type": "string"},
|
||||
"privileged": {"type": "boolean"},
|
||||
"privileged": {"type": ["boolean", "string"]},
|
||||
"secrets": {"$ref": "#/definitions/service_config_or_secret"},
|
||||
"tags": {"type": "array", "items": {"type": "string"}},
|
||||
"ulimits": {"$ref": "#/definitions/ulimits"},
|
||||
|
@ -148,7 +147,7 @@
|
|||
"type": "array",
|
||||
"items": {"$ref": "#/definitions/blkio_limit"}
|
||||
},
|
||||
"weight": {"type": "integer"},
|
||||
"weight": {"type": ["integer", "string"]},
|
||||
"weight_device": {
|
||||
"type": "array",
|
||||
"items": {"$ref": "#/definitions/blkio_weight"}
|
||||
|
@ -156,15 +155,21 @@
|
|||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"cap_add": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
|
||||
"cap_drop": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
|
||||
"cap_add": {"type": "array", "items": {"type": "string"}},
|
||||
"cap_drop": {"type": "array", "items": {"type": "string"}},
|
||||
"cgroup": {"type": "string", "enum": ["host", "private"]},
|
||||
"cgroup_parent": {"type": "string"},
|
||||
"command": {"$ref": "#/definitions/command"},
|
||||
"configs": {"$ref": "#/definitions/service_config_or_secret"},
|
||||
"container_name": {"type": "string"},
|
||||
"cpu_count": {"type": "integer", "minimum": 0},
|
||||
"cpu_percent": {"type": "integer", "minimum": 0, "maximum": 100},
|
||||
"cpu_count": {"oneOf": [
|
||||
{"type": "string"},
|
||||
{"type": "integer", "minimum": 0}
|
||||
]},
|
||||
"cpu_percent": {"oneOf": [
|
||||
{"type": "string"},
|
||||
{"type": "integer", "minimum": 0, "maximum": 100}
|
||||
]},
|
||||
"cpu_shares": {"type": ["number", "string"]},
|
||||
"cpu_quota": {"type": ["number", "string"]},
|
||||
"cpu_period": {"type": ["number", "string"]},
|
||||
|
@ -192,8 +197,9 @@
|
|||
"^[a-zA-Z0-9._-]+$": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}},
|
||||
"properties": {
|
||||
"restart": {"type": "boolean"},
|
||||
"restart": {"type": ["boolean", "string"]},
|
||||
"required": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
|
@ -210,9 +216,9 @@
|
|||
]
|
||||
},
|
||||
"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_opt": {"type": "array","items": {"type": "string"}, "uniqueItems": true},
|
||||
"dns_opt": {"type": "array","items": {"type": "string"}},
|
||||
"dns_search": {"$ref": "#/definitions/string_or_list"},
|
||||
"domainname": {"type": "string"},
|
||||
"entrypoint": {"$ref": "#/definitions/command"},
|
||||
|
@ -224,8 +230,7 @@
|
|||
"items": {
|
||||
"type": ["string", "number"],
|
||||
"format": "expose"
|
||||
},
|
||||
"uniqueItems": true
|
||||
}
|
||||
},
|
||||
"extends": {
|
||||
"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"},
|
||||
"group_add": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": ["string", "number"]
|
||||
},
|
||||
"uniqueItems": true
|
||||
}
|
||||
},
|
||||
"healthcheck": {"$ref": "#/definitions/healthcheck"},
|
||||
"hostname": {"type": "string"},
|
||||
"image": {"type": "string"},
|
||||
"init": {"type": "boolean"},
|
||||
"init": {"type": ["boolean", "string"]},
|
||||
"ipc": {"type": "string"},
|
||||
"isolation": {"type": "string"},
|
||||
"labels": {"$ref": "#/definitions/list_or_dict"},
|
||||
"links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
|
||||
"links": {"type": "array", "items": {"type": "string"}},
|
||||
"logging": {
|
||||
"type": "object",
|
||||
|
||||
|
@ -277,7 +281,7 @@
|
|||
"mac_address": {"type": "string"},
|
||||
"mem_limit": {"type": ["number", "string"]},
|
||||
"mem_reservation": {"type": ["string", "integer"]},
|
||||
"mem_swappiness": {"type": "integer"},
|
||||
"mem_swappiness": {"type": ["integer", "string"]},
|
||||
"memswap_limit": {"type": ["number", "string"]},
|
||||
"network_mode": {"type": "string"},
|
||||
"networks": {
|
||||
|
@ -315,8 +319,11 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"oom_kill_disable": {"type": "boolean"},
|
||||
"oom_score_adj": {"type": "integer", "minimum": -1000, "maximum": 1000},
|
||||
"oom_kill_disable": {"type": ["boolean", "string"]},
|
||||
"oom_score_adj": {"oneOf": [
|
||||
{"type": "string"},
|
||||
{"type": "integer", "minimum": -1000, "maximum": 1000}
|
||||
]},
|
||||
"pid": {"type": ["string", "null"]},
|
||||
"pids_limit": {"type": ["number", "string"]},
|
||||
"platform": {"type": "string"},
|
||||
|
@ -324,15 +331,15 @@
|
|||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{"type": "number", "format": "ports"},
|
||||
{"type": "string", "format": "ports"},
|
||||
{"type": "number"},
|
||||
{"type": "string"},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "string"},
|
||||
"mode": {"type": "string"},
|
||||
"host_ip": {"type": "string"},
|
||||
"target": {"type": "integer"},
|
||||
"target": {"type": ["integer", "string"]},
|
||||
"published": {"type": ["string", "integer"]},
|
||||
"protocol": {"type": "string"},
|
||||
"app_protocol": {"type": "string"}
|
||||
|
@ -341,32 +348,31 @@
|
|||
"patternProperties": {"^x-": {}}
|
||||
}
|
||||
]
|
||||
},
|
||||
"uniqueItems": true
|
||||
}
|
||||
},
|
||||
"privileged": {"type": "boolean"},
|
||||
"privileged": {"type": ["boolean", "string"]},
|
||||
"profiles": {"$ref": "#/definitions/list_of_strings"},
|
||||
"pull_policy": {"type": "string", "enum": [
|
||||
"always", "never", "if_not_present", "build", "missing"
|
||||
]},
|
||||
"read_only": {"type": "boolean"},
|
||||
"read_only": {"type": ["boolean", "string"]},
|
||||
"restart": {"type": "string"},
|
||||
"runtime": {
|
||||
"type": "string"
|
||||
},
|
||||
"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"]},
|
||||
"secrets": {"$ref": "#/definitions/service_config_or_secret"},
|
||||
"sysctls": {"$ref": "#/definitions/list_or_dict"},
|
||||
"stdin_open": {"type": "boolean"},
|
||||
"stop_grace_period": {"type": "string", "format": "duration"},
|
||||
"stdin_open": {"type": ["boolean", "string"]},
|
||||
"stop_grace_period": {"type": "string"},
|
||||
"stop_signal": {"type": "string"},
|
||||
"storage_opt": {"type": "object"},
|
||||
"tmpfs": {"$ref": "#/definitions/string_or_list"},
|
||||
"tty": {"type": "boolean"},
|
||||
"tty": {"type": ["boolean", "string"]},
|
||||
"ulimits": {"$ref": "#/definitions/ulimits"},
|
||||
"user": {"type": "string"},
|
||||
"uts": {"type": "string"},
|
||||
|
@ -383,13 +389,13 @@
|
|||
"type": {"type": "string"},
|
||||
"source": {"type": "string"},
|
||||
"target": {"type": "string"},
|
||||
"read_only": {"type": "boolean"},
|
||||
"read_only": {"type": ["boolean", "string"]},
|
||||
"consistency": {"type": "string"},
|
||||
"bind": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"propagation": {"type": "string"},
|
||||
"create_host_path": {"type": "boolean"},
|
||||
"create_host_path": {"type": ["boolean", "string"]},
|
||||
"selinux": {"type": "string", "enum": ["z", "Z"]}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
|
@ -398,7 +404,7 @@
|
|||
"volume": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"nocopy": {"type": "boolean"},
|
||||
"nocopy": {"type": ["boolean", "string"]},
|
||||
"subpath": {"type": "string"}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
|
@ -413,7 +419,7 @@
|
|||
{"type": "string"}
|
||||
]
|
||||
},
|
||||
"mode": {"type": "number"}
|
||||
"mode": {"type": ["number", "string"]}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
|
@ -423,13 +429,11 @@
|
|||
"patternProperties": {"^x-": {}}
|
||||
}
|
||||
]
|
||||
},
|
||||
"uniqueItems": true
|
||||
}
|
||||
},
|
||||
"volumes_from": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"},
|
||||
"uniqueItems": true
|
||||
"items": {"type": "string"}
|
||||
},
|
||||
"working_dir": {"type": "string"}
|
||||
},
|
||||
|
@ -441,18 +445,18 @@
|
|||
"id": "#/definitions/healthcheck",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"disable": {"type": "boolean"},
|
||||
"interval": {"type": "string", "format": "duration"},
|
||||
"retries": {"type": "number"},
|
||||
"disable": {"type": ["boolean", "string"]},
|
||||
"interval": {"type": "string"},
|
||||
"retries": {"type": ["number", "string"]},
|
||||
"test": {
|
||||
"oneOf": [
|
||||
{"type": "string"},
|
||||
{"type": "array", "items": {"type": "string"}}
|
||||
]
|
||||
},
|
||||
"timeout": {"type": "string", "format": "duration"},
|
||||
"start_period": {"type": "string", "format": "duration"},
|
||||
"start_interval": {"type": "string", "format": "duration"}
|
||||
"timeout": {"type": "string"},
|
||||
"start_period": {"type": "string"},
|
||||
"start_interval": {"type": "string"}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
|
@ -484,16 +488,16 @@
|
|||
"properties": {
|
||||
"mode": {"type": "string"},
|
||||
"endpoint_mode": {"type": "string"},
|
||||
"replicas": {"type": "integer"},
|
||||
"replicas": {"type": ["integer", "string"]},
|
||||
"labels": {"$ref": "#/definitions/list_or_dict"},
|
||||
"rollback_config": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"parallelism": {"type": "integer"},
|
||||
"delay": {"type": "string", "format": "duration"},
|
||||
"parallelism": {"type": ["integer", "string"]},
|
||||
"delay": {"type": "string"},
|
||||
"failure_action": {"type": "string"},
|
||||
"monitor": {"type": "string", "format": "duration"},
|
||||
"max_failure_ratio": {"type": "number"},
|
||||
"monitor": {"type": "string"},
|
||||
"max_failure_ratio": {"type": ["number", "string"]},
|
||||
"order": {"type": "string", "enum": [
|
||||
"start-first", "stop-first"
|
||||
]}
|
||||
|
@ -504,11 +508,11 @@
|
|||
"update_config": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"parallelism": {"type": "integer"},
|
||||
"delay": {"type": "string", "format": "duration"},
|
||||
"parallelism": {"type": ["integer", "string"]},
|
||||
"delay": {"type": "string"},
|
||||
"failure_action": {"type": "string"},
|
||||
"monitor": {"type": "string", "format": "duration"},
|
||||
"max_failure_ratio": {"type": "number"},
|
||||
"monitor": {"type": "string"},
|
||||
"max_failure_ratio": {"type": ["number", "string"]},
|
||||
"order": {"type": "string", "enum": [
|
||||
"start-first", "stop-first"
|
||||
]}
|
||||
|
@ -524,7 +528,7 @@
|
|||
"properties": {
|
||||
"cpus": {"type": ["number", "string"]},
|
||||
"memory": {"type": "string"},
|
||||
"pids": {"type": "integer"}
|
||||
"pids": {"type": ["integer", "string"]}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
|
@ -548,9 +552,9 @@
|
|||
"type": "object",
|
||||
"properties": {
|
||||
"condition": {"type": "string"},
|
||||
"delay": {"type": "string", "format": "duration"},
|
||||
"max_attempts": {"type": "integer"},
|
||||
"window": {"type": "string", "format": "duration"}
|
||||
"delay": {"type": "string"},
|
||||
"max_attempts": {"type": ["integer", "string"]},
|
||||
"window": {"type": "string"}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
|
@ -570,7 +574,7 @@
|
|||
"patternProperties": {"^x-": {}}
|
||||
}
|
||||
},
|
||||
"max_replicas_per_node": {"type": "integer"}
|
||||
"max_replicas_per_node": {"type": ["integer", "string"]}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
|
@ -590,7 +594,7 @@
|
|||
"type": "object",
|
||||
"properties": {
|
||||
"kind": {"type": "string"},
|
||||
"value": {"type": "number"}
|
||||
"value": {"type": ["number", "string"]}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
|
@ -655,7 +659,7 @@
|
|||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"subnet": {"type": "string", "format": "subnet_ip_address"},
|
||||
"subnet": {"type": "string"},
|
||||
"ip_range": {"type": "string"},
|
||||
"gateway": {"type": "string"},
|
||||
"aux_addresses": {
|
||||
|
@ -678,7 +682,7 @@
|
|||
"patternProperties": {"^x-": {}}
|
||||
},
|
||||
"external": {
|
||||
"type": ["boolean", "object"],
|
||||
"type": ["boolean", "string", "object"],
|
||||
"properties": {
|
||||
"name": {
|
||||
"deprecated": true,
|
||||
|
@ -688,9 +692,9 @@
|
|||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
},
|
||||
"internal": {"type": "boolean"},
|
||||
"enable_ipv6": {"type": "boolean"},
|
||||
"attachable": {"type": "boolean"},
|
||||
"internal": {"type": ["boolean", "string"]},
|
||||
"enable_ipv6": {"type": ["boolean", "string"]},
|
||||
"attachable": {"type": ["boolean", "string"]},
|
||||
"labels": {"$ref": "#/definitions/list_or_dict"}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
|
@ -710,7 +714,7 @@
|
|||
}
|
||||
},
|
||||
"external": {
|
||||
"type": ["boolean", "object"],
|
||||
"type": ["boolean", "string", "object"],
|
||||
"properties": {
|
||||
"name": {
|
||||
"deprecated": true,
|
||||
|
@ -734,7 +738,7 @@
|
|||
"environment": {"type": "string"},
|
||||
"file": {"type": "string"},
|
||||
"external": {
|
||||
"type": ["boolean", "object"],
|
||||
"type": ["boolean", "string", "object"],
|
||||
"properties": {
|
||||
"name": {"type": "string"}
|
||||
}
|
||||
|
@ -762,7 +766,7 @@
|
|||
"environment": {"type": "string"},
|
||||
"file": {"type": "string"},
|
||||
"external": {
|
||||
"type": ["boolean", "object"],
|
||||
"type": ["boolean", "string", "object"],
|
||||
"properties": {
|
||||
"name": {
|
||||
"deprecated": true,
|
||||
|
@ -801,7 +805,7 @@
|
|||
"type": "string"
|
||||
},
|
||||
"required": {
|
||||
"type": "boolean",
|
||||
"type": ["boolean", "string"],
|
||||
"default": true
|
||||
}
|
||||
},
|
||||
|
@ -824,8 +828,7 @@
|
|||
|
||||
"list_of_strings": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"},
|
||||
"uniqueItems": true
|
||||
"items": {"type": "string"}
|
||||
},
|
||||
|
||||
"list_or_dict": {
|
||||
|
@ -839,7 +842,7 @@
|
|||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{"type": "array", "items": {"type": "string"}, "uniqueItems": true}
|
||||
{"type": "array", "items": {"type": "string"}}
|
||||
]
|
||||
},
|
||||
|
||||
|
@ -855,7 +858,7 @@
|
|||
"type": "object",
|
||||
"properties": {
|
||||
"path": {"type": "string"},
|
||||
"weight": {"type": "integer"}
|
||||
"weight": {"type": ["integer", "string"]}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
@ -871,7 +874,7 @@
|
|||
"target": {"type": "string"},
|
||||
"uid": {"type": "string"},
|
||||
"gid": {"type": "string"},
|
||||
"mode": {"type": "number"}
|
||||
"mode": {"type": ["number", "string"]}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
|
@ -884,12 +887,12 @@
|
|||
"patternProperties": {
|
||||
"^[a-z]+$": {
|
||||
"oneOf": [
|
||||
{"type": "integer"},
|
||||
{"type": ["integer", "string"]},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"hard": {"type": "integer"},
|
||||
"soft": {"type": "integer"}
|
||||
"hard": {"type": ["integer", "string"]},
|
||||
"soft": {"type": ["integer", "string"]}
|
||||
},
|
||||
"required": ["soft", "hard"],
|
||||
"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() {
|
||||
defaultValues["services.*.build"] = defaultBuildContext
|
||||
defaultValues["services.*.secrets.*"] = defaultSecretMount
|
||||
defaultValues["services.*.ports.*"] = portDefaults
|
||||
}
|
||||
|
||||
// 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)
|
||||
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 {
|
||||
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 {
|
||||
dst.Extensions = make(map[string]any, len(src.Extensions))
|
||||
src.Extensions.DeepCopy(dst.Extensions)
|
||||
|
|
|
@ -782,9 +782,41 @@ type ExtendsConfig struct {
|
|||
// SecretConfig for a secret
|
||||
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
|
||||
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 {
|
||||
Path StringList `yaml:"path,omitempty" json:"path,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 {
|
||||
m := make(map[string]string)
|
||||
for _, v := range em {
|
||||
kv := strings.SplitN(v, "=", 2)
|
||||
m[kv[0]] = kv[1]
|
||||
key, val, found := strings.Cut(v, "=")
|
||||
if found {
|
||||
m[key] = val
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
|
|
@ -128,7 +128,7 @@ github.com/cenkalti/backoff/v4
|
|||
# github.com/cespare/xxhash/v2 v2.2.0
|
||||
## explicit; go 1.11
|
||||
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
|
||||
github.com/compose-spec/compose-go/v2/cli
|
||||
github.com/compose-spec/compose-go/v2/consts
|
||||
|
|
Loading…
Reference in New Issue