2
# Licensed under the Apache License, Version 2.0 (the "License"); you may
3
# not use this file except in compliance with the License. You may obtain
4
# a copy of the License at
6
# http://www.apache.org/licenses/LICENSE-2.0
8
# Unless required by applicable law or agreed to in writing, software
9
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
10
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
11
# License for the specific language governing permissions and limitations
17
from oslo.config import cfg
20
from heat.common import exception
21
from heat.common import template_format
22
from heat.engine import function
23
from heat.engine import rsrc_defn
24
from heat.tests import common
25
from heat.tests import utils
26
from heat.tests.v1_1 import fakes as fakes_v1_1
29
asg_tmpl_without_updt_policy = '''
31
"AWSTemplateFormatVersion" : "2010-09-09",
32
"Description" : "Template to create autoscaling group.",
36
"Type" : "AWS::AutoScaling::AutoScalingGroup",
38
"AvailabilityZones" : ["nova"],
39
"LaunchConfigurationName" : { "Ref" : "LaunchConfig" },
42
"LoadBalancerNames" : [ { "Ref" : "ElasticLoadBalancer" } ]
45
"ElasticLoadBalancer" : {
46
"Type" : "AWS::ElasticLoadBalancing::LoadBalancer",
48
"AvailabilityZones" : ["nova"],
50
"LoadBalancerPort" : "80",
51
"InstancePort" : "80",
57
"Type" : "AWS::AutoScaling::LaunchConfiguration",
59
"ImageId" : "F20-x86_64-cfntools",
60
"InstanceType" : "m1.medium",
62
"SecurityGroups" : [ "sg-1" ],
63
"UserData" : "jsconfig data"
70
asg_tmpl_with_bad_updt_policy = '''
72
"AWSTemplateFormatVersion" : "2010-09-09",
73
"Description" : "Template to create autoscaling group.",
81
"Type" : "AWS::AutoScaling::AutoScalingGroup",
83
"AvailabilityZones" : ["nova"],
84
"LaunchConfigurationName" : { "Ref" : "LaunchConfig" },
90
"Type" : "AWS::AutoScaling::LaunchConfiguration",
92
"ImageId" : "F20-x86_64-cfntools",
93
"InstanceType" : "m1.medium",
95
"SecurityGroups" : [ "sg-1" ],
96
"UserData" : "jsconfig data"
103
asg_tmpl_with_default_updt_policy = '''
105
"AWSTemplateFormatVersion" : "2010-09-09",
106
"Description" : "Template to create autoscaling group.",
111
"AutoScalingRollingUpdate" : {
114
"Type" : "AWS::AutoScaling::AutoScalingGroup",
116
"AvailabilityZones" : ["nova"],
117
"LaunchConfigurationName" : { "Ref" : "LaunchConfig" },
120
"LoadBalancerNames" : [ { "Ref" : "ElasticLoadBalancer" } ]
123
"ElasticLoadBalancer" : {
124
"Type" : "AWS::ElasticLoadBalancing::LoadBalancer",
126
"AvailabilityZones" : ["nova"],
128
"LoadBalancerPort" : "80",
129
"InstancePort" : "80",
135
"Type" : "AWS::AutoScaling::LaunchConfiguration",
137
"ImageId" : "F20-x86_64-cfntools",
138
"InstanceType" : "m1.medium",
140
"SecurityGroups" : [ "sg-1" ],
141
"UserData" : "jsconfig data"
148
asg_tmpl_with_updt_policy = '''
150
"AWSTemplateFormatVersion" : "2010-09-09",
151
"Description" : "Template to create autoscaling group.",
156
"AutoScalingRollingUpdate" : {
157
"MinInstancesInService" : "1",
158
"MaxBatchSize" : "2",
162
"Type" : "AWS::AutoScaling::AutoScalingGroup",
164
"AvailabilityZones" : ["nova"],
165
"LaunchConfigurationName" : { "Ref" : "LaunchConfig" },
168
"LoadBalancerNames" : [ { "Ref" : "ElasticLoadBalancer" } ]
171
"ElasticLoadBalancer" : {
172
"Type" : "AWS::ElasticLoadBalancing::LoadBalancer",
174
"AvailabilityZones" : ["nova"],
176
"LoadBalancerPort" : "80",
177
"InstancePort" : "80",
183
"Type" : "AWS::AutoScaling::LaunchConfiguration",
185
"ImageId" : "F20-x86_64-cfntools",
186
"InstanceType" : "m1.medium",
188
"SecurityGroups" : [ "sg-1" ],
189
"UserData" : "jsconfig data"
197
class AutoScalingGroupTest(common.HeatTestCase):
200
super(AutoScalingGroupTest, self).setUp()
201
self.fc = fakes_v1_1.FakeClient()
202
self.stub_keystoneclient(username='test_stack.CfnLBUser')
203
cfg.CONF.set_default('heat_waitcondition_server_url',
204
'http://127.0.0.1:8000/v1/waitcondition')
206
def get_launch_conf_name(self, stack, ig_name):
207
return stack[ig_name].properties['LaunchConfigurationName']
209
def test_parse_without_update_policy(self):
210
tmpl = template_format.parse(asg_tmpl_without_updt_policy)
211
stack = utils.parse_stack(tmpl)
212
self.stub_ImageConstraint_validate()
213
self.stub_KeypairConstraint_validate()
214
self.stub_FlavorConstraint_validate()
218
grp = stack['WebServerGroup']
219
self.assertFalse(grp.update_policy['AutoScalingRollingUpdate'])
222
def test_parse_with_update_policy(self):
223
tmpl = template_format.parse(asg_tmpl_with_updt_policy)
224
stack = utils.parse_stack(tmpl)
225
self.stub_ImageConstraint_validate()
226
self.stub_KeypairConstraint_validate()
227
self.stub_FlavorConstraint_validate()
231
tmpl_grp = tmpl['Resources']['WebServerGroup']
232
tmpl_policy = tmpl_grp['UpdatePolicy']['AutoScalingRollingUpdate']
233
tmpl_batch_sz = int(tmpl_policy['MaxBatchSize'])
234
grp = stack['WebServerGroup']
235
self.assertTrue(grp.update_policy)
236
self.assertEqual(1, len(grp.update_policy))
237
self.assertIn('AutoScalingRollingUpdate', grp.update_policy)
238
policy = grp.update_policy['AutoScalingRollingUpdate']
239
self.assertTrue(policy and len(policy) > 0)
240
self.assertEqual(1, int(policy['MinInstancesInService']))
241
self.assertEqual(tmpl_batch_sz, int(policy['MaxBatchSize']))
242
self.assertEqual('PT1S', policy['PauseTime'])
245
def test_parse_with_default_update_policy(self):
246
tmpl = template_format.parse(asg_tmpl_with_default_updt_policy)
247
stack = utils.parse_stack(tmpl)
248
self.stub_ImageConstraint_validate()
249
self.stub_KeypairConstraint_validate()
250
self.stub_FlavorConstraint_validate()
254
grp = stack['WebServerGroup']
255
self.assertTrue(grp.update_policy)
256
self.assertEqual(1, len(grp.update_policy))
257
self.assertIn('AutoScalingRollingUpdate', grp.update_policy)
258
policy = grp.update_policy['AutoScalingRollingUpdate']
259
self.assertTrue(policy and len(policy) > 0)
260
self.assertEqual(0, int(policy['MinInstancesInService']))
261
self.assertEqual(1, int(policy['MaxBatchSize']))
262
self.assertEqual('PT0S', policy['PauseTime'])
265
def test_parse_with_bad_update_policy(self):
266
self.stub_ImageConstraint_validate()
267
self.stub_KeypairConstraint_validate()
268
self.stub_FlavorConstraint_validate()
270
tmpl = template_format.parse(asg_tmpl_with_bad_updt_policy)
271
stack = utils.parse_stack(tmpl)
272
error = self.assertRaises(
273
exception.StackValidationFailed, stack.validate)
274
self.assertIn("foo", six.text_type(error))
276
def test_parse_with_bad_pausetime_in_update_policy(self):
277
self.stub_ImageConstraint_validate()
278
self.stub_KeypairConstraint_validate()
279
self.stub_FlavorConstraint_validate()
281
tmpl = template_format.parse(asg_tmpl_with_default_updt_policy)
282
group = tmpl['Resources']['WebServerGroup']
283
policy = group['UpdatePolicy']['AutoScalingRollingUpdate']
284
policy['PauseTime'] = 'P1YT1H'
285
stack = utils.parse_stack(tmpl)
286
error = self.assertRaises(
287
exception.StackValidationFailed, stack.validate)
288
self.assertIn("Only ISO 8601 duration format", six.text_type(error))
290
def validate_update_policy_diff(self, current, updated):
293
current_tmpl = template_format.parse(current)
294
current_stack = utils.parse_stack(current_tmpl)
296
# get the json snippet for the current InstanceGroup resource
297
current_grp = current_stack['WebServerGroup']
298
current_snippets = dict((n, r.parsed_template())
299
for n, r in current_stack.items())
300
current_grp_json = current_snippets[current_grp.name]
302
# load the updated stack
303
updated_tmpl = template_format.parse(updated)
304
updated_stack = utils.parse_stack(updated_tmpl)
306
# get the updated json snippet for the InstanceGroup resource in the
307
# context of the current stack
308
updated_grp = updated_stack['WebServerGroup']
309
updated_grp_json = function.resolve(updated_grp.t)
311
# identify the template difference
312
tmpl_diff = updated_grp.update_template_diff(
313
updated_grp_json, current_grp_json)
314
updated_policy = (updated_grp.t['UpdatePolicy']
315
if 'UpdatePolicy' in updated_grp.t else None)
316
expected = {u'UpdatePolicy': updated_policy}
317
self.assertEqual(expected, tmpl_diff)
319
# test application of the new update policy in handle_update
320
update_snippet = rsrc_defn.ResourceDefinition(
323
properties=updated_grp.t['Properties'],
324
update_policy=updated_policy)
325
current_grp._try_rolling_update = mock.MagicMock()
326
current_grp.adjust = mock.MagicMock()
327
current_grp.handle_update(update_snippet, tmpl_diff, None)
328
if updated_policy is None:
329
self.assertEqual({}, current_grp.update_policy.data)
331
self.assertEqual(updated_policy, current_grp.update_policy.data)
333
def test_update_policy_added(self):
334
self.validate_update_policy_diff(asg_tmpl_without_updt_policy,
335
asg_tmpl_with_updt_policy)
337
def test_update_policy_updated(self):
338
updt_template = json.loads(asg_tmpl_with_updt_policy)
339
grp = updt_template['Resources']['WebServerGroup']
340
policy = grp['UpdatePolicy']['AutoScalingRollingUpdate']
341
policy['MinInstancesInService'] = '2'
342
policy['MaxBatchSize'] = '4'
343
policy['PauseTime'] = 'PT1M30S'
344
self.validate_update_policy_diff(asg_tmpl_with_updt_policy,
345
json.dumps(updt_template))
347
def test_update_policy_removed(self):
348
self.validate_update_policy_diff(asg_tmpl_with_updt_policy,
349
asg_tmpl_without_updt_policy)