com.nike.cerberus.operation.core.EnableConfigReplicationOperation.java Source code

Java tutorial

Introduction

Here is the source code for com.nike.cerberus.operation.core.EnableConfigReplicationOperation.java

Source

/*
 * Copyright (c) 2016 Nike, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License 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.nike.cerberus.operation.core;

import com.amazonaws.regions.Regions;
import com.amazonaws.services.identitymanagement.AmazonIdentityManagement;
import com.amazonaws.services.identitymanagement.model.AttachRolePolicyRequest;
import com.amazonaws.services.identitymanagement.model.CreatePolicyRequest;
import com.amazonaws.services.identitymanagement.model.CreatePolicyResult;
import com.amazonaws.services.identitymanagement.model.CreateRoleRequest;
import com.amazonaws.services.identitymanagement.model.CreateRoleResult;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.BucketReplicationConfiguration;
import com.amazonaws.services.s3.model.BucketVersioningConfiguration;
import com.amazonaws.services.s3.model.CreateBucketRequest;
import com.amazonaws.services.s3.model.ObjectListing;
import com.amazonaws.services.s3.model.Region;
import com.amazonaws.services.s3.model.ReplicationDestinationConfig;
import com.amazonaws.services.s3.model.ReplicationRule;
import com.amazonaws.services.s3.model.ReplicationRuleStatus;
import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.services.s3.model.SetBucketVersioningConfigurationRequest;
import com.github.mustachejava.Mustache;
import com.github.mustachejava.MustacheFactory;
import com.nike.cerberus.command.core.EnableConfigReplicationCommand;
import com.nike.cerberus.domain.EnvironmentMetadata;
import com.nike.cerberus.domain.environment.StackName;
import com.nike.cerberus.domain.template.S3AssumeRoleInput;
import com.nike.cerberus.domain.template.S3ReplicationPolicyInput;
import com.nike.cerberus.generator.ConfigGenerationException;
import com.nike.cerberus.operation.Operation;
import com.nike.cerberus.store.ConfigStore;
import com.nike.cerberus.util.UuidSupplier;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import java.io.IOException;
import java.io.StringWriter;

/**
 * Operation for enabling config replication.  This includes consul backups.
 */
public class EnableConfigReplicationOperation implements Operation<EnableConfigReplicationCommand> {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private final String replicationBucketNameTemplate = "%s-replicated-config-%s";

    private final String replicationRoleNameTemplate = "%s-cerberus-replication-role";

    private final String replicationPolicyNameTemplate = "%s-cerberus-replication-policy";

    private final String s3AssumeRoleTemplate = "templates/s3-assume-role.json.mustache";

    private final String s3ReplicationPolicyTemplate = "templates/s3-replication-policy.json.mustache";

    private final EnvironmentMetadata environmentMetadata;

    private final ConfigStore configStore;

    private final AmazonS3 s3Client;

    private final AmazonIdentityManagement iamClient;

    private final UuidSupplier uuidSupplier;

    private final MustacheFactory mustacheFactory;

    @Inject
    public EnableConfigReplicationOperation(final EnvironmentMetadata environmentMetadata,
            final ConfigStore configStore, final AmazonS3 s3Client, final AmazonIdentityManagement iamClient,
            final UuidSupplier uuidSupplier, final MustacheFactory mustacheFactory) {
        this.environmentMetadata = environmentMetadata;
        this.configStore = configStore;
        this.s3Client = s3Client;
        this.iamClient = iamClient;
        this.uuidSupplier = uuidSupplier;
        this.mustacheFactory = mustacheFactory;
    }

    @Override
    public void run(final EnableConfigReplicationCommand command) {
        final String replicationBucketName = createReplicationBucket(command);

        logger.info("Saving replication bucket name to config store.");
        configStore.storeReplicationBucketName(replicationBucketName);

        final String roleArn = createIamRoleForReplication(replicationBucketName);

        enabledReplication(roleArn, replicationBucketName);

        touchCurrentFiles();

        logger.info("Replication enabled!");
    }

    @Override
    public boolean isRunnable(final EnableConfigReplicationCommand command) {
        boolean isRunnable = true;
        final String baseStackId = configStore.getStackId(StackName.BASE);

        if (StringUtils.isBlank(baseStackId)) {
            isRunnable = false;
            logger.error("Base components must be initialized before creating the replication bucket!");
        }

        if (StringUtils.equalsIgnoreCase(environmentMetadata.getRegionName(), command.getReplicationRegion())) {
            isRunnable = false;
            logger.error("Replication region must be different than Cerberus hosted region!");
        }

        if (StringUtils.isNotBlank(configStore.getReplicationBucketName())) {
            isRunnable = false;
            logger.error("Replicaton bucket already exists, aborting...");
        }

        return isRunnable;
    }

