defmodule ArcadiaCloud.Provisioning.Steps.LinkDeploymentResource do @moduledoc """ Attaches the freshly-registered cloud resource (set by RegisterDroplet as `cloud_resource_id` in context) to the saga's deployment: stamps `deployment_id` and `tenant_id` onto the `cloud_resources` row. This is what makes the resource show up under the deployment in inventory and bill against the right tenant. Idempotent: re-running just re-writes the same two columns. Compensation: clears `deployment_id` and `tenant_id` back to nil. The resource row itself (and the droplet) are undone by RegisterDroplet / CreateDroplet compensation. """ @behaviour ArcadiaCloud.Provisioning.Step import Ecto.Query alias ArcadiaCloud.Deployments alias ArcadiaCloud.Cloud.CloudResource alias ArcadiaCloud.Provisioning.SagaState alias ArcadiaCloud.Repo @impl true def name, do: "link_deployment_resource" @impl true def execute(state) do with {:ok, resource_id} <- fetch(state, :cloud_resource_id), {:ok, deployment} <- fetch_deployment(state) do {_, _} = from(r in CloudResource, where: r.id == ^resource_id) |> Repo.update_all( set: [ deployment_id: deployment.id, tenant_id: deployment.tenant_id, updated_at: DateTime.utc_now() |> DateTime.truncate(:second) ] ) {:ok, state} end end @impl true def compensate(state) do case SagaState.get_output(state, :cloud_resource_id) do nil -> :ok resource_id -> from(r in CloudResource, where: r.id == ^resource_id) |> Repo.update_all(set: [deployment_id: nil, tenant_id: nil]) :ok end end defp fetch(state, key) do case SagaState.get_output(state, key) do nil -> {:error, {:missing_context, key}} value -> {:ok, value} end end defp fetch_deployment(state) do case state.saga && state.saga.deployment_id do nil -> {:error, :saga_has_no_deployment} id -> case Deployments.get_deployment(id) do nil -> {:error, :deployment_not_found} deployment -> {:ok, deployment} end end end end