Issue 227: blockOnPort option when starting up a node

This commit is contained in:
Adrian Cole 2010-04-17 00:00:04 -07:00
parent f3a9fabf44
commit 670c143cff
6 changed files with 800 additions and 587 deletions

View File

@ -28,11 +28,10 @@ import org.jclouds.compute.domain.Template;
import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.domain.TemplateBuilder;
import org.jclouds.compute.internal.BaseComputeService; import org.jclouds.compute.internal.BaseComputeService;
import org.jclouds.compute.options.RunScriptOptions; import org.jclouds.compute.options.RunScriptOptions;
import org.jclouds.domain.Credentials;
import org.jclouds.domain.Location; import org.jclouds.domain.Location;
import org.jclouds.ssh.ExecResponse;
import com.google.inject.ImplementedBy; import com.google.inject.ImplementedBy;
import org.jclouds.ssh.ExecResponse;
/** /**
* Provides portable access to launching compute instances. * Provides portable access to launching compute instances.

View File

@ -61,9 +61,9 @@ import org.jclouds.compute.strategy.ListNodesStrategy;
import org.jclouds.compute.strategy.RebootNodeStrategy; import org.jclouds.compute.strategy.RebootNodeStrategy;
import org.jclouds.compute.strategy.RunNodesAndAddToSetStrategy; import org.jclouds.compute.strategy.RunNodesAndAddToSetStrategy;
import org.jclouds.compute.util.ComputeUtils; import org.jclouds.compute.util.ComputeUtils;
import org.jclouds.domain.Credentials;
import org.jclouds.domain.Location; import org.jclouds.domain.Location;
import org.jclouds.logging.Logger; import org.jclouds.logging.Logger;
import org.jclouds.ssh.ExecResponse;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
@ -72,7 +72,6 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import org.jclouds.ssh.ExecResponse;
/** /**
* *
@ -323,54 +322,58 @@ public class BaseComputeService implements ComputeService {
} }
/** /**
* @see #runScriptOnNodesWithTag(String, byte[], * @see #runScriptOnNodesWithTag(String, byte[], org.jclouds.compute.options.RunScriptOptions)
* org.jclouds.compute.options.RunScriptOptions)
*/ */
public Map<String, ExecResponse> runScriptOnNodesWithTag(String tag, public Map<String, ExecResponse> runScriptOnNodesWithTag(String tag, byte[] runScript) {
byte[] runScript) {
return runScriptOnNodesWithTag(tag, runScript, RunScriptOptions.NONE); return runScriptOnNodesWithTag(tag, runScript, RunScriptOptions.NONE);
} }
/** /**
* Run the script on all nodes with the specific tag. * Run the script on all nodes with the specific tag.
* *
* @param tag tag to look up the nodes * @param tag
* @param runScript script to run in byte format. If the script is a string, use * tag to look up the nodes
* @param runScript
* script to run in byte format. If the script is a string, use
* {@link String#getBytes()} to retrieve the bytes * {@link String#getBytes()} to retrieve the bytes
* @param options nullable options to how to run the script, whether to override credentials * @param options
* nullable options to how to run the script, whether to override credentials
* @return map with node identifiers and corresponding responses * @return map with node identifiers and corresponding responses
*/ */
public Map<String, ExecResponse> runScriptOnNodesWithTag(String tag, byte[] runScript, @Nullable RunScriptOptions options) { @SuppressWarnings("unchecked")
public Map<String, ExecResponse> runScriptOnNodesWithTag(String tag, byte[] runScript,
@Nullable RunScriptOptions options) {
checkNotEmpty(tag, "Tag must be provided"); checkNotEmpty(tag, "Tag must be provided");
checkNotNull(runScript, checkNotNull(runScript,
"The script (represented by bytes array - use \"script\".getBytes() must be provided"); "The script (represented by bytes array - use \"script\".getBytes() must be provided");
if(options == null) options = RunScriptOptions.NONE; if (options == null)
options = RunScriptOptions.NONE;
Map<String, ? extends NodeMetadata> nodes = getNodesWithTag(tag); Map<String, ? extends NodeMetadata> nodes = getNodesWithTag(tag);
Map<String, ExecResponse> responses = Maps.newHashMap(); Map<String, ExecResponse> responses = Maps.newHashMap();
for(NodeMetadata node : nodes.values()) { for (NodeMetadata node : nodes.values()) {
if(NodeState.RUNNING != node.getState()) continue; //make sure the node is active if (NodeState.RUNNING != node.getState())
continue; // make sure the node is active
if(options.getOverrideCredentials() != null) { if (options.getOverrideCredentials() != null) {
//override the credentials with provided to this method // override the credentials with provided to this method
node = ComputeUtils.installNewCredentials(node, options.getOverrideCredentials()); node = ComputeUtils.installNewCredentials(node, options.getOverrideCredentials());
} else { } else {
//don't override // don't override
checkNotNull(node.getCredentials(), "If the default credentials need to be used, they can't be null"); checkNotNull(node.getCredentials(),
"If the default credentials need to be used, they can't be null");
} }
ComputeUtils.SshCallable<?> callable; ComputeUtils.SshCallable<?> callable;
if(options.isRunAsRoot()) if (options.isRunAsRoot())
callable = utils.runScriptOnNode(node, "computeserv.sh", runScript); callable = utils.runScriptOnNode(node, "computeserv.sh", runScript);
else else
callable = utils.runScriptOnNodeAsDefaultUser(node, "computeserv.sh", runScript); callable = utils.runScriptOnNodeAsDefaultUser(node, "computeserv.sh", runScript);
Map<ComputeUtils.SshCallable<?>, ?> scriptRunResults = utils.runCallablesOnNode(node, Map<ComputeUtils.SshCallable<?>, ?> scriptRunResults = utils.runCallablesOnNode(node, Sets
Sets.newHashSet(callable), .newHashSet(callable), null);
null); responses.put(node.getId(), (ExecResponse) scriptRunResults.get(callable));
responses.put(node.getId(),
(ExecResponse) scriptRunResults.get(callable));
} }
return responses; return responses;
} }

View File

@ -30,8 +30,8 @@ public class RunScriptOptions {
/** /**
* Default options. The default settings are: * Default options. The default settings are:
* <ul> * <ul>
* <li>override the credentials with ones supplied in * <li>override the credentials with ones supplied in call to
* call to {@link org.jclouds.compute.ComputeService#runScriptOnNodesWithTag}</li> * {@link org.jclouds.compute.ComputeService#runScriptOnNodesWithTag}</li>
* <li>run the script as root (versus running with current privileges)</li> * <li>run the script as root (versus running with current privileges)</li>
* </ul> * </ul>
*/ */
@ -40,18 +40,20 @@ public class RunScriptOptions {
private Credentials overridingCredentials; private Credentials overridingCredentials;
private boolean runAsRoot = true; private boolean runAsRoot = true;
private void withOverridingCredentials(Credentials overridingCredentials) { public RunScriptOptions withOverridingCredentials(Credentials overridingCredentials) {
this.overridingCredentials = overridingCredentials; this.overridingCredentials = overridingCredentials;
return this;
} }
private void runAsRoot(boolean runAsRoot) { public RunScriptOptions runAsRoot(boolean runAsRoot) {
this.runAsRoot = runAsRoot; this.runAsRoot = runAsRoot;
return this;
} }
/** /**
* Whether to override the credentials with ones supplied in * Whether to override the credentials with ones supplied in call to
* call to {@link org.jclouds.compute.ComputeService#runScriptOnNodesWithTag}. * {@link org.jclouds.compute.ComputeService#runScriptOnNodesWithTag}. By default, true.
* By default, true. *
* @return value * @return value
*/ */
public Credentials getOverrideCredentials() { public Credentials getOverrideCredentials() {
@ -59,8 +61,8 @@ public class RunScriptOptions {
} }
/** /**
* Whether to run the script as root (or run with current privileges). * Whether to run the script as root (or run with current privileges). By default, true.
* By default, true. *
* @return value * @return value
*/ */
public boolean isRunAsRoot() { public boolean isRunAsRoot() {
@ -68,23 +70,17 @@ public class RunScriptOptions {
} }
public static class Builder { public static class Builder {
private RunScriptOptions options;
public Builder overrideCredentials(Credentials credentials) { public static RunScriptOptions overrideCredentialsWith(Credentials credentials) {
if(options == null) options = new RunScriptOptions(); RunScriptOptions options = new RunScriptOptions();
options.withOverridingCredentials(credentials); return options.withOverridingCredentials(credentials);
return this;
} }
public Builder runAsRoot(boolean value) { public static RunScriptOptions runAsRoot(boolean value) {
if(options == null) options = new RunScriptOptions(); RunScriptOptions options = new RunScriptOptions();
options.runAsRoot(value); return options.runAsRoot(value);
return this;
} }
public RunScriptOptions build() {
return options;
}
} }
} }

View File

@ -34,6 +34,18 @@ public class TemplateOptions {
private boolean destroyOnError; private boolean destroyOnError;
private int port = -1;
private int seconds = -1;
public int getPort() {
return port;
}
public int getSeconds() {
return seconds;
}
public int[] getInboundPorts() { public int[] getInboundPorts() {
return inboundPorts; return inboundPorts;
} }
@ -54,6 +66,17 @@ public class TemplateOptions {
return destroyOnError; return destroyOnError;
} }
/**
* When the node is started, wait until the following port is active
*/
public TemplateOptions blockOnPort(int port, int seconds) {
checkArgument(port > 0 && port < 65536, "port must be a positive integer < 65535");
checkArgument(seconds > 0, "seconds must be a positive integer");
this.port = port;
this.seconds = seconds;
return this;
}
/** /**
* If there is an error applying options after creating the node, destroy it. * If there is an error applying options after creating the node, destroy it.
*/ */
@ -98,6 +121,8 @@ public class TemplateOptions {
* Opens the set of ports to public access. * Opens the set of ports to public access.
*/ */
public TemplateOptions inboundPorts(int... ports) { public TemplateOptions inboundPorts(int... ports) {
for (int port : ports)
checkArgument(port > 0 && port < 65536, "port must be a positive integer < 65535");
this.inboundPorts = ports; this.inboundPorts = ports;
return this; return this;
} }
@ -119,6 +144,14 @@ public class TemplateOptions {
return options.inboundPorts(ports); return options.inboundPorts(ports);
} }
/**
* @see TemplateOptions#port
*/
public static TemplateOptions blockOnPort(int port, int seconds) {
TemplateOptions options = new TemplateOptions();
return options.blockOnPort(port, seconds);
}
/** /**
* @see TemplateOptions#runScript * @see TemplateOptions#runScript
*/ */
@ -149,6 +182,7 @@ public class TemplateOptions {
public String toString() { public String toString() {
return "TemplateOptions [inboundPorts=" + Arrays.toString(inboundPorts) + ", privateKey=" return "TemplateOptions [inboundPorts=" + Arrays.toString(inboundPorts) + ", privateKey="
+ (privateKey != null) + ", publicKey=" + (publicKey != null) + ", runScript=" + (privateKey != null) + ", publicKey=" + (publicKey != null) + ", runScript="
+ (script != null) + ", destroyOnError=" + destroyOnError + "]"; + (script != null) + ", destroyOnError=" + destroyOnError + ", port:seconds=" + port
+ ":" + seconds + "]";
} }
} }

View File

@ -32,6 +32,7 @@ import java.util.Map;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.Resource; import javax.annotation.Resource;
@ -46,6 +47,8 @@ import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.concurrent.ConcurrentUtils; import org.jclouds.concurrent.ConcurrentUtils;
import org.jclouds.domain.Credentials; import org.jclouds.domain.Credentials;
import org.jclouds.logging.Logger; import org.jclouds.logging.Logger;
import org.jclouds.predicates.RetryablePredicate;
import org.jclouds.predicates.SocketOpen;
import org.jclouds.scriptbuilder.InitBuilder; import org.jclouds.scriptbuilder.InitBuilder;
import org.jclouds.scriptbuilder.domain.OsFamily; import org.jclouds.scriptbuilder.domain.OsFamily;
import org.jclouds.ssh.ExecResponse; import org.jclouds.ssh.ExecResponse;
@ -75,6 +78,8 @@ public class ComputeUtils {
private final Predicate<InetSocketAddress> socketTester; private final Predicate<InetSocketAddress> socketTester;
private final ExecutorService executor; private final ExecutorService executor;
private int sshRetries = 3;
@Inject @Inject
public ComputeUtils(Predicate<InetSocketAddress> socketTester, public ComputeUtils(Predicate<InetSocketAddress> socketTester,
@Named("NOT_RUNNING") Predicate<SshClient> runScriptNotRunning, @Named("NOT_RUNNING") Predicate<SshClient> runScriptNotRunning,
@ -111,11 +116,29 @@ public class ComputeUtils {
if (options.getPublicKey() != null) { if (options.getPublicKey() != null) {
callables.add(authorizeKeyOnNode(node, options.getPublicKey())); callables.add(authorizeKeyOnNode(node, options.getPublicKey()));
} }
// changing the key "MUST" come last or else the other commands may fail. // changing the key "MUST" come last or else the other commands may fail.
if (callables.size() > 0 || options.getPrivateKey() != null) { if (callables.size() > 0 || options.getPrivateKey() != null) {
runCallablesOnNode(node, callables, options.getPrivateKey() != null ? installKeyOnNode( runCallablesOnNode(node, callables, options.getPrivateKey() != null ? installKeyOnNode(
node, options.getPrivateKey()) : null); node, options.getPrivateKey()) : null);
} }
if (options.getPort() > 0) {
blockUntilPortIsListeningOnPublicIp(options.getPort(), options.getSeconds(), Iterables
.get(node.getPublicAddresses(), 0));
}
}
private void blockUntilPortIsListeningOnPublicIp(int port, int seconds, InetAddress inetAddress) {
logger.debug(">> blocking on port %s:%d for %d seconds", inetAddress, port, seconds);
RetryablePredicate<InetSocketAddress> tester = new RetryablePredicate<InetSocketAddress>(
new SocketOpen(), seconds, 1, TimeUnit.SECONDS);
InetSocketAddress socket = new InetSocketAddress(inetAddress, port);
boolean passed = tester.apply(socket);
if (passed)
logger.debug("<< port %s:%d opened", inetAddress, port);
else
logger.warn("<< port %s:%d didn't open after %d seconds", seconds, inetAddress, port);
} }
public InstallRSAPrivateKey installKeyOnNode(NodeMetadata node, String privateKey) { public InstallRSAPrivateKey installKeyOnNode(NodeMetadata node, String privateKey) {
@ -130,13 +153,15 @@ public class ComputeUtils {
return new RunScriptOnNode(runScriptNotRunning, node, scriptName, script); return new RunScriptOnNode(runScriptNotRunning, node, scriptName, script);
} }
public RunScriptOnNode runScriptOnNodeAsDefaultUser(NodeMetadata node, String scriptName, byte[] script) { public RunScriptOnNode runScriptOnNodeAsDefaultUser(NodeMetadata node, String scriptName,
byte[] script) {
return new RunScriptOnNode(runScriptNotRunning, node, scriptName, script, false); return new RunScriptOnNode(runScriptNotRunning, node, scriptName, script, false);
} }
public Map<SshCallable<?>, ?> runCallablesOnNode(NodeMetadata node, Iterable<? extends SshCallable<?>> parallel, public Map<SshCallable<?>, ?> runCallablesOnNode(NodeMetadata node,
@Nullable SshCallable<?> last) { Iterable<? extends SshCallable<?>> parallel, @Nullable SshCallable<?> last) {
checkState(this.sshFactory != null, "runScript requested, but no SshModule configured"); checkState(this.sshFactory != null, "runScript requested, but no SshModule configured");
checkNotNull(node.getCredentials().key, "credentials.key for node " + node.getId());
InetSocketAddress socket = new InetSocketAddress(Iterables.get(node.getPublicAddresses(), 0), InetSocketAddress socket = new InetSocketAddress(Iterables.get(node.getPublicAddresses(), 0),
22); 22);
@ -144,7 +169,7 @@ public class ComputeUtils {
SshClient ssh = isKeyAuth(node) ? sshFactory.create(socket, node.getCredentials().account, SshClient ssh = isKeyAuth(node) ? sshFactory.create(socket, node.getCredentials().account,
node.getCredentials().key.getBytes()) : sshFactory.create(socket, node node.getCredentials().key.getBytes()) : sshFactory.create(socket, node
.getCredentials().account, node.getCredentials().key); .getCredentials().account, node.getCredentials().key);
for (int i = 0; i < 3; i++) { for (int i = 0; i < sshRetries; i++) {
try { try {
ssh.connect(); ssh.connect();
Map<SshCallable<?>, ListenableFuture<?>> responses = Maps.newHashMap(); Map<SshCallable<?>, ListenableFuture<?>> responses = Maps.newHashMap();
@ -170,6 +195,8 @@ public class ComputeUtils {
} }
return transform(responses); return transform(responses);
} catch (RuntimeException from) { } catch (RuntimeException from) {
if (i + 1 == sshRetries)
throw Throwables.propagate(from);
if (Iterables.size(Iterables.filter(Throwables.getCausalChain(from), if (Iterables.size(Iterables.filter(Throwables.getCausalChain(from),
ConnectException.class)) >= 1// auth fail sometimes happens in EC2 ConnectException.class)) >= 1// auth fail sometimes happens in EC2
|| Throwables.getRootCause(from).getMessage().indexOf("Auth fail") != -1 || Throwables.getRootCause(from).getMessage().indexOf("Auth fail") != -1
@ -187,17 +214,18 @@ public class ComputeUtils {
ssh.disconnect(); ssh.disconnect();
} }
} }
throw new RuntimeException(String.format("Couldn't connect to node %s and run the script", node.getId())); throw new RuntimeException(String.format("Couldn't connect to node %s and run the script",
node.getId()));
} }
public <T> Map<SshCallable<?>, T> transform(Map<SshCallable<?>, ListenableFuture<?>> responses) { public <T> Map<SshCallable<?>, T> transform(Map<SshCallable<?>, ListenableFuture<?>> responses) {
Map<SshCallable<?>, T> actualResponses = Maps.newHashMap(); Map<SshCallable<?>, T> actualResponses = Maps.newHashMap();
for(Map.Entry<SshCallable<?>, ListenableFuture<?>> entry : responses.entrySet()) { for (Map.Entry<SshCallable<?>, ListenableFuture<?>> entry : responses.entrySet()) {
try { try {
actualResponses.put(entry.getKey(), (T) entry.getValue().get()); actualResponses.put(entry.getKey(), (T) entry.getValue().get());
} catch(InterruptedException e) { } catch (InterruptedException e) {
throw Throwables.propagate(e); throw Throwables.propagate(e);
} catch(ExecutionException e) { } catch (ExecutionException e) {
throw Throwables.propagate(e); throw Throwables.propagate(e);
} }
} }
@ -247,8 +275,10 @@ public class ComputeUtils {
ExecResponse returnVal = ssh.exec("chmod 755 " + scriptName); ExecResponse returnVal = ssh.exec("chmod 755 " + scriptName);
returnVal = ssh.exec("./" + scriptName + " init"); returnVal = ssh.exec("./" + scriptName + " init");
if(runAsRoot) returnVal = runScriptAsRoot(); if (runAsRoot)
else returnVal = runScriptAsDefaultUser(); returnVal = runScriptAsRoot();
else
returnVal = runScriptAsDefaultUser();
runScriptNotRunning.apply(ssh); runScriptNotRunning.apply(ssh);
logger.debug("<< complete(%d)", returnVal.getExitCode()); logger.debug("<< complete(%d)", returnVal.getExitCode());
return returnVal; return returnVal;
@ -279,9 +309,8 @@ public class ComputeUtils {
} }
private ExecResponse runScriptAsDefaultUser() { private ExecResponse runScriptAsDefaultUser() {
logger.debug(">> running script %s as %s@%s", scriptName, logger.debug(">> running script %s as %s@%s", scriptName, node.getCredentials().account,
node.getCredentials().account, Iterables.get(node.getPublicAddresses(), 0) Iterables.get(node.getPublicAddresses(), 0).getHostAddress());
.getHostAddress());
return ssh.exec(String.format("./%s", scriptName + " start")); return ssh.exec(String.format("./%s", scriptName + " start"));
} }
} }
@ -353,13 +382,13 @@ public class ComputeUtils {
} }
/** /**
* Given the instances of {@link NodeMetadata} (immutable) * Given the instances of {@link NodeMetadata} (immutable) and {@link Credentials} (immutable),
* and {@link Credentials} (immutable), returns a new instance of {@link NodeMetadata} * returns a new instance of {@link NodeMetadata} that has new credentials
* that has new credentials
*/ */
public static NodeMetadata installNewCredentials(NodeMetadata node, Credentials newCredentials) { public static NodeMetadata installNewCredentials(NodeMetadata node, Credentials newCredentials) {
return new NodeMetadataImpl(node.getId(), node.getName(), node.getLocationId(), node.getUri(), return new NodeMetadataImpl(node.getId(), node.getName(), node.getLocationId(),
node.getUserMetadata(), node.getTag(), node.getState(), node. getPublicAddresses(), node.getUri(), node.getUserMetadata(), node.getTag(), node.getState(), node
node.getPrivateAddresses(), node.getExtra(), newCredentials); .getPublicAddresses(), node.getPrivateAddresses(), node.getExtra(),
newCredentials);
} }
} }

View File

@ -0,0 +1,152 @@
/**
*
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*/
package org.jclouds.compute.options;
import static org.jclouds.compute.options.TemplateOptions.Builder.authorizePublicKey;
import static org.jclouds.compute.options.TemplateOptions.Builder.blockOnPort;
import static org.jclouds.compute.options.TemplateOptions.Builder.inboundPorts;
import static org.jclouds.compute.options.TemplateOptions.Builder.installPrivateKey;
import static org.testng.Assert.assertEquals;
import org.testng.annotations.Test;
/**
* Tests possible uses of TemplateOptions and TemplateOptions.Builder.*
*
* @author Adrian Cole
*/
public class TemplateOptionsTest {
@Test(expectedExceptions = IllegalArgumentException.class)
public void testinstallPrivateKeyBadFormat() {
TemplateOptions options = new TemplateOptions();
options.installPrivateKey("whompy");
}
@Test
public void testinstallPrivateKey() {
TemplateOptions options = new TemplateOptions();
options.installPrivateKey("-----BEGIN RSA PRIVATE KEY-----");
assertEquals(options.getPrivateKey(), "-----BEGIN RSA PRIVATE KEY-----");
}
@Test
public void testNullinstallPrivateKey() {
TemplateOptions options = new TemplateOptions();
assertEquals(options.getPrivateKey(), null);
}
@Test
public void testinstallPrivateKeyStatic() {
TemplateOptions options = installPrivateKey("-----BEGIN RSA PRIVATE KEY-----");
assertEquals(options.getPrivateKey(), "-----BEGIN RSA PRIVATE KEY-----");
}
@Test(expectedExceptions = NullPointerException.class)
public void testinstallPrivateKeyNPE() {
installPrivateKey(null);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testauthorizePublicKeyBadFormat() {
TemplateOptions options = new TemplateOptions();
options.authorizePublicKey("whompy");
}
@Test
public void testauthorizePublicKey() {
TemplateOptions options = new TemplateOptions();
options.authorizePublicKey("ssh-rsa");
assertEquals(options.getPublicKey(), "ssh-rsa");
}
@Test
public void testNullauthorizePublicKey() {
TemplateOptions options = new TemplateOptions();
assertEquals(options.getPublicKey(), null);
}
@Test
public void testauthorizePublicKeyStatic() {
TemplateOptions options = authorizePublicKey("ssh-rsa");
assertEquals(options.getPublicKey(), "ssh-rsa");
}
@Test(expectedExceptions = NullPointerException.class)
public void testauthorizePublicKeyNPE() {
authorizePublicKey(null);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testblockOnPortBadFormat() {
TemplateOptions options = new TemplateOptions();
options.blockOnPort(-1, -1);
}
@Test
public void testblockOnPort() {
TemplateOptions options = new TemplateOptions();
options.blockOnPort(22, 30);
assertEquals(options.getPort(), 22);
assertEquals(options.getSeconds(), 30);
}
@Test
public void testNullblockOnPort() {
TemplateOptions options = new TemplateOptions();
assertEquals(options.getPort(), -1);
assertEquals(options.getSeconds(), -1);
}
@Test
public void testblockOnPortStatic() {
TemplateOptions options = blockOnPort(22, 30);
assertEquals(options.getPort(), 22);
assertEquals(options.getSeconds(), 30);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testinboundPortsBadFormat() {
TemplateOptions options = new TemplateOptions();
options.inboundPorts(-1, -1);
}
@Test
public void testinboundPorts() {
TemplateOptions options = new TemplateOptions();
options.inboundPorts(22, 30);
assertEquals(options.getInboundPorts()[0], 22);
assertEquals(options.getInboundPorts()[1], 30);
}
@Test
public void testDefaultOpen22() {
TemplateOptions options = new TemplateOptions();
assertEquals(options.getInboundPorts()[0], 22);
}
@Test
public void testinboundPortsStatic() {
TemplateOptions options = inboundPorts(22, 30);
assertEquals(options.getInboundPorts()[0], 22);
assertEquals(options.getInboundPorts()[1], 30);
}
}