Last active
December 22, 2015 22:49
-
-
Save zack-shoylev/6542561 to your computer and use it in GitHub Desktop.
Full Java Workshop code
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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