3 * Licensed to the Apache Software Foundation (ASF) under one or more
4 * contributor license agreements. See the NOTICE file distributed with
5 * this work for additional information regarding copyright ownership.
6 * The ASF licenses this file to You under the Apache License, Version 2.0
7 * (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 package org.apache.karaf.tooling.client;
20 import org.apache.maven.plugin.AbstractMojo;
21 import org.apache.maven.plugin.MojoExecutionException;
22 import org.apache.maven.plugins.annotations.LifecyclePhase;
23 import org.apache.maven.plugins.annotations.Mojo;
24 import org.apache.maven.plugins.annotations.Parameter;
25 import org.apache.maven.plugins.annotations.ResolutionScope;
26 import org.apache.sshd.ClientChannel;
27 import org.apache.sshd.ClientSession;
28 import org.apache.sshd.SshClient;
29 import org.apache.sshd.agent.SshAgent;
30 import org.apache.sshd.agent.local.AgentImpl;
31 import org.apache.sshd.agent.local.LocalAgentFactory;
32 import org.apache.sshd.client.UserInteraction;
33 import org.apache.sshd.client.future.ConnectFuture;
34 import org.apache.sshd.common.RuntimeSshException;
35 import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
37 import org.fusesource.jansi.Ansi;
38 import org.fusesource.jansi.Ansi.Color;
39 import org.fusesource.jansi.AnsiConsole;
41 import java.io.BufferedReader;
42 import java.io.ByteArrayInputStream;
43 import java.io.ByteArrayOutputStream;
44 import java.io.Console;
46 import java.io.FileReader;
47 import java.io.IOError;
48 import java.io.IOException;
49 import java.io.InputStream;
50 import java.io.ObjectInputStream;
51 import java.io.PrintWriter;
52 import java.io.StringWriter;
54 import java.security.KeyPair;
55 import java.util.List;
56 import java.util.concurrent.TimeUnit;
59 * Client MOJO to deployWithSsh command on a running Karaf instance
61 @Mojo(name = "client", defaultPhase = LifecyclePhase.PACKAGE, requiresDependencyResolution = ResolutionScope.RUNTIME, threadSafe = true)
62 public class ClientMojo extends AbstractMojo {
64 @Parameter(defaultValue = "8101")
67 @Parameter(defaultValue = "localhost")
70 @Parameter(defaultValue = "karaf")
73 @Parameter(defaultValue = "karaf")
74 private String password;
76 @Parameter(defaultValue = "0")
79 @Parameter(defaultValue = "2")
83 private List<String> commands;
86 private List<File> scripts;
91 private static final String NEW_LINE = System.getProperty("line.separator");
93 public void execute() throws MojoExecutionException {
94 // Add commands from scripts to already declared commands
95 if (scripts != null) {
96 for (File script : scripts) {
97 try (BufferedReader br = new BufferedReader(new FileReader(script))) {
99 while ((line = br.readLine()) != null) {
101 if (line.isEmpty()) {
106 } catch (Exception e) {
107 throw new MojoExecutionException(e, e.getMessage(), e.toString());
112 if (commands == null || commands.isEmpty()) {
113 getLog().warn("No OSGi command was specified");
117 StringWriter sw = new StringWriter();
118 PrintWriter pw = new PrintWriter(sw, true);
119 for (String cmd : commands) {
123 execute(sw.toString());
126 protected void execute(String cmd) throws MojoExecutionException {
127 SshClient client = null;
129 final Console console = System.console();
130 client = SshClient.setUpDefaultClient();
131 setupAgent(user, keyFile, client);
133 client.setUserInteraction( new UserInteraction() {
134 public void welcome(String banner) {
135 console.printf(banner);
138 public String[] interactive(String destination, String name, String instruction, String[] prompt, boolean[] echo)
140 String[] answers = new String[prompt.length];
142 for (int i = 0; i < prompt.length; i++) {
143 if (console != null) {
145 answers[i] = console.readLine(prompt[i] + " ");
148 answers[i] = new String( console.readPassword(prompt[i] + " "));
159 if (console != null) {
160 console.printf("Logging in as %s\n", user);
162 ClientSession session = connect(client);
163 if (password != null) {
164 session.addPasswordIdentity(password);
166 session.auth().verify();
168 final ClientChannel channel = session.createChannel("exec", cmd.concat(NEW_LINE));
169 channel.setIn(new ByteArrayInputStream(new byte[0]));
170 final ByteArrayOutputStream sout = new ByteArrayOutputStream();
171 final ByteArrayOutputStream serr = new ByteArrayOutputStream();
172 channel.setOut( AnsiConsole.wrapOutputStream(sout));
173 channel.setErr( AnsiConsole.wrapOutputStream(serr));
175 channel.waitFor(ClientChannel.CLOSED, 0);
177 sout.writeTo(System.out);
178 serr.writeTo(System.err);
180 // Expects issue KARAF-2623 is fixed
181 final boolean isError = (channel.getExitStatus() != null && channel.getExitStatus().intValue() != 0);
183 final String errorMarker = Ansi.ansi().fg(Color.RED).toString();
184 final int fromIndex = sout.toString().indexOf(errorMarker) + errorMarker.length();
185 final int toIndex = sout.toString().lastIndexOf(Ansi.ansi().fg(Color.DEFAULT ).toString());
186 throw new MojoExecutionException(NEW_LINE + sout.toString().substring(fromIndex, toIndex));
189 catch (MojoExecutionException e) {
192 catch (Throwable t) {
193 throw new MojoExecutionException(t, t.getMessage(), t.toString());
199 catch (Throwable t) {
200 throw new MojoExecutionException(t, t.getMessage(), t.toString());
205 private void setupAgent(String user, File keyFile, SshClient client) {
206 URL builtInPrivateKey = ClientMojo.class.getClassLoader().getResource("karaf.key");
207 SshAgent agent = startAgent(user, builtInPrivateKey, keyFile);
208 client.setAgentFactory( new LocalAgentFactory(agent));
209 client.getProperties().put(SshAgent.SSH_AUTHSOCKET_ENV_NAME, "local");
212 private SshAgent startAgent(String user, URL privateKeyUrl, File keyFile) {
213 try (InputStream is = privateKeyUrl.openStream())
215 SshAgent agent = new AgentImpl();
216 ObjectInputStream r = new ObjectInputStream(is);
217 KeyPair keyPair = (KeyPair) r.readObject();
219 agent.addIdentity(keyPair, user);
220 if (keyFile != null) {
221 String[] keyFiles = new String[] { keyFile.getAbsolutePath() };
222 FileKeyPairProvider fileKeyPairProvider = new FileKeyPairProvider(keyFiles);
223 for (KeyPair key : fileKeyPairProvider.loadKeys()) {
224 agent.addIdentity(key, user);
229 catch (Throwable e) {
230 getLog().error("Error starting ssh agent for: " + e.getMessage(), e);
235 private ClientSession connect(SshClient client) throws IOException, InterruptedException {
237 ClientSession session = null;
239 final ConnectFuture future = client.connect(user, host, port);
242 session = future.getSession();
244 catch (RuntimeSshException ex) {
245 if (retries++ < attempts) {
246 Thread.sleep(TimeUnit.SECONDS.toMillis(delay));
247 getLog().info("retrying (attempt " + retries + ") ...");
253 } while (session == null);