-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Add options to the compose loader #1128
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,6 +16,14 @@ var patternString = fmt.Sprintf( | |
|
|
||
| var pattern = regexp.MustCompile(patternString) | ||
|
|
||
| // DefaultSubstituteFuncs contains the default SubstitueFunc 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 { | ||
|
|
@@ -32,8 +40,14 @@ func (e InvalidTemplateError) Error() string { | |
| // and the absence of a value. | ||
| type Mapping func(string) (string, bool) | ||
|
|
||
| // Substitute variables in the string with their values | ||
| func Substitute(template string, mapping Mapping) (string, error) { | ||
| // 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) | ||
|
|
@@ -47,49 +61,22 @@ func Substitute(template string, mapping Mapping) (string, error) { | |
| substitution = groups["braced"] | ||
| } | ||
|
|
||
| switch { | ||
|
|
||
| case substitution == "": | ||
| if substitution == "" { | ||
| err = &InvalidTemplateError{Template: template} | ||
| return "" | ||
| } | ||
|
|
||
| // Soft default (fall back if unset or empty) | ||
| case strings.Contains(substitution, ":-"): | ||
| name, defaultValue := partition(substitution, ":-") | ||
| value, ok := mapping(name) | ||
| if !ok || value == "" { | ||
| return defaultValue | ||
| } | ||
| return value | ||
|
|
||
| // Hard default (fall back if-and-only-if empty) | ||
| case strings.Contains(substitution, "-"): | ||
| name, defaultValue := partition(substitution, "-") | ||
| value, ok := mapping(name) | ||
| if !ok { | ||
| return defaultValue | ||
| } | ||
| return value | ||
|
|
||
| case strings.Contains(substitution, ":?"): | ||
| name, errorMessage := partition(substitution, ":?") | ||
| value, ok := mapping(name) | ||
| if !ok || value == "" { | ||
| err = &InvalidTemplateError{ | ||
| Template: fmt.Sprintf("required variable %s is missing a value: %s", name, errorMessage), | ||
| } | ||
| for _, f := range subsFuncs { | ||
| var ( | ||
| value string | ||
| applied bool | ||
| ) | ||
| value, applied, err = f(substitution, mapping) | ||
| if err != nil { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is the error itself discarded?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @thaJeztah it's not if it's the last one to be applied (i.e. if one function doesn't apply, or apply with error, it doesn't necessarly mean others wont). |
||
| return "" | ||
| } | ||
| return value | ||
|
|
||
| case strings.Contains(substitution, "?"): | ||
| name, errorMessage := partition(substitution, "?") | ||
| value, ok := mapping(name) | ||
| if !ok { | ||
| err = &InvalidTemplateError{ | ||
| Template: fmt.Sprintf("required variable %s is missing a value: %s", name, errorMessage), | ||
| } | ||
| return "" | ||
| if !applied { | ||
| continue | ||
| } | ||
| return value | ||
| } | ||
|
|
@@ -101,6 +88,65 @@ func Substitute(template string, mapping Mapping) (string, error) { | |
| return result, err | ||
| } | ||
|
|
||
| // Substitute variables in the string with their values | ||
| func Substitute(template string, mapping Mapping) (string, error) { | ||
| return SubstituteWith(template, mapping, pattern, DefaultSubstituteFuncs...) | ||
| } | ||
|
|
||
| // Soft default (fall back if unset or empty) | ||
| func softDefault(substitution string, mapping Mapping) (string, bool, error) { | ||
| if !strings.Contains(substitution, ":-") { | ||
| return "", false, nil | ||
| } | ||
| name, defaultValue := partition(substitution, ":-") | ||
| 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) { | ||
| if !strings.Contains(substitution, "-") { | ||
| return "", false, nil | ||
| } | ||
| name, defaultValue := partition(substitution, "-") | ||
| value, ok := mapping(name) | ||
| if !ok { | ||
| return defaultValue, true, nil | ||
| } | ||
| return value, true, nil | ||
| } | ||
|
|
||
| func requiredNonEmpty(substitution string, mapping Mapping) (string, bool, error) { | ||
| if !strings.Contains(substitution, ":?") { | ||
| return "", false, nil | ||
| } | ||
| name, errorMessage := partition(substitution, ":?") | ||
| value, ok := mapping(name) | ||
| if !ok || value == "" { | ||
| return "", true, &InvalidTemplateError{ | ||
| Template: fmt.Sprintf("required variable %s is missing a value: %s", name, errorMessage), | ||
| } | ||
| } | ||
| return value, true, nil | ||
| } | ||
|
|
||
| func required(substitution string, mapping Mapping) (string, bool, error) { | ||
| if !strings.Contains(substitution, "?") { | ||
| return "", false, nil | ||
| } | ||
| name, errorMessage := partition(substitution, "?") | ||
| value, ok := mapping(name) | ||
| if !ok { | ||
| return "", true, &InvalidTemplateError{ | ||
| Template: fmt.Sprintf("required variable %s is missing a value: %s", name, errorMessage), | ||
| } | ||
| } | ||
| return value, true, nil | ||
| } | ||
|
|
||
| func matchGroups(matches []string) map[string]string { | ||
| groups := make(map[string]string) | ||
| for i, name := range pattern.SubexpNames()[1:] { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess the reason to allow skipping "validation" is that if "interpolation" is skipped, there's values that are invalid (because, e.g.
$portis not a valid port-number)?Is there a use-case to disable
validationorinterpolationseparately?Also, trying to think of situations where skipping validation could skip important checks (although, I guess in the end that's the daemon/API's responsibility)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we don't interpolate, for sure we don't want to validate (otherwise the composefile could be invalid). The other way could be happens, i.e. doing some interpolation but not validating (for whatever reason, like if it's later on used with something else).
Yes. And also, this is only in the library part of
cli/compose— i.e. with this PR it is not possible to not validate or interpolate (and we shouldn't allow that with thedockercli) 😉