Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 29 additions & 4 deletions src/inmanta/data_pg.py
Original file line number Diff line number Diff line change
Expand Up @@ -1828,13 +1828,13 @@ def done(self) -> int:
return self._done

@classmethod
async def _get_status_field(cls, environment, values):
async def _get_status_field(cls, environment: uuid.UUID, values: str) -> dict:
"""
This field is required to ensure backward compatibility on the API.
"""
result = {}
values = json.loads(values)
for value_entry in values:
value_entry = json.loads(value_entry)
entry_uuid = str(uuid.uuid5(environment, value_entry['id']))
result[entry_uuid] = value_entry
return result
Expand All @@ -1850,10 +1850,10 @@ async def get_list(cls, order_by_column=None, order="ASC", limit=None, offset=No
limit_statement = f"LIMIT {limit} " if limit is not None and limit > 0 else ""
offset_statement = f"OFFSET {offset} " if offset is not None and offset > 0 else ""
query = f"SELECT c.*, SUM(CASE WHEN r.status NOT IN({transient_states}) THEN 1 ELSE 0 END) AS done ," + \
Comment thread
bartv marked this conversation as resolved.
f"array(SELECT jsonb_build_object('status', r2.status, 'id', r2.resource_version_id) " + \
f"to_json(array(SELECT jsonb_build_object('status', r2.status, 'id', r2.resource_version_id) " + \
f" FROM {Resource.table_name()} AS r2 " \
f" WHERE c.environment=r2.environment AND c.version=r2.model" \
f" ) AS status " + \
f" )) AS status " + \
f"FROM {cls.table_name()} AS c LEFT OUTER JOIN {Resource.table_name()} AS r " + \
f"ON c.environment = r.environment AND c.version = r.model " + \
f"{where_statement} " + \
Expand Down Expand Up @@ -1989,6 +1989,31 @@ async def mark_done(self):
self.result = const.VersionState[result]
self.deployed = True

@classmethod
async def mark_done_if_done(cls, environment, version):
query = f"""UPDATE {ConfigurationModel.table_name()}
SET deployed=True,
result=(CASE WHEN (
EXISTS(SELECT 1
FROM {Resource.table_name()}
WHERE environment=$1 AND model=$2 AND status != $3)
)::boolean
THEN $4::versionstate
ELSE $5::versionstate END
)
WHERE environment=$1 AND version=$2 AND
total=(SELECT COUNT(*)
FROM Resource
WHERE environment=$1 AND model=$2 AND status NOT IN('available', 'deploying'
)
)"""
values = [cls._get_value(environment),
cls._get_value(version),
cls._get_value(ResourceState.deployed),
cls._get_value(const.VersionState.failed),
cls._get_value(const.VersionState.success)]
await cls._execute_query(query, *values)

async def get_increment(self):
"""
Find resources incremented by this version compared to deployment state transitions per resource
Expand Down
6 changes: 1 addition & 5 deletions src/inmanta/server/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -1450,11 +1450,7 @@ def resource_action_update(self, env, resource_ids, action_id, action, started,
if "purged" in res.attributes and res.attributes["purged"] and status == const.ResourceState.deployed:
yield data.Parameter.delete_all(environment=env.id, resource_id=res.resource_id)

model = yield data.ConfigurationModel.get_version(env.id, model_version)

if model.done == model.total:
LOGGER.info("marking model %s %d as done", env.id, model_version)
yield model.mark_done()
yield data.ConfigurationModel.mark_done_if_done(env.id, model_version)

waiting_agents = set([(Id.parse_id(prov).get_agent_name(), res.resource_version_id)
for res in resources for prov in res.provides])
Expand Down
44 changes: 44 additions & 0 deletions tests/test_data_pg.py
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,50 @@ async def test_model_set_ready(init_dataclasses_and_load_schema):
assert cm.done == 1


@pytest.mark.parametrize("resource_state, should_be_deployed", [
(const.ResourceState.unavailable, True),
(const.ResourceState.skipped, True),
(const.ResourceState.dry, True),
(const.ResourceState.deployed, True),
(const.ResourceState.failed, True),
(const.ResourceState.deploying, False),
(const.ResourceState.available, False),
(const.ResourceState.cancelled, True),
(const.ResourceState.undefined, True),
(const.ResourceState.skipped_for_undefined, True),
(const.ResourceState.processing_events, True),
])
@pytest.mark.asyncio
async def test_model_mark_done_if_done(init_dataclasses_and_load_schema, resource_state, should_be_deployed):
project = data.Project(name="test")
await project.insert()

env = data.Environment(name="dev", project=project.id, repo_url="", repo_branch="")
await env.insert()

version = int(time.time())
cm = data.ConfigurationModel(environment=env.id, version=version, date=datetime.datetime.now(), total=1, version_info={})
await cm.insert()

assert cm.done == 0

path = "/etc/file"
key = "std::File[agent1,path=" + path + "]"
resource = data.Resource.new(environment=env.id, resource_version_id=key + ",v=%d" % version,
attributes={"path": path})
await resource.insert()

assert not cm.deployed
await data.ConfigurationModel.mark_done_if_done(env.id, cm.version)
cm = await data.ConfigurationModel.get_one(version=version, environment=env.id)
assert not cm.deployed

await resource.update_fields(status=resource_state)
await data.ConfigurationModel.mark_done_if_done(env.id, cm.version)
cm = await data.ConfigurationModel.get_one(version=version, environment=env.id)
assert cm.deployed == should_be_deployed


@pytest.mark.asyncio
async def test_model_get_list(init_dataclasses_and_load_schema):
project = data.Project(name="test")
Expand Down