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
86 changes: 60 additions & 26 deletions control_plane/workflows/generic_web_deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -366,22 +366,21 @@ def execute_generic_web_deploy(
)
deploy_completed = True
if post_deploy_executor is not None:
post_deploy_update = _terminal_post_deploy_update(
post_deploy_executor(
control_plane_root,
record_store,
GenericWebPostDeployContext(
product=resolved_profile.product,
context=resolved_lane.context,
instance=resolved_lane.instance,
deployment_record_id=record_id,
target_name=resolved_target.target_name,
target_type=resolved_target.target_type,
target_id=resolved_target.target_id,
artifact_id=ship_request.artifact_id,
source_git_ref=ship_request.source_git_ref,
),
)
post_deploy_update = _run_post_deploy_extension(
control_plane_root=control_plane_root,
record_store=record_store,
context=GenericWebPostDeployContext(
product=resolved_profile.product,
context=resolved_lane.context,
instance=resolved_lane.instance,
deployment_record_id=record_id,
target_name=resolved_target.target_name,
target_type=resolved_target.target_type,
target_id=resolved_target.target_id,
artifact_id=ship_request.artifact_id,
source_git_ref=ship_request.source_git_ref,
),
post_deploy_executor=post_deploy_executor,
)
if post_deploy_update.status == "fail":
raise click.ClickException(
Expand All @@ -396,18 +395,36 @@ def execute_generic_web_deploy(
status="fail",
detail=str(exc),
)
record_store.write_deployment_record(
build_deployment_record(
request=ship_request,
record_id=record_id,
deployment_id="control-plane-dokploy",
deployment_status=deployment_status,
started_at=started_at,
finished_at=finished_at,
resolved_target=resolved_target,
post_deploy_update=post_deploy_update,
runtime_identity = (
_build_runtime_identity(
profile=resolved_profile,
lane=resolved_lane,
ship_request=ship_request,
deployment_record_id=record_id,
deployed_at=finished_at,
)
if deploy_completed
else None
)
deployment_record = build_deployment_record(
request=ship_request,
record_id=record_id,
deployment_id="control-plane-dokploy",
deployment_status=deployment_status,
started_at=started_at,
finished_at=finished_at,
resolved_target=resolved_target,
post_deploy_update=post_deploy_update,
runtime_identity=runtime_identity,
)
record_store.write_deployment_record(deployment_record)
if deploy_completed:
record_store.write_environment_inventory(
build_environment_inventory(
deployment_record=deployment_record,
updated_at=finished_at,
)
)
return GenericWebDeployResult(
deployment_record_id=record_id,
deploy_status=deployment_status,
Expand Down Expand Up @@ -479,3 +496,20 @@ def _terminal_post_deploy_update(
"Generic web post-deploy extensions must return terminal evidence."
)
return post_deploy_update


def _run_post_deploy_extension(
*,
control_plane_root: Path,
record_store: GenericWebDeployStore,
context: GenericWebPostDeployContext,
post_deploy_executor: GenericWebPostDeployExecutor,
) -> PostDeployUpdateEvidence:
try:
return _terminal_post_deploy_update(
post_deploy_executor(control_plane_root, record_store, context)
)
except click.ClickException:
raise
except Exception as exc:
raise click.ClickException(str(exc)) from exc
59 changes: 57 additions & 2 deletions tests/test_generic_web_deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,60 @@ def post_deploy(
self.assertEqual(result.error_message, "post deploy failed")
self.assertEqual(store.deployments[0].deploy.status, "pass")
self.assertEqual(store.deployments[0].post_deploy_update.status, "fail")
self.assertEqual(store.inventories, [])
self.assertIsNotNone(store.deployments[0].runtime_identity)
self.assertEqual(len(store.inventories), 1)
self.assertEqual(store.inventories[0].post_deploy_update.status, "fail")
self.assertEqual(
store.inventories[0].deployment_record_id,
store.deployments[0].record_id,
)

def test_execute_generic_web_deploy_records_unexpected_post_deploy_exception(
self,
) -> None:
store = _GenericWebDeployStore(_profile())

def post_deploy(
_root: Path,
_store: GenericWebDeployStore,
_context: GenericWebPostDeployContext,
) -> PostDeployUpdateEvidence:
raise RuntimeError("unexpected post deploy failure")

with (
patch(
"control_plane.workflows.generic_web_deploy.control_plane_dokploy.read_control_plane_dokploy_source_of_truth",
return_value=_source_of_truth(),
),
patch(
"control_plane.workflows.generic_web_deploy.control_plane_runtime_environments.resolve_runtime_environment_values",
return_value={},
),
patch(
"control_plane.workflows.generic_web_deploy.control_plane_dokploy.read_dokploy_config",
return_value=("https://dokploy.example", "token"),
),
patch("control_plane.workflows.generic_web_deploy.execute_dokploy_artifact_deploy"),
):
result = execute_generic_web_deploy(
control_plane_root=Path("."),
record_store=store,
request=_request(),
post_deploy_executor=post_deploy,
)

self.assertEqual(result.deploy_status, "pass")
self.assertEqual(result.post_deploy_status, "fail")
self.assertEqual(result.error_message, "unexpected post deploy failure")
self.assertEqual(store.deployments[0].deploy.status, "pass")
self.assertEqual(store.deployments[0].post_deploy_update.status, "fail")
self.assertEqual(
store.deployments[0].post_deploy_update.detail,
"unexpected post deploy failure",
)
self.assertIsNotNone(store.deployments[0].runtime_identity)
self.assertEqual(len(store.inventories), 1)
self.assertEqual(store.inventories[0].post_deploy_update.status, "fail")

def test_execute_generic_web_deploy_treats_returned_post_deploy_failure_as_failed_extension(
self,
Expand Down Expand Up @@ -314,7 +367,9 @@ def post_deploy(
self.assertEqual(store.deployments[0].deploy.status, "pass")
self.assertEqual(store.deployments[0].post_deploy_update.status, "fail")
self.assertEqual(store.deployments[0].post_deploy_update.detail, "returned failure")
self.assertEqual(store.inventories, [])
self.assertIsNotNone(store.deployments[0].runtime_identity)
self.assertEqual(len(store.inventories), 1)
self.assertEqual(store.inventories[0].post_deploy_update.status, "fail")

def test_execute_generic_web_deploy_uses_qualified_bare_tag(self) -> None:
store = _GenericWebDeployStore(_profile())
Expand Down