grkvlt.Ec2CleanUp.java Source code

Java tutorial

Introduction

Here is the source code for grkvlt.Ec2CleanUp.java

Source

/*
 * 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();
    }

}