Commit e94836d5 authored by Joerg Domaschka's avatar Joerg Domaschka

jUnit test to validate behaviour before starting implementation of a new state machine

parent a060f05f
......@@ -57,7 +57,7 @@ final class AppInstanceContainer {
void addComponentProperty(ComponentId cid, ComponentInstanceId cinstId, String property, Object value) {
ComponentInstanceContainer c = comps.get(cid);
if(c == null)
throw new IllegalArgumentException("not known: " + cid);
throw new IllegalArgumentException("component not known: " + cid);
c.addComponentProperty(cinstId, property, value);
}
......
......@@ -73,13 +73,14 @@ final class ComponentInstanceContainer {
if(props == null)
throw new IllegalArgumentException("not known: " + cinstId);
Object old = props.put(property, value);
String add = "";
if(old != null) {
LOGGER.warn("warning: overriding value!");
add = " (old value was " + old + ")";
}
//FIXME: wake up listeners (?)
LOGGER.error("TODO: wake up listeners");
LOGGER.info("LcaRegistry: added property: " + this + "/" + cinstId + "." + property + "=" + value);
LOGGER.info("LcaRegistry: added property: " + this + "/" + cinstId + "." + property + "=" + value + old);
}
public void addComponentInstance(ComponentInstanceId cinstId) {
......
......@@ -87,7 +87,7 @@ public final class RemoteRegistryImpl implements RmiLcaRegistry {
@Override
public String getComponentProperty(ApplicationInstanceId appInstId,
ComponentId compId, ComponentInstanceId myId, String name) {
ComponentId compId, ComponentInstanceId myId, String name) throws RemoteException {
AppInstanceContainer c = apps.get(appInstId);
if(c == null)
......
......@@ -69,8 +69,7 @@ public enum LifecycleHandlerType implements State, HandlerType {
*/
PRE_START(PreStartHandler.class, DefaultFactories.PRE_START_FACTORY),
/**
* may be used for checking that required operating system
* files are available, like files, disk space, and port
* starts the component instance
*/
START(StartHandler.class, DefaultFactories.START_FACTORY),
/**
......
......@@ -78,7 +78,6 @@ public class LifecycleAgentImpl implements LifecycleAgent {
applicationRegistered(ctx);
componentPartOfApplication(ctx, component);
ContainerManager manager = containers.getContainerManager(hostContext, os, containerType);
ContainerController cc = manager.createNewContainer(ctx, component, os);
......
......@@ -84,7 +84,7 @@ public class DockerContainerLogic implements ContainerLogic, LifecycleActionInte
@Override
public ComponentInstanceId getComponentId() {
public ComponentInstanceId getComponentInstanceId() {
return myId;
}
......
......@@ -189,7 +189,7 @@ public class PlainContainerLogic implements ContainerLogic, LifecycleActionInter
}
@Override
public ComponentInstanceId getComponentId() {
public ComponentInstanceId getComponentInstanceId() {
return this.myId;
}
......
......@@ -30,7 +30,7 @@ public interface LifecycleActionInterceptor {
void postprocess(HandlerType type);
ComponentInstanceId getComponentId();
ComponentInstanceId getComponentInstanceId();
void postprocessPortUpdate(PortDiff<DownstreamAddress> diff);
......
......@@ -79,7 +79,7 @@ public final class LifecycleController {
private void updateStateInRegistry(LifecycleHandlerType type) {
try {
accessor.updateInstanceState(interceptor.getComponentId(), type);
accessor.updateInstanceState(interceptor.getComponentInstanceId(), type);
} catch (RegistrationException ex) {
LOGGER.warn("could not update status in registry.", ex);
}
......@@ -110,7 +110,8 @@ public final class LifecycleController {
StartDetectorHandler.runStartDetector(interceptor, store.getStartDetector(), ec);
// FIXME: establish periodic invocation of stop detector
getLogger().warn("TODO: periodically run stop detector");
machine.transit(LifecycleHandlerType.START); // moves to POST_START
run(LifecycleHandlerType.START); // moves to POST_START
}
public synchronized void blockingStop() {
......
......@@ -20,6 +20,8 @@ public class LifecycleControllerTransitions {
final ExecutionContext ec;
private LifecycleControllerTransitions(LifecycleStore storeParam, ExecutionContext ecParam) {
if(storeParam == null)
throw new NullPointerException("store must be set.");
store = storeParam;
ec = ecParam;
}
......
......@@ -74,7 +74,7 @@ final class StartDetectorHandler {
return DetectorState.DETECTION_FAILED;
} finally {
if(preprocessed) {
interceptor.postprocessDetector(DetectorType.STOP);
interceptor.postprocessDetector(DetectorType.START);
}
}
}
......
......@@ -74,7 +74,7 @@ public final class StateMachine<T extends Enum<?> & State > {
final Future<?> f;
synchronized(this) {
if(status == null) {
throw new IllegalStateException();
throw new IllegalStateException("status not set");
}
if(status == endState) {
return;
......
package de.uniulm.omi.cloudiator.lance.lca;
public class EnvContextWrapper {
public static HostContext create() {
System.setProperty("host.ip.public", getPublicIp());
System.setProperty("host.ip.private", getCloudIp());
System.setProperty("host.vm.cloud.tenant.id", "tenant: 33033");
System.setProperty("host.vm.id", "vm: 33033");
System.setProperty("host.vm.cloud.id", "cloud: 33033");
return EnvContext.fromEnvironment();
}
public static String getCloudIp() {
return "127.0.0.1";
}
public static String getPublicIp() {
return "129.0.0.1";
}
public static String getContainerIp() {
return "128.0.0.1";
}
}
package de.uniulm.omi.cloudiator.lance.lca.containers.dummy;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
import de.uniulm.omi.cloudiator.lance.application.component.InPort;
import de.uniulm.omi.cloudiator.lance.container.standard.ContainerLogic;
import de.uniulm.omi.cloudiator.lance.lca.container.ContainerException;
import de.uniulm.omi.cloudiator.lance.lca.container.port.InportAccessor;
import de.uniulm.omi.cloudiator.lance.lca.container.port.PortRegistryTranslator;
import de.uniulm.omi.cloudiator.lance.lca.containers.docker.connector.DockerException;
import de.uniulm.omi.cloudiator.lance.lifecycle.LifecycleStore;
import de.uniulm.omi.cloudiator.lance.lifecycles.ResponseHistory;
public class DummyContainer implements ContainerLogic {
private volatile int invocationCounter = 0;
private final EnumMap<ContainerCalls, ResponseHistory<?>> calls = new EnumMap<>(ContainerCalls.class);
private final List<ContainerCalls> callStack = new ArrayList<>();
public DummyContainer(String[] local, Object[] ports) {
calls.put(ContainerCalls.LOCAL_ADDRESSES, ResponseHistory.forStringMethods(local));
calls.put(ContainerCalls.CREATE, ResponseHistory.forVoidMethods());
calls.put(ContainerCalls.INIT, ResponseHistory.forVoidMethods());
calls.put(ContainerCalls.PORT_MAP, ResponseHistory.forObjectMethods(ports));
}
/* interface methods */
@Override
public void doCreate() throws ContainerException {
invocationCounter++;
callStack.add(ContainerCalls.CREATE);
calls.get(ContainerCalls.CREATE).getNext();
}
@Override
public void doInit(LifecycleStore store) throws ContainerException {
invocationCounter++;
callStack.add(ContainerCalls.INIT);
calls.get(ContainerCalls.INIT).getNext();
}
@Override
public void completeInit() throws ContainerException {
invocationCounter++;
callStack.add(ContainerCalls.OTHER);
throw new UnsupportedOperationException();
}
@Override
public void doDestroy(boolean forceShutdown) throws ContainerException {
invocationCounter++;
callStack.add(ContainerCalls.OTHER);
throw new UnsupportedOperationException();
}
@Override
public String getLocalAddress() throws ContainerException {
invocationCounter++;
callStack.add(ContainerCalls.LOCAL_ADDRESSES);
String ret = (String) calls.get(ContainerCalls.LOCAL_ADDRESSES).getNext();
return ret;
}
@Override
public InportAccessor getPortMapper() {
invocationCounter++;
callStack.add(ContainerCalls.PORT_MAP);
Object o = calls.get(ContainerCalls.PORT_MAP).getNext();
final int[] mapping = (int[]) o;
return ( (portName, clientState) -> {
clientState.registerValueAtLevel(PortRegistryTranslator.PORT_HIERARCHY_0, mapping[0]);
clientState.registerValueAtLevel(PortRegistryTranslator.PORT_HIERARCHY_1, mapping[1]);
clientState.registerValueAtLevel(PortRegistryTranslator.PORT_HIERARCHY_2, mapping[2]);
});
}
/* validation methods */
public enum ContainerCalls {
LOCAL_ADDRESSES,
CREATE,
INIT,
PORT_MAP,
OTHER,
;
}
public int invocationCount() {
return invocationCounter;
}
public int getInvocationCount(EnumSet<ContainerCalls> callSet) {
int counter = 0;
for(ContainerCalls cc : callSet) {
ResponseHistory<?> rh = calls.get(cc);
counter = counter + rh.getCount();
}
return counter;
}
public boolean invocationHistoryMatches(ContainerCalls ... expected) {
if(expected == null || expected.length == 0) {
throw new IllegalStateException();
}
List<ContainerCalls> copy = new LinkedList<>(callStack);
for(ContainerCalls cc : expected) {
if(copy.isEmpty())
return false;
ContainerCalls _c = copy.remove(0);
if(cc != _c) return false;
}
if(!copy.isEmpty())
return false;
return true;
}
public void printInvocations() {
System.out.println(this);
}
@Override
public String toString() {
return calls + "-->" + callStack.toString();
}
}
package de.uniulm.omi.cloudiator.lance.lca.containers.dummy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import de.uniulm.omi.cloudiator.lance.lca.container.ComponentInstanceId;
import de.uniulm.omi.cloudiator.lance.lca.container.ContainerException;
import de.uniulm.omi.cloudiator.lance.lca.container.port.DownstreamAddress;
import de.uniulm.omi.cloudiator.lance.lca.container.port.PortDiff;
import de.uniulm.omi.cloudiator.lance.lifecycle.HandlerType;
import de.uniulm.omi.cloudiator.lance.lifecycle.LifecycleActionInterceptor;
import de.uniulm.omi.cloudiator.lance.lifecycle.detector.DetectorType;
import de.uniulm.omi.cloudiator.lance.lifecycles.CoreElements;
public class DummyInterceptor implements LifecycleActionInterceptor {
private final List<HandlerType> called = new LinkedList<>();
private final Map<HandlerType, Integer> countingHandlerCalls = new HashMap<>();
private final List<String> invocations = new LinkedList<>();
private volatile HandlerType ongoingPreparation = null;
private final static HandlerType portUpdateType = new HandlerType(){};
@Override
public void prepare(HandlerType type) throws ContainerException {
if(ongoingPreparation != null) {
throw new IllegalStateException("preparation is ongoing: " + ongoingPreparation);
}
if(countingHandlerCalls.containsKey(type)) {
throw new IllegalStateException("handler called twice: " + type);
}
ongoingPreparation = type;
countingHandlerCalls.put(type, 1);
invocations.add("prepare_"+type);
called.add(type);
}
@Override
public void postprocess(HandlerType type) {
if(!countingHandlerCalls.containsKey(type)) {
throw new IllegalStateException("prepare handler not called: " + type);
}
if(ongoingPreparation != type) {
throw new IllegalStateException("prepare handler not called: " + type);
}
int i = countingHandlerCalls.get(type);
countingHandlerCalls.put(type, i+1);
ongoingPreparation = null;
invocations.add("postprocess_"+type);
}
@Override
public ComponentInstanceId getComponentInstanceId() {
invocations.add("getComponentInstanceId");
return CoreElements.componentInstanceId;
}
@Override
public void postprocessPortUpdate(PortDiff<DownstreamAddress> diff) {
if(!countingHandlerCalls.containsKey(portUpdateType)) {
throw new IllegalStateException("prepare port update handler not called");
}
if(ongoingPreparation != portUpdateType) {
throw new IllegalStateException("prepare port update handler not called");
}
int i = countingHandlerCalls.get(portUpdateType);
countingHandlerCalls.put(portUpdateType, i+1);
ongoingPreparation = null;
invocations.add("postprocessPortUpdate_"+diff);
}
@Override
public void preprocessPortUpdate(PortDiff<DownstreamAddress> diff) throws ContainerException {
if(ongoingPreparation != null) {
throw new IllegalStateException("preparation is ongoing: " + ongoingPreparation);
}
if(countingHandlerCalls.containsKey(portUpdateType)) {
throw new IllegalStateException("handler called twice: " + portUpdateType);
}
ongoingPreparation = portUpdateType;
countingHandlerCalls.put(portUpdateType, 1);
invocations.add("preprocessPortUpdate_"+diff);
called.add(portUpdateType);
throw new UnsupportedOperationException();
}
@Override
public void postprocessDetector(DetectorType type) {
if(!countingHandlerCalls.containsKey(type)) {
throw new IllegalStateException("prepare detector handler not called: " + type + ": " +called);
}
if(ongoingPreparation != type) {
throw new IllegalStateException("prepare detector not called: " + type);
}
int i = countingHandlerCalls.get(type);
countingHandlerCalls.put(type, i+1);
ongoingPreparation = null;
invocations.add("postprocessDetector_"+type);
}
@Override
public void preprocessDetector(DetectorType type) throws ContainerException {
if(ongoingPreparation != null) {
throw new IllegalStateException("preparation is ongoing: " + ongoingPreparation);
}
if(countingHandlerCalls.containsKey(type)) {
throw new IllegalStateException("handler called twice: " + type);
}
ongoingPreparation = type;
countingHandlerCalls.put(type, 1);
invocations.add("preprocessDetector_"+type);
called.add(type);
}
public int handlerCalls() {
int sum = 0;
for(Entry<HandlerType, Integer> e : countingHandlerCalls.entrySet()){
if(e.getValue() != null) {
sum = sum + e.getValue();
} else {
throw new IllegalStateException();
}
}
return sum;
}
public List<HandlerType> invokedHandlers() {
return new ArrayList<>(called);
}
}
package de.uniulm.omi.cloudiator.lance.lca.registry.dummy;
import java.rmi.RemoteException;
import java.util.Map;
import de.uniulm.omi.cloudiator.lance.application.ApplicationId;
import de.uniulm.omi.cloudiator.lance.application.ApplicationInstanceId;
import de.uniulm.omi.cloudiator.lance.application.component.ComponentId;
import de.uniulm.omi.cloudiator.lance.lca.LcaRegistry;
import de.uniulm.omi.cloudiator.lance.lca.container.ComponentInstanceId;
import de.uniulm.omi.cloudiator.lance.lca.registry.RegistrationException;
import de.uniulm.omi.cloudiator.lance.lca.registry.rmi.RemoteRegistryImpl;
public class DummyRegistry implements LcaRegistry {
private final RemoteRegistryImpl reg = new RemoteRegistryImpl();
@Override
public boolean addApplicationInstance(ApplicationInstanceId instId, ApplicationId appId, String name)
throws RegistrationException {
try {
return reg.addApplicationInstance(instId, appId, name);
} catch(RemoteException re) {
throw new IllegalStateException();
}
}
@Override
public void addComponent(ApplicationInstanceId instId, ComponentId cid, String name) throws RegistrationException {
try {
reg.addComponent(instId, cid, name);
} catch(RemoteException re) {
throw new IllegalStateException();
}
}
@Override
public void addComponentInstance(ApplicationInstanceId instId, ComponentId cid, ComponentInstanceId cinstId)
throws RegistrationException {
try {
reg.addComponentInstance(instId, cid, cinstId);
} catch(RemoteException re) {
throw new IllegalStateException();
}
}
@Override
public void addComponentProperty(ApplicationInstanceId instId, ComponentId cid, ComponentInstanceId cinstId,
String property, Object value) throws RegistrationException {
try {
reg.addComponentProperty(instId, cid, cinstId, property, value);
} catch(RemoteException re) {
throw new IllegalStateException();
}
}
@Override
public Map<ComponentInstanceId, Map<String, String>> dumpComponent(ApplicationInstanceId instId, ComponentId compId) throws RegistrationException {
try {
return reg.dumpComponent(instId, compId);
} catch(RemoteException re) {
throw new IllegalStateException();
}
}
@Override
public String getComponentProperty(ApplicationInstanceId appInstId, ComponentId compId, ComponentInstanceId myId,
String name) throws RegistrationException {
try {
return reg.getComponentProperty(appInstId, compId, myId, name);
} catch(RemoteException re) {
throw new IllegalStateException();
}
}
@Override
public boolean applicationInstanceExists(ApplicationInstanceId appInstId) throws RegistrationException {
try {
return reg.applicationInstanceExists(appInstId);
} catch(RemoteException re) {
throw new IllegalStateException();
}
}
@Override
public boolean applicationComponentExists(ApplicationInstanceId appInstId, ComponentId compId)
throws RegistrationException {
try {
return reg.applicationComponentExists(appInstId, compId);
} catch(RemoteException re) {
throw new IllegalStateException();
}
}
}
package de.uniulm.omi.cloudiator.lance.lifecycles;
import static org.junit.Assert.*;
import java.util.Arrays;
import java.util.Map;
import org.junit.BeforeClass;
import org.junit.Test;
import de.uniulm.omi.cloudiator.lance.lca.container.ComponentInstanceId;
import de.uniulm.omi.cloudiator.lance.lca.containers.dummy.DummyInterceptor;
import de.uniulm.omi.cloudiator.lance.lca.registry.RegistrationException;
import de.uniulm.omi.cloudiator.lance.lifecycle.ExecutionContext;
import de.uniulm.omi.cloudiator.lance.lifecycle.HandlerType;
import de.uniulm.omi.cloudiator.lance.lifecycle.LifecycleController;
import de.uniulm.omi.cloudiator.lance.lifecycle.LifecycleException;
import de.uniulm.omi.cloudiator.lance.lifecycle.LifecycleHandlerType;
import de.uniulm.omi.cloudiator.lance.lifecycle.detector.DetectorType;
public class ComponentInstanceLifecycleTest {
public volatile DummyInterceptor interceptor;
public volatile LifecycleController lcc;
public volatile CoreElements core;
public volatile LifecycleStoreCreator creator;
private volatile ExecutionContext ctx;
private volatile Map<ComponentInstanceId, Map<String, String>> dumb;