-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement terraform http state backend
This along with a agent-specified `_override.tf` file will allow us to serve as a built-in state store for terraform.
- Loading branch information
1 parent
76f9dc0
commit 1ac5f18
Showing
13 changed files
with
324 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
defmodule Console.Deployments.Stacks.State do | ||
use Console.Services.Base | ||
import Console.Deployments.Policies | ||
alias Console.Schema.TerraformState | ||
|
||
def get_tf(stack_id), do: Repo.get_by(TerraformState, stack_id: stack_id) | ||
|
||
def get_terraform_state(stack_id, actor) do | ||
case get_tf(stack_id) do | ||
%TerraformState{} = state -> allow(state, actor, :state) | ||
nil -> | ||
stack = Console.Deployments.Stacks.get_stack!(stack_id) | ||
with {:ok, _} <- allow(stack, actor, :state), | ||
do: {:ok, nil} | ||
end | ||
end | ||
|
||
def update_terraform_state(state, stack_id, actor) do | ||
case get_tf(stack_id) do | ||
%TerraformState{} = state -> state | ||
_ -> %TerraformState{stack_id: stack_id} | ||
end | ||
|> TerraformState.changeset(%{state: state}) | ||
|> allow(actor, :state) | ||
|> when_ok(&Repo.insert_or_update/1) | ||
end | ||
|
||
def lock_terraform_state(%{"id" => id} = lock_info, stack_id, actor) do | ||
case get_tf(stack_id) do | ||
nil -> {:error, :not_found} | ||
%TerraformState{lock: %{id: ^id}} = state -> set_lock(lock_info, state, actor) | ||
%TerraformState{lock: %{id: _} = lock} -> {:error, {:locked, lock}} | ||
state -> set_lock(lock_info, state, actor) | ||
end | ||
end | ||
|
||
def unlock_terraform_state(stack_id, actor) do | ||
get_tf(stack_id) | ||
|> TerraformState.changeset(%{lock: nil}) | ||
|> allow(actor, :state) | ||
|> when_ok(:update) | ||
end | ||
|
||
defp set_lock(lock_info, state, actor) do | ||
TerraformState.changeset(state, %{lock: lock_info}) | ||
|> allow(actor, :state) | ||
|> when_ok(:update) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
defmodule Console.Schema.TerraformState do | ||
use Piazza.Ecto.Schema | ||
alias Console.Schema.Stack | ||
|
||
schema "terraform_states" do | ||
field :state, :binary | ||
|
||
embeds_one :lock, Lock, on_replace: :update, primary_key: false do | ||
field :id, :string | ||
field :operation, :string | ||
field :info, :string | ||
field :who, :string | ||
field :version, :string | ||
field :created, :string | ||
field :path, :string | ||
end | ||
|
||
belongs_to :stack, Stack | ||
|
||
timestamps() | ||
end | ||
|
||
def changeset(model, attrs \\ %{}) do | ||
model | ||
|> cast(attrs, ~w(state stack_id)a) | ||
|> cast_embed(:lock, with: &lock_changeset/2) | ||
|> unique_constraint(:stack_id) | ||
end | ||
|
||
def lock_changeset(model, attrs) do | ||
cast(model, attrs, ~w(id operation info who version created path)a) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
defmodule ConsoleWeb.StackController do | ||
use ConsoleWeb, :controller | ||
alias Console.Deployments.Stacks.State | ||
|
||
plug :get_actor when action in [:get_tf_state, :update_tf_state, :lock_tf_state, :unlock_tf_state] | ||
|
||
def get_tf_state(conn, %{"stack_id" => id}) do | ||
case State.get_terraform_state(id, conn.assigns.actor) do | ||
{:ok, %{state: state}} -> send_resp(conn, 200, state) | ||
{:ok, nil} -> send_resp(conn, 204, "") | ||
_ -> send_resp(conn, 403, "Forbidden") | ||
end | ||
end | ||
|
||
def update_tf_state(conn, %{"stack_id" => id}) do | ||
%{raw_body: state, actor: actor} = conn.assigns | ||
|
||
IO.iodata_to_binary(state) | ||
|> State.update_terraform_state(id, actor) | ||
|> handle_resp(conn) | ||
end | ||
|
||
def lock_tf_state(conn, %{"stack_id" => id} = lock) do | ||
State.lock_terraform_state(lock, id, conn.assigns.actor) | ||
|> handle_resp(conn) | ||
end | ||
|
||
def unlock_tf_state(conn, %{"stack_id" => id}) do | ||
State.unlock_terraform_state(id, conn.assigns.actor) | ||
|> handle_resp(conn) | ||
end | ||
|
||
defp handle_resp({:error, {:locked, lock}}, conn) do | ||
put_resp_header(conn, "content-type", "application/json") | ||
|> send_resp(423, Poison.encode!(Map.from_struct(lock))) | ||
end | ||
|
||
defp handle_resp({:error, :forbidden}, conn), do: send_resp(conn, 403, "Forbidden") | ||
defp handle_resp({:error, err}, conn), do: send_resp(conn, 400, inspect(err)) | ||
|
||
defp handle_resp({:ok, _}, conn), do: send_resp(conn, 200, "") | ||
|
||
defp get_actor(conn, _) do | ||
with {_, token} <- Plug.BasicAuth.parse_basic_auth(conn), | ||
%{} = actor <- Console.authed_user(token) do | ||
assign(conn, :actor, actor) | ||
else | ||
_ -> send_resp(conn, 403, "Forbidden") |> halt() | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
20 changes: 20 additions & 0 deletions
20
priv/repo/migrations/20240526120358_add_terraform_state.exs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
defmodule Console.Repo.Migrations.AddTerraformState do | ||
use Ecto.Migration | ||
|
||
def change do | ||
create table(:terraform_states, primary_key: false) do | ||
add :id, :uuid, primary_key: true | ||
add :stack_id, references(:stacks, type: :uuid, on_delete: :delete_all) | ||
add :state, :binary | ||
add :lock, :map | ||
|
||
timestamps() | ||
end | ||
|
||
alter table(:stacks) do | ||
add :manage_state, :boolean, default: false | ||
end | ||
|
||
create unique_index(:terraform_states, [:stack_id]) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.