package com.sequenceiq.cloudbreak.service.cluster; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; import static java.util.Collections.singletonMap; import static org.mockito.BDDMockito.given; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.doReturn; 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 java.net.ConnectException; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.Spy; import org.mockito.runners.MockitoJUnitRunner; import com.sequenceiq.ambari.client.AmbariClient; import com.sequenceiq.cloudbreak.TestUtil; import com.sequenceiq.cloudbreak.api.model.ClusterRequest; import com.sequenceiq.cloudbreak.api.model.ClusterResponse; import com.sequenceiq.cloudbreak.api.model.HostGroupAdjustmentJson; import com.sequenceiq.cloudbreak.api.model.Status; import com.sequenceiq.cloudbreak.api.model.StatusRequest; import com.sequenceiq.cloudbreak.cloud.scheduler.PollGroup; import com.sequenceiq.cloudbreak.controller.BadRequestException; import com.sequenceiq.cloudbreak.converter.scheduler.StatusToPollGroupConverter; import com.sequenceiq.cloudbreak.core.CloudbreakSecuritySetupException; import com.sequenceiq.cloudbreak.core.flow2.service.ReactorFlowManager; import com.sequenceiq.cloudbreak.domain.Cluster; import com.sequenceiq.cloudbreak.domain.HostGroup; import com.sequenceiq.cloudbreak.domain.HostMetadata; import com.sequenceiq.cloudbreak.domain.InstanceMetaData; import com.sequenceiq.cloudbreak.domain.Stack; import com.sequenceiq.cloudbreak.repository.ClusterRepository; import com.sequenceiq.cloudbreak.repository.InstanceMetaDataRepository; import com.sequenceiq.cloudbreak.service.TlsSecurityService; import com.sequenceiq.cloudbreak.service.hostgroup.HostGroupService; import com.sequenceiq.cloudbreak.service.stack.StackService; import com.sequenceiq.cloudbreak.client.HttpClientConfig; import groovyx.net.http.HttpResponseException; @RunWith(MockitoJUnitRunner.class) public class AmbariClusterHostServiceTypeTest { @Mock private StackService stackService; @Mock private ClusterRepository clusterRepository; @Mock private AmbariClient ambariClient; @Mock private AmbariClientProvider ambariClientProvider; @Mock private InstanceMetaDataRepository instanceMetadataRepository; @Mock private TlsSecurityService tlsSecurityService; @Mock private ReactorFlowManager flowManager; @Mock private HostGroupService hostGroupService; @Mock private StatusToPollGroupConverter statusToPollGroupConverter; @InjectMocks @Spy private AmbariClusterService underTest = new AmbariClusterService(); private Stack stack; private ClusterRequest clusterRequest; private ClusterResponse clusterResponse; private Cluster cluster; @Before public void setUp() throws CloudbreakSecuritySetupException { stack = TestUtil.stack(); cluster = TestUtil.cluster(TestUtil.blueprint(), stack, 1L); stack.setCluster(cluster); clusterRequest = new ClusterRequest(); clusterResponse = new ClusterResponse(); when(stackService.get(anyLong())).thenReturn(stack); when(stackService.getById(anyLong())).thenReturn(stack); when(stackService.findLazy(anyLong())).thenReturn(stack); when(clusterRepository.save(any(Cluster.class))).thenReturn(cluster); given(tlsSecurityService.buildTLSClientConfigForPrimaryGateway(anyLong(), anyString())).willReturn(new HttpClientConfig("", 8443, "/tmp", "/tmp")); } @Test(expected = BadRequestException.class) public void testStopWhenAwsHasEphemeralVolume() { cluster = TestUtil.cluster(TestUtil.blueprint(), TestUtil.stack(Status.AVAILABLE, TestUtil.awsCredential()), 1L); cluster.getStack().setCloudPlatform("AWS"); stack = TestUtil.setEphemeral(cluster.getStack()); cluster.setStatus(Status.AVAILABLE); cluster.setStack(stack); stack.setCluster(cluster); when(stackService.get(anyLong())).thenReturn(stack); when(stackService.getById(anyLong())).thenReturn(stack); underTest.updateStatus(1L, StatusRequest.STOPPED); } @Test(expected = BadRequestException.class) public void testStopWhenAwsHasSpotInstances() { cluster = TestUtil.cluster(TestUtil.blueprint(), TestUtil.stack(Status.AVAILABLE, TestUtil.awsCredential()), 1L); cluster.getStack().setCloudPlatform("AWS"); stack = TestUtil.setSpotInstances(cluster.getStack()); cluster.setStatus(Status.AVAILABLE); cluster.setStack(stack); stack.setCluster(cluster); when(stackService.get(anyLong())).thenReturn(stack); when(stackService.getById(anyLong())).thenReturn(stack); underTest.updateStatus(1L, StatusRequest.STOPPED); } @Test(expected = BadRequestException.class) public void testRetrieveClusterJsonWhenClusterJsonIsNull() throws HttpResponseException { // GIVEN doReturn(ambariClient).when(ambariClientProvider).getAmbariClient(any(HttpClientConfig.class), anyInt(), any(Cluster.class)); given(ambariClient.getClusterAsJson()).willReturn(null); // WHEN underTest.getClusterJson("123.12.3.4", 1L); } @Test(expected = BadRequestException.class) public void testUpdateHostsDoesntAcceptZeroScalingAdjustments() throws Exception { // GIVEN HostGroupAdjustmentJson hga1 = new HostGroupAdjustmentJson(); hga1.setHostGroup("slave_1"); hga1.setScalingAdjustment(0); // WHEN underTest.updateHosts(stack.getId(), hga1); } @Test(expected = BadRequestException.class) public void testUpdateHostsDoesntAcceptScalingAdjustmentsWithDifferentSigns() throws Exception { // GIVEN HostGroupAdjustmentJson hga1 = new HostGroupAdjustmentJson(); hga1.setHostGroup("slave_1"); hga1.setScalingAdjustment(-2); // WHEN underTest.updateHosts(stack.getId(), hga1); } @Test public void testUpdateHostsForDownscaleFilterAllHosts() throws ConnectException, CloudbreakSecuritySetupException { HostGroupAdjustmentJson json = new HostGroupAdjustmentJson(); json.setHostGroup("slave_1"); json.setScalingAdjustment(-1); AmbariClient ambariClient = mock(AmbariClient.class); HostMetadata metadata1 = mock(HostMetadata.class); HostMetadata metadata2 = mock(HostMetadata.class); HostMetadata metadata3 = mock(HostMetadata.class); Set<HostMetadata> hostsMetaData = new HashSet<>(); hostsMetaData.addAll(asList(metadata1, metadata2, metadata3)); HostGroup hostGroup = new HostGroup(); hostGroup.setHostMetadata(hostsMetaData); hostGroup.setName("slave_1"); when(ambariClientProvider.getAmbariClient(any(HttpClientConfig.class), anyInt(), any(Cluster.class))).thenReturn(ambariClient); when(ambariClient.getComponentsCategory("multi-node-yarn", "slave_1")).thenReturn(singletonMap("DATANODE", "SLAVE")); when(hostGroupService.getByClusterIdAndName(anyLong(), anyString())).thenReturn(hostGroup); when(statusToPollGroupConverter.convert(Mockito.any(Status.class))).thenReturn(PollGroup.POLLABLE); underTest.updateHosts(stack.getId(), json); } @Test public void testUpdateHostsForDownscaleCannotGoBelowReplication() throws ConnectException, CloudbreakSecuritySetupException { HostGroupAdjustmentJson json = new HostGroupAdjustmentJson(); json.setHostGroup("slave_1"); json.setScalingAdjustment(-1); AmbariClient ambariClient = mock(AmbariClient.class); HostMetadata metadata1 = mock(HostMetadata.class); HostMetadata metadata2 = mock(HostMetadata.class); HostMetadata metadata3 = mock(HostMetadata.class); Set<HostMetadata> hostsMetaData = new HashSet<>(); List<HostMetadata> hostsMetadataList = asList(metadata1, metadata2, metadata3); hostsMetaData.addAll(hostsMetadataList); HostGroup hostGroup = new HostGroup(); hostGroup.setHostMetadata(hostsMetaData); hostGroup.setName("slave_1"); when(ambariClientProvider.getAmbariClient(any(HttpClientConfig.class), anyInt(), any(Cluster.class))).thenReturn(ambariClient); when(ambariClient.getComponentsCategory("multi-node-yarn", "slave_1")).thenReturn(singletonMap("DATANODE", "SLAVE")); when(hostGroupService.getByClusterIdAndName(anyLong(), anyString())).thenReturn(hostGroup); when(statusToPollGroupConverter.convert(Mockito.any(Status.class))).thenReturn(PollGroup.POLLABLE); underTest.updateHosts(stack.getId(), json); verify(flowManager, times(1)).triggerClusterDownscale(stack.getId(), json); } @Test public void testUpdateHostsForDownscaleFilterOneHost() throws ConnectException, CloudbreakSecuritySetupException { HostGroupAdjustmentJson json = new HostGroupAdjustmentJson(); json.setHostGroup("slave_1"); json.setScalingAdjustment(-1); AmbariClient ambariClient = mock(AmbariClient.class); HostMetadata metadata1 = mock(HostMetadata.class); InstanceMetaData instanceMetaData1 = mock(InstanceMetaData.class); HostMetadata metadata2 = mock(HostMetadata.class); InstanceMetaData instanceMetaData2 = mock(InstanceMetaData.class); HostMetadata metadata3 = mock(HostMetadata.class); InstanceMetaData instanceMetaData3 = mock(InstanceMetaData.class); HostMetadata metadata4 = mock(HostMetadata.class); InstanceMetaData instanceMetaData4 = mock(InstanceMetaData.class); Set<HostMetadata> hostsMetaData = new HashSet<>(asList(metadata1, metadata2, metadata3, metadata4)); HostGroup hostGroup = new HostGroup(); hostGroup.setHostMetadata(hostsMetaData); hostGroup.setName("slave_1"); Map<String, Map<Long, Long>> dfsSpace = new HashMap<>(); dfsSpace.put("node2", singletonMap(85_000L, 15_000L)); dfsSpace.put("node1", singletonMap(90_000L, 10_000L)); dfsSpace.put("node3", singletonMap(80_000L, 20_000L)); dfsSpace.put("node4", singletonMap(80_000L, 11_000L)); when(metadata1.getHostName()).thenReturn("node1"); when(metadata2.getHostName()).thenReturn("node2"); when(metadata3.getHostName()).thenReturn("node3"); when(metadata4.getHostName()).thenReturn("node4"); when(instanceMetaData1.getAmbariServer()).thenReturn(false); when(instanceMetaData2.getAmbariServer()).thenReturn(false); when(instanceMetaData3.getAmbariServer()).thenReturn(false); when(instanceMetaData4.getAmbariServer()).thenReturn(false); when(ambariClientProvider.getAmbariClient(any(HttpClientConfig.class), anyInt(), any(Cluster.class))).thenReturn(ambariClient); when(ambariClient.getComponentsCategory("multi-node-yarn", "slave_1")).thenReturn(singletonMap("DATANODE", "SLAVE")); when(ambariClient.getBlueprintMap(cluster.getBlueprint().getBlueprintName())).thenReturn(singletonMap("slave_1", singletonList("DATANODE"))); when(ambariClient.getDFSSpace()).thenReturn(dfsSpace); when(instanceMetadataRepository.findHostInStack(stack.getId(), "node1")).thenReturn(instanceMetaData1); when(instanceMetadataRepository.findHostInStack(stack.getId(), "node2")).thenReturn(instanceMetaData2); when(instanceMetadataRepository.findHostInStack(stack.getId(), "node3")).thenReturn(instanceMetaData3); when(instanceMetadataRepository.findHostInStack(stack.getId(), "node4")).thenReturn(instanceMetaData4); when(hostGroupService.getByClusterIdAndName(anyLong(), anyString())).thenReturn(hostGroup); when(statusToPollGroupConverter.convert(Mockito.any(Status.class))).thenReturn(PollGroup.POLLABLE); underTest.updateHosts(stack.getId(), json); verify(flowManager, times(1)).triggerClusterDownscale(stack.getId(), json); } @Test public void testUpdateHostsForDownscaleSelectNodesWithLessData() throws ConnectException, CloudbreakSecuritySetupException { HostGroupAdjustmentJson json = new HostGroupAdjustmentJson(); json.setHostGroup("slave_1"); json.setScalingAdjustment(-1); AmbariClient ambariClient = mock(AmbariClient.class); HostMetadata metadata1 = mock(HostMetadata.class); InstanceMetaData instanceMetaData1 = mock(InstanceMetaData.class); HostMetadata metadata2 = mock(HostMetadata.class); InstanceMetaData instanceMetaData2 = mock(InstanceMetaData.class); HostMetadata metadata3 = mock(HostMetadata.class); InstanceMetaData instanceMetaData3 = mock(InstanceMetaData.class); Set<HostMetadata> hostsMetaData = new HashSet<>(); List<HostMetadata> hostsMetadataList = asList(metadata1, metadata2, metadata3); hostsMetaData.addAll(hostsMetadataList); HostGroup hostGroup = new HostGroup(); hostGroup.setHostMetadata(hostsMetaData); hostGroup.setName("slave_1"); Map<String, Map<Long, Long>> dfsSpace = new HashMap<>(); dfsSpace.put("node2", singletonMap(85_000L, 15_000L)); dfsSpace.put("node1", singletonMap(90_000L, 10_000L)); dfsSpace.put("node3", singletonMap(80_000L, 20_000L)); when(metadata1.getHostName()).thenReturn("node1"); when(metadata2.getHostName()).thenReturn("node2"); when(metadata3.getHostName()).thenReturn("node3"); when(instanceMetaData1.getAmbariServer()).thenReturn(false); when(instanceMetaData2.getAmbariServer()).thenReturn(false); when(instanceMetaData3.getAmbariServer()).thenReturn(false); when(ambariClientProvider.getAmbariClient(any(HttpClientConfig.class), anyInt(), any(Cluster.class))).thenReturn(ambariClient); when(ambariClient.getComponentsCategory("multi-node-yarn", "slave_1")).thenReturn(singletonMap("DATANODE", "SLAVE")); when(ambariClient.getBlueprintMap(cluster.getBlueprint().getBlueprintName())).thenReturn(singletonMap("slave_1", singletonList("DATANODE"))); when(ambariClient.getDFSSpace()).thenReturn(dfsSpace); when(instanceMetadataRepository.findHostInStack(stack.getId(), "node1")).thenReturn(instanceMetaData1); when(instanceMetadataRepository.findHostInStack(stack.getId(), "node2")).thenReturn(instanceMetaData2); when(instanceMetadataRepository.findHostInStack(stack.getId(), "node3")).thenReturn(instanceMetaData3); when(hostGroupService.getByClusterIdAndName(anyLong(), anyString())).thenReturn(hostGroup); when(statusToPollGroupConverter.convert(Mockito.any(Status.class))).thenReturn(PollGroup.POLLABLE); underTest.updateHosts(stack.getId(), json); verify(flowManager, times(1)).triggerClusterDownscale(stack.getId(), json); } @Test public void testUpdateHostsForDownscaleSelectMultipleNodesWithLessData() throws Exception { HostGroupAdjustmentJson json = new HostGroupAdjustmentJson(); json.setHostGroup("slave_1"); json.setScalingAdjustment(-2); AmbariClient ambariClient = mock(AmbariClient.class); HostMetadata metadata1 = mock(HostMetadata.class); InstanceMetaData instanceMetaData1 = mock(InstanceMetaData.class); HostMetadata metadata2 = mock(HostMetadata.class); InstanceMetaData instanceMetaData2 = mock(InstanceMetaData.class); HostMetadata metadata3 = mock(HostMetadata.class); InstanceMetaData instanceMetaData3 = mock(InstanceMetaData.class); HostMetadata metadata4 = mock(HostMetadata.class); InstanceMetaData instanceMetaData4 = mock(InstanceMetaData.class); Set<HostMetadata> hostsMetaData = new HashSet<>(); List<HostMetadata> hostsMetadataList = asList(metadata1, metadata2, metadata3, metadata4); hostsMetaData.addAll(hostsMetadataList); HostGroup hostGroup = new HostGroup(); hostGroup.setHostMetadata(hostsMetaData); hostGroup.setName("slave_1"); Map<String, Map<Long, Long>> dfsSpace = new HashMap<>(); dfsSpace.put("node2", singletonMap(85_000L, 15_000L)); dfsSpace.put("node1", singletonMap(90_000L, 10_000L)); dfsSpace.put("node3", singletonMap(80_000L, 20_000L)); dfsSpace.put("node4", singletonMap(90_000L, 10_000L)); when(metadata1.getHostName()).thenReturn("node1"); when(metadata2.getHostName()).thenReturn("node2"); when(metadata3.getHostName()).thenReturn("node3"); when(metadata3.getHostName()).thenReturn("node4"); when(instanceMetaData1.getAmbariServer()).thenReturn(false); when(instanceMetaData2.getAmbariServer()).thenReturn(false); when(instanceMetaData3.getAmbariServer()).thenReturn(false); when(instanceMetaData4.getAmbariServer()).thenReturn(false); when(ambariClientProvider.getAmbariClient(any(HttpClientConfig.class), anyInt(), any(Cluster.class))).thenReturn(ambariClient); when(ambariClient.getComponentsCategory("multi-node-yarn", "slave_1")).thenReturn(singletonMap("DATANODE", "SLAVE")); when(ambariClient.getBlueprintMap(cluster.getBlueprint().getBlueprintName())).thenReturn(singletonMap("slave_1", singletonList("DATANODE"))); when(ambariClient.getDFSSpace()).thenReturn(dfsSpace); when(instanceMetadataRepository.findHostInStack(stack.getId(), "node1")).thenReturn(instanceMetaData1); when(instanceMetadataRepository.findHostInStack(stack.getId(), "node2")).thenReturn(instanceMetaData2); when(instanceMetadataRepository.findHostInStack(stack.getId(), "node3")).thenReturn(instanceMetaData3); when(instanceMetadataRepository.findHostInStack(stack.getId(), "node4")).thenReturn(instanceMetaData3); when(hostGroupService.getByClusterIdAndName(anyLong(), anyString())).thenReturn(hostGroup); when(statusToPollGroupConverter.convert(Mockito.any(Status.class))).thenReturn(PollGroup.POLLABLE); underTest.updateHosts(stack.getId(), json); verify(flowManager, times(1)).triggerClusterDownscale(stack.getId(), json); } @Test public void testUpdateHostsForDownscaleWhenRemainingSpaceIsNotEnough() throws Exception { HostGroupAdjustmentJson json = new HostGroupAdjustmentJson(); json.setHostGroup("slave_1"); json.setScalingAdjustment(-1); AmbariClient ambariClient = mock(AmbariClient.class); HostMetadata metadata1 = mock(HostMetadata.class); InstanceMetaData instanceMetaData1 = mock(InstanceMetaData.class); HostMetadata metadata2 = mock(HostMetadata.class); InstanceMetaData instanceMetaData2 = mock(InstanceMetaData.class); HostMetadata metadata3 = mock(HostMetadata.class); InstanceMetaData instanceMetaData3 = mock(InstanceMetaData.class); Set<HostMetadata> hostsMetaData = new HashSet<>(); List<HostMetadata> hostsMetadataList = asList(metadata1, metadata2, metadata3); hostsMetaData.addAll(hostsMetadataList); HostGroup hostGroup = new HostGroup(); hostGroup.setHostMetadata(hostsMetaData); hostGroup.setName("slave_1"); Map<String, Map<Long, Long>> dfsSpace = new HashMap<>(); dfsSpace.put("node2", singletonMap(5_000L, 15_000L)); dfsSpace.put("node1", singletonMap(10_000L, 10_000L)); dfsSpace.put("node3", singletonMap(6_000L, 20_000L)); when(metadata1.getHostName()).thenReturn("node1"); when(metadata2.getHostName()).thenReturn("node2"); when(metadata3.getHostName()).thenReturn("node3"); when(instanceMetaData1.getAmbariServer()).thenReturn(false); when(instanceMetaData2.getAmbariServer()).thenReturn(false); when(instanceMetaData3.getAmbariServer()).thenReturn(false); when(ambariClientProvider.getAmbariClient(any(HttpClientConfig.class), anyInt(), any(Cluster.class))).thenReturn(ambariClient); when(ambariClient.getComponentsCategory("multi-node-yarn", "slave_1")).thenReturn(singletonMap("DATANODE", "SLAVE")); when(ambariClient.getBlueprintMap(cluster.getBlueprint().getBlueprintName())).thenReturn(singletonMap("slave_1", singletonList("DATANODE"))); when(ambariClient.getDFSSpace()).thenReturn(dfsSpace); when(hostGroupService.getByClusterIdAndName(anyLong(), anyString())).thenReturn(hostGroup); when(instanceMetadataRepository.findHostInStack(stack.getId(), "node1")).thenReturn(instanceMetaData1); when(instanceMetadataRepository.findHostInStack(stack.getId(), "node2")).thenReturn(instanceMetaData2); when(instanceMetadataRepository.findHostInStack(stack.getId(), "node3")).thenReturn(instanceMetaData3); when(statusToPollGroupConverter.convert(Mockito.any(Status.class))).thenReturn(PollGroup.POLLABLE); underTest.updateHosts(stack.getId(), json); verify(flowManager, times(1)).triggerClusterDownscale(stack.getId(), json); } }