mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-11-01 00:23:56 +08:00 
			
		
		
		
	Use compose-spec parser
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
This commit is contained in:
		
							
								
								
									
										191
									
								
								vendor/github.com/compose-spec/compose-go/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										191
									
								
								vendor/github.com/compose-spec/compose-go/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,191 @@ | ||||
|  | ||||
|                                  Apache License | ||||
|                            Version 2.0, January 2004 | ||||
|                         https://www.apache.org/licenses/ | ||||
|  | ||||
|    TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | ||||
|  | ||||
|    1. Definitions. | ||||
|  | ||||
|       "License" shall mean the terms and conditions for use, reproduction, | ||||
|       and distribution as defined by Sections 1 through 9 of this document. | ||||
|  | ||||
|       "Licensor" shall mean the copyright owner or entity authorized by | ||||
|       the copyright owner that is granting the License. | ||||
|  | ||||
|       "Legal Entity" shall mean the union of the acting entity and all | ||||
|       other entities that control, are controlled by, or are under common | ||||
|       control with that entity. For the purposes of this definition, | ||||
|       "control" means (i) the power, direct or indirect, to cause the | ||||
|       direction or management of such entity, whether by contract or | ||||
|       otherwise, or (ii) ownership of fifty percent (50%) or more of the | ||||
|       outstanding shares, or (iii) beneficial ownership of such entity. | ||||
|  | ||||
|       "You" (or "Your") shall mean an individual or Legal Entity | ||||
|       exercising permissions granted by this License. | ||||
|  | ||||
|       "Source" form shall mean the preferred form for making modifications, | ||||
|       including but not limited to software source code, documentation | ||||
|       source, and configuration files. | ||||
|  | ||||
|       "Object" form shall mean any form resulting from mechanical | ||||
|       transformation or translation of a Source form, including but | ||||
|       not limited to compiled object code, generated documentation, | ||||
|       and conversions to other media types. | ||||
|  | ||||
|       "Work" shall mean the work of authorship, whether in Source or | ||||
|       Object form, made available under the License, as indicated by a | ||||
|       copyright notice that is included in or attached to the work | ||||
|       (an example is provided in the Appendix below). | ||||
|  | ||||
|       "Derivative Works" shall mean any work, whether in Source or Object | ||||
|       form, that is based on (or derived from) the Work and for which the | ||||
|       editorial revisions, annotations, elaborations, or other modifications | ||||
|       represent, as a whole, an original work of authorship. For the purposes | ||||
|       of this License, Derivative Works shall not include works that remain | ||||
|       separable from, or merely link (or bind by name) to the interfaces of, | ||||
|       the Work and Derivative Works thereof. | ||||
|  | ||||
|       "Contribution" shall mean any work of authorship, including | ||||
|       the original version of the Work and any modifications or additions | ||||
|       to that Work or Derivative Works thereof, that is intentionally | ||||
|       submitted to Licensor for inclusion in the Work by the copyright owner | ||||
|       or by an individual or Legal Entity authorized to submit on behalf of | ||||
|       the copyright owner. For the purposes of this definition, "submitted" | ||||
|       means any form of electronic, verbal, or written communication sent | ||||
|       to the Licensor or its representatives, including but not limited to | ||||
|       communication on electronic mailing lists, source code control systems, | ||||
|       and issue tracking systems that are managed by, or on behalf of, the | ||||
|       Licensor for the purpose of discussing and improving the Work, but | ||||
|       excluding communication that is conspicuously marked or otherwise | ||||
|       designated in writing by the copyright owner as "Not a Contribution." | ||||
|  | ||||
|       "Contributor" shall mean Licensor and any individual or Legal Entity | ||||
|       on behalf of whom a Contribution has been received by Licensor and | ||||
|       subsequently incorporated within the Work. | ||||
|  | ||||
|    2. Grant of Copyright License. Subject to the terms and conditions of | ||||
|       this License, each Contributor hereby grants to You a perpetual, | ||||
|       worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||
|       copyright license to reproduce, prepare Derivative Works of, | ||||
|       publicly display, publicly perform, sublicense, and distribute the | ||||
|       Work and such Derivative Works in Source or Object form. | ||||
|  | ||||
|    3. Grant of Patent License. Subject to the terms and conditions of | ||||
|       this License, each Contributor hereby grants to You a perpetual, | ||||
|       worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||
|       (except as stated in this section) patent license to make, have made, | ||||
|       use, offer to sell, sell, import, and otherwise transfer the Work, | ||||
|       where such license applies only to those patent claims licensable | ||||
|       by such Contributor that are necessarily infringed by their | ||||
|       Contribution(s) alone or by combination of their Contribution(s) | ||||
|       with the Work to which such Contribution(s) was submitted. If You | ||||
|       institute patent litigation against any entity (including a | ||||
|       cross-claim or counterclaim in a lawsuit) alleging that the Work | ||||
|       or a Contribution incorporated within the Work constitutes direct | ||||
|       or contributory patent infringement, then any patent licenses | ||||
|       granted to You under this License for that Work shall terminate | ||||
|       as of the date such litigation is filed. | ||||
|  | ||||
|    4. Redistribution. You may reproduce and distribute copies of the | ||||
|       Work or Derivative Works thereof in any medium, with or without | ||||
|       modifications, and in Source or Object form, provided that You | ||||
|       meet the following conditions: | ||||
|  | ||||
|       (a) You must give any other recipients of the Work or | ||||
|           Derivative Works a copy of this License; and | ||||
|  | ||||
|       (b) You must cause any modified files to carry prominent notices | ||||
|           stating that You changed the files; and | ||||
|  | ||||
|       (c) You must retain, in the Source form of any Derivative Works | ||||
|           that You distribute, all copyright, patent, trademark, and | ||||
|           attribution notices from the Source form of the Work, | ||||
|           excluding those notices that do not pertain to any part of | ||||
|           the Derivative Works; and | ||||
|  | ||||
|       (d) If the Work includes a "NOTICE" text file as part of its | ||||
|           distribution, then any Derivative Works that You distribute must | ||||
|           include a readable copy of the attribution notices contained | ||||
|           within such NOTICE file, excluding those notices that do not | ||||
|           pertain to any part of the Derivative Works, in at least one | ||||
|           of the following places: within a NOTICE text file distributed | ||||
|           as part of the Derivative Works; within the Source form or | ||||
|           documentation, if provided along with the Derivative Works; or, | ||||
|           within a display generated by the Derivative Works, if and | ||||
|           wherever such third-party notices normally appear. The contents | ||||
|           of the NOTICE file are for informational purposes only and | ||||
|           do not modify the License. You may add Your own attribution | ||||
|           notices within Derivative Works that You distribute, alongside | ||||
|           or as an addendum to the NOTICE text from the Work, provided | ||||
|           that such additional attribution notices cannot be construed | ||||
|           as modifying the License. | ||||
|  | ||||
|       You may add Your own copyright statement to Your modifications and | ||||
|       may provide additional or different license terms and conditions | ||||
|       for use, reproduction, or distribution of Your modifications, or | ||||
|       for any such Derivative Works as a whole, provided Your use, | ||||
|       reproduction, and distribution of the Work otherwise complies with | ||||
|       the conditions stated in this License. | ||||
|  | ||||
|    5. Submission of Contributions. Unless You explicitly state otherwise, | ||||
|       any Contribution intentionally submitted for inclusion in the Work | ||||
|       by You to the Licensor shall be under the terms and conditions of | ||||
|       this License, without any additional terms or conditions. | ||||
|       Notwithstanding the above, nothing herein shall supersede or modify | ||||
|       the terms of any separate license agreement you may have executed | ||||
|       with Licensor regarding such Contributions. | ||||
|  | ||||
|    6. Trademarks. This License does not grant permission to use the trade | ||||
|       names, trademarks, service marks, or product names of the Licensor, | ||||
|       except as required for reasonable and customary use in describing the | ||||
|       origin of the Work and reproducing the content of the NOTICE file. | ||||
|  | ||||
|    7. Disclaimer of Warranty. Unless required by applicable law or | ||||
|       agreed to in writing, Licensor provides the Work (and each | ||||
|       Contributor provides its Contributions) on an "AS IS" BASIS, | ||||
|       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||||
|       implied, including, without limitation, any warranties or conditions | ||||
|       of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | ||||
|       PARTICULAR PURPOSE. You are solely responsible for determining the | ||||
|       appropriateness of using or redistributing the Work and assume any | ||||
|       risks associated with Your exercise of permissions under this License. | ||||
|  | ||||
|    8. Limitation of Liability. In no event and under no legal theory, | ||||
|       whether in tort (including negligence), contract, or otherwise, | ||||
|       unless required by applicable law (such as deliberate and grossly | ||||
|       negligent acts) or agreed to in writing, shall any Contributor be | ||||
|       liable to You for damages, including any direct, indirect, special, | ||||
|       incidental, or consequential damages of any character arising as a | ||||
|       result of this License or out of the use or inability to use the | ||||
|       Work (including but not limited to damages for loss of goodwill, | ||||
|       work stoppage, computer failure or malfunction, or any and all | ||||
|       other commercial damages or losses), even if such Contributor | ||||
|       has been advised of the possibility of such damages. | ||||
|  | ||||
|    9. Accepting Warranty or Additional Liability. While redistributing | ||||
|       the Work or Derivative Works thereof, You may choose to offer, | ||||
|       and charge a fee for, acceptance of support, warranty, indemnity, | ||||
|       or other liability obligations and/or rights consistent with this | ||||
|       License. However, in accepting such obligations, You may act only | ||||
|       on Your own behalf and on Your sole responsibility, not on behalf | ||||
|       of any other Contributor, and only if You agree to indemnify, | ||||
|       defend, and hold each Contributor harmless for any liability | ||||
|       incurred by, or claims asserted against, such Contributor by reason | ||||
|       of your accepting any such warranty or additional liability. | ||||
|  | ||||
|    END OF TERMS AND CONDITIONS | ||||
|  | ||||
|    Copyright 2013-2017 Docker, Inc. | ||||
|  | ||||
|    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 | ||||
|  | ||||
|        https://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. | ||||
							
								
								
									
										2
									
								
								vendor/github.com/compose-spec/compose-go/NOTICE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/compose-spec/compose-go/NOTICE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| The Compose Specification | ||||
| Copyright 2020 The Compose Specification Authors | ||||
							
								
								
									
										53
									
								
								vendor/github.com/compose-spec/compose-go/errdefs/errors.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								vendor/github.com/compose-spec/compose-go/errdefs/errors.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | ||||
