/*******************************************************************************
* Copyright (c) 2017 Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is made available under the terms of the
* Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
******************************************************************************/
package org.jboss.tools.livereload.reddeer.requirement;
import static org.junit.Assert.assertTrue;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jboss.ide.eclipse.as.reddeer.server.wizard.page.NewServerAdapterPage;
import org.jboss.ide.eclipse.as.reddeer.server.wizard.page.NewServerRSIWizardPage;
import org.jboss.ide.eclipse.as.reddeer.server.wizard.page.NewServerWizardPageWithErrorCheck;
import org.jboss.ide.eclipse.as.reddeer.server.wizard.page.NewServerAdapterPage.Profile;
import org.jboss.reddeer.common.condition.AbstractWaitCondition;
import org.jboss.reddeer.common.platform.RunningPlatform;
import org.jboss.reddeer.common.wait.TimePeriod;
import org.jboss.reddeer.common.wait.WaitUntil;
import org.jboss.reddeer.common.wait.WaitWhile;
import org.jboss.reddeer.core.condition.JobIsRunning;
import org.jboss.reddeer.core.condition.ShellWithTextIsAvailable;
import org.jboss.reddeer.eclipse.rse.ui.view.System;
import org.jboss.reddeer.eclipse.rse.ui.view.SystemView;
import org.jboss.reddeer.eclipse.rse.ui.wizard.NewConnectionWizardDialog;
import org.jboss.reddeer.eclipse.rse.ui.wizard.NewConnectionWizardMainPage;
import org.jboss.reddeer.eclipse.rse.ui.wizard.NewConnectionWizardSelectionPage;
import org.jboss.reddeer.eclipse.rse.ui.wizard.SystemPasswordPromptDialog;
import org.jboss.reddeer.eclipse.rse.ui.wizard.NewConnectionWizardSelectionPage.SystemType;
import org.jboss.reddeer.eclipse.wst.server.ui.view.ServersView;
import org.jboss.reddeer.eclipse.wst.server.ui.wizard.NewServerWizardDialog;
import org.jboss.reddeer.junit.requirement.Requirement;
import org.jboss.reddeer.requirements.server.ConfiguredServerInfo;
import org.jboss.reddeer.requirements.server.ServerReqBase;
import org.jboss.reddeer.swt.api.Shell;
import org.jboss.reddeer.swt.api.TreeItem;
import org.jboss.reddeer.swt.condition.ShellIsAvailable;
import org.jboss.reddeer.swt.impl.button.CheckBox;
import org.jboss.reddeer.swt.impl.button.OkButton;
import org.jboss.reddeer.swt.impl.button.PushButton;
import org.jboss.reddeer.swt.impl.button.YesButton;
import org.jboss.reddeer.swt.impl.menu.ContextMenu;
import org.jboss.reddeer.swt.impl.shell.DefaultShell;
import org.jboss.reddeer.swt.impl.text.LabeledText;
import org.jboss.reddeer.swt.impl.tree.DefaultTree;
import org.jboss.reddeer.swt.impl.tree.DefaultTreeItem;
import org.jboss.reddeer.workbench.api.Editor;
import org.jboss.reddeer.workbench.impl.editor.DefaultEditor;
import org.jboss.tools.livereload.reddeer.requirement.DockerWildflyRequirement.DockerWildfly;
import com.spotify.docker.client.DefaultDockerClient;
import com.spotify.docker.client.DockerClient;
import com.spotify.docker.client.messages.ContainerConfig;
import com.spotify.docker.client.messages.ContainerCreation;
import com.spotify.docker.client.messages.HostConfig;
import com.spotify.docker.client.messages.PortBinding;
import com.spotify.docker.client.messages.ContainerConfig.Builder;
public class DockerWildflyRequirement extends ServerReqBase implements Requirement<DockerWildfly> {
private String ipAddress;
private DockerClient docker;
private String widlflyContId;
private static ConfiguredServerInfo lastServerConfiguration;
private static String macSSHPort = "2022";
DockerWildfly config;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface DockerWildfly {
String imageName();
String name();
String homeFolder();
String userName();
String pass();
boolean cleanup() default true;
}
@Override
public boolean canFulfill() {
return true;
}
@Override
public void fulfill() {
try {
lastServerConfiguration = new ConfiguredServerInfo(config.name(), null);
docker = DefaultDockerClient.fromEnv().build();
docker.pull(config.imageName());
Builder builder = ContainerConfig.builder();
if (RunningPlatform.isOSX()) {
// http://stackoverflow.com/questions/37965790/how-do-i-ssh-to-a-docker-in-mac-container
// map container port 22 [ssh] to host 2022
final Map<String, List<PortBinding>> portBindings = new HashMap<>();
List<PortBinding> hostPorts1 = new ArrayList<>();
hostPorts1.add(PortBinding.of("0.0.0.0", macSSHPort));
List<PortBinding> hostPorts2 = new ArrayList<>();
hostPorts2.add(PortBinding.of("0.0.0.0", "8081"));
portBindings.put("22/tcp", hostPorts1);
portBindings.put("8081/tcp", hostPorts2);
HostConfig hostConfig = HostConfig.builder().portBindings(portBindings).build();
builder.hostConfig(hostConfig);
}
ContainerConfig wfConfig = builder.image(config.imageName()).build();
final ContainerCreation creation = docker.createContainer(wfConfig);
widlflyContId = creation.id();
docker.startContainer(widlflyContId);
IPIsAssigned ipIsAssigned = new IPIsAssigned();
new WaitUntil(ipIsAssigned);
if(RunningPlatform.isOSX()){
ipAddress = "localhost";
} else {
ipAddress = ipIsAssigned.getIP();
}
} catch (Exception e) {
try {
removeDocker();
} catch (Exception ex) {
ex.printStackTrace();
}
throw new AssertionError("failed docker comm. Make sure docker is installed on your system.", e);
}
try{
setupRemoteAdapter();
setupPortOffset();
ServersView sw = new ServersView();
sw.open();
sw.getServer(config.name()).start();
} catch (Exception e) {
try {
removeDocker();
} catch (Exception ex) {
ex.printStackTrace();
} throw e;
}
}
private void setupPortOffset() {
ServersView sw = new ServersView();
sw.open();
for (TreeItem i : new DefaultTree().getItems()) {
if (i.getText().startsWith(config.name())) {
i.doubleClick();
break;
}
}
Editor serverEditor = new DefaultEditor(config.name());
new CheckBox("Detect from Local Runtime").toggle(false);
new LabeledText("Port Offset").setText("1");
serverEditor.close(true);
}
private void setupRemoteAdapter() {
NewServerWizardDialog serverW = new NewServerWizardDialog();
// setup remote system first
setupRemoteSystem();
// -- Open 'New Server' wizard
serverW.open();
// -- Select the server type and fill in server name, then continue
// on next page
NewServerWizardPageWithErrorCheck sp = new NewServerWizardPageWithErrorCheck();
sp.selectType("JBoss Community", "WildFly 10.x");
sp.setName(config.name());
sp.checkErrors();
serverW.next();
// -- Select server profile (Remote)
NewServerAdapterPage ap = new NewServerAdapterPage();
ap.setProfile(Profile.REMOTE);
// TODO Fix this in NewServerAdapterPage
// ap.setAssignRuntime(false);
CheckBox extManaged = new CheckBox("Server lifecycle is externally managed.");
extManaged.toggle(true);
CheckBox check = new CheckBox("Assign a runtime to this server");
check.toggle(false);
serverW.next();
NewServerRSIWizardPage rsp = new NewServerRSIWizardPage();
rsp.setRemoteServerHome(config.homeFolder());
rsp.selectHost(ipAddress); // host was configured in
// setupRemoteSystem
serverW.finish();
}
protected void setupRemoteSystem() {
SystemView sview = new SystemView();
sview.open();
NewConnectionWizardDialog connW = sview.newConnection();
NewConnectionWizardSelectionPage sp = new NewConnectionWizardSelectionPage();
sp.selectSystemType(SystemType.SSH_ONLY);
connW.next();
NewConnectionWizardMainPage mp = new NewConnectionWizardMainPage();
mp.setHostName(ipAddress);
connW.finish();
// TODO FIX in as.reddeer
System system = sview.getSystem(ipAddress);
// system.connect("root", "root");
if (RunningPlatform.isOSX()) {
new DefaultTreeItem(ipAddress, "Ssh Shells").select();
new ContextMenu("Properties").select();
Shell propShell = new DefaultShell("Properties for Ssh Shells");
new DefaultTreeItem("Subsystem").select();
new LabeledText("Port (1-65535):").setText(macSSHPort);
new OkButton().click();
new WaitWhile(new ShellIsAvailable(propShell));
}
sview.open();
new DefaultTreeItem(ipAddress).select();
new ContextMenu("Connect").select();
new SystemPasswordPromptDialog();
new LabeledText("User ID:").setText(config.userName());
new LabeledText("Password (optional):").setText(config.pass());
new PushButton("OK").click();
new WaitUntil(new ShellWithTextIsAvailable("Warning"), TimePeriod.LONG, false);
try {
Shell shell = new DefaultShell("Warning");
new YesButton().click();
new WaitWhile(new ShellIsAvailable(shell), TimePeriod.LONG);
try {
// known_hosts does not exist - create ? shell
Shell hostsShell = new DefaultShell("Warning");
new YesButton().click();
new WaitWhile(new ShellIsAvailable(hostsShell), TimePeriod.LONG);
} catch (Exception e) {
// TODO: handle exception
}
} catch (Exception e) {
// ssh keys shell was not opened
}
new WaitWhile(new JobIsRunning(), TimePeriod.LONG);
assertTrue(system.isConnected());
}
@Override
public void setDeclaration(DockerWildfly declaration) {
this.config = declaration;
}
@Override
public void cleanUp() {
if (config.cleanup()) {
try{
if (lastServerConfiguration != null) {
ServersView sw = new ServersView();
sw.open();
sw.getServer(config.name()).delete();
lastServerConfiguration = null;
SystemView sview = new SystemView();
sview.open();
sview.getSystem(ipAddress).delete();
}
} finally {
try {
removeDocker();
} catch (Exception e) {
throw new AssertionError("failed to remove docker container", e);
}
}
}
}
private void removeDocker() throws Exception{
if (docker != null) {
docker.killContainer(widlflyContId);
// Remove container
docker.removeContainer(widlflyContId);
//remove image
docker.removeImage(config.imageName());
// Close the docker client
docker.close();
}
}
private class IPIsAssigned extends AbstractWaitCondition {
private String ip;
@Override
public boolean test() {
try {
ip = docker.inspectContainer(widlflyContId).networkSettings().ipAddress();
return ip != null && !ip.isEmpty();
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public String getIP() {
return ip;
}
}
}