|
3 | 3 | jepsen.control's use of clj-ssh with this instead."
|
4 | 4 | (:require [byte-streams :as bs]
|
5 | 5 | [clojure.tools.logging :refer [info warn]]
|
6 |
| - [jepsen [control :refer :all]] |
| 6 | + [jepsen [control :as c]] |
7 | 7 | [slingshot.slingshot :refer [try+ throw+]])
|
8 | 8 | (:import (com.jcraft.jsch.agentproxy AgentProxy
|
9 | 9 | ConnectorFactory)
|
|
15 | 15 | (net.schmizz.sshj.userauth.method AuthMethod)
|
16 | 16 | (net.schmizz.sshj.xfer FileSystemFile)
|
17 | 17 | (java.io IOException)
|
18 |
| - (java.util.concurrent TimeUnit))) |
| 18 | + (java.util.concurrent Semaphore |
| 19 | + TimeUnit))) |
19 | 20 |
|
20 | 21 | (defn auth-methods
|
21 | 22 | "Returns a list of AuthMethods we can use for logging in via an AgentProxy."
|
|
36 | 37 | to username/password."
|
37 | 38 | [^SSHClient c]
|
38 | 39 | (or ; Try given key
|
39 |
| - (when-let [k *private-key-path*] |
40 |
| - (.authPublickey c *username* (into-array [k])) |
| 40 | + (when-let [k c/*private-key-path*] |
| 41 | + (.authPublickey c c/*username* (into-array [k])) |
41 | 42 | true)
|
42 | 43 |
|
43 | 44 | ; Try agent
|
44 | 45 | (try
|
45 | 46 | (let [agent-proxy (agent-proxy)
|
46 | 47 | methods (auth-methods agent-proxy)]
|
47 |
| - (.auth c *username* methods) |
| 48 | + (.auth c c/*username* methods) |
48 | 49 | true)
|
49 | 50 | (catch UserAuthException e
|
50 | 51 | false))
|
51 | 52 |
|
52 | 53 | ; Fall back to standard id_rsa/id_dsa keys
|
53 |
| - (try (.authPublickey c ^String *username*) |
| 54 | + (try (.authPublickey c ^String c/*username*) |
54 | 55 | true
|
55 | 56 | (catch UserAuthException e
|
56 | 57 | false))
|
57 | 58 |
|
58 | 59 | ; OK, standard keys didn't work, try username+password
|
59 |
| - (.authPassword c *username* *password*))) |
| 60 | + (.authPassword c c/*username* c/*password*))) |
60 | 61 |
|
61 |
| -(defrecord SSHJRemote [^SSHClient client] |
| 62 | +(defrecord SSHJRemote [concurrency-limit |
| 63 | + ^SSHClient client |
| 64 | + ^Semaphore semaphore] |
62 | 65 | jepsen.control/Remote
|
63 | 66 | (connect [this host]
|
64 | 67 | (try+ (let [c (doto (SSHClient.)
|
65 | 68 | (.loadKnownHosts)
|
66 |
| - (.connect host *port*) |
| 69 | + (.connect host c/*port*) |
67 | 70 | auth!)]
|
68 |
| - (assoc this :client c)) |
| 71 | + (assoc this |
| 72 | + :client c |
| 73 | + :semaphore (Semaphore. concurrency-limit true))) |
69 | 74 | (catch Exception e
|
70 |
| - (throw+ (assoc (debug-data) |
| 75 | + (throw+ (assoc (c/debug-data) |
71 | 76 | :type :jepsen.control/session-error
|
72 | 77 | :message "Error opening SSH session. Verify username, password, and node hostnames are correct."
|
73 | 78 | :host host)))))
|
|
77 | 82 | (.close c)))
|
78 | 83 |
|
79 | 84 | (execute! [this action]
|
80 |
| - (with-open [session (.startSession client)] |
81 |
| - (let [cmd (.exec session (:cmd action))] |
82 |
| - ; Feed it input |
83 |
| - (when-let [input (:in action)] |
84 |
| - (let [stream (.getOutputStream cmd)] |
85 |
| - (bs/transfer input stream))) |
86 |
| - ; Wait on command |
87 |
| - (.join cmd) |
88 |
| - ; Return completion |
89 |
| - (assoc action |
90 |
| - :out (.toString (IOUtils/readFully (.getInputStream cmd))) |
91 |
| - :err (.toString (IOUtils/readFully (.getErrorStream cmd))) |
92 |
| - ; There's also a .getExitErrorMessage that might be interesting |
93 |
| - ; here? |
94 |
| - :exit (.getExitStatus cmd))))) |
| 85 | + ; (info :permits (.availablePermits semaphore)) |
| 86 | + (.acquire semaphore) |
| 87 | + (try |
| 88 | + (with-open [session (.startSession client)] |
| 89 | + (let [cmd (.exec session (:cmd action))] |
| 90 | + ; Feed it input |
| 91 | + (when-let [input (:in action)] |
| 92 | + (let [stream (.getOutputStream cmd)] |
| 93 | + (bs/transfer input stream))) |
| 94 | + ; Wait on command |
| 95 | + (.join cmd) |
| 96 | + ; Return completion |
| 97 | + (assoc action |
| 98 | + :out (.toString (IOUtils/readFully (.getInputStream cmd))) |
| 99 | + :err (.toString (IOUtils/readFully (.getErrorStream cmd))) |
| 100 | + ; There's also a .getExitErrorMessage that might be interesting |
| 101 | + ; here? |
| 102 | + :exit (.getExitStatus cmd)))) |
| 103 | + (finally |
| 104 | + (.release semaphore)))) |
95 | 105 |
|
96 | 106 | (upload! [this local-paths remote-path more]
|
97 | 107 | (with-open [sftp (.newSFTPClient client)]
|
|
101 | 111 | (with-open [sftp (.newSFTPClient client)]
|
102 | 112 | (.get sftp remote-paths (FileSystemFile. local-path)))))
|
103 | 113 |
|
| 114 | +(def concurrency-limit |
| 115 | + "OpenSSH has a standard limit of 10 concurrent channels per connection. |
| 116 | + However, commands run in quick succession with 10 concurrent *also* seem to |
| 117 | + blow out the channel limit--perhaps there's an asynchronous channel teardown |
| 118 | + process. We set the limit a bit lower here. This is experimentally determined |
| 119 | + by running jepsen.control-test's integration test... <sigh>" |
| 120 | + 6) |
| 121 | + |
104 | 122 | (defn remote
|
105 | 123 | "Constructs an SSHJ remote."
|
106 | 124 | []
|
107 |
| - (SSHJRemote. nil)) |
| 125 | + (SSHJRemote. concurrency-limit nil nil)) |
0 commit comments