Skip to content

Commit

Permalink
Merge pull request #81 from upmaru/feature/handle-component-instance-…
Browse files Browse the repository at this point in the history
…update

Add ability to modify component instance
  • Loading branch information
zacksiri authored Dec 14, 2023
2 parents 7ef635b + e0ccca5 commit ed4bd28
Show file tree
Hide file tree
Showing 12 changed files with 289 additions and 9 deletions.
12 changes: 12 additions & 0 deletions lib/uplink/clients/instellar.ex
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,18 @@ defmodule Uplink.Clients.Instellar do
to: Component,
as: :show

defdelegate get_component_instance(component_id, component_instance_id),
to: Component.Instance,
as: :get

defdelegate update_component_instance(
component_id,
component_instance_id,
params
),
to: Component.Instance,
as: :update

defdelegate create_component_instance(component_id, params),
to: Component.Instance,
as: :create
Expand Down
43 changes: 43 additions & 0 deletions lib/uplink/clients/instellar/component/instance.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,26 @@
defmodule Uplink.Clients.Instellar.Component.Instance do
alias Uplink.Clients.Instellar

def get(component_id, component_instance_id) do
[
Instellar.endpoint(),
"self",
"components",
"#{component_id}",
"instances",
"#{component_instance_id}"
]
|> Path.join()
|> Req.get!(headers: Instellar.Self.headers())
|> case do
%{status: 200, body: %{"data" => %{"attributes" => attributes}}} ->
{:ok, attributes}

%{status: _, body: body} ->
{:error, body}
end
end

