Wire a full tenant deployment as one orchestrated, compensating saga: mark → create droplet → wait active → register in inventory → link to deployment → point DNS → activate. A failure anywhere rolls the whole thing back — droplet destroyed, DNS reverted, deployment moved to cancelled. - New lifecycle state `provisioning`; deployments created via the provision path enter here and only reach `active` once the saga's ActivateDeployment step runs. - Four new steps: MarkDeploymentProvisioning (owns the deployment's failure state), LinkDeploymentResource, PointDeploymentDns, ActivateDeployment. - Provisioning.provision_deployment/2 assembles + starts the saga. - DeploymentController: POST /deployments with provision:true creates in `provisioning` and kicks the saga (202); GET /deployments/:id now returns the provisioning saga + per-step progress. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
58 lines
1.5 KiB
Elixir
58 lines
1.5 KiB
Elixir
defmodule ArcadiaCloud.Provisioning.Steps.MarkDeploymentProvisioning do
|
|
@moduledoc """
|
|
First step of the deployment-provisioning choreography.
|
|
|
|
Forward: a no-op — `Provisioning.provision_deployment/2` already
|
|
created the deployment row in the `provisioning` state. Having this
|
|
step at index 0 gives the saga a compensation hook that owns the
|
|
deployment's failure state: if ANY later step fails, the runner walks
|
|
compensation back to here and we move the deployment to `cancelled`.
|
|
|
|
Without this step a mid-saga failure would leave the deployment stuck
|
|
in `provisioning` forever.
|
|
"""
|
|
|
|
@behaviour ArcadiaCloud.Provisioning.Step
|
|
|
|
require Logger
|
|
|
|
alias ArcadiaCloud.Deployments
|
|
|
|
@impl true
|
|
def name, do: "mark_deployment_provisioning"
|
|
|
|
@impl true
|
|
def execute(state), do: {:ok, state}
|
|
|
|
@impl true
|
|
def compensate(state) do
|
|
case deployment(state) do
|
|
nil ->
|
|
:ok
|
|
|
|
deployment ->
|
|
case Deployments.transition_state(deployment, "cancelled",
|
|
reason: "provision_failed",
|
|
actor: "saga:#{state.saga_id}"
|
|
) do
|
|
{:ok, _} ->
|
|
:ok
|
|
|
|
{:error, reason} ->
|
|
Logger.warning(
|
|
"[saga #{state.saga_id}] could not cancel deployment on rollback: #{inspect(reason)}"
|
|
)
|
|
|
|
:ok
|
|
end
|
|
end
|
|
end
|
|
|
|
defp deployment(state) do
|
|
case state.saga && state.saga.deployment_id do
|
|
nil -> nil
|
|
id -> Deployments.get_deployment(id)
|
|
end
|
|
end
|
|
end
|