Skip to content

Commit

Permalink
Synced initial from complete.
Browse files Browse the repository at this point in the history
  • Loading branch information
trautonen committed Jun 9, 2015
1 parent 3418f71 commit eca5509
Show file tree
Hide file tree
Showing 34 changed files with 806 additions and 248 deletions.
8 changes: 8 additions & 0 deletions workshop/initial/aws-workshop-common/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,18 @@
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-iam</artifactId>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-ec2</artifactId>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-autoscaling</artifactId>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-simpledb</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.gofore.aws.workshop.common.asg;

import com.amazonaws.handlers.AsyncHandler;
import com.amazonaws.services.autoscaling.AmazonAutoScalingAsync;
import com.amazonaws.services.autoscaling.model.AutoScalingGroup;
import com.amazonaws.services.autoscaling.model.DescribeAutoScalingGroupsRequest;
import com.amazonaws.services.autoscaling.model.DescribeAutoScalingGroupsResult;
import com.amazonaws.services.autoscaling.model.Instance;
import com.gofore.aws.workshop.common.functional.Lists;

import java.util.List;
import java.util.concurrent.CompletableFuture;

public class AsgClient {

private final AmazonAutoScalingAsync asg;

public AsgClient(AmazonAutoScalingAsync asg) {
this.asg = asg;
}

public CompletableFuture<DescribeAutoScalingGroupsResult> describeAutoScalingGroups(DescribeAutoScalingGroupsRequest request) {
CompletableFuture<DescribeAutoScalingGroupsResult> future = new CompletableFuture<>();
asg.describeAutoScalingGroupsAsync(request, new AsyncHandler<DescribeAutoScalingGroupsRequest, DescribeAutoScalingGroupsResult>() {
@Override
public void onError(Exception exception) {
future.completeExceptionally(exception);
}

@Override
public void onSuccess(DescribeAutoScalingGroupsRequest request, DescribeAutoScalingGroupsResult describeAutoScalingGroupsResult) {
future.complete(describeAutoScalingGroupsResult);
}
});
return future;
}

public CompletableFuture<List<Instance>> getInstances(String asgName) {
return describeAutoScalingGroups(new DescribeAutoScalingGroupsRequest().withAutoScalingGroupNames(asgName))
.thenApply(DescribeAutoScalingGroupsResult::getAutoScalingGroups)
.thenApply(Lists.findFirst())
.thenApply(AutoScalingGroup::getInstances);
}
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,36 @@
package com.gofore.aws.workshop.common.cloudformation;

import static com.gofore.aws.workshop.common.functional.Lists.findFirst;

import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;

import com.amazonaws.handlers.AsyncHandler;
import com.amazonaws.services.cloudformation.AmazonCloudFormationAsync;
import com.amazonaws.services.cloudformation.model.DescribeStacksRequest;
import com.amazonaws.services.cloudformation.model.DescribeStacksResult;
import com.amazonaws.services.cloudformation.model.Output;
import com.amazonaws.services.cloudformation.model.Stack;
import com.gofore.aws.workshop.common.async.Threads;
import com.google.common.collect.ImmutableSet;

import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.function.Function;

import static com.gofore.aws.workshop.common.functional.Lists.findFirst;

public class CloudFormationClient {

private static final String[] STACK_COMPLETION_STATUSES = {
"CREATE_FAILED", "CREATE_COMPLETE",
"ROLLBACK_FAILED", "ROLLBACK_COMPLETE",
"UPDATE_COMPLETE", "UPDATE_ROLLBACK_FAILED", "UPDATE_ROLLBACK_COMPLETE"
};

private final AmazonCloudFormationAsync cloudFormation;
private final ExecutorService executor;

public CloudFormationClient(AmazonCloudFormationAsync cloudFormation) {
public CloudFormationClient(AmazonCloudFormationAsync cloudFormation, ExecutorService executor) {
this.cloudFormation = cloudFormation;
this.executor = executor;
}

public CompletableFuture<DescribeStacksResult> describeStacks(DescribeStacksRequest request) {
Expand All @@ -44,8 +56,7 @@ public void onSuccess(DescribeStacksRequest request, DescribeStacksResult result
* @return future of stack's outputs or failed future if stack does not exist
*/
public CompletableFuture<List<Output>> getStackOutputs(String stackName) {
return describeStacks(new DescribeStacksRequest().withStackName(stackName))
.thenApply(r -> r.getStacks().get(0))
return getStackWhen(stackName, STACK_COMPLETION_STATUSES)
.thenApply(Stack::getOutputs);
}

Expand All @@ -62,4 +73,20 @@ public CompletableFuture<Output> getStackOutput(String stackName, String outputK
.thenApply(findFirst(o -> o.getOutputKey().equals(outputKey)))
.thenApply(Optional::get);
}

public CompletableFuture<Stack> getStackWhen(String stackName, String... statuses) {
DescribeStacksRequest request = new DescribeStacksRequest().withStackName(stackName);
return describeStacks(request).thenApply(firstStack()).thenComposeAsync(stack -> {
if (ImmutableSet.copyOf(statuses).contains(stack.getStackStatus()) && !stack.getOutputs().isEmpty()) {
return CompletableFuture.completedFuture(stack);
} else {
Threads.sleep(1000);
return getStackWhen(stackName, statuses);
}
}, executor);
}

private Function<DescribeStacksResult, Stack> firstStack() {
return (dsr) -> dsr.getStacks().get(0);
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
package com.gofore.aws.workshop.common.di;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.AWSCredentialsProviderChain;
import com.amazonaws.auth.InstanceProfileCredentialsProvider;
import com.amazonaws.auth.profile.ProfileCredentialsProvider;
import com.amazonaws.internal.StaticCredentialsProvider;
import com.amazonaws.services.autoscaling.AmazonAutoScalingAsync;
import com.amazonaws.services.autoscaling.AmazonAutoScalingAsyncClient;
import com.amazonaws.services.cloudformation.AmazonCloudFormationAsync;
import com.amazonaws.services.cloudformation.AmazonCloudFormationAsyncClient;
import com.amazonaws.services.ec2.AmazonEC2Async;
import com.amazonaws.services.ec2.AmazonEC2AsyncClient;
import com.amazonaws.services.identitymanagement.AmazonIdentityManagementAsync;
import com.amazonaws.services.identitymanagement.AmazonIdentityManagementAsyncClient;
import com.amazonaws.services.s3.AmazonS3;
Expand All @@ -19,17 +20,19 @@
import com.amazonaws.services.simpledb.AmazonSimpleDBAsyncClient;
import com.amazonaws.services.sqs.AmazonSQSAsync;
import com.amazonaws.services.sqs.AmazonSQSAsyncClient;
import com.gofore.aws.workshop.common.asg.AsgClient;
import com.gofore.aws.workshop.common.async.ShutdownHelper;
import com.gofore.aws.workshop.common.cloudformation.CloudFormationClient;
import com.gofore.aws.workshop.common.ec2.Ec2Client;
import com.gofore.aws.workshop.common.properties.ApplicationProperties;
import com.gofore.aws.workshop.common.properties.PropertyLoader;
import com.gofore.aws.workshop.common.s3.S3Client;
import com.gofore.aws.workshop.common.simpledb.SimpleDBClient;
import com.gofore.aws.workshop.common.sqs.SqsClient;
import com.google.inject.Binder;
import com.google.inject.Module;
import com.google.inject.Provides;
import com.google.inject.Singleton;
import com.google.inject.*;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class AwsModule implements Module {

Expand All @@ -40,7 +43,7 @@ public void configure(Binder binder) {

@Provides
public ExecutorService executor() {
return Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
return Executors.newFixedThreadPool(15);
}

/**
Expand Down Expand Up @@ -132,10 +135,45 @@ public AmazonCloudFormationAsync cloudFormation(ApplicationProperties properties

@Provides
@Singleton
public CloudFormationClient cloudFormationClient(AmazonCloudFormationAsync cloudFormation) {
return new CloudFormationClient(cloudFormation);
public CloudFormationClient cloudFormationClient(AmazonCloudFormationAsync cloudFormation,
ExecutorService executor) {
return new CloudFormationClient(cloudFormation, executor);
}


@Provides
@Singleton
public AmazonEC2Async ec2(ApplicationProperties properties,
AWSCredentialsProvider credentials,
ExecutorService executor) {
AmazonEC2AsyncClient ec2 = new AmazonEC2AsyncClient(credentials, executor);
ec2.setEndpoint(properties.lookup("aws.ec2.endpoint"));
ShutdownHelper.addShutdownHook(ec2::getExecutorService, ec2::shutdown);
return ec2;
}

@Provides
@Singleton
public Ec2Client ec2Client(AmazonEC2Async ec2) {
return new Ec2Client(ec2);
}

@Provides
@Singleton
public AmazonAutoScalingAsync asg(ApplicationProperties properties,
AWSCredentialsProvider credentials,
ExecutorService executor) {
AmazonAutoScalingAsyncClient asg = new AmazonAutoScalingAsyncClient(credentials, executor);
asg.setEndpoint(properties.lookup("aws.asg.endpoint"));
ShutdownHelper.addShutdownHook(asg::getExecutorService, asg::shutdown);
return asg;
}

@Provides
@Singleton
public AsgClient asgClient(AmazonAutoScalingAsync asg) {
return new AsgClient(asg);
}

/**
* Credentials that are bound to a PropertyLoader.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.gofore.aws.workshop.common.ec2;

import com.amazonaws.handlers.AsyncHandler;
import com.amazonaws.services.ec2.AmazonEC2Async;
import com.amazonaws.services.ec2.model.*;
import com.amazonaws.util.EC2MetadataUtils;
import com.gofore.aws.workshop.common.functional.Lists;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;

import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class Ec2Client {

private final AmazonEC2Async ec2;

public Ec2Client(AmazonEC2Async ec2) {
this.ec2 = ec2;
}

public String getInstanceId() {
return EC2MetadataUtils.getInstanceId();
}

public Optional<Tag> getTag(Instance instance, String name) {
return Lists.findFirst(instance.getTags(), t -> t.getKey().equals(name));
}

public CompletableFuture<DescribeInstancesResult> describeInstances(DescribeInstancesRequest request) {
CompletableFuture<DescribeInstancesResult> future = new CompletableFuture<>();
ec2.describeInstancesAsync(request, new AsyncHandler<DescribeInstancesRequest, DescribeInstancesResult>() {
@Override
public void onError(Exception exception) {
future.completeExceptionally(exception);
}

@Override
public void onSuccess(DescribeInstancesRequest request, DescribeInstancesResult describeInstancesResult) {
future.complete(describeInstancesResult);
}
});
return future;
}

public CompletableFuture<List<Instance>> getInstances(List<String> instanceIds, boolean requirePrivateAccessible) {
return describeInstances(new DescribeInstancesRequest().withInstanceIds(instanceIds))
.thenApply(DescribeInstancesResult::getReservations)
.thenApply(rs -> rs.stream().flatMap(r -> r.getInstances().stream()))
.thenApply(instances -> instances.filter(accessible(requirePrivateAccessible)))
.thenApply(instances -> instances.collect(Collectors.toList()));
}

public CompletableFuture<Instance> getInstance(String instanceId, boolean requirePrivateAccessible) {
return getInstances(ImmutableList.of(instanceId), requirePrivateAccessible)
.thenApply(Lists.findFirst());
}

private Predicate<Instance> accessible(boolean requirePrivateAccessible) {
if (requirePrivateAccessible) {
return i -> !Strings.isNullOrEmpty(i.getPrivateIpAddress());
} else {
return i -> true;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ public static <T> Optional<T> findFirst(List<T> list, Predicate<T> predicate) {
public static <T> Function<List<T>, Optional<T>> findFirst(Predicate<T> predicate) {
return l -> findFirst(l, predicate);
}


public static <T> Function<List<T>, T> findFirst() {
Function<List<T>, Optional<T>> f = findFirst(v -> true);
return f.andThen(Optional::get);
}

private Lists() {

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ public ApplicationProperties withSystemPropertyLoader() {
return this;
}

public ApplicationProperties withEnvironmentPropertyLoader() {
chain.add(new EnvironmentPropertyLoader());
return this;
}

public ApplicationProperties withAwsCredentialsEnvLoader() {
chain.add(new AwsCredentialsEnvLoader());
return this;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,32 @@
package com.gofore.aws.workshop.common.properties;

import java.util.Map;
import java.util.Optional;

import com.amazonaws.services.cloudformation.model.Output;
import com.gofore.aws.workshop.common.cloudformation.CloudFormationClient;
import com.google.common.collect.Maps;

import java.util.Map;
import java.util.Optional;

public class CloudFormationOutputsPropertyLoader extends AbstractPropertyLoader {

private final Map<String, Output> outputs;
private final String awsStack;
private final CloudFormationClient cloudFormationClient;
private Map<String, Output> outputs;

public CloudFormationOutputsPropertyLoader(ApplicationProperties properties, CloudFormationClient cloudFormationClient) {
this.outputs = Maps.uniqueIndex(
cloudFormationClient.getStackOutputs(properties.lookup("aws.stack")).join(),
Output::getOutputKey
);
this.awsStack = properties.lookup("aws.stack");
this.cloudFormationClient = cloudFormationClient;
}

@Override
public Optional<String> lookupOptional(String name) {
public synchronized Optional<String> lookupOptional(String name) {
loadOutputs();
return Optional.ofNullable(outputs.get(name)).map(Output::getOutputValue);
}

private void loadOutputs() {
if (outputs == null) {
this.outputs = Maps.uniqueIndex(cloudFormationClient.getStackOutputs(awsStack).join(), Output::getOutputKey);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.gofore.aws.workshop.common.properties;

import java.util.Map;
import java.util.Optional;

public class EnvironmentPropertyLoader extends AbstractPropertyLoader {

private final Map<String, String> env;

public EnvironmentPropertyLoader() {
this.env = System.getenv();
}

@Override
public Optional<String> lookupOptional(String name) {
return Optional.ofNullable(env.get(name));
}
}
Loading

0 comments on commit eca5509

Please sign in to comment.