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>
46 lines
1.4 KiB
Elixir
46 lines
1.4 KiB
Elixir
defmodule ArcadiaCloud.Deployments.CloudDeployment do
|
|
use Ecto.Schema
|
|
import Ecto.Changeset
|
|
|
|
@primary_key {:id, :binary_id, autogenerate: true}
|
|
@foreign_key_type :binary_id
|
|
|
|
@states ~w(provisioning trial active past_due paused suspended cancelled archived)
|
|
@llm_modes ~w(managed byo none)
|
|
|
|
def states, do: @states
|
|
|
|
schema "cloud_deployments" do
|
|
field :tenant_id, :string
|
|
field :slug, :string
|
|
field :display_name, :string
|
|
field :template_code, :string
|
|
field :template_version, :string
|
|
field :region, :string
|
|
field :state, :string, default: "active"
|
|
field :state_since, :utc_datetime
|
|
field :llm_mode, :string, default: "none"
|
|
field :billing_action_suspended, :boolean, default: false
|
|
|
|
belongs_to :cloud_project, ArcadiaCloud.Cloud.CloudProject
|
|
|
|
timestamps(type: :utc_datetime)
|
|
end
|
|
|
|
@required ~w(tenant_id slug state state_since)a
|
|
@optional ~w(display_name template_code template_version region llm_mode
|
|
billing_action_suspended cloud_project_id)a
|
|
|
|
def changeset(deployment, attrs) do
|
|
deployment
|
|
|> cast(attrs, @required ++ @optional)
|
|
|> validate_required(@required)
|
|
|> validate_inclusion(:state, @states)
|
|
|> validate_inclusion(:llm_mode, @llm_modes)
|
|
|> validate_format(:slug, ~r/^[a-z0-9][a-z0-9-]*$/,
|
|
message: "must be lowercase alphanumeric + hyphens"
|
|
)
|
|
|> unique_constraint([:tenant_id, :slug])
|
|
end
|
|
end
|