def create(component_id, params) do
[
Instellar.endpoint(),
Expand All @@ -22,4 +42,27 @@ defmodule Uplink.Clients.Instellar.Component.Instance do
{:error, body}
end
end

def update(component_id, component_instance_id, params) do
[
Instellar.endpoint(),
"self",
"components",
"#{component_id}",
"instances",
"#{component_instance_id}"
]
|> Path.join()
|> Req.patch!(
json: params,
headers: Instellar.Self.headers()
)
|> case do
%{status: 200, body: %{"data" => %{"attributes" => attributes}}} ->
{:ok, attributes}

%{status: _, body: body} ->
{:error, body}
end
end
end
57 changes: 57 additions & 0 deletions lib/uplink/components/instance/modify.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
defmodule Uplink.Components.Instance.Modify do
use Oban.Worker, queue: :components, max_attempts: 1

alias Uplink.Drivers
alias Uplink.Clients.Instellar

def perform(%Oban.Job{args: %{"component_id" => component_id} = job_params}) do
case Instellar.get_component(component_id) do
{:ok, component_params} ->
handle_perform(component_params, job_params)

{:error, error} ->
{:error, error}
end
end

defp handle_perform(
%{
"generator" => %{"module" => module},
"credential" => credential_params
},
%{
"component_id" => component_id,
"component_instance_id" => component_instance_id,
"variable_id" => variable_id
} = job_args
) do
options =
job_args
|> Map.get("arguments", %{})
|> Enum.map(fn {k, v} -> {String.to_atom(k), v} end)

with {:ok, component_instance_attributes} <-
Instellar.get_component_instance(component_id, component_instance_id),
{:ok, credential} <-
Drivers.perform(
:modify,
module,
%{
"credential" => credential_params,
"component_instance" => component_instance_attributes
},
options
),
{:ok, component_instance_attributes} <-
Instellar.update_component_instance(
component_id,
component_instance_id,
%{
"variable_id" => variable_id,
"instance" => %{"credential" => Map.from_struct(credential)}
}
) do
{:ok, component_instance_attributes}
end
end
end
1 change: 1 addition & 0 deletions lib/uplink/components/instance/provision.ex
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ defmodule Uplink.Components.Instance.Provision do

with {:ok, credential} <-
Drivers.perform(
:provision,
module,
%{"credential" => credential_params},
options
Expand Down
7 changes: 4 additions & 3 deletions lib/uplink/drivers.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ defmodule Uplink.Drivers do
}

defmodule Behaviour do
@callback perform(map(), Keyword.t()) :: {:ok, map()}
@callback provision(map(), Keyword.t()) :: {:ok, map()}
@callback modify(map(), Keyword.t()) :: {:ok, map()}
end

def perform(module, params, options) do
def perform(call, module, params, options) do
driver = Map.fetch!(@driver_mapping, module)
driver.perform(params, options)
apply(driver, call, [params, options])
end
end
23 changes: 22 additions & 1 deletion lib/uplink/drivers/bucket.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ defmodule Uplink.Drivers.Bucket do
defmodule Aws do
@behaviour Uplink.Drivers.Behaviour

def perform(%{"credential" => credential_params}, options \\ []) do
def provision(%{"credential" => credential_params}, options \\ []) do
credential_params = Map.put(credential_params, "type", "component")

with {:ok, master_credential} <-
Expand All @@ -15,5 +15,26 @@ defmodule Uplink.Drivers.Bucket do
{:ok, generated_credential}
end
end

def modify(
%{
"credential" => credential_params,
"component_instance" => component_instance_attributes
},
options \\ []
) do
credential_params = Map.put(credential_params, "type", "component")

with {:ok, master_credential} <-
Formation.S3.Credential.create(credential_params),
{:ok, updated_credential} <-
Formation.Aws.update_credential_and_bucket(
master_credential,
component_instance_attributes,
options
) do
{:ok, updated_credential}
end
end
end
end
12 changes: 11 additions & 1 deletion lib/uplink/drivers/database.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ defmodule Uplink.Drivers.Database do
defmodule Postgresql do
@behaviour Uplink.Drivers.Behaviour

def perform(%{"credential" => credential_params}, options \\ []) do
def provision(%{"credential" => credential_params}, options \\ []) do
with {:ok, master_credential} <-
Formation.Postgresql.Credential.create(credential_params),
{:ok, generated_credential} <-
Expand All @@ -13,5 +13,15 @@ defmodule Uplink.Drivers.Database do
{:ok, generated_credential}
end
end

def modify(
%{
"credential" => credential_params,
"component_instance" => _component_instance_attributes
},
_options \\ []
) do
Formation.Postgresql.Credential.create(credential_params)
end
end
end
4 changes: 3 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ defmodule Uplink.MixProject do
end

# Specifies which paths to compile per environment.
defp elixirc_paths(:test), do: ["lib", "test/scenarios", "test/fixture"]
defp elixirc_paths(:test),
do: ["lib", "test/support", "test/scenarios", "test/fixtures"]

defp elixirc_paths(_), do: ["lib"]

# Run "mix help compile.app" to learn about applications.
Expand Down
4 changes: 2 additions & 2 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
"eventful": {:hex, :eventful, "0.2.3", "dc795c95b2d00d90b3a5d58c66bd0188b39be08b9da61a743ac40186fd313034", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "a1607d9d98ebc45e47573a81c11adfb571259a1db900748c6bee2dc25e5e171c"},
"finch": {:hex, :finch, "0.16.0", "40733f02c89f94a112518071c0a91fe86069560f5dbdb39f9150042f44dcfb1a", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6 or ~> 1.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f660174c4d519e5fec629016054d60edd822cdfe2b7270836739ac2f97735ec5"},
"formation": {:hex, :formation, "0.13.2", "69c580bc63905b52bb281a44c2e361a9cb3bed5f96f05d6937cd94b232831381", [:mix], [{:aws, "~> 0.13.0", [hex: :aws, repo: "hexpm", optional: false]}, {:ecto, "~> 3.10", [hex: :ecto, repo: "hexpm", optional: false]}, {:finch, "~> 0.16.0", [hex: :finch, repo: "hexpm", optional: false]}, {:lexdee, "~> 2.3", [hex: :lexdee, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.17.1", [hex: :postgrex, repo: "hexpm", optional: false]}], "hexpm", "bdca88c687e4d06595d358202dd93426a6fc13df9e4def4a59164dd43b8edc74"},
"formation": {:hex, :formation, "0.14.0", "a4c2b66ab722091e66aa2a44581ac3f75ef0a4e063bae6e48bccea7cd0585776", [:mix], [{:aws, "~> 0.13.0", [hex: :aws, repo: "hexpm", optional: false]}, {:ecto, "~> 3.10", [hex: :ecto, repo: "hexpm", optional: false]}, {:finch, "~> 0.16.0", [hex: :finch, repo: "hexpm", optional: false]}, {:lexdee, "~> 2.3", [hex: :lexdee, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.17.1", [hex: :postgrex, repo: "hexpm", optional: false]}], "hexpm", "db929831575063acc67ba40d12270ae09b327c7861c65423e5aaeef25eb36a60"},
"hackney": {:hex, :hackney, "1.20.1", "8d97aec62ddddd757d128bfd1df6c5861093419f8f7a4223823537bad5d064e2", [:rebar3], [{:certifi, "~> 2.12.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "fe9094e5f1a2a2c0a7d10918fee36bfec0ec2a979994cff8cfe8058cd9af38e3"},
"hpax": {:hex, :hpax, "0.1.2", "09a75600d9d8bbd064cdd741f21fc06fc1f4cf3d0fcc335e5aa19be1a7235c84", [:mix], [], "hexpm", "2c87843d5a23f5f16748ebe77969880e29809580efdaccd615cd3bed628a8c13"},
"httpoison": {:hex, :httpoison, "1.8.2", "9eb9c63ae289296a544842ef816a85d881d4a31f518a0fec089aaa744beae290", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "2bb350d26972e30c96e2ca74a1aaf8293d61d0742ff17f01e0279fef11599921"},
Expand All @@ -32,7 +32,7 @@
"mint_web_socket": {:hex, :mint_web_socket, "1.0.3", "aab42fff792a74649916236d0b01f560a0b3f03ca5dea693c230d1c44736b50e", [:mix], [{:mint, ">= 1.4.1 and < 2.0.0-0", [hex: :mint, repo: "hexpm", optional: false]}], "hexpm", "ca3810ca44cc8532e3dce499cc17f958596695d226bb578b2fbb88c09b5954b0"},
"mox": {:hex, :mox, "1.1.0", "0f5e399649ce9ab7602f72e718305c0f9cdc351190f72844599545e4996af73c", [:mix], [], "hexpm", "d44474c50be02d5b72131070281a5d3895c0e7a95c780e90bc0cfe712f633a13"},
"nebulex": {:hex, :nebulex, "2.5.1", "8ffbde30643e76d6cec712281ca68ab05f73170de9e758a39bc7e4e6987f608f", [:mix], [{:decorator, "~> 1.4", [hex: :decorator, repo: "hexpm", optional: true]}, {:shards, "~> 1.1", [hex: :shards, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "8d0d3800d98c68ee19b229b7fe35fac0192ab5963a573612cf74a388e083bccf"},
"nimble_options": {:hex, :nimble_options, "1.0.2", "92098a74df0072ff37d0c12ace58574d26880e522c22801437151a159392270e", [:mix], [], "hexpm", "fd12a8db2021036ce12a309f26f564ec367373265b53e25403f0ee697380f1b8"},
"nimble_options": {:hex, :nimble_options, "1.1.0", "3b31a57ede9cb1502071fade751ab0c7b8dbe75a9a4c2b5bbb0943a690b63172", [:mix], [], "hexpm", "8bbbb3941af3ca9acc7835f5655ea062111c9c27bcac53e004460dfd19008a99"},
"nimble_pool": {:hex, :nimble_pool, "1.0.0", "5eb82705d138f4dd4423f69ceb19ac667b3b492ae570c9f5c900bb3d2f50a847", [:mix], [], "hexpm", "80be3b882d2d351882256087078e1b1952a28bf98d0a287be87e4a24a710b67a"},
"oban": {:hex, :oban, "2.15.2", "8f934a49db39163633965139c8846d8e24c2beb4180f34a005c2c7c3f69a6aa2", [:mix], [{:ecto_sql, "~> 3.6", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ecto_sqlite3, "~> 0.9", [hex: :ecto_sqlite3, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "0f4a579ea48fc7489e0d84facf8b01566e142bdc6542d7dabce32c10e664f1e9"},
"parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"},
Expand Down
File renamed without changes.
133 changes: 133 additions & 0 deletions test/uplink/components/instance/modify_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
defmodule Uplink.Components.Instance.ModifyTest do
use ExUnit.Case
use Oban.Testing, repo: Uplink.Repo

alias Uplink.Components.Instance

import Mox

@cors_config """
[
{
"AllowedHeaders": ["*"],
"AllowedMethods": ["PUT"],
"AllowedOrigins": ["*"],
"ExposeHeaders": []
}
]
"""

@component_instance_attributes %{
"id" => 1,
"current_state" => "active",
"credential" => %{
"type" => "instance",
"username" => "someaccesskey",
"password" => "somesecretkey",
"host" => "s3.amazonaws.com",
"resource" => "us-east-1"
}
}

setup do
bypass = Bypass.open()

Application.put_env(
:uplink,
Uplink.Clients.Instellar,
endpoint: "http://localhost:#{bypass.port}/uplink"
)

{:ok, bypass: bypass}
end

describe "s3 bucket" do
test "modify s3 bucket cors config", %{bypass: bypass} do
Uplink.Drivers.Bucket.AwsMock
|> expect(:modify, fn _params, options ->
assert [acl: _, cors: _] = options

{:ok,
%Formation.S3.Credential{
secret_access_key: "somesecret",
bucket: "some-bucket-name",
access_key_id: "someaccesskey",
endpoint: "s3.us-east-1.s3endpoint.com",
region: "us-east-1",
cors: Jason.decode!(@cors_config)
}}
end)

Bypass.expect_once(
bypass,
"GET",
"/uplink/self/components/1",
fn conn ->
conn
|> Plug.Conn.put_resp_header("content-type", "application/json")
|> Plug.Conn.resp(
200,
Jason.encode!(%{
"data" => %{
"attributes" => %{
"generator" => %{"module" => "bucket/aws-s3"},
"credential" => %{
"type" => "component",
"username" => "someaccesskey",
"password" => "somesecretkey",
"host" => "s3.amazonaws.com",
"resource" => "us-east-1"
}
}
}
})
)
end
)

Bypass.expect_once(
bypass,
"GET",
"/uplink/self/components/1/instances/1",
fn conn ->
conn
|> Plug.Conn.put_resp_header("content-type", "application/json")
|> Plug.Conn.resp(
200,
Jason.encode!(%{
"data" => %{
"attributes" => @component_instance_attributes
}
})
)
end
)

Bypass.expect_once(
bypass,
"PATCH",
"/uplink/self/components/1/instances/1",
fn conn ->
conn
|> Plug.Conn.put_resp_header("content-type", "application/json")
|> Plug.Conn.resp(
200,
Jason.encode!(%{
"data" => %{
"attributes" => @component_instance_attributes
}
})
)
end
)

assert {:ok, _result} =
perform_job(Instance.Modify, %{
component_id: "1",
variable_id: "1",
component_instance_id: "1",
arguments: %{"acl" => "private", "cors" => @cors_config}
})
end
end
end
2 changes: 1 addition & 1 deletion test/uplink/components/instance/provision_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ defmodule Uplink.Components.Instance.ProvisionTest do
describe "aws s3 bucket" do
test "provision aws s3 bucket", %{bypass: bypass} do
Uplink.Drivers.Bucket.AwsMock
|> expect(:perform, fn _params, options ->
|> expect(:provision, fn _params, options ->
assert [acl: "private"] == options

{:ok, %Formation.S3.Credential{}}
Expand Down

0 comments on commit ed4bd28

Please sign in to comment.