com.amazonaws.eclipse.elasticbeanstalk.git.AWSGitPushCommand.java Source code

Java tutorial

Introduction

Here is the source code for com.amazonaws.eclipse.elasticbeanstalk.git.AWSGitPushCommand.java

Source

/*
 * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */
package com.amazonaws.eclipse.elasticbeanstalk.git;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import org.apache.commons.io.IOUtils;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.PushCommand;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryBuilder;
import org.eclipse.jgit.transport.PushResult;
import org.eclipse.jgit.util.FileUtils;

import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.eclipse.core.regions.Region;
import com.amazonaws.eclipse.core.regions.RegionUtils;
import com.amazonaws.eclipse.elasticbeanstalk.ElasticBeanstalkPlugin;
import com.amazonaws.eclipse.elasticbeanstalk.Environment;

public class AWSGitPushCommand {

    private final File repoLocation;
    private final File archiveFile;

    private final Environment environment;
    private boolean skipEnvironmentDeployment;

    private final String accessKey;
    private final String secretKey;

    public AWSGitPushCommand(File repoLocation, File archiveFile, Environment environment,
            AWSCredentials credentials) {
        this.repoLocation = repoLocation;
        this.archiveFile = archiveFile;
        this.environment = environment;
        this.accessKey = credentials.getAWSAccessKeyId();
        this.secretKey = credentials.getAWSSecretKey();
    }

    private static final Logger log = Logger.getLogger(AWSGitPushCommand.class.getCanonicalName());

    public void execute() throws CoreException {
        Iterable<PushResult> pushResults = null;
        try {
            Repository repository = initializeRepository();

            // Add the new files
            clearOutRepository(repository);
            extractZipFile(archiveFile, repoLocation);
            commitChanges(repository, "Incremental Deployment: " + new Date().toString());

            // Push to AWS
            String remoteUrl = getRemoteUrl();

            if (log.isLoggable(Level.FINE))
                log.fine("Pushing to: " + remoteUrl);
            PushCommand pushCommand = new Git(repository).push().setRemote(remoteUrl).setForce(true).add("master");
            // TODO: we could use a ProgressMonitor here for reporting status back to the UI
            pushResults = pushCommand.call();
        } catch (Throwable t) {
            throwCoreException(null, t);
        }

        for (PushResult pushResult : pushResults) {
            String messages = pushResult.getMessages();
            if (messages != null && messages.trim().length() > 0) {
                throwCoreException(messages, null);
            }
        }
    }

    /**
     * Use this method to configure this request to only create a new
     * application version, and not automatically deploy it to the specified
     * environment.
     *
     * @param skipEnvironmentDeployment
     *            True if this Git Push should only create a new application
     *            version, and not automatically deploy it to the specified
     *            environment.
     */
    public void skipEnvironmentDeployment(boolean skipEnvironmentDeployment) {
        this.skipEnvironmentDeployment = skipEnvironmentDeployment;
    }

    private void throwCoreException(String customMessage, Throwable t) throws CoreException {
        if (customMessage != null)
            customMessage = ": " + customMessage;
        else
            customMessage = "";

        String errorMessage = "Unable to update environment with an incremental deployment" + customMessage
                + "\nIf you continue having problems with incremental deployments, try turning off incremental deployments in the server configuration editor.";
        throw new CoreException(new Status(IStatus.ERROR, ElasticBeanstalkPlugin.PLUGIN_ID, errorMessage, t));
    }

    private String getRemoteUrl() throws Exception {
        AWSElasticBeanstalkGitPushRequest request = new AWSElasticBeanstalkGitPushRequest();
        request.setHost(getGitPushHost());

        Region region = RegionUtils.getRegionByEndpoint(environment.getRegionEndpoint());
        request.setRegion(region.getId());
        request.setApplication(environment.getApplicationName());

        if (!skipEnvironmentDeployment) {
            request.setEnvironment(environment.getEnvironmentName());
        }
        AWSGitPushAuth auth = new AWSGitPushAuth(request);
        URI uri = auth.deriveRemote(accessKey, secretKey);

        return uri.toString();
    }

    private String getGitPushHost() {
        // TODO: it would be better to get this from the endpoints file at some point
        String regionEndpoint = environment.getRegionEndpoint();
        if (regionEndpoint.startsWith("https://")) {
            regionEndpoint = "git." + regionEndpoint.substring("https://".length());
        } else if (regionEndpoint.startsWith("http://")) {
            regionEndpoint = "git." + regionEndpoint.substring("http://".length());
        } else {
            regionEndpoint = "git." + regionEndpoint;
        }

        if (regionEndpoint.endsWith("/")) {
            regionEndpoint = regionEndpoint.substring(0, regionEndpoint.length() - 1);
        }

        return regionEndpoint;
    }

    private void clearOutRepository(Repository repository) throws IOException {
        // Delete all the old files before copying the new files
        final File gitMetadataDirectory = repository.getIndexFile().getParentFile();

        FileFilter fileFilter = new FileFilter() {
            public boolean accept(final File file) {
                if (file.equals(gitMetadataDirectory))
                    return false;
                if (!file.getParentFile().equals(repoLocation))
                    return false;
                return true;
            }
        };

        for (File f : repoLocation.listFiles(fileFilter)) {
            FileUtils.delete(f, FileUtils.RECURSIVE);
        }
    }

    private Repository initializeRepository() throws IOException {
        if (repoLocation == null) {
            throw new RuntimeException("No repository location specified");
        }

        if ((!repoLocation.exists() && !repoLocation.mkdirs()) || !repoLocation.isDirectory()) {
            throw new RuntimeException("Unable to initialize Git repository from location: " + repoLocation);
        }

        Repository repository = new RepositoryBuilder().setWorkTree(repoLocation).build();
        if (!repository.getObjectDatabase().exists()) {
            repository.create();
        }

        return repository;
    }

    // TODO: We could use a better util for this
    private void extractZipFile(File zipFile, File destination) throws IOException {
        int BUFFER = 2048;

        FileInputStream fis = new FileInputStream(zipFile);
        ZipInputStream zis = new ZipInputStream(new BufferedInputStream(fis));
        BufferedOutputStream dest = null;
        try {
            ZipEntry entry;
            while ((entry = zis.getNextEntry()) != null) {
                int count;
                byte data[] = new byte[BUFFER];
                // write the files to the disk

                if (entry.isDirectory())
                    continue;

                File entryFile = new File(destination, entry.getName());
                if (!entryFile.getParentFile().exists()) {
                    entryFile.getParentFile().mkdirs();
                }
                FileOutputStream fos = new FileOutputStream(entryFile);
                dest = new BufferedOutputStream(fos, BUFFER);
                while ((count = zis.read(data, 0, BUFFER)) != -1) {
                    dest.write(data, 0, count);
                }
                dest.flush();
            }
        } finally {
            IOUtils.closeQuietly(fis);
            IOUtils.closeQuietly(zis);
            IOUtils.closeQuietly(dest);
        }
    }

    private void commitChanges(Repository repository, String message) throws GitAPIException, IOException {
        Git git = new Git(repository);
        // Add once (without the update flag) to add new and modified files
        git.add().addFilepattern(".").call();
        // Then again with the update flag to add deleted and modified files
        git.add().addFilepattern(".").setUpdate(true).call();

        git.commit().setMessage(message).call();
    }

}