Tuesday, September 30, 2008

Stage 3 - The Code

Now it's time to add the auto-start code.
I tried to change the existing code as little as possible (that's the idea - keep the changes small).
Ophir pointed me to great EC2 API for Java - Typica.
It has a little dependencies, so I added them to the demo, and changed the build appropriately.
The most simple and less intrusive addition I can think of is listener on the "doAddWork" operation in HttpHandler.
As it being invoked, the only new class, Ec2Instantiator is notified. There I can encapsulate all the EC2 work without intruding the example.

So, what the code does?

First of all, it checks if new server is needed - if after the addition more than 100 jobs will be in the queue, fire new server. It's stupid check, but very simple, isn't it?
If that's the case, load different EC2 properties from properties file. Those properties include:
  • AWS Access Id and Secret Key to perform any operations
  • Owner Id to find my images
  • Client Image Location to distinguish it from Server Image
  • Key Name to start the server with it
  • Custom Security Group additional security group (in addition to default) to run the server with it. In my case it's the same Terracotta security group, which opens port 9510 for the server, and ports 1190-2000 for 10 clients
Once the properties are loaded, the code gets all images by owner. From those, it picks one, which matches client image location, and launches one instance of it.

That's it! As simple as it gets! Here is the code:

@SuppressWarnings({"UseOfSystemOutOrSystemErr", "CallToPrintStackTrace"})

15
public class Ec2Instantiator implements QueueGrowListener {
16
17
private String accessId;
18
private String accessKey;
19
private String ownerId;

20
private String clientImageLocation;
21
private String keyName;
22
private String customSecurityGroup;
23
private int triggerSize;


24
25
public Ec2Instantiator(Properties appProps) {
26
this.accessId = appProps.getProperty("aws.accessId");
27
this.accessKey = appProps.getProperty("aws.secretKey");

28 ownerId = appProps.getProperty(
"aws.ownerId");
29 clientImageLocation = appProps.getProperty(
"aws.clientImageLocation");
30 keyName = appProps.getProperty(
"aws.keyName");
31 customSecurityGroup = appProps.getProperty(
"aws.customSecurityGroup");

32 triggerSize = Integer.parseInt(appProps.getProperty(
"queue.triggerSize"));
33 }

34
35
public void queueGrown(int oldSize, int newSize) {

36
37
//All this happnes only if too many jobs added to the queue
38
if (newSize > triggerSize) {
39 System.out.println(
"Queue overloaded! Will fire up another client to help");

40
41
//Start new instance asyncronicaly
42
new Thread(new Runnable() {
43
public void run() {
44
try {

45
46
//connect to EC2
47 Jec2 ec2 =
new Jec2(accessId, accessKey);
48
49
//get all my images

50 List<ImageDescription> myImages = ec2.describeImagesByOwner(asList(ownerId));
51 ImageDescription clientImage =
null;
52
for (ImageDescription image : myImages) {
53

54
//find the Terracotta client one
55
if (image.getImageLocation().equals(clientImageLocation)) {
56 clientImage = image;
57 }
58 }

59
if (clientImage == null) {
60
throw new IllegalStateException("Can't find image manifest in " + clientImageLocation);
61 }
62

63 System.out.println(
"Found JavaEdge Client image: "+clientImage.getImageId());
64
65
//launch configuration - which machine, which security groups
66 LaunchConfiguration launchConfiguration =
new LaunchConfiguration(clientImage.getImageId());
67 launchConfiguration.setKeyName(keyName);

68
69
//small instance
70 launchConfiguration.setInstanceType(InstanceType.DEFAULT);
71
72
//both default and terracotta security groups
73 launchConfiguration.setSecurityGroup(asList(
"default", customSecurityGroup));

74
75
//RUN!
76 System.out.println(
"Starting instance.");
77 ReservationDescription reservationDescription = ec2.runInstances(launchConfiguration);
78 ReservationDescription.Instance instance = reservationDescription.getInstances().get(
0);//i run one, so there is one

79
80
//check the state
81 System.out.println(
"Instance state: " + instance.getState());
82
while (!instance.isRunning()) {
83 paintActivity();

84 instance = ec2.describeInstances(asList(instance.getInstanceId())).get(
0).getInstances().get(0);//refresh
85 }
86 System.out.println(
"Instance is up. Check the UI to see the new client in action.");

87 }
catch (EC2Exception e) {
88 System.err.println(
"Error during client instance instantiation");
89 e.printStackTrace();
90 }
91

92 }
93 }).start();
94 }
95 }

No comments: