<commit_before>/*
 * 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.List;
import java.util.Map;

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.GlobalRegistryAccessor;
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.NetworkHandler;
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.lca.registry.RegistrationException;
import de.uniulm.omi.cloudiator.lance.lifecycle.LifecycleController;
import de.uniulm.omi.cloudiator.lance.lifecycle.LifecycleHandlerType;
import de.uniulm.omi.cloudiator.lance.lifecycle.LifecycleStore;

public class DockerContainerLogic implements ContainerLogic {
		
	private final ComponentInstanceId myId;
	private final DockerConnector client;
	private final DockerShellFactory shellFactory = new DockerShellFactory();
	
	private final DeployableComponent myComponent;
	private final DeploymentContext deploymentContext;

	private final LifecycleController controller;
	private final GlobalRegistryAccessor registryAccessor;
		
	private final DockerImageHandler imageHandler;
	private final NetworkHandler portHandler;
	
	DockerContainerLogic(ComponentInstanceId id, DockerConnector _client, DockerOperatingSystemTranslator translator, 
							GlobalRegistryAccessor accessor, DeployableComponent comp, DeploymentContext ctx, OperatingSystem _os,
							NetworkHandler network) {
		this(id, _client, _os, translator, ctx, accessor, comp, network);
	}
	
	private  DockerContainerLogic(ComponentInstanceId id, DockerConnector _client, OperatingSystem _os,
								DockerOperatingSystemTranslator _translator, DeploymentContext ctx,
								GlobalRegistryAccessor accessor, DeployableComponent _component, NetworkHandler _network) {
		
		if(_os == null) throw new NullPointerException("operating system has to be set.");
		
		myId = id;
		client = _client;
		imageHandler = new DockerImageHandler(_os, _translator, _client);
		deploymentContext = ctx;
		myComponent = _component;
		controller = new LifecycleController(myComponent.getLifecycleStore(), imageHandler.getOperatingSystem(), shellFactory);
		registryAccessor = accessor;
		
		portHandler = _network;
		try { registryAccessor.init(myId); }
		catch(RegistrationException re) { throw new IllegalStateException("cannot start container", re); }
	}
	
	//@Override public ComponentInstanceId getId() { return myId; }
		
	@Override
	public synchronized void doCreate() throws ContainerException {
		try { 
			executeCreation(); 
		} catch(RegistrationException re) {
			throw new ContainerException("cannot access registry", re);
		} catch(DockerException de) {
			throw new ContainerException("docker problems. cannot create container " + myId, de);
		}
		
		// add dummy values so that other components are aware of this instance, 
		// but can see that it is not ready for use yet.
		portHandler.publishLocalData(myId);
	}
	
	private void executeCreation() throws RegistrationException, DockerException {
		registryAccessor.updateState(myId, LifecycleHandlerType.INIT);
		String target = imageHandler.doPullImages(myId, createComponentInstallId());
		portHandler.initPorts(DockerContainerManagerFactory.PORT_HIERARCHY_2, "<unknown>");
		Map<Integer,Integer> ports_to_set = portHandler.findPortsToSet(deploymentContext);
		//@SuppressWarnings("unused") String dockerId = 
		client.createContainer(target, myId, ports_to_set);
	}
	
	@Override
	public void doInit(LifecycleStore store) throws ContainerException {
		try {
			registryAccessor.updateState(myId, LifecycleHandlerType.PRE_INSTALL);
			final DockerShell _dshell = doStartContainer();
			
			registryAccessor.updateState(myId, LifecycleHandlerType.INSTALL);
			executeInstallation();
			registryAccessor.updateState(myId, LifecycleHandlerType.POST_INSTALL);
			registryAccessor.updateState(myId, LifecycleHandlerType.PRE_START);
			executeConfiguration(_dshell);
			
			executeStart();
		} catch(ContainerException ce) {
			throw ce;
		} catch(Exception ex) {
			throw new ContainerException(ex);
		} finally {
			shellFactory.closeShell();
		}
	}

	@Override
	public void doDestroy() {
		throw new UnsupportedOperationException();
	}
	
	private DockerShell doStartContainer() {
		final DockerShell _dshell;
		try { _dshell = client.startContainer(myId); }
		catch(DockerException de) {
			throw new RuntimeException(new ContainerException("cannot start container: " + myId, de));
		}
		//FIXME: make sure that start detector has been run successfully 
		//FIXME: make sure that stop detectors run periodically //
		shellFactory.installDockerShell(_dshell);
		return _dshell;
	}
	
	private void executeStart() throws DockerException, ContainerException, RegistrationException {
		registryAccessor.updateState(myId, LifecycleHandlerType.PRE_START);
		registryAccessor.updateState(myId, LifecycleHandlerType.START);
		controller.blockingStart();
		
		registryAccessor.updateState(myId, LifecycleHandlerType.POST_START);
		// only that we have started, can the ports be 
		// retrieved and then registered at the registry // 
		portHandler.publishLocalData(myId);
		portHandler.startPortUpdaters();
	}
	
	private String createComponentInstallId() {
		return "dockering." + "component." + myComponent.getComponentId().toString(); 
	}
	
	private void executeInstallation() throws DockerException {<commit_msg>remove use of RuntimeException and use real exception instead.<commit_after>/*
 * 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.List;
import java.util.Map;

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.GlobalRegistryAccessor;
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.NetworkHandler;
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.lca.registry.RegistrationException;
import de.uniulm.omi.cloudiator.lance.lifecycle.LifecycleController;
import de.uniulm.omi.cloudiator.lance.lifecycle.LifecycleHandlerType;
import de.uniulm.omi.cloudiator.lance.lifecycle.LifecycleStore;

public class DockerContainerLogic implements ContainerLogic {
		
	private final ComponentInstanceId myId;
	private final DockerConnector client;
	private final DockerShellFactory shellFactory = new DockerShellFactory();
	
	private final DeployableComponent myComponent;
	private final DeploymentContext deploymentContext;

	private final LifecycleController controller;
	private final GlobalRegistryAccessor registryAccessor;
		
	private final DockerImageHandler imageHandler;
	private final NetworkHandler portHandler;
	
	DockerContainerLogic(ComponentInstanceId id, DockerConnector _client, DockerOperatingSystemTranslator translator, 
							GlobalRegistryAccessor accessor, DeployableComponent comp, DeploymentContext ctx, OperatingSystem _os,
							NetworkHandler network) {
		this(id, _client, _os, translator, ctx, accessor, comp, network);
	}
	
	private  DockerContainerLogic(ComponentInstanceId id, DockerConnector _client, OperatingSystem _os,
								DockerOperatingSystemTranslator _translator, DeploymentContext ctx,
								GlobalRegistryAccessor accessor, DeployableComponent _component, NetworkHandler _network) {
		
		if(_os == null) throw new NullPointerException("operating system has to be set.");
		
		myId = id;
		client = _client;
		imageHandler = new DockerImageHandler(_os, _translator, _client);
		deploymentContext = ctx;
		myComponent = _component;
		controller = new LifecycleController(myComponent.getLifecycleStore(), imageHandler.getOperatingSystem(), shellFactory);
		registryAccessor = accessor;
		
		portHandler = _network;
		try { registryAccessor.init(myId); }
		catch(RegistrationException re) { throw new IllegalStateException("cannot start container", re); }
	}
	
	//@Override public ComponentInstanceId getId() { return myId; }
		
	@Override
	public synchronized void doCreate() throws ContainerException {
		try { 
			executeCreation(); 
		} catch(RegistrationException re) {
			throw new ContainerException("cannot access registry", re);
		} catch(DockerException de) {
			throw new ContainerException("docker problems. cannot create container " + myId, de);
		}
		
		// add dummy values so that other components are aware of this instance, 
		// but can see that it is not ready for use yet.
		portHandler.publishLocalData(myId);
	}
	
	private void executeCreation() throws RegistrationException, DockerException {
		registryAccessor.updateState(myId, LifecycleHandlerType.INIT);
		String target = imageHandler.doPullImages(myId, createComponentInstallId());
		portHandler.initPorts(DockerContainerManagerFactory.PORT_HIERARCHY_2, "<unknown>");
		Map<Integer,Integer> ports_to_set = portHandler.findPortsToSet(deploymentContext);
		//@SuppressWarnings("unused") String dockerId = 
		client.createContainer(target, myId, ports_to_set);
	}
	
	@Override
	public void doInit(LifecycleStore store) throws ContainerException {
		try {
			registryAccessor.updateState(myId, LifecycleHandlerType.PRE_INSTALL);
			final DockerShell _dshell = doStartContainer();
			
			registryAccessor.updateState(myId, LifecycleHandlerType.INSTALL);
			executeInstallation();
			registryAccessor.updateState(myId, LifecycleHandlerType.POST_INSTALL);
			registryAccessor.updateState(myId, LifecycleHandlerType.PRE_START);
			executeConfiguration(_dshell);
			
			executeStart();
		} catch(ContainerException ce) {
			throw ce;
		} catch(Exception ex) {
			throw new ContainerException(ex);
		} finally {
			shellFactory.closeShell();
		}
	}

	@Override
	public void doDestroy() {
		throw new UnsupportedOperationException();
	}
	
	private DockerShell doStartContainer() throws ContainerException {
		final DockerShell _dshell;
		try { _dshell = client.startContainer(myId); }
		catch(DockerException de) {
			throw new ContainerException("cannot start container: " + myId, de);
		}
		//FIXME: make sure that start detector has been run successfully 
		//FIXME: make sure that stop detectors run periodically //
		shellFactory.installDockerShell(_dshell);
		return _dshell;
	}
	
	private void executeStart() throws DockerException, ContainerException, RegistrationException {
		registryAccessor.updateState(myId, LifecycleHandlerType.PRE_START);
		registryAccessor.updateState(myId, LifecycleHandlerType.START);
		controller.blockingStart();
		
		registryAccessor.updateState(myId, LifecycleHandlerType.POST_START);
		// only that we have started, can the ports be 
		// retrieved and then registered at the registry // 
		portHandler.publishLocalData(myId);
		portHandler.startPortUpdaters();
	}
	
	private String createComponentInstallId() {
		return "dockering." + "component." + myComponent.getComponentId().toString(); 
	}
	
	private void executeInstallation() throws DockerException {
{'fraction_non_alphanumeric': 0.0843233000510167, 'fraction_numerical': 0.0018949056191239705, 'mean_word_length': 5.457411764705882, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1699653', 'n_tokens_mistral': 4201, 'n_tokens_neox': 3748, 'n_words': 953}