From 634f872d58b24fac74d8e2e2819675df45d45de0 Mon Sep 17 00:00:00 2001 From: Gilbert Gilb's Date: Thu, 9 Jan 2025 09:30:40 +0100 Subject: [PATCH] vendor: github.com/compose-spec/compose-go/v2 v2.4.7 Signed-off-by: N. Le Roux --- .../compose-spec/compose-go/v2/LICENSE | 191 +++++++++ .../compose-spec/compose-go/v2/NOTICE | 2 + .../compose-go/v2/template/template.go | 377 ++++++++++++++++++ .../compose-go/v2/template/variables.go | 158 ++++++++ 4 files changed, 728 insertions(+) create mode 100644 vendor/github.com/compose-spec/compose-go/v2/LICENSE create mode 100644 vendor/github.com/compose-spec/compose-go/v2/NOTICE create mode 100644 vendor/github.com/compose-spec/compose-go/v2/template/template.go create mode 100644 vendor/github.com/compose-spec/compose-go/v2/template/variables.go diff --git a/vendor/github.com/compose-spec/compose-go/v2/LICENSE b/vendor/github.com/compose-spec/compose-go/v2/LICENSE new file mode 100644 index 000000000000..9c8e20ab85c1 --- /dev/null +++ b/vendor/github.com/compose-spec/compose-go/v2/LICENSE @@ -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. diff --git a/vendor/github.com/compose-spec/compose-go/v2/NOTICE b/vendor/github.com/compose-spec/compose-go/v2/NOTICE new file mode 100644 index 000000000000..9c2755477e7d --- /dev/null +++ b/vendor/github.com/compose-spec/compose-go/v2/NOTICE @@ -0,0 +1,2 @@ +The Compose Specification +Copyright 2020 The Compose Specification Authors diff --git a/vendor/github.com/compose-spec/compose-go/v2/template/template.go b/vendor/github.com/compose-spec/compose-go/v2/template/template.go new file mode 100644 index 000000000000..d9483cbdbcea --- /dev/null +++ b/vendor/github.com/compose-spec/compose-go/v2/template/template.go @@ -0,0 +1,377 @@ +/* + 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 ( + "errors" + "fmt" + "regexp" + "sort" + "strings" + + "github.com/sirupsen/logrus" +) + +var delimiter = "\\$" +var substitutionNamed = "[_a-z][_a-z0-9]*" +var substitutionBraced = "[_a-z][_a-z0-9]*(?::?[-+?](.*))?" + +var groupEscaped = "escaped" +var groupNamed = "named" +var groupBraced = "braced" +var groupInvalid = "invalid" + +var patternString = fmt.Sprintf( + "%s(?i:(?P<%s>%s)|(?P<%s>%s)|{(?:(?P<%s>%s)}|(?P<%s>)))", + delimiter, + groupEscaped, delimiter, + groupNamed, substitutionNamed, + groupBraced, substitutionBraced, + groupInvalid, +) + +var DefaultPattern = regexp.MustCompile(patternString) + +// 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) +} + +// MissingRequiredError is returned when a variable template is missing +type MissingRequiredError struct { + Variable string + Reason string +} + +func (e MissingRequiredError) Error() string { + if e.Reason != "" { + return fmt.Sprintf("required variable %s is missing a value: %s", e.Variable, e.Reason) + } + return fmt.Sprintf("required variable %s is missing a value", e.Variable) +} + +// 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) + +// ReplacementFunc is a user-supplied function that is apply to the matching +// substring. Returns the value as a string and an error. +type ReplacementFunc func(string, Mapping, *Config) (string, error) + +type Config struct { + pattern *regexp.Regexp + substituteFunc SubstituteFunc + replacementFunc ReplacementFunc + logging bool +} + +type Option func(*Config) + +func WithPattern(pattern *regexp.Regexp) Option { + return func(cfg *Config) { + cfg.pattern = pattern + } +} + +func WithSubstitutionFunction(subsFunc SubstituteFunc) Option { + return func(cfg *Config) { + cfg.substituteFunc = subsFunc + } +} + +func WithReplacementFunction(replacementFunc ReplacementFunc) Option { + return func(cfg *Config) { + cfg.replacementFunc = replacementFunc + } +} + +func WithoutLogging(cfg *Config) { + cfg.logging = false +} + +// SubstituteWithOptions substitute variables in the string with their values. +// It accepts additional options such as a custom function or pattern. +func SubstituteWithOptions(template string, mapping Mapping, options ...Option) (string, error) { + var returnErr error + + cfg := &Config{ + pattern: DefaultPattern, + replacementFunc: DefaultReplacementFunc, + logging: true, + } + for _, o := range options { + o(cfg) + } + + result := cfg.pattern.ReplaceAllStringFunc(template, func(substring string) string { + replacement, err := cfg.replacementFunc(substring, mapping, cfg) + if err != nil { + // Add the template for template errors + var tmplErr *InvalidTemplateError + if errors.As(err, &tmplErr) { + if tmplErr.Template == "" { + tmplErr.Template = template + } + } + // Save the first error to be returned + if returnErr == nil { + returnErr = err + } + + } + return replacement + }) + + return result, returnErr +} + +func DefaultReplacementFunc(substring string, mapping Mapping, cfg *Config) (string, error) { + value, _, err := DefaultReplacementAppliedFunc(substring, mapping, cfg) + return value, err +} + +func DefaultReplacementAppliedFunc(substring string, mapping Mapping, cfg *Config) (string, bool, error) { + pattern := cfg.pattern + subsFunc := cfg.substituteFunc + if subsFunc == nil { + _, subsFunc = getSubstitutionFunctionForTemplate(substring) + } + + closingBraceIndex := getFirstBraceClosingIndex(substring) + rest := "" + if closingBraceIndex > -1 { + rest = substring[closingBraceIndex+1:] + substring = substring[0 : closingBraceIndex+1] + } + + matches := pattern.FindStringSubmatch(substring) + groups := matchGroups(matches, pattern) + if escaped := groups[groupEscaped]; escaped != "" { + return escaped, true, nil + } + + braced := false + substitution := groups[groupNamed] + if substitution == "" { + substitution = groups[groupBraced] + braced = true + } + + if substitution == "" { + return "", false, &InvalidTemplateError{} + } + + if braced { + value, applied, err := subsFunc(substitution, mapping) + if err != nil { + return "", false, err + } + if applied { + interpolatedNested, err := SubstituteWith(rest, mapping, pattern) + if err != nil { + return "", false, err + } + return value + interpolatedNested, true, nil + } + } + + value, ok := mapping(substitution) + if !ok && cfg.logging { + logrus.Warnf("The %q variable is not set. Defaulting to a blank string.", substitution) + } + + return value, ok, nil +} + +// SubstituteWith substitute 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) { + options := []Option{ + WithPattern(pattern), + } + if len(subsFuncs) > 0 { + options = append(options, WithSubstitutionFunction(subsFuncs[0])) + } + + return SubstituteWithOptions(template, mapping, options...) +} + +func getSubstitutionFunctionForTemplate(template string) (string, SubstituteFunc) { + interpolationMapping := []struct { + string + SubstituteFunc + }{ + {":?", requiredErrorWhenEmptyOrUnset}, + {"?", requiredErrorWhenUnset}, + {":-", defaultWhenEmptyOrUnset}, + {"-", defaultWhenUnset}, + {":+", defaultWhenNotEmpty}, + {"+", defaultWhenSet}, + } + sort.Slice(interpolationMapping, func(i, j int) bool { + idxI := strings.Index(template, interpolationMapping[i].string) + idxJ := strings.Index(template, interpolationMapping[j].string) + if idxI < 0 { + return false + } + if idxJ < 0 { + return true + } + return idxI < idxJ + }) + + return interpolationMapping[0].string, interpolationMapping[0].SubstituteFunc +} + +func getFirstBraceClosingIndex(s string) int { + openVariableBraces := 0 + for i := 0; i < len(s); i++ { + if s[i] == '}' { + openVariableBraces-- + if openVariableBraces == 0 { + return i + } + } + if s[i] == '{' { + openVariableBraces++ + i++ + } + } + return -1 +} + +// Substitute variables in the string with their values +func Substitute(template string, mapping Mapping) (string, error) { + return SubstituteWith(template, mapping, DefaultPattern) +} + +// Soft default (fall back if unset or empty) +func defaultWhenEmptyOrUnset(substitution string, mapping Mapping) (string, bool, error) { + return withDefaultWhenAbsence(substitution, mapping, true) +} + +// Hard default (fall back if-and-only-if empty) +func defaultWhenUnset(substitution string, mapping Mapping) (string, bool, error) { + return withDefaultWhenAbsence(substitution, mapping, false) +} + +func defaultWhenNotEmpty(substitution string, mapping Mapping) (string, bool, error) { + return withDefaultWhenPresence(substitution, mapping, true) +} + +func defaultWhenSet(substitution string, mapping Mapping) (string, bool, error) { + return withDefaultWhenPresence(substitution, mapping, false) +} + +func requiredErrorWhenEmptyOrUnset(substitution string, mapping Mapping) (string, bool, error) { + return withRequired(substitution, mapping, ":?", func(v string) bool { return v != "" }) +} + +func requiredErrorWhenUnset(substitution string, mapping Mapping) (string, bool, error) { + return withRequired(substitution, mapping, "?", func(_ string) bool { return true }) +} + +func withDefaultWhenPresence(substitution string, mapping Mapping, notEmpty bool) (string, bool, error) { + sep := "+" + if notEmpty { + sep = ":+" + } + if !strings.Contains(substitution, sep) { + return "", false, nil + } + name, defaultValue := partition(substitution, sep) + defaultValue, err := Substitute(defaultValue, mapping) + if err != nil { + return "", false, err + } + value, ok := mapping(name) + if ok && (!notEmpty || (notEmpty && value != "")) { + return defaultValue, true, nil + } + return value, true, nil +} + +func withDefaultWhenAbsence(substitution string, mapping Mapping, emptyOrUnset bool) (string, bool, error) { + sep := "-" + if emptyOrUnset { + sep = ":-" + } + if !strings.Contains(substitution, sep) { + return "", false, nil + } + name, defaultValue := partition(substitution, sep) + defaultValue, err := Substitute(defaultValue, mapping) + if err != nil { + return "", false, err + } + value, ok := mapping(name) + if !ok || (emptyOrUnset && value == "") { + return defaultValue, true, nil + } + return value, true, nil +} + +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) + errorMessage, err := Substitute(errorMessage, mapping) + if err != nil { + return "", false, err + } + value, ok := mapping(name) + if !ok || !valid(value) { + return "", true, &MissingRequiredError{ + Reason: errorMessage, + Variable: name, + } + } + 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, "" +} diff --git a/vendor/github.com/compose-spec/compose-go/v2/template/variables.go b/vendor/github.com/compose-spec/compose-go/v2/template/variables.go new file mode 100644 index 000000000000..84edee599569 --- /dev/null +++ b/vendor/github.com/compose-spec/compose-go/v2/template/variables.go @@ -0,0 +1,158 @@ +/* + 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 ( + "regexp" + "strings" +) + +type Variable struct { + Name string + DefaultValue string + PresenceValue string + Required bool +} + +// ExtractVariables returns a map of all the variables defined in the specified +// compose file (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 +} + +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[groupEscaped]; escaped != "" { + continue + } + val := groups[groupNamed] + if val == "" { + val = groups[groupBraced] + s := match[0] + i := getFirstBraceClosingIndex(s) + if i > 0 { + val = s[2:i] + if len(s) > i { + if v, b := extractVariable(s[i+1:], pattern); b { + values = append(values, v...) + } + } + } + } + name := val + var defaultValue string + var presenceValue string + var required bool + i := strings.IndexFunc(val, func(r rune) bool { + if r >= 'a' && r <= 'z' { + return false + } + if r >= 'A' && r <= 'Z' { + return false + } + if r >= '0' && r <= '9' { + return false + } + if r == '_' { + return false + } + return true + }) + + if i > 0 { + name = val[:i] + rest := val[i:] + switch { + case strings.HasPrefix(rest, ":?"): + required = true + case strings.HasPrefix(rest, "?"): + required = true + case strings.HasPrefix(rest, ":-"): + defaultValue = rest[2:] + case strings.HasPrefix(rest, "-"): + defaultValue = rest[1:] + case strings.HasPrefix(rest, ":+"): + presenceValue = rest[2:] + case strings.HasPrefix(rest, "+"): + presenceValue = rest[1:] + } + } + + values = append(values, Variable{ + Name: name, + DefaultValue: defaultValue, + PresenceValue: presenceValue, + Required: required, + }) + + if defaultValue != "" { + if v, b := extractVariable(defaultValue, pattern); b { + values = append(values, v...) + } + } + if presenceValue != "" { + if v, b := extractVariable(presenceValue, pattern); b { + values = append(values, v...) + } + } + } + return values, len(values) > 0 +}