Skip to content

Conversation

@vdemeester
Copy link
Collaborator

@vdemeester vdemeester commented Jun 15, 2018

  • Add the possibility to skip interpolation
  • Add the possibility to skip schema validation
  • Allow customizing the substitution function, to add special cases.

Example of use.

loader.Load(composetypes.ConfigDetails{
	WorkingDir:  ".",
	ConfigFiles: files,
	Environment: env,
}, func(opts *loader.Options) {
	opts.Interpolate.Substitute = composetemplate.SubstituteWith(template, mapping, errorIfMissing)
})

// […]

func errorIfMissing(substitution string, mapping composetemplate.Mapping) (string, bool, error) {
	value, found := mapping(substitution)
	if !found {
		return "", true, &composetemplate.InvalidTemplateError{
			Template: fmt.Sprintf("required variable %s is missing a value", substitution),
		}
	}
	return value, true, nil
}

Again, this is gonna be very useful for docker/app 👼

Signed-off-by: Vincent Demeester vincent@sbr.pm

Copy link
Contributor

@silvin-lubecki silvin-lubecki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think some unit tests would help here 😄

Template: fmt.Sprintf("required variable %s is missing a value: %s", name, errorMessage),
}
for _, f := range subsFuncs {
var value string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

var(
    value string
    applied bool
)


// Soft default (fall back if unset or empty)
func softDefault(substitution string, mapping Mapping) (string, bool, error) {
if strings.Contains(substitution, ":-") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

if !strings.Contains(substitution, ":-"){
    return "", false, nil
}
...


// Hard default (fall back if-and-only-if empty)
func hardDefault(substitution string, mapping Mapping) (string, bool, error) {
if strings.Contains(substitution, "-") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

}

func requiredNonEmpty(substitution string, mapping Mapping) (string, bool, error) {
if strings.Contains(substitution, ":?") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and here

}

func required(substitution string, mapping Mapping) (string, bool, error) {
if strings.Contains(substitution, "?") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and here

@silvin-lubecki
Copy link
Contributor

Linter is complaining:

cli/compose/template/template.go:35:1:warning: comment on exported type SubstituteFunc should be of the form "SubstituteFunc ..." (with optional leading article) (golint)

@vdemeester vdemeester force-pushed the interpolation-options branch 2 times, most recently from 06c6486 to de77f86 Compare June 15, 2018 15:03
@vdemeester vdemeester force-pushed the interpolation-options branch from de77f86 to 3367be8 Compare June 25, 2018 12:39

// Substitute variables in the string with their values
func Substitute(template string, mapping Mapping) (string, error) {
// SubstituteFunc is a user-supplied function that apply substition.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/substition/substitution/

func Substitute(template string, mapping Mapping) (string, error) {
// SubstituteFunc is a user-supplied function that apply substition.
// Returns the value as a string, a bool indicating if the function could apply
// the substition and an error.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/substition/substitution/

@vdemeester vdemeester force-pushed the interpolation-options branch 4 times, most recently from f05c771 to f8ffc8e Compare June 25, 2018 12:59

// Load reads a ConfigDetails and returns a fully loaded configuration
func Load(configDetails types.ConfigDetails) (*types.Config, error) {
func Load(configDetails types.ConfigDetails, ops ...func(*Options)) (*types.Config, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps s/ops/options/ but that's a nit

}

for _, op := range ops {
op(opts)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be the standard pattern, but was just thinking why functional options cannot produce an error 🤔

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@thaJeztah they could 😉 I just though they would not here 😝

if err != nil {
return nil, err
if !opts.SkipInterpolation {
var err error
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you remove this var err .. ? There's more err variables in this function, so having this here will be confusing.


if err := schema.Validate(configDict, configDetails.Version); err != nil {
return nil, err
if !opts.SkipValidation {
Copy link
Member

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. $port is not a valid port-number)?

Is there a use-case to disable validation or interpolation separately?

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)

Copy link
Collaborator Author

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).

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)

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 the docker cli) 😉

type SubstituteFunc func(string, Mapping) (string, bool, error)

// SubstituteWith subsitute variables in the string with their values.
// It accepts additionnal substitute function.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/additionnal/additional/

// SubstituteWith subsitute variables in the string with their values.
// It accepts additionnal substitute function.
func SubstituteWith(template string, mapping Mapping, pattern *regexp.Regexp, subsFuncs ...SubstituteFunc) (string, error) {
subsFuncs = append([]SubstituteFunc{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we always append the default functions, or only do so if no custom functions are provided?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I did that to not export the default function, but looking back at it, this function should probably just take the list and use them (letting user decide which one to apply)

applied bool
)
value, applied, err = f(substitution, mapping)
if err != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the error itself discarded?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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).

- Add the possibility to skip interpolation
- Add the possibility to skip schema validation
- Allow customizing the substitution function, to add special cases.

Signed-off-by: Vincent Demeester <vincent@sbr.pm>
@vdemeester vdemeester force-pushed the interpolation-options branch from 468e96d to 9fdd14f Compare June 25, 2018 15:15
Copy link
Member

@thaJeztah thaJeztah left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@silvin-lubecki silvin-lubecki merged commit 204ab4c into docker:master Jun 26, 2018
@GordonTheTurtle GordonTheTurtle added this to the 18.06.0 milestone Jun 26, 2018
@silvin-lubecki silvin-lubecki deleted the interpolation-options branch June 26, 2018 13:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants