/** * 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.integration.tests.dm.byhand; import static org.ops4j.pax.exam.CoreOptions.mavenBundle; import static org.ops4j.pax.exam.CoreOptions.systemProperty; import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.editConfigurationFilePut; import java.io.File; import java.net.Inet4Address; import java.net.InetAddress; import java.net.NetworkInterface; import java.util.ArrayList; import java.util.Arrays; import java.util.Enumeration; import java.util.List; import java.util.logging.Logger; import javax.inject.Inject; import org.junit.Assert; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.ops4j.pax.exam.Configuration; import org.ops4j.pax.exam.Option; import org.ops4j.pax.exam.ProbeBuilder; import org.ops4j.pax.exam.TestProbeBuilder; import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy; import org.ops4j.pax.exam.spi.reactors.PerMethod; import net.roboconf.core.internal.tests.TestUtils; import net.roboconf.core.model.beans.ApplicationTemplate; import net.roboconf.core.model.beans.Instance; import net.roboconf.core.model.beans.Instance.InstanceStatus; import net.roboconf.core.model.helpers.InstanceHelpers; import net.roboconf.core.utils.Utils; import net.roboconf.dm.management.ManagedApplication; import net.roboconf.dm.management.Manager; import net.roboconf.integration.tests.commons.AbstractIntegrationTest; import net.roboconf.integration.tests.commons.ItConfigurationBean; import net.roboconf.integration.tests.commons.internal.ItUtils; import net.roboconf.integration.tests.commons.internal.parameterized.IMessagingConfiguration; import net.roboconf.integration.tests.commons.internal.runners.RoboconfITConfiguration; import net.roboconf.integration.tests.commons.internal.runners.RoboconfPaxRunner; import net.roboconf.integration.tests.dm.probes.DmTest; import net.roboconf.messaging.rabbitmq.RabbitMqConstants; import net.roboconf.target.docker.internal.DockerTestUtils; /** * A test where the DM launches Docker containers and checks agents work. * <p> * This test takes quite a long time to run (more than 3 minutes). It also requires * a high internet connection to update the Docker image system (sudo apt-get update...). * </p> * <p> * It was tested on the campus of Université Joseph Fourier (which has a very good internet * connection). It also needs a local Docker install and a local RabbitMQ with "roboconf/roboconf" * credentials ("localhost/guest/guest" does not work - "localhost" does not designate the same thing * for the Docker container and the host machine - and "guest/guest" only works with "localhost"). * </p> * <p> * For all these reasons, this test is disabled (@ignored). You can activate it when you want to * verify Docker support with the DM, agents, Karaf and OSGi environments. * </p> * * @author Vincent Zurczak - Linagora */ @RunWith( RoboconfPaxRunner.class ) @RoboconfITConfiguration( withDocker = true, withComplexRabbitMq = true ) @ExamReactorStrategy( PerMethod.class ) @Ignore public class LocalDockerWithAgentChecksTest extends DmTest { private static final String APP_LOCATION = "my.app.location"; private static final String AGENT_LOC = "agent.loc"; @Inject protected Manager manager; @ProbeBuilder public TestProbeBuilder probeConfiguration( TestProbeBuilder probe ) { // We need to specify the classes we need // and that come from external modules. probe.addTest( DmTest.class ); probe.addTest( TestUtils.class ); probe.addTest( AbstractIntegrationTest.class ); probe.addTest( IMessagingConfiguration.class ); probe.addTest( ItConfigurationBean.class ); return probe; } @Override @Configuration public Option[] config() throws Exception { List<Option> options = new ArrayList<> (); options.addAll( Arrays.asList( super.config())); Logger logger = Logger.getLogger( getClass().getName()); // Indicate the location of the application to deploy File resourcesDirectory = TestUtils.findApplicationDirectory( "lamp" ); String appLocation = resourcesDirectory.getAbsolutePath(); options.add( systemProperty( APP_LOCATION ).value( appLocation )); // Configure the DM's messaging String ipAddress = findIpAddress(); logger.info( "Configuring the DM with IP " + ipAddress ); options.add( editConfigurationFilePut( "etc/net.roboconf.messaging.rabbitmq.cfg", "net.roboconf.messaging.rabbitmq.server.ip", ipAddress )); options.add( editConfigurationFilePut( "etc/net.roboconf.messaging.rabbitmq.cfg", RabbitMqConstants.RABBITMQ_SERVER_USERNAME, RoboconfPaxRunner.RBCF_USER )); options.add( editConfigurationFilePut( "etc/net.roboconf.messaging.rabbitmq.cfg", RabbitMqConstants.RABBITMQ_SERVER_PASSWORD, RoboconfPaxRunner.RBCF_USER )); // Install Docker support String roboconfVersion = ItUtils.findRoboconfVersion(); options.add( mavenBundle() .groupId( "net.roboconf" ) .artifactId( "roboconf-target-docker" ) .version( roboconfVersion ) .start()); // Prepare the location of the agent's archive StringBuilder sb = new StringBuilder(); sb.append( ".m2/repository/net/roboconf/roboconf-karaf-dist-agent/" ); sb.append( roboconfVersion ); sb.append( "/roboconf-karaf-dist-agent-" ); sb.append( roboconfVersion ); sb.append( ".tar.gz" ); File f = new File( System.getProperty( "user.home" ), sb.toString()); options.add( systemProperty( AGENT_LOC ).value( f.getCanonicalPath())); return options.toArray( new Option[ options.size()]); } @Test public void run() throws Exception { // Load the application String appLocation = System.getProperty( APP_LOCATION ); ApplicationTemplate tpl = this.manager.applicationTemplateMngr().loadApplicationTemplate( new File( appLocation )); ManagedApplication ma = this.manager.applicationMngr().createApplication( "test", null, tpl ); Assert.assertNotNull( ma ); Assert.assertEquals( 1, this.manager.applicationMngr().getManagedApplications().size()); // Write Docker properties File appDir = new File( this.manager.configurationMngr().getWorkingDirectory(), "application-templates/Legacy LAMP - sample/graph" ); Assert.assertTrue( appDir.isDirectory()); File resDir = new File( appDir, "VM" ); Assert.assertTrue( resDir.mkdir()); String agentLocation = System.getProperty( AGENT_LOC ); Assert.assertNotNull( agentLocation ); Assert.assertTrue( new File( agentLocation ).exists()); // Prepare recipes for components resDir = new File( appDir, "Apache" ); Assert.assertTrue( resDir.mkdir()); Utils.writeStringInto( "#!/bin/bash\necho apache > apache.txt", new File( resDir, "deploy.sh" )); resDir = new File( appDir, "Tomcat" ); Assert.assertTrue( resDir.mkdir()); Utils.writeStringInto( "#!/bin/bash\necho tomcat > tomcat.txt", new File( resDir, "deploy.sh" )); resDir = new File( appDir, "MySQL" ); Assert.assertTrue( resDir.mkdir()); Utils.writeStringInto( "#!/bin/bash\necho mysql > mysql.txt", new File( resDir, "deploy.sh" )); // Wait few seconds for the Docker handler to be registered Thread.sleep( 1000 * 4 ); // Instantiate root instances Instance rootInstance = InstanceHelpers.findInstanceByPath( ma.getApplication(), "/MySQL VM" ); Assert.assertNotNull( rootInstance ); Assert.assertEquals( InstanceStatus.NOT_DEPLOYED, rootInstance.getStatus()); Instance anotherRootInstance = InstanceHelpers.findInstanceByPath( ma.getApplication(), "/Tomcat VM 1" ); Assert.assertNotNull( anotherRootInstance ); Assert.assertEquals( InstanceStatus.NOT_DEPLOYED, anotherRootInstance.getStatus()); // Create target properties StringBuilder sb = new StringBuilder(); sb.append( "id = docker-target\n" ); sb.append( "handler = docker\n" ); sb.append( "docker.endpoint = tcp://localhost:" + DockerTestUtils.DOCKER_TCP_PORT ); sb.append( "\ndocker.user = roboconf-it\ndocker.generate.image = true\n" ); sb.append( "docker.generate.image = true\n" ); sb.append( "docker.agent.package = " ); sb.append( agentLocation ); String targetId = this.manager.targetsMngr().createTarget( sb.toString()); this.manager.targetsMngr().associateTargetWith( targetId, ma.getApplication(), null ); // Try deployments Logger logger = Logger.getLogger( getClass().getName()); logger.info( "About to deploy the first root instance." ); try { // The image is generated once, on the first deployment. // 30 seconds are enough if the internet speed connection is very good... this.manager.instancesMngr().changeInstanceState( ma, rootInstance, InstanceStatus.DEPLOYED_STARTED ); Thread.sleep( 1000 * 80 ); Assert.assertEquals( InstanceStatus.DEPLOYED_STARTED, rootInstance.getStatus()); logger.info( "The first root instance was sucessfully deployed." ); // The image is reused, so we only create a new container logger.info( "About to deploy the second root instance." ); this.manager.instancesMngr().changeInstanceState( ma, anotherRootInstance, InstanceStatus.DEPLOYED_STARTED ); Thread.sleep( 1000 * 10 ); Assert.assertEquals( InstanceStatus.DEPLOYED_STARTED, anotherRootInstance.getStatus()); Assert.assertEquals( InstanceStatus.DEPLOYED_STARTED, rootInstance.getStatus()); logger.info( "The second root instance was sucessfully deployed." ); } finally { // Undeploy them all logger.info( "About to undeploy all the root instances." ); this.manager.instancesMngr().changeInstanceState( ma, rootInstance, InstanceStatus.NOT_DEPLOYED ); this.manager.instancesMngr().changeInstanceState( ma, anotherRootInstance, InstanceStatus.NOT_DEPLOYED ); Thread.sleep( 300 ); Assert.assertEquals( InstanceStatus.NOT_DEPLOYED, anotherRootInstance.getStatus()); Assert.assertEquals( InstanceStatus.NOT_DEPLOYED, rootInstance.getStatus()); } } /** * Finds a Docker-reachable IP address for this machine. * @return an IP address (which will never be 127.0.0.1) * @throws Exception if no IP address was found */ private String findIpAddress() throws Exception { String ipAddress = null; Enumeration<?> e = NetworkInterface.getNetworkInterfaces(); loop: while( e.hasMoreElements()) { NetworkInterface n = (NetworkInterface) e.nextElement(); Enumeration<?> ee = n.getInetAddresses(); while( ee.hasMoreElements()) { InetAddress i = (InetAddress) ee.nextElement(); if( i instanceof Inet4Address && ! "127.0.0.1".equals( i.getHostAddress())) { ipAddress = i.getHostAddress(); break loop; } } } if( ipAddress == null ) throw new Exception( "No IP address was found." ); return ipAddress; } }