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