Merge lp:~axwalk/juju-core/cmd-juju-unset-env into lp:~go-bot/juju-core/trunk

Proposed by Andrew Wilkins
Status: Merged
Approved by: Andrew Wilkins
Approved revision: no longer in the source branch.
Merged at revision: 2484
Proposed branch: lp:~axwalk/juju-core/cmd-juju-unset-env
Merge into: lp:~go-bot/juju-core/trunk
Diff against target: 295 lines (+198/-2)
8 files modified
cmd/juju/environment.go (+62/-0)
cmd/juju/environment_test.go (+72/-0)
cmd/juju/main.go (+1/-0)
cmd/juju/main_test.go (+2/-0)
state/api/client.go (+6/-0)
state/api/params/params.go (+6/-0)
state/apiserver/client/client.go (+8/-2)
state/apiserver/client/client_test.go (+41/-0)
To merge this branch: bzr merge lp:~axwalk/juju-core/cmd-juju-unset-env
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+212771@code.launchpad.net

Commit message

Introduce unset-environment subcommand

A new juju subcommand, unset-environment (with unset-env alias)
is introduced. This subcommand will take the existing environment
config, remove the specified keys, and update it in state. If
the specified keys have associated defaults, then they will be
assigned.

This is necessary for removing configuration for which an empty
value is not permitted, e.g. for string attributes which are
omitted by default, such as http-proxy.

Fixes lp:1295372

https://codereview.appspot.com/80380043/

Description of the change

Introduce unset-environment subcommand

A new juju subcommand, unset-environment (with unset-env alias)
is introduced. This subcommand will take the existing environment
config, remove the specified keys, and update it in state. If
the specified keys have associated defaults, then they will be
assigned.

This is necessary for removing configuration for which an empty
value is not permitted, e.g. for string attributes which are
omitted by default, such as http-proxy.

Fixes lp:1295372

https://codereview.appspot.com/80380043/

To post a comment you must log in.
Revision history for this message
Andrew Wilkins (axwalk) wrote :

Reviewers: mp+212771_code.launchpad.net,

Message:
Please take a look.

Description:
Introduce unset-environment subcommand

A new juju subcommand, unset-environment (with unset-env alias)
is introduced. This subcommand will take the existing environment
config, remove the specified keys, and update it in state. If
the specified keys have associated defaults, then they will be
assigned.

This is necessary for removing configuration for which an empty
value is not permitted, e.g. for string attributes which are
omitted by default, such as http-proxy.

Fixes lp:1295372

https://code.launchpad.net/~axwalk/juju-core/cmd-juju-unset-env/+merge/212771

(do not edit description out of merge proposal)

Please review this at https://codereview.appspot.com/80380043/

Affected files (+196, -2 lines):
   A [revision details]
   M cmd/juju/environment.go
   M cmd/juju/environment_test.go
   M cmd/juju/main.go
   M cmd/juju/main_test.go
   M state/api/client.go
   M state/api/params/params.go
   M state/apiserver/client/client.go
   M state/apiserver/client/client_test.go

Revision history for this message
Ian Booth (wallyworld) wrote :

LGTM but I'd rather see more useful error message if an unset fails

https://codereview.appspot.com/80380043/diff/1/cmd/juju/environment.go
File cmd/juju/environment.go (right):

https://codereview.appspot.com/80380043/diff/1/cmd/juju/environment.go#newcode192
cmd/juju/environment.go:192: Set one or more the environment
configuration attributes to its default value
s/Set/Reset ?

https://codereview.appspot.com/80380043/diff/1/cmd/juju/environment.go#newcode193
cmd/juju/environment.go:193: in a running Juju instance. Attributes
without defaults are removed.
Perhaps include an example to show they are space separated? Or mention
that in the help?

https://codereview.appspot.com/80380043/diff/1/state/api/params/params.go
File state/api/params/params.go (right):

