Add .tox/ to .gitignore
[odlparent.git] / karaf / karaf-maven-plugin / src / main / java / org / apache / karaf / tooling / client / ClientMojo.java
1 /**
2  *
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
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
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.
17  */
18 package org.apache.karaf.tooling.client;
19
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;
36
37 import org.fusesource.jansi.Ansi;
38 import org.fusesource.jansi.Ansi.Color;
39 import org.fusesource.jansi.AnsiConsole;
40
41 import java.io.BufferedReader;
42 import java.io.ByteArrayInputStream;
43 import java.io.ByteArrayOutputStream;
44 import java.io.Console;
45 import java.io.File;
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;
53 import java.net.URL;
54 import java.security.KeyPair;
55 import java.util.List;
56 import java.util.concurrent.TimeUnit;
57
58 /**
59  * Client MOJO to deployWithSsh command on a running Karaf instance
60  */
61 @Mojo(name = "client", defaultPhase = LifecyclePhase.PACKAGE, requiresDependencyResolution = ResolutionScope.RUNTIME, threadSafe = true)
62 public class ClientMojo extends AbstractMojo {
63
64     @Parameter(defaultValue = "8101")
65     private int port;
66
67     @Parameter(defaultValue = "localhost")
68     private String host;
69
70     @Parameter(defaultValue = "karaf")
71     private String user;
72
73     @Parameter(defaultValue = "karaf")
74     private String password;
75
76     @Parameter(defaultValue = "0")
77     private int attempts;
78
79     @Parameter(defaultValue = "2")
80     private int delay;
81
82     @Parameter
83     private List<String> commands;
84
85     @Parameter
86     private List<File> scripts;
87
88     @Parameter
89     private File keyFile;
90
91     private static final String NEW_LINE = System.getProperty("line.separator");
92
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))) {
98                     String line;
99                     while ((line = br.readLine()) != null) {
100                         line = line.trim();
101                         if (line.isEmpty()) {
102                             continue;
103                         }
104                         commands.add(line);
105                     }
106                 } catch (Exception e) {
107                     throw new MojoExecutionException(e, e.getMessage(), e.toString());
108                 }
109             }
110         }
111
112         if (commands == null || commands.isEmpty()) {
113             getLog().warn("No OSGi command was specified");
114             return;
115         }
116
117         StringWriter sw = new StringWriter();
118         PrintWriter pw = new PrintWriter(sw, true);
119         for (String cmd : commands) {
120             getLog().info(cmd);
121             pw.println(cmd);
122         }
123         execute(sw.toString());
124     }
125
126     protected void execute(String cmd) throws MojoExecutionException {
127         SshClient client = null;
128         try {
129             final Console console = System.console();
130             client = SshClient.setUpDefaultClient();
131             setupAgent(user, keyFile, client);
132
133             client.setUserInteraction( new UserInteraction() {
134                 public void welcome(String banner) {
135                     console.printf(banner);
136                 }
137
138                 public String[] interactive(String destination, String name, String instruction, String[] prompt, boolean[] echo)
139                 {
140                     String[] answers = new String[prompt.length];
141                     try {
142                         for (int i = 0; i < prompt.length; i++) {
143                             if (console != null) {
144                                 if (echo[i]) {
145                                     answers[i] = console.readLine(prompt[i] + " ");
146                                 }
147                                 else {
148                                     answers[i] = new String( console.readPassword(prompt[i] + " "));
149                                 }
150                             }
151                         }
152                     }
153                     catch (IOError e) {
154                     }
155                     return answers;
156                 }
157             });
158             client.start();
159             if (console != null) {
160                 console.printf("Logging in as %s\n", user);
161             }
162             ClientSession session = connect(client);
163             if (password != null) {
164                 session.addPasswordIdentity(password);
165             }
166             session.auth().verify();
167
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));
174             channel.open();
175             channel.waitFor(ClientChannel.CLOSED, 0);
176
177             sout.writeTo(System.out);
178             serr.writeTo(System.err);
179
180             // Expects issue KARAF-2623 is fixed
181             final boolean isError = (channel.getExitStatus() != null && channel.getExitStatus().intValue() != 0);
182             if (isError) {
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));
187             }
188         }
189         catch (MojoExecutionException e) {
190             throw e;
191         }
192         catch (Throwable t) {
193             throw new MojoExecutionException(t, t.getMessage(), t.toString());
194         }
195         finally {
196             try {
197                 client.stop();
198             }
199             catch (Throwable t) {
200                 throw new MojoExecutionException(t, t.getMessage(), t.toString());
201             }
202         }
203     }
204
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");
210     }
211
212     private SshAgent startAgent(String user, URL privateKeyUrl, File keyFile) {
213         try (InputStream is = privateKeyUrl.openStream())
214         {
215             SshAgent agent = new AgentImpl();
216             ObjectInputStream r = new ObjectInputStream(is);
217             KeyPair keyPair = (KeyPair) r.readObject();
218             is.close();
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);
225                 }
226             }
227             return agent;
228         }
229         catch (Throwable e) {
230             getLog().error("Error starting ssh agent for: " + e.getMessage(), e);
231             return null;
232         }
233     }
234
235     private ClientSession connect(SshClient client) throws IOException, InterruptedException {
236         int retries = 0;
237         ClientSession session = null;
238         do {
239             final ConnectFuture future = client.connect(user, host, port);
240             future.await();
241             try {
242                 session = future.getSession();
243             }
244             catch (RuntimeSshException ex) {
245                 if (retries++ < attempts) {
246                     Thread.sleep(TimeUnit.SECONDS.toMillis(delay));
247                     getLog().info("retrying (attempt " + retries + ") ...");
248                 }
249                 else {
250                     throw ex;
251                 }
252             }
253         } while (session == null);
254         return session;
255     }
256
257 }
258