diff --git a/heat/engine/service_software_config.py b/heat/engine/service_software_config.py index c90f14f58a..6d036d1b43 100644 --- a/heat/engine/service_software_config.py +++ b/heat/engine/service_software_config.py @@ -108,22 +108,37 @@ class SoftwareConfigService(object): deployments = self.metadata_software_deployments(cnxt, server_id) md = rs.rsrc_metadata or {} md['deployments'] = deployments - rows_updated = db_api.resource_update( - cnxt, rs.id, {'rsrc_metadata': md}, rs.atomic_key) - if not rows_updated: - action = _('deployments of server %s') % server_id - raise exception.ConcurrentTransaction(action=action) metadata_put_url = None metadata_queue_id = None + metadata_headers = None for rd in rs.data: if rd.key == 'metadata_put_url': metadata_put_url = rd.value if rd.key == 'metadata_queue_id': metadata_queue_id = rd.value + if metadata_put_url: + data = requests.head(metadata_put_url) + etag = data.headers.get('etag') + if etag: + metadata_headers = {'if-match': etag} + else: + LOG.warning('Couldn\'t find existing Swift metadata') + + rows_updated = db_api.resource_update( + cnxt, rs.id, {'rsrc_metadata': md}, rs.atomic_key) + if not rows_updated: + LOG.debug('Conflict on database deployment update, retrying') + action = _('deployments of server %s') % server_id + raise exception.ConcurrentTransaction(action=action) if metadata_put_url: json_md = jsonutils.dumps(md) - requests.put(metadata_put_url, json_md) + resp = requests.put(metadata_put_url, json_md, + headers=metadata_headers) + if resp.status_code == 412: + LOG.debug('Conflict on Swift deployment update, retrying') + action = _('deployments of server %s') % server_id + raise exception.ConcurrentTransaction(action=action) if metadata_queue_id: project = stack_user_project_id queue = self._get_zaqar_queue(cnxt, rs, project, metadata_queue_id) diff --git a/heat/tests/engine/service/test_software_config.py b/heat/tests/engine/service/test_software_config.py index c99d15565e..38c23eeb78 100644 --- a/heat/tests/engine/service/test_software_config.py +++ b/heat/tests/engine/service/test_software_config.py @@ -692,8 +692,9 @@ class SoftwareConfigServiceTest(common.HeatTestCase): @mock.patch.object(db_api, 'resource_update') @mock.patch.object(db_api, 'resource_get_by_physical_resource_id') @mock.patch.object(service_software_config.requests, 'put') + @mock.patch.object(service_software_config.requests, 'head') def test_push_metadata_software_deployments_temp_url( - self, put, res_get, res_upd, md_sd): + self, head, put, res_get, res_upd, md_sd): rs = mock.Mock() rs.rsrc_metadata = {'original': 'metadata'} rs.id = '1234' @@ -705,6 +706,14 @@ class SoftwareConfigServiceTest(common.HeatTestCase): res_get.return_value = rs res_upd.return_value = 1 + head_response = mock.Mock() + head_response.headers = {'etag': '1234'} + head_response.status_code = 200 + head.return_value = head_response + + put_response = mock.Mock() + put_response.status_code = 201 + deployments = {'deploy': 'this'} md_sd.return_value = deployments @@ -719,7 +728,8 @@ class SoftwareConfigServiceTest(common.HeatTestCase): self.ctx, '1234', {'rsrc_metadata': result_metadata}, 1) put.assert_called_once_with( - 'http://192.168.2.2/foo/bar', json.dumps(result_metadata)) + 'http://192.168.2.2/foo/bar', json.dumps(result_metadata), + headers={'if-match': '1234'}) @mock.patch.object(service_software_config.SoftwareConfigService, 'metadata_software_deployments')