// Copyright 2016 Twitter. 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.
// 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.twitter.heron.uploader.s3;
import java.io.File;
import java.net.URI;
import java.net.URL;
import java.util.Map;
import com.amazonaws.SdkClientException;
import com.amazonaws.services.s3.AmazonS3;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import com.twitter.heron.spi.common.Config;
import com.twitter.heron.spi.common.Key;
import com.twitter.heron.spi.uploader.UploaderException;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class S3UploaderTest {
private S3Uploader uploader;
private AmazonS3 mockS3Client;
private Config.Builder configBuilder;
private File tempFile;
@Before
public void setUp() throws Exception {
mockS3Client = mock(AmazonS3.class);
tempFile = File.createTempFile("temp-file-name", ".tmp");
tempFile.deleteOnExit();
configBuilder = Config.newBuilder()
.put(S3Context.HERON_UPLOADER_S3_BUCKET, "bucket")
.put(Key.TOPOLOGY_NAME, "test-topology")
.put(Key.TOPOLOGY_PACKAGE_FILE, "topology.tar.gz");
uploader = new S3Uploader();
uploader.initialize(configBuilder.build());
uploader.s3Client = mockS3Client;
}
@Test
public void uploadTopologyToS3CompatibleStorage() throws Exception {
Map<String, String> env = System.getenv();
String s3Url = env.get("S3_URL");
String accessKey = env.get("S3_ACCESS_KEY");
String secretKey = env.get("S3_SECRET_KEY");
if (s3Url == null || accessKey == null || secretKey == null) {
// skip test no detail provided
return;
}
Config.Builder s3Config = Config.newBuilder()
.put(S3Context.HERON_UPLOADER_S3_BUCKET, "testbucket")
.put(S3Context.HERON_UPLOADER_S3_ACCESS_KEY, accessKey)
.put(S3Context.HERON_UPLOADER_S3_SECRET_KEY, secretKey)
.put(S3Context.HERON_UPLOADER_S3_URI, s3Url)
.put(Key.TOPOLOGY_NAME, "test-topology")
.put(Key.TOPOLOGY_PACKAGE_FILE, tempFile.getAbsolutePath());
S3Uploader s3Uploader = new S3Uploader();
s3Uploader.initialize(s3Config.build());
String expectedUri = s3Url + "testbucket/test-topology/" + tempFile.getName();
assertEquals(new URI(expectedUri), s3Uploader.uploadPackage());
}
@Test
public void uploadTopologyToS3() throws Exception {
String expectedRemotePath = "test-topology/topology.tar.gz";
String expectedBucket = "bucket";
when(mockS3Client.doesObjectExist(expectedBucket, expectedRemotePath)).thenReturn(false);
when(mockS3Client.getUrl(expectedBucket, expectedRemotePath)).thenReturn(new URL("http://url"));
URI uri = uploader.uploadPackage();
verify(mockS3Client).putObject(Mockito.eq(expectedBucket),
Mockito.eq(expectedRemotePath), Mockito.any(File.class));
verify(mockS3Client).getUrl(expectedBucket, expectedRemotePath);
assertEquals(new URI("http://url"), uri);
}
@Test
public void backupPreviousVersionOnDeployForRollback() throws Exception {
String expectedRemotePath = "test-topology/topology.tar.gz";
String expectedPreviousVersionPath = "test-topology/previous_topology.tar.gz";
String expectedBucket = "bucket";
when(mockS3Client.doesObjectExist(expectedBucket, expectedRemotePath)).thenReturn(true);
when(mockS3Client.getUrl(expectedBucket, expectedRemotePath)).thenReturn(new URL("http://url"));
URI uri = uploader.uploadPackage();
verify(mockS3Client).copyObject(expectedBucket, expectedRemotePath, expectedBucket,
expectedPreviousVersionPath);
verify(mockS3Client).putObject(Mockito.eq(expectedBucket), Mockito.eq(expectedRemotePath),
Mockito.any(File.class));
verify(mockS3Client).getUrl(expectedBucket, expectedRemotePath);
assertEquals(new URI("http://url"), uri);
}
@Test(expected = UploaderException.class)
@SuppressWarnings("unchecked")
public void handlePutObjectExceptionOnUpload() throws Exception {
String expectedRemotePath = "test-topology/topology.tar.gz";
String expectedBucket = "bucket";
when(mockS3Client.doesObjectExist(expectedBucket, expectedRemotePath)).thenReturn(true);
when(mockS3Client.putObject(Mockito.eq(expectedBucket), Mockito.eq(expectedRemotePath),
Mockito.any(File.class))).thenThrow(SdkClientException.class);
uploader.uploadPackage();
}
@Test
public void restoreThePreviousVersionOnUndo() {
String expectedRemotePath = "test-topology/topology.tar.gz";
String expectedPreviousVersionPath = "test-topology/previous_topology.tar.gz";
String expectedBucket = "bucket";
when(mockS3Client.doesObjectExist(
expectedBucket, expectedPreviousVersionPath)).thenReturn(true);
uploader.undo();
verify(mockS3Client).copyObject(expectedBucket, expectedPreviousVersionPath,
expectedBucket, expectedRemotePath);
}
@Test
public void doNotRestorePreviousVersionIfItDoesNotExist() {
String expectedPreviousVersionPath = "test-topology/previous_topology.tar.gz";
String expectedBucket = "bucket";
when(mockS3Client.doesObjectExist(expectedBucket, expectedPreviousVersionPath))
.thenReturn(false);
uploader.undo();
verify(mockS3Client, never()).copyObject(Mockito.anyString(),
Mockito.anyString(), Mockito.anyString(), Mockito.anyString());
}
@Test
public void cleanUpPreviousVersionOnClose() {
String expectedPreviousVersionPath = "test-topology/previous_topology.tar.gz";
String expectedBucket = "bucket";
uploader.close();
verify(mockS3Client).deleteObject(expectedBucket, expectedPreviousVersionPath);
}
@Test
public void PrefixUploadPathWithSpecifiedPrefix() throws Exception {
configBuilder.put(S3Context.HERON_UPLOADER_S3_PATH_PREFIX, "path/prefix");
uploader.initialize(configBuilder.build());
uploader.s3Client = mockS3Client;
String expectedRemotePath = "path/prefix/test-topology/topology.tar.gz";
String expectedBucket = "bucket";
when(mockS3Client.getUrl(expectedBucket, expectedRemotePath)).thenReturn(new URL("http://url"));
uploader.uploadPackage();
verify(mockS3Client).putObject(Mockito.eq("bucket"),
Mockito.eq(expectedRemotePath), Mockito.any(File.class));
}
}