mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-11-01 00:23:56 +08:00 
			
		
		
		
	vendor: update compose-go to v2.0.0-rc.8
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
This commit is contained in:
		
							
								
								
									
										83
									
								
								vendor/github.com/compose-spec/compose-go/v2/cli/options.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										83
									
								
								vendor/github.com/compose-spec/compose-go/v2/cli/options.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -37,8 +37,6 @@ import ( | ||||
|  | ||||
| // ProjectOptions provides common configuration for loading a project. | ||||
| type ProjectOptions struct { | ||||
| 	ctx context.Context | ||||
|  | ||||
| 	// Name is a valid Compose project name to be used or empty. | ||||
| 	// | ||||
| 	// If empty, the project loader will automatically infer a reasonable | ||||
| @@ -80,6 +78,10 @@ type ProjectOptions struct { | ||||
| 	EnvFiles []string | ||||
|  | ||||
| 	loadOptions []func(*loader.Options) | ||||
|  | ||||
| 	// Callbacks to retrieve metadata information during parse defined before | ||||
| 	// creating the project | ||||
| 	Listeners []loader.Listener | ||||
| } | ||||
|  | ||||
| type ProjectOptionsFn func(*ProjectOptions) error | ||||
| @@ -89,6 +91,7 @@ func NewProjectOptions(configs []string, opts ...ProjectOptionsFn) (*ProjectOpti | ||||
| 	options := &ProjectOptions{ | ||||
| 		ConfigPaths: configs, | ||||
| 		Environment: map[string]string{}, | ||||
| 		Listeners:   []loader.Listener{}, | ||||
| 	} | ||||
| 	for _, o := range opts { | ||||
| 		err := o(options) | ||||
| @@ -334,14 +337,6 @@ func WithResolvedPaths(resolve bool) ProjectOptionsFn { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // WithContext sets the context used to load model and resources | ||||
| func WithContext(ctx context.Context) ProjectOptionsFn { | ||||
| 	return func(o *ProjectOptions) error { | ||||
| 		o.ctx = ctx | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // WithResourceLoader register support for ResourceLoader to manage remote resources | ||||
| func WithResourceLoader(r loader.ResourceLoader) ProjectOptionsFn { | ||||
| 	return func(o *ProjectOptions) error { | ||||
| @@ -352,6 +347,24 @@ func WithResourceLoader(r loader.ResourceLoader) ProjectOptionsFn { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // WithExtension register a know extension `x-*` with the go struct type to decode into | ||||
| func WithExtension(name string, typ any) ProjectOptionsFn { | ||||
| 	return func(o *ProjectOptions) error { | ||||
| 		o.loadOptions = append(o.loadOptions, func(options *loader.Options) { | ||||
| 			if options.KnownExtensions == nil { | ||||
| 				options.KnownExtensions = map[string]any{} | ||||
| 			} | ||||
| 			options.KnownExtensions[name] = typ | ||||
| 		}) | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Append listener to event | ||||
| func (o *ProjectOptions) WithListeners(listeners ...loader.Listener) { | ||||
| 	o.Listeners = append(o.Listeners, listeners...) | ||||
| } | ||||
|  | ||||
| // WithoutEnvironmentResolution disable environment resolution | ||||
| func WithoutEnvironmentResolution(o *ProjectOptions) error { | ||||
| 	o.loadOptions = append(o.loadOptions, func(options *loader.Options) { | ||||
| @@ -368,7 +381,7 @@ var DefaultOverrideFileNames = []string{"compose.override.yml", "compose.overrid | ||||
|  | ||||
| func (o ProjectOptions) GetWorkingDir() (string, error) { | ||||
| 	if o.WorkingDir != "" { | ||||
| 		return o.WorkingDir, nil | ||||
| 		return filepath.Abs(o.WorkingDir) | ||||
| 	} | ||||
| 	for _, path := range o.ConfigPaths { | ||||
| 		if path != "-" { | ||||
| @@ -382,9 +395,8 @@ func (o ProjectOptions) GetWorkingDir() (string, error) { | ||||
| 	return os.Getwd() | ||||
| } | ||||
|  | ||||
| // ProjectFromOptions load a compose project based on command line options | ||||
| func ProjectFromOptions(options *ProjectOptions) (*types.Project, error) { | ||||
| 	configPaths, err := getConfigPathsFromOptions(options) | ||||
| func (o ProjectOptions) GeConfigFiles() ([]types.ConfigFile, error) { | ||||
| 	configPaths, err := o.getConfigPaths() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| @@ -412,24 +424,25 @@ func ProjectFromOptions(options *ProjectOptions) (*types.Project, error) { | ||||
| 			Content:  b, | ||||
| 		}) | ||||
| 	} | ||||
| 	return configs, err | ||||
| } | ||||
|  | ||||
| // ProjectFromOptions load a compose project based on command line options | ||||
| func ProjectFromOptions(ctx context.Context, options *ProjectOptions) (*types.Project, error) { | ||||
| 	configs, err := options.GeConfigFiles() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	workingDir, err := options.GetWorkingDir() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	absWorkingDir, err := filepath.Abs(workingDir) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	options.loadOptions = append(options.loadOptions, | ||||
| 		withNamePrecedenceLoad(absWorkingDir, options), | ||||
| 		withConvertWindowsPaths(options)) | ||||
|  | ||||
| 	ctx := options.ctx | ||||
| 	if ctx == nil { | ||||
| 		ctx = context.Background() | ||||
| 	} | ||||
| 		withNamePrecedenceLoad(workingDir, options), | ||||
| 		withConvertWindowsPaths(options), | ||||
| 		withListeners(options)) | ||||
|  | ||||
| 	project, err := loader.LoadWithContext(ctx, types.ConfigDetails{ | ||||
| 		ConfigFiles: configs, | ||||
| @@ -440,7 +453,10 @@ func ProjectFromOptions(options *ProjectOptions) (*types.Project, error) { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	project.ComposeFiles = configPaths | ||||
| 	for _, config := range configs { | ||||
| 		project.ComposeFiles = append(project.ComposeFiles, config.Filename) | ||||
| 	} | ||||
|  | ||||
| 	return project, nil | ||||
| } | ||||
|  | ||||
| @@ -467,10 +483,17 @@ func withConvertWindowsPaths(options *ProjectOptions) func(*loader.Options) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // getConfigPathsFromOptions retrieves the config files for project based on project options | ||||
| func getConfigPathsFromOptions(options *ProjectOptions) ([]string, error) { | ||||
| 	if len(options.ConfigPaths) != 0 { | ||||
| 		return absolutePaths(options.ConfigPaths) | ||||
| // save listeners from ProjectOptions (compose) to loader.Options | ||||
| func withListeners(options *ProjectOptions) func(*loader.Options) { | ||||
| 	return func(opts *loader.Options) { | ||||
| 		opts.Listeners = append(opts.Listeners, options.Listeners...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // getConfigPaths retrieves the config files for project based on project options | ||||
| func (o *ProjectOptions) getConfigPaths() ([]string, error) { | ||||
| 	if len(o.ConfigPaths) != 0 { | ||||
| 		return absolutePaths(o.ConfigPaths) | ||||
| 	} | ||||
| 	return nil, fmt.Errorf("no configuration file provided: %w", errdefs.ErrNotFound) | ||||
| } | ||||
|   | ||||
							
								
								
									
										59
									
								
								vendor/github.com/compose-spec/compose-go/v2/loader/environment.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								vendor/github.com/compose-spec/compose-go/v2/loader/environment.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| /* | ||||
|    Copyright 2020 The Compose Specification Authors. | ||||
|  | ||||
|    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|    you may not use this file except in compliance with the License. | ||||
|    You may obtain a copy of the License at | ||||
|  | ||||
|        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
|    Unless required by applicable law or agreed to in writing, software | ||||
|    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|    See the License for the specific language governing permissions and | ||||
|    limitations under the License. | ||||
| */ | ||||
|  | ||||
| package loader | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
|  | ||||
| 	"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) { | ||||
| 	services, ok := dict["services"].(map[string]any) | ||||
| 	if !ok { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	for service, cfg := range services { | ||||
| 		serviceConfig, ok := cfg.(map[string]any) | ||||
| 		if !ok { | ||||
| 			continue | ||||
| 		} | ||||
| 		serviceEnv, ok := serviceConfig["environment"].([]any) | ||||
| 		if !ok { | ||||
| 			continue | ||||
| 		} | ||||
| 		envs := []any{} | ||||
| 		for _, env := range serviceEnv { | ||||
| 			varEnv, ok := env.(string) | ||||
| 			if !ok { | ||||
| 				continue | ||||
| 			} | ||||
| 			if found, ok := config.Environment[varEnv]; ok { | ||||
| 				envs = append(envs, fmt.Sprintf("%s=%s", varEnv, found)) | ||||
| 			} else { | ||||
| 				// either does not exist or it was already resolved in interpolation | ||||
| 				envs = append(envs, varEnv) | ||||
| 			} | ||||
| 		} | ||||
| 		serviceConfig["environment"] = envs | ||||
| 		services[service] = serviceConfig | ||||
| 	} | ||||
| 	dict["services"] = services | ||||
| } | ||||
							
								
								
									
										50
									
								
								vendor/github.com/compose-spec/compose-go/v2/loader/extends.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										50
									
								
								vendor/github.com/compose-spec/compose-go/v2/loader/extends.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -20,6 +20,7 @@ import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/compose-spec/compose-go/v2/consts" | ||||
| 	"github.com/compose-spec/compose-go/v2/override" | ||||
| @@ -60,11 +61,8 @@ func applyServiceExtends(ctx context.Context, name string, services map[string]a | ||||
| 		return s, nil | ||||
| 	} | ||||
| 	filename := ctx.Value(consts.ComposeFileKey{}).(string) | ||||
| 	tracker, err := tracker.Add(filename, name) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	var ( | ||||
| 		err  error | ||||
| 		ref  string | ||||
| 		file any | ||||
| 	) | ||||
| @@ -72,14 +70,16 @@ func applyServiceExtends(ctx context.Context, name string, services map[string]a | ||||
| 	case map[string]any: | ||||
| 		ref = v["service"].(string) | ||||
| 		file = v["file"] | ||||
| 		opts.ProcessEvent("extends", v) | ||||
| 	case string: | ||||
| 		ref = v | ||||
| 		opts.ProcessEvent("extends", map[string]any{"service": ref}) | ||||
| 	} | ||||
|  | ||||
| 	var base any | ||||
| 	if file != nil { | ||||
| 		path := file.(string) | ||||
| 		services, err = getExtendsBaseFromFile(ctx, ref, path, opts, tracker) | ||||
| 		filename = file.(string) | ||||
| 		services, err = getExtendsBaseFromFile(ctx, ref, filename, opts, tracker) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| @@ -89,6 +89,12 @@ func applyServiceExtends(ctx context.Context, name string, services map[string]a | ||||
| 			return nil, fmt.Errorf("cannot extend service %q in %s: service not found", name, filename) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	tracker, err = tracker.Add(filename, name) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	// recursively apply `extends` | ||||
| 	base, err = applyServiceExtends(ctx, ref, services, opts, tracker, post...) | ||||
| 	if err != nil { | ||||
| @@ -99,6 +105,12 @@ func applyServiceExtends(ctx context.Context, name string, services map[string]a | ||||
| 		return service, nil | ||||
| 	} | ||||
| 	source := deepClone(base).(map[string]any) | ||||
|  | ||||
| 	err = validateExtendSource(source, ref) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	for _, processor := range post { | ||||
| 		processor.Apply(map[string]any{ | ||||
| 			"services": map[string]any{ | ||||
| @@ -111,9 +123,34 @@ func applyServiceExtends(ctx context.Context, name string, services map[string]a | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	delete(merged, "extends") | ||||
| 	services[name] = merged | ||||
| 	return merged, nil | ||||
| } | ||||
|  | ||||
| // validateExtendSource check the source for `extends` doesn't refer to another container/service | ||||
| func validateExtendSource(source map[string]any, ref string) error { | ||||
| 	forbidden := []string{"links", "volumes_from", "depends_on"} | ||||
| 	for _, key := range forbidden { | ||||
| 		if _, ok := source[key]; ok { | ||||
| 			return fmt.Errorf("service %q can't be used with `extends` as it declare `%s`", ref, key) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	sharedNamespace := []string{"network_mode", "ipc", "pid", "net", "cgroup", "userns_mode", "uts"} | ||||
| 	for _, key := range sharedNamespace { | ||||
| 		if v, ok := source[key]; ok { | ||||
| 			val := v.(string) | ||||
| 			if strings.HasPrefix(val, types.ContainerPrefix) { | ||||
| 				return fmt.Errorf("service %q can't be used with `extends` as it shares `%s` with another container", ref, key) | ||||
| 			} | ||||
| 			if strings.HasPrefix(val, types.ServicePrefix) { | ||||
| 				return fmt.Errorf("service %q can't be used with `extends` as it shares `%s` with another service", ref, key) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func getExtendsBaseFromFile(ctx context.Context, name string, path string, opts *Options, ct *cycleTracker) (map[string]any, error) { | ||||
| 	for _, loader := range opts.ResourceLoaders { | ||||
| 		if !loader.Accept(path) { | ||||
| @@ -137,6 +174,7 @@ func getExtendsBaseFromFile(ctx context.Context, name string, path string, opts | ||||
| 		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{ | ||||
|   | ||||
							
								
								
									
										6
									
								
								vendor/github.com/compose-spec/compose-go/v2/loader/full-example.yml
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								vendor/github.com/compose-spec/compose-go/v2/loader/full-example.yml
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -26,7 +26,8 @@ services: | ||||
|       additional_contexts: | ||||
|         foo: ./bar | ||||
|       secrets: | ||||
|         - secret1 | ||||
|         - source: secret1 | ||||
|           target: /run/secrets/secret1 | ||||
|         - source: secret2 | ||||
|           target: my_secret | ||||
|           uid: '103' | ||||
| @@ -257,7 +258,8 @@ services: | ||||
|     restart: always | ||||
|  | ||||
|     secrets: | ||||
|       - secret1 | ||||
|       - source: secret1 | ||||
|         target: /run/secrets/secret1 | ||||
|       - source: secret2 | ||||
|         target: my_secret | ||||
|         uid: '103' | ||||
|   | ||||
							
								
								
									
										43
									
								
								vendor/github.com/compose-spec/compose-go/v2/loader/include.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										43
									
								
								vendor/github.com/compose-spec/compose-go/v2/loader/include.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -34,6 +34,14 @@ func loadIncludeConfig(source any) ([]types.IncludeConfig, error) { | ||||
| 	if source == nil { | ||||
| 		return nil, nil | ||||
| 	} | ||||
| 	configs := source.([]any) | ||||
| 	for i, config := range configs { | ||||
| 		if v, ok := config.(string); ok { | ||||
| 			configs[i] = map[string]any{ | ||||
| 				"path": v, | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	var requires []types.IncludeConfig | ||||
| 	err := Transform(source, &requires) | ||||
| 	return requires, err | ||||
| @@ -45,6 +53,13 @@ func ApplyInclude(ctx context.Context, configDetails types.ConfigDetails, model | ||||
| 		return err | ||||
| 	} | ||||
| 	for _, r := range includeConfig { | ||||
| 		for _, listener := range options.Listeners { | ||||
| 			listener("include", map[string]any{ | ||||
| 				"path":       r.Path, | ||||
| 				"workingdir": configDetails.WorkingDir, | ||||
| 			}) | ||||
| 		} | ||||
|  | ||||
| 		for i, p := range r.Path { | ||||
| 			for _, loader := range options.ResourceLoaders { | ||||
| 				if loader.Accept(p) { | ||||
| @@ -56,7 +71,7 @@ func ApplyInclude(ctx context.Context, configDetails types.ConfigDetails, model | ||||
| 					break | ||||
| 				} | ||||
| 			} | ||||
| 			r.Path[i] = absPath(configDetails.WorkingDir, p) | ||||
| 			r.Path[i] = p | ||||
| 		} | ||||
|  | ||||
| 		mainFile := r.Path[0] | ||||
| @@ -70,17 +85,41 @@ func ApplyInclude(ctx context.Context, configDetails types.ConfigDetails, model | ||||
| 		if r.ProjectDirectory == "" { | ||||
| 			r.ProjectDirectory = filepath.Dir(mainFile) | ||||
| 		} | ||||
| 		relworkingdir, err := filepath.Rel(configDetails.WorkingDir, r.ProjectDirectory) | ||||
| 		if err != nil { | ||||
| 			// included file path is not inside project working directory => use absolute path | ||||
| 			relworkingdir = r.ProjectDirectory | ||||
| 		} | ||||
|  | ||||
| 		loadOptions := options.clone() | ||||
| 		loadOptions.ResolvePaths = true | ||||
| 		loadOptions.SkipNormalization = true | ||||
| 		loadOptions.SkipConsistencyCheck = true | ||||
| 		loadOptions.ResourceLoaders = append(loadOptions.RemoteResourceLoaders(), localResourceLoader{ | ||||
| 			WorkingDir: relworkingdir, | ||||
| 		}) | ||||
|  | ||||
| 		if len(r.EnvFile) == 0 { | ||||
| 			f := filepath.Join(r.ProjectDirectory, ".env") | ||||
| 			if s, err := os.Stat(f); err == nil && !s.IsDir() { | ||||
| 				r.EnvFile = types.StringList{f} | ||||
| 			} | ||||
| 		} else { | ||||
| 			envFile := []string{} | ||||
| 			for _, f := range r.EnvFile { | ||||
| 				if !filepath.IsAbs(f) { | ||||
| 					f = filepath.Join(configDetails.WorkingDir, f) | ||||
| 					s, err := os.Stat(f) | ||||
| 					if err != nil { | ||||
| 						return err | ||||
| 					} | ||||
| 					if s.IsDir() { | ||||
| 						return fmt.Errorf("%s is not a file", f) | ||||
| 					} | ||||
| 				} | ||||
| 				envFile = append(envFile, f) | ||||
| 			} | ||||
| 			r.EnvFile = envFile | ||||
| 		} | ||||
|  | ||||
| 		envFromFile, err := dotenv.GetEnvFromFile(configDetails.Environment, r.EnvFile) | ||||
| @@ -89,7 +128,7 @@ func ApplyInclude(ctx context.Context, configDetails types.ConfigDetails, model | ||||
| 		} | ||||
|  | ||||
| 		config := types.ConfigDetails{ | ||||
| 			WorkingDir:  r.ProjectDirectory, | ||||
| 			WorkingDir:  relworkingdir, | ||||
| 			ConfigFiles: types.ToConfigFiles(r.Path), | ||||
| 			Environment: configDetails.Environment.Clone().Merge(envFromFile), | ||||
| 		} | ||||
|   | ||||
							
								
								
									
										182
									
								
								vendor/github.com/compose-spec/compose-go/v2/loader/loader.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										182
									
								
								vendor/github.com/compose-spec/compose-go/v2/loader/loader.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -64,6 +64,8 @@ type Options struct { | ||||
| 	SkipInclude bool | ||||
| 	// SkipResolveEnvironment will ignore computing `environment` for services | ||||
| 	SkipResolveEnvironment bool | ||||
| 	// SkipDefaultValues will ignore missing required attributes | ||||
| 	SkipDefaultValues bool | ||||
| 	// Interpolation options | ||||
| 	Interpolate *interp.Options | ||||
| 	// Discard 'env_file' entries after resolving to 'environment' section | ||||
| @@ -76,6 +78,19 @@ type Options struct { | ||||
| 	Profiles []string | ||||
| 	// ResourceLoaders manages support for remote resources | ||||
| 	ResourceLoaders []ResourceLoader | ||||
| 	// KnownExtensions manages x-* attribute we know and the corresponding go structs | ||||
| 	KnownExtensions map[string]any | ||||
| 	// Metada for telemetry | ||||
| 	Listeners []Listener | ||||
| } | ||||
|  | ||||
| type Listener = func(event string, metadata map[string]any) | ||||
|  | ||||
| // Invoke all listeners for an event | ||||
| func (o *Options) ProcessEvent(event string, metadata map[string]any) { | ||||
| 	for _, l := range o.Listeners { | ||||
| 		l(event, metadata) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // ResourceLoader is a plugable remote resource resolver | ||||
| @@ -148,6 +163,8 @@ func (o *Options) clone() *Options { | ||||
| 		projectNameImperativelySet: o.projectNameImperativelySet, | ||||
| 		Profiles:                   o.Profiles, | ||||
| 		ResourceLoaders:            o.ResourceLoaders, | ||||
| 		KnownExtensions:            o.KnownExtensions, | ||||
| 		Listeners:                  o.Listeners, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -288,18 +305,17 @@ func LoadWithContext(ctx context.Context, configDetails types.ConfigDetails, opt | ||||
| 	} | ||||
| 	opts.ResourceLoaders = append(opts.ResourceLoaders, localResourceLoader{configDetails.WorkingDir}) | ||||
|  | ||||
| 	projectName, err := projectName(configDetails, opts) | ||||
| 	err := projectName(configDetails, opts) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	opts.projectName = projectName | ||||
|  | ||||
| 	// TODO(milas): this should probably ALWAYS set (overriding any existing) | ||||
| 	if _, ok := configDetails.Environment[consts.ComposeProjectName]; !ok && projectName != "" { | ||||
| 	if _, ok := configDetails.Environment[consts.ComposeProjectName]; !ok && opts.projectName != "" { | ||||
| 		if configDetails.Environment == nil { | ||||
| 			configDetails.Environment = map[string]string{} | ||||
| 		} | ||||
| 		configDetails.Environment[consts.ComposeProjectName] = projectName | ||||
| 		configDetails.Environment[consts.ComposeProjectName] = opts.projectName | ||||
| 	} | ||||
|  | ||||
| 	return load(ctx, configDetails, opts, nil) | ||||
| @@ -312,7 +328,7 @@ func loadYamlModel(ctx context.Context, config types.ConfigDetails, opts *Option | ||||
| 	) | ||||
| 	for _, file := range config.ConfigFiles { | ||||
| 		fctx := context.WithValue(ctx, consts.ComposeFileKey{}, file.Filename) | ||||
| 		if len(file.Content) == 0 && file.Config == nil { | ||||
| 		if file.Content == nil && file.Config == nil { | ||||
| 			content, err := os.ReadFile(file.Filename) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| @@ -352,6 +368,14 @@ func loadYamlModel(ctx context.Context, config types.ConfigDetails, opts *Option | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			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 | ||||
| @@ -400,9 +424,14 @@ func loadYamlModel(ctx context.Context, config types.ConfigDetails, opts *Option | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	if !opts.SkipInclude { | ||||
| 		included = append(included, config.ConfigFiles[0].Filename) | ||||
| 		err = ApplyInclude(ctx, config, dict, opts, included) | ||||
| 	// 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 | ||||
| 	} | ||||
|  | ||||
| 	if !opts.SkipDefaultValues { | ||||
| 		dict, err = transform.SetDefaultValues(dict) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| @@ -424,6 +453,7 @@ func loadYamlModel(ctx context.Context, config types.ConfigDetails, opts *Option | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
| 	resolveServicesEnvironment(dict, config) | ||||
|  | ||||
| 	return dict, nil | ||||
| } | ||||
| @@ -438,8 +468,6 @@ func load(ctx context.Context, configDetails types.ConfigDetails, opts *Options, | ||||
| 	} | ||||
| 	loaded = append(loaded, mainFile) | ||||
|  | ||||
| 	includeRefs := make(map[string][]types.IncludeConfig) | ||||
|  | ||||
| 	dict, err := loadYamlModel(ctx, configDetails, opts, &cycleTracker{}, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| @@ -449,6 +477,10 @@ func load(ctx context.Context, configDetails types.ConfigDetails, opts *Options, | ||||
| 		return nil, errors.New("empty compose file") | ||||
| 	} | ||||
|  | ||||
| 	if opts.projectName == "" { | ||||
| 		return nil, errors.New("project name must not be empty") | ||||
| 	} | ||||
|  | ||||
| 	project := &types.Project{ | ||||
| 		Name:        opts.projectName, | ||||
| 		WorkingDir:  configDetails.WorkingDir, | ||||
| @@ -456,14 +488,14 @@ func load(ctx context.Context, configDetails types.ConfigDetails, opts *Options, | ||||
| 	} | ||||
| 	delete(dict, "name") // project name set by yaml must be identified by caller as opts.projectName | ||||
|  | ||||
| 	dict = groupXFieldsIntoExtensions(dict, tree.NewPath()) | ||||
| 	err = Transform(dict, project) | ||||
| 	dict, err = processExtensions(dict, tree.NewPath(), opts.KnownExtensions) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	if len(includeRefs) != 0 { | ||||
| 		project.IncludeReferences = includeRefs | ||||
| 	err = Transform(dict, project) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	if !opts.SkipNormalization { | ||||
| @@ -516,69 +548,68 @@ func InvalidProjectNameErr(v string) error { | ||||
| // | ||||
| // TODO(milas): restructure loading so that we don't need to re-parse the YAML | ||||
| // here, as it's both wasteful and makes this code error-prone. | ||||
| func projectName(details types.ConfigDetails, opts *Options) (string, error) { | ||||
| 	projectName, projectNameImperativelySet := opts.GetProjectName() | ||||
| func projectName(details types.ConfigDetails, opts *Options) error { | ||||
| 	if opts.projectNameImperativelySet { | ||||
| 		if NormalizeProjectName(opts.projectName) != opts.projectName { | ||||
| 			return InvalidProjectNameErr(opts.projectName) | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	type named struct { | ||||
| 		Name string `yaml:"name"` | ||||
| 	} | ||||
|  | ||||
| 	// if user did NOT provide a name explicitly, then see if one is defined | ||||
| 	// in any of the config files | ||||
| 	if !projectNameImperativelySet { | ||||
| 		var pjNameFromConfigFile string | ||||
| 		for _, configFile := range details.ConfigFiles { | ||||
| 			content := configFile.Content | ||||
| 			if content == nil { | ||||
| 				// This can be hit when Filename is set but Content is not. One | ||||
| 				// example is when using ToConfigFiles(). | ||||
| 				d, err := os.ReadFile(configFile.Filename) | ||||
| 				if err != nil { | ||||
| 					return "", fmt.Errorf("failed to read file %q: %w", configFile.Filename, err) | ||||
| 				} | ||||
| 				content = d | ||||
| 	var pjNameFromConfigFile string | ||||
| 	for _, configFile := range details.ConfigFiles { | ||||
| 		content := configFile.Content | ||||
| 		if content == nil { | ||||
| 			// This can be hit when Filename is set but Content is not. One | ||||
| 			// example is when using ToConfigFiles(). | ||||
| 			d, err := os.ReadFile(configFile.Filename) | ||||
| 			if err != nil { | ||||
| 				return fmt.Errorf("failed to read file %q: %w", configFile.Filename, err) | ||||
| 			} | ||||
| 			content = d | ||||
| 			configFile.Content = d | ||||
| 		} | ||||
| 		var n named | ||||
| 		r := bytes.NewReader(content) | ||||
| 		decoder := yaml.NewDecoder(r) | ||||
| 		for { | ||||
| 			err := decoder.Decode(&n) | ||||
| 			if err != nil && errors.Is(err, io.EOF) { | ||||
| 				break | ||||
| 			} | ||||
| 			yml, err := ParseYAML(content) | ||||
| 			if err != nil { | ||||
| 				// HACK: the way that loading is currently structured, this is | ||||
| 				// a duplicative parse just for the `name`. if it fails, we | ||||
| 				// give up but don't return the error, knowing that it'll get | ||||
| 				// caught downstream for us | ||||
| 				return "", nil | ||||
| 				break | ||||
| 			} | ||||
| 			if val, ok := yml["name"]; ok && val != "" { | ||||
| 				sVal, ok := val.(string) | ||||
| 				if !ok { | ||||
| 					// HACK: see above - this is a temporary parsed version | ||||
| 					// that hasn't been schema-validated, but we don't want | ||||
| 					// to be the ones to actually report that, so give up, | ||||
| 					// knowing that it'll get caught downstream for us | ||||
| 					return "", nil | ||||
| 				} | ||||
| 				pjNameFromConfigFile = sVal | ||||
| 			if n.Name != "" { | ||||
| 				pjNameFromConfigFile = n.Name | ||||
| 			} | ||||
| 		} | ||||
| 		if !opts.SkipInterpolation { | ||||
| 			interpolated, err := interp.Interpolate( | ||||
| 				map[string]interface{}{"name": pjNameFromConfigFile}, | ||||
| 				*opts.Interpolate, | ||||
| 			) | ||||
| 			if err != nil { | ||||
| 				return "", err | ||||
| 			} | ||||
| 			pjNameFromConfigFile = interpolated["name"].(string) | ||||
| 		} | ||||
| 		pjNameFromConfigFile = NormalizeProjectName(pjNameFromConfigFile) | ||||
| 		if pjNameFromConfigFile != "" { | ||||
| 			projectName = pjNameFromConfigFile | ||||
| 	} | ||||
| 	if !opts.SkipInterpolation { | ||||
| 		interpolated, err := interp.Interpolate( | ||||
| 			map[string]interface{}{"name": pjNameFromConfigFile}, | ||||
| 			*opts.Interpolate, | ||||
| 		) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		pjNameFromConfigFile = interpolated["name"].(string) | ||||
| 	} | ||||
|  | ||||
| 	if projectName == "" { | ||||
| 		return "", errors.New("project name must not be empty") | ||||
| 	pjNameFromConfigFile = NormalizeProjectName(pjNameFromConfigFile) | ||||
| 	if pjNameFromConfigFile != "" { | ||||
| 		opts.projectName = pjNameFromConfigFile | ||||
| 	} | ||||
|  | ||||
| 	if NormalizeProjectName(projectName) != projectName { | ||||
| 		return "", InvalidProjectNameErr(projectName) | ||||
| 	} | ||||
|  | ||||
| 	return projectName, nil | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func NormalizeProjectName(s string) string { | ||||
| @@ -596,8 +627,9 @@ var userDefinedKeys = []tree.Path{ | ||||
| 	"configs", | ||||
| } | ||||
|  | ||||
| func groupXFieldsIntoExtensions(dict map[string]interface{}, p tree.Path) map[string]interface{} { | ||||
| 	extras := map[string]interface{}{} | ||||
| func processExtensions(dict map[string]any, p tree.Path, extensions map[string]any) (map[string]interface{}, error) { | ||||
| 	extras := map[string]any{} | ||||
| 	var err error | ||||
| 	for key, value := range dict { | ||||
| 		skip := false | ||||
| 		for _, uk := range userDefinedKeys { | ||||
| @@ -613,19 +645,35 @@ func groupXFieldsIntoExtensions(dict map[string]interface{}, p tree.Path) map[st | ||||
| 		} | ||||
| 		switch v := value.(type) { | ||||
| 		case map[string]interface{}: | ||||
| 			dict[key] = groupXFieldsIntoExtensions(v, p.Next(key)) | ||||
| 			dict[key], err = processExtensions(v, p.Next(key), extensions) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 		case []interface{}: | ||||
| 			for i, e := range v { | ||||
| 				if m, ok := e.(map[string]interface{}); ok { | ||||
| 					v[i] = groupXFieldsIntoExtensions(m, p.Next(strconv.Itoa(i))) | ||||
| 					v[i], err = processExtensions(m, p.Next(strconv.Itoa(i)), extensions) | ||||
| 					if err != nil { | ||||
| 						return nil, err | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	for name, val := range extras { | ||||
| 		if typ, ok := extensions[name]; ok { | ||||
| 			target := reflect.New(reflect.TypeOf(typ)).Elem().Interface() | ||||
| 			err = Transform(val, &target) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			extras[name] = target | ||||
| 		} | ||||
| 	} | ||||
| 	if len(extras) > 0 { | ||||
| 		dict[consts.Extensions] = extras | ||||
| 	} | ||||
| 	return dict | ||||
| 	return dict, nil | ||||
| } | ||||
|  | ||||
| // Transform converts the source into the target struct with compose types transformer | ||||
|   | ||||
							
								
								
									
										19
									
								
								vendor/github.com/compose-spec/compose-go/v2/loader/paths.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										19
									
								
								vendor/github.com/compose-spec/compose-go/v2/loader/paths.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -37,25 +37,6 @@ func ResolveRelativePaths(project *types.Project) error { | ||||
| 		return err | ||||
| 	} | ||||
| 	project.ComposeFiles = absComposeFiles | ||||
|  | ||||
| 	// don't coerce a nil map to an empty map | ||||
| 	if project.IncludeReferences != nil { | ||||
| 		absIncludes := make(map[string][]types.IncludeConfig, len(project.IncludeReferences)) | ||||
| 		for filename, config := range project.IncludeReferences { | ||||
| 			filename = absPath(project.WorkingDir, filename) | ||||
| 			absConfigs := make([]types.IncludeConfig, len(config)) | ||||
| 			for i, c := range config { | ||||
| 				absConfigs[i] = types.IncludeConfig{ | ||||
| 					Path:             resolvePaths(project.WorkingDir, c.Path), | ||||
| 					ProjectDirectory: absPath(project.WorkingDir, c.ProjectDirectory), | ||||
| 					EnvFile:          resolvePaths(project.WorkingDir, c.EnvFile), | ||||
| 				} | ||||
| 			} | ||||
| 			absIncludes[filename] = absConfigs | ||||
| 		} | ||||
| 		project.IncludeReferences = absIncludes | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										17
									
								
								vendor/github.com/compose-spec/compose-go/v2/loader/validate.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										17
									
								
								vendor/github.com/compose-spec/compose-go/v2/loader/validate.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -29,6 +29,7 @@ import ( | ||||
|  | ||||
| // checkConsistency validate a compose model is consistent | ||||
| func checkConsistency(project *types.Project) error { | ||||
| 	containerNames := map[string]string{} | ||||
| 	for _, s := range project.Services { | ||||
| 		if s.Build == nil && s.Image == "" { | ||||
| 			return fmt.Errorf("service %q has neither an image nor a build context specified: %w", s.Name, errdefs.ErrInvalid) | ||||
| @@ -123,6 +124,13 @@ func checkConsistency(project *types.Project) error { | ||||
| 			s.Deploy.Replicas = s.Scale | ||||
| 		} | ||||
|  | ||||
| 		if s.ContainerName != "" { | ||||
| 			if existing, ok := containerNames[s.ContainerName]; ok { | ||||
| 				return fmt.Errorf(`"services.%s": container name "%s" is already in use by "services.%s": %w`, s.Name, s.ContainerName, existing, errdefs.ErrInvalid) | ||||
| 			} | ||||
| 			containerNames[s.ContainerName] = s.Name | ||||
| 		} | ||||
|  | ||||
| 		if s.GetScale() > 1 && s.ContainerName != "" { | ||||
| 			attr := "scale" | ||||
| 			if s.Scale == nil { | ||||
| @@ -131,6 +139,15 @@ func checkConsistency(project *types.Project) error { | ||||
| 			return fmt.Errorf("services.%s: can't set container_name and %s as container name must be unique: %w", attr, | ||||
| 				s.Name, errdefs.ErrInvalid) | ||||
| 		} | ||||
|  | ||||
| 		if s.Develop != nil && s.Develop.Watch != nil { | ||||
| 			for _, watch := range s.Develop.Watch { | ||||
| 				if watch.Action != types.WatchActionRebuild && watch.Target == "" { | ||||
| 					return fmt.Errorf("services.%s.develop.watch: target is required for non-rebuild actions: %w", s.Name, errdefs.ErrInvalid) | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	for name, secret := range project.Secrets { | ||||
|   | ||||
							
								
								
									
										8
									
								
								vendor/github.com/compose-spec/compose-go/v2/override/merge.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								vendor/github.com/compose-spec/compose-go/v2/override/merge.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -17,6 +17,7 @@ | ||||
| package override | ||||
|  | ||||
| import ( | ||||
| 	"cmp" | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
|  | ||||
| @@ -40,10 +41,13 @@ var mergeSpecials = map[tree.Path]merger{} | ||||
|  | ||||
| func init() { | ||||
| 	mergeSpecials["networks.*.ipam.config"] = mergeIPAMConfig | ||||
| 	mergeSpecials["networks.*.labels"] = mergeToSequence | ||||
| 	mergeSpecials["volumes.*.labels"] = mergeToSequence | ||||
| 	mergeSpecials["services.*.annotations"] = mergeToSequence | ||||
| 	mergeSpecials["services.*.build"] = mergeBuild | ||||
| 	mergeSpecials["services.*.build.args"] = mergeToSequence | ||||
| 	mergeSpecials["services.*.build.additional_contexts"] = mergeToSequence | ||||
| 	mergeSpecials["services.*.build.extra_hosts"] = mergeToSequence | ||||
| 	mergeSpecials["services.*.build.labels"] = mergeToSequence | ||||
| 	mergeSpecials["services.*.command"] = override | ||||
| 	mergeSpecials["services.*.depends_on"] = mergeDependsOn | ||||
| @@ -178,8 +182,8 @@ func convertIntoSequence(value any) []any { | ||||
| 			} | ||||
| 			i++ | ||||
| 		} | ||||
| 		slices.SortFunc(seq, func(a, b any) bool { | ||||
| 			return a.(string) < b.(string) | ||||
| 		slices.SortFunc(seq, func(a, b any) int { | ||||
| 			return cmp.Compare(a.(string), b.(string)) | ||||
| 		}) | ||||
| 		return seq | ||||
| 	case []any: | ||||
|   | ||||
							
								
								
									
										16
									
								
								vendor/github.com/compose-spec/compose-go/v2/override/uncity.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										16
									
								
								vendor/github.com/compose-spec/compose-go/v2/override/uncity.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -107,13 +107,17 @@ func enforceUnicity(value any, p tree.Path) (any, error) { | ||||
| 	return value, nil | ||||
| } | ||||
|  | ||||
| func keyValueIndexer(y any, _ tree.Path) (string, error) { | ||||
| 	value := y.(string) | ||||
| 	key, _, found := strings.Cut(value, "=") | ||||
| 	if !found { | ||||
| 		return value, nil | ||||
| func keyValueIndexer(y any, p tree.Path) (string, error) { | ||||
| 	switch value := y.(type) { | ||||
| 	case string: | ||||
| 		key, _, found := strings.Cut(value, "=") | ||||
| 		if !found { | ||||
| 			return value, nil | ||||
| 		} | ||||
| 		return key, nil | ||||
| 	default: | ||||
| 		return "", fmt.Errorf("%s: unexpected type %T", p, y) | ||||
| 	} | ||||
| 	return key, nil | ||||
| } | ||||
|  | ||||
| func volumeIndexer(y any, p tree.Path) (string, error) { | ||||
|   | ||||
							
								
								
									
										2
									
								
								vendor/github.com/compose-spec/compose-go/v2/schema/compose-spec.json
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/compose-spec/compose-go/v2/schema/compose-spec.json
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -455,6 +455,7 @@ | ||||
|           "type": "array", | ||||
|           "items": { | ||||
|             "type": "object", | ||||
|             "required": ["path", "action"], | ||||
|             "properties": { | ||||
|               "ignore": {"type": "array", "items": {"type": "string"}}, | ||||
|               "path": {"type": "string"}, | ||||
| @@ -462,7 +463,6 @@ | ||||
|               "target": {"type": "string"} | ||||
|             } | ||||
|           }, | ||||
|           "required": ["path", "action"], | ||||
|           "additionalProperties": false, | ||||
|           "patternProperties": {"^x-": {}} | ||||
|         } | ||||
|   | ||||
							
								
								
									
										2
									
								
								vendor/github.com/compose-spec/compose-go/v2/template/template.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/compose-spec/compose-go/v2/template/template.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -258,7 +258,7 @@ func getFirstBraceClosingIndex(s string) int { | ||||
| 				return i | ||||
| 			} | ||||
| 		} | ||||
| 		if strings.HasPrefix(s[i:], "${") { | ||||
| 		if s[i] == '{' { | ||||
| 			openVariableBraces++ | ||||
| 			i++ | ||||
| 		} | ||||
|   | ||||
							
								
								
									
										15
									
								
								vendor/github.com/compose-spec/compose-go/v2/transform/build.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										15
									
								
								vendor/github.com/compose-spec/compose-go/v2/transform/build.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -25,9 +25,6 @@ import ( | ||||
| func transformBuild(data any, p tree.Path) (any, error) { | ||||
| 	switch v := data.(type) { | ||||
| 	case map[string]any: | ||||
| 		if _, ok := v["context"]; !ok { | ||||
| 			v["context"] = "." // TODO(ndeloof) maybe we miss an explicit "set-defaults" loading phase | ||||
| 		} | ||||
| 		return transformMapping(v, p) | ||||
| 	case string: | ||||
| 		return map[string]any{ | ||||
| @@ -37,3 +34,15 @@ func transformBuild(data any, p tree.Path) (any, error) { | ||||
| 		return data, fmt.Errorf("%s: invalid type %T for build", p, v) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func defaultBuildContext(data any, _ tree.Path) (any, error) { | ||||
| 	switch v := data.(type) { | ||||
| 	case map[string]any: | ||||
| 		if _, ok := v["context"]; !ok { | ||||
| 			v["context"] = "." | ||||
| 		} | ||||
| 		return v, nil | ||||
| 	default: | ||||
| 		return data, nil | ||||
| 	} | ||||
| } | ||||
|   | ||||
							
								
								
									
										87
									
								
								vendor/github.com/compose-spec/compose-go/v2/transform/defaults.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								vendor/github.com/compose-spec/compose-go/v2/transform/defaults.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | ||||
| /* | ||||
|    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 transform | ||||
|  | ||||
| import ( | ||||
| 	"github.com/compose-spec/compose-go/v2/tree" | ||||
| ) | ||||
|  | ||||
| var defaultValues = map[tree.Path]transformFunc{} | ||||
|  | ||||
| func init() { | ||||
| 	defaultValues["services.*.build"] = defaultBuildContext | ||||
| 	defaultValues["services.*.secrets.*"] = defaultSecretMount | ||||
| } | ||||
|  | ||||
| // SetDefaultValues transforms a compose model to set default values to missing attributes | ||||
| func SetDefaultValues(yaml map[string]any) (map[string]any, error) { | ||||
| 	result, err := setDefaults(yaml, tree.NewPath()) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return result.(map[string]any), nil | ||||
| } | ||||
|  | ||||
| func setDefaults(data any, p tree.Path) (any, error) { | ||||
| 	for pattern, transformer := range defaultValues { | ||||
| 		if p.Matches(pattern) { | ||||
| 			t, err := transformer(data, p) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			return t, nil | ||||
| 		} | ||||
| 	} | ||||
| 	switch v := data.(type) { | ||||
| 	case map[string]any: | ||||
| 		a, err := setDefaultsMapping(v, p) | ||||
| 		if err != nil { | ||||
| 			return a, err | ||||
| 		} | ||||
| 		return v, nil | ||||
| 	case []any: | ||||
| 		a, err := setDefaultsSequence(v, p) | ||||
| 		if err != nil { | ||||
| 			return a, err | ||||
| 		} | ||||
| 		return v, nil | ||||
| 	default: | ||||
| 		return data, nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func setDefaultsSequence(v []any, p tree.Path) ([]any, error) { | ||||
| 	for i, e := range v { | ||||
| 		t, err := setDefaults(e, p.Next("[]")) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		v[i] = t | ||||
| 	} | ||||
| 	return v, nil | ||||
| } | ||||
|  | ||||
| func setDefaultsMapping(v map[string]any, p tree.Path) (map[string]any, error) { | ||||
| 	for k, e := range v { | ||||
| 		t, err := setDefaults(e, p.Next(k)) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		v[k] = t | ||||
| 	} | ||||
| 	return v, nil | ||||
| } | ||||
							
								
								
									
										2
									
								
								vendor/github.com/compose-spec/compose-go/v2/transform/ports.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/compose-spec/compose-go/v2/transform/ports.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -48,7 +48,7 @@ func transformPorts(data any, p tree.Path) (any, error) { | ||||
| 			case string: | ||||
| 				parsed, err := types.ParsePortConfig(value) | ||||
| 				if err != nil { | ||||
| 					return data, err | ||||
| 					return data, nil | ||||
| 				} | ||||
| 				if err != nil { | ||||
| 					return nil, err | ||||
|   | ||||
							
								
								
									
										13
									
								
								vendor/github.com/compose-spec/compose-go/v2/transform/secrets.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										13
									
								
								vendor/github.com/compose-spec/compose-go/v2/transform/secrets.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -34,3 +34,16 @@ func transformFileMount(data any, p tree.Path) (any, error) { | ||||
| 		return nil, fmt.Errorf("%s: unsupported type %T", p, data) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func defaultSecretMount(data any, p tree.Path) (any, error) { | ||||
| 	switch v := data.(type) { | ||||
| 	case map[string]any: | ||||
| 		source := v["source"] | ||||
| 		if _, ok := v["target"]; !ok { | ||||
| 			v["target"] = fmt.Sprintf("/run/secrets/%s", source) | ||||
| 		} | ||||
| 		return v, nil | ||||
| 	default: | ||||
| 		return nil, fmt.Errorf("%s: unsupported type %T", p, data) | ||||
| 	} | ||||
| } | ||||
|   | ||||
							
								
								
									
										10
									
								
								vendor/github.com/compose-spec/compose-go/v2/types/develop.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								vendor/github.com/compose-spec/compose-go/v2/types/develop.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -17,7 +17,7 @@ | ||||
| package types | ||||
|  | ||||
| type DevelopConfig struct { | ||||
| 	Watch []Trigger `json:"watch,omitempty"` | ||||
| 	Watch []Trigger `yaml:"watch,omitempty" json:"watch,omitempty"` | ||||
|  | ||||
| 	Extensions Extensions `yaml:"#extensions,inline,omitempty" json:"-"` | ||||
| } | ||||
| @@ -31,8 +31,8 @@ const ( | ||||
| ) | ||||
|  | ||||
| type Trigger struct { | ||||
| 	Path   string      `json:"path,omitempty"` | ||||
| 	Action WatchAction `json:"action,omitempty"` | ||||
| 	Target string      `json:"target,omitempty"` | ||||
| 	Ignore []string    `json:"ignore,omitempty"` | ||||
| 	Path   string      `yaml:"path" json:"path"` | ||||
| 	Action WatchAction `yaml:"action" json:"action"` | ||||
| 	Target string      `yaml:"target,omitempty" json:"target,omitempty"` | ||||
| 	Ignore []string    `yaml:"ignore,omitempty" json:"ignore,omitempty"` | ||||
| } | ||||
|   | ||||
							
								
								
									
										66
									
								
								vendor/github.com/compose-spec/compose-go/v2/types/hostList.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										66
									
								
								vendor/github.com/compose-spec/compose-go/v2/types/hostList.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -24,7 +24,33 @@ import ( | ||||
| ) | ||||
|  | ||||
| // HostsList is a list of colon-separated host-ip mappings | ||||
| type HostsList map[string]string | ||||
| type HostsList map[string][]string | ||||
|  | ||||
| // NewHostsList creates a HostsList from a list of `host=ip` strings | ||||
| func NewHostsList(hosts []string) (HostsList, error) { | ||||
| 	list := HostsList{} | ||||
| 	for _, s := range hosts { | ||||
| 		var found bool | ||||
| 		for _, sep := range hostListSerapators { | ||||
| 			host, ip, ok := strings.Cut(s, sep) | ||||
| 			if ok { | ||||
| 				// Mapping found with this separator, stop here. | ||||
| 				if ips, ok := list[host]; ok { | ||||
| 					list[host] = append(ips, ip) | ||||
| 				} else { | ||||
| 					list[host] = []string{ip} | ||||
| 				} | ||||
| 				found = true | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		if !found { | ||||
| 			return nil, fmt.Errorf("invalid additional host, missing IP: %s", s) | ||||
| 		} | ||||
| 	} | ||||
| 	err := list.cleanup() | ||||
| 	return list, err | ||||
| } | ||||
|  | ||||
| // AsList returns host-ip mappings as a list of strings, using the given | ||||
| // separator. The Docker Engine API expects ':' separators, the original format | ||||
| @@ -34,7 +60,9 @@ type HostsList map[string]string | ||||
| func (h HostsList) AsList(sep string) []string { | ||||
| 	l := make([]string, 0, len(h)) | ||||
| 	for k, v := range h { | ||||
| 		l = append(l, fmt.Sprintf("%s%s%s", k, sep, v)) | ||||
| 		for _, ip := range v { | ||||
| 			l = append(l, fmt.Sprintf("%s%s%s", k, sep, ip)) | ||||
| 		} | ||||
| 	} | ||||
| 	return l | ||||
| } | ||||
| @@ -51,6 +79,8 @@ func (h HostsList) MarshalJSON() ([]byte, error) { | ||||
| 	return json.Marshal(list) | ||||
| } | ||||
|  | ||||
| var hostListSerapators = []string{"=", ":"} | ||||
|  | ||||
| func (h *HostsList) DecodeMapstructure(value interface{}) error { | ||||
| 	switch v := value.(type) { | ||||
| 	case map[string]interface{}: | ||||
| @@ -59,25 +89,45 @@ func (h *HostsList) DecodeMapstructure(value interface{}) error { | ||||
| 			if e == nil { | ||||
| 				e = "" | ||||
| 			} | ||||
| 			list[i] = fmt.Sprint(e) | ||||
| 			list[i] = []string{fmt.Sprint(e)} | ||||
| 		} | ||||
| 		err := list.cleanup() | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		*h = list | ||||
| 		return nil | ||||
| 	case []interface{}: | ||||
| 		*h = decodeMapping(v, "=", ":") | ||||
| 		s := make([]string, len(v)) | ||||
| 		for i, e := range v { | ||||
| 			s[i] = fmt.Sprint(e) | ||||
| 		} | ||||
| 		list, err := NewHostsList(s) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		*h = list | ||||
| 		return nil | ||||
| 	default: | ||||
| 		return fmt.Errorf("unexpected value type %T for mapping", value) | ||||
| 	} | ||||
| 	for host, ip := range *h { | ||||
| } | ||||
|  | ||||
| func (h HostsList) cleanup() error { | ||||
| 	for host, ips := range h { | ||||
| 		// Check that there is a hostname and that it doesn't contain either | ||||
| 		// of the allowed separators, to generate a clearer error than the | ||||
| 		// engine would do if it splits the string differently. | ||||
| 		if host == "" || strings.ContainsAny(host, ":=") { | ||||
| 			return fmt.Errorf("bad host name '%s'", host) | ||||
| 		} | ||||
| 		// Remove brackets from IP addresses (for example "[::1]" -> "::1"). | ||||
| 		if len(ip) > 2 && ip[0] == '[' && ip[len(ip)-1] == ']' { | ||||
| 			(*h)[host] = ip[1 : len(ip)-1] | ||||
| 		for i, ip := range ips { | ||||
| 			// Remove brackets from IP addresses (for example "[::1]" -> "::1"). | ||||
| 			if len(ip) > 2 && ip[0] == '[' && ip[len(ip)-1] == ']' { | ||||
| 				ips[i] = ip[1 : len(ip)-1] | ||||
| 			} | ||||
| 		} | ||||
| 		h[host] = ips | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|   | ||||
							
								
								
									
										62
									
								
								vendor/github.com/compose-spec/compose-go/v2/types/project.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										62
									
								
								vendor/github.com/compose-spec/compose-go/v2/types/project.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -29,6 +29,7 @@ import ( | ||||
| 	"github.com/distribution/reference" | ||||
| 	"github.com/mitchellh/copystructure" | ||||
| 	godigest "github.com/opencontainers/go-digest" | ||||
| 	"golang.org/x/exp/maps" | ||||
| 	"golang.org/x/sync/errgroup" | ||||
| 	"gopkg.in/yaml.v3" | ||||
| ) | ||||
| @@ -46,13 +47,8 @@ type Project struct { | ||||
| 	Configs    Configs    `yaml:"configs,omitempty" json:"configs,omitempty"` | ||||
| 	Extensions Extensions `yaml:"#extensions,inline,omitempty" json:"-"` // https://github.com/golang/go/issues/6213 | ||||
|  | ||||
| 	// IncludeReferences is keyed by Compose YAML filename and contains config for | ||||
| 	// other Compose YAML files it directly triggered a load of via `include`. | ||||
| 	// | ||||
| 	// Note: this is | ||||
| 	IncludeReferences map[string][]IncludeConfig `yaml:"-" json:"-"` | ||||
| 	ComposeFiles      []string                   `yaml:"-" json:"-"` | ||||
| 	Environment       Mapping                    `yaml:"-" json:"-"` | ||||
| 	ComposeFiles []string `yaml:"-" json:"-"` | ||||
| 	Environment  Mapping  `yaml:"-" json:"-"` | ||||
|  | ||||
| 	// DisabledServices track services which have been disable as profile is not active | ||||
| 	DisabledServices Services `yaml:"-" json:"-"` | ||||
| @@ -119,6 +115,58 @@ func (p *Project) ConfigNames() []string { | ||||
| 	return names | ||||
| } | ||||
|  | ||||
| func (p *Project) ServicesWithBuild() []string { | ||||
| 	servicesBuild := p.Services.Filter(func(s ServiceConfig) bool { | ||||
| 		return s.Build != nil && s.Build.Context != "" | ||||
| 	}) | ||||
| 	return maps.Keys(servicesBuild) | ||||
| } | ||||
|  | ||||
| func (p *Project) ServicesWithExtends() []string { | ||||
| 	servicesExtends := p.Services.Filter(func(s ServiceConfig) bool { | ||||
| 		return s.Extends != nil && *s.Extends != (ExtendsConfig{}) | ||||
| 	}) | ||||
| 	return maps.Keys(servicesExtends) | ||||
| } | ||||
|  | ||||
| func (p *Project) ServicesWithDependsOn() []string { | ||||
| 	servicesDependsOn := p.Services.Filter(func(s ServiceConfig) bool { | ||||
| 		return len(s.DependsOn) > 0 | ||||
| 	}) | ||||
| 	return maps.Keys(servicesDependsOn) | ||||
| } | ||||
|  | ||||
| func (p *Project) ServicesWithCapabilities() ([]string, []string, []string) { | ||||
| 	capabilities := []string{} | ||||
| 	gpu := []string{} | ||||
| 	tpu := []string{} | ||||
| 	for _, service := range p.Services { | ||||
| 		deploy := service.Deploy | ||||
| 		if deploy == nil { | ||||
| 			continue | ||||
| 		} | ||||
| 		reservation := deploy.Resources.Reservations | ||||
| 		if reservation == nil { | ||||
| 			continue | ||||
| 		} | ||||
| 		devices := reservation.Devices | ||||
| 		for _, d := range devices { | ||||
| 			if len(d.Capabilities) > 0 { | ||||
| 				capabilities = append(capabilities, service.Name) | ||||
| 			} | ||||
| 			for _, c := range d.Capabilities { | ||||
| 				if c == "gpu" { | ||||
| 					gpu = append(gpu, service.Name) | ||||
| 				} else if c == "tpu" { | ||||
| 					tpu = append(tpu, service.Name) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return utils.RemoveDuplicates(capabilities), utils.RemoveDuplicates(gpu), utils.RemoveDuplicates(tpu) | ||||
| } | ||||
|  | ||||
| // GetServices retrieve services by names, or return all services if no name specified | ||||
| func (p *Project) GetServices(names ...string) (Services, error) { | ||||
| 	if len(names) == 0 { | ||||
|   | ||||
							
								
								
									
										10
									
								
								vendor/github.com/compose-spec/compose-go/v2/types/services.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								vendor/github.com/compose-spec/compose-go/v2/types/services.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -33,3 +33,13 @@ func (s Services) GetProfiles() []string { | ||||
| 	} | ||||
| 	return profiles | ||||
| } | ||||
|  | ||||
| func (s Services) Filter(predicate func(ServiceConfig) bool) Services { | ||||
| 	services := Services{} | ||||
| 	for name, service := range s { | ||||
| 		if predicate(service) { | ||||
| 			services[name] = service | ||||
| 		} | ||||
| 	} | ||||
| 	return services | ||||
| } | ||||
|   | ||||
							
								
								
									
										3
									
								
								vendor/github.com/compose-spec/compose-go/v2/types/types.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								vendor/github.com/compose-spec/compose-go/v2/types/types.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -162,6 +162,9 @@ func (s *ServiceConfig) NetworksByPriority() []string { | ||||
| 		}) | ||||
| 	} | ||||
| 	sort.Slice(keys, func(i, j int) bool { | ||||
| 		if keys[i].priority == keys[j].priority { | ||||
| 			return keys[i].name < keys[j].name | ||||
| 		} | ||||
| 		return keys[i].priority > keys[j].priority | ||||
| 	}) | ||||
| 	var sorted []string | ||||
|   | ||||
							
								
								
									
										15
									
								
								vendor/github.com/compose-spec/compose-go/v2/utils/collectionutils.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										15
									
								
								vendor/github.com/compose-spec/compose-go/v2/utils/collectionutils.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -51,3 +51,18 @@ func ArrayContains[T comparable](source []T, toCheck []T) bool { | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func RemoveDuplicates[T comparable](slice []T) []T { | ||||
| 	// Create a map to store unique elements | ||||
| 	seen := make(map[T]bool) | ||||
| 	result := []T{} | ||||
|  | ||||
| 	// Loop through the slice, adding elements to the map if they haven't been seen before | ||||
| 	for _, val := range slice { | ||||
| 		if _, ok := seen[val]; !ok { | ||||
| 			seen[val] = true | ||||
| 			result = append(result, val) | ||||
| 		} | ||||
| 	} | ||||
| 	return result | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 CrazyMax
					CrazyMax