package org.ovirt.engine.core.bll.hostedengine;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.hamcrest.Matchers.hasKey;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import javax.enterprise.inject.Instance;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.verification.VerificationMode;
import org.ovirt.engine.core.bll.interfaces.BackendInternal;
import org.ovirt.engine.core.common.action.VdcActionParametersBase;
import org.ovirt.engine.core.common.action.VdcActionType;
import org.ovirt.engine.core.common.action.VdcReturnValueBase;
import org.ovirt.engine.core.common.businessentities.storage.DiskImage;
import org.ovirt.engine.core.common.vdscommands.VDSCommandType;
import org.ovirt.engine.core.common.vdscommands.VDSParametersBase;
import org.ovirt.engine.core.common.vdscommands.VDSReturnValue;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.utils.MockConfigRule;
import org.ovirt.engine.core.vdsbroker.ResourceManager;
@RunWith(MockitoJUnitRunner.class)
public class HostedEngineConfigFetcherTest {
private static final Guid DOMAIN_ID = Guid.newGuid();
private static final Guid POOL_ID = Guid.newGuid();
private static final Guid HOST_ID = Guid.newGuid();
private static final Guid IMAGE_ID = Guid.newGuid();
private static final Guid VOLUME_ID = Guid.newGuid();
@Rule
public MockConfigRule mcr = new MockConfigRule();
@Mock
private ResourceManager resourceManager;
@Mock
private BackendInternal backend;
@Mock
private Instance<HostedEngineHelper> hostedEngineHelperInstance;
@Mock
private HostedEngineHelper hostedEngineHelper;
@InjectMocks
private HostedEngineConfigFetcher configFetcher;
@Before
public void setUp() throws Exception {
when(hostedEngineHelperInstance.get()).thenReturn(hostedEngineHelper);
when(hostedEngineHelper.getStorageDomainId()).thenReturn(DOMAIN_ID);
when(hostedEngineHelper.getStoragePoolId()).thenReturn(POOL_ID);
when(hostedEngineHelper.getRunningHostId()).thenReturn(HOST_ID);
}
@Test
public void fetchThrowException() {
// given
doThrow(NullPointerException.class)
.when(resourceManager).runVdsCommand(any(), any());
// when
CompletableFuture<Map<String, String>> fetch = configFetcher.fetchPromise();
// then
assertThatThrownBy(() -> fetch.join())
.isInstanceOf(CompletionException.class)
.hasCauseInstanceOf(NullPointerException.class);
assertTrue(fetch.isCompletedExceptionally());
verifyCalled(VDSCommandType.GetImagesList, times(1));
verifyNoMoreInteractions(resourceManager);
}
@Test
public void imagesListIsNull() {
// given
mockVdsCommand(VDSCommandType.GetImagesList, unsuccessfulReturnValue(null));
// when
Map<String, String> config = fetchConfig();
// then
verifyCalled(VDSCommandType.GetImagesList, times(1));
verifyCalled(VDSCommandType.GetVolumesList, never());
assertThat(config, is(Collections.emptyMap()));
}
@Test
public void imagesListIsEmpty() {
// given
mockVdsCommand(VDSCommandType.GetImagesList, successfulReturnValue(Collections.emptyList()));
// when
Map<String, String> config = fetchConfig();
// then
verifyCalled(VDSCommandType.GetImagesList, times(1));
verifyCalled(VDSCommandType.GetVolumesList, never());
assertThat(config, is(Collections.emptyMap()));
}
@Test
public void volumesListIsEmpty() {
// given
mockVdsCommand(VDSCommandType.GetImagesList, successfulReturnValue(Collections.singletonList(IMAGE_ID)));
mockVdsCommand(VDSCommandType.GetVolumesList, successfulReturnValue(Collections.emptyList()));
// when
Map<String, String> config = fetchConfig();
// then
verifyCalled(VDSCommandType.GetImagesList, times(1));
verifyCalled(VDSCommandType.GetVolumesList, times(1));
assertThat(config, is(Collections.emptyMap()));
}
@Test
public void volumeInfoIsEmpty() {
// given
givenListOfImagesAndVolumes();
mockVdsCommand(VDSCommandType.GetImageInfo, unsuccessfulReturnValue(null));
// when
Map<String, String> config = fetchConfig();
// then
verifyCalled(VDSCommandType.GetImagesList, times(1));
verifyCalled(VDSCommandType.GetVolumesList, times(1));
verifyCalled(VDSCommandType.GetImageInfo, times(1));
verifyCalled(VdcActionType.RetrieveImageData, never());
assertThat(config, is(Collections.emptyMap()));
}
@Test
public void configVolumeDescriptionNotMatch() {
// given
givenListOfImagesAndVolumes();
mockVdsCommand(VDSCommandType.GetImageInfo, successfulReturnValue(newDisk("nonMatchingDesc")));
// when
Map<String, String> config = fetchConfig();
// then
verifyCalled(VDSCommandType.GetImagesList, times(1));
verifyCalled(VDSCommandType.GetVolumesList, times(1));
verifyCalled(VDSCommandType.GetImageInfo, times(1));
verifyCalled(VdcActionType.RetrieveImageData, never());
assertThat(config, is(Collections.emptyMap()));
}
@Test
public void untarFails() {
// given
givenListOfImagesAndVolumes();
givenTheWantedDiskImage();
mockVdcCommand(VdcActionType.RetrieveImageData,
successfulVdcReturnValue(null));
// when
Map<String, String> config = fetchConfig();
// then
verifyCalled(VDSCommandType.GetImagesList, times(1));
verifyCalled(VDSCommandType.GetVolumesList, times(1));
verifyCalled(VDSCommandType.GetImageInfo, times(1));
verifyCalled(VdcActionType.RetrieveImageData, times(1));
assertThat(config, is(Collections.emptyMap()));
}
@Test
public void confFileNotExistInTar() {
// given
givenListOfImagesAndVolumes();
givenTheWantedDiskImage();
mockVdcCommand(VdcActionType.RetrieveImageData,
successfulVdcReturnValue(new byte[10]));
// when
Map<String, String> config = fetchConfig();
// then
assertThat(config, is(Collections.emptyMap()));
}
@Test
public void confFileConvertToMapFails() throws IOException, URISyntaxException {
// given
givenListOfImagesAndVolumes();
givenTheWantedDiskImage();
mockVdcCommand(VdcActionType.RetrieveImageData,
successfulVdcReturnValue(load("not-a-valid-hosted-engine-config-tar.tar")));
// when
Map<String, String> config = fetchConfig();
// then
verifyCalled(VdcActionType.RetrieveImageData, times(1));
assertThat(config, is(Collections.emptyMap()));
}
@Test
public void successfulConfFileConvertToMap() throws IOException, URISyntaxException {
// given
givenListOfImagesAndVolumes();
givenTheWantedDiskImage();
mockVdcCommand(VdcActionType.RetrieveImageData,
successfulVdcReturnValue(load("hosted-engine-config.tar")));
// when
Map<String, String> config = fetchConfig();
// then
verifyCalled(VdcActionType.RetrieveImageData, times(1));
assertThat(config, hasKey("sdUUID"));
assertThat(config, hasKey("host_id"));
}
private void givenListOfImagesAndVolumes() {
mockVdsCommand(VDSCommandType.GetImagesList, successfulReturnValue(Collections.singletonList(IMAGE_ID)));
mockVdsCommand(VDSCommandType.GetVolumesList, successfulReturnValue(Collections.singletonList(VOLUME_ID)));
}
private void givenTheWantedDiskImage() {
mockVdsCommand(
VDSCommandType.GetImageInfo,
successfulReturnValue(newDisk(HostedEngineConfigFetcher.HOSTED_ENGINE_CONFIGURATION_IMAGE)));
}
private void mockVdsCommand(VDSCommandType cmdType, VDSReturnValue returnValue) {
doReturn(returnValue)
.when(resourceManager).runVdsCommand(eq(cmdType), any(VDSParametersBase.class));
}
private void mockVdcCommand(VdcActionType vdcActionType, VdcReturnValueBase returnValue) {
doReturn(returnValue)
.when(backend).runInternalAction(eq(vdcActionType), any(VdcActionParametersBase.class));
}
private VDSReturnValue verifyCalled(VDSCommandType vdsmCmd, VerificationMode times) {
return verify(resourceManager, times).runVdsCommand(eq(vdsmCmd), any());
}
private VdcReturnValueBase verifyCalled(VdcActionType vdcActionType, VerificationMode times) {
return verify(backend, times).runInternalAction(eq(vdcActionType), any(VdcActionParametersBase.class));
}
private VDSReturnValue successfulReturnValue(Object value) {
VDSReturnValue vdsReturnValue = new VDSReturnValue();
vdsReturnValue.setSucceeded(true);
vdsReturnValue.setReturnValue(value);
return vdsReturnValue;
}
private VDSReturnValue unsuccessfulReturnValue(Object value) {
VDSReturnValue vdsReturnValue = new VDSReturnValue();
vdsReturnValue.setSucceeded(false);
vdsReturnValue.setReturnValue(value);
return vdsReturnValue;
}
private VdcReturnValueBase successfulVdcReturnValue(Object value) {
VdcReturnValueBase returnValue = new VdcReturnValueBase();
returnValue.setSucceeded(true);
returnValue.setActionReturnValue(value);
return returnValue;
}
public Map<String, String> fetchConfig() {
return configFetcher.fetch();
}
public DiskImage newDisk(String description) {
DiskImage diskImage = new DiskImage();
diskImage.setDescription(description);
diskImage.setId(IMAGE_ID);
diskImage.setImageId(VOLUME_ID);
diskImage.setSize(1024 >> 20);
return diskImage;
}
private byte[] load(String file) throws IOException, URISyntaxException {
Path path = Paths.get(ClassLoader.getSystemResource(file).toURI());
return Files.readAllBytes(path);
}
}