Merge lp:~axwalk/juju-core/azure-mode-nounitplacement 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: 2555
Proposed branch: lp:~axwalk/juju-core/azure-mode-nounitplacement
Merge into: lp:~go-bot/juju-core/trunk
Prerequisite: lp:~axwalk/juju-core/state-environcapability
Diff against target: 301 lines (+154/-12)
5 files modified
provider/azure/config.go (+16/-0)
provider/azure/config_test.go (+33/-0)
provider/azure/environ.go (+19/-11)
provider/azure/environ_test.go (+77/-0)
provider/azure/environprovider.go (+9/-1)
To merge this branch: bzr merge lp:~axwalk/juju-core/azure-mode-nounitplacement
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+211863@code.launchpad.net

Commit message

provider/azure: availability-sets-enabled config

A new config attribute availability-sets-enabled
is defined, which controls the mode in which the
Azure environment operates. If true (the default),
availability set support is enabled and instances
will be grouped by service. The attribute cannot
be changed after the environment is prepared.

If the availability-sets-enabled is true, unit
placement is prohibited. This is necessary to
avoid various issues such as incorrectly load-
balancing ports across heterogenous services, and
makes it possible to unambiguously determine
which Cloud Service to assign units to for high
availability.

https://codereview.appspot.com/77950045/

Description of the change

provider/azure: availability-sets-enabled config

A new config attribute availability-sets-enabled
is defined, which controls the mode in which the
Azure environment operates. If true (the default),
availability set support is enabled and instances
will be grouped by service. The attribute cannot
be changed after the environment is prepared.

If the availability-sets-enabled is true, unit
placement is prohibited. This is necessary to
avoid various issues such as incorrectly load-
balancing ports across heterogenous services, and
makes it possible to unambiguously determine
which Cloud Service to assign units to for high
availability.

https://codereview.appspot.com/77950045/

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

Reviewers: mp+211863_code.launchpad.net,

Message:
Please take a look.

Description:
provider/azure: availability-sets-enabled config

A new config attribute availability-sets-enabled
is defined, which controls the mode in which the
Azure environment operates. If true (the default),
availability set support is enabled and instances
will be grouped by service. The attribute cannot
be changed after the environment is prepared.

If the availability-sets-enabled is true, unit
placement is prohibited. This is necessary to
avoid various issues such as incorrectly load-
balancing ports across heterogenous services, and
makes it possible to unambiguously determine
which Cloud Service to assign units to for high
availability.

https://code.launchpad.net/~axwalk/juju-core/azure-mode-nounitplacement/+merge/211863

Requires:
https://code.launchpad.net/~axwalk/juju-core/state-environcapability/+merge/211749

(do not edit description out of merge proposal)

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

Affected files (+83, -2 lines):
   A [revision details]
   M provider/azure/config.go
   M provider/azure/config_test.go
   M provider/azure/environ.go
   M provider/azure/environ_test.go
   M provider/azure/environprovider.go

Index: [revision details]
=== added file '[revision details]'
--- [revision details] 2012-01-01 00:00:00 +0000
+++ [revision details] 2012-01-01 00:00:00 +0000
@@ -0,0 +1,2 @@
+Old revision: <email address hidden>
+New revision: <email address hidden>

Index: provider/azure/config.go
=== modified file 'provider/azure/config.go'
--- provider/azure/config.go 2014-03-10 20:22:44 +0000
+++ provider/azure/config.go 2014-03-20 04:44:57 +0000
@@ -18,12 +18,16 @@
   "management-certificate": schema.String(),
   "storage-account-name": schema.String(),
   "force-image-name": schema.String(),
+ "availability-sets-enabled": schema.Bool(),
  }
  var configDefaults = schema.Defaults{
   "location": "",
   "management-certificate": "",
   "management-certificate-path": "",
   "force-image-name": "",
+ // availability-sets-enabled is set to Omit (equivalent
+ // to false) for backwards compatibility.
+ "availability-sets-enabled": schema.Omit,
  }

  type azureEnvironConfig struct {
@@ -51,6 +55,11 @@
   return cfg.attrs["force-image-name"].(string)
  }

