diff --git a/lib/sshkit.ex b/lib/sshkit.ex index 79c3ca5c..2aca011d 100644 --- a/lib/sshkit.ex +++ b/lib/sshkit.ex @@ -308,17 +308,27 @@ defmodule SSHKit do assert "Hello World!\n" == stdout ``` """ - def run(context, command) do + def run(context, command, mode \\ :sequential, timeout \\ :infinity) do cmd = Context.build(context, command) - run = fn host -> + op = fn host -> {:ok, conn} = SSH.connect(host.name, host.options) - res = SSH.run(conn, cmd) + res = SSH.run(conn, cmd, [timeout: timeout]) :ok = SSH.close(conn) res end - Enum.map(context.hosts, run) + perform(context.hosts, op, mode) + end + + defp perform(hosts, op, :sequential) do + Enum.map(hosts, op) + end + + defp perform(hosts, op, :parallel) do + hosts + |> Enum.map(fn host -> Task.async(fn -> op.(host) end) end) + |> Enum.map(fn task -> Task.await(task, :infinity) end) end @doc ~S""" diff --git a/test/sshkit_functional_test.exs b/test/sshkit_functional_test.exs index 58723d4e..9d51cc10 100644 --- a/test/sshkit_functional_test.exs +++ b/test/sshkit_functional_test.exs @@ -17,6 +17,22 @@ defmodule SSHKitFunctionalTest do assert name == host.options[:user] end + @tag boot: [@bootconf] + test "connects as the login user and runs commands in parallel", %{hosts: [host]} do + begin_time = Time.utc_now() + [{:ok, output1, 0},{:ok, output2, 0}] = + [host, host] + |> SSHKit.context() + |> SSHKit.run("sleep 2; id -un", :parallel) + end_time = Time.utc_now() + run_time = Time.diff(end_time, begin_time, :second) + + assert run_time < 4 + assert String.trim(stdout(output1)) == host.options[:user] + assert String.trim(stdout(output2)) == host.options[:user] + + end + @tag boot: [@bootconf] test "runs commands and returns their output and exit status", %{hosts: [host]} do context = SSHKit.context(host)