https://codereview.appspot.com/80380043/diff/1/state/api/params/params.go#newcode594
state/api/params/params.go:594: type EnvironmentUnset struct {
I wish there were a generic string args struct. Maybe there is, not
sure.

https://codereview.appspot.com/80380043/diff/1/state/apiserver/client/client_test.go
File state/apiserver/client/client_test.go (right):

https://codereview.appspot.com/80380043/diff/1/state/apiserver/client/client_test.go#newcode1579
state/apiserver/client/client_test.go:1579: c.Assert(err,
gc.ErrorMatches, "type: expected string, got nothing")
I don't like this error message. I realise it's the output of validate
but I'd expect to see "cannot unset blah" or something if i were a user.
Perhaps we could just wrap the error so it reads `cannot unset
environment attribute "type": blah blah`

https://codereview.appspot.com/80380043/

Revision history for this message
Andrew Wilkins (axwalk) wrote :

Please take a look.

https://codereview.appspot.com/80380043/diff/1/cmd/juju/environment.go
File cmd/juju/environment.go (right):

https://codereview.appspot.com/80380043/diff/1/cmd/juju/environment.go#newcode192
cmd/juju/environment.go:192: Set one or more the environment
configuration attributes to its default value
On 2014/03/26 06:13:43, wallyworld wrote:
> s/Set/Reset ?

Done.

https://codereview.appspot.com/80380043/diff/1/cmd/juju/environment.go#newcode193
cmd/juju/environment.go:193: in a running Juju instance. Attributes
without defaults are removed.
On 2014/03/26 06:13:43, wallyworld wrote:
> Perhaps include an example to show they are space separated? Or
mention that in
> the help?

Clarified in documentation.

https://codereview.appspot.com/80380043/diff/1/state/api/params/params.go
File state/api/params/params.go (right):

https://codereview.appspot.com/80380043/diff/1/state/api/params/params.go#newcode594
state/api/params/params.go:594: type EnvironmentUnset struct {
On 2014/03/26 06:13:43, wallyworld wrote:
> I wish there were a generic string args struct. Maybe there is, not
sure.

Nope, that I can see. Anyway, having API-specific ones means the struct
can be updated with additional fields if necessary.

https://codereview.appspot.com/80380043/diff/1/state/apiserver/client/client_test.go
File state/apiserver/client/client_test.go (right):

https://codereview.appspot.com/80380043/diff/1/state/apiserver/client/client_test.go#newcode1579
state/apiserver/client/client_test.go:1579: c.Assert(err,
gc.ErrorMatches, "type: expected string, got nothing")
On 2014/03/26 06:13:43, wallyworld wrote:
> I don't like this error message. I realise it's the output of validate
but I'd
> expect to see "cannot unset blah" or something if i were a user.
Perhaps we
> could just wrap the error so it reads `cannot unset environment
attribute
> "type": blah blah`

I agree the error sucks. The operaton isn't that granular, though. We're
saying "make all these changes to the config", and then checking if the
result is any good.

You can't necessarily remove them one at a time, either. It's feasible
that there are co-requisite attributes.

Perhaps the right thing to do is to have a method on EnvironProvider
that returns a config with defaults for all attributes that have
defaults. Then the attribute removal code can just reset them back to
those values, or error if there's no default and it's required. That's a
big change, though, and not something I want to drag into this.

https://codereview.appspot.com/80380043/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'cmd/juju/environment.go'
2--- cmd/juju/environment.go 2014-03-12 22:33:09 +0000
3+++ cmd/juju/environment.go 2014-03-26 06:33:09 +0000
4@@ -181,3 +181,65 @@
5 }
6 return err
7 }
8+
9+// UnsetEnvironment
10+type UnsetEnvironmentCommand struct {
11+ cmd.EnvCommandBase
12+ keys []string
13+}
14+
15+const unsetEnvHelpDoc = `
16+Reset one or more the environment configuration attributes to its default
17+value in a running Juju instance. Attributes without defaults are removed,
18+and attempting to remove a required attribute with no default will result
19+in an error.
20+
21+Multiple attributes may be removed at once; keys are space-separated.
22+`
23+
24+func (c *UnsetEnvironmentCommand) Info() *cmd.Info {
25+ return &cmd.Info{
26+ Name: "unset-environment",
27+ Args: "<environment key> ...",
28+ Purpose: "unset environment values",
29+ Doc: strings.TrimSpace(unsetEnvHelpDoc),
30+ Aliases: []string{"unset-env"},
31+ }
32+}
33+
34+func (c *UnsetEnvironmentCommand) Init(args []string) (err error) {
35+ if len(args) == 0 {
36+ return fmt.Errorf("No keys specified")
37+ }
38+ c.keys = args
39+ return nil
40+}
41+
42+// run1dot16 runs matches client.EnvironmentUnset using a direct DB
43+// connection to maintain compatibility with an API server running 1.16 or
44+// older (when EnvironmentUnset was not available). This fallback can be removed
45+// when we no longer maintain 1.16 compatibility.
46+func (c *UnsetEnvironmentCommand) run1dot16() error {
47+ conn, err := juju.NewConnFromName(c.EnvName)
48+ if err != nil {
49+ return err
50+ }
51+ defer conn.Close()
52+ return conn.State.UpdateEnvironConfig(nil, c.keys, nil)
53+}
54+
55+func (c *UnsetEnvironmentCommand) Run(ctx *cmd.Context) error {
56+ client, err := juju.NewAPIClientFromName(c.EnvName)
57+ if err != nil {
58+ return err
59+ }
60+ defer client.Close()
61+
62+ err = client.EnvironmentUnset(c.keys...)
63+ if params.IsCodeNotImplemented(err) {
64+ logger.Infof("EnvironmentUnset not supported by the API server, " +
65+ "falling back to 1.16 compatibility mode (direct DB access)")
66+ err = c.run1dot16()
67+ }
68+ return err
69+}
70
71=== modified file 'cmd/juju/environment_test.go'
72--- cmd/juju/environment_test.go 2014-03-17 06:03:51 +0000
73+++ cmd/juju/environment_test.go 2014-03-26 06:33:09 +0000
74@@ -7,8 +7,10 @@
75 "fmt"
76 "strings"
77
78+ jc "github.com/juju/testing/checkers"
79 gc "launchpad.net/gocheck"
80
81+ "launchpad.net/juju-core/environs/config"
82 jujutesting "launchpad.net/juju-core/juju/testing"
83 "launchpad.net/juju-core/provider/dummy"
84 _ "launchpad.net/juju-core/provider/local"
85@@ -178,3 +180,73 @@
86 c.Assert(err, gc.ErrorMatches, errorPattern)
87 }
88 }
89+
90+type UnsetEnvironmentSuite struct {
91+ jujutesting.RepoSuite
92+}
93+
94+var _ = gc.Suite(&UnsetEnvironmentSuite{})
95+
96+var unsetEnvTests = []struct {
97+ args []string
98+ err string
99+ expected attributes
100+ unexpected []string
101+}{
102+ {
103+ args: []string{},
104+ err: "No keys specified",
105+ }, {
106+ args: []string{"xyz", "xyz"},
107+ unexpected: []string{"xyz"},
108+ }, {
109+ args: []string{"type", "xyz"},
110+ err: "type: expected string, got nothing",
111+ expected: attributes{
112+ "type": "dummy",
113+ "xyz": 123,
114+ },
115+ }, {
116+ args: []string{"syslog-port"},
117+ expected: attributes{
118+ "syslog-port": config.DefaultSyslogPort,
119+ },
120+ }, {
121+ args: []string{"xyz2", "xyz"},
122+ unexpected: []string{"xyz"},
123+ },
124+}
125+
126+func (s *UnsetEnvironmentSuite) initConfig(c *gc.C) {
127+ err := s.State.UpdateEnvironConfig(map[string]interface{}{
128+ "syslog-port": 1234,
129+ "xyz": 123,
130+ }, nil, nil)
131+ c.Assert(err, gc.IsNil)
132+}
133+
134+func (s *UnsetEnvironmentSuite) TestUnsetEnvironment(c *gc.C) {
135+ for _, t := range unsetEnvTests {
136+ c.Logf("testing unset-env %v", t.args)
137+ s.initConfig(c)
138+ _, err := testing.RunCommand(c, &UnsetEnvironmentCommand{}, t.args)
139+ if t.err != "" {
140+ c.Assert(err, gc.ErrorMatches, t.err)
141+ } else {
142+ c.Assert(err, gc.IsNil)
143+ }
144+ if len(t.expected)+len(t.unexpected) != 0 {
145+ stateConfig, err := s.State.EnvironConfig()
146+ c.Assert(err, gc.IsNil)
147+ for k, v := range t.expected {
148+ vstate, ok := stateConfig.AllAttrs()[k]
149+ c.Assert(ok, jc.IsTrue)
150+ c.Assert(vstate, gc.Equals, v)
151+ }
152+ for _, k := range t.unexpected {
153+ _, ok := stateConfig.AllAttrs()[k]
154+ c.Assert(ok, jc.IsFalse)
155+ }
156+ }
157+ }
158+}
159
160=== modified file 'cmd/juju/main.go'
161--- cmd/juju/main.go 2014-03-21 14:28:36 +0000
162+++ cmd/juju/main.go 2014-03-26 06:33:09 +0000
163@@ -108,6 +108,7 @@
164 jujucmd.Register(wrap(&SetConstraintsCommand{}))
165 jujucmd.Register(wrap(&GetEnvironmentCommand{}))
166 jujucmd.Register(wrap(&SetEnvironmentCommand{}))
167+ jujucmd.Register(wrap(&UnsetEnvironmentCommand{}))
168 jujucmd.Register(wrap(&ExposeCommand{}))
169 jujucmd.Register(wrap(&SyncToolsCommand{}))
170 jujucmd.Register(wrap(&UnexposeCommand{}))
171
172=== modified file 'cmd/juju/main_test.go'
173--- cmd/juju/main_test.go 2014-03-26 01:22:46 +0000
174+++ cmd/juju/main_test.go 2014-03-26 06:33:09 +0000
175@@ -253,6 +253,8 @@
176 "terminate-machine", // alias for destroy-machine
177 "unexpose",
178 "unset",
179+ "unset-env", // alias for unset-environment
180+ "unset-environment",
181 "upgrade-charm",
182 "upgrade-juju",
183 "version",
184
185=== modified file 'state/api/client.go'
186--- state/api/client.go 2014-03-26 06:28:38 +0000
187+++ state/api/client.go 2014-03-26 06:33:09 +0000
188@@ -441,6 +441,12 @@
189 return c.call("EnvironmentSet", args, nil)
190 }
191
192+// EnvironmentUnset sets the given key-value pairs in the environment.
193+func (c *Client) EnvironmentUnset(keys ...string) error {
194+ args := params.EnvironmentUnset{Keys: keys}
195+ return c.call("EnvironmentUnset", args, nil)
196+}
197+
198 // SetEnvironAgentVersion sets the environment agent-version setting
199 // to the given value.
200 func (c *Client) SetEnvironAgentVersion(version version.Number) error {
201
202=== modified file 'state/api/params/params.go'
203--- state/api/params/params.go 2014-03-26 04:13:52 +0000
204+++ state/api/params/params.go 2014-03-26 06:33:09 +0000
205@@ -589,6 +589,12 @@
206 Config map[string]interface{}
207 }
208
209+// EnvironmentUnset contains the arguments for EnvironmentUnset client API
210+// call.
211+type EnvironmentUnset struct {
212+ Keys []string
213+}
214+
215 // SetEnvironAgentVersion contains the arguments for
216 // SetEnvironAgentVersion client API call.
217 type SetEnvironAgentVersion struct {
218
219=== modified file 'state/apiserver/client/client.go'
220--- state/apiserver/client/client.go 2014-03-26 04:13:52 +0000
221+++ state/apiserver/client/client.go 2014-03-26 06:33:09 +0000
222@@ -802,13 +802,19 @@
223 }
224 return nil
225 }
226-
227 // TODO(waigani) 2014-3-11 #1167616
228 // Add a txn retry loop to ensure that the settings on disk have not
229 // changed underneath us.
230-
231 return c.api.state.UpdateEnvironConfig(args.Config, nil, checkAgentVersion)
232+}
233
234+// EnvironmentUnset implements the server-side part of the
235+// set-environment CLI command.
236+func (c *Client) EnvironmentUnset(args params.EnvironmentUnset) error {
237+ // TODO(waigani) 2014-3-11 #1167616
238+ // Add a txn retry loop to ensure that the settings on disk have not
239+ // changed underneath us.
240+ return c.api.state.UpdateEnvironConfig(nil, args.Keys, nil)
241 }
242
243 // SetEnvironAgentVersion sets the environment agent version.
244
245=== modified file 'state/apiserver/client/client_test.go'
246--- state/apiserver/client/client_test.go 2014-03-26 00:24:30 +0000
247+++ state/apiserver/client/client_test.go 2014-03-26 06:33:09 +0000
248@@ -1542,6 +1542,47 @@
249 c.Assert(err, gc.IsNil)
250 }
251
252+func (s *clientSuite) TestClientEnvironmentUnset(c *gc.C) {
253+ err := s.State.UpdateEnvironConfig(map[string]interface{}{"abc": 123}, nil, nil)
254+ c.Assert(err, gc.IsNil)
255+ envConfig, err := s.State.EnvironConfig()
256+ c.Assert(err, gc.IsNil)
257+ _, found := envConfig.AllAttrs()["abc"]
258+ c.Assert(found, jc.IsTrue)
259+
260+ err = s.APIState.Client().EnvironmentUnset("abc")
261+ c.Assert(err, gc.IsNil)
262+ envConfig, err = s.State.EnvironConfig()
263+ c.Assert(err, gc.IsNil)
264+ _, found = envConfig.AllAttrs()["abc"]
265+ c.Assert(found, jc.IsFalse)
266+}
267+
268+func (s *clientSuite) TestClientEnvironmentUnsetMissing(c *gc.C) {
269+ // It's okay to unset a non-existent attribute.
270+ err := s.APIState.Client().EnvironmentUnset("not_there")
271+ c.Assert(err, gc.IsNil)
272+}
273+
274+func (s *clientSuite) TestClientEnvironmentUnsetError(c *gc.C) {
275+ err := s.State.UpdateEnvironConfig(map[string]interface{}{"abc": 123}, nil, nil)
276+ c.Assert(err, gc.IsNil)
277+ envConfig, err := s.State.EnvironConfig()
278+ c.Assert(err, gc.IsNil)
279+ _, found := envConfig.AllAttrs()["abc"]
280+ c.Assert(found, jc.IsTrue)
281+
282+ // "type" may not be removed, and this will cause an error.
283+ // If any one attribute's removal causes an error, there
284+ // should be no change.
285+ err = s.APIState.Client().EnvironmentUnset("abc", "type")
286+ c.Assert(err, gc.ErrorMatches, "type: expected string, got nothing")
287+ envConfig, err = s.State.EnvironConfig()
288+ c.Assert(err, gc.IsNil)
289+ _, found = envConfig.AllAttrs()["abc"]
290+ c.Assert(found, jc.IsTrue)
291+}
292+
293 func (s *clientSuite) TestClientFindTools(c *gc.C) {
294 result, err := s.APIState.Client().FindTools(2, -1, "", "")
295 c.Assert(err, gc.IsNil)

Subscribers

People subscribed via source and target branches

to status/vote changes: