/** * Copyright 2014-2017 Linagora, Université Joseph Fourier, Floralis * * The present code is developed in the scope of the joint LINAGORA - * Université Joseph Fourier - Floralis research program and is designated * as a "Result" pursuant to the terms and conditions of the LINAGORA * - Université Joseph Fourier - Floralis research program. Each copyright * holder of Results enumerated here above fully & independently holds complete * ownership of the complete Intellectual Property rights applicable to the whole * of said Results, and may freely exploit it in any manner which does not infringe * the moral rights of the other copyright holders. * * 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 net.roboconf.target.docker.internal; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.logging.Logger; import org.junit.After; import org.junit.Assert; import org.junit.Assume; import org.junit.Before; import org.junit.Test; import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.model.Container; import com.github.dockerjava.api.model.Image; import com.github.dockerjava.core.DefaultDockerClientConfig; import com.github.dockerjava.core.DefaultDockerClientConfig.Builder; import com.github.dockerjava.core.DockerClientBuilder; import net.roboconf.core.model.beans.Instance; import net.roboconf.core.model.helpers.InstanceHelpers; import net.roboconf.core.utils.Utils; import net.roboconf.target.api.TargetException; import net.roboconf.target.api.TargetHandlerParameters; import net.roboconf.target.docker.internal.DockerMachineConfigurator.RoboconfBuildImageResultCallback; /** * @author Pierre-Yves Gibello - Linagora * @author Pierre Bourret - Université Joseph Fourier */ public class DockerHandlerWithContainerTest { private final Logger logger = Logger.getLogger( getClass().getName()); private boolean dockerIsInstalled = true; private DockerClient docker; private String dockerImageId; private Map<String, String> msgCfg = new LinkedHashMap<>(); @Before public void setMessagingConfiguration() { this.msgCfg = new LinkedHashMap<>(); this.msgCfg.put("net.roboconf.messaging.type", "telepathy"); this.msgCfg.put("mindControl", "false"); this.msgCfg.put("psychosisProtection", "active"); } @Before public void checkDockerIsInstalled() throws Exception { Assume.assumeTrue( this.dockerIsInstalled ); try { DockerTestUtils.checkDockerIsInstalled(); prepareDockerTest(); } catch( IOException | InterruptedException e ) { this.logger.warning( "Tests are skipped because Docker is not installed or misconfigured." ); Utils.logException( this.logger, e ); this.dockerIsInstalled = false; Assume.assumeNoException( e ); } } @After public void dockerCleanup() { if( this.docker != null ) { DockerUtils.deleteImageIfItExists( this.dockerImageId, this.docker ); try { this.docker.close(); } catch( IOException e ) { // nothing } } } @Test public void testValidConfiguration() throws Exception { Assume.assumeTrue( this.dockerIsInstalled ); Map<String, String> targetProperties = loadTargetProperties(); DockerClient client = DockerUtils.createDockerClient( targetProperties ); Assert.assertNotNull( client ); } @Test public void testCreateAndTerminateVM() throws Exception { Assume.assumeTrue( this.dockerIsInstalled ); Map<String,String> targetProperties = loadTargetProperties(); testCreateAndTerminateVM( targetProperties ); } @Test public void testCreateAndTerminateVM_withOptions() throws Exception { Assume.assumeTrue( this.dockerIsInstalled ); Map<String,String> targetProperties = loadTargetProperties(); targetProperties.put( DockerHandler.OPTION_PREFIX_RUN + "cap-add", "SYS_PTRACE" ); testCreateAndTerminateVM( targetProperties ); } @Test( expected = TargetException.class ) public void testConfigureVM_invalidBaseImage() throws Exception { Assume.assumeTrue( this.dockerIsInstalled ); Map<String,String> targetProperties = loadTargetProperties(); targetProperties.put( DockerHandler.IMAGE_ID, "will-not-be-generated" ); targetProperties.put( DockerHandler.BASE_IMAGE, "oops81:unknown" ); DockerMachineConfigurator configurator = new DockerMachineConfigurator( targetProperties, this.msgCfg, "656sdf6sd", "/test", "app", new Instance()); try { configurator.dockerClient = this.docker; configurator.createImage( "will-not-be-generated" ); } finally { configurator.dockerClient = null; configurator.close(); Assert.assertNull( DockerUtils.findImageByIdOrByTag( "will-not-be-generated", this.docker )); } } @Test( expected = TargetException.class ) public void testCreateVM_missingParameters() throws Exception { Assume.assumeTrue( this.dockerIsInstalled ); DockerHandler target = new DockerHandler(); Map<String,String> targetProperties = loadTargetProperties(); targetProperties.remove( DockerHandler.IMAGE_ID ); TargetHandlerParameters parameters = new TargetHandlerParameters() .targetProperties( targetProperties ) .messagingProperties( this.msgCfg ) .applicationName( "roboconf" ) .domain( "my-domain" ) .scopedInstancePath( "test" ); target.createMachine( parameters ); } @Test public void checkImagesAreFoundCorrectly() { Assert.assertNull( DockerUtils.findImageByIdOrByTag( "oops81:unknown", this.docker )); Assert.assertNotNull( DockerUtils.findImageByIdOrByTag( "ubuntu", this.docker )); Assert.assertNotNull( DockerUtils.findImageByIdOrByTag( "ubuntu:latest", this.docker )); } @Test public void testDockerUtils_onLimits() { DockerUtils.deleteImageIfItExists( null, this.docker ); Assert.assertTrue( "No exception is thrown trying to delete a null image ID.", true ); DockerUtils.deleteImageIfItExists( "bla 11 4 2 bla", this.docker ); Assert.assertTrue( "No exception is thrown trying to delete something that does not exist.", true ); Container container = DockerUtils.findContainerByIdOrByName( "bla 11 4 2 bla", this.docker ); Assert.assertNull( container ); Image image = DockerUtils.findImageByIdOrByTag( null, this.docker ); Assert.assertNull( image ); image = DockerUtils.findImageByIdOrByTag( "invalid", this.docker ); Assert.assertNull( image ); } /** * Loads the target properties for the configuration of Docker. */ private Map<String,String> loadTargetProperties() throws Exception { URL res = Thread.currentThread().getContextClassLoader().getResource("conf/docker.properties"); File propertiesFile = new File( res.getFile()); Properties p = Utils.readPropertiesFile( propertiesFile ); HashMap<String,String> targetProperties = new HashMap<>(); for( Map.Entry<Object,Object> entry : p.entrySet()) targetProperties.put( entry.getKey().toString(), entry.getValue().toString()); if( this.dockerImageId != null ) targetProperties.put( DockerHandler.IMAGE_ID, this.dockerImageId ); return targetProperties; } /** * Prepares the docker environment (image, etc...) for testing. * @throws IOException */ private void prepareDockerTest() throws Exception { final String tag = "roboconf-test"; Builder config = DefaultDockerClientConfig.createDefaultConfigBuilder(); config.withDockerHost( "tcp://localhost:" + DockerTestUtils.DOCKER_TCP_PORT ); this.docker = DockerClientBuilder.getInstance( config.build()).build(); File baseDir = new File( Thread.currentThread().getContextClassLoader().getResource("./image").getFile()); String builtImageId = this.docker.buildImageCmd(baseDir) .withNoCache( true ).withTag( tag ) .exec( new RoboconfBuildImageResultCallback()) .awaitImageId(); this.logger.finest( "Built image ID: " + builtImageId ); List<Image> images = this.docker.listImagesCmd().exec(); images = images == null ? new ArrayList<Image>( 0 ) : images; Image img = DockerUtils.findImageByTag( tag, images ); this.dockerImageId = img.getId(); } /** * Creates, checks and terminates a Docker container. * @param targetProperties the target properties * @throws Exception */ private void testCreateAndTerminateVM( Map<String,String> targetProperties ) throws Exception { DockerHandler target = new DockerHandler(); Instance scopedInstance = new Instance( "test-596598515" ); String path = InstanceHelpers.computeInstancePath( scopedInstance ); TargetHandlerParameters parameters = new TargetHandlerParameters() .targetProperties( targetProperties ) .messagingProperties( this.msgCfg ) .applicationName( "roboconf" ) .domain( "my-domain" ) .scopedInstancePath( path ); try { target.start(); String containerId = target.createMachine( parameters ); Assert.assertNotNull( containerId ); Assert.assertNull( scopedInstance.data.get( Instance.MACHINE_ID )); // DockerMachineConfigurator is implemented in such a way that it runs only // once when the image already exists. However, we must wait for the thread pool // executor to pick up the configurator. target.configureMachine( parameters, containerId, scopedInstance ); // Be careful, the Docker target changes the machine ID containerId = DockerTestUtils.waitForMachineId( containerId, scopedInstance.data, DockerTestUtils.DOCKER_CONFIGURE_TIMEOUT); Assert.assertNotNull( containerId ); // Check the machine is running Assert.assertTrue( target.isMachineRunning( parameters, containerId )); // Just for verification, try to terminate an invalid container target.terminateMachine( parameters, "invalid identifier" ); Assert.assertTrue( target.isMachineRunning( parameters, containerId )); // Terminate the container target.terminateMachine( parameters, containerId ); Assert.assertFalse( target.isMachineRunning( parameters, containerId )); } finally { target.stop(); } } }