Skip to content
This repository has been archived by the owner on Jan 12, 2024. It is now read-only.

Commit

Permalink
* add command to sync configs between regions (#120)
Browse files Browse the repository at this point in the history
* add command to sync configs between regions
  • Loading branch information
mayitbeegh authored Mar 27, 2018
1 parent c253bc2 commit af7381e
Show file tree
Hide file tree
Showing 10 changed files with 193 additions and 7 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@

group=com.nike
artifactId=cerberus-lifecycle-cli
version=4.4.0
version=4.5.0
4 changes: 3 additions & 1 deletion src/main/java/com/nike/cerberus/cli/CerberusRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@
import com.nike.cerberus.command.composite.PrintAllStackInformationCommand;
import com.nike.cerberus.command.certificates.RotateCertificatesCommand;
import com.nike.cerberus.command.core.InitializeEnvironmentCommand;
import com.nike.cerberus.command.core.UpdateAllStackTagsCommand;
import com.nike.cerberus.command.core.SyncConfigCommand;
import com.nike.cerberus.command.composite.UpdateAllStackTagsCommand;
import com.nike.cerberus.command.rds.CleanUpRdsSnapshotsCommand;
import com.nike.cerberus.command.rds.CopyRdsSnapshotsCommand;
import com.nike.cerberus.command.rds.CreateDatabaseCommand;
Expand Down Expand Up @@ -235,6 +236,7 @@ private void registerAllCommands() {
registerCommand(new EnableAuditLoggingForExistingEnvironmentCommand());
registerCommand(new UpdateStackTagsCommand());
registerCommand(new UpdateAllStackTagsCommand());
registerCommand(new SyncConfigCommand());
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import com.nike.cerberus.command.certificates.GenerateAndRotateCertificatesCommand;
import com.nike.cerberus.command.certificates.RotateCertificatesCommand;
import com.nike.cerberus.command.core.InitializeEnvironmentCommand;
import com.nike.cerberus.command.core.UpdateAllStackTagsCommand;
import com.nike.cerberus.command.composite.UpdateAllStackTagsCommand;
import com.nike.cerberus.command.rds.CreateDatabaseCommand;
import com.nike.cerberus.command.core.CreateEdgeDomainRecordCommand;
import com.nike.cerberus.command.core.CreateLoadBalancerCommand;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

package com.nike.cerberus.command.core;
package com.nike.cerberus.command.composite;

import com.beust.jcommander.Parameters;
import com.beust.jcommander.ParametersDelegate;
Expand All @@ -23,7 +23,7 @@
import com.nike.cerberus.operation.Operation;
import com.nike.cerberus.operation.composite.UpdateAllStackTagsOperation;

import static com.nike.cerberus.command.core.UpdateAllStackTagsCommand.COMMAND_NAME;
import static com.nike.cerberus.command.composite.UpdateAllStackTagsCommand.COMMAND_NAME;


/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright (c) 2018 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.command.core;

import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
import com.nike.cerberus.command.Command;
import com.nike.cerberus.operation.Operation;
import com.nike.cerberus.operation.core.SyncConfigOperation;

import static com.nike.cerberus.command.core.UpdateStackCommand.COMMAND_NAME;


/**
* Command for syncing configs between regions.
*/
@Parameters(commandNames = COMMAND_NAME, commandDescription = "Syncs configs between regions. Recursively copies all files from the source region's config bucket (as defined in --region) to the destination region's config bucket.")
public class SyncConfigCommand implements Command {

public static final String COMMAND_NAME = "sync-config";
public static final String DESTINATION_REGION_LONG_ARG = "--destination-region";
public static final String DRY_LONG_ARG = "--dry";
public static final String ALL_LONG_ARG = "--all";

@Parameter(names = {DESTINATION_REGION_LONG_ARG}, description = "The destination region")
private String destinationRegionName;

@Parameter(names = {DRY_LONG_ARG}, description = "Displays destination buckets and files to be copied over without actually running them")
private boolean dryrun;

@Parameter(names = {ALL_LONG_ARG}, description = "Sync up all regions as defines in the environment data")
private boolean all;

public String getDestinationRegionName() {
return destinationRegionName;
}

public boolean isDryrun() {
return dryrun;
}

public boolean isAll() {
return all;
}

@Override
public String getCommandName() {
return COMMAND_NAME;
}

@Override
public Class<? extends Operation<?>> getOperationClass() {
return SyncConfigOperation.class;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

package com.nike.cerberus.operation.composite;

import com.nike.cerberus.command.core.UpdateAllStackTagsCommand;
import com.nike.cerberus.command.composite.UpdateAllStackTagsCommand;
import com.nike.cerberus.command.core.UpdateStackTagsCommand;
import com.nike.cerberus.domain.environment.Stack;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright (c) 2018 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.nike.cerberus.command.core.SyncConfigCommand;
import com.nike.cerberus.operation.Operation;
import com.nike.cerberus.store.ConfigStore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import javax.inject.Named;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import static com.nike.cerberus.module.CerberusModule.CONFIG_REGION;

/**
* Operation for syncing configs between regions.
*/
public class SyncConfigOperation implements Operation<SyncConfigCommand> {


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

private final ConfigStore configStore;

private Regions configRegion;

@Inject
public SyncConfigOperation(ConfigStore configStore, @Named(CONFIG_REGION) String configRegion) {
this.configStore = configStore;
this.configRegion = Regions.fromName(configRegion);
}

@Override
public void run(SyncConfigCommand command) {
List<Regions> destinationRegions = command.isAll() ? configStore.getSyncDestinationRegions() : Arrays.asList(Regions.fromName(command.getDestinationRegionName()));

if (command.isDryrun()){
logger.info("Destination buckets: {}", destinationRegions.stream()
.map(region -> configStore.getConfigBucketForRegion(region)).collect(Collectors.joining(", ")));
logger.info("Files to be copied over:");
configStore.listKeys().forEach(key -> logger.info(key));
} else {
for (Regions region: destinationRegions) {
logger.info("Destination bucket: {}", configStore.getConfigBucketForRegion(region));
configStore.sync(region);
}
}

}

@Override
public boolean isRunnable(SyncConfigCommand command) {
boolean isRunnable = true;
if (!command.isAll() && configRegion.equals(Regions.fromName(command.getDestinationRegionName()))){
isRunnable = false;
logger.error("The source and destination region must be different.");
}
return isRunnable;
}
}
6 changes: 6 additions & 0 deletions src/main/java/com/nike/cerberus/service/S3StoreService.java
Original file line number Diff line number Diff line change
Expand Up @@ -170,4 +170,10 @@ public Optional<String> getHash(String path) {
logger.debug("The hash for {} is {}", path, objectMetadata.getETag().toString());
return Optional.ofNullable(objectMetadata.getETag());
}

@Override
public void copyFrom(String sourceBucketName, String sourceS3key) {
logger.info("Copying {}", sourceS3key);
s3Client.copyObject(sourceBucketName, sourceS3key, s3Bucket, sourceS3key);
}
}
2 changes: 2 additions & 0 deletions src/main/java/com/nike/cerberus/service/StoreService.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,6 @@ public interface StoreService {
void deleteAllKeysOnPartialPath(String path);

Optional<String> getHash(String path);

void copyFrom(String sourceIdentifier, String path);
}
29 changes: 28 additions & 1 deletion src/main/java/com/nike/cerberus/store/ConfigStore.java
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,12 @@ public Regions getPrimaryRegion() {
return getDecryptedEnvironmentData().getPrimaryRegion();
}

public List<Regions> getSyncDestinationRegions() {
List<Regions> regions = getDecryptedEnvironmentData().getConfigRegions();
regions.remove(configRegion);
return regions;
}

public List<Regions> getConfigEnabledRegions() {
return getDecryptedEnvironmentData().getConfigRegions();
}
Expand Down Expand Up @@ -757,7 +763,7 @@ public boolean isConfigSynchronized(){
StoreService storeService = getStoreServiceForRegion(currentRegion, environmentData);

// null hash values are treated as if they're equal
Map<String, String> s3KeyToHashValueMap = Maps.uniqueIndex(storeService.getKeysInPartialPath(""), s -> storeService.getHash(s).toString());
Map<String, String> s3KeyToHashValueMap = Maps.asMap(storeService.getKeysInPartialPath(""), key -> storeService.getHash(key).toString());
if (firstRegion == null){
firstS3KeyToHashValueMap = s3KeyToHashValueMap;
firstRegion = currentRegion;
Expand All @@ -769,4 +775,25 @@ public boolean isConfigSynchronized(){

return result;
}

/***
* Copy all files from config region's config bucket to destination region's config bucket
* @param destinationRegion Region to copy files to
*/
public void sync(Regions destinationRegion) {
EnvironmentData decryptedEnvironmentData = getDecryptedEnvironmentData();
StoreService destinationStoreService = getStoreServiceForRegion(destinationRegion, decryptedEnvironmentData);
String sourceBucket = findConfigBucketInSuppliedConfigRegion();
listKeys().forEach(k -> destinationStoreService.copyFrom(sourceBucket, k));
}

/***
* List every key in the config bucket in the config region
* @return Set of keys
*/
public Set<String> listKeys() {
StoreService storeService = getStoreServiceForRegion(configRegion, getDecryptedEnvironmentData());
Set<String> keys = storeService.getKeysInPartialPath("");
return keys;
}
}

0 comments on commit af7381e

Please sign in to comment.