    private String createReplicationBucket(final EnableConfigReplicationCommand command) {
        final Region originalRegion = s3Client.getRegion();
        try {
            s3Client.setRegion(
                    com.amazonaws.regions.Region.getRegion(Regions.fromName(command.getReplicationRegion())));

            // 1. Create the replication bucket.
            final String bucketName = String.format(replicationBucketNameTemplate, environmentMetadata.getName(),
                    uuidSupplier.get());

            final CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);

            logger.info("Creating the replication bucket, {}", bucketName);
            s3Client.createBucket(createBucketRequest);

            // 2. Enable versioning on the replication bucket.
            final BucketVersioningConfiguration configuration = new BucketVersioningConfiguration()
                    .withStatus("Enabled");
            final SetBucketVersioningConfigurationRequest setBucketVersioningConfigurationRequest = new SetBucketVersioningConfigurationRequest(
                    bucketName, configuration);

            logger.info("Enabling versioning on the replication bucket.");
            s3Client.setBucketVersioningConfiguration(setBucketVersioningConfigurationRequest);

            return bucketName;
        } finally {
            s3Client.setRegion(originalRegion.toAWSRegion());
        }
    }

    private String createIamRoleForReplication(final String replicationBucketName) {
        final Mustache s3AssumeRoleTemplateCompiler = mustacheFactory.compile(s3AssumeRoleTemplate);
        final Mustache s3ReplicationPolicyTemplateCompiler = mustacheFactory.compile(s3ReplicationPolicyTemplate);
        final StringWriter s3AssumeRoleWriter = new StringWriter();
        final StringWriter s3ReplicationPolicyWriter = new StringWriter();
        final S3ReplicationPolicyInput s3ReplicationPolicyInput = new S3ReplicationPolicyInput();
        s3ReplicationPolicyInput.setSourceBucket(environmentMetadata.getBucketName());
        s3ReplicationPolicyInput.setReplicationBucket(replicationBucketName);

        try {
            s3AssumeRoleTemplateCompiler.execute(s3AssumeRoleWriter, new S3AssumeRoleInput()).flush();
            s3ReplicationPolicyTemplateCompiler.execute(s3ReplicationPolicyWriter, s3ReplicationPolicyInput)
                    .flush();
        } catch (IOException e) {
            throw new ConfigGenerationException("Failed to generate the policy documents for the replication role!",
                    e);
        }

        // 1. Create the IAM role.
        final CreateRoleRequest createRoleRequest = new CreateRoleRequest();
        createRoleRequest.setRoleName(String.format(replicationRoleNameTemplate, environmentMetadata.getName()));
        createRoleRequest.setAssumeRolePolicyDocument(s3AssumeRoleWriter.toString());
        createRoleRequest.setPath("/");

        logger.info("Creating the IAM role for replication.");
        final CreateRoleResult createRoleResult = iamClient.createRole(createRoleRequest);

        // 2. Create the IAM policy.
        final CreatePolicyRequest createPolicyRequest = new CreatePolicyRequest();
        createPolicyRequest
                .setPolicyName(String.format(replicationPolicyNameTemplate, environmentMetadata.getName()));
        createPolicyRequest.setPath("/");
        createPolicyRequest.setDescription("S3 bucket replication policy for Cerberus.");
        createPolicyRequest.setPolicyDocument(s3ReplicationPolicyWriter.toString());

        logger.info("Creating the IAM policy for replication.");
        final CreatePolicyResult createPolicyResult = iamClient.createPolicy(createPolicyRequest);

        // 3. Attach the policy to the role.
        final AttachRolePolicyRequest attachRolePolicyRequest = new AttachRolePolicyRequest();
        attachRolePolicyRequest.setRoleName(createRoleResult.getRole().getRoleName());
        attachRolePolicyRequest.setPolicyArn(createPolicyResult.getPolicy().getArn());

        logger.info("Attaching the policy to the IAM role.");
        iamClient.attachRolePolicy(attachRolePolicyRequest);

        return createRoleResult.getRole().getArn();
    }

    private void enabledReplication(final String roleArn, final String replicationBucketName) {
        final BucketReplicationConfiguration bucketReplicationConfiguration = new BucketReplicationConfiguration();
        bucketReplicationConfiguration.setRoleARN(roleArn);

        final ReplicationRule replicationRule = new ReplicationRule();
        replicationRule.setStatus(ReplicationRuleStatus.Enabled);
        replicationRule.setPrefix("");
        replicationRule.setDestinationConfig(
                new ReplicationDestinationConfig().withBucketARN("arn:aws:s3:::" + replicationBucketName));

        bucketReplicationConfiguration.addRule("replication-rule", replicationRule);

        logger.info("Enabling replication configuration on the config bucket.");
        s3Client.setBucketReplicationConfiguration(environmentMetadata.getBucketName(),
                bucketReplicationConfiguration);
    }

    private void touchCurrentFiles() {
        final String bucketName = environmentMetadata.getBucketName();
        final ObjectListing objectListing = s3Client.listObjects(bucketName);

        logger.info("Touching config files that already exist so they are replicated.");
        objectListing.getObjectSummaries().forEach(os -> {
            if (!StringUtils.startsWith(os.getKey(), "consul")) {
                logger.debug("Touching {}.", os.getKey());
                final S3Object object = s3Client.getObject(bucketName, os.getKey());
                s3Client.putObject(bucketName, object.getKey(), object.getObjectContent(),
                        object.getObjectMetadata());
            }
        });
    }
}