diff --git a/.envrc b/.envrc index 27f2f76..2854f9b 100644 --- a/.envrc +++ b/.envrc @@ -1,4 +1,4 @@ use flake -export RELEASE_NODE=frajtano-test@ +export RELEASE_NODE=frajtano-test@$(cat /etc/hostname) export FRAJTANO_DIR=$PWD/.frajtano_state diff --git a/default.nix b/default.nix index 8afc230..0e39100 100644 --- a/default.nix +++ b/default.nix @@ -3,9 +3,10 @@ pkgs, mixRelease, elixir, + fetchMixDeps, }: let pname = "frajtano"; - pkg = mixRelease { + pkg = mixRelease rec { inherit pname; version = "0.0.1"; @@ -16,6 +17,18 @@ filter = path: _type: baseNameOf path != "flake.nix" && baseNameOf path != "flake.lock"; }; + # Adapted from https://blog.eigenvalue.net/nix-rerunning-fixed-output-derivations/ + # deps hash should change any time ./mix.lock changes, and not otherwise + mixFodDeps = let + deps = fetchMixDeps { + pname = "mix-deps-${pname}"; + inherit version src; + sha256 = "sha256-4g5lUlr5+l+mNVp0InZkx6X31g2cckI8NNQp37QgBis="; + }; + hash = builtins.substring 11 32 "${./mix.lock}"; + in + deps.overrideAttrs (attrs: {name = "${deps.name}-${hash}";}); + ELIXIR_MAKE_CACHE_DIR = "/tmp/.elixir-make-cache"; meta.mainProgram = pname; }; @@ -25,14 +38,24 @@ file="$FRAJTANO_DIR/cookie" (umask 077; [ -f "$file" ] || ${pkgs.coreutils}/bin/head -c 128 /dev/urandom | ${pkgs.coreutils}/bin/base64 -w0 > "$file") export RELEASE_COOKIE=$(${pkgs.coreutils}/bin/cat "$file") - export RELEASE_NODE="frajtano-$(${pkgs.coreutils}/bin/whoami)@$(${pkgs.coreutils}/bin/cat /etc/hostname)" + [ -n $RELEASE_NODE ] || export RELEASE_NODE="frajtano-$(${pkgs.coreutils}/bin/whoami)@$(${pkgs.coreutils}/bin/cat /etc/hostname)" run() { exec ${lib.getExe pkg} "$@" } + list() { + for i in "$@"; do + echo "\"$i\", " + done + } + case "''${1:-}" in assimilate) - run rpc ":ok = \"$(echo -n "$2" | ${pkgs.coreutils}/bin/base64)\" |> Base.decode64!() |> Frajtano.Agent.add_peer()" + run rpc ":ok = \"$(echo -n "$2" | ${pkgs.coreutils}/bin/base64)\" |> Base.decode64!() |> Frajtano.Agent.assimilate()" + ;; + spawn) + shift + run rpc ":ok = Frajtano.Agent.spawn_peer({\"/usr/bin/env\", [$(list "$@")]})" ;; socket) echo $FRAJTANO_DIR/agent.sock @@ -42,4 +65,9 @@ ;; esac ''; -in pkgs.symlinkJoin { name = pname; paths = [ script pkg ]; meta.mainProgram = pname; } +in + pkgs.symlinkJoin { + name = pname; + paths = [script pkg]; + meta.mainProgram = pname; + } diff --git a/lib/agent.ex b/lib/agent.ex index b6244a5..4c291c2 100644 --- a/lib/agent.ex +++ b/lib/agent.ex @@ -56,9 +56,8 @@ defmodule Frajtano.Agent do end @impl true - def handle_call({:add_peer, path}, _from, state) do - # TODO: deduplicate peers by socket path - case Peer.start(path) do + def handle_call({:add_peer, spec}, _from, state) do + case Peer.start(spec) do {:ok, _} -> {:reply, :ok, state} {:error, error} -> {:reply, {:error, error}, state} end @@ -73,7 +72,11 @@ defmodule Frajtano.Agent do GenServer.call(__MODULE__, {:sign, request}, :infinity) end - def add_peer(path) do - GenServer.call(__MODULE__, {:add_peer, path}) + def assimilate(path) do + GenServer.call(__MODULE__, {:add_peer, {:socket, path}}) + end + + def spawn_peer(spec) do + GenServer.call(__MODULE__, {:add_peer, {:spawn, spec}}) end end diff --git a/lib/peer.ex b/lib/peer.ex index 808dc56..1891c85 100644 --- a/lib/peer.ex +++ b/lib/peer.ex @@ -3,20 +3,59 @@ defmodule Frajtano.Peer do require Logger use GenServer, restart: :temporary - def start(path) do - DynamicSupervisor.start_child(Frajtano.PeerSupervisor, {__MODULE__, path}) + defmodule Spawner do + use Supervisor, restart: :permanent + + def start_link(spec) do + Supervisor.start_link(__MODULE__, spec, []) + end + + @impl true + def init({executable, args}) do + Temp.track!() + path = Path.join(Temp.mkdir!(), "agent.sock") + + children = [ + Supervisor.child_spec({MuonTrap.Daemon, [executable, args ++ [path]]}, restart: :temporary, significant: true), + Supervisor.child_spec({Frajtano.Peer, {path, :spawned, {executable, args}}}, restart: :permanent) + ] + + Supervisor.init(children, strategy: :one_for_all, auto_shutdown: :any_significant) + end end - def start_link(path) do - GenServer.start_link(__MODULE__, path, name: {:via, Registry, {Frajtano.Peers, path}}) + def start({:socket, path}) do + DynamicSupervisor.start_child(Frajtano.PeerSupervisor, {__MODULE__, {path}}) + end + + def start({:spawn, spec}) do + DynamicSupervisor.start_child(Frajtano.PeerSupervisor, {Spawner, spec}) + end + + def start_link({path}) do + GenServer.start_link(__MODULE__, {path}, name: {:via, Registry, {Frajtano.Peers, path}}) + end + def start_link({_, _, _} = spec) do + GenServer.start_link(__MODULE__, spec, name: {:via, Registry, {Frajtano.Peers, spec}}) end @impl true - def init(path) do + def init({path}) do {:ok, conn} = :gen_tcp.connect({:local, path}, 0, [:binary, active: :once]) {:ok, %{conn: conn, clients: :queue.new(), buffer: <<>>}} end + @impl true + def init({path, :spawned, _}) do + if File.exists?(path) do + {:ok, conn} = :gen_tcp.connect({:local, path}, 0, [:binary, active: :once]) + {:ok, %{conn: conn, clients: :queue.new(), buffer: <<>>}} + else + Process.sleep(100) + init({path, :spawned, nil}) + end + end + def reply({client, ref}, msg) do send(client, {ref, msg}) end diff --git a/mix.exs b/mix.exs index ff47929..68d63c6 100644 --- a/mix.exs +++ b/mix.exs @@ -19,6 +19,9 @@ defmodule Frajtano.MixProject do end defp deps do - [] + [ + {:muontrap, "~> 1.0"}, + {:temp, "~> 0.4"}, + ] end end diff --git a/mix.lock b/mix.lock index 8b13789..996ae01 100644 --- a/mix.lock +++ b/mix.lock @@ -1 +1,5 @@ - +%{ + "elixir_make": {:hex, :elixir_make, "0.8.4", "4960a03ce79081dee8fe119d80ad372c4e7badb84c493cc75983f9d3bc8bde0f", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.0", [hex: :certifi, repo: "hexpm", optional: true]}], "hexpm", "6e7f1d619b5f61dfabd0a20aa268e575572b542ac31723293a4c1a567d5ef040"}, + "muontrap": {:hex, :muontrap, "1.5.0", "bf5c273872379968615a39974458328209ac97fa1f588396192131ff973d1ca2", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "daf605e877f60b5be9215e3420d7971fc468677b29921e40915b15fd928273d4"}, + "temp": {:hex, :temp, "0.4.9", "eb6355bfa7925a568b3d9eb3bb57e89aa6d2b78bfe8dfb6b698e090631b7f41f", [:mix], [], "hexpm", "bc8bf7b27d9105bef933ef4bf4ba37ac6b899dbeba329deaa88c60b62d6b4b6d"}, +}