In the spirit of other Citrus mock services, there is support for simulating an external SSH server as well as for connecting to SSH servers as a client during the test execution. Citrus translates SSH requests and responses to simple XML documents for better validation with the common Citrus mechanisms.
This means that the Citrus test case does not deal with pure SSH protocol commands. Instead of this we use the powerful XML validation capabilities in Citrus when dealing with the simple XML documents that represent the SSH request/response data.
Let us clarify this with a little example. Once the real SSH server daemon is fired up within Citrus we accept a SSH EXEC request for instance. The request is translated into a XML message of the following format:
<ssh-request xmlns="http://www.citrusframework.org/schema/ssh/message"> <command>cat - | sed -e 's/Hello/Hello SSH/'</command> <stdin>Hello World</stdin> </ssh-request>
This message can be validated with the usual Citrus mechanism in a receive test action. If you do not know how to do this, please read one of the sections about XML message validation in this reference guide first. Now after having received this request message the respective SSH response should be provided as appropriate answer. This is done with a message sending action on a reply handler as it is known from synchronous http message communication in Citrus for instance. The SSH XML representation of a response message looks like this:
<ssh-response xmlns="http://www.citrusframework.org/schema/ssh/message"> <stdout>Hello SSH World</stdout> <stderr></stderr> <exit>0</exit> </ssh-response>
Besides simulating a full featured SSH server, Citrus also provides SSH client functionality. This client uses the same request message pattern, which is translated into a real SSH call to an SSH server. The SSH response received is also translated into a XML message as shown above so we can validate it with known validation mechanisms in Citrus.
Similar to the other Citrus modules (http, soap), a Citrus SSH server and client is configured in Citrus Spring application context. There is a dedicated ssh namespace available for all ssh Citrus components. The namespace declaration goes into the context top-level element as usual:
<beans [...] xmlns:citrus-ssh="http://www.citrusframework.org/schema/ssh/config" [...] xsi:schemaLocation=" [...] http://www.citrusframework.org/schema/ssh/config http://www.citrusframework.org/schema/ssh/config/citrus-ssh-config.xsd [...] "> [...] </beans>
Both, SSH server and client along with their configuration options are described in the following two sections.
A Citrus SSH client is useful for testing against a real SSH server. So Citrus is able to invoke SSH commands on the external server and validate the SSH response accordingly. The test case does not deal with the pure SSH protocol within this communication. The Citrus SSH client component expects a customized XML representation and automatically translates these request messages into a real SSH call to a specific host. Once the synchronous SSH response was received the result gets translated back to the XML response message representation. On this translated response we can easily apply the validation steps by the usual Citrus means.
The SSH client components receive its configuration in the Spring application context as usual. We can use the special SSH module namespace for easy configuration:
<citrus-ssh:client id="sshClient" port="9072" user="roland" private-key-path="classpath:com/consol/citrus/ssh/test_user.priv" strict-host-checking="false" host="localhost"/>
The SSH client receives several attributes, these are:
- id: Id identifying the bean and used as reference from with test descriptions. (e.g. id="sshClient")
- host: Host to connect to for sending an SSH Exec request. Default is 'localhost' (e.g. host="localhost")
- port Port to use. Default is 2222 (e.g. port="9072")
- private-key-path: Path to a private key, which can be either a plain file path or an class resource if prefixed with 'classpath' (e.g. private-key-path="classpath:test_user.priv")
- private-key-password: Optional password for the private key (e.g. password="s!cr!t")
- user: User used for connecting to the SSH server (e.g. user="roland")
- password: Password used for password based authentication. Might be combined with "private-key-path" in which case both authentication mechanism are tried (e.g. password="ps!st)
- strict-host-checking: Whether the host key should be verified by looking it up in a 'known_hosts' file. Default is false (e.g. strict-host-checking="true")
- known-hosts-path: Path to a known hosts file. If prefixed with 'classpath:' this file is looked up as a resource in the classpath (e.g. known-hosts-path="/etc/ssh/known_hosts")
- command-timeout: Timeout in milliseconds for how long to wait for the SSH command to complete. Default is 5 minutes (e.g. command-timeout="300000")
- connection-timeout: Timeout in milliseconds for how long to for a connectiuon to connect. Default is 1 minute (e.g. connection-timeout="60000")
- actor: Actor used for switching groups of actions (e.g. actor="ssh-mock")
Once defines as client component in the Spring application context test cases can reference the client in every send test action.
<send endpoint="sshClient"> <message> <payload> <ssh-request xmlns="http://www.citrusframework.org/schema/ssh/message"> <command>shutdown</command> <stdin>input</stdin> </ssh-request> </payload> </message> </send> <receive endpoint="sshClient"> <message> <payload> <ssh-response xmlns="http://www.citrusframework.org/schema/ssh/message"> <stdout>Hello Citrus</stdout> <stderr/> <exit>0</exit> </ssh-response> </payload> </message> </receive>
As you can see we use usual send and receive test actions. The XML SSH representation helps us to specify the request and response data for validation. This way you can call SSH commands against an external SSH server and validate the response data.
Now that we have used Citrus on the client side we can also use Citrus SSH server module in order to provide a full stacked SSH server daemon. We can accept SSH client connections and provide proper response messages as an answer.
Given the above SSH module namespace declaration, adding a new SSH server is quite simple:
<citrus-ssh:server id="sshServer" allowed-key-path="classpath:com/consol/citrus/ssh/test_user_pub.pem" user="roland" port="9072" auto-start="true" endpoint-adapter="sshEndpointAdapter"/>
endpoint-adapter is the handler which receives the SSH request as messages (in the request format described above). Endpoint adapter implementations are fully described in http-serverAll adapters described there are supported in SSH server module, too.
<citrus-ssh:server> supports the following attributes:
SSH Server Attributes:
- id: Name of the SSH server which identifies it unique within the Citrus Spring context (e.g. id="sshServer")
- host-key-path: Path to PEM encoded key pair (public and private key) which is used as host key. By default, a standard, pre-generate, fixed keypair is used. The path can be specified either as an file path, or, if prefixed with classpath: is looked up from within the classpath. The path the is relative from to the top-level package, so no leading slash should be used (e.g. hist-key-path="/etc/citrus_ssh_server.pem)
- user: User which is allowed to connect (e.g. user="roland")
- allowed-key-path: Path to a SSH public key stored in PEM format. These are the keys, which are allowed to connect to the SSH server when publickey authentication is used. It seves the same purpose asauthorized_keysfor standard SSH installations. The path can be specified either as an file path, or, if prefixed with classpath: is looked up from within the classpath. The path the is relative from to the top-level package, so no leading slash should be used (e.g. allowed-key-path="classpath:test_user_pub.pem)
- password: Password which should be used when password authentication is used. Both publickey authentication and password based authentication can be used together in which case both methods are tried in turn (e.g. password="s!cr!t")
- host: Host address (e.g. localhost)
- port: Port on which to listen. The SSH server will bind on localhost to this port (e.g. port="9072")
- auto-start: Whether to start this SSH server automatically. Default is true . If set to false, a test action is responsible for starting/stopping the server (e.g. auto-start="true")
- endpoint-adapter: Bean reference to a endpoint adapter which processes the incoming SSH request. The message format for the request and response are described above (e.g. endpoint-adapter="sshEndpointAdapter")
Once the SSH server component is added to the Spring application context with a proper endpoint adapter like the MessageChannel forwarding adapter we can receive incoming requests in a test case and provide a respone message for the client.
<receive endpoint="sshServer"> <message> <payload> <ssh-request xmlns="http://www.citrusframework.org/schema/ssh/message"> <command>shutdown</command> <stdin>input</stdin> </ssh-request> </payload> </message> </receive> <send endpoint="sshServer"> <message> <payload> <ssh-response xmlns="http://www.citrusframework.org/schema/ssh/message"> <stdout>Hello Citrus</stdout> <exit>0</exit> </ssh-response> </payload> </message> </send>