+func (cfg *azureEnvironConfig) availabilitySetsEnabled() bool {
+ enabled, _ := cfg.attrs["availability-sets-enabled"].(bool)
+ return enabled
+}
+
  func (prov azureEnvironProvider) newConfig(cfg *config.Config)
(*azureEnvironConfig, error) {
   validCfg, err := prov.Validate(cfg, nil)
   if err != nil {
@@ -71,6 +80,13 @@
    return nil, err
   }

+ // User cannot change availability-sets-enabled after environment is
prepared.
+ if oldCfg != nil {
+ if oldCfg.AllAttrs()["availability-sets-enabled"] !=
cfg.AllAttrs()["availability-sets-enabled"] {
+ return nil, fmt.Errorf("cannot change availability-sets-enabled")
+ }
+ }
+
   validated, err := cfg.ValidateUnknownAttrs(configFields, configDefaults)
   if err != nil {
    return nil, e...

Read more...

Revision history for this message
William Reade (fwereade) wrote :

LGTM, but please don't land it until the followups are ready, so we
don't run any risk of releasing half-implemented functionality.

https://codereview.appspot.com/77950045/diff/1/provider/azure/environ.go
File provider/azure/environ.go (right):

https://codereview.appspot.com/77950045/diff/1/provider/azure/environ.go#newcode903
provider/azure/environ.go:903: return fmt.Errorf("unit placement is not
permitted with availability-sets-enabled")
s/permitted/supported/

?

https://codereview.appspot.com/77950045/diff/1/provider/azure/environprovider.go
File provider/azure/environprovider.go (right):

https://codereview.appspot.com/77950045/diff/1/provider/azure/environprovider.go#newcode47
provider/azure/environprovider.go:47: // by default, unless the user set
a value.
This behaviour definitely demands documentation -- please make sure that
(1) the release notes mention it and (2) you write something for the
html docs.

https://codereview.appspot.com/77950045/

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

Please take a look.

https://codereview.appspot.com/77950045/diff/1/provider/azure/environ.go
File provider/azure/environ.go (right):

https://codereview.appspot.com/77950045/diff/1/provider/azure/environ.go#newcode903
provider/azure/environ.go:903: return fmt.Errorf("unit placement is not
permitted with availability-sets-enabled")
On 2014/03/24 09:55:21, fwereade wrote:
> s/permitted/supported/

> ?

Done.

https://codereview.appspot.com/77950045/diff/1/provider/azure/environprovider.go
File provider/azure/environprovider.go (right):

https://codereview.appspot.com/77950045/diff/1/provider/azure/environprovider.go#newcode47
provider/azure/environprovider.go:47: // by default, unless the user set
a value.
On 2014/03/24 09:55:21, fwereade wrote:
> This behaviour definitely demands documentation -- please make sure
that (1) the
> release notes mention it and (2) you write something for the html
docs.

Filed https://bugs.launchpad.net/juju-core/+bug/1297066

https://codereview.appspot.com/77950045/

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

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'provider/azure/config.go'
2--- provider/azure/config.go 2014-03-10 20:22:44 +0000
3+++ provider/azure/config.go 2014-04-03 09:09:45 +0000
4@@ -18,12 +18,16 @@
5 "management-certificate": schema.String(),
6 "storage-account-name": schema.String(),
7 "force-image-name": schema.String(),
8+ "availability-sets-enabled": schema.Bool(),
9 }
10 var configDefaults = schema.Defaults{
11 "location": "",
12 "management-certificate": "",
13 "management-certificate-path": "",
14 "force-image-name": "",
15+ // availability-sets-enabled is set to Omit (equivalent
16+ // to false) for backwards compatibility.
17+ "availability-sets-enabled": schema.Omit,
18 }
19
20 type azureEnvironConfig struct {
21@@ -51,6 +55,11 @@
22 return cfg.attrs["force-image-name"].(string)
23 }
24
25+func (cfg *azureEnvironConfig) availabilitySetsEnabled() bool {
26+ enabled, _ := cfg.attrs["availability-sets-enabled"].(bool)
27+ return enabled
28+}
29+
30 func (prov azureEnvironProvider) newConfig(cfg *config.Config) (*azureEnvironConfig, error) {
31 validCfg, err := prov.Validate(cfg, nil)
32 if err != nil {
33@@ -71,6 +80,13 @@
34 return nil, err
35 }
36
37+ // User cannot change availability-sets-enabled after environment is prepared.
38+ if oldCfg != nil {
39+ if oldCfg.AllAttrs()["availability-sets-enabled"] != cfg.AllAttrs()["availability-sets-enabled"] {
40+ return nil, fmt.Errorf("cannot change availability-sets-enabled")
41+ }
42+ }
43+
44 validated, err := cfg.ValidateUnknownAttrs(configFields, configDefaults)
45 if err != nil {
46 return nil, err
47
48=== modified file 'provider/azure/config_test.go'
49--- provider/azure/config_test.go 2014-02-12 04:54:19 +0000
50+++ provider/azure/config_test.go 2014-04-03 09:09:45 +0000
51@@ -7,6 +7,7 @@
52 "io/ioutil"
53 "strings"
54
55+ jc "github.com/juju/testing/checkers"
56 gc "launchpad.net/gocheck"
57
58 "launchpad.net/juju-core/environs/config"
59@@ -202,3 +203,35 @@
60 _, err = provider.Validate(cfg, nil)
61 c.Assert(err, gc.IsNil)
62 }
63+
64+func (*configSuite) TestAvailabilitySetsEnabledDefault(c *gc.C) {
65+ userValues := []interface{}{nil, false, true}
66+ for _, userValue := range userValues {
67+ attrs := makeAzureConfigMap(c)
68+ // If availability-sets-enabled isn't specified, it's set to true.
69+ checker := jc.IsTrue
70+ if userValue, ok := userValue.(bool); ok {
71+ attrs["availability-sets-enabled"] = userValue
72+ if !userValue {
73+ checker = jc.IsFalse
74+ }
75+ }
76+ cfg, err := config.New(config.UseDefaults, attrs)
77+ c.Assert(err, gc.IsNil)
78+ env, err := azureEnvironProvider{}.Prepare(testing.Context(c), cfg)
79+ c.Assert(err, gc.IsNil)
80+ azureEnv := env.(*azureEnviron)
81+ c.Assert(azureEnv.ecfg.availabilitySetsEnabled(), checker)
82+ }
83+}
84+
85+func (*configSuite) TestAvailabilitySetsEnabledImmutable(c *gc.C) {
86+ cfg, err := config.New(config.UseDefaults, makeAzureConfigMap(c))
87+ c.Assert(err, gc.IsNil)
88+ env, err := azureEnvironProvider{}.Prepare(testing.Context(c), cfg)
89+ c.Assert(err, gc.IsNil)
90+ cfg, err = env.Config().Apply(map[string]interface{}{"availability-sets-enabled": false})
91+ c.Assert(err, gc.IsNil)
92+ err = env.SetConfig(cfg)
93+ c.Assert(err, gc.ErrorMatches, "cannot change availability-sets-enabled")
94+}
95
96=== modified file 'provider/azure/environ.go'
97--- provider/azure/environ.go 2014-04-03 07:14:57 +0000
98+++ provider/azure/environ.go 2014-04-03 09:09:45 +0000
99@@ -53,9 +53,13 @@
100 stateServerLabel = "juju-state-server"
101 )
102
103+// vars for testing purposes.
104+var (
105+ createInstance = (*azureEnviron).createInstance
106+)
107+
108 type azureEnviron struct {
109 common.NopPrecheckerPolicy
110- common.SupportsUnitPlacementPolicy
111
112 // Except where indicated otherwise, all fields in this object should
113 // only be accessed using a lock or a snapshot.
114@@ -436,8 +440,10 @@
115 var err error
116 var service *gwacl.HostedService
117 if serviceName != "" {
118+ logger.Debugf("creating instance in existing cloud service %q", serviceName)
119 service, err = azure.GetHostedServiceProperties(serviceName, true)
120 } else {
121+ logger.Debugf("creating instance in new cloud service")
122 // If we're creating a cloud service for state servers,
123 // we will want to open additional ports. We need to
124 // record this against the cloud service, so we use a
125@@ -503,14 +509,10 @@
126
127 // StartInstance is specified in the InstanceBroker interface.
128 func (env *azureEnviron) StartInstance(args environs.StartInstanceParams) (_ instance.Instance, _ *instance.HardwareCharacteristics, err error) {
129-
130 if args.MachineConfig.HasNetworks() {
131 return nil, nil, fmt.Errorf("starting instances with networks is not supported yet.")
132 }
133
134- // Declaring "err" in the function signature so that we can "defer"
135- // any cleanup that needs to run during error returns.
136-
137 err = environs.FinishMachineConfig(args.MachineConfig, env.Config(), args.Constraints)
138 if err != nil {
139 return nil, nil, err
140@@ -533,7 +535,8 @@
141 }
142 defer env.releaseManagementAPI(azure)
143
144- location := env.getSnapshot().ecfg.location()
145+ snapshot := env.getSnapshot()
146+ location := snapshot.ecfg.location()
147 instanceType, sourceImageName, err := env.selectInstanceTypeAndImage(&instances.InstanceConstraint{
148 Region: location,
149 Series: args.Tools.OneSeries(),
150@@ -548,9 +551,7 @@
151 // the same affinity, so that machines can be be allocated to the
152 // same availability set.
153 var cloudServiceName string
154- // TODO(axw) replace "false &&" with mode check once
155- // availability-sets-enabled change is landed.
156- if false && args.DistributionGroup != nil {
157+ if args.DistributionGroup != nil && snapshot.ecfg.availabilitySetsEnabled() {
158 instanceIds, err := args.DistributionGroup()
159 if err != nil {
160 return nil, nil, err
161@@ -561,7 +562,6 @@
162 break
163 }
164 }
165- logger.Debugf("using existing cloud service: %q", cloudServiceName)
166 }
167
168 vhd := env.newOSDisk(sourceImageName)
169@@ -575,7 +575,7 @@
170 }
171 }
172 role := env.newRole(instanceType, vhd, userData, stateServer)
173- inst, err := env.createInstance(azure.ManagementAPI, role, cloudServiceName, stateServer)
174+ inst, err := createInstance(env, azure.ManagementAPI, role, cloudServiceName, stateServer)
175 if err != nil {
176 return nil, nil, err
177 }
178@@ -1165,3 +1165,11 @@
179 Endpoint: string(gwacl.GetEndpoint(ecfg.location())),
180 }, nil
181 }
182+
183+// SupportsUnitPlacement is specified in the state.EnvironCapability interface.
184+func (env *azureEnviron) SupportsUnitPlacement() error {
185+ if env.getSnapshot().ecfg.availabilitySetsEnabled() {
186+ return fmt.Errorf("unit placement is not supported with availability-sets-enabled")
187+ }
188+ return nil
189+}
190
191=== modified file 'provider/azure/environ_test.go'
192--- provider/azure/environ_test.go 2014-04-03 07:14:57 +0000
193+++ provider/azure/environ_test.go 2014-04-03 09:09:45 +0000
194@@ -31,6 +31,9 @@
195 envtesting "launchpad.net/juju-core/environs/testing"
196 "launchpad.net/juju-core/environs/tools"
197 "launchpad.net/juju-core/instance"
198+ "launchpad.net/juju-core/state"
199+ "launchpad.net/juju-core/state/api"
200+ apiparams "launchpad.net/juju-core/state/api/params"
201 "launchpad.net/juju-core/testing"
202 )
203
204@@ -1393,3 +1396,77 @@
205 c.Assert(len(sources), gc.Equals, 1)
206 assertSourceContents(c, sources[0], "filename", data)
207 }
208+
209+func (s *environSuite) TestCheckUnitAssignment(c *gc.C) {
210+ // If availability-sets-enabled is true, then placement is disabled.
211+ attrs := makeAzureConfigMap(c)
212+ attrs["availability-sets-enabled"] = true
213+ env := environs.Environ(makeEnvironWithConfig(c, attrs))
214+ err := env.SupportsUnitPlacement()
215+ c.Assert(err, gc.ErrorMatches, "unit placement is not supported with availability-sets-enabled")
216+
217+ // If the user disables availability sets, they can do what they want.
218+ attrs["availability-sets-enabled"] = false
219+ env = environs.Environ(makeEnvironWithConfig(c, attrs))
220+ err = env.SupportsUnitPlacement()
221+ c.Assert(err, gc.IsNil)
222+}
223+
224+func (s *environSuite) TestStartInstance(c *gc.C) {
225+ env := makeEnviron(c)
226+ s.setDummyStorage(c, env)
227+ env.ecfg.attrs["force-image-name"] = "my-image"
228+ stateInfo := &state.Info{
229+ Addrs: []string{"localhost:123"},
230+ CACert: []byte(testing.CACert),
231+ Password: "password",
232+ Tag: "machine-1",
233+ }
234+ apiInfo := &api.Info{
235+ Addrs: []string{"localhost:124"},
236+ CACert: []byte(testing.CACert),
237+ Password: "admin",
238+ Tag: "machine-1",
239+ }
240+ var params environs.StartInstanceParams
241+ params.Tools = envtesting.AssertUploadFakeToolsVersions(c, env.storage, envtesting.V120p...)
242+ params.MachineConfig = environs.NewMachineConfig("1", "yanonce", nil, nil, stateInfo, apiInfo)
243+
244+ // Start out with availability sets disabled.
245+ env.ecfg.attrs["availability-sets-enabled"] = false
246+
247+ var expectServiceName string
248+ var expectStateServer bool
249+ s.PatchValue(&createInstance, func(env *azureEnviron, azure *gwacl.ManagementAPI, role *gwacl.Role, serviceName string, stateServer bool) (instance.Instance, error) {
250+ c.Assert(serviceName, gc.Equals, expectServiceName)
251+ c.Assert(stateServer, gc.Equals, expectStateServer)
252+ return nil, nil
253+ })
254+ env.StartInstance(params)
255+
256+ // DistributionGroup won't have an effect if availability-sets-enabled=false.
257+ expectServiceName = ""
258+ params.DistributionGroup = func() ([]instance.Id, error) {
259+ return []instance.Id{instance.Id(env.getEnvPrefix() + "whatever-role0")}, nil
260+ }
261+ env.StartInstance(params)
262+
263+ env.ecfg.attrs["availability-sets-enabled"] = true
264+ expectServiceName = "juju-testenv-whatever"
265+ env.StartInstance(params)
266+
267+ expectServiceName = ""
268+ params.DistributionGroup = nil
269+ env.StartInstance(params)
270+
271+ // Empty distribution group is equivalent to no DistributionGroup function.
272+ params.DistributionGroup = func() ([]instance.Id, error) {
273+ return nil, nil
274+ }
275+ env.StartInstance(params)
276+
277+ // If the machine has the JobManagesEnviron job, we should see stateServer==true.
278+ expectStateServer = true
279+ params.MachineConfig.Jobs = []apiparams.MachineJob{apiparams.JobHostUnits, apiparams.JobManageEnviron}
280+ env.StartInstance(params)
281+}
282
283=== modified file 'provider/azure/environprovider.go'
284--- provider/azure/environprovider.go 2014-04-02 11:35:49 +0000
285+++ provider/azure/environprovider.go 2014-04-03 09:09:45 +0000
286@@ -39,6 +39,14 @@
287
288 // Prepare is specified in the EnvironProvider interface.
289 func (prov azureEnvironProvider) Prepare(ctx environs.BootstrapContext, cfg *config.Config) (environs.Environ, error) {
290- // TODO prepare environment as necessary
291+ // Set availability-sets-enabled to true
292+ // by default, unless the user set a value.
293+ if _, ok := cfg.AllAttrs()["availability-sets-enabled"]; !ok {
294+ var err error
295+ cfg, err = cfg.Apply(map[string]interface{}{"availability-sets-enabled": true})
296+ if err != nil {
297+ return nil, err
298+ }
299+ }
300 return prov.Open(cfg)
301 }

Subscribers

People subscribed via source and target branches

to status/vote changes: