Skip to content

Instantly share code, notes, and snippets.

@zack-shoylev
Last active December 22, 2015 22:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zack-shoylev/6542561 to your computer and use it in GitHub Desktop.
Save zack-shoylev/6542561 to your computer and use it in GitHub Desktop.
Full Java Workshop code
package org.jclouds.workshop;
import static com.google.common.base.Charsets.UTF_8;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.jclouds.compute.predicates.NodePredicates.inGroup;
import static org.jclouds.scriptbuilder.domain.Statements.exec;
import static org.jclouds.util.Predicates2.retry;
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
import java.util.concurrent.TimeoutException;
import org.jclouds.ContextBuilder;
import org.jclouds.compute.ComputeService;
import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.Template;
import org.jclouds.compute.options.RunScriptOptions;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.domain.LoginCredentials;
import org.jclouds.logging.slf4j.config.SLF4JLoggingModule;
import org.jclouds.openstack.nova.v2_0.NovaApi;
import org.jclouds.openstack.nova.v2_0.NovaAsyncApi; // Gone in 1.7!
import org.jclouds.openstack.nova.v2_0.domain.SecurityGroup;
import org.jclouds.openstack.nova.v2_0.extensions.SecurityGroupApi;
import org.jclouds.predicates.SocketOpen;
import org.jclouds.rest.RestContext;
import org.jclouds.scriptbuilder.ScriptBuilder;
import org.jclouds.scriptbuilder.domain.OsFamily;
import org.jclouds.ssh.SshKeys;
import org.jclouds.sshj.config.SshjSshClientModule;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.io.Files;
import com.google.common.net.HostAndPort;
import com.google.inject.Module;
@SuppressWarnings({ "deprecation" })
public class JCloudsWorkshop implements Closeable {
String provider = "openstack-nova";
String user = "";
String password = "";
String zone = "";
String address = "";
String privateKey = "";
private ComputeService compute; // High-level abstraction layer
private RestContext<NovaApi, NovaAsyncApi> nova; // Lower level abstraction layer: openstack
private NodeMetadata node; // Represents a compute node (server)
private static Scanner s = new Scanner(System.in); // To pause execution
public static void main(String[] args) {
JCloudsWorkshop jcloudsWorkshop = new JCloudsWorkshop();
try {
jcloudsWorkshop.init(args);
jcloudsWorkshop.createNode();
jcloudsWorkshop.configureAndStartWebserver();
jcloudsWorkshop.detectExtensions();
jcloudsWorkshop.printResults();
jcloudsWorkshop.deleteNode();
}
catch (Throwable e) {
e.printStackTrace();
}
finally {
jcloudsWorkshop.close();
}
}
private void init(String[] args) throws FileNotFoundException, IOException {
// Get credentials from a file, if it exists. First line should be "user;password"
if( (new File("credentials.txt")).exists() ) {
BufferedReader br = null;
try {
br = (new BufferedReader(new FileReader("credentials.txt")));
String[] credentials = br.readLine().split(";");
user = credentials[0];
password = credentials[1];
}
finally {
if(br!=null) br.close();
}
} else {
if("".equals(user) || "".equals(password)) {
System.out.println("No credentials");
System.exit(1);
}
}
// Choose local devstack or remote rackspace endpoint. Rackspace has better default images!
// The rackspace-specific provider has the endpoint included (cloudservers)
String endpoint = "demo:demo".equals(user) ? "http://127.0.0.1:5000/v2.0/" : "https://identity.api.rackspacecloud.com/v2.0";
Iterable<Module> modules = ImmutableSet.<Module> of(
new SshjSshClientModule(), // SSH module
new SLF4JLoggingModule()); // Logging module
// Get a compute service context. This is a jclouds abstraction that establishes a "connection" to the service.
ComputeServiceContext context = ContextBuilder.newBuilder(provider)
.credentials(user, password)
.endpoint(endpoint)
.modules(modules)
.apiVersion("2")
.buildView(ComputeServiceContext.class);
compute = context.getComputeService(); // The abstraction layer
nova = context.unwrap(); // Get the lower-level layer from the higher level abstraction
}
private void createNode() {
try {
// Use jclouds to generate a keypair
Map<String,String> keys = SshKeys.generate();
// Get the private key from the keypair map
privateKey = keys.get("private");
Files.write(privateKey, new File("jclouds.pem"), UTF_8);
// Uses security groups if available (determines right approach automatically) to enable SSH and HTTP
TemplateOptions options = compute.templateOptions().inboundPorts(22, 80);
// GOTCHA: cirros image has no bash-shopt, so jclouds can't authorize a public key using TemplateOptions
// Workaround possible: Use key pair and nova abstraction layer
if("demo:demo".equals(user)) {
// jclouds allows you to override credentials if needed
options.overrideLoginCredentials(
LoginCredentials.builder()
.user("cirros")
.password("cubswin:)")
.build());
} else {
// jclouds allows you to authorize a public key on the server
// We also override the private key to use the one from the keypair generated above
options.authorizePublicKey(keys.get("public"))
.overrideLoginPrivateKey(privateKey);
}
// The template builder saves a lot of code when creating a server
// Figures out images, flavors, security groups.
Template template = compute.templateBuilder()
// GOTCHA: 512mb will not always boot properly - you might not be able to ssh in!
.minRam(1024)
.smallest()
// GOTCHA: "cirros-0.3.1-x86_64-uec" would boot the wrong image, such as -ramdisk;
// use regular expressions "$" to fix that
.imageNameMatches("demo:demo".equals(user) ?
"cirros-0\\.3\\.1-x86_64-uec$"
: ".*Cent.*")
.options(options)
.build();
System.out.println("ENTER to create node");
s.nextLine();
// Apply template and options
// Wait until node is created
Set<? extends NodeMetadata> nodesInGroup = compute.createNodesInGroup("jclouds-workshop", 1, template);
System.out.println("ENTER to continue");
s.nextLine();
node = nodesInGroup.iterator().next();
System.out.println("Node created: " + node.toString());
System.out.println("Node Image: " + node.getImageId());
}
catch (Throwable e) {
e.printStackTrace();
}
}
private void configureAndStartWebserver() {
try {
if("demo:demo".equals(user)) {
// Devstack not configured for public IPs
address = node.getPrivateAddresses().iterator().next();
// GOTCHA: The cirros image is missing a lot of features. It is mostly only viable for testing.
// No bash-shopt: jclouds can't run scripts on it
// No yum/apt/etc... can't install
// No toolchain... can't build
// Very fast boots and good for testing!
return;
} else {
// Get the public IP from Rackspace
address = node.getPublicAddresses().iterator().next();
}
System.out.println("SSHing to " + address);
// Wait for the ssh service to start on the server
awaitSsh(address);
System.out.println("Done!");
String message = new StringBuilder()
.append("Hello from ")
.append(node.getHostname())
.append(" @ ")
.append(address)
.append(" in ")
.append(node.getLocation().getParent().getId())
.toString();
// Install apache! However the script might be different for a different image.
String script = new ScriptBuilder()
.addStatement(exec("yum -y install httpd"))
.addStatement(exec("/usr/sbin/apachectl start"))
.addStatement(exec("iptables -I INPUT -p tcp --dport 80 -j ACCEPT"))
.addStatement(exec("echo '" + message + "' > /var/www/html/index.html"))
.render(OsFamily.UNIX);
// Script runner takes care of authentication and saves us some code
RunScriptOptions options = RunScriptOptions.Builder
.blockOnComplete(true)
.overrideLoginPrivateKey(privateKey);
compute.runScriptOnNode(node.getId(), script, options);
}
catch (Throwable e) {
e.printStackTrace();
}
}
private void awaitSsh(String ip) throws TimeoutException {
SocketOpen socketOpen = compute.getContext().utils().injector().getInstance(SocketOpen.class);
Predicate<HostAndPort> socketTester = retry(socketOpen, 300, 5, 5, SECONDS);
socketTester.apply(HostAndPort.fromParts(ip, 22));
}
private void detectExtensions() {
try {
Set<String> zones = nova.getApi().getConfiguredZones();
String zone = Iterables.getFirst(zones, null);
String provider = compute.getContext().toString().contains("rackspace") ?
"Rackspace CloudServers" : "Openstack Nova";
// Use jclouds to check if security groups are supported for this provider
Optional<? extends SecurityGroupApi> securityGroupExt =
nova.getApi().getSecurityGroupExtensionForZone(zone);
// This showcases how jclouds supports provider-specific extensions, such as security groups.
System.out.println("Provider: " + provider);
System.out.println(" Security Group Support: " + securityGroupExt.isPresent());
if (securityGroupExt.isPresent()) {
SecurityGroupApi securityGroupApi = securityGroupExt.get();
for (SecurityGroup securityGroup: securityGroupApi.list()) {
System.out.println(" Security Group: " + securityGroup.getName());
}
}
}
catch (Throwable e) {
e.printStackTrace();
}
}
private void printResults() throws IOException {
try {
String provider = compute.getContext().toString().contains("rackspace") ?
"Rackspace CloudServers" : "Openstack Nova";
LoginCredentials credentials = node.getCredentials();
System.out.println("Provider: " + provider);
System.out.println("Credentials: " + credentials);
if(credentials != null) {
if (credentials.getOptionalPrivateKey().isPresent()) {
System.out.println(" Login: ssh -i jclouds.pem " + credentials.getUser() + "@" + address);
}
else {
System.out.println(" Login: ssh " + node.getCredentials().getUser() + "@" + address);
System.out.println(" Password: " + node.getCredentials().getPassword());
}
}
if(!"demo:demo".equals(user)) {
System.out.println(" Go to http://" + address);
}
}
catch (Throwable e) {
e.printStackTrace();
}
}
/**
* This will delete all servers in group "jclouds-workshop"
*/
private void deleteNode() {
System.out.println("ENTER to delete node");
s.nextLine();
try {
// Destroy all nodes in the "jclouds-workshop" group
// Saves a lot of search-and-delete code
Set<? extends NodeMetadata> servers = compute.destroyNodesMatching(inGroup("jclouds-workshop"));
for (NodeMetadata nodeMetadata: servers) {
System.out.println(" " + nodeMetadata);
}
}
catch (Throwable e) {
e.printStackTrace();
}
}
public void close() {
if (compute != null) {
compute.getContext().close();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment