From 3806c94919943db33868d14444f1445f42077a6e Mon Sep 17 00:00:00 2001 From: Seungjin Kim Date: Mon, 27 Aug 2018 22:08:22 +0900 Subject: [PATCH 1/6] Allowing executing on hosts asynchronously. --- lib/sshkit.ex | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/sshkit.ex b/lib/sshkit.ex index 79c3ca5c..18d69556 100644 --- a/lib/sshkit.ex +++ b/lib/sshkit.ex @@ -321,6 +321,21 @@ defmodule SSHKit do Enum.map(context.hosts, run) end + def run(context, command, timeout \\ 10000, mode: :parallel) do + cmd = Context.build(context, command) + + run = fn host -> + {:ok, conn} = SSH.connect(host.name, host.options) + res = SSH.run(conn, cmd) + :ok = SSH.close(conn) + res + end + + Enum.map(context.hosts, fn h -> Task.async(fn -> run.(h) end) end) + |> Enum.map(fn n -> Task.await(n,timeout) end) + + end + @doc ~S""" Upload a file or files to the given context. From 9a7236507dad3cd471b3a793616e1c3a78c33223 Mon Sep 17 00:00:00 2001 From: Seungjin Kim Date: Thu, 30 Aug 2018 23:02:11 +0900 Subject: [PATCH 2/6] Internal run function is now shared with sequential and parallel runs. Advice from @pmeinhardt, https://github.com/bitcrowd/sshkit.ex/issues/40#issuecomment-416975435 --- lib/sshkit.ex | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/lib/sshkit.ex b/lib/sshkit.ex index 18d69556..50055715 100644 --- a/lib/sshkit.ex +++ b/lib/sshkit.ex @@ -308,34 +308,29 @@ defmodule SSHKit do assert "Hello World!\n" == stdout ``` """ - def run(context, command) do + def run(context, command, mode \\ :sequential) 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) :ok = SSH.close(conn) res end - Enum.map(context.hosts, run) + perform(context.hosts, op, mode) end - def run(context, command, timeout \\ 10000, mode: :parallel) do - cmd = Context.build(context, command) - - run = fn host -> - {:ok, conn} = SSH.connect(host.name, host.options) - res = SSH.run(conn, cmd) - :ok = SSH.close(conn) - res - end - - Enum.map(context.hosts, fn h -> Task.async(fn -> run.(h) end) end) - |> Enum.map(fn n -> Task.await(n,timeout) 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) end) + end + @doc ~S""" Upload a file or files to the given context. From a5a9d21495b43e736df3591432b38e13f1d39289 Mon Sep 17 00:00:00 2001 From: Seungjin Kim Date: Thu, 30 Aug 2018 23:05:04 +0900 Subject: [PATCH 3/6] timeout option for seq and parallel runs. --- lib/sshkit.ex | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/sshkit.ex b/lib/sshkit.ex index 50055715..0bfcc30d 100644 --- a/lib/sshkit.ex +++ b/lib/sshkit.ex @@ -308,12 +308,12 @@ defmodule SSHKit do assert "Hello World!\n" == stdout ``` """ - def run(context, command, mode \\ :sequential) do + def run(context, command, mode \\ :sequential, timeout \\ :infinity) do cmd = Context.build(context, command) 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 @@ -328,7 +328,7 @@ defmodule SSHKit do 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) end) + |> Enum.map(fn task -> Task.await(task, :infinity) end) end @doc ~S""" From 95ed0f779462cb1d636ea15a08075cd25fd87806 Mon Sep 17 00:00:00 2001 From: Seungjin Kim Date: Fri, 31 Aug 2018 10:18:06 +0900 Subject: [PATCH 4/6] No trailing white-space at the end of a line. --- lib/sshkit.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sshkit.ex b/lib/sshkit.ex index 0bfcc30d..2aca011d 100644 --- a/lib/sshkit.ex +++ b/lib/sshkit.ex @@ -330,7 +330,7 @@ defmodule SSHKit do |> Enum.map(fn host -> Task.async(fn -> op.(host) end) end) |> Enum.map(fn task -> Task.await(task, :infinity) end) end - + @doc ~S""" Upload a file or files to the given context. From 0d239f68217d6807782cd02fd34c44791994ce62 Mon Sep 17 00:00:00 2001 From: Seungjin Kim Date: Mon, 3 Sep 2018 04:45:48 -0700 Subject: [PATCH 5/6] test for parallel SSHKit.run execution with :parallel option --- test/sshkit_functional_test.exs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/sshkit_functional_test.exs b/test/sshkit_functional_test.exs index 58723d4e..7032ecc2 100644 --- a/test/sshkit_functional_test.exs +++ b/test/sshkit_functional_test.exs @@ -17,6 +17,23 @@ 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},{:ok, output3, 0}] = + [host, 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 < 3 + assert String.trim(stdout(output1)) == host.options[:user] + assert String.trim(stdout(output2)) == host.options[:user] + assert String.trim(stdout(output3)) == host.options[:user] + + end + @tag boot: [@bootconf] test "runs commands and returns their output and exit status", %{hosts: [host]} do context = SSHKit.context(host) From 7c84642a0e7a3c95c873de6baa279b09f4f7abe3 Mon Sep 17 00:00:00 2001 From: Seungjin Kim Date: Mon, 3 Sep 2018 04:58:36 -0700 Subject: [PATCH 6/6] Parallel run test's threshold changed for some slower environment. Running 'sleep 2' cmd in parallel (to 2 connections) and let's call it a pass when it returns its run time less than 4 sec. Sequential runs more than 4 sec. --- test/sshkit_functional_test.exs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test/sshkit_functional_test.exs b/test/sshkit_functional_test.exs index 7032ecc2..9d51cc10 100644 --- a/test/sshkit_functional_test.exs +++ b/test/sshkit_functional_test.exs @@ -20,17 +20,16 @@ defmodule SSHKitFunctionalTest do @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},{:ok, output3, 0}] = - [host, host, host] + [{: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 < 3 + assert run_time < 4 assert String.trim(stdout(output1)) == host.options[:user] assert String.trim(stdout(output2)) == host.options[:user] - assert String.trim(stdout(output3)) == host.options[:user] end