package jetbrains.buildServer.clouds.vmware;
import com.intellij.openapi.util.Pair;
import com.vmware.vim25.CustomizationSpec;
import com.vmware.vim25.mo.Datacenter;
import com.vmware.vim25.mo.ManagedEntity;
import java.io.File;
import java.net.MalformedURLException;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import jetbrains.buildServer.BaseTestCase;
import jetbrains.buildServer.clouds.CloudClientParameters;
import jetbrains.buildServer.clouds.CloudException;
import jetbrains.buildServer.clouds.CloudImageParameters;
import jetbrains.buildServer.clouds.vmware.connector.VMWareApiConnector;
import jetbrains.buildServer.clouds.vmware.errors.VmwareCheckedCloudException;
import jetbrains.buildServer.clouds.vmware.stubs.FakeApiConnector;
import jetbrains.buildServer.clouds.vmware.stubs.FakeModel;
import jetbrains.buildServer.clouds.vmware.tasks.VmwareUpdateInstanceTask;
import jetbrains.buildServer.clouds.vmware.tasks.VmwarePooledUpdateInstanceTask;
import jetbrains.buildServer.clouds.vmware.tasks.VmwareUpdateTaskManager;
import org.jetbrains.annotations.NotNull;
import org.testng.SkipException;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
/**
* Created by sergeypak on 27/10/2016.
*/
@Test
public class VmwarePooledUpdateInstanceTaskTest extends BaseTestCase {
protected static final String PROFILE_ID = "cp1";
protected static final String TEST_SERVER_UUID = "1234-5678-9012";
private File myIdxStorage;
private FakeApiConnector myFakeApiConnector;
private VmwareUpdateTaskManager myTaskManager;
@BeforeMethod
@Override
protected void setUp() throws Exception {
super.setUp();
myIdxStorage = createTempDir();
FakeModel.instance().addDatacenter("dc");
FakeModel.instance().addFolder("cf").setParent("dc", Datacenter.class);
FakeModel.instance().addResourcePool("rp").setParentFolder("cf");
FakeModel.instance().addVM("image1").setParentFolder("cf");
FakeModel.instance().addVM("image2").setParentFolder("cf");
FakeModel.instance().addVM("image_template").setParentFolder("cf");
FakeModel.instance().addVMSnapshot("image2", "snap");
FakeModel.instance().getCustomizationSpecs().put("someCustomization", new CustomizationSpec());
FakeModel.instance().getCustomizationSpecs().put("linux", new CustomizationSpec());
myFakeApiConnector = new FakeApiConnector(TEST_SERVER_UUID, PROFILE_ID, null);
setInternalProperty("teamcity.vsphere.instance.status.update.delay.ms", "250");
myTaskManager = new VmwareUpdateTaskManager();
}
public void check_called_once() throws MalformedURLException {
if (true)
throw new SkipException("In progress");
final AtomicBoolean listAllCanBeCalled = new AtomicBoolean();
final AtomicBoolean listAllCalledOnce = new AtomicBoolean();
final AtomicBoolean getByNameCanBeCalled = new AtomicBoolean();
final AtomicBoolean getByNameCalledOnce = new AtomicBoolean();
myFakeApiConnector = new FakeApiConnector(TEST_SERVER_UUID, PROFILE_ID, null) {
@Override
protected <T extends ManagedEntity> Collection<T> findAllEntitiesOld(final Class<T> instanceType) throws VmwareCheckedCloudException {
if (!listAllCanBeCalled.get()) {
fail("Shouldn't be called");
}
assertFalse(listAllCalledOnce.get());
listAllCalledOnce.set(true);
return super.findAllEntitiesOld(instanceType);
}
@NotNull
@Override
protected <T extends ManagedEntity> Pair<T,Datacenter> findEntityByIdNameOld(final String idName, final Class<T> instanceType) throws VmwareCheckedCloudException {
if (!getByNameCanBeCalled.get()) {
fail("Shouldn't be called");
}
assertFalse(getByNameCalledOnce.get());
getByNameCalledOnce.set(true);
return super.findEntityByIdNameOld(idName, instanceType);
}
};
final CloudClientParameters clientParameters1 = new CloudClientParameters();
clientParameters1.setCloudImages(CloudImageParameters.collectionFromJson("[{sourceVmName:'image1', behaviour:'START_STOP'}]"));
final VMWareCloudClient client1 = new MyClient(clientParameters1, null);
final CloudClientParameters clientParameters2 = new CloudClientParameters();
clientParameters2.setCloudImages(CloudImageParameters.collectionFromJson(
"[{sourceVmName:'image2',snapshot:'snap*',folder:'cf',pool:'rp'," +
"maxInstances:3,behaviour:'ON_DEMAND_CLONE',customizationSpec:'someCustomization'}]"));
final VMWareCloudClient client2 = new MyClient(clientParameters2, null);
final CloudClientParameters clientParameters3 = new CloudClientParameters();
clientParameters3.setCloudImages(CloudImageParameters.collectionFromJson(
"[{'source-id':'image_template',sourceVmName:'image_template', snapshot:'" + VmwareConstants.CURRENT_STATE +
"',folder:'cf',pool:'rp',maxInstances:3,behaviour:'FRESH_CLONE', customizationSpec: 'linux'}]"
));
final VMWareCloudClient client3 = new MyClient(clientParameters3, null);
final VmwareUpdateInstanceTask task1 = myTaskManager.createUpdateTask(myFakeApiConnector, client1);
final VmwareUpdateInstanceTask task2 = myTaskManager.createUpdateTask(myFakeApiConnector, client2);
final VmwareUpdateInstanceTask task3 = myTaskManager.createUpdateTask(myFakeApiConnector, client3);
listAllCanBeCalled.set(true);
listAllCalledOnce.set(false);
getByNameCalledOnce.set(false);
getByNameCanBeCalled.set(true);
task1.run();
task2.run();
task3.run();
assertTrue(listAllCalledOnce.get());
assertTrue(getByNameCalledOnce.get());
}
public void check_cleared_after_dispose(){
final CloudClientParameters clientParameters1 = new CloudClientParameters();
clientParameters1.setCloudImages(CloudImageParameters.collectionFromJson("[{sourceVmName:'image1', behaviour:'START_STOP'}]"));
final VMWareCloudClient client1 = new MyClient(clientParameters1, null);
final CloudClientParameters clientParameters2 = new CloudClientParameters();
clientParameters2.setCloudImages(CloudImageParameters.collectionFromJson(
"[{sourceVmName:'image2',snapshot:'snap*',folder:'cf',pool:'rp'," +
"maxInstances:3,behaviour:'ON_DEMAND_CLONE',customizationSpec:'someCustomization'}]"));
final VMWareCloudClient client2 = new MyClient(clientParameters2, null);
final AtomicInteger expectedImagesCount = new AtomicInteger();
final AtomicBoolean hasRun = new AtomicBoolean(false);
final AtomicReference<Collection<VmwareCloudImage>> images2process = new AtomicReference<>();
myTaskManager = new VmwareUpdateTaskManager(){
@Override
protected VmwarePooledUpdateInstanceTask createNewPooledTask(@NotNull final VMWareApiConnector connector, @NotNull final VMWareCloudClient client) {
return new VmwarePooledUpdateInstanceTask(connector, client, this){
@Override
public void run() {
final Collection<VmwareCloudImage> images = getImages();
images2process.set(images);
assertEquals(expectedImagesCount.get(), images.size());
hasRun.set(true);
}
};
}
};
myTaskManager.createUpdateTask(myFakeApiConnector, client1);
VmwareUpdateInstanceTask task = myTaskManager.createUpdateTask(myFakeApiConnector, client2);
client1.dispose(); // the base task will remain
expectedImagesCount.set(1);
task.run();
assertTrue(hasRun.get());
assertEquals(1, images2process.get().size());
assertEquals("image2", images2process.get().iterator().next().getName());
hasRun.set(false);
client2.dispose();
task.run();
assertFalse(hasRun.get());
}
public void check_cleared_after_dispose_2() throws MalformedURLException {
final AtomicBoolean canBeCalled = new AtomicBoolean();
final AtomicBoolean actuallCalled = new AtomicBoolean();
myFakeApiConnector = new FakeApiConnector(TEST_SERVER_UUID, PROFILE_ID, null) {
@Override
protected <T extends ManagedEntity> Collection<T> findAllEntitiesOld(final Class<T> instanceType) throws VmwareCheckedCloudException {
processChecks();
return Collections.emptyList();
}
private void processChecks() {
if (!canBeCalled.get()) {
fail("Shouldn't be called");
}
assertFalse(actuallCalled.get());
actuallCalled.set(true);
}
@Override
protected <T extends ManagedEntity> T findEntityByIdNameNullableOld(final String name, final Class<T> instanceType, final Datacenter dc) throws VmwareCheckedCloudException {
processChecks();
return null;
}
};
final CloudClientParameters clientParameters1 = new CloudClientParameters();
clientParameters1.setCloudImages(CloudImageParameters.collectionFromJson("[{sourceVmName:'image1', behaviour:'START_STOP'}]"));
final VMWareCloudClient client1 = new MyClient(clientParameters1, null);
final CloudClientParameters clientParameters2 = new CloudClientParameters();
clientParameters2.setCloudImages(CloudImageParameters.collectionFromJson(
"[{sourceVmName:'image2',snapshot:'snap*',folder:'cf',pool:'rp'," +
"maxInstances:3,behaviour:'ON_DEMAND_CLONE',customizationSpec:'someCustomization'}]"));
final VMWareCloudClient client2 = new MyClient(clientParameters2, null);
final VmwareUpdateInstanceTask task1 = myTaskManager.createUpdateTask(myFakeApiConnector, client1);
canBeCalled.set(true);
actuallCalled.set(false);
task1.run();
assertTrue(actuallCalled.get());
client1.dispose();
final VmwareUpdateInstanceTask task2 = myTaskManager.createUpdateTask(myFakeApiConnector, client2);
canBeCalled.set(true);
actuallCalled.set(false);
task2.run();
assertTrue(actuallCalled.get());
}
private class MyClient extends VMWareCloudClient{
private final Map<VMWareCloudClient, Boolean> myGetImagesCalled;
public MyClient(@NotNull final CloudClientParameters cloudClientParameters,
Map<VMWareCloudClient, Boolean> getImagesCalled) {
super(cloudClientParameters, myFakeApiConnector, myTaskManager, myIdxStorage);
myGetImagesCalled = getImagesCalled;
populateImagesData(VMWareCloudClientFactory.parseImageDataInternal(cloudClientParameters));
}
@NotNull
@Override
public Collection<VmwareCloudImage> getImages() throws CloudException {
if (myGetImagesCalled != null)
assertNull(myGetImagesCalled.put(this, true));
return super.getImages();
}
@Override
public void populateImagesData(@NotNull final Collection<VmwareCloudImageDetails> imageDetails) {
imageDetails.stream().forEach((key) -> myImageMap.put(key.getSourceId(), checkAndCreateImage(key)));
}
}
@AfterMethod
@Override
protected void tearDown() throws Exception {
System.getProperties().remove("teamcity.vsphere.instance.status.update.delay.ms");
FakeModel.instance().clear();
super.tearDown();
}
}