| /* | ||||
|    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 errdefs | ||||
|  | ||||
| import "errors" | ||||
|  | ||||
| var ( | ||||
| 	// ErrNotFound is returned when an object is not found | ||||
| 	ErrNotFound = errors.New("not found") | ||||
|  | ||||
| 	// ErrInvalid is returned when a compose project is invalid | ||||
| 	ErrInvalid = errors.New("invalid compose project") | ||||
|  | ||||
| 	// ErrUnsupported is returned when a compose project uses an unsupported attribute | ||||
| 	ErrUnsupported = errors.New("unsupported attribute") | ||||
|  | ||||
| 	// ErrIncompatible is returned when a compose project uses an incompatible attribute | ||||
| 	ErrIncompatible = errors.New("incompatible attribute") | ||||
| ) | ||||
|  | ||||
| // IsNotFoundError returns true if the unwrapped error is ErrNotFound | ||||
| func IsNotFoundError(err error) bool { | ||||
| 	return errors.Is(err, ErrNotFound) | ||||
| } | ||||
|  | ||||
| // IsInvalidError returns true if the unwrapped error is ErrInvalid | ||||
| func IsInvalidError(err error) bool { | ||||
| 	return errors.Is(err, ErrInvalid) | ||||
| } | ||||
|  | ||||
| // IsUnsupportedError returns true if the unwrapped error is ErrUnsupported | ||||
| func IsUnsupportedError(err error) bool { | ||||
| 	return errors.Is(err, ErrUnsupported) | ||||
| } | ||||
|  | ||||
| // IsUnsupportedError returns true if the unwrapped error is ErrIncompatible | ||||
| func IsIncompatibleError(err error) bool { | ||||
| 	return errors.Is(err, ErrIncompatible) | ||||
| } | ||||
							
								
								
									
										177
									
								
								vendor/github.com/compose-spec/compose-go/interpolation/interpolation.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								vendor/github.com/compose-spec/compose-go/interpolation/interpolation.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,177 @@ | ||||
| /* | ||||
|    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 interpolation | ||||
|  | ||||
| import ( | ||||
| 	"os" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/compose-spec/compose-go/template" | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
|  | ||||
| // Options supported by Interpolate | ||||
| type Options struct { | ||||
| 	// LookupValue from a key | ||||
| 	LookupValue LookupValue | ||||
| 	// TypeCastMapping maps key paths to functions to cast to a type | ||||
| 	TypeCastMapping map[Path]Cast | ||||
| 	// Substitution function to use | ||||
| 	Substitute func(string, template.Mapping) (string, error) | ||||
| } | ||||
|  | ||||
| // LookupValue is a function which maps from variable names to values. | ||||
| // Returns the value as a string and a bool indicating whether | ||||
| // the value is present, to distinguish between an empty string | ||||
| // and the absence of a value. | ||||
| type LookupValue func(key string) (string, bool) | ||||
|  | ||||
| // Cast a value to a new type, or return an error if the value can't be cast | ||||
| type Cast func(value string) (interface{}, error) | ||||
|  | ||||
| // Interpolate replaces variables in a string with the values from a mapping | ||||
| func Interpolate(config map[string]interface{}, opts Options) (map[string]interface{}, error) { | ||||
| 	if opts.LookupValue == nil { | ||||
| 		opts.LookupValue = os.LookupEnv | ||||
| 	} | ||||
| 	if opts.TypeCastMapping == nil { | ||||
| 		opts.TypeCastMapping = make(map[Path]Cast) | ||||
| 	} | ||||
| 	if opts.Substitute == nil { | ||||
| 		opts.Substitute = template.Substitute | ||||
| 	} | ||||
|  | ||||
| 	out := map[string]interface{}{} | ||||
|  | ||||
| 	for key, value := range config { | ||||
| 		interpolatedValue, err := recursiveInterpolate(value, NewPath(key), opts) | ||||
| 		if err != nil { | ||||
| 			return out, err | ||||
| 		} | ||||
| 		out[key] = interpolatedValue | ||||
| 	} | ||||
|  | ||||
| 	return out, nil | ||||
| } | ||||
|  | ||||
| func recursiveInterpolate(value interface{}, path Path, opts Options) (interface{}, error) { | ||||
| 	switch value := value.(type) { | ||||
| 	case string: | ||||
| 		newValue, err := opts.Substitute(value, template.Mapping(opts.LookupValue)) | ||||
| 		if err != nil || newValue == value { | ||||
| 			return value, newPathError(path, err) | ||||
| 		} | ||||
| 		caster, ok := opts.getCasterForPath(path) | ||||
| 		if !ok { | ||||
| 			return newValue, nil | ||||
| 		} | ||||
| 		casted, err := caster(newValue) | ||||
| 		return casted, newPathError(path, errors.Wrap(err, "failed to cast to expected type")) | ||||
|  | ||||
| 	case map[string]interface{}: | ||||
| 		out := map[string]interface{}{} | ||||
| 		for key, elem := range value { | ||||
| 			interpolatedElem, err := recursiveInterpolate(elem, path.Next(key), opts) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			out[key] = interpolatedElem | ||||
| 		} | ||||
| 		return out, nil | ||||
|  | ||||
| 	case []interface{}: | ||||
| 		out := make([]interface{}, len(value)) | ||||
| 		for i, elem := range value { | ||||
| 			interpolatedElem, err := recursiveInterpolate(elem, path.Next(PathMatchList), opts) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			out[i] = interpolatedElem | ||||
| 		} | ||||
| 		return out, nil | ||||
|  | ||||
| 	default: | ||||
| 		return value, nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func newPathError(path Path, err error) error { | ||||
| 	switch err := err.(type) { | ||||
| 	case nil: | ||||
| 		return nil | ||||
| 	case *template.InvalidTemplateError: | ||||
| 		return errors.Errorf( | ||||
| 			"invalid interpolation format for %s: %#v. You may need to escape any $ with another $.", | ||||
| 			path, err.Template) | ||||
| 	default: | ||||
| 		return errors.Wrapf(err, "error while interpolating %s", path) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| const pathSeparator = "." | ||||
|  | ||||
| // PathMatchAll is a token used as part of a Path to match any key at that level | ||||
| // in the nested structure | ||||
| const PathMatchAll = "*" | ||||
|  | ||||
| // PathMatchList is a token used as part of a Path to match items in a list | ||||
| const PathMatchList = "[]" | ||||
|  | ||||
| // Path is a dotted path of keys to a value in a nested mapping structure. A * | ||||
| // section in a path will match any key in the mapping structure. | ||||
| type Path string | ||||
|  | ||||
| // NewPath returns a new Path | ||||
| func NewPath(items ...string) Path { | ||||
| 	return Path(strings.Join(items, pathSeparator)) | ||||
| } | ||||
|  | ||||
| // Next returns a new path by append part to the current path | ||||
| func (p Path) Next(part string) Path { | ||||
| 	return Path(string(p) + pathSeparator + part) | ||||
| } | ||||
|  | ||||
| func (p Path) parts() []string { | ||||
| 	return strings.Split(string(p), pathSeparator) | ||||
| } | ||||
|  | ||||
| func (p Path) matches(pattern Path) bool { | ||||
| 	patternParts := pattern.parts() | ||||
| 	parts := p.parts() | ||||
|  | ||||
| 	if len(patternParts) != len(parts) { | ||||
| 		return false | ||||
| 	} | ||||
| 	for index, part := range parts { | ||||
| 		switch patternParts[index] { | ||||
| 		case PathMatchAll, part: | ||||
| 			continue | ||||
| 		default: | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (o Options) getCasterForPath(path Path) (Cast, bool) { | ||||
| 	for pattern, caster := range o.TypeCastMapping { | ||||
| 		if path.matches(pattern) { | ||||
| 			return caster, true | ||||
| 		} | ||||
| 	} | ||||
| 	return nil, false | ||||
| } | ||||
							
								
								
									
										8
									
								
								vendor/github.com/compose-spec/compose-go/loader/example1.env
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								vendor/github.com/compose-spec/compose-go/loader/example1.env
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| # passed through | ||||
| FOO=foo_from_env_file | ||||
|  | ||||
| # overridden in example2.env | ||||
| BAR=bar_from_env_file | ||||
|  | ||||
| # overridden in full-example.yml | ||||
| BAZ=baz_from_env_file | ||||
							
								
								
									
										4
									
								
								vendor/github.com/compose-spec/compose-go/loader/example2.env
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/compose-spec/compose-go/loader/example2.env
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| BAR=bar_from_env_file_2 | ||||
|  | ||||
| # overridden in configDetails.Environment | ||||
| QUX=quz_from_env_file_2 | ||||
							
								
								
									
										412
									
								
								vendor/github.com/compose-spec/compose-go/loader/full-example.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										412
									
								
								vendor/github.com/compose-spec/compose-go/loader/full-example.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,412 @@ | ||||
| services: | ||||
|   foo: | ||||
|  | ||||
|     build: | ||||
|       context: ./dir | ||||
|       dockerfile: Dockerfile | ||||
|       args: | ||||
|         foo: bar | ||||
|       target: foo | ||||
|       network: foo | ||||
|       cache_from: | ||||
|         - foo | ||||
|         - bar | ||||
|       labels: [FOO=BAR] | ||||
|  | ||||
|  | ||||
|     cap_add: | ||||
|       - ALL | ||||
|  | ||||
|     cap_drop: | ||||
|       - NET_ADMIN | ||||
|       - SYS_ADMIN | ||||
|  | ||||
|     cgroup_parent: m-executor-abcd | ||||
|  | ||||
|     # String or list | ||||
|     command: bundle exec thin -p 3000 | ||||
|     # command: ["bundle", "exec", "thin", "-p", "3000"] | ||||
|  | ||||
|     configs: | ||||
|       - config1 | ||||
|       - source: config2 | ||||
|         target: /my_config | ||||
|         uid: '103' | ||||
|         gid: '103' | ||||
|         mode: 0440 | ||||
|  | ||||
|     container_name: my-web-container | ||||
|  | ||||
|     depends_on: | ||||
|       - db | ||||
|       - redis | ||||
|  | ||||
|     deploy: | ||||
|       mode: replicated | ||||
|       replicas: 6 | ||||
|       labels: [FOO=BAR] | ||||
|       rollback_config: | ||||
|         parallelism: 3 | ||||
|         delay: 10s | ||||
|         failure_action: continue | ||||
|         monitor: 60s | ||||
|         max_failure_ratio: 0.3 | ||||
|         order: start-first | ||||
|       update_config: | ||||
|         parallelism: 3 | ||||
|         delay: 10s | ||||
|         failure_action: continue | ||||
|         monitor: 60s | ||||
|         max_failure_ratio: 0.3 | ||||
|         order: start-first | ||||
|       resources: | ||||
|         limits: | ||||
|           cpus: '0.001' | ||||
|           memory: 50M | ||||
|         reservations: | ||||
|           cpus: '0.0001' | ||||
|           memory: 20M | ||||
|           generic_resources: | ||||
|             - discrete_resource_spec: | ||||
|                 kind: 'gpu' | ||||
|                 value: 2 | ||||
|             - discrete_resource_spec: | ||||
|                 kind: 'ssd' | ||||
|                 value: 1 | ||||
|       restart_policy: | ||||
|         condition: on-failure | ||||
|         delay: 5s | ||||
|         max_attempts: 3 | ||||
|         window: 120s | ||||
|       placement: | ||||
|         constraints: [node=foo] | ||||
|         max_replicas_per_node: 5 | ||||
|         preferences: | ||||
|           - spread: node.labels.az | ||||
|       endpoint_mode: dnsrr | ||||
|  | ||||
|     devices: | ||||
|       - "/dev/ttyUSB0:/dev/ttyUSB0" | ||||
|  | ||||
|     # String or list | ||||
|     # dns: 8.8.8.8 | ||||
|     dns: | ||||
|       - 8.8.8.8 | ||||
|       - 9.9.9.9 | ||||
|  | ||||
|     # String or list | ||||
|     # dns_search: example.com | ||||
|     dns_search: | ||||
|       - dc1.example.com | ||||
|       - dc2.example.com | ||||
|  | ||||
|     domainname: foo.com | ||||
|  | ||||
|     # String or list | ||||
|     # entrypoint: /code/entrypoint.sh -p 3000 | ||||
|     entrypoint: ["/code/entrypoint.sh", "-p", "3000"] | ||||
|  | ||||
|     # String or list | ||||
|     # env_file: .env | ||||
|     env_file: | ||||
|       - ./example1.env | ||||
|       - ./example2.env | ||||
|  | ||||
|     # Mapping or list | ||||
|     # Mapping values can be strings, numbers or null | ||||
|     # Booleans are not allowed - must be quoted | ||||
|     environment: | ||||
|       BAZ: baz_from_service_def | ||||
|       QUX: | ||||
|     # environment: | ||||
|     #   - RACK_ENV=development | ||||
|     #   - SHOW=true | ||||
|     #   - SESSION_SECRET | ||||
|  | ||||
|     # Items can be strings or numbers | ||||
|     expose: | ||||
|      - "3000" | ||||
|      - 8000 | ||||
|  | ||||
|     external_links: | ||||
|       - redis_1 | ||||
|       - project_db_1:mysql | ||||
|       - project_db_1:postgresql | ||||
|  | ||||
|     # Mapping or list | ||||
|     # Mapping values must be strings | ||||
|     # extra_hosts: | ||||
|     #   somehost: "162.242.195.82" | ||||
|     #   otherhost: "50.31.209.229" | ||||
|     extra_hosts: | ||||
|       - "somehost:162.242.195.82" | ||||
|       - "otherhost:50.31.209.229" | ||||
|  | ||||
|     hostname: foo | ||||
|  | ||||
|     healthcheck: | ||||
|       test: echo "hello world" | ||||
|       interval: 10s | ||||
|       timeout: 1s | ||||
|       retries: 5 | ||||
|       start_period: 15s | ||||
|  | ||||
|     # Any valid image reference - repo, tag, id, sha | ||||
|     image: redis | ||||
|     # image: ubuntu:14.04 | ||||
|     # image: tutum/influxdb | ||||
|     # image: example-registry.com:4000/postgresql | ||||
|     # image: a4bc65fd | ||||
|     # image: busybox@sha256:38a203e1986cf79639cfb9b2e1d6e773de84002feea2d4eb006b52004ee8502d | ||||
|  | ||||
|     ipc: host | ||||
|  | ||||
|     # Mapping or list | ||||
|     # Mapping values can be strings, numbers or null | ||||
|     labels: | ||||
|       com.example.description: "Accounting webapp" | ||||
|       com.example.number: 42 | ||||
|       com.example.empty-label: | ||||
|     # labels: | ||||
|     #   - "com.example.description=Accounting webapp" | ||||
|     #   - "com.example.number=42" | ||||
|     #   - "com.example.empty-label" | ||||
|  | ||||
|     links: | ||||
|      - db | ||||
|      - db:database | ||||
|      - redis | ||||
|  | ||||
|     logging: | ||||
|       driver: syslog | ||||
|       options: | ||||
|         syslog-address: "tcp://192.168.0.42:123" | ||||
|  | ||||
|     mac_address: 02:42:ac:11:65:43 | ||||
|  | ||||
|     # network_mode: "bridge" | ||||
|     # network_mode: "host" | ||||
|     # network_mode: "none" | ||||
|     # Use the network mode of an arbitrary container from another service | ||||
|     # network_mode: "service:db" | ||||
|     # Use the network mode of another container, specified by name or id | ||||
|     # network_mode: "container:some-container" | ||||
|     network_mode: "container:0cfeab0f748b9a743dc3da582046357c6ef497631c1a016d28d2bf9b4f899f7b" | ||||
|  | ||||
|     networks: | ||||
|       some-network: | ||||
|         aliases: | ||||
|          - alias1 | ||||
|          - alias3 | ||||
|       other-network: | ||||
|         ipv4_address: 172.16.238.10 | ||||
|         ipv6_address: 2001:3984:3989::10 | ||||
|       other-other-network: | ||||
|  | ||||
|     pid: "host" | ||||
|  | ||||
|     ports: | ||||
|       - 3000 | ||||
|       - "3001-3005" | ||||
|       - "8000:8000" | ||||
|       - "9090-9091:8080-8081" | ||||
|       - "49100:22" | ||||
|       - "127.0.0.1:8001:8001" | ||||
|       - "127.0.0.1:5000-5010:5000-5010" | ||||
|  | ||||
|     privileged: true | ||||
|  | ||||
|     read_only: true | ||||
|  | ||||
|     restart: always | ||||
|  | ||||
|     secrets: | ||||
|       - secret1 | ||||
|       - source: secret2 | ||||
|         target: my_secret | ||||
|         uid: '103' | ||||
|         gid: '103' | ||||
|         mode: 0440 | ||||
|  | ||||
|     security_opt: | ||||
|       - label=level:s0:c100,c200 | ||||
|       - label=type:svirt_apache_t | ||||
|  | ||||
|     stdin_open: true | ||||
|  | ||||
|     stop_grace_period: 20s | ||||
|  | ||||
|     stop_signal: SIGUSR1 | ||||
|  | ||||
|     sysctls: | ||||
|       net.core.somaxconn: 1024 | ||||
|       net.ipv4.tcp_syncookies: 0 | ||||
|  | ||||
|     # String or list | ||||
|     # tmpfs: /run | ||||
|     tmpfs: | ||||
|       - /run | ||||
|       - /tmp | ||||
|  | ||||
|     tty: true | ||||
|  | ||||
|     ulimits: | ||||
|       # Single number or mapping with soft + hard limits | ||||
|       nproc: 65535 | ||||
|       nofile: | ||||
|         soft: 20000 | ||||
|         hard: 40000 | ||||
|  | ||||
|     user: someone | ||||
|  | ||||
|     volumes: | ||||
|       # Just specify a path and let the Engine create a volume | ||||
|       - /var/lib/mysql | ||||
|       # Specify an absolute path mapping | ||||
|       - /opt/data:/var/lib/mysql | ||||
|       # Path on the host, relative to the Compose file | ||||
|       - .:/code | ||||
|       - ./static:/var/www/html | ||||
|       # User-relative path | ||||
|       - ~/configs:/etc/configs/:ro | ||||
|       # Named volume | ||||
|       - datavolume:/var/lib/mysql | ||||
|       - type: bind | ||||
|         source: ./opt | ||||
|         target: /opt | ||||
|         consistency: cached | ||||
|       - type: tmpfs | ||||
|         target: /opt | ||||
|         tmpfs: | ||||
|           size: 10000 | ||||
|  | ||||
|     working_dir: /code | ||||
|     x-bar: baz | ||||
|     x-foo: bar | ||||
|  | ||||
| networks: | ||||
|   # Entries can be null, which specifies simply that a network | ||||
|   # called "{project name}_some-network" should be created and | ||||
|   # use the default driver | ||||
|   some-network: | ||||
|  | ||||
|   other-network: | ||||
|     driver: overlay | ||||
|  | ||||
|     driver_opts: | ||||
|       # Values can be strings or numbers | ||||
|       foo: "bar" | ||||
|       baz: 1 | ||||
|  | ||||
|     ipam: | ||||
|       driver: overlay | ||||
|       # driver_opts: | ||||
|       #   # Values can be strings or numbers | ||||
|       #   com.docker.network.enable_ipv6: "true" | ||||
|       #   com.docker.network.numeric_value: 1 | ||||
|       config: | ||||
|       - subnet: 172.28.0.0/16 | ||||
|         ip_range: 172.28.5.0/24 | ||||
|         gateway: 172.28.5.254 | ||||
|         aux_addresses: | ||||
|           host1: 172.28.1.5 | ||||
|           host2: 172.28.1.6 | ||||
|           host3: 172.28.1.7 | ||||
|       - subnet: 2001:3984:3989::/64 | ||||
|         gateway: 2001:3984:3989::1 | ||||
|  | ||||
|     labels: | ||||
|       foo: bar | ||||
|  | ||||
|   external-network: | ||||
|     # Specifies that a pre-existing network called "external-network" | ||||
|     # can be referred to within this file as "external-network" | ||||
|     external: true | ||||
|  | ||||
|   other-external-network: | ||||
|     # Specifies that a pre-existing network called "my-cool-network" | ||||
|     # can be referred to within this file as "other-external-network" | ||||
|     external: | ||||
|       name: my-cool-network | ||||
|     x-bar: baz | ||||
|     x-foo: bar | ||||
|  | ||||
| volumes: | ||||
|   # Entries can be null, which specifies simply that a volume | ||||
|   # called "{project name}_some-volume" should be created and | ||||
|   # use the default driver | ||||
|   some-volume: | ||||
|  | ||||
|   other-volume: | ||||
|     driver: flocker | ||||
|  | ||||
|     driver_opts: | ||||
|       # Values can be strings or numbers | ||||
|       foo: "bar" | ||||
|       baz: 1 | ||||
|     labels: | ||||
|       foo: bar | ||||
|  | ||||
|   another-volume: | ||||
|     name: "user_specified_name" | ||||
|     driver: vsphere | ||||
|  | ||||
|     driver_opts: | ||||
|       # Values can be strings or numbers | ||||
|       foo: "bar" | ||||
|       baz: 1 | ||||
|  | ||||
|   external-volume: | ||||
|     # Specifies that a pre-existing volume called "external-volume" | ||||
|     # can be referred to within this file as "external-volume" | ||||
|     external: true | ||||
|  | ||||
|   other-external-volume: | ||||
|     # Specifies that a pre-existing volume called "my-cool-volume" | ||||
|     # can be referred to within this file as "other-external-volume" | ||||
|     # This example uses the deprecated "volume.external.name" (replaced by "volume.name") | ||||
|     external: | ||||
|       name: my-cool-volume | ||||
|  | ||||
|   external-volume3: | ||||
|     # Specifies that a pre-existing volume called "this-is-volume3" | ||||
|     # can be referred to within this file as "external-volume3" | ||||
|     name: this-is-volume3 | ||||
|     external: true | ||||
|     x-bar: baz | ||||
|     x-foo: bar | ||||
|  | ||||
| configs: | ||||
|   config1: | ||||
|     file: ./config_data | ||||
|     labels: | ||||
|       foo: bar | ||||
|   config2: | ||||
|     external: | ||||
|       name: my_config | ||||
|   config3: | ||||
|     external: true | ||||
|   config4: | ||||
|     name: foo | ||||
|     x-bar: baz | ||||
|     x-foo: bar | ||||
|  | ||||
| secrets: | ||||
|   secret1: | ||||
|     file: ./secret_data | ||||
|     labels: | ||||
|       foo: bar | ||||
|   secret2: | ||||
|     external: | ||||
|       name: my_secret | ||||
|   secret3: | ||||
|     external: true | ||||
|   secret4: | ||||
|     name: bar | ||||
|     x-bar: baz | ||||
|     x-foo: bar | ||||
| x-bar: baz | ||||
| x-foo: bar | ||||
| x-nested: | ||||
|   bar: baz | ||||
|   foo: bar | ||||
							
								
								
									
										84
									
								
								vendor/github.com/compose-spec/compose-go/loader/interpolate.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								vendor/github.com/compose-spec/compose-go/loader/interpolate.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,84 @@ | ||||
| /* | ||||
|    Copyright 2020 The Compose Specification Authors. | ||||
|  | ||||
|    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|    you may not use this file except in compliance with the License. | ||||
|    You may obtain a copy of the License at | ||||
|  | ||||
|        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
|    Unless required by applicable law or agreed to in writing, software | ||||
|    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|    See the License for the specific language governing permissions and | ||||
|    limitations under the License. | ||||
| */ | ||||
|  | ||||
| package loader | ||||
|  | ||||
| import ( | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
|  | ||||
| 	interp "github.com/compose-spec/compose-go/interpolation" | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
|  | ||||
| var interpolateTypeCastMapping = map[interp.Path]interp.Cast{ | ||||
| 	servicePath("configs", interp.PathMatchList, "mode"):             toInt, | ||||
| 	servicePath("secrets", interp.PathMatchList, "mode"):             toInt, | ||||
| 	servicePath("healthcheck", "retries"):                            toInt, | ||||
| 	servicePath("healthcheck", "disable"):                            toBoolean, | ||||
| 	servicePath("deploy", "replicas"):                                toInt, | ||||
| 	servicePath("deploy", "update_config", "parallelism"):            toInt, | ||||
| 	servicePath("deploy", "update_config", "max_failure_ratio"):      toFloat, | ||||
| 	servicePath("deploy", "rollback_config", "parallelism"):          toInt, | ||||
| 	servicePath("deploy", "rollback_config", "max_failure_ratio"):    toFloat, | ||||
| 	servicePath("deploy", "restart_policy", "max_attempts"):          toInt, | ||||
| 	servicePath("deploy", "placement", "max_replicas_per_node"):      toInt, | ||||
| 	servicePath("ports", interp.PathMatchList, "target"):             toInt, | ||||
| 	servicePath("ports", interp.PathMatchList, "published"):          toInt, | ||||
| 	servicePath("ulimits", interp.PathMatchAll):                      toInt, | ||||
| 	servicePath("ulimits", interp.PathMatchAll, "hard"):              toInt, | ||||
| 	servicePath("ulimits", interp.PathMatchAll, "soft"):              toInt, | ||||
| 	servicePath("privileged"):                                        toBoolean, | ||||
| 	servicePath("read_only"):                                         toBoolean, | ||||
| 	servicePath("stdin_open"):                                        toBoolean, | ||||
| 	servicePath("tty"):                                               toBoolean, | ||||
| 	servicePath("volumes", interp.PathMatchList, "read_only"):        toBoolean, | ||||
| 	servicePath("volumes", interp.PathMatchList, "volume", "nocopy"): toBoolean, | ||||
| 	iPath("networks", interp.PathMatchAll, "external"):               toBoolean, | ||||
| 	iPath("networks", interp.PathMatchAll, "internal"):               toBoolean, | ||||
| 	iPath("networks", interp.PathMatchAll, "attachable"):             toBoolean, | ||||
| 	iPath("volumes", interp.PathMatchAll, "external"):                toBoolean, | ||||
| 	iPath("secrets", interp.PathMatchAll, "external"):                toBoolean, | ||||
| 	iPath("configs", interp.PathMatchAll, "external"):                toBoolean, | ||||
| } | ||||
|  | ||||
| func iPath(parts ...string) interp.Path { | ||||
| 	return interp.NewPath(parts...) | ||||
| } | ||||
|  | ||||
| func servicePath(parts ...string) interp.Path { | ||||
| 	return iPath(append([]string{"services", interp.PathMatchAll}, parts...)...) | ||||
| } | ||||
|  | ||||
| func toInt(value string) (interface{}, error) { | ||||
| 	return strconv.Atoi(value) | ||||
| } | ||||
|  | ||||
| func toFloat(value string) (interface{}, error) { | ||||
| 	return strconv.ParseFloat(value, 64) | ||||
| } | ||||
|  | ||||
| // should match http://yaml.org/type/bool.html | ||||
| func toBoolean(value string) (interface{}, error) { | ||||
| 	switch strings.ToLower(value) { | ||||
| 	case "y", "yes", "true", "on": | ||||
| 		return true, nil | ||||
| 	case "n", "no", "false", "off": | ||||
| 		return false, nil | ||||
| 	default: | ||||
| 		return nil, errors.Errorf("invalid boolean: %s", value) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										1050
									
								
								vendor/github.com/compose-spec/compose-go/loader/loader.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1050
									
								
								vendor/github.com/compose-spec/compose-go/loader/loader.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										291
									
								
								vendor/github.com/compose-spec/compose-go/loader/merge.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										291
									
								
								vendor/github.com/compose-spec/compose-go/loader/merge.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,291 @@ | ||||
| /* | ||||
|    Copyright 2020 The Compose Specification Authors. | ||||
|  | ||||
|    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|    you may not use this file except in compliance with the License. | ||||
|    You may obtain a copy of the License at | ||||
|  | ||||
|        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
|    Unless required by applicable law or agreed to in writing, software | ||||
|    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|    See the License for the specific language governing permissions and | ||||
|    limitations under the License. | ||||
| */ | ||||
|  | ||||
| package loader | ||||
|  | ||||
| import ( | ||||
| 	"reflect" | ||||
| 	"sort" | ||||
|  | ||||
| 	"github.com/compose-spec/compose-go/types" | ||||
| 	"github.com/imdario/mergo" | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
|  | ||||
| type specials struct { | ||||
| 	m map[reflect.Type]func(dst, src reflect.Value) error | ||||
| } | ||||
|  | ||||
| var serviceSpecials = &specials{ | ||||
| 	m: map[reflect.Type]func(dst, src reflect.Value) error{ | ||||
| 		reflect.TypeOf(&types.LoggingConfig{}):           safelyMerge(mergeLoggingConfig), | ||||
| 		reflect.TypeOf(&types.UlimitsConfig{}):           safelyMerge(mergeUlimitsConfig), | ||||
| 		reflect.TypeOf([]types.ServicePortConfig{}):      mergeSlice(toServicePortConfigsMap, toServicePortConfigsSlice), | ||||
| 		reflect.TypeOf([]types.ServiceSecretConfig{}):    mergeSlice(toServiceSecretConfigsMap, toServiceSecretConfigsSlice), | ||||
| 		reflect.TypeOf([]types.ServiceConfigObjConfig{}): mergeSlice(toServiceConfigObjConfigsMap, toSServiceConfigObjConfigsSlice), | ||||
| 		reflect.TypeOf(&types.UlimitsConfig{}):           mergeUlimitsConfig, | ||||
| 		reflect.TypeOf(&types.ServiceNetworkConfig{}):    mergeServiceNetworkConfig, | ||||
| 	}, | ||||
| } | ||||
|  | ||||
| func (s *specials) Transformer(t reflect.Type) func(dst, src reflect.Value) error { | ||||
| 	if fn, ok := s.m[t]; ok { | ||||
| 		return fn | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func merge(configs []*types.Config) (*types.Config, error) { | ||||
| 	base := configs[0] | ||||
| 	for _, override := range configs[1:] { | ||||
| 		var err error | ||||
| 		base.Services, err = mergeServices(base.Services, override.Services) | ||||
| 		if err != nil { | ||||
| 			return base, errors.Wrapf(err, "cannot merge services from %s", override.Filename) | ||||
| 		} | ||||
| 		base.Volumes, err = mergeVolumes(base.Volumes, override.Volumes) | ||||
| 		if err != nil { | ||||
| 			return base, errors.Wrapf(err, "cannot merge volumes from %s", override.Filename) | ||||
| 		} | ||||
| 		base.Networks, err = mergeNetworks(base.Networks, override.Networks) | ||||
| 		if err != nil { | ||||
| 			return base, errors.Wrapf(err, "cannot merge networks from %s", override.Filename) | ||||
| 		} | ||||
| 		base.Secrets, err = mergeSecrets(base.Secrets, override.Secrets) | ||||
| 		if err != nil { | ||||
| 			return base, errors.Wrapf(err, "cannot merge secrets from %s", override.Filename) | ||||
| 		} | ||||
| 		base.Configs, err = mergeConfigs(base.Configs, override.Configs) | ||||
| 		if err != nil { | ||||
| 			return base, errors.Wrapf(err, "cannot merge configs from %s", override.Filename) | ||||
| 		} | ||||
| 		base.Extensions, err = mergeExtensions(base.Extensions, override.Extensions) | ||||
| 		if err != nil { | ||||
| 			return base, errors.Wrapf(err, "cannot merge extensions from %s", override.Filename) | ||||
| 		} | ||||
| 	} | ||||
| 	return base, nil | ||||
| } | ||||
|  | ||||
| func mergeServices(base, override []types.ServiceConfig) ([]types.ServiceConfig, error) { | ||||
| 	baseServices := mapByName(base) | ||||
| 	overrideServices := mapByName(override) | ||||
| 	for name, overrideService := range overrideServices { | ||||
| 		overrideService := overrideService | ||||
| 		if baseService, ok := baseServices[name]; ok { | ||||
| 			if err := mergo.Merge(&baseService, &overrideService, mergo.WithAppendSlice, mergo.WithOverride, mergo.WithTransformers(serviceSpecials)); err != nil { | ||||
| 				return base, errors.Wrapf(err, "cannot merge service %s", name) | ||||
| 			} | ||||
| 			if len(overrideService.Command) > 0 { | ||||
| 				baseService.Command = overrideService.Command | ||||
| 			} | ||||
| 			baseServices[name] = baseService | ||||
| 			continue | ||||
| 		} | ||||
| 		baseServices[name] = overrideService | ||||
| 	} | ||||
| 	services := []types.ServiceConfig{} | ||||
| 	for _, baseService := range baseServices { | ||||
| 		services = append(services, baseService) | ||||
| 	} | ||||
| 	sort.Slice(services, func(i, j int) bool { return services[i].Name < services[j].Name }) | ||||
| 	return services, nil | ||||
| } | ||||
|  | ||||
| func toServiceSecretConfigsMap(s interface{}) (map[interface{}]interface{}, error) { | ||||
| 	secrets, ok := s.([]types.ServiceSecretConfig) | ||||
| 	if !ok { | ||||
| 		return nil, errors.Errorf("not a serviceSecretConfig: %v", s) | ||||
| 	} | ||||
| 	m := map[interface{}]interface{}{} | ||||
| 	for _, secret := range secrets { | ||||
| 		m[secret.Source] = secret | ||||
| 	} | ||||
| 	return m, nil | ||||
| } | ||||
|  | ||||
| func toServiceConfigObjConfigsMap(s interface{}) (map[interface{}]interface{}, error) { | ||||
| 	secrets, ok := s.([]types.ServiceConfigObjConfig) | ||||
| 	if !ok { | ||||
| 		return nil, errors.Errorf("not a serviceSecretConfig: %v", s) | ||||
| 	} | ||||
| 	m := map[interface{}]interface{}{} | ||||
| 	for _, secret := range secrets { | ||||
| 		m[secret.Source] = secret | ||||
| 	} | ||||
| 	return m, nil | ||||
| } | ||||
|  | ||||
| func toServicePortConfigsMap(s interface{}) (map[interface{}]interface{}, error) { | ||||
| 	ports, ok := s.([]types.ServicePortConfig) | ||||
| 	if !ok { | ||||
| 		return nil, errors.Errorf("not a servicePortConfig slice: %v", s) | ||||
| 	} | ||||
| 	m := map[interface{}]interface{}{} | ||||
| 	for _, p := range ports { | ||||
| 		m[p.Published] = p | ||||
| 	} | ||||
| 	return m, nil | ||||
| } | ||||
|  | ||||
| func toServiceSecretConfigsSlice(dst reflect.Value, m map[interface{}]interface{}) error { | ||||
| 	s := []types.ServiceSecretConfig{} | ||||
| 	for _, v := range m { | ||||
| 		s = append(s, v.(types.ServiceSecretConfig)) | ||||
| 	} | ||||
| 	sort.Slice(s, func(i, j int) bool { return s[i].Source < s[j].Source }) | ||||
| 	dst.Set(reflect.ValueOf(s)) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func toSServiceConfigObjConfigsSlice(dst reflect.Value, m map[interface{}]interface{}) error { | ||||
| 	s := []types.ServiceConfigObjConfig{} | ||||
| 	for _, v := range m { | ||||
| 		s = append(s, v.(types.ServiceConfigObjConfig)) | ||||
| 	} | ||||
| 	sort.Slice(s, func(i, j int) bool { return s[i].Source < s[j].Source }) | ||||
| 	dst.Set(reflect.ValueOf(s)) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func toServicePortConfigsSlice(dst reflect.Value, m map[interface{}]interface{}) error { | ||||
| 	s := []types.ServicePortConfig{} | ||||
| 	for _, v := range m { | ||||
| 		s = append(s, v.(types.ServicePortConfig)) | ||||
| 	} | ||||
| 	sort.Slice(s, func(i, j int) bool { return s[i].Published < s[j].Published }) | ||||
| 	dst.Set(reflect.ValueOf(s)) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| type tomapFn func(s interface{}) (map[interface{}]interface{}, error) | ||||
| type writeValueFromMapFn func(reflect.Value, map[interface{}]interface{}) error | ||||
|  | ||||
| func safelyMerge(mergeFn func(dst, src reflect.Value) error) func(dst, src reflect.Value) error { | ||||
| 	return func(dst, src reflect.Value) error { | ||||
| 		if src.IsNil() { | ||||
| 			return nil | ||||
| 		} | ||||
| 		if dst.IsNil() { | ||||
| 			dst.Set(src) | ||||
| 			return nil | ||||
| 		} | ||||
| 		return mergeFn(dst, src) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func mergeSlice(tomap tomapFn, writeValue writeValueFromMapFn) func(dst, src reflect.Value) error { | ||||
| 	return func(dst, src reflect.Value) error { | ||||
| 		dstMap, err := sliceToMap(tomap, dst) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		srcMap, err := sliceToMap(tomap, src) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if err := mergo.Map(&dstMap, srcMap, mergo.WithOverride); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		return writeValue(dst, dstMap) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func sliceToMap(tomap tomapFn, v reflect.Value) (map[interface{}]interface{}, error) { | ||||
| 	// check if valid | ||||
| 	if !v.IsValid() { | ||||
| 		return nil, errors.Errorf("invalid value : %+v", v) | ||||
| 	} | ||||
| 	return tomap(v.Interface()) | ||||
| } | ||||
|  | ||||
| func mergeLoggingConfig(dst, src reflect.Value) error { | ||||
| 	// Same driver, merging options | ||||
| 	if getLoggingDriver(dst.Elem()) == getLoggingDriver(src.Elem()) || | ||||
| 		getLoggingDriver(dst.Elem()) == "" || getLoggingDriver(src.Elem()) == "" { | ||||
| 		if getLoggingDriver(dst.Elem()) == "" { | ||||
| 			dst.Elem().FieldByName("Driver").SetString(getLoggingDriver(src.Elem())) | ||||
| 		} | ||||
| 		dstOptions := dst.Elem().FieldByName("Options").Interface().(map[string]string) | ||||
| 		srcOptions := src.Elem().FieldByName("Options").Interface().(map[string]string) | ||||
| 		return mergo.Merge(&dstOptions, srcOptions, mergo.WithOverride) | ||||
| 	} | ||||
| 	// Different driver, override with src | ||||
| 	dst.Set(src) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| //nolint: unparam | ||||
| func mergeUlimitsConfig(dst, src reflect.Value) error { | ||||
| 	if src.Interface() != reflect.Zero(reflect.TypeOf(src.Interface())).Interface() { | ||||
| 		dst.Elem().Set(src.Elem()) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| //nolint: unparam | ||||
| func mergeServiceNetworkConfig(dst, src reflect.Value) error { | ||||
| 	if src.Interface() != reflect.Zero(reflect.TypeOf(src.Interface())).Interface() { | ||||
| 		dst.Elem().FieldByName("Aliases").Set(src.Elem().FieldByName("Aliases")) | ||||
| 		if ipv4 := src.Elem().FieldByName("Ipv4Address").Interface().(string); ipv4 != "" { | ||||
| 			dst.Elem().FieldByName("Ipv4Address").SetString(ipv4) | ||||
| 		} | ||||
| 		if ipv6 := src.Elem().FieldByName("Ipv6Address").Interface().(string); ipv6 != "" { | ||||
| 			dst.Elem().FieldByName("Ipv6Address").SetString(ipv6) | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func getLoggingDriver(v reflect.Value) string { | ||||
| 	return v.FieldByName("Driver").String() | ||||
| } | ||||
|  | ||||
| func mapByName(services []types.ServiceConfig) map[string]types.ServiceConfig { | ||||
| 	m := map[string]types.ServiceConfig{} | ||||
| 	for _, service := range services { | ||||
| 		m[service.Name] = service | ||||
| 	} | ||||
| 	return m | ||||
| } | ||||
|  | ||||
| func mergeVolumes(base, override map[string]types.VolumeConfig) (map[string]types.VolumeConfig, error) { | ||||
| 	err := mergo.Map(&base, &override, mergo.WithOverride) | ||||
| 	return base, err | ||||
| } | ||||
|  | ||||
| func mergeNetworks(base, override map[string]types.NetworkConfig) (map[string]types.NetworkConfig, error) { | ||||
| 	err := mergo.Map(&base, &override, mergo.WithOverride) | ||||
| 	return base, err | ||||
| } | ||||
|  | ||||
| func mergeSecrets(base, override map[string]types.SecretConfig) (map[string]types.SecretConfig, error) { | ||||
| 	err := mergo.Map(&base, &override, mergo.WithOverride) | ||||
| 	return base, err | ||||
| } | ||||
|  | ||||
| func mergeConfigs(base, override map[string]types.ConfigObjConfig) (map[string]types.ConfigObjConfig, error) { | ||||
| 	err := mergo.Map(&base, &override, mergo.WithOverride) | ||||
| 	return base, err | ||||
| } | ||||
|  | ||||
| func mergeExtensions(base, override map[string]interface{}) (map[string]interface{}, error) { | ||||
| 	if base == nil { | ||||
| 		base = map[string]interface{}{} | ||||
| 	} | ||||
| 	err := mergo.Map(&base, &override, mergo.WithOverride) | ||||
| 	return base, err | ||||
| } | ||||
							
								
								
									
										239
									
								
								vendor/github.com/compose-spec/compose-go/loader/normalize.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										239
									
								
								vendor/github.com/compose-spec/compose-go/loader/normalize.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,239 @@ | ||||
| /* | ||||
|    Copyright 2020 The Compose Specification Authors. | ||||
|  | ||||
|    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|    you may not use this file except in compliance with the License. | ||||
|    You may obtain a copy of the License at | ||||
|  | ||||
|        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
|    Unless required by applicable law or agreed to in writing, software | ||||
|    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|    See the License for the specific language governing permissions and | ||||
|    limitations under the License. | ||||
| */ | ||||
|  | ||||
| package loader | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
|  | ||||
| 	"github.com/compose-spec/compose-go/errdefs" | ||||
| 	"github.com/compose-spec/compose-go/types" | ||||
| 	"github.com/pkg/errors" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| ) | ||||
|  | ||||
| // normalize compose project by moving deprecated attributes to their canonical position and injecting implicit defaults | ||||
| func normalize(project *types.Project) error { | ||||
| 	absWorkingDir, err := filepath.Abs(project.WorkingDir) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	project.WorkingDir = absWorkingDir | ||||
|  | ||||
| 	absComposeFiles, err := absComposeFiles(project.ComposeFiles) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	project.ComposeFiles = absComposeFiles | ||||
|  | ||||
| 	// If not declared explicitly, Compose model involves an implicit "default" network | ||||
| 	if _, ok := project.Networks["default"]; !ok { | ||||
| 		project.Networks["default"] = types.NetworkConfig{} | ||||
| 	} | ||||
|  | ||||
| 	err = relocateExternalName(project) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	for i, s := range project.Services { | ||||
| 		if len(s.Networks) == 0 && s.NetworkMode == "" { | ||||
| 			// Service without explicit network attachment are implicitly exposed on default network | ||||
| 			s.Networks = map[string]*types.ServiceNetworkConfig{"default": nil} | ||||
| 		} | ||||
|  | ||||
| 		if s.PullPolicy == types.PullPolicyIfNotPresent { | ||||
| 			s.PullPolicy = types.PullPolicyMissing | ||||
| 		} | ||||
|  | ||||
| 		fn := func(s string) (string, bool) { | ||||
| 			v, ok := project.Environment[s] | ||||
| 			return v, ok | ||||
| 		} | ||||
|  | ||||
| 		if s.Build != nil { | ||||
| 			if s.Build.Dockerfile == "" { | ||||
| 				s.Build.Dockerfile = "Dockerfile" | ||||
| 			} | ||||
| 			localContext := absPath(project.WorkingDir, s.Build.Context) | ||||
| 			if _, err := os.Stat(localContext); err == nil { | ||||
| 				s.Build.Context = localContext | ||||
| 				s.Build.Dockerfile = absPath(localContext, s.Build.Dockerfile) | ||||
| 			} else { | ||||
| 				// might be a remote http/git context. Unfortunately supported "remote" syntax is highly ambiguous | ||||
| 				// in moby/moby and not defined by compose-spec, so let's assume runtime will check | ||||
| 			} | ||||
| 			s.Build.Args = s.Build.Args.Resolve(fn) | ||||
| 		} | ||||
| 		s.Environment = s.Environment.Resolve(fn) | ||||
|  | ||||
| 		err := relocateLogDriver(s) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		err = relocateLogOpt(s) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		err = relocateDockerfile(s) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		project.Services[i] = s | ||||
| 	} | ||||
|  | ||||
| 	setNameFromKey(project) | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func absComposeFiles(composeFiles []string) ([]string, error) { | ||||
| 	absComposeFiles := make([]string, len(composeFiles)) | ||||
| 	for i, composeFile := range composeFiles { | ||||
| 		absComposefile, err := filepath.Abs(composeFile) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		absComposeFiles[i] = absComposefile | ||||
| 	} | ||||
| 	return absComposeFiles, nil | ||||
| } | ||||
|  | ||||
| // Resources with no explicit name are actually named by their key in map | ||||
| func setNameFromKey(project *types.Project) { | ||||
| 	for i, n := range project.Networks { | ||||
| 		if n.Name == "" { | ||||
| 			n.Name = fmt.Sprintf("%s_%s", project.Name, i) | ||||
| 			project.Networks[i] = n | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	for i, v := range project.Volumes { | ||||
| 		if v.Name == "" { | ||||
| 			v.Name = fmt.Sprintf("%s_%s", project.Name, i) | ||||
| 			project.Volumes[i] = v | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	for i, c := range project.Configs { | ||||
| 		if c.Name == "" { | ||||
| 			c.Name = fmt.Sprintf("%s_%s", project.Name, i) | ||||
| 			project.Configs[i] = c | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	for i, s := range project.Secrets { | ||||
| 		if s.Name == "" { | ||||
| 			s.Name = fmt.Sprintf("%s_%s", project.Name, i) | ||||
| 			project.Secrets[i] = s | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func relocateExternalName(project *types.Project) error { | ||||
| 	for i, n := range project.Networks { | ||||
| 		if n.External.Name != "" { | ||||
| 			if n.Name != "" { | ||||
| 				return errors.Wrap(errdefs.ErrInvalid, "can't use both 'networks.external.name' (deprecated) and 'networks.name'") | ||||
| 			} | ||||
| 			n.Name = n.External.Name | ||||
| 		} | ||||
| 		project.Networks[i] = n | ||||
| 	} | ||||
|  | ||||
| 	for i, v := range project.Volumes { | ||||
| 		if v.External.Name != "" { | ||||
| 			if v.Name != "" { | ||||
| 				return errors.Wrap(errdefs.ErrInvalid, "can't use both 'volumes.external.name' (deprecated) and 'volumes.name'") | ||||
| 			} | ||||
| 			v.Name = v.External.Name | ||||
| 		} | ||||
| 		project.Volumes[i] = v | ||||
| 	} | ||||
|  | ||||
| 	for i, s := range project.Secrets { | ||||
| 		if s.External.Name != "" { | ||||
| 			if s.Name != "" { | ||||
| 				return errors.Wrap(errdefs.ErrInvalid, "can't use both 'secrets.external.name' (deprecated) and 'secrets.name'") | ||||
| 			} | ||||
| 			s.Name = s.External.Name | ||||
| 		} | ||||
| 		project.Secrets[i] = s | ||||
| 	} | ||||
|  | ||||
| 	for i, c := range project.Configs { | ||||
| 		if c.External.Name != "" { | ||||
| 			if c.Name != "" { | ||||
| 				return errors.Wrap(errdefs.ErrInvalid, "can't use both 'configs.external.name' (deprecated) and 'configs.name'") | ||||
| 			} | ||||
| 			c.Name = c.External.Name | ||||
| 		} | ||||
| 		project.Configs[i] = c | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func relocateLogOpt(s types.ServiceConfig) error { | ||||
| 	if len(s.LogOpt) != 0 { | ||||
| 		logrus.Warn("`log_opts` is deprecated. Use the `logging` element") | ||||
| 		if s.Logging == nil { | ||||
| 			s.Logging = &types.LoggingConfig{} | ||||
| 		} | ||||
| 		for k, v := range s.LogOpt { | ||||
| 			if _, ok := s.Logging.Options[k]; !ok { | ||||
| 				s.Logging.Options[k] = v | ||||
| 			} else { | ||||
| 				return errors.Wrap(errdefs.ErrInvalid, "can't use both 'log_opt' (deprecated) and 'logging.options'") | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func relocateLogDriver(s types.ServiceConfig) error { | ||||
| 	if s.LogDriver != "" { | ||||
| 		logrus.Warn("`log_driver` is deprecated. Use the `logging` element") | ||||
| 		if s.Logging == nil { | ||||
| 			s.Logging = &types.LoggingConfig{} | ||||
| 		} | ||||
| 		if s.Logging.Driver == "" { | ||||
| 			s.Logging.Driver = s.LogDriver | ||||
| 		} else { | ||||
| 			return errors.Wrap(errdefs.ErrInvalid, "can't use both 'log_driver' (deprecated) and 'logging.driver'") | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func relocateDockerfile(s types.ServiceConfig) error { | ||||
| 	if s.Dockerfile != "" { | ||||
| 		logrus.Warn("`dockerfile` is deprecated. Use the `build` element") | ||||
| 		if s.Build == nil { | ||||
| 			s.Build = &types.BuildConfig{} | ||||
| 		} | ||||
| 		if s.Dockerfile == "" { | ||||
| 			s.Build.Dockerfile = s.Dockerfile | ||||
| 		} else { | ||||
| 			return errors.Wrap(errdefs.ErrInvalid, "can't use both 'dockerfile' (deprecated) and 'build.dockerfile'") | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										77
									
								
								vendor/github.com/compose-spec/compose-go/loader/validate.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								vendor/github.com/compose-spec/compose-go/loader/validate.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,77 @@ | ||||
| /* | ||||
|    Copyright 2020 The Compose Specification Authors. | ||||
|  | ||||
|    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|    you may not use this file except in compliance with the License. | ||||
|    You may obtain a copy of the License at | ||||
|  | ||||
|        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
|    Unless required by applicable law or agreed to in writing, software | ||||
|    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|    See the License for the specific language governing permissions and | ||||
|    limitations under the License. | ||||
| */ | ||||
|  | ||||
| package loader | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/compose-spec/compose-go/errdefs" | ||||
| 	"github.com/compose-spec/compose-go/types" | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
|  | ||||
| // checkConsistency validate a compose model is consistent | ||||
| func checkConsistency(project *types.Project) error { | ||||
| 	for _, s := range project.Services { | ||||
| 		if s.Build == nil && s.Image == "" { | ||||
| 			return errors.Wrapf(errdefs.ErrInvalid, "service %q has neither an image nor a build context specified", s.Name) | ||||
| 		} | ||||
|  | ||||
| 		for network := range s.Networks { | ||||
| 			if _, ok := project.Networks[network]; !ok { | ||||
| 				return errors.Wrap(errdefs.ErrInvalid, fmt.Sprintf("service %q refers to undefined network %s", s.Name, network)) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if strings.HasPrefix(s.NetworkMode, types.NetworkModeServicePrefix) { | ||||
| 			serviceName := s.NetworkMode[len(types.NetworkModeServicePrefix):] | ||||
| 			if _, err := project.GetServices(serviceName); err != nil { | ||||
| 				return fmt.Errorf("service %q not found for network_mode 'service:%s'", serviceName, serviceName) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if strings.HasPrefix(s.NetworkMode, types.NetworkModeContainerPrefix) { | ||||
| 			containerName := s.NetworkMode[len(types.NetworkModeContainerPrefix):] | ||||
| 			if _, err := project.GetByContainerName(containerName); err != nil { | ||||
| 				return fmt.Errorf("service with container_name %q not found for network_mode 'container:%s'", containerName, containerName) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		for _, volume := range s.Volumes { | ||||
| 			switch volume.Type { | ||||
| 			case types.VolumeTypeVolume: | ||||
| 				if volume.Source != "" { // non anonymous volumes | ||||
| 					if _, ok := project.Volumes[volume.Source]; !ok { | ||||
| 						return errors.Wrap(errdefs.ErrInvalid, fmt.Sprintf("service %q refers to undefined volume %s", s.Name, volume.Source)) | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		for _, secret := range s.Secrets { | ||||
| 			if _, ok := project.Secrets[secret.Source]; !ok { | ||||
| 				return errors.Wrap(errdefs.ErrInvalid, fmt.Sprintf("service %q refers to undefined secret %s", s.Name, secret.Source)) | ||||
| 			} | ||||
| 		} | ||||
| 		for _, config := range s.Configs { | ||||
| 			if _, ok := project.Configs[config.Source]; !ok { | ||||
| 				return errors.Wrap(errdefs.ErrInvalid, fmt.Sprintf("service %q refers to undefined config %s", s.Name, config.Source)) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										154
									
								
								vendor/github.com/compose-spec/compose-go/loader/volume.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								vendor/github.com/compose-spec/compose-go/loader/volume.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,154 @@ | ||||
| /* | ||||
|    Copyright 2020 The Compose Specification Authors. | ||||
|  | ||||
|    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|    you may not use this file except in compliance with the License. | ||||
|    You may obtain a copy of the License at | ||||
|  | ||||
|        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
|    Unless required by applicable law or agreed to in writing, software | ||||
|    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|    See the License for the specific language governing permissions and | ||||
|    limitations under the License. | ||||
| */ | ||||
|  | ||||
| package loader | ||||
|  | ||||
| import ( | ||||
| 	"strings" | ||||
| 	"unicode" | ||||
| 	"unicode/utf8" | ||||
|  | ||||
| 	"github.com/compose-spec/compose-go/types" | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
|  | ||||
| const endOfSpec = rune(0) | ||||
|  | ||||
| // ParseVolume parses a volume spec without any knowledge of the target platform | ||||
| func ParseVolume(spec string) (types.ServiceVolumeConfig, error) { | ||||
| 	volume := types.ServiceVolumeConfig{} | ||||
|  | ||||
| 	switch len(spec) { | ||||
| 	case 0: | ||||
| 		return volume, errors.New("invalid empty volume spec") | ||||
| 	case 1, 2: | ||||
| 		volume.Target = spec | ||||
| 		volume.Type = string(types.VolumeTypeVolume) | ||||
| 		return volume, nil | ||||
| 	} | ||||
|  | ||||
| 	buffer := []rune{} | ||||
| 	for _, char := range spec + string(endOfSpec) { | ||||
| 		switch { | ||||
| 		case isWindowsDrive(buffer, char): | ||||
| 			buffer = append(buffer, char) | ||||
| 		case char == ':' || char == endOfSpec: | ||||
| 			if err := populateFieldFromBuffer(char, buffer, &volume); err != nil { | ||||
| 				populateType(&volume) | ||||
| 				return volume, errors.Wrapf(err, "invalid spec: %s", spec) | ||||
| 			} | ||||
| 			buffer = []rune{} | ||||
| 		default: | ||||
| 			buffer = append(buffer, char) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	populateType(&volume) | ||||
| 	return volume, nil | ||||
| } | ||||
|  | ||||
| func isWindowsDrive(buffer []rune, char rune) bool { | ||||
| 	return char == ':' && len(buffer) == 1 && unicode.IsLetter(buffer[0]) | ||||
| } | ||||
|  | ||||
| func populateFieldFromBuffer(char rune, buffer []rune, volume *types.ServiceVolumeConfig) error { | ||||
| 	strBuffer := string(buffer) | ||||
| 	switch { | ||||
| 	case len(buffer) == 0: | ||||
| 		return errors.New("empty section between colons") | ||||
| 	// Anonymous volume | ||||
| 	case volume.Source == "" && char == endOfSpec: | ||||
| 		volume.Target = strBuffer | ||||
| 		return nil | ||||
| 	case volume.Source == "": | ||||
| 		volume.Source = strBuffer | ||||
| 		return nil | ||||
| 	case volume.Target == "": | ||||
| 		volume.Target = strBuffer | ||||
| 		return nil | ||||
| 	case char == ':': | ||||
| 		return errors.New("too many colons") | ||||
| 	} | ||||
| 	for _, option := range strings.Split(strBuffer, ",") { | ||||
| 		switch option { | ||||
| 		case "ro": | ||||
| 			volume.ReadOnly = true | ||||
| 		case "rw": | ||||
| 			volume.ReadOnly = false | ||||
| 		case "nocopy": | ||||
| 			volume.Volume = &types.ServiceVolumeVolume{NoCopy: true} | ||||
| 		default: | ||||
| 			if isBindOption(option) { | ||||
| 				volume.Bind = &types.ServiceVolumeBind{Propagation: option} | ||||
| 			} | ||||
| 			// ignore unknown options | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| var Propagations = []string{ | ||||
| 	types.PropagationRPrivate, | ||||
| 	types.PropagationPrivate, | ||||
| 	types.PropagationRShared, | ||||
| 	types.PropagationShared, | ||||
| 	types.PropagationRSlave, | ||||
| 	types.PropagationSlave, | ||||
| } | ||||
|  | ||||
| func isBindOption(option string) bool { | ||||
| 	for _, propagation := range Propagations { | ||||
| 		if option == propagation { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| func populateType(volume *types.ServiceVolumeConfig) { | ||||
| 	if isFilePath(volume.Source) { | ||||
| 		volume.Type = types.VolumeTypeBind | ||||
| 		if volume.Bind == nil { | ||||
| 			volume.Bind = &types.ServiceVolumeBind{} | ||||
| 		} | ||||
| 		// For backward compatibility with docker-compose legacy, using short notation involves | ||||
| 		// bind will create missing host path | ||||
| 		volume.Bind.CreateHostPath = true | ||||
| 	} else { | ||||
| 		volume.Type = types.VolumeTypeVolume | ||||
| 		if volume.Volume == nil { | ||||
| 			volume.Volume = &types.ServiceVolumeVolume{} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func isFilePath(source string) bool { | ||||
| 	if source == "" { | ||||
| 		return false | ||||
| 	} | ||||
| 	switch source[0] { | ||||
| 	case '.', '/', '~': | ||||
| 		return true | ||||
| 	} | ||||
|  | ||||
| 	// windows named pipes | ||||
| 	if strings.HasPrefix(source, `\\`) { | ||||
| 		return true | ||||
| 	} | ||||
|  | ||||
| 	first, nextIndex := utf8.DecodeRuneInString(source) | ||||
| 	return isWindowsDrive([]rune{first}, rune(source[nextIndex])) | ||||
| } | ||||
							
								
								
									
										82
									
								
								vendor/github.com/compose-spec/compose-go/loader/windows_path.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								vendor/github.com/compose-spec/compose-go/loader/windows_path.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,82 @@ | ||||
| /* | ||||
|    Copyright 2020 The Compose Specification Authors. | ||||
|  | ||||
|    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|    you may not use this file except in compliance with the License. | ||||
|    You may obtain a copy of the License at | ||||
|  | ||||
|        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
|    Unless required by applicable law or agreed to in writing, software | ||||
|    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|    See the License for the specific language governing permissions and | ||||
|    limitations under the License. | ||||
| */ | ||||
|  | ||||
| package loader | ||||
|  | ||||
| // Copyright 2010 The Go Authors. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file. | ||||
| // https://github.com/golang/go/blob/master/LICENSE | ||||
|  | ||||
| // This file contains utilities to check for Windows absolute paths on Linux. | ||||
| // The code in this file was largely copied from the Golang filepath package | ||||
| // https://github.com/golang/go/blob/1d0e94b1e13d5e8a323a63cd1cc1ef95290c9c36/src/path/filepath/path_windows.go#L12-L65 | ||||
|  | ||||
| func isSlash(c uint8) bool { | ||||
| 	return c == '\\' || c == '/' | ||||
| } | ||||
|  | ||||
| // isAbs reports whether the path is a Windows absolute path. | ||||
| func isAbs(path string) (b bool) { | ||||
| 	l := volumeNameLen(path) | ||||
| 	if l == 0 { | ||||
| 		return false | ||||
| 	} | ||||
| 	path = path[l:] | ||||
| 	if path == "" { | ||||
| 		return false | ||||
| 	} | ||||
| 	return isSlash(path[0]) | ||||
| } | ||||
|  | ||||
| // volumeNameLen returns length of the leading volume name on Windows. | ||||
| // It returns 0 elsewhere. | ||||
| // nolint: gocyclo | ||||
| func volumeNameLen(path string) int { | ||||
| 	if len(path) < 2 { | ||||
| 		return 0 | ||||
| 	} | ||||
| 	// with drive letter | ||||
| 	c := path[0] | ||||
| 	if path[1] == ':' && ('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z') { | ||||
| 		return 2 | ||||
| 	} | ||||
| 	// is it UNC? https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx | ||||
| 	if l := len(path); l >= 5 && isSlash(path[0]) && isSlash(path[1]) && | ||||
| 		!isSlash(path[2]) && path[2] != '.' { | ||||
| 		// first, leading `\\` and next shouldn't be `\`. its server name. | ||||
| 		for n := 3; n < l-1; n++ { | ||||
| 			// second, next '\' shouldn't be repeated. | ||||
| 			if isSlash(path[n]) { | ||||
| 				n++ | ||||
| 				// third, following something characters. its share name. | ||||
| 				if !isSlash(path[n]) { | ||||
| 					if path[n] == '.' { | ||||
| 						break | ||||
| 					} | ||||
| 					for ; n < l; n++ { | ||||
| 						if isSlash(path[n]) { | ||||
| 							break | ||||
| 						} | ||||
| 					} | ||||
| 					return n | ||||
| 				} | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
							
								
								
									
										813
									
								
								vendor/github.com/compose-spec/compose-go/schema/compose-spec.json
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										813
									
								
								vendor/github.com/compose-spec/compose-go/schema/compose-spec.json
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,813 @@ | ||||
| { | ||||
|   "$schema": "http://json-schema.org/draft/2019-09/schema#", | ||||
|   "id": "compose_spec.json", | ||||
|   "type": "object", | ||||
|   "title": "Compose Specification", | ||||
|   "description": "The Compose file is a YAML file defining a multi-containers based application.", | ||||
|  | ||||
|   "properties": { | ||||
|     "version": { | ||||
|       "type": "string", | ||||
|       "description": "Version of the Compose specification used. Tools not implementing required version MUST reject the configuration file." | ||||
|     }, | ||||
|  | ||||
|     "services": { | ||||
|       "id": "#/properties/services", | ||||
|       "type": "object", | ||||
|       "patternProperties": { | ||||
|         "^[a-zA-Z0-9._-]+$": { | ||||
|           "$ref": "#/definitions/service" | ||||
|         } | ||||
|       }, | ||||
|       "additionalProperties": false | ||||
|     }, | ||||
|  | ||||
|     "networks": { | ||||
|       "id": "#/properties/networks", | ||||
|       "type": "object", | ||||
|       "patternProperties": { | ||||
|         "^[a-zA-Z0-9._-]+$": { | ||||
|           "$ref": "#/definitions/network" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     "volumes": { | ||||
|       "id": "#/properties/volumes", | ||||
|       "type": "object", | ||||
|       "patternProperties": { | ||||
|         "^[a-zA-Z0-9._-]+$": { | ||||
|           "$ref": "#/definitions/volume" | ||||
|         } | ||||
|       }, | ||||
|       "additionalProperties": false | ||||
|     }, | ||||
|  | ||||
|     "secrets": { | ||||
|       "id": "#/properties/secrets", | ||||
|       "type": "object", | ||||
|       "patternProperties": { | ||||
|         "^[a-zA-Z0-9._-]+$": { | ||||
|           "$ref": "#/definitions/secret" | ||||
|         } | ||||
|       }, | ||||
|       "additionalProperties": false | ||||
|     }, | ||||
|  | ||||
|     "configs": { | ||||
|       "id": "#/properties/configs", | ||||
|       "type": "object", | ||||
|       "patternProperties": { | ||||
|         "^[a-zA-Z0-9._-]+$": { | ||||
|           "$ref": "#/definitions/config" | ||||
|         } | ||||
|       }, | ||||
|       "additionalProperties": false | ||||
|     } | ||||
|   }, | ||||
|  | ||||
|   "patternProperties": {"^x-": {}}, | ||||
|   "additionalProperties": false, | ||||
|  | ||||
|   "definitions": { | ||||
|  | ||||
|     "service": { | ||||
|       "id": "#/definitions/service", | ||||
|       "type": "object", | ||||
|  | ||||
|       "properties": { | ||||
|         "deploy": {"$ref": "#/definitions/deployment"}, | ||||
|         "build": { | ||||
|           "oneOf": [ | ||||
|             {"type": "string"}, | ||||
|             { | ||||
|               "type": "object", | ||||
|               "properties": { | ||||
|                 "context": {"type": "string"}, | ||||
|                 "dockerfile": {"type": "string"}, | ||||
|                 "args": {"$ref": "#/definitions/list_or_dict"}, | ||||
|                 "labels": {"$ref": "#/definitions/list_or_dict"}, | ||||
|                 "cache_from": {"type": "array", "items": {"type": "string"}}, | ||||
|                 "network": {"type": "string"}, | ||||
|                 "target": {"type": "string"}, | ||||
|                 "shm_size": {"type": ["integer", "string"]}, | ||||
|                 "extra_hosts": {"$ref": "#/definitions/list_or_dict"}, | ||||
|                 "isolation": {"type": "string"} | ||||
|               }, | ||||
|               "additionalProperties": false, | ||||
|               "patternProperties": {"^x-": {}} | ||||
|             } | ||||
|           ] | ||||
|         }, | ||||
|         "blkio_config": { | ||||
|           "type": "object", | ||||
|           "properties": { | ||||
|             "device_read_bps": { | ||||
|               "type": "array", | ||||
|               "items": {"$ref": "#/definitions/blkio_limit"} | ||||
|             }, | ||||
|             "device_read_iops": { | ||||
|               "type": "array", | ||||
|               "items": {"$ref": "#/definitions/blkio_limit"} | ||||
|             }, | ||||
|             "device_write_bps": { | ||||
|               "type": "array", | ||||
|               "items": {"$ref": "#/definitions/blkio_limit"} | ||||
|             }, | ||||
|             "device_write_iops": { | ||||
|               "type": "array", | ||||
|               "items": {"$ref": "#/definitions/blkio_limit"} | ||||
|             }, | ||||
|             "weight": {"type": "integer"}, | ||||
|             "weight_device": { | ||||
|               "type": "array", | ||||
|               "items": {"$ref": "#/definitions/blkio_weight"} | ||||
|             } | ||||
|           }, | ||||
|           "additionalProperties": false | ||||
|         }, | ||||
|         "cap_add": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, | ||||
|         "cap_drop": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, | ||||
|         "cgroup_parent": {"type": "string"}, | ||||
|         "command": { | ||||
|           "oneOf": [ | ||||
|             {"type": "string"}, | ||||
|             {"type": "array", "items": {"type": "string"}} | ||||
|           ] | ||||
|         }, | ||||
|         "configs": { | ||||
|           "type": "array", | ||||
|           "items": { | ||||
|             "oneOf": [ | ||||
|               {"type": "string"}, | ||||
|               { | ||||
|                 "type": "object", | ||||
|                 "properties": { | ||||
|                   "source": {"type": "string"}, | ||||
|                   "target": {"type": "string"}, | ||||
|                   "uid": {"type": "string"}, | ||||
|                   "gid": {"type": "string"}, | ||||
|                   "mode": {"type": "number"} | ||||
|                 }, | ||||
|                 "additionalProperties": false, | ||||
|                 "patternProperties": {"^x-": {}} | ||||
|               } | ||||
|             ] | ||||
|           } | ||||
|         }, | ||||
|         "container_name": {"type": "string"}, | ||||
|         "cpu_count": {"type": "integer", "minimum": 0}, | ||||
|         "cpu_percent": {"type": "integer", "minimum": 0, "maximum": 100}, | ||||
|         "cpu_shares": {"type": ["number", "string"]}, | ||||
|         "cpu_quota": {"type": ["number", "string"]}, | ||||
|         "cpu_period": {"type": ["number", "string"]}, | ||||
|         "cpu_rt_period": {"type": ["number", "string"]}, | ||||
|         "cpu_rt_runtime": {"type": ["number", "string"]}, | ||||
|         "cpus": {"type": ["number", "string"]}, | ||||
|         "cpuset": {"type": "string"}, | ||||
|         "credential_spec": { | ||||
|           "type": "object", | ||||
|           "properties": { | ||||
|             "config": {"type": "string"}, | ||||
|             "file": {"type": "string"}, | ||||
|             "registry": {"type": "string"} | ||||
|           }, | ||||
|           "additionalProperties": false, | ||||
|           "patternProperties": {"^x-": {}} | ||||
|         }, | ||||
|         "depends_on": { | ||||
|           "oneOf": [ | ||||
|             {"$ref": "#/definitions/list_of_strings"}, | ||||
|             { | ||||
|               "type": "object", | ||||
|               "additionalProperties": false, | ||||
|               "patternProperties": { | ||||
|                 "^[a-zA-Z0-9._-]+$": { | ||||
|                   "type": "object", | ||||
|                   "additionalProperties": false, | ||||
|                   "properties": { | ||||
|                     "condition": { | ||||
|                       "type": "string", | ||||
|                       "enum": ["service_started", "service_healthy", "service_completed_successfully"] | ||||
|                     } | ||||
|                   }, | ||||
|                   "required": ["condition"] | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|           ] | ||||
|         }, | ||||
|         "device_cgroup_rules": {"$ref": "#/definitions/list_of_strings"}, | ||||
|         "devices": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, | ||||
|         "dns": {"$ref": "#/definitions/string_or_list"}, | ||||
|         "dns_opt": {"type": "array","items": {"type": "string"}, "uniqueItems": true}, | ||||
|         "dns_search": {"$ref": "#/definitions/string_or_list"}, | ||||
|         "domainname": {"type": "string"}, | ||||
|         "entrypoint": { | ||||
|           "oneOf": [ | ||||
|             {"type": "string"}, | ||||
|             {"type": "array", "items": {"type": "string"}} | ||||
|           ] | ||||
|         }, | ||||
|         "env_file": {"$ref": "#/definitions/string_or_list"}, | ||||
|         "environment": {"$ref": "#/definitions/list_or_dict"}, | ||||
|  | ||||
|         "expose": { | ||||
|           "type": "array", | ||||
|           "items": { | ||||
|             "type": ["string", "number"], | ||||
|             "format": "expose" | ||||
|           }, | ||||
|           "uniqueItems": true | ||||
|         }, | ||||
|         "extends": { | ||||
|           "oneOf": [ | ||||
|             {"type": "string"}, | ||||
|             { | ||||
|               "type": "object", | ||||
|  | ||||
|               "properties": { | ||||
|                 "service": {"type": "string"}, | ||||
|                 "file": {"type": "string"} | ||||
|               }, | ||||
|               "required": ["service"], | ||||
|               "additionalProperties": false | ||||
|             } | ||||
|           ] | ||||
|         }, | ||||
|         "external_links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, | ||||
|         "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"}, | ||||
|         "ipc": {"type": "string"}, | ||||
|         "isolation": {"type": "string"}, | ||||
|         "labels": {"$ref": "#/definitions/list_or_dict"}, | ||||
|         "links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, | ||||
|         "logging": { | ||||
|           "type": "object", | ||||
|  | ||||
|           "properties": { | ||||
|             "driver": {"type": "string"}, | ||||
|             "options": { | ||||
|               "type": "object", | ||||
|               "patternProperties": { | ||||
|                 "^.+$": {"type": ["string", "number", "null"]} | ||||
|               } | ||||
|             } | ||||
|           }, | ||||
|           "additionalProperties": false, | ||||
|           "patternProperties": {"^x-": {}} | ||||
|         }, | ||||
|         "mac_address": {"type": "string"}, | ||||
|         "mem_limit": {"type": ["number", "string"]}, | ||||
|         "mem_reservation": {"type": ["string", "integer"]}, | ||||
|         "mem_swappiness": {"type": "integer"}, | ||||
|         "memswap_limit": {"type": ["number", "string"]}, | ||||
|         "network_mode": {"type": "string"}, | ||||
|         "networks": { | ||||
|           "oneOf": [ | ||||
|             {"$ref": "#/definitions/list_of_strings"}, | ||||
|             { | ||||
|               "type": "object", | ||||
|               "patternProperties": { | ||||
|                 "^[a-zA-Z0-9._-]+$": { | ||||
|                   "oneOf": [ | ||||
|                     { | ||||
|                       "type": "object", | ||||
|                       "properties": { | ||||
|                         "aliases": {"$ref": "#/definitions/list_of_strings"}, | ||||
|                         "ipv4_address": {"type": "string"}, | ||||
|                         "ipv6_address": {"type": "string"}, | ||||
|                         "link_local_ips": {"$ref": "#/definitions/list_of_strings"}, | ||||
|                         "priority": {"type": "number"} | ||||
|                       }, | ||||
|                       "additionalProperties": false, | ||||
|                       "patternProperties": {"^x-": {}} | ||||
|                     }, | ||||
|                     {"type": "null"} | ||||
|                   ] | ||||
|                 } | ||||
|               }, | ||||
|               "additionalProperties": false | ||||
|             } | ||||
|           ] | ||||
|         }, | ||||
|         "oom_kill_disable": {"type": "boolean"}, | ||||
|         "oom_score_adj": {"type": "integer", "minimum": -1000, "maximum": 1000}, | ||||
|         "pid": {"type": ["string", "null"]}, | ||||
|         "pids_limit": {"type": ["number", "string"]}, | ||||
|         "platform": {"type": "string"}, | ||||
|         "ports": { | ||||
|           "type": "array", | ||||
|           "items": { | ||||
|             "oneOf": [ | ||||
|               {"type": "number", "format": "ports"}, | ||||
|               {"type": "string", "format": "ports"}, | ||||
|               { | ||||
|                 "type": "object", | ||||
|                 "properties": { | ||||
|                   "mode": {"type": "string"}, | ||||
|                   "host_ip": {"type": "string"}, | ||||
|                   "target": {"type": "integer"}, | ||||
|                   "published": {"type": "integer"}, | ||||
|                   "protocol": {"type": "string"} | ||||
|                 }, | ||||
|                 "additionalProperties": false, | ||||
|                 "patternProperties": {"^x-": {}} | ||||
|               } | ||||
|             ] | ||||
|           }, | ||||
|           "uniqueItems": true | ||||
|         }, | ||||
|         "privileged": {"type": "boolean"}, | ||||
|         "profiles": {"$ref": "#/definitions/list_of_strings"}, | ||||
|         "pull_policy": {"type": "string", "enum": [ | ||||
|           "always", "never", "if_not_present", "build" | ||||
|         ]}, | ||||
|         "read_only": {"type": "boolean"}, | ||||
|         "restart": {"type": "string"}, | ||||
|         "runtime": { | ||||
|           "type": "string" | ||||
|         }, | ||||
|         "scale": { | ||||
|           "type": "integer" | ||||
|         }, | ||||
|         "security_opt": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, | ||||
|         "shm_size": {"type": ["number", "string"]}, | ||||
|         "secrets": { | ||||
|           "type": "array", | ||||
|           "items": { | ||||
|             "oneOf": [ | ||||
|               {"type": "string"}, | ||||
|               { | ||||
|                 "type": "object", | ||||
|                 "properties": { | ||||
|                   "source": {"type": "string"}, | ||||
|                   "target": {"type": "string"}, | ||||
|                   "uid": {"type": "string"}, | ||||
|                   "gid": {"type": "string"}, | ||||
|                   "mode": {"type": "number"} | ||||
|                 }, | ||||
|                 "additionalProperties": false, | ||||
|                 "patternProperties": {"^x-": {}} | ||||
|               } | ||||
|             ] | ||||
|           } | ||||
|         }, | ||||
|         "sysctls": {"$ref": "#/definitions/list_or_dict"}, | ||||
|         "stdin_open": {"type": "boolean"}, | ||||
|         "stop_grace_period": {"type": "string", "format": "duration"}, | ||||
|         "stop_signal": {"type": "string"}, | ||||
|         "tmpfs": {"$ref": "#/definitions/string_or_list"}, | ||||
|         "tty": {"type": "boolean"}, | ||||
|         "ulimits": { | ||||
|           "type": "object", | ||||
|           "patternProperties": { | ||||
|             "^[a-z]+$": { | ||||
|               "oneOf": [ | ||||
|                 {"type": "integer"}, | ||||
|                 { | ||||
|                   "type": "object", | ||||
|                   "properties": { | ||||
|                     "hard": {"type": "integer"}, | ||||
|                     "soft": {"type": "integer"} | ||||
|                   }, | ||||
|                   "required": ["soft", "hard"], | ||||
|                   "additionalProperties": false, | ||||
|                   "patternProperties": {"^x-": {}} | ||||
|                 } | ||||
|               ] | ||||
|             } | ||||
|           } | ||||
|         }, | ||||
|         "user": {"type": "string"}, | ||||
|         "userns_mode": {"type": "string"}, | ||||
|         "volumes": { | ||||
|           "type": "array", | ||||
|           "items": { | ||||
|             "oneOf": [ | ||||
|               {"type": "string"}, | ||||
|               { | ||||
|                 "type": "object", | ||||
|                 "required": ["type"], | ||||
|                 "properties": { | ||||
|                   "type": {"type": "string"}, | ||||
|                   "source": {"type": "string"}, | ||||
|                   "target": {"type": "string"}, | ||||
|                   "read_only": {"type": "boolean"}, | ||||
|                   "consistency": {"type": "string"}, | ||||
|                   "bind": { | ||||
|                     "type": "object", | ||||
|                     "properties": { | ||||
|                       "propagation": {"type": "string"}, | ||||
|                       "create_host_path": {"type": "boolean"} | ||||
|                     }, | ||||
|                     "additionalProperties": false, | ||||
|                     "patternProperties": {"^x-": {}} | ||||
|                   }, | ||||
|                   "volume": { | ||||
|                     "type": "object", | ||||
|                     "properties": { | ||||
|                       "nocopy": {"type": "boolean"} | ||||
|                     }, | ||||
|                     "additionalProperties": false, | ||||
|                     "patternProperties": {"^x-": {}} | ||||
|                   }, | ||||
|                   "tmpfs": { | ||||
|                     "type": "object", | ||||
|                     "properties": { | ||||
|                       "size": { | ||||
|                         "type": "integer", | ||||
|                         "minimum": 0 | ||||
|                       } | ||||
|                     }, | ||||
|                     "additionalProperties": false, | ||||
|                     "patternProperties": {"^x-": {}} | ||||
|                   } | ||||
|                 }, | ||||
|                 "additionalProperties": false, | ||||
|                 "patternProperties": {"^x-": {}} | ||||
|               } | ||||
|             ] | ||||
|           }, | ||||
|           "uniqueItems": true | ||||
|         }, | ||||
|         "volumes_from": { | ||||
|           "type": "array", | ||||
|           "items": {"type": "string"}, | ||||
|           "uniqueItems": true | ||||
|         }, | ||||
|         "working_dir": {"type": "string"} | ||||
|       }, | ||||
|       "patternProperties": {"^x-": {}}, | ||||
|       "additionalProperties": false | ||||
|     }, | ||||
|  | ||||
|     "healthcheck": { | ||||
|       "id": "#/definitions/healthcheck", | ||||
|       "type": "object", | ||||
|       "properties": { | ||||
|         "disable": {"type": "boolean"}, | ||||
|         "interval": {"type": "string", "format": "duration"}, | ||||
|         "retries": {"type": "number"}, | ||||
|         "test": { | ||||
|           "oneOf": [ | ||||
|             {"type": "string"}, | ||||
|             {"type": "array", "items": {"type": "string"}} | ||||
|           ] | ||||
|         }, | ||||
|         "timeout": {"type": "string", "format": "duration"}, | ||||
|         "start_period": {"type": "string", "format": "duration"} | ||||
|       }, | ||||
|       "additionalProperties": false, | ||||
|       "patternProperties": {"^x-": {}} | ||||
|     }, | ||||
|     "deployment": { | ||||
|       "id": "#/definitions/deployment", | ||||
|       "type": ["object", "null"], | ||||
|       "properties": { | ||||
|         "mode": {"type": "string"}, | ||||
|         "endpoint_mode": {"type": "string"}, | ||||
|         "replicas": {"type": "integer"}, | ||||
|         "labels": {"$ref": "#/definitions/list_or_dict"}, | ||||
|         "rollback_config": { | ||||
|           "type": "object", | ||||
|           "properties": { | ||||
|             "parallelism": {"type": "integer"}, | ||||
|             "delay": {"type": "string", "format": "duration"}, | ||||
|             "failure_action": {"type": "string"}, | ||||
|             "monitor": {"type": "string", "format": "duration"}, | ||||
|             "max_failure_ratio": {"type": "number"}, | ||||
|             "order": {"type": "string", "enum": [ | ||||
|               "start-first", "stop-first" | ||||
|             ]} | ||||
|           }, | ||||
|           "additionalProperties": false, | ||||
|           "patternProperties": {"^x-": {}} | ||||
|         }, | ||||
|         "update_config": { | ||||
|           "type": "object", | ||||
|           "properties": { | ||||
|             "parallelism": {"type": "integer"}, | ||||
|             "delay": {"type": "string", "format": "duration"}, | ||||
|             "failure_action": {"type": "string"}, | ||||
|             "monitor": {"type": "string", "format": "duration"}, | ||||
|             "max_failure_ratio": {"type": "number"}, | ||||
|             "order": {"type": "string", "enum": [ | ||||
|               "start-first", "stop-first" | ||||
|             ]} | ||||
|           }, | ||||
|           "additionalProperties": false, | ||||
|           "patternProperties": {"^x-": {}} | ||||
|         }, | ||||
|         "resources": { | ||||
|           "type": "object", | ||||
|           "properties": { | ||||
|             "limits": { | ||||
|               "type": "object", | ||||
|               "properties": { | ||||
|                 "cpus": {"type": ["number", "string"]}, | ||||
|                 "memory": {"type": "string"} | ||||
|               }, | ||||
|               "additionalProperties": false, | ||||
|               "patternProperties": {"^x-": {}} | ||||
|             }, | ||||
|             "reservations": { | ||||
|               "type": "object", | ||||
|               "properties": { | ||||
|                 "cpus": {"type": ["number", "string"]}, | ||||
|                 "memory": {"type": "string"}, | ||||
|                 "generic_resources": {"$ref": "#/definitions/generic_resources"}, | ||||
|                 "devices": {"$ref": "#/definitions/devices"} | ||||
|               }, | ||||
|               "additionalProperties": false, | ||||
|               "patternProperties": {"^x-": {}} | ||||
|             } | ||||
|           }, | ||||
|           "additionalProperties": false, | ||||
|           "patternProperties": {"^x-": {}} | ||||
|         }, | ||||
|         "restart_policy": { | ||||
|           "type": "object", | ||||
|           "properties": { | ||||
|             "condition": {"type": "string"}, | ||||
|             "delay": {"type": "string", "format": "duration"}, | ||||
|             "max_attempts": {"type": "integer"}, | ||||
|             "window": {"type": "string", "format": "duration"} | ||||
|           }, | ||||
|           "additionalProperties": false, | ||||
|           "patternProperties": {"^x-": {}} | ||||
|         }, | ||||
|         "placement": { | ||||
|           "type": "object", | ||||
|           "properties": { | ||||
|             "constraints": {"type": "array", "items": {"type": "string"}}, | ||||
|             "preferences": { | ||||
|               "type": "array", | ||||
|               "items": { | ||||
|                 "type": "object", | ||||
|                 "properties": { | ||||
|                   "spread": {"type": "string"} | ||||
|                 }, | ||||
|                 "additionalProperties": false, | ||||
|                 "patternProperties": {"^x-": {}} | ||||
|               } | ||||
|             }, | ||||
|             "max_replicas_per_node": {"type": "integer"} | ||||
|           }, | ||||
|           "additionalProperties": false, | ||||
|           "patternProperties": {"^x-": {}} | ||||
|         } | ||||
|       }, | ||||
|       "additionalProperties": false, | ||||
|       "patternProperties": {"^x-": {}} | ||||
|     }, | ||||
|  | ||||
|     "generic_resources": { | ||||
|       "id": "#/definitions/generic_resources", | ||||
|       "type": "array", | ||||
|       "items": { | ||||
|         "type": "object", | ||||
|         "properties": { | ||||
|           "discrete_resource_spec": { | ||||
|             "type": "object", | ||||
|             "properties": { | ||||
|               "kind": {"type": "string"}, | ||||
|               "value": {"type": "number"} | ||||
|             }, | ||||
|             "additionalProperties": false, | ||||
|             "patternProperties": {"^x-": {}} | ||||
|           } | ||||
|         }, | ||||
|         "additionalProperties": false, | ||||
|         "patternProperties": {"^x-": {}} | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     "devices": { | ||||
|       "id": "#/definitions/devices", | ||||
|       "type": "array", | ||||
|       "items": { | ||||
|         "type": "object", | ||||
|         "properties": { | ||||
|           "capabilities": {"$ref": "#/definitions/list_of_strings"}, | ||||
|           "count": {"type": ["string", "integer"]}, | ||||
|           "device_ids": {"$ref": "#/definitions/list_of_strings"}, | ||||
|           "driver":{"type": "string"}, | ||||
|           "options":{"$ref": "#/definitions/list_or_dict"} | ||||
|         }, | ||||
|         "additionalProperties": false, | ||||
|         "patternProperties": {"^x-": {}} | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     "network": { | ||||
|       "id": "#/definitions/network", | ||||
|       "type": ["object", "null"], | ||||
|       "properties": { | ||||
|         "name": {"type": "string"}, | ||||
|         "driver": {"type": "string"}, | ||||
|         "driver_opts": { | ||||
|           "type": "object", | ||||
|           "patternProperties": { | ||||
|             "^.+$": {"type": ["string", "number"]} | ||||
|           } | ||||
|         }, | ||||
|         "ipam": { | ||||
|           "type": "object", | ||||
|           "properties": { | ||||
|             "driver": {"type": "string"}, | ||||
|             "config": { | ||||
|               "type": "array", | ||||
|               "items": { | ||||
|                 "type": "object", | ||||
|                 "properties": { | ||||
|                   "subnet": {"type": "string", "format": "subnet_ip_address"}, | ||||
|                   "ip_range": {"type": "string"}, | ||||
|                   "gateway": {"type": "string"}, | ||||
|                   "aux_addresses": { | ||||
|                     "type": "object", | ||||
|                     "additionalProperties": false, | ||||
|                     "patternProperties": {"^.+$": {"type": "string"}} | ||||
|                   } | ||||
|                 }, | ||||
|                 "additionalProperties": false, | ||||
|                 "patternProperties": {"^x-": {}} | ||||
|               } | ||||
|             }, | ||||
|             "options": { | ||||
|               "type": "object", | ||||
|               "additionalProperties": false, | ||||
|               "patternProperties": {"^.+$": {"type": "string"}} | ||||
|             } | ||||
|           }, | ||||
|           "additionalProperties": false, | ||||
|           "patternProperties": {"^x-": {}} | ||||
|         }, | ||||
|         "external": { | ||||
|           "type": ["boolean", "object"], | ||||
|           "properties": { | ||||
|             "name": { | ||||
|               "deprecated": true, | ||||
|               "type": "string" | ||||
|             } | ||||
|           }, | ||||
|           "additionalProperties": false, | ||||
|           "patternProperties": {"^x-": {}} | ||||
|         }, | ||||
|         "internal": {"type": "boolean"}, | ||||
|         "enable_ipv6": {"type": "boolean"}, | ||||
|         "attachable": {"type": "boolean"}, | ||||
|         "labels": {"$ref": "#/definitions/list_or_dict"} | ||||
|       }, | ||||
|       "additionalProperties": false, | ||||
|       "patternProperties": {"^x-": {}} | ||||
|     }, | ||||
|  | ||||
|     "volume": { | ||||
|       "id": "#/definitions/volume", | ||||
|       "type": ["object", "null"], | ||||
|       "properties": { | ||||
|         "name": {"type": "string"}, | ||||
|         "driver": {"type": "string"}, | ||||
|         "driver_opts": { | ||||
|           "type": "object", | ||||
|           "patternProperties": { | ||||
|             "^.+$": {"type": ["string", "number"]} | ||||
|           } | ||||
|         }, | ||||
|         "external": { | ||||
|           "type": ["boolean", "object"], | ||||
|           "properties": { | ||||
|             "name": { | ||||
|               "deprecated": true, | ||||
|               "type": "string" | ||||
|             } | ||||
|           }, | ||||
|           "additionalProperties": false, | ||||
|           "patternProperties": {"^x-": {}} | ||||
|         }, | ||||
|         "labels": {"$ref": "#/definitions/list_or_dict"} | ||||
|       }, | ||||
|       "additionalProperties": false, | ||||
|       "patternProperties": {"^x-": {}} | ||||
|     }, | ||||
|  | ||||
|     "secret": { | ||||
|       "id": "#/definitions/secret", | ||||
|       "type": "object", | ||||
|       "properties": { | ||||
|         "name": {"type": "string"}, | ||||
|         "file": {"type": "string"}, | ||||
|         "external": { | ||||
|           "type": ["boolean", "object"], | ||||
|           "properties": { | ||||
|             "name": {"type": "string"} | ||||
|           } | ||||
|         }, | ||||
|         "labels": {"$ref": "#/definitions/list_or_dict"}, | ||||
|         "driver": {"type": "string"}, | ||||
|         "driver_opts": { | ||||
|           "type": "object", | ||||
|           "patternProperties": { | ||||
|             "^.+$": {"type": ["string", "number"]} | ||||
|           } | ||||
|         }, | ||||
|         "template_driver": {"type": "string"} | ||||
|       }, | ||||
|       "additionalProperties": false, | ||||
|       "patternProperties": {"^x-": {}} | ||||
|     }, | ||||
|  | ||||
|     "config": { | ||||
|       "id": "#/definitions/config", | ||||
|       "type": "object", | ||||
|       "properties": { | ||||
|         "name": {"type": "string"}, | ||||
|         "file": {"type": "string"}, | ||||
|         "external": { | ||||
|           "type": ["boolean", "object"], | ||||
|           "properties": { | ||||
|             "name": { | ||||
|               "deprecated": true, | ||||
|               "type": "string" | ||||
|             } | ||||
|           } | ||||
|         }, | ||||
|         "labels": {"$ref": "#/definitions/list_or_dict"}, | ||||
|         "template_driver": {"type": "string"} | ||||
|       }, | ||||
|       "additionalProperties": false, | ||||
|       "patternProperties": {"^x-": {}} | ||||
|     }, | ||||
|  | ||||
|     "string_or_list": { | ||||
|       "oneOf": [ | ||||
|         {"type": "string"}, | ||||
|         {"$ref": "#/definitions/list_of_strings"} | ||||
|       ] | ||||
|     }, | ||||
|  | ||||
|     "list_of_strings": { | ||||
|       "type": "array", | ||||
|       "items": {"type": "string"}, | ||||
|       "uniqueItems": true | ||||
|     }, | ||||
|  | ||||
|     "list_or_dict": { | ||||
|       "oneOf": [ | ||||
|         { | ||||
|           "type": "object", | ||||
|           "patternProperties": { | ||||
|             ".+": { | ||||
|               "type": ["string", "number", "null"] | ||||
|             } | ||||
|           }, | ||||
|           "additionalProperties": false | ||||
|         }, | ||||
|         {"type": "array", "items": {"type": "string"}, "uniqueItems": true} | ||||
|       ] | ||||
|     }, | ||||
|  | ||||
|     "blkio_limit": { | ||||
|       "type": "object", | ||||
|       "properties": { | ||||
|         "path": {"type": "string"}, | ||||
|         "rate": {"type": ["integer", "string"]} | ||||
|       }, | ||||
|       "additionalProperties": false | ||||
|     }, | ||||
|     "blkio_weight": { | ||||
|       "type": "object", | ||||
|       "properties": { | ||||
|         "path": {"type": "string"}, | ||||
|         "weight": {"type": "integer"} | ||||
|       }, | ||||
|       "additionalProperties": false | ||||
|     }, | ||||
|  | ||||
|     "constraints": { | ||||
|       "service": { | ||||
|         "id": "#/definitions/constraints/service", | ||||
|         "anyOf": [ | ||||
|           {"required": ["build"]}, | ||||
|           {"required": ["image"]} | ||||
|         ], | ||||
|         "properties": { | ||||
|           "build": { | ||||
|             "required": ["context"] | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										169
									
								
								vendor/github.com/compose-spec/compose-go/schema/schema.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								vendor/github.com/compose-spec/compose-go/schema/schema.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,169 @@ | ||||
| /* | ||||
|    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 schema | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/xeipuuv/gojsonschema" | ||||
|  | ||||
| 	// Enable support for embedded static resources | ||||
| 	_ "embed" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	defaultVersion = "1.0" | ||||
| 	versionField   = "version" | ||||
| ) | ||||
|  | ||||
| type portsFormatChecker struct{} | ||||
|  | ||||
| func (checker portsFormatChecker) IsFormat(input interface{}) bool { | ||||
| 	// TODO: implement this | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| type durationFormatChecker struct{} | ||||
|  | ||||
| func (checker durationFormatChecker) IsFormat(input interface{}) bool { | ||||
| 	value, ok := input.(string) | ||||
| 	if !ok { | ||||
| 		return false | ||||
| 	} | ||||
| 	_, err := time.ParseDuration(value) | ||||
| 	return err == nil | ||||
| } | ||||
|  | ||||
| func init() { | ||||
| 	gojsonschema.FormatCheckers.Add("expose", portsFormatChecker{}) | ||||
| 	gojsonschema.FormatCheckers.Add("ports", portsFormatChecker{}) | ||||
| 	gojsonschema.FormatCheckers.Add("duration", durationFormatChecker{}) | ||||
| } | ||||
|  | ||||
| // Schema is the compose-spec JSON schema | ||||
| //go:embed compose-spec.json | ||||
| var Schema string | ||||
|  | ||||
| // Validate uses the jsonschema to validate the configuration | ||||
| func Validate(config map[string]interface{}) error { | ||||
| 	schemaLoader := gojsonschema.NewStringLoader(Schema) | ||||
| 	dataLoader := gojsonschema.NewGoLoader(config) | ||||
|  | ||||
| 	result, err := gojsonschema.Validate(schemaLoader, dataLoader) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if !result.Valid() { | ||||
| 		return toError(result) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func toError(result *gojsonschema.Result) error { | ||||
| 	err := getMostSpecificError(result.Errors()) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| const ( | ||||
| 	jsonschemaOneOf = "number_one_of" | ||||
| 	jsonschemaAnyOf = "number_any_of" | ||||
| ) | ||||
|  | ||||
| func getDescription(err validationError) string { | ||||
| 	switch err.parent.Type() { | ||||
| 	case "invalid_type": | ||||
| 		if expectedType, ok := err.parent.Details()["expected"].(string); ok { | ||||
| 			return fmt.Sprintf("must be a %s", humanReadableType(expectedType)) | ||||
| 		} | ||||
| 	case jsonschemaOneOf, jsonschemaAnyOf: | ||||
| 		if err.child == nil { | ||||
| 			return err.parent.Description() | ||||
| 		} | ||||
| 		return err.child.Description() | ||||
| 	} | ||||
| 	return err.parent.Description() | ||||
| } | ||||
|  | ||||
| func humanReadableType(definition string) string { | ||||
| 	if definition[0:1] == "[" { | ||||
| 		allTypes := strings.Split(definition[1:len(definition)-1], ",") | ||||
| 		for i, t := range allTypes { | ||||
| 			allTypes[i] = humanReadableType(t) | ||||
| 		} | ||||
| 		return fmt.Sprintf( | ||||
| 			"%s or %s", | ||||
| 			strings.Join(allTypes[0:len(allTypes)-1], ", "), | ||||
| 			allTypes[len(allTypes)-1], | ||||
| 		) | ||||
| 	} | ||||
| 	if definition == "object" { | ||||
| 		return "mapping" | ||||
| 	} | ||||
| 	if definition == "array" { | ||||
| 		return "list" | ||||
| 	} | ||||
| 	return definition | ||||
| } | ||||
|  | ||||
| type validationError struct { | ||||
| 	parent gojsonschema.ResultError | ||||
| 	child  gojsonschema.ResultError | ||||
| } | ||||
|  | ||||
| func (err validationError) Error() string { | ||||
| 	description := getDescription(err) | ||||
| 	return fmt.Sprintf("%s %s", err.parent.Field(), description) | ||||
| } | ||||
|  | ||||
| func getMostSpecificError(errors []gojsonschema.ResultError) validationError { | ||||
| 	mostSpecificError := 0 | ||||
| 	for i, err := range errors { | ||||
| 		if specificity(err) > specificity(errors[mostSpecificError]) { | ||||
| 			mostSpecificError = i | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		if specificity(err) == specificity(errors[mostSpecificError]) { | ||||
| 			// Invalid type errors win in a tie-breaker for most specific field name | ||||
| 			if err.Type() == "invalid_type" && errors[mostSpecificError].Type() != "invalid_type" { | ||||
| 				mostSpecificError = i | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if mostSpecificError+1 == len(errors) { | ||||
| 		return validationError{parent: errors[mostSpecificError]} | ||||
| 	} | ||||
|  | ||||
| 	switch errors[mostSpecificError].Type() { | ||||
| 	case "number_one_of", "number_any_of": | ||||
| 		return validationError{ | ||||
| 			parent: errors[mostSpecificError], | ||||
| 			child:  errors[mostSpecificError+1], | ||||
| 		} | ||||
| 	default: | ||||
| 		return validationError{parent: errors[mostSpecificError]} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func specificity(err gojsonschema.ResultError) int { | ||||
| 	return len(strings.Split(err.Field(), ".")) | ||||
| } | ||||
							
								
								
									
										269
									
								
								vendor/github.com/compose-spec/compose-go/template/template.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										269
									
								
								vendor/github.com/compose-spec/compose-go/template/template.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,269 @@ | ||||
| /* | ||||
|    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 template | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"regexp" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| var delimiter = "\\$" | ||||
| var substitution = "[_a-z][_a-z0-9]*(?::?[-?][^}]*)?" | ||||
|  | ||||
| var patternString = fmt.Sprintf( | ||||
| 	"%s(?i:(?P<escaped>%s)|(?P<named>%s)|{(?P<braced>%s)}|(?P<invalid>))", | ||||
| 	delimiter, delimiter, substitution, substitution, | ||||
| ) | ||||
|  | ||||
| var defaultPattern = regexp.MustCompile(patternString) | ||||
|  | ||||
| // DefaultSubstituteFuncs contains the default SubstituteFunc used by the docker cli | ||||
| var DefaultSubstituteFuncs = []SubstituteFunc{ | ||||
| 	softDefault, | ||||
| 	hardDefault, | ||||
| 	requiredNonEmpty, | ||||
| 	required, | ||||
| } | ||||
|  | ||||
| // InvalidTemplateError is returned when a variable template is not in a valid | ||||
| // format | ||||
| type InvalidTemplateError struct { | ||||
| 	Template string | ||||
| } | ||||
|  | ||||
| func (e InvalidTemplateError) Error() string { | ||||
| 	return fmt.Sprintf("Invalid template: %#v", e.Template) | ||||
| } | ||||
|  | ||||
| // Mapping is a user-supplied function which maps from variable names to values. | ||||
| // Returns the value as a string and a bool indicating whether | ||||
| // the value is present, to distinguish between an empty string | ||||
| // and the absence of a value. | ||||
| type Mapping func(string) (string, bool) | ||||
|  | ||||
| // SubstituteFunc is a user-supplied function that apply substitution. | ||||
| // Returns the value as a string, a bool indicating if the function could apply | ||||
| // the substitution and an error. | ||||
| type SubstituteFunc func(string, Mapping) (string, bool, error) | ||||
|  | ||||
| // SubstituteWith subsitute variables in the string with their values. | ||||
| // It accepts additional substitute function. | ||||
| func SubstituteWith(template string, mapping Mapping, pattern *regexp.Regexp, subsFuncs ...SubstituteFunc) (string, error) { | ||||
| 	var err error | ||||
| 	result := pattern.ReplaceAllStringFunc(template, func(substring string) string { | ||||
| 		matches := pattern.FindStringSubmatch(substring) | ||||
| 		groups := matchGroups(matches, pattern) | ||||
| 		if escaped := groups["escaped"]; escaped != "" { | ||||
| 			return escaped | ||||
| 		} | ||||
|  | ||||
| 		substitution := groups["named"] | ||||
| 		if substitution == "" { | ||||
| 			substitution = groups["braced"] | ||||
| 		} | ||||
|  | ||||
| 		if substitution == "" { | ||||
| 			err = &InvalidTemplateError{Template: template} | ||||
| 			return "" | ||||
| 		} | ||||
|  | ||||
| 		for _, f := range subsFuncs { | ||||
| 			var ( | ||||
| 				value   string | ||||
| 				applied bool | ||||
| 			) | ||||
| 			value, applied, err = f(substitution, mapping) | ||||
| 			if err != nil { | ||||
| 				return "" | ||||
| 			} | ||||
| 			if !applied { | ||||
| 				continue | ||||
| 			} | ||||
| 			return value | ||||
| 		} | ||||
|  | ||||
| 		value, _ := mapping(substitution) | ||||
| 		return value | ||||
| 	}) | ||||
|  | ||||
| 	return result, err | ||||
| } | ||||
|  | ||||
| // Substitute variables in the string with their values | ||||
| func Substitute(template string, mapping Mapping) (string, error) { | ||||
| 	return SubstituteWith(template, mapping, defaultPattern, DefaultSubstituteFuncs...) | ||||
| } | ||||
|  | ||||
| // ExtractVariables returns a map of all the variables defined in the specified | ||||
| // composefile (dict representation) and their default value if any. | ||||
| func ExtractVariables(configDict map[string]interface{}, pattern *regexp.Regexp) map[string]Variable { | ||||
| 	if pattern == nil { | ||||
| 		pattern = defaultPattern | ||||
| 	} | ||||
| 	return recurseExtract(configDict, pattern) | ||||
| } | ||||
|  | ||||
| func recurseExtract(value interface{}, pattern *regexp.Regexp) map[string]Variable { | ||||
| 	m := map[string]Variable{} | ||||
|  | ||||
| 	switch value := value.(type) { | ||||
| 	case string: | ||||
| 		if values, is := extractVariable(value, pattern); is { | ||||
| 			for _, v := range values { | ||||
| 				m[v.Name] = v | ||||
| 			} | ||||
| 		} | ||||
| 	case map[string]interface{}: | ||||
| 		for _, elem := range value { | ||||
| 			submap := recurseExtract(elem, pattern) | ||||
| 			for key, value := range submap { | ||||
| 				m[key] = value | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 	case []interface{}: | ||||
| 		for _, elem := range value { | ||||
| 			if values, is := extractVariable(elem, pattern); is { | ||||
| 				for _, v := range values { | ||||
| 					m[v.Name] = v | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return m | ||||
| } | ||||
|  | ||||
| type Variable struct { | ||||
| 	Name         string | ||||
| 	DefaultValue string | ||||
| 	Required     bool | ||||
| } | ||||
|  | ||||
| func extractVariable(value interface{}, pattern *regexp.Regexp) ([]Variable, bool) { | ||||
| 	sValue, ok := value.(string) | ||||
| 	if !ok { | ||||
| 		return []Variable{}, false | ||||
| 	} | ||||
| 	matches := pattern.FindAllStringSubmatch(sValue, -1) | ||||
| 	if len(matches) == 0 { | ||||
| 		return []Variable{}, false | ||||
| 	} | ||||
| 	values := []Variable{} | ||||
| 	for _, match := range matches { | ||||
| 		groups := matchGroups(match, pattern) | ||||
| 		if escaped := groups["escaped"]; escaped != "" { | ||||
| 			continue | ||||
| 		} | ||||
| 		val := groups["named"] | ||||
| 		if val == "" { | ||||
| 			val = groups["braced"] | ||||
| 		} | ||||
| 		name := val | ||||
| 		var defaultValue string | ||||
| 		var required bool | ||||
| 		switch { | ||||
| 		case strings.Contains(val, ":?"): | ||||
| 			name, _ = partition(val, ":?") | ||||
| 			required = true | ||||
| 		case strings.Contains(val, "?"): | ||||
| 			name, _ = partition(val, "?") | ||||
| 			required = true | ||||
| 		case strings.Contains(val, ":-"): | ||||
| 			name, defaultValue = partition(val, ":-") | ||||
| 		case strings.Contains(val, "-"): | ||||
| 			name, defaultValue = partition(val, "-") | ||||
| 		} | ||||
| 		values = append(values, Variable{ | ||||
| 			Name:         name, | ||||
| 			DefaultValue: defaultValue, | ||||
| 			Required:     required, | ||||
| 		}) | ||||
| 	} | ||||
| 	return values, len(values) > 0 | ||||
| } | ||||
|  | ||||
| // Soft default (fall back if unset or empty) | ||||
| func softDefault(substitution string, mapping Mapping) (string, bool, error) { | ||||
| 	sep := ":-" | ||||
| 	if !strings.Contains(substitution, sep) { | ||||
| 		return "", false, nil | ||||
| 	} | ||||
| 	name, defaultValue := partition(substitution, sep) | ||||
| 	value, ok := mapping(name) | ||||
| 	if !ok || value == "" { | ||||
| 		return defaultValue, true, nil | ||||
| 	} | ||||
| 	return value, true, nil | ||||
| } | ||||
|  | ||||
| // Hard default (fall back if-and-only-if empty) | ||||
| func hardDefault(substitution string, mapping Mapping) (string, bool, error) { | ||||
| 	sep := "-" | ||||
| 	if !strings.Contains(substitution, sep) { | ||||
| 		return "", false, nil | ||||
| 	} | ||||
| 	name, defaultValue := partition(substitution, sep) | ||||
| 	value, ok := mapping(name) | ||||
| 	if !ok { | ||||
| 		return defaultValue, true, nil | ||||
| 	} | ||||
| 	return value, true, nil | ||||
| } | ||||
|  | ||||
| func requiredNonEmpty(substitution string, mapping Mapping) (string, bool, error) { | ||||
| 	return withRequired(substitution, mapping, ":?", func(v string) bool { return v != "" }) | ||||
| } | ||||
|  | ||||
| func required(substitution string, mapping Mapping) (string, bool, error) { | ||||
| 	return withRequired(substitution, mapping, "?", func(_ string) bool { return true }) | ||||
| } | ||||
|  | ||||
| func withRequired(substitution string, mapping Mapping, sep string, valid func(string) bool) (string, bool, error) { | ||||
| 	if !strings.Contains(substitution, sep) { | ||||
| 		return "", false, nil | ||||
| 	} | ||||
| 	name, errorMessage := partition(substitution, sep) | ||||
| 	value, ok := mapping(name) | ||||
| 	if !ok || !valid(value) { | ||||
| 		return "", true, &InvalidTemplateError{ | ||||
| 			Template: fmt.Sprintf("required variable %s is missing a value: %s", name, errorMessage), | ||||
| 		} | ||||
| 	} | ||||
| 	return value, true, nil | ||||
| } | ||||
|  | ||||
| func matchGroups(matches []string, pattern *regexp.Regexp) map[string]string { | ||||
| 	groups := make(map[string]string) | ||||
| 	for i, name := range pattern.SubexpNames()[1:] { | ||||
| 		groups[name] = matches[i+1] | ||||
| 	} | ||||
| 	return groups | ||||
| } | ||||
|  | ||||
| // Split the string at the first occurrence of sep, and return the part before the separator, | ||||
| // and the part after the separator. | ||||
| // | ||||
| // If the separator is not found, return the string itself, followed by an empty string. | ||||
| func partition(s, sep string) (string, string) { | ||||
| 	if strings.Contains(s, sep) { | ||||
| 		parts := strings.SplitN(s, sep, 2) | ||||
| 		return parts[0], parts[1] | ||||
| 	} | ||||
| 	return s, "" | ||||
| } | ||||
							
								
								
									
										109
									
								
								vendor/github.com/compose-spec/compose-go/types/config.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								vendor/github.com/compose-spec/compose-go/types/config.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,109 @@ | ||||
| /* | ||||
|    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 types | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
|  | ||||
| 	"github.com/mitchellh/mapstructure" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| ) | ||||
|  | ||||
| // ConfigDetails are the details about a group of ConfigFiles | ||||
| type ConfigDetails struct { | ||||
| 	Version     string | ||||
| 	WorkingDir  string | ||||
| 	ConfigFiles []ConfigFile | ||||
| 	Environment map[string]string | ||||
| } | ||||
|  | ||||
| // LookupEnv provides a lookup function for environment variables | ||||
| func (cd ConfigDetails) LookupEnv(key string) (string, bool) { | ||||
| 	v, ok := cd.Environment[key] | ||||
| 	if !ok { | ||||
| 		logrus.Warnf("The %s variable is not set. Defaulting to a blank string.", key) | ||||
| 	} | ||||
| 	return v, ok | ||||
| } | ||||
|  | ||||
| // ConfigFile is a filename and the contents of the file as a Dict | ||||
| type ConfigFile struct { | ||||
| 	// Filename is the name of the yaml configuration file | ||||
| 	Filename string | ||||
| 	// Content is the raw yaml content. Will be loaded from Filename if not set | ||||
| 	Content []byte | ||||
| 	// Config if the yaml tree for this config file. Will be parsed from Content if not set | ||||
| 	Config map[string]interface{} | ||||
| } | ||||
|  | ||||
| // Config is a full compose file configuration and model | ||||
| type Config struct { | ||||
| 	Filename   string     `yaml:"-" json:"-"` | ||||
| 	Services   Services   `json:"services"` | ||||
| 	Networks   Networks   `yaml:",omitempty" json:"networks,omitempty"` | ||||
| 	Volumes    Volumes    `yaml:",omitempty" json:"volumes,omitempty"` | ||||
| 	Secrets    Secrets    `yaml:",omitempty" json:"secrets,omitempty"` | ||||
| 	Configs    Configs    `yaml:",omitempty" json:"configs,omitempty"` | ||||
| 	Extensions Extensions `yaml:",inline" json:"-"` | ||||
| } | ||||
|  | ||||
| // Volumes is a map of VolumeConfig | ||||
| type Volumes map[string]VolumeConfig | ||||
|  | ||||
| // Networks is a map of NetworkConfig | ||||
| type Networks map[string]NetworkConfig | ||||
|  | ||||
| // Secrets is a map of SecretConfig | ||||
| type Secrets map[string]SecretConfig | ||||
|  | ||||
| // Configs is a map of ConfigObjConfig | ||||
| type Configs map[string]ConfigObjConfig | ||||
|  | ||||
| // Extensions is a map of custom extension | ||||
| type Extensions map[string]interface{} | ||||
|  | ||||
| // MarshalJSON makes Config implement json.Marshaler | ||||
| func (c Config) MarshalJSON() ([]byte, error) { | ||||
| 	m := map[string]interface{}{ | ||||
| 		"services": c.Services, | ||||
| 	} | ||||
|  | ||||
| 	if len(c.Networks) > 0 { | ||||
| 		m["networks"] = c.Networks | ||||
| 	} | ||||
| 	if len(c.Volumes) > 0 { | ||||
| 		m["volumes"] = c.Volumes | ||||
| 	} | ||||
| 	if len(c.Secrets) > 0 { | ||||
| 		m["secrets"] = c.Secrets | ||||
| 	} | ||||
| 	if len(c.Configs) > 0 { | ||||
| 		m["configs"] = c.Configs | ||||
| 	} | ||||
| 	for k, v := range c.Extensions { | ||||
| 		m[k] = v | ||||
| 	} | ||||
| 	return json.Marshal(m) | ||||
| } | ||||
|  | ||||
| func (e Extensions) Get(name string, target interface{}) (bool, error) { | ||||
| 	if v, ok := e[name]; ok { | ||||
| 		err := mapstructure.Decode(v, target) | ||||
| 		return true, err | ||||
| 	} | ||||
| 	return false, nil | ||||
| } | ||||
							
								
								
									
										356
									
								
								vendor/github.com/compose-spec/compose-go/types/project.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										356
									
								
								vendor/github.com/compose-spec/compose-go/types/project.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,356 @@ | ||||
| /* | ||||
|    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 types | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"sort" | ||||
|  | ||||
| 	"github.com/distribution/distribution/v3/reference" | ||||
| 	"github.com/opencontainers/go-digest" | ||||
| 	"golang.org/x/sync/errgroup" | ||||
| ) | ||||
|  | ||||
| // Project is the result of loading a set of compose files | ||||
| type Project struct { | ||||
| 	Name         string            `yaml:"-" json:"-"` | ||||
| 	WorkingDir   string            `yaml:"-" json:"-"` | ||||
| 	Services     Services          `json:"services"` | ||||
| 	Networks     Networks          `yaml:",omitempty" json:"networks,omitempty"` | ||||
| 	Volumes      Volumes           `yaml:",omitempty" json:"volumes,omitempty"` | ||||
| 	Secrets      Secrets           `yaml:",omitempty" json:"secrets,omitempty"` | ||||
| 	Configs      Configs           `yaml:",omitempty" json:"configs,omitempty"` | ||||
| 	Extensions   Extensions        `yaml:",inline" json:"-"` // https://github.com/golang/go/issues/6213 | ||||
| 	ComposeFiles []string          `yaml:"-" json:"-"` | ||||
| 	Environment  map[string]string `yaml:"-" json:"-"` | ||||
|  | ||||
| 	// DisabledServices track services which have been disable as profile is not active | ||||
| 	DisabledServices Services `yaml:"-" json:"-"` | ||||
| } | ||||
|  | ||||
| // ServiceNames return names for all services in this Compose config | ||||
| func (p Project) ServiceNames() []string { | ||||
| 	names := []string{} | ||||
| 	for _, s := range p.Services { | ||||
| 		names = append(names, s.Name) | ||||
| 	} | ||||
| 	sort.Strings(names) | ||||
| 	return names | ||||
| } | ||||
|  | ||||
| // VolumeNames return names for all volumes in this Compose config | ||||
| func (p Project) VolumeNames() []string { | ||||
| 	names := []string{} | ||||
| 	for k := range p.Volumes { | ||||
| 		names = append(names, k) | ||||
| 	} | ||||
| 	sort.Strings(names) | ||||
| 	return names | ||||
| } | ||||
|  | ||||
| // NetworkNames return names for all volumes in this Compose config | ||||
| func (p Project) NetworkNames() []string { | ||||
| 	names := []string{} | ||||
| 	for k := range p.Networks { | ||||
| 		names = append(names, k) | ||||
| 	} | ||||
| 	sort.Strings(names) | ||||
| 	return names | ||||
| } | ||||
|  | ||||
| // SecretNames return names for all secrets in this Compose config | ||||
| func (p Project) SecretNames() []string { | ||||
| 	names := []string{} | ||||
| 	for k := range p.Secrets { | ||||
| 		names = append(names, k) | ||||
| 	} | ||||
| 	sort.Strings(names) | ||||
| 	return names | ||||
| } | ||||
|  | ||||
| // ConfigNames return names for all configs in this Compose config | ||||
| func (p Project) ConfigNames() []string { | ||||
| 	names := []string{} | ||||
| 	for k := range p.Configs { | ||||
| 		names = append(names, k) | ||||
| 	} | ||||
| 	sort.Strings(names) | ||||
| 	return names | ||||
| } | ||||
|  | ||||
| func (p Project) GetByContainerName(names ...string) (Services, error) { | ||||
| 	if len(names) == 0 { | ||||
| 		return p.Services, nil | ||||
| 	} | ||||
| 	services := Services{} | ||||
| outLoop: | ||||
| 	for _, name := range names { | ||||
| 		for _, s := range p.Services { | ||||
| 			if name == s.ContainerName { | ||||
| 				services = append(services, s) | ||||
| 				continue outLoop | ||||
| 			} | ||||
| 		} | ||||
| 		return nil, fmt.Errorf("service with container_name %q could not be found", name) | ||||
| 	} | ||||
| 	return services, nil | ||||
| } | ||||
|  | ||||
| // 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 { | ||||
| 		return p.Services, nil | ||||
| 	} | ||||
| 	services := Services{} | ||||
| 	for _, name := range names { | ||||
| 		var serviceConfig *ServiceConfig | ||||
| 		for _, s := range p.Services { | ||||
| 			if s.Name == name { | ||||
| 				serviceConfig = &s | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		if serviceConfig == nil { | ||||
| 			return services, fmt.Errorf("no such service: %s", name) | ||||
| 		} | ||||
| 		services = append(services, *serviceConfig) | ||||
| 	} | ||||
| 	return services, nil | ||||
| } | ||||
|  | ||||
| // GetService retrieve a specific service by name | ||||
| func (p Project) GetService(name string) (ServiceConfig, error) { | ||||
| 	services, err := p.GetServices(name) | ||||
| 	if err != nil { | ||||
| 		return ServiceConfig{}, err | ||||
| 	} | ||||
| 	if len(services) == 0 { | ||||
| 		return ServiceConfig{}, fmt.Errorf("no such service: %s", name) | ||||
| 	} | ||||
| 	return services[0], nil | ||||
| } | ||||
|  | ||||
| func (p Project) AllServices() Services { | ||||
| 	var all Services | ||||
| 	all = append(all, p.Services...) | ||||
| 	all = append(all, p.DisabledServices...) | ||||
| 	return all | ||||
| } | ||||
|  | ||||
| type ServiceFunc func(service ServiceConfig) error | ||||
|  | ||||
| // WithServices run ServiceFunc on each service and dependencies in dependency order | ||||
| func (p Project) WithServices(names []string, fn ServiceFunc) error { | ||||
| 	return p.withServices(names, fn, map[string]bool{}) | ||||
| } | ||||
|  | ||||
| func (p Project) withServices(names []string, fn ServiceFunc, done map[string]bool) error { | ||||
| 	services, err := p.GetServices(names...) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	for _, service := range services { | ||||
| 		if done[service.Name] { | ||||
| 			continue | ||||
| 		} | ||||
| 		dependencies := service.GetDependencies() | ||||
| 		if len(dependencies) > 0 { | ||||
| 			err := p.withServices(dependencies, fn, done) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 		} | ||||
| 		if err := fn(service); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		done[service.Name] = true | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // RelativePath resolve a relative path based project's working directory | ||||
| func (p *Project) RelativePath(path string) string { | ||||
| 	if path[0] == '~' { | ||||
| 		home, _ := os.UserHomeDir() | ||||
| 		path = filepath.Join(home, path[1:]) | ||||
| 	} | ||||
| 	if filepath.IsAbs(path) { | ||||
| 		return path | ||||
| 	} | ||||
| 	return filepath.Join(p.WorkingDir, path) | ||||
| } | ||||
|  | ||||
| // HasProfile return true if service has no profile declared or has at least one profile matching | ||||
| func (service ServiceConfig) HasProfile(profiles []string) bool { | ||||
| 	if len(service.Profiles) == 0 { | ||||
| 		return true | ||||
| 	} | ||||
| 	for _, p := range profiles { | ||||
| 		for _, sp := range service.Profiles { | ||||
| 			if sp == p { | ||||
| 				return true | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| // GetProfiles retrieve the profiles implicitly enabled by explicitly targeting selected services | ||||
| func (s Services) GetProfiles() []string { | ||||
| 	set := map[string]struct{}{} | ||||
| 	for _, service := range s { | ||||
| 		for _, p := range service.Profiles { | ||||
| 			set[p] = struct{}{} | ||||
| 		} | ||||
| 	} | ||||
| 	var profiles []string | ||||
| 	for k := range set { | ||||
| 		profiles = append(profiles, k) | ||||
| 	} | ||||
| 	return profiles | ||||
| } | ||||
|  | ||||
| // ApplyProfiles disables service which don't match selected profiles | ||||
| func (p *Project) ApplyProfiles(profiles []string) { | ||||
| 	var enabled, disabled Services | ||||
| 	for _, service := range p.Services { | ||||
| 		if service.HasProfile(profiles) { | ||||
| 			enabled = append(enabled, service) | ||||
| 		} else { | ||||
| 			disabled = append(disabled, service) | ||||
| 		} | ||||
| 	} | ||||
| 	p.Services = enabled | ||||
| 	p.DisabledServices = disabled | ||||
| } | ||||
|  | ||||
| // WithoutUnnecessaryResources drops networks/volumes/secrets/configs that are not referenced by active services | ||||
| func (p *Project) WithoutUnnecessaryResources() { | ||||
| 	requiredNetworks := map[string]struct{}{} | ||||
| 	requiredVolumes := map[string]struct{}{} | ||||
| 	requiredSecrets := map[string]struct{}{} | ||||
| 	requiredConfigs := map[string]struct{}{} | ||||
| 	for _, s := range p.Services { | ||||
| 		for k := range s.Networks { | ||||
| 			requiredNetworks[k] = struct{}{} | ||||
| 		} | ||||
| 		for _, v := range s.Volumes { | ||||
| 			if v.Type != VolumeTypeVolume || v.Source == "" { | ||||
| 				continue | ||||
| 			} | ||||
| 			requiredVolumes[v.Source] = struct{}{} | ||||
| 		} | ||||
| 		for _, v := range s.Secrets { | ||||
| 			requiredSecrets[v.Source] = struct{}{} | ||||
| 		} | ||||
| 		for _, v := range s.Configs { | ||||
| 			requiredConfigs[v.Source] = struct{}{} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	networks := Networks{} | ||||
| 	for k := range requiredNetworks { | ||||
| 		networks[k] = p.Networks[k] | ||||
| 	} | ||||
| 	p.Networks = networks | ||||
|  | ||||
| 	volumes := Volumes{} | ||||
| 	for k := range requiredVolumes { | ||||
| 		volumes[k] = p.Volumes[k] | ||||
| 	} | ||||
| 	p.Volumes = volumes | ||||
|  | ||||
| 	secrets := Secrets{} | ||||
| 	for k := range requiredSecrets { | ||||
| 		secrets[k] = p.Secrets[k] | ||||
| 	} | ||||
| 	p.Secrets = secrets | ||||
|  | ||||
| 	configs := Configs{} | ||||
| 	for k := range requiredConfigs { | ||||
| 		configs[k] = p.Configs[k] | ||||
| 	} | ||||
| 	p.Configs = configs | ||||
| } | ||||
|  | ||||
| // ForServices restrict the project model to a subset of services | ||||
| func (p *Project) ForServices(names []string) error { | ||||
| 	if len(names) == 0 { | ||||
| 		// All services | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	set := map[string]struct{}{} | ||||
| 	err := p.WithServices(names, func(service ServiceConfig) error { | ||||
| 		set[service.Name] = struct{}{} | ||||
| 		return nil | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	// Disable all services which are not explicit target or dependencies | ||||
| 	var enabled Services | ||||
| 	for _, s := range p.Services { | ||||
| 		if _, ok := set[s.Name]; ok { | ||||
| 			enabled = append(enabled, s) | ||||
| 		} else { | ||||
| 			p.DisabledServices = append(p.DisabledServices, s) | ||||
| 		} | ||||
| 	} | ||||
| 	p.Services = enabled | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // ResolveImages updates services images to include digest computed by a resolver function | ||||
| func (p *Project) ResolveImages(resolver func(named reference.Named) (digest.Digest, error)) error { | ||||
| 	eg := errgroup.Group{} | ||||
| 	for i, s := range p.Services { | ||||
| 		idx := i | ||||
| 		service := s | ||||
|  | ||||
| 		if service.Image == "" { | ||||
| 			continue | ||||
| 		} | ||||
| 		eg.Go(func() error { | ||||
| 			named, err := reference.ParseDockerRef(service.Image) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
|  | ||||
| 			if _, ok := named.(reference.Canonical); !ok { | ||||
| 				// image is named but not digested reference | ||||
| 				digest, err := resolver(named) | ||||
| 				if err != nil { | ||||
| 					return err | ||||
| 				} | ||||
|  | ||||
| 				named, err = reference.WithDigest(named, digest) | ||||
| 				if err != nil { | ||||
| 					return err | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			service.Image = named.String() | ||||
| 			p.Services[idx] = service | ||||
| 			return nil | ||||
| 		}) | ||||
| 	} | ||||
| 	return eg.Wait() | ||||
| } | ||||
							
								
								
									
										830
									
								
								vendor/github.com/compose-spec/compose-go/types/types.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										830
									
								
								vendor/github.com/compose-spec/compose-go/types/types.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,830 @@ | ||||
| /* | ||||
|    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 types | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"sort" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/docker/go-connections/nat" | ||||
| ) | ||||
|  | ||||
| // Duration is a thin wrapper around time.Duration with improved JSON marshalling | ||||
| type Duration time.Duration | ||||
|  | ||||
| func (d Duration) String() string { | ||||
| 	return time.Duration(d).String() | ||||
| } | ||||
|  | ||||
| // ConvertDurationPtr converts a typedefined Duration pointer to a time.Duration pointer with the same value. | ||||
| func ConvertDurationPtr(d *Duration) *time.Duration { | ||||
| 	if d == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	res := time.Duration(*d) | ||||
| 	return &res | ||||
| } | ||||
|  | ||||
| // MarshalJSON makes Duration implement json.Marshaler | ||||
| func (d Duration) MarshalJSON() ([]byte, error) { | ||||
| 	return json.Marshal(d.String()) | ||||
| } | ||||
|  | ||||
| // MarshalYAML makes Duration implement yaml.Marshaler | ||||
| func (d Duration) MarshalYAML() (interface{}, error) { | ||||
| 	return d.String(), nil | ||||
| } | ||||
|  | ||||
| func (d *Duration) UnmarshalJSON(b []byte) error { | ||||
| 	s := strings.Trim(string(b), "\"") | ||||
| 	timeDuration, err := time.ParseDuration(s) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	*d = Duration(timeDuration) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Services is a list of ServiceConfig | ||||
| type Services []ServiceConfig | ||||
|  | ||||
| // MarshalYAML makes Services implement yaml.Marshaller | ||||
| func (s Services) MarshalYAML() (interface{}, error) { | ||||
| 	services := map[string]ServiceConfig{} | ||||
| 	for _, service := range s { | ||||
| 		services[service.Name] = service | ||||
| 	} | ||||
| 	return services, nil | ||||
| } | ||||
|  | ||||
| // MarshalJSON makes Services implement json.Marshaler | ||||
| func (s Services) MarshalJSON() ([]byte, error) { | ||||
| 	data, err := s.MarshalYAML() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return json.MarshalIndent(data, "", "  ") | ||||
| } | ||||
|  | ||||
| // ServiceConfig is the configuration of one service | ||||
| type ServiceConfig struct { | ||||
| 	Name     string   `yaml:"-" json:"-"` | ||||
| 	Profiles []string `mapstructure:"profiles" yaml:"profiles,omitempty" json:"profiles,omitempty"` | ||||
|  | ||||
| 	Build           *BuildConfig                     `yaml:",omitempty" json:"build,omitempty"` | ||||
| 	BlkioConfig     *BlkioConfig                     `yaml:",omitempty" json:"blkio_config,omitempty"` | ||||
| 	CapAdd          []string                         `mapstructure:"cap_add" yaml:"cap_add,omitempty" json:"cap_add,omitempty"` | ||||
| 	CapDrop         []string                         `mapstructure:"cap_drop" yaml:"cap_drop,omitempty" json:"cap_drop,omitempty"` | ||||
| 	CgroupParent    string                           `mapstructure:"cgroup_parent" yaml:"cgroup_parent,omitempty" json:"cgroup_parent,omitempty"` | ||||
| 	CPUCount        int64                            `mapstructure:"cpu_count" yaml:"cpu_count,omitempty" json:"cpu_count,omitempty"` | ||||
| 	CPUPercent      float32                          `mapstructure:"cpu_percent" yaml:"cpu_percent,omitempty" json:"cpu_percent,omitempty"` | ||||
| 	CPUPeriod       int64                            `mapstructure:"cpu_period" yaml:"cpu_period,omitempty" json:"cpu_period,omitempty"` | ||||
| 	CPUQuota        int64                            `mapstructure:"cpu_quota" yaml:"cpu_quota,omitempty" json:"cpu_quota,omitempty"` | ||||
| 	CPURTPeriod     int64                            `mapstructure:"cpu_rt_period" yaml:"cpu_rt_period,omitempty" json:"cpu_rt_period,omitempty"` | ||||
| 	CPURTRuntime    int64                            `mapstructure:"cpu_rt_runtime" yaml:"cpu_rt_runtime,omitempty" json:"cpu_rt_runtime,omitempty"` | ||||
| 	CPUS            float32                          `mapstructure:"cpus" yaml:"cpus,omitempty" json:"cpus,omitempty"` | ||||
| 	CPUSet          string                           `mapstructure:"cpuset" yaml:"cpuset,omitempty" json:"cpuset,omitempty"` | ||||
| 	CPUShares       int64                            `mapstructure:"cpu_shares" yaml:"cpu_shares,omitempty" json:"cpu_shares,omitempty"` | ||||
| 	Command         ShellCommand                     `yaml:",omitempty" json:"command,omitempty"` | ||||
| 	Configs         []ServiceConfigObjConfig         `yaml:",omitempty" json:"configs,omitempty"` | ||||
| 	ContainerName   string                           `mapstructure:"container_name" yaml:"container_name,omitempty" json:"container_name,omitempty"` | ||||
| 	CredentialSpec  *CredentialSpecConfig            `mapstructure:"credential_spec" yaml:"credential_spec,omitempty" json:"credential_spec,omitempty"` | ||||
| 	DependsOn       DependsOnConfig                  `mapstructure:"depends_on" yaml:"depends_on,omitempty" json:"depends_on,omitempty"` | ||||
| 	Deploy          *DeployConfig                    `yaml:",omitempty" json:"deploy,omitempty"` | ||||
| 	Devices         []string                         `yaml:",omitempty" json:"devices,omitempty"` | ||||
| 	DNS             StringList                       `yaml:",omitempty" json:"dns,omitempty"` | ||||
| 	DNSOpts         []string                         `mapstructure:"dns_opt" yaml:"dns_opt,omitempty" json:"dns_opt,omitempty"` | ||||
| 	DNSSearch       StringList                       `mapstructure:"dns_search" yaml:"dns_search,omitempty" json:"dns_search,omitempty"` | ||||
| 	Dockerfile      string                           `yaml:"dockerfile,omitempty" json:"dockerfile,omitempty"` | ||||
| 	DomainName      string                           `mapstructure:"domainname" yaml:"domainname,omitempty" json:"domainname,omitempty"` | ||||
| 	Entrypoint      ShellCommand                     `yaml:",omitempty" json:"entrypoint,omitempty"` | ||||
| 	Environment     MappingWithEquals                `yaml:",omitempty" json:"environment,omitempty"` | ||||
| 	EnvFile         StringList                       `mapstructure:"env_file" yaml:"env_file,omitempty" json:"env_file,omitempty"` | ||||
| 	Expose          StringOrNumberList               `yaml:",omitempty" json:"expose,omitempty"` | ||||
| 	Extends         ExtendsConfig                    `yaml:"extends,omitempty" json:"extends,omitempty"` | ||||
| 	ExternalLinks   []string                         `mapstructure:"external_links" yaml:"external_links,omitempty" json:"external_links,omitempty"` | ||||
| 	ExtraHosts      HostsList                        `mapstructure:"extra_hosts" yaml:"extra_hosts,omitempty" json:"extra_hosts,omitempty"` | ||||
| 	GroupAdd        []string                         `mapstructure:"group_app" yaml:"group_add,omitempty" json:"group_add,omitempty"` | ||||
| 	Hostname        string                           `yaml:",omitempty" json:"hostname,omitempty"` | ||||
| 	HealthCheck     *HealthCheckConfig               `yaml:",omitempty" json:"healthcheck,omitempty"` | ||||
| 	Image           string                           `yaml:",omitempty" json:"image,omitempty"` | ||||
| 	Init            *bool                            `yaml:",omitempty" json:"init,omitempty"` | ||||
| 	Ipc             string                           `yaml:",omitempty" json:"ipc,omitempty"` | ||||
| 	Isolation       string                           `mapstructure:"isolation" yaml:"isolation,omitempty" json:"isolation,omitempty"` | ||||
| 	Labels          Labels                           `yaml:",omitempty" json:"labels,omitempty"` | ||||
| 	Links           []string                         `yaml:",omitempty" json:"links,omitempty"` | ||||
| 	Logging         *LoggingConfig                   `yaml:",omitempty" json:"logging,omitempty"` | ||||
| 	LogDriver       string                           `mapstructure:"log_driver" yaml:"log_driver,omitempty" json:"log_driver,omitempty"` | ||||
| 	LogOpt          map[string]string                `mapstructure:"log_opt" yaml:"log_opt,omitempty" json:"log_opt,omitempty"` | ||||
| 	MemLimit        UnitBytes                        `mapstructure:"mem_limit" yaml:"mem_limit,omitempty" json:"mem_limit,omitempty"` | ||||
| 	MemReservation  UnitBytes                        `mapstructure:"mem_reservation" yaml:"mem_reservation,omitempty" json:"mem_reservation,omitempty"` | ||||
| 	MemSwapLimit    UnitBytes                        `mapstructure:"memswap_limit" yaml:"memswap_limit,omitempty" json:"memswap_limit,omitempty"` | ||||
| 	MemSwappiness   UnitBytes                        `mapstructure:"mem_swappiness" yaml:"mem_swappiness,omitempty" json:"mem_swappiness,omitempty"` | ||||
| 	MacAddress      string                           `mapstructure:"mac_address" yaml:"mac_address,omitempty" json:"mac_address,omitempty"` | ||||
| 	Net             string                           `yaml:"net,omitempty" json:"net,omitempty"` | ||||
| 	NetworkMode     string                           `mapstructure:"network_mode" yaml:"network_mode,omitempty" json:"network_mode,omitempty"` | ||||
| 	Networks        map[string]*ServiceNetworkConfig `yaml:",omitempty" json:"networks,omitempty"` | ||||
| 	OomKillDisable  bool                             `mapstructure:"oom_kill_disable" yaml:"oom_kill_disable,omitempty" json:"oom_kill_disable,omitempty"` | ||||
| 	OomScoreAdj     int64                            `mapstructure:"oom_score_adj" yaml:"oom_score_adj,omitempty" json:"oom_score_adj,omitempty"` | ||||
| 	Pid             string                           `yaml:",omitempty" json:"pid,omitempty"` | ||||
| 	PidsLimit       int64                            `mapstructure:"pids_limit" yaml:"pids_limit,omitempty" json:"pids_limit,omitempty"` | ||||
| 	Platform        string                           `yaml:",omitempty" json:"platform,omitempty"` | ||||
| 	Ports           []ServicePortConfig              `yaml:",omitempty" json:"ports,omitempty"` | ||||
| 	Privileged      bool                             `yaml:",omitempty" json:"privileged,omitempty"` | ||||
| 	PullPolicy      string                           `mapstructure:"pull_policy" yaml:"pull_policy,omitempty" json:"pull_policy,omitempty"` | ||||
| 	ReadOnly        bool                             `mapstructure:"read_only" yaml:"read_only,omitempty" json:"read_only,omitempty"` | ||||
| 	Restart         string                           `yaml:",omitempty" json:"restart,omitempty"` | ||||
| 	Runtime         string                           `yaml:",omitempty" json:"runtime,omitempty"` | ||||
| 	Scale           int                              `yaml:",omitempty" json:"scale,omitempty"` | ||||
| 	Secrets         []ServiceSecretConfig            `yaml:",omitempty" json:"secrets,omitempty"` | ||||
| 	SecurityOpt     []string                         `mapstructure:"security_opt" yaml:"security_opt,omitempty" json:"security_opt,omitempty"` | ||||
| 	ShmSize         UnitBytes                        `mapstructure:"shm_size" yaml:"shm_size,omitempty" json:"shm_size,omitempty"` | ||||
| 	StdinOpen       bool                             `mapstructure:"stdin_open" yaml:"stdin_open,omitempty" json:"stdin_open,omitempty"` | ||||
| 	StopGracePeriod *Duration                        `mapstructure:"stop_grace_period" yaml:"stop_grace_period,omitempty" json:"stop_grace_period,omitempty"` | ||||
| 	StopSignal      string                           `mapstructure:"stop_signal" yaml:"stop_signal,omitempty" json:"stop_signal,omitempty"` | ||||
| 	Sysctls         Mapping                          `yaml:",omitempty" json:"sysctls,omitempty"` | ||||
| 	Tmpfs           StringList                       `yaml:",omitempty" json:"tmpfs,omitempty"` | ||||
| 	Tty             bool                             `mapstructure:"tty" yaml:"tty,omitempty" json:"tty,omitempty"` | ||||
| 	Ulimits         map[string]*UlimitsConfig        `yaml:",omitempty" json:"ulimits,omitempty"` | ||||
| 	User            string                           `yaml:",omitempty" json:"user,omitempty"` | ||||
| 	UserNSMode      string                           `mapstructure:"userns_mode" yaml:"userns_mode,omitempty" json:"userns_mode,omitempty"` | ||||
| 	Uts             string                           `yaml:"uts,omitempty" json:"uts,omitempty"` | ||||
| 	VolumeDriver    string                           `mapstructure:"volume_driver" yaml:"volume_driver,omitempty" json:"volume_driver,omitempty"` | ||||
| 	Volumes         []ServiceVolumeConfig            `yaml:",omitempty" json:"volumes,omitempty"` | ||||
| 	VolumesFrom     []string                         `mapstructure:"volumes_from" yaml:"volumes_from,omitempty" json:"volumes_from,omitempty"` | ||||
| 	WorkingDir      string                           `mapstructure:"working_dir" yaml:"working_dir,omitempty" json:"working_dir,omitempty"` | ||||
|  | ||||
| 	Extensions map[string]interface{} `yaml:",inline" json:"-"` | ||||
| } | ||||
|  | ||||
| // NetworksByPriority return the service networks IDs sorted according to Priority | ||||
| func (s *ServiceConfig) NetworksByPriority() []string { | ||||
| 	type key struct { | ||||
| 		name     string | ||||
| 		priority int | ||||
| 	} | ||||
| 	var keys []key | ||||
| 	for k, v := range s.Networks { | ||||
| 		priority := 0 | ||||
| 		if v != nil { | ||||
| 			priority = v.Priority | ||||
| 		} | ||||
| 		keys = append(keys, key{ | ||||
| 			name:     k, | ||||
| 			priority: priority, | ||||
| 		}) | ||||
| 	} | ||||
| 	sort.Slice(keys, func(i, j int) bool { | ||||
| 		return keys[i].priority > keys[j].priority | ||||
| 	}) | ||||
| 	var sorted []string | ||||
| 	for _, k := range keys { | ||||
| 		sorted = append(sorted, k.name) | ||||
| 	} | ||||
| 	return sorted | ||||
| } | ||||
|  | ||||
| const ( | ||||
| 	//PullPolicyAlways always pull images | ||||
| 	PullPolicyAlways = "always" | ||||
| 	//PullPolicyNever never pull images | ||||
| 	PullPolicyNever = "never" | ||||
| 	//PullPolicyIfNotPresent pull missing images | ||||
| 	PullPolicyIfNotPresent = "if_not_present" | ||||
| 	//PullPolicyIfNotPresent pull missing images | ||||
| 	PullPolicyMissing = "missing" | ||||
| 	//PullPolicyBuild force building images | ||||
| 	PullPolicyBuild = "build" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	//RestartPolicyAlways always restart the container if it stops | ||||
| 	RestartPolicyAlways = "always" | ||||
| 	//RestartPolicyOnFailure restart the container if it exits due to an error | ||||
| 	RestartPolicyOnFailure = "on-failure" | ||||
| 	//RestartPolicyNo do not automatically restart the container | ||||
| 	RestartPolicyNo = "no" | ||||
| 	//RestartPolicyUnlessStopped always restart the container unless the container is stopped (manually or otherwise) | ||||
| 	RestartPolicyUnlessStopped = "unless-stopped" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	// NetworkModeServicePrefix is the prefix for network_mode pointing to a service | ||||
| 	NetworkModeServicePrefix = "service:" | ||||
| 	// NetworkModeContainerPrefix is the prefix for network_mode pointing to a container | ||||
| 	NetworkModeContainerPrefix = "container:" | ||||
| ) | ||||
|  | ||||
| // GetDependencies retrieve all services this service depends on | ||||
| func (s ServiceConfig) GetDependencies() []string { | ||||
| 	dependencies := make(set) | ||||
| 	for dependency := range s.DependsOn { | ||||
| 		dependencies.append(dependency) | ||||
| 	} | ||||
| 	for _, link := range s.Links { | ||||
| 		parts := strings.Split(link, ":") | ||||
| 		if len(parts) == 2 { | ||||
| 			dependencies.append(parts[0]) | ||||
| 		} else { | ||||
| 			dependencies.append(link) | ||||
| 		} | ||||
| 	} | ||||
| 	if strings.HasPrefix(s.NetworkMode, NetworkModeServicePrefix) { | ||||
| 		dependencies.append(s.NetworkMode[len(NetworkModeServicePrefix):]) | ||||
| 	} | ||||
| 	return dependencies.toSlice() | ||||
| } | ||||
|  | ||||
| type set map[string]struct{} | ||||
|  | ||||
| func (s set) append(strings ...string) { | ||||
| 	for _, str := range strings { | ||||
| 		s[str] = struct{}{} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (s set) toSlice() []string { | ||||
| 	slice := make([]string, 0, len(s)) | ||||
| 	for v := range s { | ||||
| 		slice = append(slice, v) | ||||
| 	} | ||||
| 	return slice | ||||
| } | ||||
|  | ||||
| // BuildConfig is a type for build | ||||
| type BuildConfig struct { | ||||
| 	Context    string            `yaml:",omitempty" json:"context,omitempty"` | ||||
| 	Dockerfile string            `yaml:",omitempty" json:"dockerfile,omitempty"` | ||||
| 	Args       MappingWithEquals `yaml:",omitempty" json:"args,omitempty"` | ||||
| 	Labels     Labels            `yaml:",omitempty" json:"labels,omitempty"` | ||||
| 	CacheFrom  StringList        `mapstructure:"cache_from" yaml:"cache_from,omitempty" json:"cache_from,omitempty"` | ||||
| 	ExtraHosts HostsList         `mapstructure:"extra_hosts" yaml:"extra_hosts,omitempty" json:"extra_hosts,omitempty"` | ||||
| 	Isolation  string            `yaml:",omitempty" json:"isolation,omitempty"` | ||||
| 	Network    string            `yaml:",omitempty" json:"network,omitempty"` | ||||
| 	Target     string            `yaml:",omitempty" json:"target,omitempty"` | ||||
|  | ||||
| 	Extensions map[string]interface{} `yaml:",inline" json:"-"` | ||||
| } | ||||
|  | ||||
| // BlkioConfig define blkio config | ||||
| type BlkioConfig struct { | ||||
| 	Weight          uint16           `yaml:",omitempty" json:"weight,omitempty"` | ||||
| 	WeightDevice    []WeightDevice   `yaml:",omitempty" json:"weight_device,omitempty"` | ||||
| 	DeviceReadBps   []ThrottleDevice `yaml:",omitempty" json:"device_read_bps,omitempty"` | ||||
| 	DeviceReadIOps  []ThrottleDevice `yaml:",omitempty" json:"device_read_iops,omitempty"` | ||||
| 	DeviceWriteBps  []ThrottleDevice `yaml:",omitempty" json:"device_write_bps,omitempty"` | ||||
| 	DeviceWriteIOps []ThrottleDevice `yaml:",omitempty" json:"device_write_iops,omitempty"` | ||||
|  | ||||
| 	Extensions map[string]interface{} `yaml:",inline" json:"-"` | ||||
| } | ||||
|  | ||||
| // WeightDevice is a structure that holds device:weight pair | ||||
| type WeightDevice struct { | ||||
| 	Path   string | ||||
| 	Weight uint16 | ||||
|  | ||||
| 	Extensions map[string]interface{} `yaml:",inline" json:"-"` | ||||
| } | ||||
|  | ||||
| // ThrottleDevice is a structure that holds device:rate_per_second pair | ||||
| type ThrottleDevice struct { | ||||
| 	Path string | ||||
| 	Rate uint64 | ||||
|  | ||||
| 	Extensions map[string]interface{} `yaml:",inline" json:"-"` | ||||
| } | ||||
|  | ||||
| // ShellCommand is a string or list of string args | ||||
| type ShellCommand []string | ||||
|  | ||||
| // StringList is a type for fields that can be a string or list of strings | ||||
| type StringList []string | ||||
|  | ||||
| // StringOrNumberList is a type for fields that can be a list of strings or | ||||
| // numbers | ||||
| type StringOrNumberList []string | ||||
|  | ||||
| // MappingWithEquals is a mapping type that can be converted from a list of | ||||
| // key[=value] strings. | ||||
| // For the key with an empty value (`key=`), the mapped value is set to a pointer to `""`. | ||||
| // For the key without value (`key`), the mapped value is set to nil. | ||||
| type MappingWithEquals map[string]*string | ||||
|  | ||||
| // NewMappingWithEquals build a new Mapping from a set of KEY=VALUE strings | ||||
| func NewMappingWithEquals(values []string) MappingWithEquals { | ||||
| 	mapping := MappingWithEquals{} | ||||
| 	for _, env := range values { | ||||
| 		tokens := strings.SplitN(env, "=", 2) | ||||
| 		if len(tokens) > 1 { | ||||
| 			mapping[tokens[0]] = &tokens[1] | ||||
| 		} else { | ||||
| 			mapping[env] = nil | ||||
| 		} | ||||
| 	} | ||||
| 	return mapping | ||||
| } | ||||
|  | ||||
| // OverrideBy update MappingWithEquals with values from another MappingWithEquals | ||||
| func (e MappingWithEquals) OverrideBy(other MappingWithEquals) MappingWithEquals { | ||||
| 	for k, v := range other { | ||||
| 		e[k] = v | ||||
| 	} | ||||
| 	return e | ||||
| } | ||||
|  | ||||
| // Resolve update a MappingWithEquals for keys without value (`key`, but not `key=`) | ||||
| func (e MappingWithEquals) Resolve(lookupFn func(string) (string, bool)) MappingWithEquals { | ||||
| 	for k, v := range e { | ||||
| 		if v == nil || *v == "" { | ||||
| 			if value, ok := lookupFn(k); ok { | ||||
| 				e[k] = &value | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return e | ||||
| } | ||||
|  | ||||
| // RemoveEmpty excludes keys that are not associated with a value | ||||
| func (e MappingWithEquals) RemoveEmpty() MappingWithEquals { | ||||
| 	for k, v := range e { | ||||
| 		if v == nil { | ||||
| 			delete(e, k) | ||||
| 		} | ||||
| 	} | ||||
| 	return e | ||||
| } | ||||
|  | ||||
| // Mapping is a mapping type that can be converted from a list of | ||||
| // key[=value] strings. | ||||
| // For the key with an empty value (`key=`), or key without value (`key`), the | ||||
| // mapped value is set to an empty string `""`. | ||||
| type Mapping map[string]string | ||||
|  | ||||
| // NewMapping build a new Mapping from a set of KEY=VALUE strings | ||||
| func NewMapping(values []string) Mapping { | ||||
| 	mapping := Mapping{} | ||||
| 	for _, value := range values { | ||||
| 		parts := strings.SplitN(value, "=", 2) | ||||
| 		key := parts[0] | ||||
| 		switch { | ||||
| 		case len(parts) == 1: | ||||
| 			mapping[key] = "" | ||||
| 		default: | ||||
| 			mapping[key] = parts[1] | ||||
| 		} | ||||
| 	} | ||||
| 	return mapping | ||||
| } | ||||
|  | ||||
| // Labels is a mapping type for labels | ||||
| type Labels map[string]string | ||||
|  | ||||
| func (l Labels) Add(key, value string) Labels { | ||||
| 	if l == nil { | ||||
| 		l = Labels{} | ||||
| 	} | ||||
| 	l[key] = value | ||||
| 	return l | ||||
| } | ||||
|  | ||||
| // MappingWithColon is a mapping type that can be converted from a list of | ||||
| // 'key: value' strings | ||||
| type MappingWithColon map[string]string | ||||
|  | ||||
| // HostsList is a list of colon-separated host-ip mappings | ||||
| type HostsList []string | ||||
|  | ||||
| // LoggingConfig the logging configuration for a service | ||||
| type LoggingConfig struct { | ||||
| 	Driver  string            `yaml:",omitempty" json:"driver,omitempty"` | ||||
| 	Options map[string]string `yaml:",omitempty" json:"options,omitempty"` | ||||
|  | ||||
| 	Extensions map[string]interface{} `yaml:",inline" json:"-"` | ||||
| } | ||||
|  | ||||
| // DeployConfig the deployment configuration for a service | ||||
| type DeployConfig struct { | ||||
| 	Mode           string         `yaml:",omitempty" json:"mode,omitempty"` | ||||
| 	Replicas       *uint64        `yaml:",omitempty" json:"replicas,omitempty"` | ||||
| 	Labels         Labels         `yaml:",omitempty" json:"labels,omitempty"` | ||||
| 	UpdateConfig   *UpdateConfig  `mapstructure:"update_config" yaml:"update_config,omitempty" json:"update_config,omitempty"` | ||||
| 	RollbackConfig *UpdateConfig  `mapstructure:"rollback_config" yaml:"rollback_config,omitempty" json:"rollback_config,omitempty"` | ||||
| 	Resources      Resources      `yaml:",omitempty" json:"resources,omitempty"` | ||||
| 	RestartPolicy  *RestartPolicy `mapstructure:"restart_policy" yaml:"restart_policy,omitempty" json:"restart_policy,omitempty"` | ||||
| 	Placement      Placement      `yaml:",omitempty" json:"placement,omitempty"` | ||||
| 	EndpointMode   string         `mapstructure:"endpoint_mode" yaml:"endpoint_mode,omitempty" json:"endpoint_mode,omitempty"` | ||||
|  | ||||
| 	Extensions map[string]interface{} `yaml:",inline" json:"-"` | ||||
| } | ||||
|  | ||||
| // HealthCheckConfig the healthcheck configuration for a service | ||||
| type HealthCheckConfig struct { | ||||
| 	Test        HealthCheckTest `yaml:",omitempty" json:"test,omitempty"` | ||||
| 	Timeout     *Duration       `yaml:",omitempty" json:"timeout,omitempty"` | ||||
| 	Interval    *Duration       `yaml:",omitempty" json:"interval,omitempty"` | ||||
| 	Retries     *uint64         `yaml:",omitempty" json:"retries,omitempty"` | ||||
| 	StartPeriod *Duration       `mapstructure:"start_period" yaml:"start_period,omitempty" json:"start_period,omitempty"` | ||||
| 	Disable     bool            `yaml:",omitempty" json:"disable,omitempty"` | ||||
|  | ||||
| 	Extensions map[string]interface{} `yaml:",inline" json:"-"` | ||||
| } | ||||
|  | ||||
| // HealthCheckTest is the command run to test the health of a service | ||||
| type HealthCheckTest []string | ||||
|  | ||||
| // UpdateConfig the service update configuration | ||||
| type UpdateConfig struct { | ||||
| 	Parallelism     *uint64  `yaml:",omitempty" json:"parallelism,omitempty"` | ||||
| 	Delay           Duration `yaml:",omitempty" json:"delay,omitempty"` | ||||
| 	FailureAction   string   `mapstructure:"failure_action" yaml:"failure_action,omitempty" json:"failure_action,omitempty"` | ||||
| 	Monitor         Duration `yaml:",omitempty" json:"monitor,omitempty"` | ||||
| 	MaxFailureRatio float32  `mapstructure:"max_failure_ratio" yaml:"max_failure_ratio,omitempty" json:"max_failure_ratio,omitempty"` | ||||
| 	Order           string   `yaml:",omitempty" json:"order,omitempty"` | ||||
|  | ||||
| 	Extensions map[string]interface{} `yaml:",inline" json:"-"` | ||||
| } | ||||
|  | ||||
| // Resources the resource limits and reservations | ||||
| type Resources struct { | ||||
| 	Limits       *Resource `yaml:",omitempty" json:"limits,omitempty"` | ||||
| 	Reservations *Resource `yaml:",omitempty" json:"reservations,omitempty"` | ||||
|  | ||||
| 	Extensions map[string]interface{} `yaml:",inline" json:"-"` | ||||
| } | ||||
|  | ||||
| // Resource is a resource to be limited or reserved | ||||
| type Resource struct { | ||||
| 	// TODO: types to convert from units and ratios | ||||
| 	NanoCPUs         string            `mapstructure:"cpus" yaml:"cpus,omitempty" json:"cpus,omitempty"` | ||||
| 	MemoryBytes      UnitBytes         `mapstructure:"memory" yaml:"memory,omitempty" json:"memory,omitempty"` | ||||
| 	Devices          []DeviceRequest   `mapstructure:"devices" yaml:"devices,omitempty" json:"devices,omitempty"` | ||||
| 	GenericResources []GenericResource `mapstructure:"generic_resources" yaml:"generic_resources,omitempty" json:"generic_resources,omitempty"` | ||||
|  | ||||
| 	Extensions map[string]interface{} `yaml:",inline" json:"-"` | ||||
| } | ||||
|  | ||||
| type DeviceRequest struct { | ||||
| 	Capabilities []string `mapstructure:"capabilities" yaml:"capabilities,omitempty" json:"capabilities,omitempty"` | ||||
| 	Driver       string   `mapstructure:"driver" yaml:"driver,omitempty" json:"driver,omitempty"` | ||||
| 	Count        int64    `mapstructure:"count" yaml:"count,omitempty" json:"count,omitempty"` | ||||
| 	IDs          []string `mapstructure:"device_ids" yaml:"device_ids,omitempty" json:"device_ids,omitempty"` | ||||
| } | ||||
|  | ||||
| // GenericResource represents a "user defined" resource which can | ||||
| // only be an integer (e.g: SSD=3) for a service | ||||
| type GenericResource struct { | ||||
| 	DiscreteResourceSpec *DiscreteGenericResource `mapstructure:"discrete_resource_spec" yaml:"discrete_resource_spec,omitempty" json:"discrete_resource_spec,omitempty"` | ||||
|  | ||||
| 	Extensions map[string]interface{} `yaml:",inline" json:"-"` | ||||
| } | ||||
|  | ||||
| // DiscreteGenericResource represents a "user defined" resource which is defined | ||||
| // as an integer | ||||
| // "Kind" is used to describe the Kind of a resource (e.g: "GPU", "FPGA", "SSD", ...) | ||||
| // Value is used to count the resource (SSD=5, HDD=3, ...) | ||||
| type DiscreteGenericResource struct { | ||||
| 	Kind  string `json:"kind"` | ||||
| 	Value int64  `json:"value"` | ||||
|  | ||||
| 	Extensions map[string]interface{} `yaml:",inline" json:"-"` | ||||
| } | ||||
|  | ||||
| // UnitBytes is the bytes type | ||||
| type UnitBytes int64 | ||||
|  | ||||
| // MarshalYAML makes UnitBytes implement yaml.Marshaller | ||||
| func (u UnitBytes) MarshalYAML() (interface{}, error) { | ||||
| 	return fmt.Sprintf("%d", u), nil | ||||
| } | ||||
|  | ||||
| // MarshalJSON makes UnitBytes implement json.Marshaler | ||||
| func (u UnitBytes) MarshalJSON() ([]byte, error) { | ||||
| 	return []byte(fmt.Sprintf(`"%d"`, u)), nil | ||||
| } | ||||
|  | ||||
| // RestartPolicy the service restart policy | ||||
| type RestartPolicy struct { | ||||
| 	Condition   string    `yaml:",omitempty" json:"condition,omitempty"` | ||||
| 	Delay       *Duration `yaml:",omitempty" json:"delay,omitempty"` | ||||
| 	MaxAttempts *uint64   `mapstructure:"max_attempts" yaml:"max_attempts,omitempty" json:"max_attempts,omitempty"` | ||||
| 	Window      *Duration `yaml:",omitempty" json:"window,omitempty"` | ||||
|  | ||||
| 	Extensions map[string]interface{} `yaml:",inline" json:"-"` | ||||
| } | ||||
|  | ||||
| // Placement constraints for the service | ||||
| type Placement struct { | ||||
| 	Constraints []string               `yaml:",omitempty" json:"constraints,omitempty"` | ||||
| 	Preferences []PlacementPreferences `yaml:",omitempty" json:"preferences,omitempty"` | ||||
| 	MaxReplicas uint64                 `mapstructure:"max_replicas_per_node" yaml:"max_replicas_per_node,omitempty" json:"max_replicas_per_node,omitempty"` | ||||
|  | ||||
| 	Extensions map[string]interface{} `yaml:",inline" json:"-"` | ||||
| } | ||||
|  | ||||
| // PlacementPreferences is the preferences for a service placement | ||||
| type PlacementPreferences struct { | ||||
| 	Spread string `yaml:",omitempty" json:"spread,omitempty"` | ||||
|  | ||||
| 	Extensions map[string]interface{} `yaml:",inline" json:"-"` | ||||
| } | ||||
|  | ||||
| // ServiceNetworkConfig is the network configuration for a service | ||||
| type ServiceNetworkConfig struct { | ||||
| 	Priority    int      `yaml:",omitempty" json:"priotirt,omitempty"` | ||||
| 	Aliases     []string `yaml:",omitempty" json:"aliases,omitempty"` | ||||
| 	Ipv4Address string   `mapstructure:"ipv4_address" yaml:"ipv4_address,omitempty" json:"ipv4_address,omitempty"` | ||||
| 	Ipv6Address string   `mapstructure:"ipv6_address" yaml:"ipv6_address,omitempty" json:"ipv6_address,omitempty"` | ||||
|  | ||||
| 	Extensions map[string]interface{} `yaml:",inline" json:"-"` | ||||
| } | ||||
|  | ||||
| // ServicePortConfig is the port configuration for a service | ||||
| type ServicePortConfig struct { | ||||
| 	Mode      string `yaml:",omitempty" json:"mode,omitempty"` | ||||
| 	HostIP    string `yaml:"host_ip,omitempty" json:"host_ip,omitempty"` | ||||
| 	Target    uint32 `yaml:",omitempty" json:"target,omitempty"` | ||||
| 	Published uint32 `yaml:",omitempty" json:"published,omitempty"` | ||||
| 	Protocol  string `yaml:",omitempty" json:"protocol,omitempty"` | ||||
|  | ||||
| 	Extensions map[string]interface{} `yaml:",inline" json:"-"` | ||||
| } | ||||
|  | ||||
| // ParsePortConfig parse short syntax for service port configuration | ||||
| func ParsePortConfig(value string) ([]ServicePortConfig, error) { | ||||
| 	var portConfigs []ServicePortConfig | ||||
| 	ports, portBindings, err := nat.ParsePortSpecs([]string{value}) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	// We need to sort the key of the ports to make sure it is consistent | ||||
| 	keys := []string{} | ||||
| 	for port := range ports { | ||||
| 		keys = append(keys, string(port)) | ||||
| 	} | ||||
| 	sort.Strings(keys) | ||||
|  | ||||
| 	for _, key := range keys { | ||||
| 		port := nat.Port(key) | ||||
| 		converted, err := convertPortToPortConfig(port, portBindings) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		portConfigs = append(portConfigs, converted...) | ||||
| 	} | ||||
| 	return portConfigs, nil | ||||
| } | ||||
|  | ||||
| func convertPortToPortConfig(port nat.Port, portBindings map[nat.Port][]nat.PortBinding) ([]ServicePortConfig, error) { | ||||
| 	portConfigs := []ServicePortConfig{} | ||||
| 	for _, binding := range portBindings[port] { | ||||
| 		startHostPort, endHostPort, err := nat.ParsePortRange(binding.HostPort) | ||||
|  | ||||
| 		if err != nil && binding.HostPort != "" { | ||||
| 			return nil, fmt.Errorf("invalid hostport binding (%s) for port (%s)", binding.HostPort, port.Port()) | ||||
| 		} | ||||
|  | ||||
| 		for i := startHostPort; i <= endHostPort; i++ { | ||||
| 			portConfigs = append(portConfigs, ServicePortConfig{ | ||||
| 				HostIP:    binding.HostIP, | ||||
| 				Protocol:  strings.ToLower(port.Proto()), | ||||
| 				Target:    uint32(port.Int()), | ||||
| 				Published: uint32(i), | ||||
| 				Mode:      "ingress", | ||||
| 			}) | ||||
| 		} | ||||
| 	} | ||||
| 	return portConfigs, nil | ||||
| } | ||||
|  | ||||
| // ServiceVolumeConfig are references to a volume used by a service | ||||
| type ServiceVolumeConfig struct { | ||||
| 	Type        string               `yaml:",omitempty" json:"type,omitempty"` | ||||
| 	Source      string               `yaml:",omitempty" json:"source,omitempty"` | ||||
| 	Target      string               `yaml:",omitempty" json:"target,omitempty"` | ||||
| 	ReadOnly    bool                 `mapstructure:"read_only" yaml:"read_only,omitempty" json:"read_only,omitempty"` | ||||
| 	Consistency string               `yaml:",omitempty" json:"consistency,omitempty"` | ||||
| 	Bind        *ServiceVolumeBind   `yaml:",omitempty" json:"bind,omitempty"` | ||||
| 	Volume      *ServiceVolumeVolume `yaml:",omitempty" json:"volume,omitempty"` | ||||
| 	Tmpfs       *ServiceVolumeTmpfs  `yaml:",omitempty" json:"tmpfs,omitempty"` | ||||
|  | ||||
| 	Extensions map[string]interface{} `yaml:",inline" json:"-"` | ||||
| } | ||||
|  | ||||
| const ( | ||||
| 	// TypeBind is the type for mounting host dir | ||||
| 	VolumeTypeBind = "bind" | ||||
| 	// TypeVolume is the type for remote storage volumes | ||||
| 	VolumeTypeVolume = "volume" | ||||
| 	// TypeTmpfs is the type for mounting tmpfs | ||||
| 	VolumeTypeTmpfs = "tmpfs" | ||||
| 	// TypeNamedPipe is the type for mounting Windows named pipes | ||||
| 	VolumeTypeNamedPipe = "npipe" | ||||
| ) | ||||
|  | ||||
| // ServiceVolumeBind are options for a service volume of type bind | ||||
| type ServiceVolumeBind struct { | ||||
| 	Propagation    string `yaml:",omitempty" json:"propagation,omitempty"` | ||||
| 	CreateHostPath bool   `mapstructure:"create_host_path" yaml:"create_host_path,omitempty" json:"create_host_path,omitempty"` | ||||
|  | ||||
| 	Extensions map[string]interface{} `yaml:",inline" json:"-"` | ||||
| } | ||||
|  | ||||
| // Propagation represents the propagation of a mount. | ||||
| const ( | ||||
| 	// PropagationRPrivate RPRIVATE | ||||
| 	PropagationRPrivate string = "rprivate" | ||||
| 	// PropagationPrivate PRIVATE | ||||
| 	PropagationPrivate string = "private" | ||||
| 	// PropagationRShared RSHARED | ||||
| 	PropagationRShared string = "rshared" | ||||
| 	// PropagationShared SHARED | ||||
| 	PropagationShared string = "shared" | ||||
| 	// PropagationRSlave RSLAVE | ||||
| 	PropagationRSlave string = "rslave" | ||||
| 	// PropagationSlave SLAVE | ||||
| 	PropagationSlave string = "slave" | ||||
| ) | ||||
|  | ||||
| // ServiceVolumeVolume are options for a service volume of type volume | ||||
| type ServiceVolumeVolume struct { | ||||
| 	NoCopy bool `mapstructure:"nocopy" yaml:"nocopy,omitempty" json:"nocopy,omitempty"` | ||||
|  | ||||
| 	Extensions map[string]interface{} `yaml:",inline" json:"-"` | ||||
| } | ||||
|  | ||||
| // ServiceVolumeTmpfs are options for a service volume of type tmpfs | ||||
| type ServiceVolumeTmpfs struct { | ||||
| 	Size int64 `yaml:",omitempty" json:"size,omitempty"` | ||||
|  | ||||
| 	Extensions map[string]interface{} `yaml:",inline" json:"-"` | ||||
| } | ||||
|  | ||||
| // FileReferenceConfig for a reference to a swarm file object | ||||
| type FileReferenceConfig struct { | ||||
| 	Source string  `yaml:",omitempty" json:"source,omitempty"` | ||||
| 	Target string  `yaml:",omitempty" json:"target,omitempty"` | ||||
| 	UID    string  `yaml:",omitempty" json:"uid,omitempty"` | ||||
| 	GID    string  `yaml:",omitempty" json:"gid,omitempty"` | ||||
| 	Mode   *uint32 `yaml:",omitempty" json:"mode,omitempty"` | ||||
|  | ||||
| 	Extensions map[string]interface{} `yaml:",inline" json:"-"` | ||||
| } | ||||
|  | ||||
| // ServiceConfigObjConfig is the config obj configuration for a service | ||||
| type ServiceConfigObjConfig FileReferenceConfig | ||||
|  | ||||
| // ServiceSecretConfig is the secret configuration for a service | ||||
| type ServiceSecretConfig FileReferenceConfig | ||||
|  | ||||
| // UlimitsConfig the ulimit configuration | ||||
| type UlimitsConfig struct { | ||||
| 	Single int `yaml:",omitempty" json:"single,omitempty"` | ||||
| 	Soft   int `yaml:",omitempty" json:"soft,omitempty"` | ||||
| 	Hard   int `yaml:",omitempty" json:"hard,omitempty"` | ||||
|  | ||||
| 	Extensions map[string]interface{} `yaml:",inline" json:"-"` | ||||
| } | ||||
|  | ||||
| // MarshalYAML makes UlimitsConfig implement yaml.Marshaller | ||||
| func (u *UlimitsConfig) MarshalYAML() (interface{}, error) { | ||||
| 	if u.Single != 0 { | ||||
| 		return u.Single, nil | ||||
| 	} | ||||
| 	return u, nil | ||||
| } | ||||
|  | ||||
| // MarshalJSON makes UlimitsConfig implement json.Marshaller | ||||
| func (u *UlimitsConfig) MarshalJSON() ([]byte, error) { | ||||
| 	if u.Single != 0 { | ||||
| 		return json.Marshal(u.Single) | ||||
| 	} | ||||
| 	// Pass as a value to avoid re-entering this method and use the default implementation | ||||
| 	return json.Marshal(*u) | ||||
| } | ||||
|  | ||||
| // NetworkConfig for a network | ||||
| type NetworkConfig struct { | ||||
| 	Name       string                 `yaml:",omitempty" json:"name,omitempty"` | ||||
| 	Driver     string                 `yaml:",omitempty" json:"driver,omitempty"` | ||||
| 	DriverOpts map[string]string      `mapstructure:"driver_opts" yaml:"driver_opts,omitempty" json:"driver_opts,omitempty"` | ||||
| 	Ipam       IPAMConfig             `yaml:",omitempty" json:"ipam,omitempty"` | ||||
| 	External   External               `yaml:",omitempty" json:"external,omitempty"` | ||||
| 	Internal   bool                   `yaml:",omitempty" json:"internal,omitempty"` | ||||
| 	Attachable bool                   `yaml:",omitempty" json:"attachable,omitempty"` | ||||
| 	Labels     Labels                 `yaml:",omitempty" json:"labels,omitempty"` | ||||
| 	Extensions map[string]interface{} `yaml:",inline" json:"-"` | ||||
| } | ||||
|  | ||||
| // IPAMConfig for a network | ||||
| type IPAMConfig struct { | ||||
| 	Driver     string                 `yaml:",omitempty" json:"driver,omitempty"` | ||||
| 	Config     []*IPAMPool            `yaml:",omitempty" json:"config,omitempty"` | ||||
| 	Extensions map[string]interface{} `yaml:",inline" json:"-"` | ||||
| } | ||||
|  | ||||
| // IPAMPool for a network | ||||
| type IPAMPool struct { | ||||
| 	Subnet             string                 `yaml:",omitempty" json:"subnet,omitempty"` | ||||
| 	Gateway            string                 `yaml:",omitempty" json:"gateway,omitempty"` | ||||
| 	IPRange            string                 `mapstructure:"ip_range" yaml:"ip_range,omitempty" json:"ip_range,omitempty"` | ||||
| 	AuxiliaryAddresses map[string]string      `mapstructure:"aux_addresses" yaml:"aux_addresses,omitempty" json:"aux_addresses,omitempty"` | ||||
| 	Extensions         map[string]interface{} `yaml:",inline" json:"-"` | ||||
| } | ||||
|  | ||||
| // VolumeConfig for a volume | ||||
| type VolumeConfig struct { | ||||
| 	Name       string                 `yaml:",omitempty" json:"name,omitempty"` | ||||
| 	Driver     string                 `yaml:",omitempty" json:"driver,omitempty"` | ||||
| 	DriverOpts map[string]string      `mapstructure:"driver_opts" yaml:"driver_opts,omitempty" json:"driver_opts,omitempty"` | ||||
| 	External   External               `yaml:",omitempty" json:"external,omitempty"` | ||||
| 	Labels     Labels                 `yaml:",omitempty" json:"labels,omitempty"` | ||||
| 	Extensions map[string]interface{} `yaml:",inline" json:"-"` | ||||
| } | ||||
|  | ||||
| // External identifies a Volume or Network as a reference to a resource that is | ||||
| // not managed, and should already exist. | ||||
| // External.name is deprecated and replaced by Volume.name | ||||
| type External struct { | ||||
| 	Name       string                 `yaml:",omitempty" json:"name,omitempty"` | ||||
| 	External   bool                   `yaml:",omitempty" json:"external,omitempty"` | ||||
| 	Extensions map[string]interface{} `yaml:",inline" json:"-"` | ||||
| } | ||||
|  | ||||
| // MarshalYAML makes External implement yaml.Marshaller | ||||
| func (e External) MarshalYAML() (interface{}, error) { | ||||
| 	if e.Name == "" { | ||||
| 		return e.External, nil | ||||
| 	} | ||||
| 	return External{Name: e.Name}, nil | ||||
| } | ||||
|  | ||||
| // MarshalJSON makes External implement json.Marshaller | ||||
| func (e External) MarshalJSON() ([]byte, error) { | ||||
| 	if e.Name == "" { | ||||
| 		return []byte(fmt.Sprintf("%v", e.External)), nil | ||||
| 	} | ||||
| 	return []byte(fmt.Sprintf(`{"name": %q}`, e.Name)), nil | ||||
| } | ||||
|  | ||||
| // CredentialSpecConfig for credential spec on Windows | ||||
| type CredentialSpecConfig struct { | ||||
| 	Config     string                 `yaml:",omitempty" json:"config,omitempty"` // Config was added in API v1.40 | ||||
| 	File       string                 `yaml:",omitempty" json:"file,omitempty"` | ||||
| 	Registry   string                 `yaml:",omitempty" json:"registry,omitempty"` | ||||
| 	Extensions map[string]interface{} `yaml:",inline" json:"-"` | ||||
| } | ||||
|  | ||||
| // FileObjectConfig is a config type for a file used by a service | ||||
| type FileObjectConfig struct { | ||||
| 	Name           string                 `yaml:",omitempty" json:"name,omitempty"` | ||||
| 	File           string                 `yaml:",omitempty" json:"file,omitempty"` | ||||
| 	External       External               `yaml:",omitempty" json:"external,omitempty"` | ||||
| 	Labels         Labels                 `yaml:",omitempty" json:"labels,omitempty"` | ||||
| 	Driver         string                 `yaml:",omitempty" json:"driver,omitempty"` | ||||
| 	DriverOpts     map[string]string      `mapstructure:"driver_opts" yaml:"driver_opts,omitempty" json:"driver_opts,omitempty"` | ||||
| 	TemplateDriver string                 `mapstructure:"template_driver" yaml:"template_driver,omitempty" json:"template_driver,omitempty"` | ||||
| 	Extensions     map[string]interface{} `yaml:",inline" json:"-"` | ||||
| } | ||||
|  | ||||
| const ( | ||||
| 	// ServiceConditionCompletedSuccessfully is the type for waiting until a service has completed successfully (exit code 0). | ||||
| 	ServiceConditionCompletedSuccessfully = "service_completed_successfully" | ||||
|  | ||||
| 	// ServiceConditionHealthy is the type for waiting until a service is healthy. | ||||
| 	ServiceConditionHealthy = "service_healthy" | ||||
|  | ||||
| 	// ServiceConditionStarted is the type for waiting until a service has started (default). | ||||
| 	ServiceConditionStarted = "service_started" | ||||
| ) | ||||
|  | ||||
| type DependsOnConfig map[string]ServiceDependency | ||||
|  | ||||
| type ServiceDependency struct { | ||||
| 	Condition  string                 `yaml:",omitempty" json:"condition,omitempty"` | ||||
| 	Extensions map[string]interface{} `yaml:",inline" json:"-"` | ||||
| } | ||||
|  | ||||
| type ExtendsConfig MappingWithEquals | ||||
|  | ||||
| // SecretConfig for a secret | ||||
| type SecretConfig FileObjectConfig | ||||
|  | ||||
| // ConfigObjConfig is the config for the swarm "Config" object | ||||
| type ConfigObjConfig FileObjectConfig | ||||
		Reference in New Issue
	
	Block a user
	 CrazyMax
					CrazyMax