Implement update_stack
This would definitely be desirable for shade users. Specifically an ansible module should to a update_stack if the stack already exists. If there are no changes the operation would be idempotent. Change-Id: I833027883a4eed11f4568760d44ffec87889021f
This commit is contained in:
parent
395cf48cbf
commit
40b30a6178
4
releasenotes/notes/stack-update-5886e91fd6e423bf.yaml
Normal file
4
releasenotes/notes/stack-update-5886e91fd6e423bf.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
---
|
||||
features:
|
||||
- Implement update_stack to perform the update action on existing
|
||||
orchestration stacks.
|
@ -764,6 +764,11 @@ class StackCreate(task_manager.Task):
|
||||
return client.heat_client.stacks.create(**self.args)
|
||||
|
||||
|
||||
class StackUpdate(task_manager.Task):
|
||||
def main(self, client):
|
||||
return client.heat_client.stacks.update(**self.args)
|
||||
|
||||
|
||||
class StackDelete(task_manager.Task):
|
||||
def main(self, client):
|
||||
return client.heat_client.stacks.delete(self.args['id'])
|
||||
|
@ -877,6 +877,71 @@ class OpenStackCloud(object):
|
||||
action='CREATE')
|
||||
return self.get_stack(name)
|
||||
|
||||
def update_stack(
|
||||
self, name_or_id,
|
||||
template_file=None, template_url=None,
|
||||
template_object=None, files=None,
|
||||
rollback=True,
|
||||
wait=False, timeout=3600,
|
||||
environment_files=None,
|
||||
**parameters):
|
||||
"""Update a Heat Stack.
|
||||
|
||||
:param string name_or_id: Name or id of the stack to update.
|
||||
:param string template_file: Path to the template.
|
||||
:param string template_url: URL of template.
|
||||
:param string template_object: URL to retrieve template object.
|
||||
:param dict files: dict of additional file content to include.
|
||||
:param boolean rollback: Enable rollback on update failure.
|
||||
:param boolean wait: Whether to wait for the delete to finish.
|
||||
:param int timeout: Stack update timeout in seconds.
|
||||
:param list environment_files: Paths to environment files to apply.
|
||||
|
||||
Other arguments will be passed as stack parameters which will take
|
||||
precedence over any parameters specified in the environments.
|
||||
|
||||
Only one of template_file, template_url, template_object should be
|
||||
specified.
|
||||
|
||||
:returns: a dict containing the stack description
|
||||
|
||||
:raises: ``OpenStackCloudException`` if something goes wrong during
|
||||
the openstack API calls
|
||||
"""
|
||||
envfiles, env = template_utils.process_multiple_environments_and_files(
|
||||
env_paths=environment_files)
|
||||
tpl_files, template = template_utils.get_template_contents(
|
||||
template_file=template_file,
|
||||
template_url=template_url,
|
||||
template_object=template_object,
|
||||
files=files)
|
||||
params = dict(
|
||||
stack_id=name_or_id,
|
||||
disable_rollback=not rollback,
|
||||
parameters=parameters,
|
||||
template=template,
|
||||
files=dict(list(tpl_files.items()) + list(envfiles.items())),
|
||||
environment=env,
|
||||
timeout_mins=timeout // 60,
|
||||
)
|
||||
if wait:
|
||||
# find the last event to use as the marker
|
||||
events = event_utils.get_events(self.heat_client,
|
||||
name_or_id,
|
||||
event_args={'sort_dir': 'desc',
|
||||
'limit': 1})
|
||||
marker = events[0].id if events else None
|
||||
|
||||
with _utils.heat_exceptions("Error updating stack {name}".format(
|
||||
name=name_or_id)):
|
||||
self.manager.submitTask(_tasks.StackUpdate(**params))
|
||||
if wait:
|
||||
event_utils.poll_for_events(self.heat_client,
|
||||
name_or_id,
|
||||
action='UPDATE',
|
||||
marker=marker)
|
||||
return self.get_stack(name_or_id)
|
||||
|
||||
def delete_stack(self, name_or_id):
|
||||
"""Delete a Heat Stack
|
||||
|
||||
|
@ -120,6 +120,29 @@ class TestStack(base.TestCase):
|
||||
stack_ids = [s['id'] for s in stacks]
|
||||
self.assertIn(stack['id'], stack_ids)
|
||||
|
||||
# update with no changes
|
||||
stack = self.cloud.update_stack(self.stack_name,
|
||||
template_file=test_template.name,
|
||||
wait=True)
|
||||
|
||||
# assert no change in updated stack
|
||||
self.assertEqual('UPDATE_COMPLETE', stack['stack_status'])
|
||||
rand = stack['outputs'][0]['output_value']
|
||||
self.assertEqual(rand, stack['outputs'][0]['output_value'])
|
||||
|
||||
# update with changes
|
||||
stack = self.cloud.update_stack(self.stack_name,
|
||||
template_file=test_template.name,
|
||||
wait=True,
|
||||
length=12)
|
||||
|
||||
# assert changed output in updated stack
|
||||
stack = self.cloud.get_stack(self.stack_name)
|
||||
self.assertEqual('UPDATE_COMPLETE', stack['stack_status'])
|
||||
new_rand = stack['outputs'][0]['output_value']
|
||||
self.assertNotEqual(rand, new_rand)
|
||||
self.assertEqual(12, len(new_rand))
|
||||
|
||||
def test_stack_nested(self):
|
||||
|
||||
test_template = tempfile.NamedTemporaryFile(
|
||||
|
@ -148,6 +148,46 @@ class TestStack(base.TestCase):
|
||||
self.assertEqual(1, mock_poll.call_count)
|
||||
self.assertEqual(stack, ret)
|
||||
|
||||
@mock.patch.object(template_utils, 'get_template_contents')
|
||||
@mock.patch.object(shade.OpenStackCloud, 'heat_client')
|
||||
def test_update_stack(self, mock_heat, mock_template):
|
||||
mock_template.return_value = ({}, {})
|
||||
self.cloud.update_stack('stack_name')
|
||||
self.assertTrue(mock_template.called)
|
||||
mock_heat.stacks.update.assert_called_once_with(
|
||||
stack_id='stack_name',
|
||||
disable_rollback=False,
|
||||
environment={},
|
||||
parameters={},
|
||||
template={},
|
||||
files={},
|
||||
timeout_mins=60,
|
||||
)
|
||||
|
||||
@mock.patch.object(event_utils, 'poll_for_events')
|
||||
@mock.patch.object(template_utils, 'get_template_contents')
|
||||
@mock.patch.object(shade.OpenStackCloud, 'get_stack')
|
||||
@mock.patch.object(shade.OpenStackCloud, 'heat_client')
|
||||
def test_update_stack_wait(self, mock_heat, mock_get, mock_template,
|
||||
mock_poll):
|
||||
stack = {'id': 'stack_id', 'name': 'stack_name'}
|
||||
mock_template.return_value = ({}, {})
|
||||
mock_get.return_value = stack
|
||||
ret = self.cloud.update_stack('stack_name', wait=True)
|
||||
self.assertTrue(mock_template.called)
|
||||
mock_heat.stacks.update.assert_called_once_with(
|
||||
stack_id='stack_name',
|
||||
disable_rollback=False,
|
||||
environment={},
|
||||
parameters={},
|
||||
template={},
|
||||
files={},
|
||||
timeout_mins=60,
|
||||
)
|
||||
self.assertEqual(1, mock_get.call_count)
|
||||
self.assertEqual(1, mock_poll.call_count)
|
||||
self.assertEqual(stack, ret)
|
||||
|
||||
@mock.patch.object(shade.OpenStackCloud, 'heat_client')
|
||||
def test_get_stack(self, mock_heat):
|
||||
stack = fakes.FakeStack('azerty', 'stack',)
|
||||
|
Loading…
x
Reference in New Issue
Block a user