Add .tox/ to .gitignore
[odlparent.git] / karaf / karaf-maven-plugin / src / main / java / org / apache / karaf / tooling / client / DeployMojo.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.karaf.tooling.utils.MojoSupport;
21 import org.apache.maven.artifact.Artifact;
22 import org.apache.maven.plugin.AbstractMojo;
23 import org.apache.maven.plugin.MojoExecutionException;
24 import org.apache.maven.plugins.annotations.LifecyclePhase;
25 import org.apache.maven.plugins.annotations.Mojo;
26 import org.apache.maven.plugins.annotations.Parameter;
27 import org.apache.maven.plugins.annotations.ResolutionScope;
28 import org.apache.sshd.ClientChannel;
29 import org.apache.sshd.ClientSession;
30 import org.apache.sshd.SshClient;
31 import org.apache.sshd.agent.SshAgent;
32 import org.apache.sshd.agent.local.AgentImpl;
33 import org.apache.sshd.agent.local.LocalAgentFactory;
34 import org.apache.sshd.client.UserInteraction;
35 import org.apache.sshd.client.future.ConnectFuture;
36 import org.apache.sshd.common.RuntimeSshException;
37 import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
38
39 import org.fusesource.jansi.Ansi;
40 import org.fusesource.jansi.Ansi.Color;
41 import org.fusesource.jansi.AnsiConsole;
42
43 import javax.management.MBeanServerConnection;
44 import javax.management.ObjectName;
45 import javax.management.remote.JMXConnector;
46 import javax.management.remote.JMXConnectorFactory;
47 import javax.management.remote.JMXServiceURL;
48 import javax.management.remote.MBeanServerForwarder;
49 import java.io.BufferedReader;
50 import java.io.ByteArrayInputStream;
51 import java.io.ByteArrayOutputStream;
52 import java.io.Console;
53 import java.io.File;
54 import java.io.FileReader;
55 import java.io.IOError;
56 import java.io.IOException;
57 import java.io.InputStream;
58 import java.io.ObjectInputStream;
59 import java.io.PrintWriter;
60 import java.io.StringWriter;
61 import java.net.URL;
62 import java.security.KeyPair;
63 import java.util.ArrayList;
64 import java.util.HashMap;
65 import java.util.List;
66 import java.util.concurrent.TimeUnit;
67
68 /**
69  * Deploy MOJO to deploy an artifact remotely on a running Karaf instance, using ssh or JMX
70  */
71 @Mojo(name = "deploy", defaultPhase = LifecyclePhase.PACKAGE, requiresDependencyResolution = ResolutionScope.RUNTIME, threadSafe = true)
72 public class DeployMojo extends MojoSupport {
73
74     @Parameter(defaultValue = "8101")
75     private int port;
76
77     @Parameter(defaultValue = "localhost")
78     private String host;
79
80     @Parameter(defaultValue = "karaf")
81     private String user;
82
83     @Parameter(defaultValue = "karaf")
84     private String password;
85
86     @Parameter(defaultValue = "karaf-root")
87     private String instance = "karaf-root";
88
89     @Parameter(defaultValue = "0")
90     private int attempts;
91
92     @Parameter(defaultValue = "2")
93     private int delay;
94
95     @Parameter(defaultValue = "true")
96     private boolean useSsh = true;
97
98     @Parameter(defaultValue = "true")
99     private boolean useProjectArtifact = true;
100
101     @Parameter
102     List<String> artifactLocations;
103
104     @Parameter
105     private File keyFile;
106
107     private static final String NEW_LINE = System.getProperty("line.separator");
108
109     public void execute() throws MojoExecutionException {
110         List<String> artifacts = new ArrayList<>();
111         if (useProjectArtifact) {
112             Artifact projectArtifact = project.getArtifact();
113             artifacts.add("mvn:" + projectArtifact.getGroupId() + "/" + projectArtifact.getArtifactId() + "/" + projectArtifact.getVersion());
114         }
115         artifacts.addAll(artifactLocations);
116         if (useSsh)
117             deployWithSsh(artifactLocations);
118         else deployWithJmx(artifactLocations);
119     }
120
121     protected void deployWithJmx(List<String> locations) throws MojoExecutionException {
122         try {
123             JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + host + ":" + port + "/" + instance);
124             ArrayList<String> list = new ArrayList<>();
125             if (user != null)
126                 list.add(user);
127             if (password != null)
128                 list.add(password);
129             HashMap env = new HashMap();
130             String[] credentials = list.toArray(new String[list.size()]);
131             env.put(JMXConnector.CREDENTIALS, credentials);
132             JMXConnector jmxConnector = null;
133             if (credentials.length > 0)
134                 jmxConnector = JMXConnectorFactory.connect(jmxServiceURL, env);
135             else jmxConnector = JMXConnectorFactory.connect(jmxServiceURL);
136             MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection();
137             for (String location : locations) {
138                 mBeanServerConnection.invoke(new ObjectName("org.apache.karaf:type=bundle,name=*"), "install", new Object[]{ location, true }, new String[]{ "java.lang.String", "boolean" });
139             }
140         } catch (Exception e) {
141             throw new MojoExecutionException("Can't deploy using JMX", e);
142         }
143     }
144
145     protected void deployWithSsh(List<String> locations) throws MojoExecutionException {
146         SshClient client = null;
147         try {
148             final Console console = System.console();
149             client = SshClient.setUpDefaultClient();
150             setupAgent(user, keyFile, client);
151
152             client.setUserInteraction( new UserInteraction() {
153                 public void welcome(String banner) {
154                     console.printf(banner);
155                 }
156
157                 public String[] interactive(String destination, String name, String instruction, String[] prompt, boolean[] echo)
158                 {
159                     String[] answers = new String[prompt.length];
160                     try {
161                         for (int i = 0; i < prompt.length; i++) {
162                             if (console != null) {
163                                 if (echo[i]) {
164                                     answers[i] = console.readLine(prompt[i] + " ");
165                                 }
166                                 else {
167                                     answers[i] = new String( console.readPassword(prompt[i] + " "));
168                                 }
169                             }
170                         }
171                     }
172                     catch (IOError e) {
173                     }
174                     return answers;
175                 }
176             });
177             client.start();
178             if (console != null) {
179                 console.printf("Logging in as %s\n", user);
180             }
181             ClientSession session = connect(client);
182             if (password != null) {
183                 session.addPasswordIdentity(password);
184             }
185             session.auth().verify();
186
187             StringWriter writer = new StringWriter();
188             PrintWriter print = new PrintWriter(writer, true);
189             for (String location : locations) {
190                 print.println("bundle:install -s " + location);
191             }
192
193             final ClientChannel channel = session.createChannel("exec", print.toString().concat(NEW_LINE));
194             channel.setIn(new ByteArrayInputStream(new byte[0]));
195             final ByteArrayOutputStream sout = new ByteArrayOutputStream();
196             final ByteArrayOutputStream serr = new ByteArrayOutputStream();
197             channel.setOut( AnsiConsole.wrapOutputStream(sout));
198             channel.setErr( AnsiConsole.wrapOutputStream(serr));
199             channel.open();
200             channel.waitFor(ClientChannel.CLOSED, 0);
201
202             sout.writeTo(System.out);
203             serr.writeTo(System.err);
204
205             // Expects issue KARAF-2623 is fixed
206             final boolean isError = (channel.getExitStatus() != null && channel.getExitStatus().intValue() != 0);
207             if (isError) {
208                 final String errorMarker = Ansi.ansi().fg(Color.RED).toString();
209                 final int fromIndex = sout.toString().indexOf(errorMarker) + errorMarker.length();
210                 final int toIndex = sout.toString().lastIndexOf(Ansi.ansi().fg(Color.DEFAULT ).toString());
211                 throw new MojoExecutionException(NEW_LINE + sout.toString().substring(fromIndex, toIndex));
212             }
213         }
214         catch (MojoExecutionException e) {
215             throw e;
216         }
217         catch (Throwable t) {
218             throw new MojoExecutionException(t, t.getMessage(), t.toString());
219         }
220         finally {
221             try {
222                 client.stop();
223             }
224             catch (Throwable t) {
225                 throw new MojoExecutionException(t, t.getMessage(), t.toString());
226             }
227         }
228     }
229
230     private void setupAgent(String user, File keyFile, SshClient client) {
231         URL builtInPrivateKey = ClientMojo.class.getClassLoader().getResource("karaf.key");
232         SshAgent agent = startAgent(user, builtInPrivateKey, keyFile);
233         client.setAgentFactory( new LocalAgentFactory(agent));
234         client.getProperties().put(SshAgent.SSH_AUTHSOCKET_ENV_NAME, "local");
235     }
236
237     private SshAgent startAgent(String user, URL privateKeyUrl, File keyFile) {
238         try (InputStream is = privateKeyUrl.openStream())
239         {
240             SshAgent agent = new AgentImpl();
241             ObjectInputStream r = new ObjectInputStream(is);
242             KeyPair keyPair = (KeyPair) r.readObject();
243             is.close();
244             agent.addIdentity(keyPair, user);
245             if (keyFile != null) {
246                 String[] keyFiles = new String[] { keyFile.getAbsolutePath() };
247                 FileKeyPairProvider fileKeyPairProvider = new FileKeyPairProvider(keyFiles);
248                 for (KeyPair key : fileKeyPairProvider.loadKeys()) {
249                     agent.addIdentity(key, user);
250                 }
251             }
252             return agent;
253         }
254         catch (Throwable e) {
255             getLog().error("Error starting ssh agent for: " + e.getMessage(), e);
256             return null;
257         }
258     }
259
260     private ClientSession connect(SshClient client) throws IOException, InterruptedException {
261         int retries = 0;
262         ClientSession session = null;
263         do {
264             final ConnectFuture future = client.connect(user, host, port);
265             future.await();
266             try {
267                 session = future.getSession();
268             }
269             catch (RuntimeSshException ex) {
270                 if (retries++ < attempts) {
271                     Thread.sleep(TimeUnit.SECONDS.toMillis(delay));
272                     getLog().info("retrying (attempt " + retries + ") ...");
273                 }
274                 else {
275                     throw ex;
276                 }
277             }
278         } while (session == null);
279         return session;
280     }
281
282 }
283