Java tutorial
/* * Copyright 2013 by Andrew Kennedy * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package grkvlt; import java.util.List; import java.util.Set; import org.jclouds.ContextBuilder; import org.jclouds.ec2.EC2AsyncClient; import org.jclouds.ec2.EC2Client; import org.jclouds.ec2.domain.KeyPair; import org.jclouds.ec2.domain.SecurityGroup; import org.jclouds.ec2.domain.Tag; import org.jclouds.ec2.domain.Volume; import org.jclouds.ec2.features.TagApi; import org.jclouds.ec2.services.ElasticBlockStoreClient; import org.jclouds.ec2.services.KeyPairClient; import org.jclouds.ec2.services.SecurityGroupClient; import org.jclouds.ec2.util.TagFilterBuilder; import org.jclouds.javax.annotation.Nullable; import org.jclouds.logging.slf4j.config.SLF4JLoggingModule; import org.jclouds.rest.RestContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.inject.Module; /** * Utility to clean up AWS EC2 security groups and keypairs * * @author Andrew Kennedy * @version 0.1.0-SNAPSHOT */ public class Ec2CleanUp { private static final Logger LOG = LoggerFactory.getLogger(Ec2CleanUp.class); /** Amazon Europe Region. */ private static final String AWS_EUROPE = "eu-west-1"; /** Default jclouds object name pattern. */ private static final String JCLOUDS_NAME_REGEXP = "jclouds#.*"; /** System property for AWS EC2 access key. */ private static final String IDENTITY_PROPERTY = "aws-ec2.identity"; /** System property for AWS EC2 secret key. */ private static final String CREDENTIAL_PROPERTY = "aws-ec2.credential"; private final String region; private final String regexp; private final String identity; private final String credential; private final boolean check; /** * Initialise the region and regular expressions. */ public Ec2CleanUp(String region, String regexp, String identity, String credential, boolean check) { this.region = region; this.regexp = regexp; this.identity = identity; this.credential = credential; this.check = check; } /** * Perform the cleanup. */ public void cleanUp() throws Exception { RestContext<EC2Client, EC2AsyncClient> context = getContext(); try { EC2Client api = context.getApi(); deleteKeyPairs(api.getKeyPairServices()); deleteSecurityGroups(api.getSecurityGroupServices()); Optional<? extends TagApi> tagApi = api.getTagApiForRegion(region); if (tagApi.isPresent()) { deleteVolumes(api.getElasticBlockStoreServices(), tagApi.get()); } else { LOG.info("No tag API, not " + (check ? "checking" : "cleaning") + " volumes"); } } catch (Exception e) { LOG.error("{}", e.getMessage()); System.exit(1); } finally { context.close(); } } /** * Delete all matching {@link KeyPair}s. */ public void deleteKeyPairs(KeyPairClient keyPairApi) throws Exception { Set<KeyPair> keys = keyPairApi.describeKeyPairsInRegion(region); Iterable<String> filtered = Iterables.filter(Iterables.transform(keys, new Function<KeyPair, String>() { @Override public String apply(@Nullable KeyPair input) { return input.getKeyName(); } }), Predicates.containsPattern("^" + regexp + "$")); LOG.info("Found {} matching KeyPairs", Iterables.size(filtered)); if (!check) { int deleted = 0; for (String name : filtered) { try { keyPairApi.deleteKeyPairInRegion(region, name); deleted++; } catch (Exception e) { if (e.getMessage() != null && e.getMessage().contains("RequestLimitExceeded")) { Thread.sleep(1000l); // Avoid triggering rate-limiter again } LOG.warn("Error deleting KeyPair '{}': {}", name, e.getMessage()); } } LOG.info("Deleted {} KeyPairs", deleted); } } /** * Delete all matching {@link SecurityGroup}s. */ public void deleteSecurityGroups(SecurityGroupClient securityGroupApi) throws Exception { Set<SecurityGroup> groups = securityGroupApi.describeSecurityGroupsInRegion(region); Iterable<String> filtered = Iterables .filter(Iterables.transform(groups, new Function<SecurityGroup, String>() { @Override public String apply(@Nullable SecurityGroup input) { return input.getName(); } }), Predicates.containsPattern("^" + regexp + "$")); LOG.info("Found {} matching SecurityGroups", Iterables.size(filtered)); if (!check) { int deleted = 0; for (String name : filtered) { try { securityGroupApi.deleteSecurityGroupInRegion(region, name); deleted++; } catch (Exception e) { if (e.getMessage() != null && e.getMessage().contains("RequestLimitExceeded")) { Thread.sleep(1000l); // Avoid triggering rate-limiter again } LOG.warn("Error deleting SecurityGroup '{}': {}", name, e.getMessage()); } } LOG.info("Deleted {} SecurityGroups", deleted); } } public void deleteVolumes(ElasticBlockStoreClient ebsApi, TagApi tagApi) throws Exception { Iterable<String> filtered = "".equals(regexp) ? getVolumesWithNoName(ebsApi, tagApi) : getVolumesMatchingName(tagApi, regexp); if (!check) { int deleted = 0; for (String id : filtered) { try { ebsApi.deleteVolumeInRegion(region, id); deleted++; } catch (Exception e) { if (e.getMessage() != null && e.getMessage().contains("RequestLimitExceeded")) { Thread.sleep(1000l); // Avoid triggering rate-limiter again } LOG.warn("Error deleting Volume '{}': {}", id, e.getMessage()); } } LOG.info("Deleted {} Volumes", deleted); } } private Iterable<String> getVolumesWithNoName(ElasticBlockStoreClient ebsApi, TagApi tagApi) { Set<String> namedVolumes = tagApi.filter(new TagFilterBuilder().volume().key("Name").build()) .transform(new Function<Tag, String>() { @Override public String apply(Tag input) { return input.getResourceId(); } }).toSet(); Set<String> allVolumes = FluentIterable.from(ebsApi.describeVolumesInRegion(region)) .transform(new Function<Volume, String>() { @Override public String apply(Volume input) { if (input.getId() == null && LOG.isTraceEnabled()) LOG.trace("No id on volume: " + input); return input.getId() != null ? input.getId() : null; } }).filter(Predicates.notNull()).toSet(); Set<String> unnamedVolumes = Sets.difference(allVolumes, namedVolumes); LOG.info("Found {} unnamed Volumes", Iterables.size(unnamedVolumes)); return unnamedVolumes; } private Iterable<String> getVolumesMatchingName(TagApi tagApi, final String name) { FluentIterable<Tag> volumeTags = tagApi.filter(new TagFilterBuilder().volume().key("Name").build()); LOG.info("Found {} named Volumes", Iterables.size(volumeTags)); FluentIterable<String> filtered = volumeTags.filter(new Predicate<Tag>() { @Override public boolean apply(Tag input) { return Predicates.containsPattern("^" + name + "$").apply(input.getValue().orNull()); } }).transform(new Function<Tag, String>() { @Override public String apply(Tag input) { return input.getResourceId(); } }); LOG.info("Found {} matching Volumes", Iterables.size(filtered)); return filtered; } /** * Create a jclouds {@link RestContext} to access the EC2 API. */ public RestContext<EC2Client, EC2AsyncClient> getContext() throws Exception { ImmutableSet<Module> modules = ImmutableSet.<Module>of(new SLF4JLoggingModule()); RestContext<EC2Client, EC2AsyncClient> context = ContextBuilder.newBuilder("aws-ec2") .credentials(identity, credential).modules(modules).build(); return context; } /** * Command-line entry point. * * See {@code README.md} for usage example. */ public static void main(String... argv) throws Exception { String regionParam = AWS_EUROPE; String regexpParam = JCLOUDS_NAME_REGEXP; boolean checkParam = Boolean.FALSE; // Set check, region and regular expression parameters from command line arguments List<String> parameters = Lists.newArrayList(argv); if (parameters.remove("check")) { checkParam = Boolean.TRUE; } if (parameters.size() > 0) regionParam = parameters.get(0); if (parameters.size() > 1) regexpParam = parameters.get(1); LOG.info("{} SecurityGroups, KeyPairs and Volumes in aws-ec2:{} matching '{}'", new Object[] { checkParam ? "Checking" : "Cleaning", regionParam, regexpParam }); // Set EC2 identity and credential from system properties String identityValue = System.getProperty(IDENTITY_PROPERTY); String credentialValue = System.getProperty(CREDENTIAL_PROPERTY); Preconditions.checkNotNull(identityValue, String.format("The %s property must be set to your EC2 access key", IDENTITY_PROPERTY)); Preconditions.checkNotNull(credentialValue, String.format("The %s property must be set to your EC2 secret key", CREDENTIAL_PROPERTY)); // Initialise and then execute the cleanUp method Ec2CleanUp cleaner = new Ec2CleanUp(regionParam, regexpParam, identityValue, credentialValue, checkParam); cleaner.cleanUp(); } }