lib/caddy/bootstrap.ex

defmodule Caddy.Bootstrap do
  @moduledoc """

  Caddy Bootstrap

  Start Caddy Bootstrap

  """
  require Logger
  alias Caddy.Config

  use GenServer

  def get(key) do
    GenServer.call(__MODULE__, {:get, key})
  end

  def restart() do
    GenServer.stop(__MODULE__)
  end

  def start_link(args) do
    GenServer.start_link(__MODULE__, args, name: __MODULE__)
  end

  def init(_args) do
    Logger.info("Caddy Bootstrap init")

    with true <- Config.ensure_path_exists(),
         :ok <- stop_exists_server(),
         bin_path <- Config.get(:caddy_bin),
         {version, 0} <- System.cmd(bin_path, ["version"]),
         {modules, 0} <- get_modules(bin_path),
         {:ok, envfile} <- init_env_file(),
         {env, 0} <- get_env(bin_path),
         {:ok, config_path} <- init_config_file() do
      state = %{
        version: version,
        bin_path: bin_path,
        config_path: config_path,
        envfile: envfile,
        env: env,
        pidfile: Config.pid_file(),
        modules: modules
      }

      {:ok, state}
    else
      error ->
        Logger.error("Caddy Bootstrap [init] error: #{inspect(error)}")
        {:stop, error}
    end
  end

  def handle_call({:get, key}, _from, state) do
    value = Map.get(state, key)
    {:reply, value, state}
  end

  def stop_exists_server() do
    pidfile = Config.pid_file()

    if pidfile |> File.exists?() do
      pid = pidfile |> File.read!() |> String.trim()
      Logger.info("Caddy Bootstrap pidfile exists: #{pid}")
      File.rm(pidfile)
      System.cmd("kill", ["-9", "#{pid}"])
      :ok
    else
      :ok
    end
  end

  defp init_config_file() do
    with config <- Config.get(:config),
         {:ok, cfg} <- Jason.encode(config),
         :ok <- File.write(Config.init_file(), cfg) do
      {:ok, Config.init_file()}
    else
      error ->
        Logger.error("[init_config_file] error: #{inspect(error)}")
        {:error, error}
    end
  end

  defp init_env_file() do
    envfile = Caddy.Config.env_file()

    case File.write(envfile, Caddy.Config.env()) do
      :ok ->
        {:ok, envfile}

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

  defp get_modules(bin_path) do
    case System.cmd(bin_path, ["list-modules"]) do
      {ms, 0} ->
        modules = ms |> String.split("\n") |> Enum.filter(fn l -> String.match?(l, ~r/^\S+/) end)
        {modules, 0}

      {output, code} ->
        {output, code}
    end
  end

  defp get_env(bin_path) do
    case System.cmd(bin_path, ["environ", "--envfile", Config.env_file()]) do
      {env, 0} ->
        {env, 0}

      {output, code} ->
        {output, code}
    end
  end
end