Skip to content

Commit d56017f

Browse files
authored
reset proposed change status on unexpected merge failure (#9164)
* reset proposed change state on unexpected merge failure * add changelog * improve error handling
1 parent 8689f7d commit d56017f

3 files changed

Lines changed: 80 additions & 22 deletions

File tree

backend/infrahub/proposed_change/tasks.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -226,16 +226,27 @@ async def merge_proposed_change(
226226
)
227227

228228
log.info("Proposed change is eligible to be merged")
229+
230+
merge_succeeded = False
229231
try:
230232
await merge_branch(branch=source_branch.name, context=context, proposed_change_id=proposed_change_id)
233+
merge_succeeded = True
231234
except MergeFailedError as exc:
232-
await _proposed_change_transition_state(
233-
proposed_change=proposed_change,
234-
state=ProposedChangeState.OPEN,
235-
database=db,
236-
user_id=context.account.account_id,
237-
)
238-
return Failed(message=f"Merge failure when trying to merge {exc.message}")
235+
return Failed(message=exc.message)
236+
except Exception as exc:
237+
log.exception("Unexpected failure during proposed change merge")
238+
return Failed(message=f"Merge failure when trying to merge {source_branch.name}: {exc}")
239+
except BaseException as exc:
240+
log.exception("Unexpected failure during proposed change merge")
241+
raise exc
242+
finally:
243+
if not merge_succeeded:
244+
await _proposed_change_transition_state(
245+
proposed_change=proposed_change,
246+
state=ProposedChangeState.OPEN,
247+
database=db,
248+
user_id=context.account.account_id,
249+
)
239250

240251
log.info(f"Branch {source_branch.name} has been merged successfully")
241252

backend/tests/component/graphql/mutations/test_proposed_change.py

Lines changed: 61 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,14 @@
1616
from infrahub.core.manager import NodeManager
1717
from infrahub.core.node import Node
1818
from infrahub.core.schema.schema_branch import SchemaBranch
19-
from infrahub.core.timestamp import Timestamp
2019
from infrahub.database import InfrahubDatabase
2120
from infrahub.graphql.initialization import prepare_graphql_params
22-
from infrahub.message_bus.types import KVTTL
2321
from infrahub.permissions import AssignedPermissions, PermissionBackend
2422
from infrahub.proposed_change.constants import ProposedChangeState
2523
from infrahub.proposed_change.models import RequestProposedChangePipeline
2624
from infrahub.services import InfrahubServices
2725
from infrahub.services.adapters.workflow.local import WorkflowLocalExecution
2826
from infrahub.services.component import InfrahubComponent
29-
from infrahub.worker import WORKER_IDENTITY
3027
from infrahub.workers.dependencies import build_client
3128
from infrahub.workflows.catalogue import REQUEST_PROPOSED_CHANGE_PIPELINE
3229
from infrahub.workflows.initialization import setup_deployments, setup_worker_pools
@@ -617,18 +614,7 @@ async def test_merge_proposed_change_permission_failure(
617614
await setup_deployments(prefect_client)
618615

619616
branch_name = "merge-proposed-change-perm"
620-
branch = await create_branch(branch_name=branch_name, db=db)
621-
await service.cache.set(
622-
key=f"workers:schema_hash:branch:{str(branch.get_uuid)}:{service.component_type.value}:worker:{WORKER_IDENTITY}",
623-
value=branch.active_schema_hash.main,
624-
expires=KVTTL.TWO_HOURS,
625-
)
626-
await service.cache.set(
627-
key=f"workers:active:{service.component_type.value}:worker:{WORKER_IDENTITY}",
628-
value=Timestamp().to_string(),
629-
expires=KVTTL.FIFTEEN,
630-
)
631-
await service.component.refresh_heartbeat()
617+
await create_branch(branch_name=branch_name, db=db)
632618

633619
proposed_change = await Node.init(db=db, schema=InfrahubKind.PROPOSEDCHANGE)
634620
await proposed_change.new(
@@ -658,6 +644,66 @@ async def test_merge_proposed_change_permission_failure(
658644
assert not update_status.errors
659645

660646

647+
class TestMergeProposedChangeUnexpectedFailure(TestInfrahubApp):
648+
async def test_merge_proposed_change_unexpected_failure_resets_state(
649+
self,
650+
db: InfrahubDatabase,
651+
default_permission_backend: None,
652+
register_core_models_schema: None,
653+
session_admin: AccountSession,
654+
client: InfrahubClient,
655+
dependency_provider: Provider,
656+
) -> None:
657+
with dependency_provider.scope(build_client, lambda: client):
658+
cache = MemoryCache()
659+
message_bus = BusRecorder()
660+
service = await InfrahubServices.new(
661+
database=db,
662+
message_bus=message_bus,
663+
workflow=WorkflowLocalExecution(),
664+
cache=cache,
665+
client=client,
666+
component=InfrahubComponent(
667+
cache=cache, db=db, message_bus=message_bus, component_type=ComponentType.NONE
668+
),
669+
)
670+
671+
async with get_client(sync_client=False) as prefect_client:
672+
await setup_worker_pools(client=prefect_client)
673+
await setup_deployments(prefect_client)
674+
675+
branch_name = "merge-proposed-change-fail"
676+
await create_branch(branch_name=branch_name, db=db)
677+
678+
proposed_change = await Node.init(db=db, schema=InfrahubKind.PROPOSEDCHANGE)
679+
await proposed_change.new(
680+
db=db, name="pc-merge-fail-1234", destination_branch="main", source_branch=branch_name, state="open"
681+
)
682+
await proposed_change.save(db=db)
683+
684+
with patch(
685+
"infrahub.proposed_change.tasks.merge_branch",
686+
side_effect=RuntimeError("simulated post-merge failure"),
687+
):
688+
update_status = await graphql_mutation(
689+
query=UPDATE_PROPOSED_CHANGE,
690+
db=db,
691+
variables={"proposed_change": proposed_change.id, "state": "merged"},
692+
account_session=session_admin,
693+
service=service,
694+
)
695+
696+
assert update_status.errors
697+
assert update_status.errors[0].message == (
698+
f"Merge failure when trying to merge {branch_name}: simulated post-merge failure"
699+
)
700+
701+
refreshed_pc = await NodeManager.get_one(db=db, id=proposed_change.id, raise_on_error=True)
702+
assert refreshed_pc.get_attribute("state").value.value == ProposedChangeState.OPEN.value
703+
branch = await Branch.get_by_name(db=db, name=branch_name)
704+
assert branch.status == BranchStatus.OPEN
705+
706+
661707
async def test_create_thread(
662708
db: InfrahubDatabase,
663709
register_core_models_schema: None,

changelog/+53a1cf44.fixed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Reset a Proposed Change's status to "open" following a merge failure that raises an unexpected error.

0 commit comments

Comments
 (0)