Current section

Files

Jump to
sops_config_provider lib sops_config_provider.ex
Raw

lib/sops_config_provider.ex

defmodule SopsConfigProvider do
@behaviour Config.Provider
alias SopsConfigProvider.State
alias SopsConfigProvider.Utils
@sops_module Application.compile_env(
:sops_config_provider,
:sops_module,
SopsConfigProvider.Sops
)
defmodule InitStateError do
defexception message: ~s"""
Please set appropriate options maps as second parameter on the release.
Example:
release: [
my_app: [
config_providers: [
{SopsConfigProvider, %{app_name: :my_app, secret_file_path: "/path", sops_binary_path: "/usr/local/bin/sops"}}
]
]
]
"""
end
@impl true
@spec init(State.t() | map() | term()) :: State.t()
def init(%State{} = state), do: state
def init(state) when is_map(state), do: state |> State.new!()
def init(_),
do: raise(InitStateError)
@impl true
def load(config, %State{mappings: mappings, env_override: env_override} = state) do
checked_state = @sops_module.check_sops_availability!(state)
sops_config =
checked_state
|> get_paths()
|> Enum.reduce([], fn path, acc ->
file_config =
%{checked_state | secret_file_path: path}
|> Utils.resolve_secret_file_location!()
|> Utils.get_file_type()
|> @sops_module.decrypt!()
|> Utils.convert_to_keyword_list!()
Config.Reader.merge(acc, file_config)
end)
|> apply_mappings(mappings)
|> apply_env_overrides(env_override)
Config.Reader.merge(config, sops_config)
end
defp get_paths(%State{secret_file_path: path}) do
path |> String.split(",") |> Enum.map(&String.trim/1)
end
defp apply_mappings(sops_config, mappings) when map_size(mappings) == 0, do: sops_config
defp apply_mappings(sops_config, mappings) do
Enum.map(sops_config, fn {app, kw} ->
app_mappings = Map.get(mappings, app, %{})
{app, apply_app_mappings(kw, app_mappings)}
end)
end
defp apply_app_mappings(kw, app_mappings) when map_size(app_mappings) == 0, do: kw
defp apply_app_mappings(kw, app_mappings) do
{mapped, unmapped} =
Enum.split_with(kw, fn {key, _} -> Map.has_key?(app_mappings, key) end)
module_configs =
Enum.reduce(mapped, %{}, fn {key, value}, acc ->
{module, nested_key} = Map.get(app_mappings, key)
Map.update(acc, module, [{nested_key, value}], &[{nested_key, value} | &1])
end)
unmapped ++ Enum.map(module_configs, fn {mod, kw} -> {mod, kw} end)
end
defp apply_env_overrides(sops_config, false), do: sops_config
defp apply_env_overrides(sops_config, true) do
Enum.map(sops_config, fn {app, kw} ->
{app, override_flat_keys(app, kw)}
end)
end
defp override_flat_keys(app, kw) do
Enum.map(kw, fn
{key, val} when is_atom(key) ->
env_var = derive_env_var(app, key)
case System.get_env(env_var) do
nil -> {key, val}
"" -> {key, val}
override -> {key, override}
end
other ->
other
end)
end
defp derive_env_var(app, key) do
app_str = app |> Atom.to_string() |> String.upcase()
key_str = key |> Atom.to_string() |> String.upcase()
"#{app_str}_#{key_str}"
end
end