Commit e08d7f7e authored by Joerg Domaschka's avatar Joerg Domaschka

adapted test scripts to error aware container;

fixed bugs in error aware container implementation;
decided on exception handling for container errors during transition.
parent 198f2df0
package de.uniulm.omi.cloudiator.lance.lca.container;
public class ContainerConfigurationException extends ContainerException {
private static final long serialVersionUID = 1L;
public ContainerConfigurationException() {
super();
}
public ContainerConfigurationException(String message, Throwable cause) {
super(message, cause);
}
public ContainerConfigurationException(String message) {
super(message);
}
public ContainerConfigurationException(Throwable cause) {
super(cause);
}
}
......@@ -18,7 +18,7 @@
package de.uniulm.omi.cloudiator.lance.lca.container;
public final class ContainerException extends Exception {
public class ContainerException extends Exception {
private static final long serialVersionUID = 3097082722316018152L;
......
package de.uniulm.omi.cloudiator.lance.lca.container;
public class ContainerOperationException extends ContainerException {
public ContainerOperationException() {
super();
}
public ContainerOperationException(String message, Throwable cause) {
super(message, cause);
}
public ContainerOperationException(String message) {
super(message);
}
public ContainerOperationException(Throwable cause) {
super(cause);
}
}
package de.uniulm.omi.cloudiator.lance.lca.container;
public class UnexpectedContainerStateException extends ContainerException {
public UnexpectedContainerStateException() {
super();
}
public UnexpectedContainerStateException(String message, Throwable cause) {
super(message, cause);
}
public UnexpectedContainerStateException(String message) {
super(message);
}
public UnexpectedContainerStateException(Throwable cause) {
super(cause);
}
}
......@@ -24,8 +24,11 @@ import org.slf4j.LoggerFactory;
import de.uniulm.omi.cloudiator.lance.lca.GlobalRegistryAccessor;
import de.uniulm.omi.cloudiator.lance.lca.container.ContainerController;
import de.uniulm.omi.cloudiator.lance.lca.container.ComponentInstanceId;
import de.uniulm.omi.cloudiator.lance.lca.container.ContainerConfigurationException;
import de.uniulm.omi.cloudiator.lance.lca.container.ContainerException;
import de.uniulm.omi.cloudiator.lance.lca.container.ContainerOperationException;
import de.uniulm.omi.cloudiator.lance.lca.container.ContainerStatus;
import de.uniulm.omi.cloudiator.lance.lca.container.UnexpectedContainerStateException;
import de.uniulm.omi.cloudiator.lance.lca.container.port.NetworkHandler;
import de.uniulm.omi.cloudiator.lance.lca.container.port.PortRegistryTranslator;
import de.uniulm.omi.cloudiator.lance.lca.registry.RegistrationException;
......@@ -34,17 +37,11 @@ import de.uniulm.omi.cloudiator.lance.lifecycle.LifecycleException;
import de.uniulm.omi.cloudiator.lance.lifecycle.LifecycleStore;
import de.uniulm.omi.cloudiator.lance.util.state.ErrorAwareStateMachine;
import de.uniulm.omi.cloudiator.lance.util.state.ErrorAwareStateMachineBuilder;
import de.uniulm.omi.cloudiator.lance.util.state.TransitionAction;
import static de.uniulm.omi.cloudiator.lance.container.standard.StandardContainerHelper.checkForBootstrapParameters;
import static de.uniulm.omi.cloudiator.lance.container.standard.StandardContainerHelper.checkForCreationParameters;
// FIXME: move status updates to network handler to this class instead of keeping
// them in the individual container classes
// FIXME: update component instance status each time a state has been reached
// FIXME: introduce error states to life cycle handling
public final class ErrorAwareContainer<T extends ContainerLogic> implements ContainerController {
private static final Logger LOGGER = LoggerFactory.getLogger(ErrorAwareContainer.class);
......@@ -98,14 +95,15 @@ public final class ErrorAwareContainer<T extends ContainerLogic> implements Cont
}
@Override
public void awaitCreation() throws ContainerException {
public void awaitCreation() throws ContainerOperationException, ContainerConfigurationException, UnexpectedContainerStateException {
ContainerStatus stat = stateMachine.waitForEndOfCurrentTransition();
if(CreateTransitionAction.isSuccessfullEndState(stat)) {
return;
}
if(CreateTransitionAction.isKnownErrorState(stat)) {
throw new ContainerException("container creation failed. container is now in error state: " + stat, stateMachine.collectExceptions());
throw new ContainerOperationException("container creation failed. container is now in error state: " + stat, stateMachine.collectExceptions());
}
throwExceptionIfGenericErrorStateOrOtherState(stat);
}
@Override
......@@ -114,14 +112,15 @@ public final class ErrorAwareContainer<T extends ContainerLogic> implements Cont
}
@Override
public void awaitBootstrap() throws ContainerException {
public void awaitBootstrap() throws ContainerOperationException, ContainerConfigurationException, UnexpectedContainerStateException {
ContainerStatus stat = stateMachine.waitForEndOfCurrentTransition();
if(BootstrapTransitionAction.isSuccessfullEndState(stat)) {
return;
}
if(BootstrapTransitionAction.isKnownErrorState(stat)) {
throw new ContainerException("container bootstrap failed. container is now in error state: " + stat, stateMachine.collectExceptions());
throw new ContainerOperationException("container bootstrap failed. container is now in error state: " + stat, stateMachine.collectExceptions());
}
throwExceptionIfGenericErrorStateOrOtherState(stat);
}
@Override
......@@ -130,15 +129,15 @@ public final class ErrorAwareContainer<T extends ContainerLogic> implements Cont
}
@Override
public void awaitInitialisation() throws ContainerException {
stateMachine.waitForEndOfCurrentTransition();
public void awaitInitialisation() throws ContainerOperationException, ContainerConfigurationException, UnexpectedContainerStateException {
ContainerStatus stat = stateMachine.waitForEndOfCurrentTransition();
if(InitTransitionAction.isSuccessfullEndState(stat)) {
return;
}
if(InitTransitionAction.isKnownErrorState(stat)) {
throw new ContainerException("container initialisation failed. container is now in error state: " + stat, stateMachine.collectExceptions());
throw new ContainerOperationException("container initialisation failed. container is now in error state: " + stat, stateMachine.collectExceptions());
}
throwExceptionIfGenericErrorStateOrOtherState(stat);
}
@Override
......@@ -147,18 +146,17 @@ public final class ErrorAwareContainer<T extends ContainerLogic> implements Cont
}
@Override
public void awaitDestruction() throws ContainerException {
stateMachine.waitForEndOfCurrentTransition();
public void awaitDestruction() throws ContainerOperationException, ContainerConfigurationException, UnexpectedContainerStateException {
ContainerStatus stat = stateMachine.waitForEndOfCurrentTransition();
if(DestroyTransitionAction.isSuccessfullEndState(stat)) {
return;
}
if(DestroyTransitionAction.isKnownErrorState(stat)) {
throw new ContainerException("container deletion failed. container is now in error state: " + stat, stateMachine.collectExceptions());
throw new ContainerOperationException("container deletion failed. container is now in error state: " + stat, stateMachine.collectExceptions());
}
throwExceptionIfGenericErrorStateOrOtherState(stat);
}
void setNetworking() throws ContainerException {
String address = logic.getLocalAddress();
try {
......@@ -208,4 +206,11 @@ public final class ErrorAwareContainer<T extends ContainerLogic> implements Cont
void registerStatus(ContainerStatus status) throws RegistrationException {
accessor.updateContainerState(containerId, status);
}
void throwExceptionIfGenericErrorStateOrOtherState(ContainerStatus stat) throws ContainerConfigurationException, UnexpectedContainerStateException {
if(stateMachine.isGenericErrorState(stat)){
throw new ContainerConfigurationException("generic error state reached: " + stat, stateMachine.collectExceptions());
}
throw new UnexpectedContainerStateException("unexpected state reached: " + stat, stateMachine.collectExceptions());
}
}
......@@ -18,8 +18,8 @@ final class InitTransitionAction implements TransitionAction {
@Override
public void transit(Object[] params) throws TransitionException {
//FIXME: add code for starting from snapshot (skip init and install steps)
// has to be realised at a different place
// FIXME: add code for starting from snapshot (skip init and install steps)
// probably has to be realised at a different place
try {
theContainer.preInitAction();
theContainer.logic.completeInit();
......@@ -33,7 +33,7 @@ final class InitTransitionAction implements TransitionAction {
public static void create(ErrorAwareTransitionBuilder<ContainerStatus> transitionBuilder,
ErrorAwareContainer<?> container) {
CreateTransitionAction action = new CreateTransitionAction(container);
InitTransitionAction action = new InitTransitionAction(container);
// FIXME: add error handler //
transitionBuilder.setStartState(ContainerStatus.BOOTSTRAPPED).
......
/*
* Copyright (c) 2014-2015 University of Ulm
*
* See the NOTICE file distributed with this work for additional information
* regarding copyright ownership. 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 de.uniulm.omi.cloudiator.lance.lca.container;
import de.uniulm.omi.cloudiator.lance.lifecycle.LifecycleStore;
public interface ContainerController {
ComponentInstanceId getId();
ContainerStatus getState();
void create() throws ContainerException;
void init(LifecycleStore store) throws ContainerException;
void tearDown() throws ContainerException;
void awaitInitialisation() throws ContainerException;
void awaitCreation() throws ContainerException;
void awaitDestruction() throws ContainerException;
void bootstrap() throws ContainerException;
void awaitBootstrap() throws ContainerException;
}
/*
* Copyright (c) 2014-2015 University of Ulm
*
* See the NOTICE file distributed with this work for additional information
* regarding copyright ownership. 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 de.uniulm.omi.cloudiator.lance.lca.container;
import de.uniulm.omi.cloudiator.lance.lifecycle.LifecycleStore;
public interface ContainerController {
ComponentInstanceId getId();
ContainerStatus getState();
void create() ;
void bootstrap() ;
void init(LifecycleStore store) ;
void tearDown() ;
void awaitInitialisation() throws ContainerException;
void awaitCreation() throws ContainerException;
void awaitDestruction() throws ContainerException;
void awaitBootstrap() throws ContainerException;
}
\ No newline at end of file
/*
* Copyright (c) 2014-2015 University of Ulm
*
* See the NOTICE file distributed with this work for additional information
* regarding copyright ownership. 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 de.uniulm.omi.cloudiator.lance.lca.containers.docker;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import de.uniulm.omi.cloudiator.lance.application.DeploymentContext;
import de.uniulm.omi.cloudiator.lance.application.component.DeployableComponent;
import de.uniulm.omi.cloudiator.lance.application.component.InPort;
import de.uniulm.omi.cloudiator.lance.container.spec.os.OperatingSystem;
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.ComponentInstanceId;
import de.uniulm.omi.cloudiator.lance.lca.container.environment.BashExportBasedVisitor;
import de.uniulm.omi.cloudiator.lance.lca.container.port.DownstreamAddress;
import de.uniulm.omi.cloudiator.lance.lca.container.port.InportAccessor;
import de.uniulm.omi.cloudiator.lance.lca.container.port.NetworkHandler;
import de.uniulm.omi.cloudiator.lance.lca.container.port.PortDiff;
import de.uniulm.omi.cloudiator.lance.lca.container.port.PortRegistryTranslator;
import de.uniulm.omi.cloudiator.lance.lca.containers.docker.connector.DockerConnector;
import de.uniulm.omi.cloudiator.lance.lca.containers.docker.connector.DockerException;
import de.uniulm.omi.cloudiator.lance.lifecycle.HandlerType;
import de.uniulm.omi.cloudiator.lance.lifecycle.LifecycleActionInterceptor;
import de.uniulm.omi.cloudiator.lance.lifecycle.LifecycleHandlerType;
import de.uniulm.omi.cloudiator.lance.lifecycle.LifecycleStore;
import de.uniulm.omi.cloudiator.lance.lifecycle.detector.DetectorType;
public class DockerContainerLogic implements ContainerLogic, LifecycleActionInterceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(DockerContainerManager.class);
private final ComponentInstanceId myId;
private final DockerConnector client;
private final DockerShellFactory shellFactory;
private final DeploymentContext deploymentContext;
private final DockerImageHandler imageHandler;
private final NetworkHandler networkHandler;
private final DeployableComponent myComponent;
DockerContainerLogic(ComponentInstanceId id, DockerConnector client, DeployableComponent comp,
DeploymentContext ctx, OperatingSystem os, NetworkHandler network,
DockerShellFactory shellFactoryParam) {
this(id, client, os, ctx, comp, network, shellFactoryParam);
}
private DockerContainerLogic(ComponentInstanceId id, DockerConnector clientParam, OperatingSystem osParam,
DeploymentContext ctx, DeployableComponent componentParam,
NetworkHandler networkParam, DockerShellFactory shellFactoryParam) {
if(osParam == null)
throw new NullPointerException("operating system has to be set.");
myId = id;
client = clientParam;
imageHandler = new DockerImageHandler(osParam, new DockerOperatingSystemTranslator(), clientParam, componentParam);
deploymentContext = ctx;
shellFactory = shellFactoryParam;
myComponent = componentParam;
networkHandler = networkParam;
}
@Override
public ComponentInstanceId getComponentInstanceId() {
return myId;
}
@Override
public synchronized void doCreate() throws ContainerException {
try {
executeCreation();
} catch(DockerException de) {
throw new ContainerException("docker problems. cannot create container " + myId, de);
}
}
@Override
public void doInit(LifecycleStore store) throws ContainerException {
try {
DockerShell shell = doStartContainer();
shellFactory.installDockerShell(shell);
} catch(ContainerException ce) {
throw ce;
} catch(Exception ex) {
throw new ContainerException(ex);
}
}
@Override
public void doDestroy(boolean force) throws ContainerException {
/* docker ignores the flag */
try {
client.stopContainer(myId);
} catch(DockerException de) {
throw new ContainerException(de);
}
}
@Override
public InportAccessor getPortMapper() {
return ( (portName, clientState) -> {
try {
Integer portNumber = (Integer) deploymentContext.getProperty(portName, InPort.class);
int mapped = client.getPortMapping(myId, portNumber);
Integer i = Integer.valueOf(mapped);
clientState.registerValueAtLevel(PortRegistryTranslator.PORT_HIERARCHY_0, i);
clientState.registerValueAtLevel(PortRegistryTranslator.PORT_HIERARCHY_1, i);
clientState.registerValueAtLevel(PortRegistryTranslator.PORT_HIERARCHY_2, portNumber);
} catch(DockerException de) {
throw new ContainerException("coulnd not register all port mappings", de);
}
});
}
@Override
public String getLocalAddress() {
try {
return client.getContainerIp(myId);
} catch(DockerException de) {
// this means that that the container is not
// up and running; hence, no IP address is
// available. it is up to the caller to figure
// out the semantics of this state
}
return null;
}
@Override
public void completeInit() throws ContainerException {
shellFactory.closeShell();
}
@Override
public void prepare(HandlerType type) throws ContainerException {
if(type == LifecycleHandlerType.INSTALL) {
preInstallAction();
}
}
@Override
public void preprocessPortUpdate(PortDiff<DownstreamAddress> diffSet) throws ContainerException {
try {
DockerShell shell = client.getSideShell(myId);
prepareEnvironment(shell, diffSet);
shellFactory.installDockerShell(shell);
} catch(DockerException de) {
throw new ContainerException("cannot create shell for port updates.", de);
}
}
@Override
public void postprocessPortUpdate(PortDiff<DownstreamAddress> diffSet) {
shellFactory.closeShell();
}
@Override
public void postprocess(HandlerType type) {
if(type == LifecycleHandlerType.PRE_INSTALL) {
postPreInstall();
} else if(type == LifecycleHandlerType.POST_INSTALL) {
// TODO: do we have to make a snapshot after this? //
}
}
@Override
public void preprocessDetector(DetectorType type) throws ContainerException {
// nothing special to do; just create a shell and prepare an environment //
try {
DockerShell shell = client.getSideShell(myId);
prepareEnvironment(shell);
shellFactory.installDockerShell(shell);
} catch(DockerException de) {
throw new ContainerException("cannot create shell for port updates.", de);
}
}
@Override
public void postprocessDetector(DetectorType type) {
// nothing special to do; just create a shell //
shellFactory.closeShell();
}
private DockerShell doStartContainer() throws ContainerException {
final DockerShell dshell;
try {
dshell = client.startContainer(myId);
} catch(DockerException de) {
throw new ContainerException("cannot start container: " + myId, de);
}
shellFactory.installDockerShell(dshell);
return dshell;
}
private void preInstallAction() {
DockerShellWrapper w = shellFactory.createShell();
prepareEnvironment(w.shell);
}
private void postPreInstall() {
try {
imageHandler.runPostInstallAction(myId);
} catch (DockerException de) {
LOGGER.warn("could not update finalise image handling.", de);
}
}
private void prepareEnvironment(DockerShell dshell) {
prepareEnvironment(dshell, null);
}
private void prepareEnvironment(DockerShell dshell, PortDiff<DownstreamAddress> diff) {
BashExportBasedVisitor visitor = new BashExportBasedVisitor(dshell);
visitor.addEnvironmentVariable("TERM", "dumb");
networkHandler.accept(visitor, diff);
myComponent.accept(deploymentContext, visitor);
}
private void executeCreation() throws DockerException {
String target = imageHandler.doPullImages(myId);
Map<Integer,Integer> portsToSet = networkHandler.findPortsToSet(deploymentContext);
//@SuppressWarnings("unused") String dockerId =
client.createContainer(target, myId, portsToSet);
}
}
/*
* Copyright (c) 2014-2015 University of Ulm
*
* See the NOTICE file distributed with this work for additional information