/* * 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.sql; import static com.cloudera.director.google.sql.GoogleCloudSQLInstanceTemplateConfigurationProperty.ENGINE; import static com.cloudera.director.google.sql.GoogleCloudSQLInstanceTemplateConfigurationProperty.MASTER_USER_PASSWORD; import static com.cloudera.director.google.sql.GoogleCloudSQLInstanceTemplateConfigurationProperty.MASTER_USERNAME; import static com.cloudera.director.google.sql.GoogleCloudSQLInstanceTemplateConfigurationProperty.TIER; import static com.cloudera.director.google.sql.GoogleCloudSQLProviderConfigurationProperty.REGION; import static com.cloudera.director.spi.v1.model.InstanceTemplate.InstanceTemplateConfigurationPropertyToken.INSTANCE_NAME_PREFIX; import static junit.framework.Assert.assertEquals; 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.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.sqladmin.SQLAdmin; import com.cloudera.director.google.shaded.com.google.api.services.sqladmin.model.DatabaseInstance; import com.cloudera.director.google.shaded.com.google.api.services.sqladmin.model.IpMapping; import com.cloudera.director.google.shaded.com.google.api.services.sqladmin.model.Operation; import com.cloudera.director.google.shaded.com.google.api.services.sqladmin.model.OperationError; import com.cloudera.director.google.shaded.com.google.api.services.sqladmin.model.OperationErrors; import com.cloudera.director.google.shaded.com.google.api.services.sqladmin.model.User; 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.beans.IntrospectionException; 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 GoogleCloudSQLProvider}. */ public class GoogleCloudSQLProviderTest { private static final Logger LOG = Logger.getLogger(GoogleCloudSQLProviderTest.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 = "us-central"; private static final String TIER_NAME = "D2"; private static final String USER_PASSWORD = "admin"; private static final String USERNAME = "admin"; private static final String DATABASE_TYPE = "MYSQL"; private static final String INVALID_INSTANCE_NAME_PREFIX = "-starts-with-dash"; private GoogleCloudSQLProvider sqlProvider; private GoogleCredentials credentials; private SQLAdmin sqlAdmin; @Before public void setUp() throws IOException { credentials = mock(GoogleCredentials.class); sqlAdmin = mock(SQLAdmin.class); when(credentials.getSQLAdmin()).thenReturn(sqlAdmin); when(credentials.getProjectId()).thenReturn(PROJECT_ID); SQLAdmin.Tiers.List sqlTierList = mockSQLAdminToTiersList(); // We don't need to actually return a list of tiers, we just need to not throw a 404. when(sqlTierList.execute()).thenReturn(null); // Prepare configuration for Google Cloud SQL provider. Map<String, String> sqlAdminConfig = new HashMap<String, String>(); sqlAdminConfig.put(REGION.unwrap().getConfigKey(), REGION_NAME); Configured resourceProviderConfiguration = new SimpleConfiguration(sqlAdminConfig); // Create the Google Cloud SQL provider. sqlProvider = new GoogleCloudSQLProvider(resourceProviderConfiguration, credentials, TestUtils.buildApplicationPropertiesConfig(), TestUtils.buildGoogleConfig(), DEFAULT_LOCALIZATION_CONTEXT); } private SQLAdmin.Tiers.List mockSQLAdminToTiersList() throws IOException { SQLAdmin.Tiers sqlAdminTiers = mock(SQLAdmin.Tiers.class); SQLAdmin.Tiers.List sqlAdminTierList = mock(SQLAdmin.Tiers.List.class); when(sqlAdmin.tiers()).thenReturn(sqlAdminTiers); when(sqlAdminTiers.list(PROJECT_ID)).thenReturn(sqlAdminTierList); return sqlAdminTierList; } private SQLAdmin.Instances mockSQLAdminToInstances() { SQLAdmin.Instances sqlAdminInstances = mock(SQLAdmin.Instances.class); when(sqlAdmin.instances()).thenReturn(sqlAdminInstances); return sqlAdminInstances; } private SQLAdmin.Instances.Insert mockSQLAdminInstancesInsert(SQLAdmin.Instances sqlAdminInstances) throws IOException { SQLAdmin.Instances.Insert sqlAdminInstancesInsert = mock(SQLAdmin.Instances.Insert.class); when(sqlAdminInstances.insert(eq(PROJECT_ID), any(DatabaseInstance.class))).thenReturn(sqlAdminInstancesInsert); return sqlAdminInstancesInsert; } private SQLAdmin.Operations mockSQLAdminToOperations() { SQLAdmin.Operations sqlAdminOperations = mock(SQLAdmin.Operations.class); when(sqlAdmin.operations()).thenReturn(sqlAdminOperations); return sqlAdminOperations; } private SQLAdmin.Instances.Delete mockSQLAdminInstancesDelete( SQLAdmin.Instances sqlAdminInstances, String instanceName) throws IOException { SQLAdmin.Instances.Delete sqlAdminInstancesDelete = mock(SQLAdmin.Instances.Delete.class); when(sqlAdminInstances.delete(eq(PROJECT_ID), eq(instanceName))).thenReturn(sqlAdminInstancesDelete); return sqlAdminInstancesDelete; } private SQLAdmin.Instances.Get mockSQLAdminInstancesGet( SQLAdmin.Instances sqlAdminInstances, String instanceName) throws IOException { SQLAdmin.Instances.Get sqlAdminInstancesGet = mock(SQLAdmin.Instances.Get.class); when(sqlAdminInstances.get(PROJECT_ID, instanceName)).thenReturn(sqlAdminInstancesGet); return sqlAdminInstancesGet; } private SQLAdmin.Users mockSQLAdminUsers() { SQLAdmin.Users sqlAdminUsers = mock(SQLAdmin.Users.class); when(sqlAdmin.users()).thenReturn(sqlAdminUsers); return sqlAdminUsers; } private SQLAdmin.Users.Insert mockSQLAdminUsersInsert( SQLAdmin.Users sqlAdminUsers, String instanceName) throws IOException { SQLAdmin.Users.Insert sqlAdminUsersInsert = mock(SQLAdmin.Users.Insert.class); when(sqlAdminUsers.insert(eq(PROJECT_ID), eq(instanceName), any(User.class))).thenReturn(sqlAdminUsersInsert); return sqlAdminUsersInsert; } @Test public void testAllocate_Standard() throws InterruptedException, IOException { // Prepare configuration for resource template. Map<String, String> templateConfig = new HashMap<String, String>(); templateConfig.put(TIER.unwrap().getConfigKey(), TIER_NAME); templateConfig.put(MASTER_USERNAME.unwrap().getConfigKey(), USERNAME); templateConfig.put(MASTER_USER_PASSWORD.unwrap().getConfigKey(), USER_PASSWORD); templateConfig.put(ENGINE.unwrap().getConfigKey(), DATABASE_TYPE); // Create the resource template. GoogleCloudSQLInstanceTemplate template = sqlProvider.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; SQLAdmin.Instances sqlAdminInstances = mockSQLAdminToInstances(); SQLAdmin.Instances.Insert sqlAdminInstancesInsert = mockSQLAdminInstancesInsert(sqlAdminInstances); Operation dbCreationOperation = buildInitialOperation("CREATE_DATABASE", decoratedInstanceName); when(sqlAdminInstancesInsert.execute()).thenReturn(dbCreationOperation); SQLAdmin.Operations sqlAdminOperations = mockSQLAdminToOperations(); SQLAdmin.Operations.Get sqlAdminOperationsGet = mock(SQLAdmin.Operations.Get.class); when(sqlAdminOperations.get(PROJECT_ID, dbCreationOperation.getName())).thenReturn(sqlAdminOperationsGet); when(sqlAdminOperationsGet.execute()).then( new OperationAnswer(dbCreationOperation, new String[]{"PENDING", "RUNNING", "DONE"})); // Configure stub for successful user insertion operation. SQLAdmin.Users sqlAdminUsers = mockSQLAdminUsers(); SQLAdmin.Users.Insert sqlAdminUsersInsert = mockSQLAdminUsersInsert(sqlAdminUsers, decoratedInstanceName); Operation userCreationOperation = buildInitialOperation("CREATE_USER", decoratedInstanceName); when(sqlAdminUsersInsert.execute()).thenReturn(userCreationOperation); SQLAdmin.Operations.Get sqlAdminUserOperationsGet = mock(SQLAdmin.Operations.Get.class); when(sqlAdminOperations.get(PROJECT_ID, userCreationOperation.getName())).thenReturn(sqlAdminUserOperationsGet); when(sqlAdminUserOperationsGet.execute()).then( new OperationAnswer(userCreationOperation, new String[]{"PENDING", "RUNNING", "DONE"})); sqlProvider.allocate(template, Lists.newArrayList(instanceName), 1); // Verify instance insertion call was made. ArgumentCaptor<DatabaseInstance> instanceArgumentCaptor = ArgumentCaptor.forClass(DatabaseInstance.class); verify(sqlAdminInstances).insert(eq(PROJECT_ID), instanceArgumentCaptor.capture()); DatabaseInstance insertedInstance = instanceArgumentCaptor.getValue(); // Verify instance name and metadata. assertThat(insertedInstance.getName()).isEqualTo(decoratedInstanceName); assertEquals(insertedInstance.getRegion(), REGION_NAME); // Verify user insertion call was made. ArgumentCaptor<User> userArgumentCaptor = ArgumentCaptor.forClass(User.class); verify(sqlAdminUsers).insert(eq(PROJECT_ID), eq(decoratedInstanceName), userArgumentCaptor.capture()); User insertedUser = userArgumentCaptor.getValue(); // Verify user name and password. assertThat(insertedUser.getName()).isEqualTo(USERNAME); assertThat(insertedUser.getPassword()).isEqualTo(USER_PASSWORD); } @Test public void testAllocate_CreationFails_BelowMinCount() throws InterruptedException, IOException { // Prepare configuration for resource template. Map<String, String> templateConfig = new HashMap<String, String>(); templateConfig.put(TIER.unwrap().getConfigKey(), TIER_NAME); templateConfig.put(MASTER_USERNAME.unwrap().getConfigKey(), USERNAME); templateConfig.put(MASTER_USER_PASSWORD.unwrap().getConfigKey(), USER_PASSWORD); templateConfig.put(ENGINE.unwrap().getConfigKey(), DATABASE_TYPE); // Create the resource template. GoogleCloudSQLInstanceTemplate template = sqlProvider.createResourceTemplate("template-1", new SimpleConfiguration(templateConfig), new HashMap<String, String>()); // Configure stub for first successful instance insertion operation. String instanceName1 = UUID.randomUUID().toString(); String decoratedInstanceName1 = INSTANCE_NAME_PREFIX.unwrap().getDefaultValue() + "-" + instanceName1; SQLAdmin.Instances sqlAdminInstances = mockSQLAdminToInstances(); SQLAdmin.Instances.Insert sqlAdminInstancesInsert = mockSQLAdminInstancesInsert(sqlAdminInstances); Operation dbCreationOperation1 = buildInitialOperation("CREATE_DATABASE", decoratedInstanceName1); OngoingStubbing<Operation> ongoingInsertionStub = when(sqlAdminInstancesInsert.execute()).thenReturn(dbCreationOperation1); SQLAdmin.Operations sqlAdminOperations = mockSQLAdminToOperations(); SQLAdmin.Operations.Get sqlAdminInstanceOperationsGet1 = mock(SQLAdmin.Operations.Get.class); when(sqlAdminOperations.get(PROJECT_ID, dbCreationOperation1.getName())).thenReturn(sqlAdminInstanceOperationsGet1); when(sqlAdminInstanceOperationsGet1.execute()).then( new OperationAnswer(dbCreationOperation1, 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; Operation dbCreationOperation2 = buildInitialOperation("CREATE_DATABASE", decoratedInstanceName2); ongoingInsertionStub.thenReturn(dbCreationOperation2); SQLAdmin.Operations.Get sqlAdminOperationsGet2 = mock(SQLAdmin.Operations.Get.class); when(sqlAdminOperations.get(PROJECT_ID, dbCreationOperation2.getName())).thenReturn(sqlAdminOperationsGet2); when(sqlAdminOperationsGet2.execute()).then( new OperationAnswer(dbCreationOperation2, new String[]{"PENDING", "RUNNING", "DONE"}, "SOME_ERROR_CODE", "Some error message...")); // Configure stub for first successful user insertion operation. SQLAdmin.Users sqlAdminUsers = mockSQLAdminUsers(); SQLAdmin.Users.Insert sqlAdminUsersInsert1 = mockSQLAdminUsersInsert(sqlAdminUsers, decoratedInstanceName1); Operation userCreationOperation1 = buildInitialOperation("CREATE_USER", decoratedInstanceName1); when(sqlAdminUsersInsert1.execute()).thenReturn(userCreationOperation1); SQLAdmin.Operations.Get sqlAdminUserOperationsGet1 = mock(SQLAdmin.Operations.Get.class); when(sqlAdminOperations.get(PROJECT_ID, userCreationOperation1.getName())).thenReturn(sqlAdminUserOperationsGet1); when(sqlAdminUserOperationsGet1.execute()).then( new OperationAnswer(userCreationOperation1, new String[]{"PENDING", "RUNNING", "DONE"})); // Configure stub for second successful user insertion operation. SQLAdmin.Users.Insert sqlAdminUsersInsert2 = mockSQLAdminUsersInsert(sqlAdminUsers, decoratedInstanceName2); Operation userCreationOperation2 = buildInitialOperation("CREATE_USER", decoratedInstanceName2); when(sqlAdminUsersInsert2.execute()).thenReturn(userCreationOperation2); SQLAdmin.Operations.Get sqlAdminUserOperationsGet2 = mock(SQLAdmin.Operations.Get.class); when(sqlAdminOperations.get(PROJECT_ID, userCreationOperation2.getName())).thenReturn(sqlAdminUserOperationsGet2); when(sqlAdminUserOperationsGet2.execute()).then( new OperationAnswer(userCreationOperation2, new String[]{"PENDING", "RUNNING", "DONE"})); // Configure stub for first successful instance deletion operation. SQLAdmin.Instances.Delete sqlAdminInstancesDelete1 = mockSQLAdminInstancesDelete(sqlAdminInstances, decoratedInstanceName1); Operation dbDeletionOperation1 = buildInitialOperation("DELETE_DATABASE", decoratedInstanceName1); when(sqlAdminInstancesDelete1.execute()).thenReturn(dbDeletionOperation1); SQLAdmin.Operations.Get sqlAdminOperationsGet3 = mock(SQLAdmin.Operations.Get.class); when(sqlAdminOperations.get(PROJECT_ID, dbDeletionOperation1.getName())).thenReturn(sqlAdminOperationsGet3); when(sqlAdminOperationsGet3.execute()).then( new OperationAnswer(dbDeletionOperation1, new String[]{"PENDING", "RUNNING", "DONE"})); // Configure stub for second successful instance deletion operation. SQLAdmin.Instances.Delete sqlAdminInstancesDelete2 = mockSQLAdminInstancesDelete(sqlAdminInstances, decoratedInstanceName2); Operation dbDeletionOperation2 = buildInitialOperation("DELETE_DATABASE", decoratedInstanceName2); when(sqlAdminInstancesDelete2.execute()).thenReturn(dbDeletionOperation2); SQLAdmin.Operations.Get sqlAdminOperationsGet4 = mock(SQLAdmin.Operations.Get.class); when(sqlAdminOperations.get(PROJECT_ID, dbDeletionOperation2.getName())).thenReturn(sqlAdminOperationsGet4); when(sqlAdminOperationsGet4.execute()).then( new OperationAnswer(dbDeletionOperation2, new String[]{"PENDING", "RUNNING", "DONE"})); try { sqlProvider.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<DatabaseInstance> insertArgumentCaptor = ArgumentCaptor.forClass(DatabaseInstance.class); verify(sqlAdminInstances, times(2)).insert(eq(PROJECT_ID), insertArgumentCaptor.capture()); List<DatabaseInstance> insertedInstanceList = insertArgumentCaptor.getAllValues(); DatabaseInstance insertedInstance1 = insertedInstanceList.get(0); // Verify first instance name. assertThat(insertedInstance1.getName()).isEqualTo(decoratedInstanceName1); // Verify second instance insertion call was made. DatabaseInstance insertedInstance2 = insertedInstanceList.get(1); // Verify second instance name and metadata. assertThat(insertedInstance2.getName()).isEqualTo(decoratedInstanceName2); // Verify first instance deletion call instance name. verify(sqlAdminInstances).delete(eq(PROJECT_ID), eq(decoratedInstanceName1)); } @Test public void testAllocate_CreationFails_ReachesMinCount() throws InterruptedException, IOException { // Prepare configuration for resource template. Map<String, String> templateConfig = new HashMap<String, String>(); templateConfig.put(TIER.unwrap().getConfigKey(), TIER_NAME); templateConfig.put(MASTER_USERNAME.unwrap().getConfigKey(), USERNAME); templateConfig.put(MASTER_USER_PASSWORD.unwrap().getConfigKey(), USER_PASSWORD); templateConfig.put(ENGINE.unwrap().getConfigKey(), DATABASE_TYPE); // Create the resource template. GoogleCloudSQLInstanceTemplate template = sqlProvider.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; SQLAdmin.Instances sqlAdminInstances = mockSQLAdminToInstances(); SQLAdmin.Instances.Insert sqlAdminInstancesInsert = mockSQLAdminInstancesInsert(sqlAdminInstances); Operation dbCreationOperation1 = buildInitialOperation("CREATE_DATABASE", decoratedInstanceName1); OngoingStubbing<Operation> ongoingInsertionStub = when(sqlAdminInstancesInsert.execute()).thenReturn(dbCreationOperation1); SQLAdmin.Operations sqlAdminOperations = mockSQLAdminToOperations(); SQLAdmin.Operations.Get sqlAdminOperationsGet1 = mock(SQLAdmin.Operations.Get.class); when(sqlAdminOperations.get(PROJECT_ID, dbCreationOperation1.getName())).thenReturn(sqlAdminOperationsGet1); when(sqlAdminOperationsGet1.execute()).then( new OperationAnswer(dbCreationOperation1, 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; Operation dbCreationOperation2 = buildInitialOperation("CREATE_DATABASE", decoratedInstanceName2); ongoingInsertionStub.thenReturn(dbCreationOperation2); SQLAdmin.Operations.Get sqlAdminOperationsGet2 = mock(SQLAdmin.Operations.Get.class); when(sqlAdminOperations.get(PROJECT_ID, dbCreationOperation2.getName())).thenReturn(sqlAdminOperationsGet2); when(sqlAdminOperationsGet2.execute()).then( new OperationAnswer(dbCreationOperation2, new String[]{"PENDING", "RUNNING", "DONE"}, "SOME_ERROR_CODE", "Some error message...")); // Configure stub for first successful user insertion operation. SQLAdmin.Users sqlAdminUsers = mockSQLAdminUsers(); SQLAdmin.Users.Insert sqlAdminUsersInsert1 = mockSQLAdminUsersInsert(sqlAdminUsers, decoratedInstanceName1); Operation userCreationOperation1 = buildInitialOperation("CREATE_USER", decoratedInstanceName1); when(sqlAdminUsersInsert1.execute()).thenReturn(userCreationOperation1); SQLAdmin.Operations.Get sqlAdminUserOperationsGet1 = mock(SQLAdmin.Operations.Get.class); when(sqlAdminOperations.get(PROJECT_ID, userCreationOperation1.getName())).thenReturn(sqlAdminUserOperationsGet1); when(sqlAdminUserOperationsGet1.execute()).then( new OperationAnswer(userCreationOperation1, new String[]{"PENDING", "RUNNING", "DONE"})); // Configure stub for second successful user insertion operation. SQLAdmin.Users.Insert sqlAdminUsersInsert2 = mockSQLAdminUsersInsert(sqlAdminUsers, decoratedInstanceName2); Operation userCreationOperation2 = buildInitialOperation("CREATE_USER", decoratedInstanceName2); when(sqlAdminUsersInsert2.execute()).thenReturn(userCreationOperation2); SQLAdmin.Operations.Get sqlAdminUserOperationsGet2 = mock(SQLAdmin.Operations.Get.class); when(sqlAdminOperations.get(PROJECT_ID, userCreationOperation2.getName())).thenReturn(sqlAdminUserOperationsGet2); when(sqlAdminUserOperationsGet2.execute()).then( new OperationAnswer(userCreationOperation2, new String[]{"PENDING", "RUNNING", "DONE"})); sqlProvider.allocate(template, Lists.newArrayList(instanceName1, instanceName2), 1); // Verify first instance insertion call was made. ArgumentCaptor<DatabaseInstance> insertArgumentCaptor = ArgumentCaptor.forClass(DatabaseInstance.class); verify(sqlAdminInstances, times(2)).insert(eq(PROJECT_ID), insertArgumentCaptor.capture()); List<DatabaseInstance> insertedInstanceList = insertArgumentCaptor.getAllValues(); DatabaseInstance insertedInstance1 = insertedInstanceList.get(0); // Verify first instance name. assertThat(insertedInstance1.getName()).isEqualTo(decoratedInstanceName1); // Verify second instance insertion call was made. DatabaseInstance insertedInstance2 = insertedInstanceList.get(1); // Verify second instance name. assertThat(insertedInstance2.getName()).isEqualTo(decoratedInstanceName2); // NPE would be thrown (due to lack of mocks) if the Google Cloud SQL provider attempted actual deletion calls // against Google Cloud SQL. If no NPE's are thrown, the test is a success. } @Test public void testAllocate_CreationFails_UnsuccessfulUserInsertion_BelowMinCount() throws InterruptedException, IOException { // Prepare configuration for resource template. Map<String, String> templateConfig = new HashMap<String, String>(); templateConfig.put(TIER.unwrap().getConfigKey(), TIER_NAME); templateConfig.put(MASTER_USERNAME.unwrap().getConfigKey(), USERNAME); templateConfig.put(MASTER_USER_PASSWORD.unwrap().getConfigKey(), USER_PASSWORD); templateConfig.put(ENGINE.unwrap().getConfigKey(), DATABASE_TYPE); // Create the resource template. GoogleCloudSQLInstanceTemplate template = sqlProvider.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; SQLAdmin.Instances sqlAdminInstances = mockSQLAdminToInstances(); SQLAdmin.Instances.Insert sqlAdminInstancesInsert = mockSQLAdminInstancesInsert(sqlAdminInstances); Operation dbCreationOperation = buildInitialOperation("CREATE_DATABASE", decoratedInstanceName); when(sqlAdminInstancesInsert.execute()).thenReturn(dbCreationOperation); SQLAdmin.Operations sqlAdminOperations = mockSQLAdminToOperations(); SQLAdmin.Operations.Get sqlAdminOperationsGet1 = mock(SQLAdmin.Operations.Get.class); when(sqlAdminOperations.get(PROJECT_ID, dbCreationOperation.getName())).thenReturn(sqlAdminOperationsGet1); when(sqlAdminOperationsGet1.execute()).then( new OperationAnswer(dbCreationOperation, new String[]{"PENDING", "RUNNING", "DONE"})); // Configure stub for unsuccessful user insertion operation. SQLAdmin.Users sqlAdminUsers = mockSQLAdminUsers(); SQLAdmin.Users.Insert sqlAdminUsersInsert = mockSQLAdminUsersInsert(sqlAdminUsers, decoratedInstanceName); Operation userCreationOperation = buildInitialOperation("CREATE_DATABASE", decoratedInstanceName); when(sqlAdminUsersInsert.execute()).thenReturn(userCreationOperation); SQLAdmin.Operations.Get sqlAdminUserOperationsGet = mock(SQLAdmin.Operations.Get.class); when(sqlAdminOperations.get(PROJECT_ID, userCreationOperation.getName())).thenReturn(sqlAdminUserOperationsGet); when(sqlAdminUserOperationsGet.execute()).then( new OperationAnswer(userCreationOperation, new String[]{"PENDING", "RUNNING", "DONE"}, "SOME_ERROR_CODE", "Some error message...")); // Configure stub for first successful instance deletion operation. SQLAdmin.Instances.Delete sqlAdminInstancesDelete = mockSQLAdminInstancesDelete(sqlAdminInstances, decoratedInstanceName); Operation dbDeletionOperation = buildInitialOperation("DELETE_DATABASE", decoratedInstanceName); when(sqlAdminInstancesDelete.execute()).thenReturn(dbDeletionOperation); SQLAdmin.Operations.Get sqlAdminOperationsGet3 = mock(SQLAdmin.Operations.Get.class); when(sqlAdminOperations.get(PROJECT_ID, dbDeletionOperation.getName())).thenReturn(sqlAdminOperationsGet3); when(sqlAdminOperationsGet3.execute()).then( new OperationAnswer(dbDeletionOperation, new String[]{"PENDING", "RUNNING", "DONE"})); try { sqlProvider.allocate(template, Lists.newArrayList(instanceName), 1); 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 deletion call instance name. verify(sqlAdminInstances).delete(eq(PROJECT_ID), eq(decoratedInstanceName)); } @Test public void testAllocate_PreExistingInstance() throws InterruptedException, IOException { // Prepare configuration for resource template. Map<String, String> templateConfig = new HashMap<String, String>(); templateConfig.put(TIER.unwrap().getConfigKey(), TIER_NAME); templateConfig.put(MASTER_USERNAME.unwrap().getConfigKey(), USERNAME); templateConfig.put(MASTER_USER_PASSWORD.unwrap().getConfigKey(), USER_PASSWORD); templateConfig.put(ENGINE.unwrap().getConfigKey(), DATABASE_TYPE); // Create the resource template. GoogleCloudSQLInstanceTemplate template = sqlProvider.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; SQLAdmin.Instances sqlAdminInstances = mockSQLAdminToInstances(); SQLAdmin.Instances.Insert sqlAdminInstancesInsert = mockSQLAdminInstancesInsert(sqlAdminInstances); GoogleJsonResponseException exception = GoogleJsonResponseExceptionFactoryTesting.newMock(new MockJsonFactory(), 409, "already exists"); when(sqlAdminInstancesInsert.execute()).thenThrow(exception); // Configure stub for successful user insertion operation. SQLAdmin.Users sqlAdminUsers = mockSQLAdminUsers(); SQLAdmin.Users.Insert sqlAdminUsersInsert = mockSQLAdminUsersInsert(sqlAdminUsers, decoratedInstanceName); Operation userCreationOperation = buildInitialOperation("CREATE_USER", decoratedInstanceName); when(sqlAdminUsersInsert.execute()).thenReturn(userCreationOperation); SQLAdmin.Operations.Get sqlAdminOperationsGet2 = mock(SQLAdmin.Operations.Get.class); SQLAdmin.Operations sqlAdminOperations = mockSQLAdminToOperations(); when(sqlAdminOperations.get(PROJECT_ID, userCreationOperation.getName())).thenReturn(sqlAdminOperationsGet2); when(sqlAdminOperationsGet2.execute()).then( new OperationAnswer(userCreationOperation, new String[]{"PENDING", "RUNNING", "DONE"})); sqlProvider.allocate(template, Lists.newArrayList(instanceName), 1); // Verify user insertion call was made. ArgumentCaptor<User> userArgumentCaptor = ArgumentCaptor.forClass(User.class); verify(sqlAdminUsers).insert(eq(PROJECT_ID), eq(decoratedInstanceName), userArgumentCaptor.capture()); User insertedUser = userArgumentCaptor.getValue(); // Verify user name and password. assertThat(insertedUser.getName()).isEqualTo(USERNAME); assertThat(insertedUser.getPassword()).isEqualTo(USER_PASSWORD); } @Test public void testFind() throws InterruptedException, IOException { // Prepare configuration for resource template. Map<String, String> templateConfig = new HashMap<String, String>(); templateConfig.put(TIER.unwrap().getConfigKey(), TIER_NAME); templateConfig.put(MASTER_USERNAME.unwrap().getConfigKey(), USERNAME); templateConfig.put(MASTER_USER_PASSWORD.unwrap().getConfigKey(), USER_PASSWORD); templateConfig.put(ENGINE.unwrap().getConfigKey(), DATABASE_TYPE); // Create the resource template. GoogleCloudSQLInstanceTemplate template = sqlProvider.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. SQLAdmin.Instances sqlAdminInstances = mockSQLAdminToInstances(); SQLAdmin.Instances.Get sqlAdminInstancesGet1 = mockSQLAdminInstancesGet(sqlAdminInstances, decoratedInstanceName1); DatabaseInstance instance1 = new DatabaseInstance(); // Configure network interface. IpMapping ipMapping1 = new IpMapping(); ipMapping1.setIpAddress("1.2.3.4"); List<IpMapping> ipMappingList1 = Lists.newArrayList(ipMapping1); instance1.setIpAddresses(ipMappingList1); when(sqlAdminInstancesGet1.execute()).thenReturn(instance1); // Configure stub for second successful instance retrieval. SQLAdmin.Instances.Get sqlAdminInstancesGet2 = mockSQLAdminInstancesGet(sqlAdminInstances, decoratedInstanceName2); DatabaseInstance instance2 = new DatabaseInstance(); // Configure network interface. IpMapping ipMapping2 = new IpMapping(); ipMapping2.setIpAddress("5.6.7.8"); List<IpMapping> ipMappingList2 = Lists.newArrayList(ipMapping2); instance2.setIpAddresses(ipMappingList2); when(sqlAdminInstancesGet2.execute()).thenReturn(instance2); Collection<GoogleCloudSQLInstance> foundInstances = sqlProvider.find(template, instanceIds); // Verify that both of the two requested instances were returned. assertThat(foundInstances.size()).isEqualTo(instanceIds.size()); // Verify the properties of the first returned instance. Iterator<GoogleCloudSQLInstance> instanceIterator = foundInstances.iterator(); GoogleCloudSQLInstance foundInstance1 = instanceIterator.next(); assertThat(foundInstance1.getId()).isEqualTo(instanceName1); // Verify the properties of the second returned instance. GoogleCloudSQLInstance foundInstance2 = instanceIterator.next(); assertThat(foundInstance2.getId()).isEqualTo(instanceName2); } @Test public void testFind_PartialSuccess() throws InterruptedException, IOException { // Prepare configuration for resource template. Map<String, String> templateConfig = new HashMap<String, String>(); templateConfig.put(MASTER_USERNAME.unwrap().getConfigKey(), USERNAME); templateConfig.put(MASTER_USER_PASSWORD.unwrap().getConfigKey(), USER_PASSWORD); templateConfig.put(ENGINE.unwrap().getConfigKey(), DATABASE_TYPE); // Create the resource template. GoogleCloudSQLInstanceTemplate template = sqlProvider.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; String instanceName3 = UUID.randomUUID().toString(); String decoratedInstanceName3 = INSTANCE_NAME_PREFIX.unwrap().getDefaultValue() + "-" + instanceName3; List<String> instanceIds = Lists.newArrayList(instanceName1, instanceName2, instanceName3); // Configure stub for successful instance retrieval. SQLAdmin.Instances sqlAdminInstances = mockSQLAdminToInstances(); SQLAdmin.Instances.Get sqlAdminInstancesGet1 = mockSQLAdminInstancesGet(sqlAdminInstances, decoratedInstanceName1); DatabaseInstance instance1 = new DatabaseInstance(); // Configure network interface. IpMapping ipMapping1 = new IpMapping(); ipMapping1.setIpAddress("1.2.3.4"); List<IpMapping> ipMappingList1 = Lists.newArrayList(ipMapping1); instance1.setIpAddresses(ipMappingList1); when(sqlAdminInstancesGet1.execute()).thenReturn(instance1); // Configure stub for unsuccessful instance retrieval (throws 404). SQLAdmin.Instances.Get sqlAdminInstancesGet2 = mockSQLAdminInstancesGet(sqlAdminInstances, decoratedInstanceName2); GoogleJsonResponseException exception = GoogleJsonResponseExceptionFactoryTesting.newMock(new MockJsonFactory(), 403, "not authorized"); when(sqlAdminInstancesGet2.execute()).thenThrow(exception); // Configure stub for unsuccessful instance retrieval (throws 404). SQLAdmin.Instances.Get sqlAdminInstancesGet3 = mockSQLAdminInstancesGet(sqlAdminInstances, decoratedInstanceName3); GoogleJsonResponseException exception2 = GoogleJsonResponseExceptionFactoryTesting.newMock(new MockJsonFactory(), 404, "not found"); when(sqlAdminInstancesGet3.execute()).thenThrow(exception2); Collection<GoogleCloudSQLInstance> foundInstances = sqlProvider.find(template, instanceIds); // Verify that exactly one of the three requested instances was returned. assertThat(foundInstances.size()).isEqualTo(1); // Verify the properties of the returned instance. GoogleCloudSQLInstance foundInstance = foundInstances.iterator().next(); assertThat(foundInstance.getId()).isEqualTo(instanceName1); } @Test public void testFind_InvalidPrefix() throws InterruptedException, IOException { // Prepare configuration for resource template. Map<String, String> templateConfig = new HashMap<String, String>(); templateConfig.put(MASTER_USERNAME.unwrap().getConfigKey(), USERNAME); templateConfig.put(MASTER_USER_PASSWORD.unwrap().getConfigKey(), USER_PASSWORD); templateConfig.put(ENGINE.unwrap().getConfigKey(), DATABASE_TYPE); templateConfig.put(INSTANCE_NAME_PREFIX.unwrap().getConfigKey(), INVALID_INSTANCE_NAME_PREFIX); // Create the resource template. GoogleCloudSQLInstanceTemplate template = sqlProvider.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 Google Cloud SQL provider attempted actual calls against Google // Cloud SQL. When the instance name prefix is deemed invalid, no calls are attempted against Google Cloud SQL. Collection<GoogleCloudSQLInstance> foundInstances = sqlProvider.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(MASTER_USERNAME.unwrap().getConfigKey(), USERNAME); templateConfig.put(MASTER_USER_PASSWORD.unwrap().getConfigKey(), USER_PASSWORD); templateConfig.put(ENGINE.unwrap().getConfigKey(), DATABASE_TYPE); // Create the resource template. GoogleCloudSQLInstanceTemplate template = sqlProvider.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. SQLAdmin.Instances sqlAdminInstances = mockSQLAdminToInstances(); SQLAdmin.Instances.Get sqlAdminInstancesGet1 = mockSQLAdminInstancesGet(sqlAdminInstances, decoratedInstanceName1); DatabaseInstance instance1 = new DatabaseInstance(); instance1.setState("PENDING_CREATE"); when(sqlAdminInstancesGet1.execute()).thenReturn(instance1); // Configure stub for second successful instance retrieval. SQLAdmin.Instances.Get sqlAdminInstancesGet2 = mockSQLAdminInstancesGet(sqlAdminInstances, decoratedInstanceName2); DatabaseInstance instance2 = new DatabaseInstance(); instance2.setState("RUNNABLE"); when(sqlAdminInstancesGet2.execute()).thenReturn(instance2); Map<String, InstanceState> instanceStates = sqlProvider.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(MASTER_USERNAME.unwrap().getConfigKey(), USERNAME); templateConfig.put(MASTER_USER_PASSWORD.unwrap().getConfigKey(), USER_PASSWORD); templateConfig.put(ENGINE.unwrap().getConfigKey(), DATABASE_TYPE); // Create the resource template. GoogleCloudSQLInstanceTemplate template = sqlProvider.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; String instanceName3 = UUID.randomUUID().toString(); String decoratedInstanceName3 = INSTANCE_NAME_PREFIX.unwrap().getDefaultValue() + "-" + instanceName3; List<String> instanceIds = Lists.newArrayList(instanceName1, instanceName2, instanceName3); // Configure stub for successful instance retrieval. SQLAdmin.Instances sqlAdminInstances = mockSQLAdminToInstances(); SQLAdmin.Instances.Get sqlAdminInstancesGet1 = mockSQLAdminInstancesGet(sqlAdminInstances, decoratedInstanceName1); DatabaseInstance instance1 = new DatabaseInstance(); instance1.setState("PENDING_CREATE"); when(sqlAdminInstancesGet1.execute()).thenReturn(instance1); // Configure stub for unsuccessful instance retrieval (throws 403). SQLAdmin.Instances.Get sqlAdminInstancesGet2 = mockSQLAdminInstancesGet(sqlAdminInstances, decoratedInstanceName2); GoogleJsonResponseException exception = GoogleJsonResponseExceptionFactoryTesting.newMock(new MockJsonFactory(), 403, "not authorized"); when(sqlAdminInstancesGet2.execute()).thenThrow(exception); // Configure stub for unsuccessful instance retrieval (throws 404). SQLAdmin.Instances.Get sqlAdminInstancesGet3 = mockSQLAdminInstancesGet(sqlAdminInstances, decoratedInstanceName3); GoogleJsonResponseException exception2 = GoogleJsonResponseExceptionFactoryTesting.newMock(new MockJsonFactory(), 404, "not found"); when(sqlAdminInstancesGet3.execute()).thenThrow(exception2); Map<String, InstanceState> instanceStates = sqlProvider.getInstanceState(template, instanceIds); // Verify that the states of all instances were returned. assertThat(instanceStates.size()).isEqualTo(instanceIds.size()); // 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); // Verify the state of the third instance. InstanceState instanceState3 = instanceStates.get(instanceName3); assertThat(instanceState3.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(MASTER_USERNAME.unwrap().getConfigKey(), USERNAME); templateConfig.put(MASTER_USER_PASSWORD.unwrap().getConfigKey(), USER_PASSWORD); templateConfig.put(ENGINE.unwrap().getConfigKey(), DATABASE_TYPE); templateConfig.put(INSTANCE_NAME_PREFIX.unwrap().getConfigKey(), INVALID_INSTANCE_NAME_PREFIX); // Create the resource template. GoogleCloudSQLInstanceTemplate template = sqlProvider.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 Google Cloud SQL provider attempted actual calls against Google // Cloud SQL. When the instance name prefix is deemed invalid, no calls are attempted against Google Cloud SQL. Map<String, InstanceState> instanceStates = sqlProvider.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(MASTER_USERNAME.unwrap().getConfigKey(), USERNAME); templateConfig.put(MASTER_USER_PASSWORD.unwrap().getConfigKey(), USER_PASSWORD); templateConfig.put(ENGINE.unwrap().getConfigKey(), DATABASE_TYPE); // Create the resource template. GoogleCloudSQLInstanceTemplate template = sqlProvider.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 deletion operation. SQLAdmin.Instances sqlAdminInstances = mockSQLAdminToInstances(); SQLAdmin.Instances.Delete sqlAdminInstancesDelete1 = mockSQLAdminInstancesDelete(sqlAdminInstances, decoratedInstanceName1); Operation dbDeletionOperation1 = buildInitialOperation("DELETE_DATABASE", decoratedInstanceName1); when(sqlAdminInstancesDelete1.execute()).thenReturn(dbDeletionOperation1); SQLAdmin.Operations sqlAdminOperations = mockSQLAdminToOperations(); SQLAdmin.Operations.Get sqlAdminOperationsGet1 = mock(SQLAdmin.Operations.Get.class); when(sqlAdminOperations.get(PROJECT_ID, dbDeletionOperation1.getName())).thenReturn(sqlAdminOperationsGet1); when(sqlAdminOperationsGet1.execute()).then( new OperationAnswer(dbDeletionOperation1, new String[]{"PENDING", "RUNNING", "DONE"})); // Configure stub for successful instance deletion operation. SQLAdmin.Instances.Delete sqlAdminInstancesDelete2 = mockSQLAdminInstancesDelete(sqlAdminInstances, decoratedInstanceName2); Operation dbDeletionOperation2 = buildInitialOperation("DELETE_DATABASE", decoratedInstanceName2); when(sqlAdminInstancesDelete2.execute()).thenReturn(dbDeletionOperation2); SQLAdmin.Operations.Get sqlAdminOperationsGet2 = mock(SQLAdmin.Operations.Get.class); when(sqlAdminOperations.get(PROJECT_ID, dbDeletionOperation2.getName())).thenReturn(sqlAdminOperationsGet2); when(sqlAdminOperationsGet2.execute()).then( new OperationAnswer(dbDeletionOperation2, new String[]{"PENDING", "RUNNING", "DONE"})); sqlProvider.delete(template, instanceIds); // Verify first instance deletion call was made. verify(sqlAdminInstances).delete(eq(PROJECT_ID), eq(decoratedInstanceName1)); // Verify second instance deletion call was made. verify(sqlAdminInstances).delete(eq(PROJECT_ID), eq(decoratedInstanceName2)); } @Test public void testDelete_409StatusCode() throws InterruptedException, IOException { // Prepare configuration for resource template. Map<String, String> templateConfig = new HashMap<String, String>(); templateConfig.put(MASTER_USERNAME.unwrap().getConfigKey(), USERNAME); templateConfig.put(MASTER_USER_PASSWORD.unwrap().getConfigKey(), USER_PASSWORD); templateConfig.put(ENGINE.unwrap().getConfigKey(), DATABASE_TYPE); // Create the resource template. GoogleCloudSQLInstanceTemplate template = sqlProvider.createResourceTemplate("template-1", new SimpleConfiguration(templateConfig), new HashMap<String, String>()); String instanceName = UUID.randomUUID().toString(); String decoratedInstanceName = INSTANCE_NAME_PREFIX.unwrap().getDefaultValue() + "-" + instanceName; List<String> instanceIds = Lists.newArrayList(instanceName); // Configure stub for unsuccessful instance deletion operation. SQLAdmin.Instances sqlAdminInstances = mockSQLAdminToInstances(); SQLAdmin.Instances.Delete sqlAdminInstancesDelete = mockSQLAdminInstancesDelete(sqlAdminInstances, decoratedInstanceName); GoogleJsonResponseException exception = GoogleJsonResponseExceptionFactoryTesting.newMock(new MockJsonFactory(), 409, "deleting"); when(sqlAdminInstancesDelete.execute()).thenThrow(exception); sqlProvider.delete(template, instanceIds); // Verify instance deletion call was made. verify(sqlAdminInstances).delete(eq(PROJECT_ID), eq(decoratedInstanceName)); } @Test public void testDelete_PartialSuccess() throws InterruptedException, IOException { // Prepare configuration for resource template. Map<String, String> templateConfig = new HashMap<String, String>(); templateConfig.put(MASTER_USERNAME.unwrap().getConfigKey(), USERNAME); templateConfig.put(MASTER_USER_PASSWORD.unwrap().getConfigKey(), USER_PASSWORD); templateConfig.put(ENGINE.unwrap().getConfigKey(), DATABASE_TYPE); // Create the resource template. GoogleCloudSQLInstanceTemplate template = sqlProvider.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 deletion operation. SQLAdmin.Instances sqlAdminInstances = mockSQLAdminToInstances(); SQLAdmin.Instances.Delete sqlAdminInstancesDelete1 = mockSQLAdminInstancesDelete(sqlAdminInstances, decoratedInstanceName1); Operation dbDeletionOperation1 = buildInitialOperation("DELETE_DATABASE", decoratedInstanceName1); when(sqlAdminInstancesDelete1.execute()).thenReturn(dbDeletionOperation1); SQLAdmin.Operations sqlAdminOperations = mockSQLAdminToOperations(); SQLAdmin.Operations.Get sqlAdminOperationsGet1 = mock(SQLAdmin.Operations.Get.class); when(sqlAdminOperations.get(PROJECT_ID, dbDeletionOperation1.getName())).thenReturn(sqlAdminOperationsGet1); when(sqlAdminOperationsGet1.execute()).then( new OperationAnswer(dbDeletionOperation1, new String[]{"PENDING", "RUNNING", "DONE"})); // Configure stub for unsuccessful instance deletion operation. SQLAdmin.Instances.Delete sqlAdminInstancesDelete2 = mockSQLAdminInstancesDelete(sqlAdminInstances, decoratedInstanceName2); GoogleJsonResponseException exception = GoogleJsonResponseExceptionFactoryTesting.newMock(new MockJsonFactory(), 404, "not found"); when(sqlAdminInstancesDelete2.execute()).thenThrow(exception); sqlProvider.delete(template, instanceIds); // Verify first instance deletion call was made. verify(sqlAdminInstances).delete(eq(PROJECT_ID), eq(decoratedInstanceName1)); // Verify second instance deletion call was made. verify(sqlAdminInstances).delete(eq(PROJECT_ID), 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(MASTER_USERNAME.unwrap().getConfigKey(), USERNAME); templateConfig.put(MASTER_USER_PASSWORD.unwrap().getConfigKey(), USER_PASSWORD); templateConfig.put(ENGINE.unwrap().getConfigKey(), DATABASE_TYPE); templateConfig.put(INSTANCE_NAME_PREFIX.unwrap().getConfigKey(), INVALID_INSTANCE_NAME_PREFIX); // Create the resource template. GoogleCloudSQLInstanceTemplate template = sqlProvider.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 Google Cloud SQL provider attempted actual calls against Google // Cloud SQL. When the instance name prefix is deemed invalid, no calls are attempted against Google Cloud SQL. // If no NPE's are thrown, the test is a success. sqlProvider.delete(template, instanceIds); } private static Operation buildInitialOperation(String operationType, String targetId) { return buildOperation(UUID.randomUUID().toString(), operationType, targetId, "PENDING"); } private static Operation buildOperation(String operationName, String operationType, String targetId, String status) { Operation operation = new Operation(); operation.setName(operationName); operation.setOperationType(operationType); operation.setStatus(status); operation.setTargetId(targetId); return operation; } /** * Used to return operations with a series of statuses in response to the sql 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(subjectOperation.getName(), subjectOperation.getOperationType(), subjectOperation.getTargetId(), statusQueue.remove()); if (polledOperation.getStatus().equals("DONE") && errorCode != null) { OperationError errors = new OperationError(); errors.setCode(errorCode); errors.setMessage(errorMessage); List<OperationError> errorsList = Lists.newArrayList(errors); OperationErrors error = new OperationErrors(); error.setErrors(errorsList); polledOperation.setError(error); } return polledOperation; } } /** * 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 contains 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); } }