Load LB_ env vars as app secrets (#1615)

This commit is contained in:
Alexandre de Souza 2023-01-04 11:10:33 -03:00 committed by GitHub
parent 9e328e83ca
commit d4bda6042c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 74 additions and 22 deletions

View file

@ -46,6 +46,7 @@ defmodule Livebook.Application do
case Supervisor.start_link(children, opts) do case Supervisor.start_link(children, opts) do
{:ok, _} = result -> {:ok, _} = result ->
load_lb_env_vars()
clear_env_vars() clear_env_vars()
display_startup_info() display_startup_info()
insert_development_hub() insert_development_hub()
@ -169,6 +170,16 @@ defmodule Livebook.Application do
end end
end end
defp load_lb_env_vars do
secrets =
for {"LB_" <> name = var, value} <- System.get_env() do
System.delete_env(var)
%Livebook.Secrets.Secret{name: name, value: value}
end
Livebook.Secrets.set_temporary_secrets(secrets)
end
defp config_env_var?("LIVEBOOK_" <> _), do: true defp config_env_var?("LIVEBOOK_" <> _), do: true
defp config_env_var?("RELEASE_" <> _), do: true defp config_env_var?("RELEASE_" <> _), do: true
defp config_env_var?(_), do: false defp config_env_var?(_), do: false

View file

@ -6,14 +6,19 @@ defmodule Livebook.Secrets do
alias Livebook.Storage alias Livebook.Storage
alias Livebook.Secrets.Secret alias Livebook.Secrets.Secret
@temporary_key :livebook_temporary_secrets
@doc """ @doc """
Get the secrets list from storage. Get the secrets list from storage.
""" """
@spec fetch_secrets() :: list(Secret.t()) @spec fetch_secrets() :: list(Secret.t())
def fetch_secrets() do def fetch_secrets do
temporary_secrets = :persistent_term.get(@temporary_key, [])
for fields <- Storage.all(:secrets) do for fields <- Storage.all(:secrets) do
struct!(Secret, Map.delete(fields, :id)) struct!(Secret, Map.delete(fields, :id))
end end
|> Enum.concat(temporary_secrets)
|> Enum.sort() |> Enum.sort()
end end
@ -69,6 +74,14 @@ defmodule Livebook.Secrets do
:ok :ok
end end
@doc """
Sets additional secrets that are kept only in memory.
"""
@spec set_temporary_secrets(list(Secret.t())) :: :ok
def set_temporary_secrets(secrets) do
:persistent_term.put(@temporary_key, secrets)
end
@doc """ @doc """
Subscribe to secrets updates. Subscribe to secrets updates.

View file

@ -703,22 +703,24 @@ defmodule LivebookWeb.SessionLive do
<span class="text-sm font-mono break-all flex-row"> <span class="text-sm font-mono break-all flex-row">
<%= secret_value %> <%= secret_value %>
</span> </span>
<button <%= if Secrets.secret_exists?(secret_name) do %>
id={"app-secret-#{secret_name}-delete"} <button
type="button" id={"app-secret-#{secret_name}-delete"}
phx-click={ type="button"
with_confirm( phx-click={
JS.push("delete_app_secret", value: %{secret_name: secret_name}), with_confirm(
title: "Delete app secret - #{secret_name}", JS.push("delete_app_secret", value: %{secret_name: secret_name}),
description: "Are you sure you want to delete this app secret?", title: "Delete app secret - #{secret_name}",
confirm_text: "Delete", description: "Are you sure you want to delete this app secret?",
confirm_icon: "delete-bin-6-line" confirm_text: "Delete",
) confirm_icon: "delete-bin-6-line"
} )
class="hover:text-red-600" }
> class="hover:text-red-600"
<.remix_icon icon="delete-bin-line" /> >
</button> <.remix_icon icon="delete-bin-line" />
</button>
<% end %>
</div> </div>
</div> </div>
</div> </div>

View file

@ -5,12 +5,28 @@ defmodule Livebook.SecretsTest do
alias Livebook.Secrets alias Livebook.Secrets
alias Livebook.Secrets.Secret alias Livebook.Secrets.Secret
test "fetch secrets" do describe "fetch_secrets/0" do
Secrets.set_secret(%Secret{name: "FOO", value: "111"}) test "returns a list of secrets from storage" do
assert %Secret{name: "FOO", value: "111"} in Secrets.fetch_secrets() secret = %Secret{name: "FOO", value: "111"}
Secrets.unset_secret("FOO") Secrets.set_secret(secret)
refute %Secret{name: "FOO", value: "111"} in Secrets.fetch_secrets() assert secret in Secrets.fetch_secrets()
Secrets.unset_secret(secret.name)
refute secret in Secrets.fetch_secrets()
end
test "returns a list of secrets from temporary storage" do
secret = %Secret{name: "BAR", value: "222"}
Secrets.set_temporary_secrets([secret])
assert secret in Secrets.fetch_secrets()
# We can't delete from temporary storage, since it will be deleted
# on next startup, if not provided
Secrets.unset_secret(secret.name)
assert secret in Secrets.fetch_secrets()
end
end end
test "fetch an specific secret" do test "fetch an specific secret" do

View file

@ -1036,6 +1036,16 @@ defmodule LivebookWeb.SessionLiveTest do
|> element("span", "Add secret") |> element("span", "Add secret")
|> has_element?() |> has_element?()
end end
test "loads secret from temporary storage", %{conn: conn, session: session} do
secret = %Secret{name: "FOOBARBAZ", value: "ChonkyCat"}
Livebook.Secrets.set_temporary_secrets([secret])
{:ok, view, _} = live(conn, "/sessions/#{session.id}")
assert render(view) =~ secret.name
assert render(view) =~ secret.value
end
end end
describe "environment variables" do describe "environment variables" do