Add support for --config key=value for juju deploy #8206

Merged
merged 1 commit into from Dec 13, 2017

Conversation

Projects
None yet
3 participants
Owner

wallyworld commented Dec 12, 2017

Description of change

juju deploy help claims that it supports
$ juju deploy mysql --config foo=bar

But it didn't. It only supported --config path/to/yaml
(where the yaml file wasn't a key values but a map kyed on charm name).

Add support for --config key=value along with a yaml file.
If more than one yaml file is specified, that's an error.
If both config name values and a yaml file are specified, the config name values take precedence over the values in the yaml file.

QA steps

bootstrap
deploy a charm and specify key=value and ensure these are used

Documentation changes

None because juju didn't behave like the doc said it should.

Owner

wallyworld commented Dec 12, 2017

!!build!!

Owner

wallyworld commented Dec 12, 2017

!!build!!

cmd/juju/application/deploy.go
+ return errors.Trace(err)
+ }
+ if len(files) > 1 {
+ return errors.Errorf("only a single config YAMl file can be specified, got %d", len(files))
cmd/juju/application/deploy.go
+ // So we need to combine the two here to have only one.
+ if apiRoot.BestFacadeVersion("Application") < 6 && len(appConfig) > 0 {
+ var configFromFile map[string]map[interface{}]interface{}
+ err := yaml.Unmarshal(configYAML, &configFromFile)
@axw

axw Dec 12, 2017

Member

configYAML is (was) intentionally not decoded on the client side

I'm not sure if there's a safe way to do this with gopkg.in/yaml. Looks like it'll use encoding.TextUnmarshaler, so probably that way. If it were json, I'd suggest unmarshalling into a map[string]json.RawMessage, combining those, and then marshalling them again.

IMO, just raise an error here and people can upgrade to a newer controller. Or at least defer this work.

@wallyworld

wallyworld Dec 12, 2017

Owner

Why do we not decode client side? We're not interpreting anything or coercing into values. We're simply adding extra values.

@axw

axw Dec 12, 2017

Member

Why do we not decode client side?

Because we don't have the schema on the client side. We don't know which type to use. 42 could be a unmarshaled into any of string, int, or float. By unmarshaling into map[string]interface{}, you get the default-according-to-gopkg.in/yaml.

@wallyworld

wallyworld Dec 12, 2017

Owner

Right, but it goes across the wire as a string when we marshal back to yaml. So "42" will round trip back to "42" and will be parsed using the schema on the server side.

@axw

axw Dec 12, 2017

Member

I chose a bad example, substitute in 42.0. That will be unmarshaled as float64 42. Float64 42 marshals as "42" and not "42.0".

If the charm setting was in fact a float, then that'd be fine. But if it's a string, you've dropped the ".0", which may not be fine.

@wallyworld

wallyworld Dec 12, 2017

Owner

I changed the type we unmarshal into to be:

map[string]charm.Settings

This preserves the string values as written into the YAML, ie "123.0" remains as "123.0" during the round trip as it is unmarshalled into a string value

@axw

axw Dec 13, 2017

Member

charm.Settings is just a map[string]interface{}

I just tested this:

val := charm.Settings{}
yaml.Unmarshal([]byte("x: 42.0"), &val)
out, _ := yaml.Marshal(val)
fmt.Printf("%s\n", out)

and the output was x: 42

@wallyworld

wallyworld Dec 13, 2017

Owner

I had it as map[string]string and mistakenly though charm.Settings was also map[string]string.
I changed it back.

    val := map[string]string{}
    yaml.Unmarshal([]byte("x: 42.0"), &val)
    out, _ := yaml.Marshal(val)
    fmt.Printf("%s\n", out)

prints x: "42.0"

cmd/juju/application/deploy.go
+ var configFromFile map[string]map[interface{}]interface{}
+ err := yaml.Unmarshal(configYAML, &configFromFile)
+ if err != nil {
+ return errors.Annotate(err, "badly formatted YAMl config file")
cmd/juju/application/deploy.go
+ charmSettings[k] = v
+ }
+ configFromFile[applicationName] = charmSettings
+ configYAML, err = yaml.Marshal(configFromFile)
@axw

axw Dec 12, 2017

Member

shouldn't you be setting appConfig to nil somewhere here?

@wallyworld

wallyworld Dec 12, 2017

Owner

it doesn't matter because if there's a configYAML, it will mask any config map entries

@axw

axw Dec 12, 2017

Member

true, but it would be nicer - no unnecessary data on the wire

cmd/juju/application/flags.go
@@ -136,3 +139,70 @@ func (m stringMap) String() string {
}
return strings.Join(pairs, ";")
}
+
+// configFlag records k=v attributes from command arguments
@axw

axw Dec 12, 2017

Member

most of this is the same as common.ConfigFlag - why not just modify that a bit? AFAICS, all you need is to add methods for accessing the k=v attrs and the filenames

@wallyworld

wallyworld Dec 12, 2017

Owner

I didn't want to break encapsulation. Either way works for me. I changed to use common.ConfigFlag

cmd/juju/application/flags.go
+ if fields[1] == "" {
+ value = ""
+ } else {
+ if err := yaml.Unmarshal([]byte(fields[1]), &value); err != nil {
@axw

axw Dec 12, 2017

Member

I don't think we should be parsing the value as YAML for application config. Can we make that bit configurable please, and disable it for the "juju config" command?

axw approved these changes Dec 13, 2017

cmd/juju/application/deploy.go
+ }
+ appConfig := make(map[string]string)
+ for k, v := range attr {
+ appConfig[k] = fmt.Sprintf("%v", v)
@axw

axw Dec 13, 2017

Member

should be safe to just v.(string), since you've said to preserve string values

cmd/juju/common/flags.go
+ preserveType bool
+}
+
+// SetPreserveType sets whether name values should be
@axw

axw Dec 13, 2017

Member

Based on how preserveType is used, I don't understand this at all. Is it meant to be preserving the string type? Or inferring the type based on the value's contents (i.e. decoding as YAML)?

Perhaps be a bit more literal, and make this

// SetDecodeYAMLValues controls whether or not config values
// are decoded as YAML into their default types, or to store the
// original string values. The default is to decode as YAML.
func (*ConfigFlag) SetDecodeYAMLValues(bool)
@wallyworld

wallyworld Dec 13, 2017

Owner

Changed to "SetPreserveStringValue" and expanded the comment to hopefully clarify.

Owner

wallyworld commented Dec 13, 2017

$$merge$$

Contributor

jujubot commented Dec 13, 2017

Status: merge request accepted. Url: http://ci.jujucharms.com/job/github-merge-juju

Contributor

jujubot commented Dec 13, 2017

Build failed: Tests failed
build url: http://ci.jujucharms.com/job/github-merge-juju/670

Owner

wallyworld commented Dec 13, 2017

$$merge$$

Contributor

jujubot commented Dec 13, 2017

Status: merge request accepted. Url: http://ci.jujucharms.com/job/github-merge-juju

@jujubot jujubot merged commit fdcd718 into juju:develop Dec 13, 2017

1 check passed

continuous-integration/jenkins/pr-merge This commit looks good
Details

wallyworld added a commit to wallyworld/juju that referenced this pull request Dec 15, 2017

Merge pull request #8206 from wallyworld/deploy-config
Add support for --config key=value for juju deploy

juju deploy help claims that it supports
$ juju deploy mysql --config foo=bar

But it didn't. It only supported --config path/to/yaml
(where the yaml file wasn't a key values but a map kyed on charm name).

Add support for --config key=value along with a yaml file.
If more than one yaml file is specified, that's an error.
If both config name values and a yaml file are specified, the config name values take precedence over the values in the yaml file.

bootstrap
deploy a charm and specify key=value and ensure these are used

None because juju didn't behave like the doc said it should.

@wallyworld wallyworld referenced this pull request Dec 15, 2017

Merged

Backport 2.4 fixes #8230

jujubot added a commit that referenced this pull request Dec 15, 2017

Merge pull request #8230 from wallyworld/backport-2.4-fixes
Backport 2.4 fixes

## Description of change

Backport 2 fixes:
#8206 which adds support for juju deploy --config name=value
#8227 which adds relation status on import if they are missing
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment