/*
* Copyright (c) 2015 Google, 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.cloudera.director.google.compute;
import static com.cloudera.director.google.compute.GoogleComputeInstanceTemplateConfigurationProperty.BOOT_DISK_TYPE;
import static com.cloudera.director.google.compute.GoogleComputeInstanceTemplateConfigurationProperty.DATA_DISK_COUNT;
import static com.cloudera.director.google.compute.GoogleComputeInstanceTemplateConfigurationProperty.DATA_DISK_SIZE_GB;
import static com.cloudera.director.google.compute.GoogleComputeInstanceTemplateConfigurationProperty.DATA_DISK_TYPE;
import static com.cloudera.director.google.compute.GoogleComputeInstanceTemplateConfigurationProperty.IMAGE;
import static com.cloudera.director.google.compute.GoogleComputeInstanceTemplateConfigurationProperty.NETWORK_NAME;
import static com.cloudera.director.google.compute.GoogleComputeInstanceTemplateConfigurationProperty.TYPE;
import static com.cloudera.director.google.compute.GoogleComputeInstanceTemplateConfigurationProperty.ZONE;
import static com.cloudera.director.google.compute.GoogleComputeProviderConfigurationProperty.REGION;
import static com.cloudera.director.spi.v1.model.InstanceTemplate.InstanceTemplateConfigurationPropertyToken.INSTANCE_NAME_PREFIX;
import static junit.framework.Assert.fail;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.cloudera.director.google.TestUtils;
import com.cloudera.director.google.compute.util.ComputeUrls;
import com.cloudera.director.google.internal.GoogleCredentials;
import com.cloudera.director.google.shaded.com.google.api.client.googleapis.json.GoogleJsonResponseException;
import com.cloudera.director.google.shaded.com.google.api.client.googleapis.testing.json.GoogleJsonResponseExceptionFactoryTesting;
import com.cloudera.director.google.shaded.com.google.api.client.testing.json.MockJsonFactory;
import com.cloudera.director.google.shaded.com.google.api.services.compute.Compute;
import com.cloudera.director.google.shaded.com.google.api.services.compute.model.AttachedDisk;
import com.cloudera.director.google.shaded.com.google.api.services.compute.model.AttachedDiskInitializeParams;
import com.cloudera.director.google.shaded.com.google.api.services.compute.model.Disk;
import com.cloudera.director.google.shaded.com.google.api.services.compute.model.Instance;
import com.cloudera.director.google.shaded.com.google.api.services.compute.model.NetworkInterface;
import com.cloudera.director.google.shaded.com.google.api.services.compute.model.Operation;
import com.cloudera.director.spi.v1.model.Configured;
import com.cloudera.director.spi.v1.model.InstanceState;
import com.cloudera.director.spi.v1.model.InstanceStatus;
import com.cloudera.director.spi.v1.model.exception.PluginExceptionCondition;
import com.cloudera.director.spi.v1.model.exception.PluginExceptionDetails;
import com.cloudera.director.spi.v1.model.exception.UnrecoverableProviderException;
import com.cloudera.director.spi.v1.model.util.DefaultLocalizationContext;
import com.cloudera.director.spi.v1.model.util.SimpleConfiguration;
import com.google.common.collect.Lists;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.mockito.stubbing.OngoingStubbing;
import java.io.IOException;
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Deque;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.SortedSet;
import java.util.UUID;
import java.util.logging.Logger;
/**
* Tests {@link GoogleComputeProvider}.
*/
public class GoogleComputeProviderTest {
private static final Logger LOG = Logger.getLogger(GoogleComputeProviderTest.class.getName());
private static final DefaultLocalizationContext DEFAULT_LOCALIZATION_CONTEXT =
new DefaultLocalizationContext(Locale.getDefault(), "");
private static final String PROJECT_ID = "some-project";
private static final String REGION_NAME_1 = "us-central1";
private static final String ZONE_NAME = "us-central1-a";
private static final String IMAGE_ALIAS_RHEL = "rhel6";
private static final String IMAGE_PROJECT_ID = "rhel-cloud";
private static final String IMAGE_NAME = "rhel-6-v20150526";
private static final String IMAGE_URL_UBUNTU =
"https://www.googleapis.com/compute/v1/projects/ubuntu-os-cloud/global/images/ubuntu-1404-trusty-v20150805";
private static final String MACHINE_TYPE_NAME = "n1-standard-1";
private static final String NETWORK_NAME_VALUE = "some-network";
private static final String INVALID_INSTANCE_NAME_PREFIX = "-starts-with-dash";
private GoogleComputeProvider computeProvider;
private GoogleCredentials credentials;
private Compute compute;
@Before
public void setUp() throws IOException {
credentials = mock(GoogleCredentials.class);
compute = mock(Compute.class);
when(credentials.getCompute()).thenReturn(compute);
when(credentials.getProjectId()).thenReturn(PROJECT_ID);
Compute.Zones.List computeZonesList = mockComputeToZonesList();
// We don't need to actually return a list of zones, we just need to not throw a 404.
when(computeZonesList.execute()).thenReturn(null);
// Prepare configuration for Google compute provider.
Map<String, String> computeConfig = new HashMap<String, String>();
computeConfig.put(REGION.unwrap().getConfigKey(), REGION_NAME_1);
Configured resourceProviderConfiguration = new SimpleConfiguration(computeConfig);
// Create the Google compute provider.
computeProvider = new GoogleComputeProvider(resourceProviderConfiguration, credentials,
TestUtils.buildApplicationPropertiesConfig(), TestUtils.buildGoogleConfig(), DEFAULT_LOCALIZATION_CONTEXT);
}
private Compute.Zones.List mockComputeToZonesList() throws IOException {
Compute.Zones computeZones = mock(Compute.Zones.class);
Compute.Zones.List computeZonesList = mock(Compute.Zones.List.class);
when(compute.zones()).thenReturn(computeZones);
when(computeZones.list(PROJECT_ID)).thenReturn(computeZonesList);
return computeZonesList;
}
private Compute.Instances mockComputeToInstances() {
Compute.Instances computeInstances = mock(Compute.Instances.class);
when(compute.instances()).thenReturn(computeInstances);
return computeInstances;
}
private Compute.Instances.Insert mockComputeInstancesInsert(Compute.Instances computeInstances) throws IOException {
Compute.Instances.Insert computeInstancesInsert = mock(Compute.Instances.Insert.class);
when(computeInstances.insert(
eq(PROJECT_ID), eq(ZONE_NAME), any(Instance.class))).thenReturn(computeInstancesInsert);
return computeInstancesInsert;
}
private Compute.Instances.Delete mockComputeInstancesDelete(
Compute.Instances computeInstances, String instanceName) throws IOException {
Compute.Instances.Delete computeInstancesDelete = mock(Compute.Instances.Delete.class);
when(computeInstances.delete(eq(PROJECT_ID), eq(ZONE_NAME), eq(instanceName))).thenReturn(computeInstancesDelete);
return computeInstancesDelete;
}
private Compute.Instances.Get mockComputeInstancesGet(
Compute.Instances computeInstances, String instanceName) throws IOException {
Compute.Instances.Get computeInstancesGet = mock(Compute.Instances.Get.class);
when(computeInstances.get(PROJECT_ID, ZONE_NAME, instanceName)).thenReturn(computeInstancesGet);
return computeInstancesGet;
}
private Compute.ZoneOperations mockComputeToZoneOperations() {
Compute.ZoneOperations computeZoneOperations = mock(Compute.ZoneOperations.class);
when(compute.zoneOperations()).thenReturn(computeZoneOperations);
return computeZoneOperations;
}
private Compute.Disks mockComputeToDisks() {
Compute.Disks computeDisks = mock(Compute.Disks.class);
when(compute.disks()).thenReturn(computeDisks);
return computeDisks;
}
private Compute.Disks.Insert mockComputeDisksInsert(Compute.Disks computeDisks) throws IOException {
Compute.Disks.Insert computeDisksInsert = mock(Compute.Disks.Insert.class);
when(computeDisks.insert(
eq(PROJECT_ID), eq(ZONE_NAME), any(Disk.class))).thenReturn(computeDisksInsert);
return computeDisksInsert;
}
private Compute.Disks.Get mockComputeDisksGet(
Compute.Disks computeDisks, String diskName) throws IOException {
Compute.Disks.Get computeDisksGet = mock(Compute.Disks.Get.class);
when(computeDisks.get(
eq(PROJECT_ID), eq(ZONE_NAME), eq(diskName))).thenReturn(computeDisksGet);
return computeDisksGet;
}
private Compute.Disks.Delete mockComputeDisksDelete(
Compute.Disks computeDisks, String diskName) throws IOException {
Compute.Disks.Delete computeDisksDelete = mock(Compute.Disks.Delete.class);
when(computeDisks.delete(
eq(PROJECT_ID), eq(ZONE_NAME), eq(diskName))).thenReturn(computeDisksDelete);
return computeDisksDelete;
}
@Test
public void testAllocate_LocalSSD() throws InterruptedException, IOException {
// Prepare configuration for resource template.
Map<String, String> templateConfig = new HashMap<String, String>();
templateConfig.put(IMAGE.unwrap().getConfigKey(), IMAGE_ALIAS_RHEL);
templateConfig.put(TYPE.unwrap().getConfigKey(), MACHINE_TYPE_NAME);
templateConfig.put(NETWORK_NAME.unwrap().getConfigKey(), NETWORK_NAME_VALUE);
templateConfig.put(ZONE.unwrap().getConfigKey(), ZONE_NAME);
// Create the resource template.
GoogleComputeInstanceTemplate template = computeProvider.createResourceTemplate("template-1",
new SimpleConfiguration(templateConfig), new HashMap<String, String>());
// Configure stub for successful instance insertion operation.
String instanceName = UUID.randomUUID().toString();
String decoratedInstanceName = INSTANCE_NAME_PREFIX.unwrap().getDefaultValue() + "-" + instanceName;
String instanceUrl = TestUtils.buildComputeInstanceUrl(PROJECT_ID, ZONE_NAME, decoratedInstanceName);
Compute.Instances computeInstances = mockComputeToInstances();
Compute.Instances.Insert computeInstancesInsert = mockComputeInstancesInsert(computeInstances);
Operation vmCreationOperation = buildInitialOperation(ZONE_NAME, "insert", instanceUrl);
when(computeInstancesInsert.execute()).thenReturn(vmCreationOperation);
Compute.ZoneOperations computeZoneOperations = mockComputeToZoneOperations();
Compute.ZoneOperations.Get computeZoneOperationsGet = mock(Compute.ZoneOperations.Get.class);
when(computeZoneOperations.get(PROJECT_ID, ZONE_NAME,
vmCreationOperation.getName())).thenReturn(computeZoneOperationsGet);
when(computeZoneOperationsGet.execute()).then(
new OperationAnswer(vmCreationOperation, new String[]{"PENDING", "RUNNING", "DONE"}));
computeProvider.allocate(template, Lists.newArrayList(instanceName), 1);
// Verify instance insertion call was made.
ArgumentCaptor<Instance> argumentCaptor = ArgumentCaptor.forClass(Instance.class);
verify(computeInstances).insert(eq(PROJECT_ID), eq(ZONE_NAME), argumentCaptor.capture());
Instance insertedInstance = argumentCaptor.getValue();
// Verify instance name and metadata.
assertThat(insertedInstance.getName()).isEqualTo(decoratedInstanceName);
assertThat(insertedInstance.getMetadata().getItems()).isEqualTo(Lists.newArrayList());
List<AttachedDisk> attachedDiskList = insertedInstance.getDisks();
assertThat(attachedDiskList.size()).isEqualTo(3);
// Verify boot disk.
verifyAttachedDiskAttributes(attachedDiskList.get(0), true, true,
ComputeUrls.buildDiskTypeUrl(PROJECT_ID, ZONE_NAME, "SSD"), 60L,
TestUtils.buildImageUrl(IMAGE_PROJECT_ID, IMAGE_NAME), null, null, null);
// Verify data disk.
verifyAttachedDiskAttributes(attachedDiskList.get(1), null, true,
ComputeUrls.buildDiskTypeUrl(PROJECT_ID, ZONE_NAME, "LocalSSD"), null, null, "SCSI", "SCRATCH", null);
// Verify data disk.
verifyAttachedDiskAttributes(attachedDiskList.get(2), null, true,
ComputeUrls.buildDiskTypeUrl(PROJECT_ID, ZONE_NAME, "LocalSSD"), null, null, "SCSI", "SCRATCH", null);
}
@Test
public void testAllocate_LocalSSD_StandardBoot() throws InterruptedException, IOException {
// Prepare configuration for resource template.
Map<String, String> templateConfig = new HashMap<String, String>();
templateConfig.put(BOOT_DISK_TYPE.unwrap().getConfigKey(), "Standard");
templateConfig.put(IMAGE.unwrap().getConfigKey(), IMAGE_ALIAS_RHEL);
templateConfig.put(TYPE.unwrap().getConfigKey(), MACHINE_TYPE_NAME);
templateConfig.put(NETWORK_NAME.unwrap().getConfigKey(), NETWORK_NAME_VALUE);
templateConfig.put(ZONE.unwrap().getConfigKey(), ZONE_NAME);
// Create the resource template.
GoogleComputeInstanceTemplate template = computeProvider.createResourceTemplate("template-1",
new SimpleConfiguration(templateConfig), new HashMap<String, String>());
// Configure stub for successful instance insertion operation.
String instanceName = UUID.randomUUID().toString();
String decoratedInstanceName = INSTANCE_NAME_PREFIX.unwrap().getDefaultValue() + "-" + instanceName;
String instanceUrl = TestUtils.buildComputeInstanceUrl(PROJECT_ID, ZONE_NAME, decoratedInstanceName);
Compute.Instances computeInstances = mockComputeToInstances();
Compute.Instances.Insert computeInstancesInsert = mockComputeInstancesInsert(computeInstances);
Operation vmCreationOperation = buildInitialOperation(ZONE_NAME, "insert", instanceUrl);
when(computeInstancesInsert.execute()).thenReturn(vmCreationOperation);
Compute.ZoneOperations computeZoneOperations = mockComputeToZoneOperations();
Compute.ZoneOperations.Get computeZoneOperationsGet = mock(Compute.ZoneOperations.Get.class);
when(computeZoneOperations.get(PROJECT_ID, ZONE_NAME,
vmCreationOperation.getName())).thenReturn(computeZoneOperationsGet);
when(computeZoneOperationsGet.execute()).then(
new OperationAnswer(vmCreationOperation, new String[]{"PENDING", "RUNNING", "DONE"}));
computeProvider.allocate(template, Lists.newArrayList(instanceName), 1);
// Verify instance insertion call was made.
ArgumentCaptor<Instance> argumentCaptor = ArgumentCaptor.forClass(Instance.class);
verify(computeInstances).insert(eq(PROJECT_ID), eq(ZONE_NAME), argumentCaptor.capture());
Instance insertedInstance = argumentCaptor.getValue();
// Verify instance name and metadata.
assertThat(insertedInstance.getName()).isEqualTo(decoratedInstanceName);
assertThat(insertedInstance.getMetadata().getItems()).isEqualTo(Lists.newArrayList());
List<AttachedDisk> attachedDiskList = insertedInstance.getDisks();
assertThat(attachedDiskList.size()).isEqualTo(3);
// Verify boot disk.
verifyAttachedDiskAttributes(attachedDiskList.get(0), true, true,
ComputeUrls.buildDiskTypeUrl(PROJECT_ID, ZONE_NAME, "Standard"), 60L,
TestUtils.buildImageUrl(IMAGE_PROJECT_ID, IMAGE_NAME), null, null, null);
// Verify data disk.
verifyAttachedDiskAttributes(attachedDiskList.get(1), null, true,
ComputeUrls.buildDiskTypeUrl(PROJECT_ID, ZONE_NAME, "LocalSSD"), null, null, "SCSI", "SCRATCH", null);
// Verify data disk.
verifyAttachedDiskAttributes(attachedDiskList.get(2), null, true,
ComputeUrls.buildDiskTypeUrl(PROJECT_ID, ZONE_NAME, "LocalSSD"), null, null, "SCSI", "SCRATCH", null);
}
@Test
public void testAllocate_LocalSSD_FullImageUrl() throws InterruptedException, IOException {
// Prepare configuration for resource template.
Map<String, String> templateConfig = new HashMap<String, String>();
templateConfig.put(IMAGE.unwrap().getConfigKey(), IMAGE_URL_UBUNTU);
templateConfig.put(TYPE.unwrap().getConfigKey(), MACHINE_TYPE_NAME);
templateConfig.put(NETWORK_NAME.unwrap().getConfigKey(), NETWORK_NAME_VALUE);
templateConfig.put(ZONE.unwrap().getConfigKey(), ZONE_NAME);
// Create the resource template.
GoogleComputeInstanceTemplate template = computeProvider.createResourceTemplate("template-1",
new SimpleConfiguration(templateConfig), new HashMap<String, String>());
// Configure stub for successful instance insertion operation.
String instanceName = UUID.randomUUID().toString();
String decoratedInstanceName = INSTANCE_NAME_PREFIX.unwrap().getDefaultValue() + "-" + instanceName;
String instanceUrl = TestUtils.buildComputeInstanceUrl(PROJECT_ID, ZONE_NAME, decoratedInstanceName);
Compute.Instances computeInstances = mockComputeToInstances();
Compute.Instances.Insert computeInstancesInsert = mockComputeInstancesInsert(computeInstances);
Operation vmCreationOperation = buildInitialOperation(ZONE_NAME, "insert", instanceUrl);
when(computeInstancesInsert.execute()).thenReturn(vmCreationOperation);
Compute.ZoneOperations computeZoneOperations = mockComputeToZoneOperations();
Compute.ZoneOperations.Get computeZoneOperationsGet = mock(Compute.ZoneOperations.Get.class);
when(computeZoneOperations.get(PROJECT_ID, ZONE_NAME,
vmCreationOperation.getName())).thenReturn(computeZoneOperationsGet);
when(computeZoneOperationsGet.execute()).then(
new OperationAnswer(vmCreationOperation, new String[]{"PENDING", "RUNNING", "DONE"}));
computeProvider.allocate(template, Lists.newArrayList(instanceName), 1);
// Verify instance insertion call was made.
ArgumentCaptor<Instance> argumentCaptor = ArgumentCaptor.forClass(Instance.class);
verify(computeInstances).insert(eq(PROJECT_ID), eq(ZONE_NAME), argumentCaptor.capture());
Instance insertedInstance = argumentCaptor.getValue();
// Verify instance name and metadata.
assertThat(insertedInstance.getName()).isEqualTo(decoratedInstanceName);
assertThat(insertedInstance.getMetadata().getItems()).isEqualTo(Lists.newArrayList());
List<AttachedDisk> attachedDiskList = insertedInstance.getDisks();
assertThat(attachedDiskList.size()).isEqualTo(3);
// Verify boot disk.
verifyAttachedDiskAttributes(attachedDiskList.get(0), true, true,
ComputeUrls.buildDiskTypeUrl(PROJECT_ID, ZONE_NAME, "SSD"), 60L, IMAGE_URL_UBUNTU, null, null, null);
// Verify data disk.
verifyAttachedDiskAttributes(attachedDiskList.get(1), null, true,
ComputeUrls.buildDiskTypeUrl(PROJECT_ID, ZONE_NAME, "LocalSSD"), null, null, "SCSI", "SCRATCH", null);
// Verify data disk.
verifyAttachedDiskAttributes(attachedDiskList.get(2), null, true,
ComputeUrls.buildDiskTypeUrl(PROJECT_ID, ZONE_NAME, "LocalSSD"), null, null, "SCSI", "SCRATCH", null);
}
@Test
public void testAllocate_LocalSSD_CreationFails_BelowMinCount() throws InterruptedException, IOException {
// Prepare configuration for resource template.
Map<String, String> templateConfig = new HashMap<String, String>();
templateConfig.put(IMAGE.unwrap().getConfigKey(), IMAGE_ALIAS_RHEL);
templateConfig.put(TYPE.unwrap().getConfigKey(), MACHINE_TYPE_NAME);
templateConfig.put(NETWORK_NAME.unwrap().getConfigKey(), NETWORK_NAME_VALUE);
templateConfig.put(ZONE.unwrap().getConfigKey(), ZONE_NAME);
// Create the resource template.
GoogleComputeInstanceTemplate template = computeProvider.createResourceTemplate("template-1",
new SimpleConfiguration(templateConfig), new HashMap<String, String>());
// Configure stub for successful instance insertion operation.
String instanceName1 = UUID.randomUUID().toString();
String decoratedInstanceName1 = INSTANCE_NAME_PREFIX.unwrap().getDefaultValue() + "-" + instanceName1;
String instanceUrl1 = TestUtils.buildComputeInstanceUrl(PROJECT_ID, ZONE_NAME, decoratedInstanceName1);
Compute.Instances computeInstances = mockComputeToInstances();
Compute.Instances.Insert computeInstancesInsert1 = mockComputeInstancesInsert(computeInstances);
Operation vmCreationOperation1 = buildInitialOperation(ZONE_NAME, "insert", instanceUrl1);
OngoingStubbing<Operation> ongoingInsertionStub =
when(computeInstancesInsert1.execute()).thenReturn(vmCreationOperation1);
Compute.ZoneOperations computeZoneOperations = mockComputeToZoneOperations();
Compute.ZoneOperations.Get computeZoneOperationsGet1 = mock(Compute.ZoneOperations.Get.class);
when(computeZoneOperations.get(PROJECT_ID, ZONE_NAME,
vmCreationOperation1.getName())).thenReturn(computeZoneOperationsGet1);
when(computeZoneOperationsGet1.execute()).then(
new OperationAnswer(vmCreationOperation1, new String[]{"PENDING", "RUNNING", "DONE"}));
// Configure stub for unsuccessful instance insertion operation.
String instanceName2 = UUID.randomUUID().toString();
String decoratedInstanceName2 = INSTANCE_NAME_PREFIX.unwrap().getDefaultValue() + "-" + instanceName2;
String instanceUrl2 = TestUtils.buildComputeInstanceUrl(PROJECT_ID, ZONE_NAME, decoratedInstanceName2);
Operation vmCreationOperation2 = buildInitialOperation(ZONE_NAME, "insert", instanceUrl2);
ongoingInsertionStub.thenReturn(vmCreationOperation2);
Compute.ZoneOperations.Get computeZoneOperationsGet2 = mock(Compute.ZoneOperations.Get.class);
when(computeZoneOperations.get(PROJECT_ID, ZONE_NAME,
vmCreationOperation2.getName())).thenReturn(computeZoneOperationsGet2);
when(computeZoneOperationsGet2.execute()).then(
new OperationAnswer(vmCreationOperation2, new String[]{"PENDING", "RUNNING", "DONE"},
"SOME_ERROR_CODE", "Some error message..."));
// Configure stub for successful instance deletion operation.
Compute.Instances.Delete computeInstancesDelete1 =
mockComputeInstancesDelete(computeInstances, decoratedInstanceName1);
Operation vmDeletionOperation1 = buildInitialOperation(ZONE_NAME, "delete", instanceUrl1);
when(computeInstancesDelete1.execute()).thenReturn(vmDeletionOperation1);
Compute.ZoneOperations.Get computeZoneOperationsGet3 = mock(Compute.ZoneOperations.Get.class);
when(computeZoneOperations.get(PROJECT_ID, ZONE_NAME,
vmDeletionOperation1.getName())).thenReturn(computeZoneOperationsGet3);
when(computeZoneOperationsGet3.execute()).then(
new OperationAnswer(vmDeletionOperation1, new String[]{"PENDING", "RUNNING", "DONE"}));
// Configure stub for successful instance deletion operation.
Compute.Instances.Delete computeInstancesDelete2 =
mockComputeInstancesDelete(computeInstances, decoratedInstanceName2);
Operation vmDeletionOperation2 = buildInitialOperation(ZONE_NAME, "delete", instanceUrl2);
when(computeInstancesDelete2.execute()).thenReturn(vmDeletionOperation2);
Compute.ZoneOperations.Get computeZoneOperationsGet4 = mock(Compute.ZoneOperations.Get.class);
when(computeZoneOperations.get(PROJECT_ID, ZONE_NAME,
vmDeletionOperation2.getName())).thenReturn(computeZoneOperationsGet4);
when(computeZoneOperationsGet4.execute()).then(
new OperationAnswer(vmDeletionOperation2, new String[]{"PENDING", "RUNNING", "DONE"}));
try {
computeProvider.allocate(template, Lists.newArrayList(instanceName1, instanceName2), 2);
fail("An exception should have been thrown when we failed to provision at least minCount instances.");
} catch (UnrecoverableProviderException e) {
LOG.info("Caught: " + e.getMessage());
assertThat(e.getMessage()).isEqualTo("Problem allocating instances.");
verifySingleError(e.getDetails(), "Some error message...");
}
// Verify first instance insertion call was made.
ArgumentCaptor<Instance> insertArgumentCaptor = ArgumentCaptor.forClass(Instance.class);
verify(computeInstances, times(2)).insert(eq(PROJECT_ID), eq(ZONE_NAME), insertArgumentCaptor.capture());
List<Instance> insertedInstanceList = insertArgumentCaptor.getAllValues();
Instance insertedInstance1 = insertedInstanceList.get(0);
// Verify first instance name and metadata.
assertThat(insertedInstance1.getName()).isEqualTo(decoratedInstanceName1);
assertThat(insertedInstance1.getMetadata().getItems()).isEqualTo(Lists.newArrayList());
List<AttachedDisk> attachedDiskList1 = insertedInstance1.getDisks();
assertThat(attachedDiskList1.size()).isEqualTo(3);
// Verify boot disk.
verifyAttachedDiskAttributes(attachedDiskList1.get(0), true, true,
ComputeUrls.buildDiskTypeUrl(PROJECT_ID, ZONE_NAME, "SSD"), 60L,
TestUtils.buildImageUrl(IMAGE_PROJECT_ID, IMAGE_NAME), null, null, null);
// Verify data disk.
verifyAttachedDiskAttributes(attachedDiskList1.get(1), null, true,
ComputeUrls.buildDiskTypeUrl(PROJECT_ID, ZONE_NAME, "LocalSSD"), null, null, "SCSI", "SCRATCH", null);
// Verify data disk.
verifyAttachedDiskAttributes(attachedDiskList1.get(2), null, true,
ComputeUrls.buildDiskTypeUrl(PROJECT_ID, ZONE_NAME, "LocalSSD"), null, null, "SCSI", "SCRATCH", null);
// Verify second instance insertion call was made.
Instance insertedInstance2 = insertedInstanceList.get(1);
// Verify second instance name and metadata.
assertThat(insertedInstance2.getName()).isEqualTo(decoratedInstanceName2);
assertThat(insertedInstance2.getMetadata().getItems()).isEqualTo(Lists.newArrayList());
List<AttachedDisk> attachedDiskList2 = insertedInstance2.getDisks();
assertThat(attachedDiskList2.size()).isEqualTo(3);
// Verify boot disk.
verifyAttachedDiskAttributes(attachedDiskList2.get(0), true, true,
ComputeUrls.buildDiskTypeUrl(PROJECT_ID, ZONE_NAME, "SSD"), 60L,
TestUtils.buildImageUrl(IMAGE_PROJECT_ID, IMAGE_NAME), null, null, null);
// Verify data disk.
verifyAttachedDiskAttributes(attachedDiskList2.get(1), null, true,
ComputeUrls.buildDiskTypeUrl(PROJECT_ID, ZONE_NAME, "LocalSSD"), null, null, "SCSI", "SCRATCH", null);
// Verify data disk.
verifyAttachedDiskAttributes(attachedDiskList2.get(2), null, true,
ComputeUrls.buildDiskTypeUrl(PROJECT_ID, ZONE_NAME, "LocalSSD"), null, null, "SCSI", "SCRATCH", null);
// Verify first instance deletion call was made.
verify(computeInstances).delete(eq(PROJECT_ID), eq(ZONE_NAME), eq(decoratedInstanceName1));
// Verify second instance deletion call was made.
verify(computeInstances).delete(eq(PROJECT_ID), eq(ZONE_NAME), eq(decoratedInstanceName2));
}
@Test
public void testAllocate_LocalSSD_CreationFails_ReachesMinCount() throws InterruptedException, IOException {
// Prepare configuration for resource template.
Map<String, String> templateConfig = new HashMap<String, String>();
templateConfig.put(IMAGE.unwrap().getConfigKey(), IMAGE_ALIAS_RHEL);
templateConfig.put(TYPE.unwrap().getConfigKey(), MACHINE_TYPE_NAME);
templateConfig.put(NETWORK_NAME.unwrap().getConfigKey(), NETWORK_NAME_VALUE);
templateConfig.put(ZONE.unwrap().getConfigKey(), ZONE_NAME);
// Create the resource template.
GoogleComputeInstanceTemplate template = computeProvider.createResourceTemplate("template-1",
new SimpleConfiguration(templateConfig), new HashMap<String, String>());
// Configure stub for successful instance insertion operation.
String instanceName1 = UUID.randomUUID().toString();
String decoratedInstanceName1 = INSTANCE_NAME_PREFIX.unwrap().getDefaultValue() + "-" + instanceName1;
String instanceUrl1 = TestUtils.buildComputeInstanceUrl(PROJECT_ID, ZONE_NAME, decoratedInstanceName1);
Compute.Instances computeInstances = mockComputeToInstances();
Compute.Instances.Insert computeInstancesInsert1 = mockComputeInstancesInsert(computeInstances);
Operation vmCreationOperation1 = buildInitialOperation(ZONE_NAME, "insert", instanceUrl1);
OngoingStubbing<Operation> ongoingInsertionStub =
when(computeInstancesInsert1.execute()).thenReturn(vmCreationOperation1);
Compute.ZoneOperations computeZoneOperations = mockComputeToZoneOperations();
Compute.ZoneOperations.Get computeZoneOperationsGet1 = mock(Compute.ZoneOperations.Get.class);
when(computeZoneOperations.get(PROJECT_ID, ZONE_NAME,
vmCreationOperation1.getName())).thenReturn(computeZoneOperationsGet1);
when(computeZoneOperationsGet1.execute()).then(
new OperationAnswer(vmCreationOperation1, new String[]{"PENDING", "RUNNING", "DONE"}));
// Configure stub for unsuccessful instance insertion operation.
String instanceName2 = UUID.randomUUID().toString();
String decoratedInstanceName2 = INSTANCE_NAME_PREFIX.unwrap().getDefaultValue() + "-" + instanceName2;
String instanceUrl2 = TestUtils.buildComputeInstanceUrl(PROJECT_ID, ZONE_NAME, decoratedInstanceName2);
Operation vmCreationOperation2 = buildInitialOperation(ZONE_NAME, "insert", instanceUrl2);
ongoingInsertionStub.thenReturn(vmCreationOperation2);
Compute.ZoneOperations.Get computeZoneOperationsGet2 = mock(Compute.ZoneOperations.Get.class);
when(computeZoneOperations.get(PROJECT_ID, ZONE_NAME,
vmCreationOperation2.getName())).thenReturn(computeZoneOperationsGet2);
when(computeZoneOperationsGet2.execute()).then(
new OperationAnswer(vmCreationOperation2, new String[]{"PENDING", "RUNNING", "DONE"},
"SOME_ERROR_CODE", "Some error message..."));
computeProvider.allocate(template, Lists.newArrayList(instanceName1, instanceName2), 1);
// Verify first instance insertion call was made.
ArgumentCaptor<Instance> insertArgumentCaptor = ArgumentCaptor.forClass(Instance.class);
verify(computeInstances, times(2)).insert(eq(PROJECT_ID), eq(ZONE_NAME), insertArgumentCaptor.capture());
List<Instance> insertedInstanceList = insertArgumentCaptor.getAllValues();
Instance insertedInstance1 = insertedInstanceList.get(0);
// Verify first instance name and metadata.
assertThat(insertedInstance1.getName()).isEqualTo(decoratedInstanceName1);
assertThat(insertedInstance1.getMetadata().getItems()).isEqualTo(Lists.newArrayList());
List<AttachedDisk> attachedDiskList1 = insertedInstance1.getDisks();
assertThat(attachedDiskList1.size()).isEqualTo(3);
// Verify boot disk.
verifyAttachedDiskAttributes(attachedDiskList1.get(0), true, true,
ComputeUrls.buildDiskTypeUrl(PROJECT_ID, ZONE_NAME, "SSD"), 60L,
TestUtils.buildImageUrl(IMAGE_PROJECT_ID, IMAGE_NAME), null, null, null);
// Verify data disk.
verifyAttachedDiskAttributes(attachedDiskList1.get(1), null, true,
ComputeUrls.buildDiskTypeUrl(PROJECT_ID, ZONE_NAME, "LocalSSD"), null, null, "SCSI", "SCRATCH", null);
// Verify data disk.
verifyAttachedDiskAttributes(attachedDiskList1.get(2), null, true,
ComputeUrls.buildDiskTypeUrl(PROJECT_ID, ZONE_NAME, "LocalSSD"), null, null, "SCSI", "SCRATCH", null);
// Verify second instance insertion call was made.
Instance insertedInstance2 = insertedInstanceList.get(1);
// Verify second instance name and metadata.
assertThat(insertedInstance2.getName()).isEqualTo(decoratedInstanceName2);
assertThat(insertedInstance2.getMetadata().getItems()).isEqualTo(Lists.newArrayList());
List<AttachedDisk> attachedDiskList2 = insertedInstance2.getDisks();
assertThat(attachedDiskList2.size()).isEqualTo(3);
// Verify boot disk.
verifyAttachedDiskAttributes(attachedDiskList2.get(0), true, true,
ComputeUrls.buildDiskTypeUrl(PROJECT_ID, ZONE_NAME, "SSD"), 60L,
TestUtils.buildImageUrl(IMAGE_PROJECT_ID, IMAGE_NAME), null, null, null);
// Verify data disk.
verifyAttachedDiskAttributes(attachedDiskList2.get(1), null, true,
ComputeUrls.buildDiskTypeUrl(PROJECT_ID, ZONE_NAME, "LocalSSD"), null, null, "SCSI", "SCRATCH", null);
// Verify data disk.
verifyAttachedDiskAttributes(attachedDiskList2.get(2), null, true,
ComputeUrls.buildDiskTypeUrl(PROJECT_ID, ZONE_NAME, "LocalSSD"), null, null, "SCSI", "SCRATCH", null);
// NPE would be thrown (due to lack of mocks) if the compute provider attempted actual deletion calls against GCE.
// If no NPE's are thrown, the test is a success.
}
@Test
public void testAllocate_Standard() throws InterruptedException, IOException {
// Prepare configuration for resource template.
Map<String, String> templateConfig = new HashMap<String, String>();
templateConfig.put(IMAGE.unwrap().getConfigKey(), IMAGE_ALIAS_RHEL);
templateConfig.put(TYPE.unwrap().getConfigKey(), MACHINE_TYPE_NAME);
templateConfig.put(NETWORK_NAME.unwrap().getConfigKey(), NETWORK_NAME_VALUE);
templateConfig.put(ZONE.unwrap().getConfigKey(), ZONE_NAME);
templateConfig.put(DATA_DISK_COUNT.unwrap().getConfigKey(), "1");
templateConfig.put(DATA_DISK_TYPE.unwrap().getConfigKey(), "Standard");
templateConfig.put(DATA_DISK_SIZE_GB.unwrap().getConfigKey(), "250");
// Create the resource template.
GoogleComputeInstanceTemplate template = computeProvider.createResourceTemplate("template-1",
new SimpleConfiguration(templateConfig), new HashMap<String, String>());
String instanceName = UUID.randomUUID().toString();
String decoratedInstanceName = INSTANCE_NAME_PREFIX.unwrap().getDefaultValue() + "-" + instanceName;
String instanceUrl = TestUtils.buildComputeInstanceUrl(PROJECT_ID, ZONE_NAME, decoratedInstanceName);
// Configure stub for successful disk insertion operation.
String diskName = INSTANCE_NAME_PREFIX.unwrap().getDefaultValue() + "-" + instanceName + "-pd-0";
String diskUrl = ComputeUrls.buildDiskUrl(PROJECT_ID, ZONE_NAME, diskName);
Compute.Disks computeDisks = mockComputeToDisks();
Compute.Disks.Insert computeDisksInsert = mockComputeDisksInsert(computeDisks);
Operation diskCreationOperation = buildInitialOperation(ZONE_NAME, "insert", diskUrl);
when(computeDisksInsert.execute()).thenReturn(diskCreationOperation);
Compute.ZoneOperations computeZoneOperations = mockComputeToZoneOperations();
Compute.ZoneOperations.Get computeZoneOperationsGet1 = mock(Compute.ZoneOperations.Get.class);
when(computeZoneOperations.get(PROJECT_ID, ZONE_NAME,
diskCreationOperation.getName())).thenReturn(computeZoneOperationsGet1);
when(computeZoneOperationsGet1.execute()).then(
new OperationAnswer(diskCreationOperation, new String[]{"PENDING", "RUNNING", "DONE"}));
// Configure stub for successful instance insertion operation.
Compute.Instances computeInstances = mockComputeToInstances();
Compute.Instances.Insert computeInstancesInsert = mockComputeInstancesInsert(computeInstances);
Operation vmCreationOperation = buildInitialOperation(ZONE_NAME, "insert", instanceUrl);
when(computeInstancesInsert.execute()).thenReturn(vmCreationOperation);
Compute.ZoneOperations.Get computeZoneOperationsGet2 = mock(Compute.ZoneOperations.Get.class);
when(computeZoneOperations.get(PROJECT_ID, ZONE_NAME,
vmCreationOperation.getName())).thenReturn(computeZoneOperationsGet2);
when(computeZoneOperationsGet2.execute()).then(
new OperationAnswer(vmCreationOperation, new String[]{"PENDING", "RUNNING", "DONE"}));
computeProvider.allocate(template, Lists.newArrayList(instanceName), 1);
// Verify persistent disk insertion call was made.
ArgumentCaptor<Disk> argumentCaptor1 = ArgumentCaptor.forClass(Disk.class);
verify(computeDisks).insert(eq(PROJECT_ID), eq(ZONE_NAME), argumentCaptor1.capture());
Disk insertedDisk = argumentCaptor1.getValue();
// Verify disk name, size and type.
assertThat(insertedDisk.getName()).isEqualTo(diskName);
assertThat(insertedDisk.getSizeGb()).isEqualTo(250);
assertThat(insertedDisk.getType()).isEqualTo(ComputeUrls.buildDiskTypeUrl(PROJECT_ID, ZONE_NAME, "Standard"));
// Verify instance insertion call was made.
ArgumentCaptor<Instance> argumentCaptor2 = ArgumentCaptor.forClass(Instance.class);
verify(computeInstances).insert(eq(PROJECT_ID), eq(ZONE_NAME), argumentCaptor2.capture());
Instance insertedInstance = argumentCaptor2.getValue();
// Verify instance name and metadata.
assertThat(insertedInstance.getName()).isEqualTo(decoratedInstanceName);
assertThat(insertedInstance.getMetadata().getItems()).isEqualTo(Lists.newArrayList());
List<AttachedDisk> attachedDiskList = insertedInstance.getDisks();
assertThat(attachedDiskList.size()).isEqualTo(2);
// Verify boot disk.
verifyAttachedDiskAttributes(attachedDiskList.get(0), true, true,
ComputeUrls.buildDiskTypeUrl(PROJECT_ID, ZONE_NAME, "SSD"), 60L,
TestUtils.buildImageUrl(IMAGE_PROJECT_ID, IMAGE_NAME), null, null, null);
// Verify data disk.
verifyAttachedDiskAttributes(attachedDiskList.get(1), null, true, null, null,
null, null, "PERSISTENT", diskUrl);
}
@Test
public void testAllocate_SSD_CreationFails_BelowMinCount() throws InterruptedException, IOException {
// Prepare configuration for resource template.
Map<String, String> templateConfig = new HashMap<String, String>();
templateConfig.put(IMAGE.unwrap().getConfigKey(), IMAGE_ALIAS_RHEL);
templateConfig.put(TYPE.unwrap().getConfigKey(), MACHINE_TYPE_NAME);
templateConfig.put(NETWORK_NAME.unwrap().getConfigKey(), NETWORK_NAME_VALUE);
templateConfig.put(ZONE.unwrap().getConfigKey(), ZONE_NAME);
templateConfig.put(DATA_DISK_COUNT.unwrap().getConfigKey(), "1");
templateConfig.put(DATA_DISK_TYPE.unwrap().getConfigKey(), "SSD");
templateConfig.put(DATA_DISK_SIZE_GB.unwrap().getConfigKey(), "500");
// Create the resource template.
GoogleComputeInstanceTemplate template = computeProvider.createResourceTemplate("template-1",
new SimpleConfiguration(templateConfig), new HashMap<String, String>());
String instanceName1 = UUID.randomUUID().toString();
String decoratedInstanceName1 = INSTANCE_NAME_PREFIX.unwrap().getDefaultValue() + "-" + instanceName1;
String instanceUrl1 = TestUtils.buildComputeInstanceUrl(PROJECT_ID, ZONE_NAME, decoratedInstanceName1);
String instanceName2 = UUID.randomUUID().toString();
String decoratedInstanceName2 = INSTANCE_NAME_PREFIX.unwrap().getDefaultValue() + "-" + instanceName2;
String instanceUrl2 = TestUtils.buildComputeInstanceUrl(PROJECT_ID, ZONE_NAME, decoratedInstanceName2);
// Configure stub for first successful disk insertion operation.
String diskName1 = INSTANCE_NAME_PREFIX.unwrap().getDefaultValue() + "-" + instanceName1 + "-pd-0";
String diskUrl1 = ComputeUrls.buildDiskUrl(PROJECT_ID, ZONE_NAME, diskName1);
Compute.Disks computeDisks = mockComputeToDisks();
Compute.Disks.Insert computeDisksInsert = mockComputeDisksInsert(computeDisks);
Operation diskCreationOperation1 = buildInitialOperation(ZONE_NAME, "insert", diskUrl1);
OngoingStubbing<Operation> ongoingDiskStub =
when(computeDisksInsert.execute()).thenReturn(diskCreationOperation1);
Compute.ZoneOperations computeZoneOperations = mockComputeToZoneOperations();
Compute.ZoneOperations.Get computeZoneOperationsGet1 = mock(Compute.ZoneOperations.Get.class);
when(computeZoneOperations.get(PROJECT_ID, ZONE_NAME,
diskCreationOperation1.getName())).thenReturn(computeZoneOperationsGet1);
when(computeZoneOperationsGet1.execute()).then(
new OperationAnswer(diskCreationOperation1, new String[]{"PENDING", "RUNNING", "DONE"}));
// Configure stub for second successful disk insertion operation.
String diskName2 = INSTANCE_NAME_PREFIX.unwrap().getDefaultValue() + "-" + instanceName2 + "-pd-0";
String diskUrl2 = ComputeUrls.buildDiskUrl(PROJECT_ID, ZONE_NAME, diskName2);
Operation diskCreationOperation2 = buildInitialOperation(ZONE_NAME, "insert", diskUrl2);
ongoingDiskStub.thenReturn(diskCreationOperation2);
Compute.ZoneOperations.Get computeZoneOperationsGet2 = mock(Compute.ZoneOperations.Get.class);
when(computeZoneOperations.get(PROJECT_ID, ZONE_NAME,
diskCreationOperation2.getName())).thenReturn(computeZoneOperationsGet2);
when(computeZoneOperationsGet2.execute()).then(
new OperationAnswer(diskCreationOperation2, new String[]{"PENDING", "RUNNING", "DONE"}));
// Configure stub for successful instance insertion operation.
Compute.Instances computeInstances = mockComputeToInstances();
Compute.Instances.Insert computeInstancesInsert1 = mockComputeInstancesInsert(computeInstances);
Operation vmCreationOperation1 = buildInitialOperation(ZONE_NAME, "insert", instanceUrl1);
OngoingStubbing<Operation> ongoingInsertionStub =
when(computeInstancesInsert1.execute()).thenReturn(vmCreationOperation1);
Compute.ZoneOperations.Get computeZoneOperationsGet3 = mock(Compute.ZoneOperations.Get.class);
when(computeZoneOperations.get(PROJECT_ID, ZONE_NAME,
vmCreationOperation1.getName())).thenReturn(computeZoneOperationsGet3);
when(computeZoneOperationsGet3.execute()).then(
new OperationAnswer(vmCreationOperation1, new String[]{"PENDING", "RUNNING", "DONE"}));
// Configure stub for unsuccessful instance insertion operation.
Operation vmCreationOperation2 = buildInitialOperation(ZONE_NAME, "insert", instanceUrl2);
ongoingInsertionStub.thenReturn(vmCreationOperation2);
Compute.ZoneOperations.Get computeZoneOperationsGet4 = mock(Compute.ZoneOperations.Get.class);
when(computeZoneOperations.get(PROJECT_ID, ZONE_NAME,
vmCreationOperation2.getName())).thenReturn(computeZoneOperationsGet4);
when(computeZoneOperationsGet4.execute()).then(
new OperationAnswer(vmCreationOperation2, new String[]{"PENDING", "RUNNING", "DONE"},
"SOME_ERROR_CODE", "Some error message..."));
// Now configure the expected tearDown operations.
// Configure stub for successful instance retrieval with attached persistent disk.
Compute.Instances.Get computeInstancesGet1 = mockComputeInstancesGet(computeInstances, decoratedInstanceName1);
Instance instance1 = new Instance();
AttachedDisk attachedDisk1 = new AttachedDisk();
attachedDisk1.setSource(diskUrl1);
List<AttachedDisk> diskList1 = Lists.newArrayList(attachedDisk1);
instance1.setDisks(diskList1);
when(computeInstancesGet1.execute()).thenReturn(instance1);
// Configure stub for unsuccessful instance retrieval (throws 404).
Compute.Instances.Get computeInstancesGet2 = mockComputeInstancesGet(computeInstances, decoratedInstanceName2);
GoogleJsonResponseException exception =
GoogleJsonResponseExceptionFactoryTesting.newMock(new MockJsonFactory(), 404, "not found");
when(computeInstancesGet2.execute()).thenThrow(exception);
// Configure stub for successful instance deletion operation.
Compute.Instances.Delete computeInstancesDelete1 =
mockComputeInstancesDelete(computeInstances, decoratedInstanceName1);
Operation vmDeletionOperation1 = buildInitialOperation(ZONE_NAME, "delete", instanceUrl1);
when(computeInstancesDelete1.execute()).thenReturn(vmDeletionOperation1);
Compute.ZoneOperations.Get computeZoneOperationsGet5 = mock(Compute.ZoneOperations.Get.class);
when(computeZoneOperations.get(PROJECT_ID, ZONE_NAME,
vmDeletionOperation1.getName())).thenReturn(computeZoneOperationsGet5);
when(computeZoneOperationsGet5.execute()).then(
new OperationAnswer(vmDeletionOperation1, new String[]{"PENDING", "RUNNING", "DONE"}));
// Configure stub for successful disk deletion operation.
// The first disk was attached to the first instance, so we rely on auto-delete for that one.
// The second disk was never attached to an instance since the instance creation failed. So it
// must be explicitly deleted.
Compute.Disks.Delete computeDisksDelete = mockComputeDisksDelete(computeDisks, diskName2);
Operation diskDeletionOperation = buildInitialOperation(ZONE_NAME, "delete", diskUrl2);
when(computeDisksDelete.execute()).thenReturn(diskDeletionOperation);
Compute.ZoneOperations.Get computeZoneOperationsGet7 = mock(Compute.ZoneOperations.Get.class);
when(computeZoneOperations.get(PROJECT_ID, ZONE_NAME,
diskDeletionOperation.getName())).thenReturn(computeZoneOperationsGet7);
when(computeZoneOperationsGet7.execute()).then(
new OperationAnswer(diskDeletionOperation, new String[]{"PENDING", "RUNNING", "DONE"}));
try {
computeProvider.allocate(template, Lists.newArrayList(instanceName1, instanceName2), 2);
fail("An exception should have been thrown when we failed to provision at least minCount instances.");
} catch (UnrecoverableProviderException e) {
LOG.info("Caught: " + e.getMessage());
assertThat(e.getMessage()).isEqualTo("Problem allocating instances.");
verifySingleError(e.getDetails(), "Some error message...");
}
// Verify persistent disk insertion call was made.
ArgumentCaptor<Disk> argumentCaptor1 = ArgumentCaptor.forClass(Disk.class);
verify(computeDisks, times(2)).insert(eq(PROJECT_ID), eq(ZONE_NAME), argumentCaptor1.capture());
List<Disk> insertedDisks = argumentCaptor1.getAllValues();
Disk insertedDisk1 = insertedDisks.get(0);
// Verify first disk name, size and type.
assertThat(insertedDisk1.getName()).isEqualTo(diskName1);
assertThat(insertedDisk1.getSizeGb()).isEqualTo(500);
assertThat(insertedDisk1.getType()).isEqualTo(ComputeUrls.buildDiskTypeUrl(PROJECT_ID, ZONE_NAME, "SSD"));
// Verify second persistent disk insertion call was made.
Disk insertedDisk2 = insertedDisks.get(1);
// Verify second disk name, size and type.
assertThat(insertedDisk2.getName()).isEqualTo(diskName2);
assertThat(insertedDisk2.getSizeGb()).isEqualTo(500);
assertThat(insertedDisk2.getType()).isEqualTo(ComputeUrls.buildDiskTypeUrl(PROJECT_ID, ZONE_NAME, "SSD"));
// Verify first instance insertion call was made.
ArgumentCaptor<Instance> argumentCaptor2 = ArgumentCaptor.forClass(Instance.class);
verify(computeInstances, times(2)).insert(eq(PROJECT_ID), eq(ZONE_NAME), argumentCaptor2.capture());
List<Instance> insertedInstanceList = argumentCaptor2.getAllValues();
Instance insertedInstance1 = insertedInstanceList.get(0);
// Verify first instance name and metadata.
assertThat(insertedInstance1.getName()).isEqualTo(decoratedInstanceName1);
assertThat(insertedInstance1.getMetadata().getItems()).isEqualTo(Lists.newArrayList());
List<AttachedDisk> attachedDiskList1 = insertedInstance1.getDisks();
assertThat(attachedDiskList1.size()).isEqualTo(2);
// Verify boot disk.
verifyAttachedDiskAttributes(attachedDiskList1.get(0), true, true,
ComputeUrls.buildDiskTypeUrl(PROJECT_ID, ZONE_NAME, "SSD"), 60L,
TestUtils.buildImageUrl(IMAGE_PROJECT_ID, IMAGE_NAME), null, null, null);
// Verify data disk.
verifyAttachedDiskAttributes(attachedDiskList1.get(1), null, true, null, null,
null, null, "PERSISTENT", diskUrl1);
// Verify second instance insertion call was made.
Instance insertedInstance2 = insertedInstanceList.get(1);
// Verify second instance name and metadata.
assertThat(insertedInstance2.getName()).isEqualTo(decoratedInstanceName2);
assertThat(insertedInstance2.getMetadata().getItems()).isEqualTo(Lists.newArrayList());
List<AttachedDisk> attachedDiskList2 = insertedInstance2.getDisks();
assertThat(attachedDiskList2.size()).isEqualTo(2);
// Verify boot disk.
verifyAttachedDiskAttributes(attachedDiskList2.get(0), true, true,
ComputeUrls.buildDiskTypeUrl(PROJECT_ID, ZONE_NAME, "SSD"), 60L,
TestUtils.buildImageUrl(IMAGE_PROJECT_ID, IMAGE_NAME), null, null, null);
// Verify data disk.
verifyAttachedDiskAttributes(attachedDiskList2.get(1), null, true, null, null,
null, null, "PERSISTENT", diskUrl2);
// Verify disk deletion call was made (of disk two).
ArgumentCaptor<String> argumentCaptor3 = ArgumentCaptor.forClass(String.class);
verify(computeDisks).delete(eq(PROJECT_ID), eq(ZONE_NAME), argumentCaptor3.capture());
String deletedDiskName = argumentCaptor3.getValue();
assertThat(deletedDiskName).isEqualTo(diskName2);
// Verify instance deletion call was made (of instance one).
verify(computeInstances).delete(eq(PROJECT_ID), eq(ZONE_NAME), eq(decoratedInstanceName1));
}
@Test
public void testAllocate_RESOURCE_ALREADY_EXISTS() throws InterruptedException, IOException {
// Prepare configuration for resource template.
Map<String, String> templateConfig = new HashMap<String, String>();
templateConfig.put(IMAGE.unwrap().getConfigKey(), IMAGE_ALIAS_RHEL);
templateConfig.put(TYPE.unwrap().getConfigKey(), MACHINE_TYPE_NAME);
templateConfig.put(NETWORK_NAME.unwrap().getConfigKey(), NETWORK_NAME_VALUE);
templateConfig.put(ZONE.unwrap().getConfigKey(), ZONE_NAME);
// Create the resource template.
GoogleComputeInstanceTemplate template = computeProvider.createResourceTemplate("template-1",
new SimpleConfiguration(templateConfig), new HashMap<String, String>());
// Configure stub for unsuccessful instance insertion operation of instance that already exists.
String instanceName = UUID.randomUUID().toString();
String decoratedInstanceName = INSTANCE_NAME_PREFIX.unwrap().getDefaultValue() + "-" + instanceName;
String instanceUrl = TestUtils.buildComputeInstanceUrl(PROJECT_ID, ZONE_NAME, decoratedInstanceName);
Compute.Instances computeInstances = mockComputeToInstances();
Compute.Instances.Insert computeInstancesInsert = mockComputeInstancesInsert(computeInstances);
Operation vmCreationOperation = buildInitialOperation(ZONE_NAME, "insert", instanceUrl);
when(computeInstancesInsert.execute()).thenReturn(vmCreationOperation);
Compute.ZoneOperations computeZoneOperations = mockComputeToZoneOperations();
Compute.ZoneOperations.Get computeZoneOperationsGet = mock(Compute.ZoneOperations.Get.class);
when(computeZoneOperations.get(PROJECT_ID, ZONE_NAME,
vmCreationOperation.getName())).thenReturn(computeZoneOperationsGet);
when(computeZoneOperationsGet.execute()).then(
new OperationAnswer(vmCreationOperation, new String[]{"PENDING", "RUNNING", "DONE"},
"RESOURCE_ALREADY_EXISTS", "Some error message..."));
// An UnrecoverableProviderException would be thrown if the compute provider did not treat an error code of
// RESOURCE_ALREADY_EXISTS on insertion as acceptable.
// If no UnrecoverableProviderException is thrown, the test is a success.
computeProvider.allocate(template, Lists.newArrayList(instanceName), 1);
// Verify instance insertion call was made.
ArgumentCaptor<Instance> argumentCaptor = ArgumentCaptor.forClass(Instance.class);
verify(computeInstances).insert(eq(PROJECT_ID), eq(ZONE_NAME), argumentCaptor.capture());
Instance insertedInstance = argumentCaptor.getValue();
// Verify instance name and metadata.
assertThat(insertedInstance.getName()).isEqualTo(decoratedInstanceName);
assertThat(insertedInstance.getMetadata().getItems()).isEqualTo(Lists.newArrayList());
List<AttachedDisk> attachedDiskList = insertedInstance.getDisks();
assertThat(attachedDiskList.size()).isEqualTo(3);
// Verify boot disk.
verifyAttachedDiskAttributes(attachedDiskList.get(0), true, true,
ComputeUrls.buildDiskTypeUrl(PROJECT_ID, ZONE_NAME, "SSD"), 60L,
TestUtils.buildImageUrl(IMAGE_PROJECT_ID, IMAGE_NAME), null, null, null);
// Verify data disk.
verifyAttachedDiskAttributes(attachedDiskList.get(1), null, true,
ComputeUrls.buildDiskTypeUrl(PROJECT_ID, ZONE_NAME, "LocalSSD"), null, null, "SCSI", "SCRATCH", null);
// Verify data disk.
verifyAttachedDiskAttributes(attachedDiskList.get(2), null, true,
ComputeUrls.buildDiskTypeUrl(PROJECT_ID, ZONE_NAME, "LocalSSD"), null, null, "SCSI", "SCRATCH", null);
}
@Test
public void testFind() throws InterruptedException, IOException {
// Prepare configuration for resource template.
Map<String, String> templateConfig = new HashMap<String, String>();
templateConfig.put(ZONE.unwrap().getConfigKey(), ZONE_NAME);
// Create the resource template.
GoogleComputeInstanceTemplate template = computeProvider.createResourceTemplate("template-1",
new SimpleConfiguration(templateConfig), new HashMap<String, String>());
String instanceName1 = UUID.randomUUID().toString();
String decoratedInstanceName1 = INSTANCE_NAME_PREFIX.unwrap().getDefaultValue() + "-" + instanceName1;
String instanceName2 = UUID.randomUUID().toString();
String decoratedInstanceName2 = INSTANCE_NAME_PREFIX.unwrap().getDefaultValue() + "-" + instanceName2;
List<String> instanceIds = Lists.newArrayList(instanceName1, instanceName2);
// Configure stub for first successful instance retrieval.
Compute.Instances computeInstances = mockComputeToInstances();
Compute.Instances.Get computeInstancesGet1 = mockComputeInstancesGet(computeInstances, decoratedInstanceName1);
Instance instance1 = new Instance();
// Configure boot disk.
AttachedDisk attachedDisk1 = new AttachedDisk();
String diskName1 = UUID.randomUUID().toString();
String diskUrl1 = ComputeUrls.buildDiskUrl(PROJECT_ID, ZONE_NAME, diskName1);
attachedDisk1.setBoot(true);
attachedDisk1.setSource(diskUrl1);
List<AttachedDisk> diskList1 = Lists.newArrayList(attachedDisk1);
instance1.setDisks(diskList1);
// Configure network interface.
NetworkInterface networkInterface1 = new NetworkInterface();
networkInterface1.setNetworkIP("1.2.3.4");
List<NetworkInterface> networkInterfaceList1 = Lists.newArrayList(networkInterface1);
instance1.setNetworkInterfaces(networkInterfaceList1);
when(computeInstancesGet1.execute()).thenReturn(instance1);
// Configure stub for second successful instance retrieval.
Compute.Instances.Get computeInstancesGet2 = mockComputeInstancesGet(computeInstances, decoratedInstanceName2);
Instance instance2 = new Instance();
// Configure boot disk.
AttachedDisk attachedDisk2 = new AttachedDisk();
String diskName2 = UUID.randomUUID().toString();
String diskUrl2 = ComputeUrls.buildDiskUrl(PROJECT_ID, ZONE_NAME, diskName2);
attachedDisk2.setBoot(true);
attachedDisk2.setSource(diskUrl2);
List<AttachedDisk> diskList2 = Lists.newArrayList(attachedDisk2);
instance2.setDisks(diskList2);
// Configure network interface.
NetworkInterface networkInterface2 = new NetworkInterface();
networkInterface2.setNetworkIP("5.6.7.8");
List<NetworkInterface> networkInterfaceList2 = Lists.newArrayList(networkInterface2);
instance2.setNetworkInterfaces(networkInterfaceList2);
when(computeInstancesGet2.execute()).thenReturn(instance2);
// Configure stub for first successful boot disk retrieval.
Compute.Disks computeDisks = mockComputeToDisks();
Compute.Disks.Get computeDisksGet1 = mockComputeDisksGet(computeDisks, diskName1);
Disk bootDisk1 = new Disk();
bootDisk1.setSourceImage(diskUrl1);
when(computeDisksGet1.execute()).thenReturn(bootDisk1);
// Configure stub for second successful boot disk retrieval.
Compute.Disks.Get computeDisksGet2 = mockComputeDisksGet(computeDisks, diskName2);
Disk bootDisk2 = new Disk();
bootDisk2.setSourceImage(diskUrl2);
when(computeDisksGet2.execute()).thenReturn(bootDisk2);
Collection<GoogleComputeInstance> foundInstances = computeProvider.find(template, instanceIds);
// Verify that both of the two requested instances were returned.
assertThat(foundInstances.size()).isEqualTo(2);
// Verify the properties of the first returned instance.
Iterator<GoogleComputeInstance> instanceIterator = foundInstances.iterator();
GoogleComputeInstance foundInstance1 = instanceIterator.next();
assertThat(foundInstance1.getId()).isEqualTo(instanceName1);
assertThat(foundInstance1.getBootDisk().getSourceImage()).isEqualTo(diskUrl1);
assertThat(foundInstance1.getPrivateIpAddress().getHostAddress()).isEqualTo("1.2.3.4");
// Verify the properties of the second returned instance.
GoogleComputeInstance foundInstance2 = instanceIterator.next();
assertThat(foundInstance2.getId()).isEqualTo(instanceName2);
assertThat(foundInstance2.getBootDisk().getSourceImage()).isEqualTo(diskUrl2);
assertThat(foundInstance2.getPrivateIpAddress().getHostAddress()).isEqualTo("5.6.7.8");
}
@Test
public void testFind_PartialSuccess() throws InterruptedException, IOException {
// Prepare configuration for resource template.
Map<String, String> templateConfig = new HashMap<String, String>();
templateConfig.put(ZONE.unwrap().getConfigKey(), ZONE_NAME);
// Create the resource template.
GoogleComputeInstanceTemplate template = computeProvider.createResourceTemplate("template-1",
new SimpleConfiguration(templateConfig), new HashMap<String, String>());
String instanceName1 = UUID.randomUUID().toString();
String decoratedInstanceName1 = INSTANCE_NAME_PREFIX.unwrap().getDefaultValue() + "-" + instanceName1;
String instanceName2 = UUID.randomUUID().toString();
String decoratedInstanceName2 = INSTANCE_NAME_PREFIX.unwrap().getDefaultValue() + "-" + instanceName2;
List<String> instanceIds = Lists.newArrayList(instanceName1, instanceName2);
// Configure stub for successful instance retrieval.
Compute.Instances computeInstances = mockComputeToInstances();
Compute.Instances.Get computeInstancesGet1 = mockComputeInstancesGet(computeInstances, decoratedInstanceName1);
Instance instance1 = new Instance();
// Configure boot disk.
AttachedDisk attachedDisk1 = new AttachedDisk();
String diskName = UUID.randomUUID().toString();
String diskUrl = ComputeUrls.buildDiskUrl(PROJECT_ID, ZONE_NAME, diskName);
attachedDisk1.setBoot(true);
attachedDisk1.setSource(diskUrl);
List<AttachedDisk> diskList1 = Lists.newArrayList(attachedDisk1);
instance1.setDisks(diskList1);
// Configure network interface.
NetworkInterface networkInterface = new NetworkInterface();
networkInterface.setNetworkIP("1.2.3.4");
List<NetworkInterface> networkInterfaceList = Lists.newArrayList(networkInterface);
instance1.setNetworkInterfaces(networkInterfaceList);
when(computeInstancesGet1.execute()).thenReturn(instance1);
// Configure stub for unsuccessful instance retrieval (throws 404).
Compute.Instances.Get computeInstancesGet2 = mockComputeInstancesGet(computeInstances, decoratedInstanceName2);
GoogleJsonResponseException exception =
GoogleJsonResponseExceptionFactoryTesting.newMock(new MockJsonFactory(), 404, "not found");
when(computeInstancesGet2.execute()).thenThrow(exception);
// Configure stub for successful boot disk retrieval.
Compute.Disks computeDisks = mockComputeToDisks();
Compute.Disks.Get computeDisksGet = mockComputeDisksGet(computeDisks, diskName);
Disk bootDisk = new Disk();
bootDisk.setSourceImage(diskUrl);
when(computeDisksGet.execute()).thenReturn(bootDisk);
Collection<GoogleComputeInstance> foundInstances = computeProvider.find(template, instanceIds);
// Verify that exactly one of the two requested instances was returned.
assertThat(foundInstances.size()).isEqualTo(1);
// Verify the properties of the returned instance.
GoogleComputeInstance foundInstance = foundInstances.iterator().next();
assertThat(foundInstance.getId()).isEqualTo(instanceName1);
assertThat(foundInstance.getBootDisk().getSourceImage()).isEqualTo(diskUrl);
assertThat(foundInstance.getPrivateIpAddress().getHostAddress()).isEqualTo("1.2.3.4");
}
@Test
public void testFind_InvalidPrefix() throws InterruptedException, IOException {
// Prepare configuration for resource template.
Map<String, String> templateConfig = new HashMap<String, String>();
templateConfig.put(ZONE.unwrap().getConfigKey(), ZONE_NAME);
templateConfig.put(INSTANCE_NAME_PREFIX.unwrap().getConfigKey(), INVALID_INSTANCE_NAME_PREFIX);
// Create the resource template.
GoogleComputeInstanceTemplate template = computeProvider.createResourceTemplate("template-1",
new SimpleConfiguration(templateConfig), new HashMap<String, String>());
String instanceName1 = UUID.randomUUID().toString();
String instanceName2 = UUID.randomUUID().toString();
List<String> instanceIds = Lists.newArrayList(instanceName1, instanceName2);
// NPE would be thrown (due to lack of mocks) if the compute provider attempted actual calls against GCE.
// When the instance name prefix is deemed invalid, no calls are attempted against GCE.
Collection<GoogleComputeInstance> foundInstances = computeProvider.find(template, instanceIds);
// Verify that no instances were returned.
assertThat(foundInstances.size()).isEqualTo(0);
}
@Test
public void testGetInstanceState() throws InterruptedException, IOException {
// Prepare configuration for resource template.
Map<String, String> templateConfig = new HashMap<String, String>();
templateConfig.put(ZONE.unwrap().getConfigKey(), ZONE_NAME);
// Create the resource template.
GoogleComputeInstanceTemplate template = computeProvider.createResourceTemplate("template-1",
new SimpleConfiguration(templateConfig), new HashMap<String, String>());
String instanceName1 = UUID.randomUUID().toString();
String decoratedInstanceName1 = INSTANCE_NAME_PREFIX.unwrap().getDefaultValue() + "-" + instanceName1;
String instanceName2 = UUID.randomUUID().toString();
String decoratedInstanceName2 = INSTANCE_NAME_PREFIX.unwrap().getDefaultValue() + "-" + instanceName2;
List<String> instanceIds = Lists.newArrayList(instanceName1, instanceName2);
// Configure stub for first successful instance retrieval.
Compute.Instances computeInstances = mockComputeToInstances();
Compute.Instances.Get computeInstancesGet1 = mockComputeInstancesGet(computeInstances, decoratedInstanceName1);
Instance instance1 = new Instance();
instance1.setStatus("PROVISIONING");
when(computeInstancesGet1.execute()).thenReturn(instance1);
// Configure stub for second successful instance retrieval.
Compute.Instances.Get computeInstancesGet2 = mockComputeInstancesGet(computeInstances, decoratedInstanceName2);
Instance instance2 = new Instance();
instance2.setStatus("RUNNING");
when(computeInstancesGet2.execute()).thenReturn(instance2);
Map<String, InstanceState> instanceStates = computeProvider.getInstanceState(template, instanceIds);
// Verify that the state of both instances was returned.
assertThat(instanceStates.size()).isEqualTo(2);
// Verify the state of the first instance.
InstanceState instanceState1 = instanceStates.get(instanceName1);
assertThat(instanceState1.getInstanceStatus()).isEqualTo(InstanceStatus.PENDING);
// Verify the state of the second instance.
InstanceState instanceState2 = instanceStates.get(instanceName2);
assertThat(instanceState2.getInstanceStatus()).isEqualTo(InstanceStatus.RUNNING);
}
@Test
public void testGetInstanceState_PartialSuccess() throws InterruptedException, IOException {
// Prepare configuration for resource template.
Map<String, String> templateConfig = new HashMap<String, String>();
templateConfig.put(ZONE.unwrap().getConfigKey(), ZONE_NAME);
// Create the resource template.
GoogleComputeInstanceTemplate template = computeProvider.createResourceTemplate("template-1",
new SimpleConfiguration(templateConfig), new HashMap<String, String>());
String instanceName1 = UUID.randomUUID().toString();
String decoratedInstanceName1 = INSTANCE_NAME_PREFIX.unwrap().getDefaultValue() + "-" + instanceName1;
String instanceName2 = UUID.randomUUID().toString();
String decoratedInstanceName2 = INSTANCE_NAME_PREFIX.unwrap().getDefaultValue() + "-" + instanceName2;
List<String> instanceIds = Lists.newArrayList(instanceName1, instanceName2);
// Configure stub for first successful instance retrieval.
Compute.Instances computeInstances = mockComputeToInstances();
Compute.Instances.Get computeInstancesGet1 = mockComputeInstancesGet(computeInstances, decoratedInstanceName1);
Instance instance1 = new Instance();
instance1.setStatus("STAGING");
when(computeInstancesGet1.execute()).thenReturn(instance1);
// Configure stub for unsuccessful instance retrieval (throws 404).
Compute.Instances.Get computeInstancesGet2 = mockComputeInstancesGet(computeInstances, decoratedInstanceName2);
GoogleJsonResponseException exception =
GoogleJsonResponseExceptionFactoryTesting.newMock(new MockJsonFactory(), 404, "not found");
when(computeInstancesGet2.execute()).thenThrow(exception);
Map<String, InstanceState> instanceStates = computeProvider.getInstanceState(template, instanceIds);
// Verify that the state of both instances was returned.
assertThat(instanceStates.size()).isEqualTo(2);
// Verify the state of the first instance.
InstanceState instanceState1 = instanceStates.get(instanceName1);
assertThat(instanceState1.getInstanceStatus()).isEqualTo(InstanceStatus.PENDING);
// Verify the state of the second instance.
InstanceState instanceState2 = instanceStates.get(instanceName2);
assertThat(instanceState2.getInstanceStatus()).isEqualTo(InstanceStatus.UNKNOWN);
}
@Test
public void testGetInstanceState_InvalidPrefix() throws InterruptedException, IOException {
// Prepare configuration for resource template.
Map<String, String> templateConfig = new HashMap<String, String>();
templateConfig.put(ZONE.unwrap().getConfigKey(), ZONE_NAME);
templateConfig.put(INSTANCE_NAME_PREFIX.unwrap().getConfigKey(), INVALID_INSTANCE_NAME_PREFIX);
// Create the resource template.
GoogleComputeInstanceTemplate template = computeProvider.createResourceTemplate("template-1",
new SimpleConfiguration(templateConfig), new HashMap<String, String>());
String instanceName1 = UUID.randomUUID().toString();
String instanceName2 = UUID.randomUUID().toString();
List<String> instanceIds = Lists.newArrayList(instanceName1, instanceName2);
// NPE would be thrown (due to lack of mocks) if the compute provider attempted actual calls against GCE.
// When the instance name prefix is deemed invalid, no calls are attempted against GCE.
Map<String, InstanceState> instanceStates = computeProvider.getInstanceState(template, instanceIds);
// Verify that the state of both instances was returned.
assertThat(instanceStates.size()).isEqualTo(2);
// Verify the state of the first instance.
InstanceState instanceState1 = instanceStates.get(instanceName1);
assertThat(instanceState1.getInstanceStatus()).isEqualTo(InstanceStatus.UNKNOWN);
// Verify the state of the second instance.
InstanceState instanceState2 = instanceStates.get(instanceName2);
assertThat(instanceState2.getInstanceStatus()).isEqualTo(InstanceStatus.UNKNOWN);
}
@Test
public void testDelete() throws InterruptedException, IOException {
// Prepare configuration for resource template.
Map<String, String> templateConfig = new HashMap<String, String>();
templateConfig.put(ZONE.unwrap().getConfigKey(), ZONE_NAME);
// Create the resource template.
GoogleComputeInstanceTemplate template = computeProvider.createResourceTemplate("template-1",
new SimpleConfiguration(templateConfig), new HashMap<String, String>());
String instanceName1 = UUID.randomUUID().toString();
String decoratedInstanceName1 = INSTANCE_NAME_PREFIX.unwrap().getDefaultValue() + "-" + instanceName1;
String instanceUrl1 = TestUtils.buildComputeInstanceUrl(PROJECT_ID, ZONE_NAME, decoratedInstanceName1);
String instanceName2 = UUID.randomUUID().toString();
String decoratedInstanceName2 = INSTANCE_NAME_PREFIX.unwrap().getDefaultValue() + "-" + instanceName2;
String instanceUrl2 = TestUtils.buildComputeInstanceUrl(PROJECT_ID, ZONE_NAME, decoratedInstanceName2);
List<String> instanceIds = Lists.newArrayList(instanceName1, instanceName2);
// Configure stub for successful instance deletion operation.
Compute.Instances computeInstances = mockComputeToInstances();
Compute.Instances.Delete computeInstancesDelete1 =
mockComputeInstancesDelete(computeInstances, decoratedInstanceName1);
Operation vmDeletionOperation1 = buildInitialOperation(ZONE_NAME, "delete", instanceUrl1);
when(computeInstancesDelete1.execute()).thenReturn(vmDeletionOperation1);
Compute.ZoneOperations computeZoneOperations = mockComputeToZoneOperations();
Compute.ZoneOperations.Get computeZoneOperationsGet1 = mock(Compute.ZoneOperations.Get.class);
when(computeZoneOperations.get(PROJECT_ID, ZONE_NAME,
vmDeletionOperation1.getName())).thenReturn(computeZoneOperationsGet1);
when(computeZoneOperationsGet1.execute()).then(
new OperationAnswer(vmDeletionOperation1, new String[]{"PENDING", "RUNNING", "DONE"}));
// Configure stub for successful instance deletion operation.
Compute.Instances.Delete computeInstancesDelete2 =
mockComputeInstancesDelete(computeInstances, decoratedInstanceName2);
Operation vmDeletionOperation2 = buildInitialOperation(ZONE_NAME, "delete", instanceUrl2);
when(computeInstancesDelete2.execute()).thenReturn(vmDeletionOperation2);
Compute.ZoneOperations.Get computeZoneOperationsGet2 = mock(Compute.ZoneOperations.Get.class);
when(computeZoneOperations.get(PROJECT_ID, ZONE_NAME,
vmDeletionOperation2.getName())).thenReturn(computeZoneOperationsGet2);
when(computeZoneOperationsGet2.execute()).then(
new OperationAnswer(vmDeletionOperation2, new String[]{"PENDING", "RUNNING", "DONE"}));
computeProvider.delete(template, instanceIds);
// Verify first instance deletion call was made.
verify(computeInstances).delete(eq(PROJECT_ID), eq(ZONE_NAME), eq(decoratedInstanceName1));
// Verify second instance deletion call was made.
verify(computeInstances).delete(eq(PROJECT_ID), eq(ZONE_NAME), eq(decoratedInstanceName2));
}
@Test
public void testDelete_PartialSuccess() throws InterruptedException, IOException {
// Prepare configuration for resource template.
Map<String, String> templateConfig = new HashMap<String, String>();
templateConfig.put(ZONE.unwrap().getConfigKey(), ZONE_NAME);
// Create the resource template.
GoogleComputeInstanceTemplate template = computeProvider.createResourceTemplate("template-1",
new SimpleConfiguration(templateConfig), new HashMap<String, String>());
String instanceName1 = UUID.randomUUID().toString();
String decoratedInstanceName1 = INSTANCE_NAME_PREFIX.unwrap().getDefaultValue() + "-" + instanceName1;
String instanceUrl1 = TestUtils.buildComputeInstanceUrl(PROJECT_ID, ZONE_NAME, decoratedInstanceName1);
String instanceName2 = UUID.randomUUID().toString();
String decoratedInstanceName2 = INSTANCE_NAME_PREFIX.unwrap().getDefaultValue() + "-" + instanceName2;
List<String> instanceIds = Lists.newArrayList(instanceName1, instanceName2);
// Configure stub for successful instance deletion operation.
Compute.Instances computeInstances = mockComputeToInstances();
Compute.Instances.Delete computeInstancesDelete1 =
mockComputeInstancesDelete(computeInstances, decoratedInstanceName1);
Operation vmDeletionOperation1 = buildInitialOperation(ZONE_NAME, "delete", instanceUrl1);
when(computeInstancesDelete1.execute()).thenReturn(vmDeletionOperation1);
Compute.ZoneOperations computeZoneOperations = mockComputeToZoneOperations();
Compute.ZoneOperations.Get computeZoneOperationsGet1 = mock(Compute.ZoneOperations.Get.class);
when(computeZoneOperations.get(PROJECT_ID, ZONE_NAME,
vmDeletionOperation1.getName())).thenReturn(computeZoneOperationsGet1);
when(computeZoneOperationsGet1.execute()).then(
new OperationAnswer(vmDeletionOperation1, new String[]{"PENDING", "RUNNING", "DONE"}));
// Configure stub for unsuccessful instance deletion operation.
Compute.Instances.Delete computeInstancesDelete2 =
mockComputeInstancesDelete(computeInstances, decoratedInstanceName2);
GoogleJsonResponseException exception =
GoogleJsonResponseExceptionFactoryTesting.newMock(new MockJsonFactory(), 404, "not found");
when(computeInstancesDelete2.execute()).thenThrow(exception);
computeProvider.delete(template, instanceIds);
// Verify first instance deletion call was made.
verify(computeInstances).delete(eq(PROJECT_ID), eq(ZONE_NAME), eq(decoratedInstanceName1));
// Verify second instance deletion call was made.
verify(computeInstances).delete(eq(PROJECT_ID), eq(ZONE_NAME), eq(decoratedInstanceName2));
}
@Test
public void testDelete_InvalidPrefix() throws InterruptedException, IOException {
// Prepare configuration for resource template.
Map<String, String> templateConfig = new HashMap<String, String>();
templateConfig.put(ZONE.unwrap().getConfigKey(), ZONE_NAME);
templateConfig.put(INSTANCE_NAME_PREFIX.unwrap().getConfigKey(), INVALID_INSTANCE_NAME_PREFIX);
// Create the resource template.
GoogleComputeInstanceTemplate template = computeProvider.createResourceTemplate("template-1",
new SimpleConfiguration(templateConfig), new HashMap<String, String>());
String instanceName1 = UUID.randomUUID().toString();
String instanceName2 = UUID.randomUUID().toString();
List<String> instanceIds = Lists.newArrayList(instanceName1, instanceName2);
// NPE would be thrown (due to lack of mocks) if the compute provider attempted actual calls against GCE.
// When the instance name prefix is deemed invalid, no calls are attempted against GCE.
// If no NPE's are thrown, the test is a success.
computeProvider.delete(template, instanceIds);
}
@Test
public void testDelete_RESOURCE_NOT_FOUND() throws InterruptedException, IOException {
// Prepare configuration for resource template.
Map<String, String> templateConfig = new HashMap<String, String>();
templateConfig.put(ZONE.unwrap().getConfigKey(), ZONE_NAME);
// Create the resource template.
GoogleComputeInstanceTemplate template = computeProvider.createResourceTemplate("template-1",
new SimpleConfiguration(templateConfig), new HashMap<String, String>());
String instanceName = UUID.randomUUID().toString();
String decoratedInstanceName = INSTANCE_NAME_PREFIX.unwrap().getDefaultValue() + "-" + instanceName;
String instanceUrl = TestUtils.buildComputeInstanceUrl(PROJECT_ID, ZONE_NAME, decoratedInstanceName);
List<String> instanceIds = Lists.newArrayList(instanceName);
// Configure stub for unsuccessful instance deletion operation of instance that does not exist.
Compute.Instances computeInstances = mockComputeToInstances();
Compute.Instances.Delete computeInstancesDelete =
mockComputeInstancesDelete(computeInstances, decoratedInstanceName);
Operation vmDeletionOperation = buildInitialOperation(ZONE_NAME, "delete", instanceUrl);
when(computeInstancesDelete.execute()).thenReturn(vmDeletionOperation);
Compute.ZoneOperations computeZoneOperations = mockComputeToZoneOperations();
Compute.ZoneOperations.Get computeZoneOperationsGet = mock(Compute.ZoneOperations.Get.class);
when(computeZoneOperations.get(PROJECT_ID, ZONE_NAME,
vmDeletionOperation.getName())).thenReturn(computeZoneOperationsGet);
when(computeZoneOperationsGet.execute()).then(
new OperationAnswer(vmDeletionOperation, new String[]{"PENDING", "DONE"},
"RESOURCE_NOT_FOUND", "Some error message..."));
// An UnrecoverableProviderException would be thrown if the compute provider did not treat an error code of
// RESOURCE_NOT_FOUND on deletion as acceptable.
// If no UnrecoverableProviderException is thrown, the test is a success.
computeProvider.delete(template, instanceIds);
// Verify instance deletion call was made.
verify(computeInstances).delete(eq(PROJECT_ID), eq(ZONE_NAME), eq(decoratedInstanceName));
}
@Test
public void testDelete_RESOURCE_NOT_READY() throws InterruptedException, IOException {
// Prepare configuration for resource template.
Map<String, String> templateConfig = new HashMap<String, String>();
templateConfig.put(ZONE.unwrap().getConfigKey(), ZONE_NAME);
// Create the resource template.
GoogleComputeInstanceTemplate template = computeProvider.createResourceTemplate("template-1",
new SimpleConfiguration(templateConfig), new HashMap<String, String>());
String instanceName = UUID.randomUUID().toString();
String decoratedInstanceName = INSTANCE_NAME_PREFIX.unwrap().getDefaultValue() + "-" + instanceName;
String instanceUrl = TestUtils.buildComputeInstanceUrl(PROJECT_ID, ZONE_NAME, decoratedInstanceName);
List<String> instanceIds = Lists.newArrayList(instanceName);
// Configure stub for unsuccessful instance deletion operation of instance that does not exist.
Compute.Instances computeInstances = mockComputeToInstances();
Compute.Instances.Delete computeInstancesDelete =
mockComputeInstancesDelete(computeInstances, decoratedInstanceName);
Operation vmDeletionOperation = buildInitialOperation(ZONE_NAME, "delete", instanceUrl);
when(computeInstancesDelete.execute()).thenReturn(vmDeletionOperation);
Compute.ZoneOperations computeZoneOperations = mockComputeToZoneOperations();
Compute.ZoneOperations.Get computeZoneOperationsGet = mock(Compute.ZoneOperations.Get.class);
when(computeZoneOperations.get(PROJECT_ID, ZONE_NAME,
vmDeletionOperation.getName())).thenReturn(computeZoneOperationsGet);
when(computeZoneOperationsGet.execute()).then(
new OperationAnswer(vmDeletionOperation, new String[]{"PENDING", "DONE"},
"RESOURCE_NOT_READY", "Some error message..."));
// An UnrecoverableProviderException would be thrown if the compute provider did not treat an error code of
// RESOURCE_NOT_READY on deletion as acceptable.
// If no UnrecoverableProviderException is thrown, the test is a success.
computeProvider.delete(template, instanceIds);
// Verify instance deletion call was made.
verify(computeInstances).delete(eq(PROJECT_ID), eq(ZONE_NAME), eq(decoratedInstanceName));
}
private static Operation buildInitialOperation(String zone, String operationType, String targetLinkUrl) {
return buildOperation(zone, UUID.randomUUID().toString(), operationType, targetLinkUrl, "PENDING");
}
private static Operation buildOperation(String zone, String operationName, String operationType, String targetLinkUrl,
String status) {
Operation operation = new Operation();
operation.setZone(ComputeUrls.buildZonalUrl(PROJECT_ID, zone));
operation.setName(operationName);
operation.setOperationType(operationType);
operation.setTargetLink(targetLinkUrl);
operation.setStatus(status);
return operation;
}
/**
* Used to return operations with a series of statuses in response to the compute provider polling.
* If both the errorCode and the errorMessage are not null and not empty, an error will be set on the
* returned operation.
*/
class OperationAnswer implements Answer<Operation> {
Operation subjectOperation;
private Deque<String> statusQueue;
private String errorCode;
private String errorMessage;
public OperationAnswer(Operation subjectOperation, String[] statuses) {
this.subjectOperation = subjectOperation;
this.statusQueue = new ArrayDeque<String>();
for (String status : statuses) {
statusQueue.add(status);
}
}
public OperationAnswer(Operation subjectOperation, String[] statuses, String errorCode, String errorMessage) {
this(subjectOperation, statuses);
this.errorCode = errorCode;
this.errorMessage = errorMessage;
}
@Override
public Operation answer(InvocationOnMock invocationOnMock) throws Throwable {
Operation polledOperation = buildOperation(ZONE_NAME, subjectOperation.getName(),
subjectOperation.getOperationType(), subjectOperation.getTargetLink(), statusQueue.remove());
if (polledOperation.getStatus().equals("DONE") && errorCode != null) {
Operation.Error.Errors errors = new Operation.Error.Errors();
errors.setCode(errorCode);
errors.setMessage(errorMessage);
List<Operation.Error.Errors> errorsList = Lists.newArrayList(errors);
Operation.Error error = new Operation.Error();
error.setErrors(errorsList);
polledOperation.setError(error);
}
return polledOperation;
}
}
/**
* Verifies that the properties of the specified attached disk match the specified arguments.
*
* @param attachedDisk the disk to examine. Must not be null.
* @param boot whether or not this is a boot disk. May be null.
* @param autoDelete whether or not this disk will auto-delete. May be null.
* @param diskType the type of the disk. May be null.
* @param diskSizeGb the size of the disk in GB. May be null.
* @param sourceImage the image from which the disk is to be created. May be null.
* @param localSSDInterfaceType the interface type, if this is a Local SSD disk. May be null.
* @param scratchOrPersistent either "SCRATCH" or "PERSISTENT". May be null.
* @param source the source disk url if this is a persistent disk. May be null.
*/
private static void verifyAttachedDiskAttributes(AttachedDisk attachedDisk, Boolean boot, Boolean autoDelete,
String diskType, Long diskSizeGb, String sourceImage, String localSSDInterfaceType,
String scratchOrPersistent, String source) {
assertThat(attachedDisk.getBoot()).isEqualTo(boot);
assertThat(attachedDisk.getAutoDelete()).isEqualTo(autoDelete);
AttachedDiskInitializeParams initializeParams = attachedDisk.getInitializeParams();
if (initializeParams != null) {
assertThat(initializeParams.getDiskType()).isEqualTo(diskType);
assertThat(initializeParams.getDiskSizeGb()).isEqualTo(diskSizeGb);
assertThat(initializeParams.getSourceImage()).isEqualTo(sourceImage);
}
assertThat(attachedDisk.getInterface()).isEqualTo(localSSDInterfaceType);
assertThat(attachedDisk.getType()).isEqualTo(scratchOrPersistent);
assertThat(attachedDisk.getSource()).isEqualTo(source);
}
/**
* Verifies that the specified plugin exception details contain exactly one condition, which must be an
* error with the specified message.
*
* @param pluginExceptionDetails the exception details containins the error conditions
* @param errorMsg the expected error message
*/
private static void verifySingleError(PluginExceptionDetails pluginExceptionDetails, String errorMsg) {
Map<String, SortedSet<PluginExceptionCondition>> conditionsByKey = pluginExceptionDetails.getConditionsByKey();
assertThat(conditionsByKey).hasSize(1);
Collection<PluginExceptionCondition> keyConditions = conditionsByKey.get(null);
assertThat(keyConditions).hasSize(1);
PluginExceptionCondition condition = keyConditions.iterator().next();
assertThat(condition.getMessage()).isEqualTo(errorMsg);
}
}