View Javadoc
1   package org.apache.turbine.testcontainer;
2   
3   import static junit.framework.TestCase.assertEquals;
4   import static junit.framework.TestCase.assertFalse;
5   import static junit.framework.TestCase.assertTrue;
6   
7   import java.io.File;
8   import java.nio.file.FileSystems;
9   import java.nio.file.Path;
10  import java.nio.file.Paths;
11  import java.sql.Connection;
12  import java.sql.DriverManager;
13  import java.sql.PreparedStatement;
14  import java.sql.ResultSet;
15  import java.sql.SQLException;
16  
17  import org.apache.logging.log4j.LogManager;
18  import org.apache.logging.log4j.Logger;
19  import org.junit.jupiter.api.Assertions;
20  import org.junit.jupiter.api.BeforeAll;
21  import org.junit.jupiter.api.BeforeEach;
22  import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
23  import org.junit.jupiter.api.Order;
24  import org.junit.jupiter.api.Tag;
25  import org.junit.jupiter.api.Test;
26  import org.junit.jupiter.api.TestMethodOrder;
27  import org.testcontainers.containers.GenericContainer;
28  import org.testcontainers.images.builder.ImageFromDockerfile;
29  import org.testcontainers.junit.jupiter.Container;
30  import org.testcontainers.junit.jupiter.Testcontainers;
31  
32  /**
33   * Steps to run this
34   *
35   * Requirements:
36   * <li>Unix: (Debian stretch tested):set <code>DOCKER_HOST=unix:///var/run/docker.sock</code> in docker-java.properties
37   * (find the template in conf/docker-resources/db/dj.p.template) and comment out all other environment keys.
38   * <li>Windows 10: Docker Desktop should provide all required configuration by default or
39   * you need to create a local machine, e.g. with <code>docker-machine -d hyperv <vmname-default></code>
40   * For more information https://docs.docker.com/machine/get-started/, https://docs.docker.com/machine/drivers/hyper-v/.
41   * <li>Windows 7/VirtualBox: copy DOCKER_* properties to ~/.docker-java.properties or docker-java.properties in classpath..
42   * To get the environment run: <code>docker-machine env default</code>, if your default docker machine is named default.
43   * Verify the name with <code>docker-machine ls</code>.
44   *
45   * Turbine pom.xml has folder conf/docker-resources enabled as test-resource, you may put the files there.
46   * You may need to copy machines/&lt;docker-machine-name&gt;/certs from DOCKER_CERT_PATH to local path ~/.docker/machine/certs
47   *
48   * Note/Hints:
49   * <li>Testcontainers starts docker-machine, if not started.
50   * <li>Windows 7: Before running manually any docker-machine command, you must close the VirtualBox GUI if opened.
51   * <li>To get results from <code>docker images</code>, you have to set the environment variables, see output from <code>docker-machine env <vmname></code>.
52   *
53   * Lookup of repository:
54   *
55   * Testcontainers checks
56   * <li>~/.testcontainers.properties, then <code>classpath/testcontainers.properties</code>
57   * <li>~/.docker-java.properties, then docker-java.properties -> set DOCKER_* properties,
58   * may set DOCKER_CERT_PATHalways with forward slashes.
59   * <li>At last also ~/.docker/config.json is checked for username/password for docker.io
60   * Additional
61   * More info for database init sql:
62   * <li>https://www.testcontainers.org/modules/databases/mysql/
63   * <li>https://www.testcontainers.org/modules/databases/#using-an-init-script-from-a-file
64   *
65   * Bugs: docker virtualbox vm seems to auto pause.
66   * Check your docker vm with <code>docker-machine ls</code> and <code>docker-machine start <vmname></code>.
67   *
68   * @author gkallidis
69   *
70   */
71  @TestMethodOrder(OrderAnnotation.class)
72  @Testcontainers
73  @Tag("docker")
74  class BuildContainerWithDockerfileTest {
75  
76     public static final Path RESOURCE_PATH =
77             FileSystems.getDefault().getPath(".").resolve("conf/docker-resources/db/");
78  
79     private static Logger log = LogManager.getLogger();
80  
81     public static int SERVICE_PORT = 3306;
82  
83     public static String DATABASE_NAME = "default";
84  
85     Connection connection;
86  
87     @Container
88     public static GenericContainer MY_SQL_CONTAINER =   new GenericContainer<>(
89             new ImageFromDockerfile()
90              .withFileFromPath(".", RESOURCE_PATH)
91          ).withExposedPorts( SERVICE_PORT ) //.withStartupAttempts( 2 )
92           .withEnv(  "MYSQL_DATABASE", DATABASE_NAME )
93           .withEnv( "MYSQL_USER", "userdb"  )
94           .withEnv( "MYSQL_PASSWORD", "test1234" )
95           .withEnv( "MYSQL_ROOT_PASSWORD","test1234" );
96  
97  // reduce dependencies, but might use for debugging
98  //    MY_SQL_CONTAINER = new MySQLContainer<>()
99  //   .withDatabaseName( DATABASE_NAME).withUsername( "userdb" ).withPassword( "test1234" )
100 //   .withInitScript( "./db/mysql/initdb.d/data.sql" )
101 //   .withExposedPorts( SERVICEPORT )
102 
103    @BeforeAll
104    public static void init() {
105 
106       MY_SQL_CONTAINER.setStartupAttempts( 3 );   // see MySQLContainer
107    }
108 
109    @BeforeEach
110    public void before() throws Exception {
111       connection = getConnection();
112    }
113 
114    @Test
115    @Order(2)
116    void createUser() throws SQLException {
117       if (connection == null) return;
118       try (PreparedStatement preparedStatement =
119               connection.prepareStatement(
120                  "INSERT INTO TURBINE_USER (USER_ID,LOGIN_NAME,PASSWORD_VALUE,FIRST_NAME,LAST_NAME) values (?,?,?,?,?)")) {
121          preparedStatement.setString(1, "4");
122          preparedStatement.setString(2, "kzipfel");
123          preparedStatement.setString(3, "kzipfel");
124          preparedStatement.setString(4, "Konrad");
125          preparedStatement.setString(5, "Zipfel");
126          assertFalse(preparedStatement.execute());
127          Assertions.assertEquals(1, preparedStatement.getUpdateCount());
128       }
129    }
130 
131    @Test
132    @Order(1)
133    void selectExistingUser() throws SQLException {
134       if (connection == null) return;
135       try (PreparedStatement preparedStatement =
136               connection.prepareStatement(
137                  "select USER_ID, LAST_NAME, FIRST_NAME from TURBINE_USER where USER_ID=?")) {
138          preparedStatement.setString(1, "1");
139          ResultSet resultSet = preparedStatement.executeQuery();
140          assertTrue(resultSet.next());
141          assertEquals("Admin", resultSet.getString("LAST_NAME"));
142          assertEquals("", resultSet.getString("FIRST_NAME"));
143          resultSet.close();
144       }
145    }
146 
147    @Test
148    @Order(3)
149    void selectNewUser() throws SQLException {
150       if (connection == null) return;
151       try (PreparedStatement preparedStatement =
152               connection.prepareStatement(
153                  "select USER_ID, LAST_NAME, FIRST_NAME from TURBINE_USER where USER_ID=?")) {
154          preparedStatement.setString(1, "4");
155          ResultSet resultSet = preparedStatement.executeQuery();
156          assertTrue(resultSet.next());
157          assertEquals("Zipfel", resultSet.getString("LAST_NAME"));
158          assertEquals("Konrad", resultSet.getString("FIRST_NAME"));
159       }
160    }
161 
162    public static Connection getConnection() throws SQLException {
163      String jdbcStr = generateJdbcUrl();
164      if (jdbcStr == null) {
165          return null;
166      }
167       return DriverManager
168          .getConnection(jdbcStr, "userdb", "test1234");
169    }
170 
171    // https://www.testcontainers.org/modules/databases/
172    // String.format("jdbc:tc:mysql:5.7.22://%s/%s", "dummy_host",
173    // "test"); this will use database test, but allows e.g. custom cfg: ?TC_MY_CNF=x.cfg
174    // TODO inform torque about mapped port, use overriding configuration in torque 4.1
175    public static String generateJdbcUrl() {
176       if (MY_SQL_CONTAINER == null) { return null; }
177       if (!MY_SQL_CONTAINER.isRunning()) {
178           MY_SQL_CONTAINER.start();
179       }
180 
181       String serviceHost = MY_SQL_CONTAINER.getContainerIpAddress();
182       Integer mappedPort = MY_SQL_CONTAINER.getMappedPort(SERVICE_PORT);// e.g. 32811
183       log.info("generate jdbc url from {}, mapped Port: {}, bounded port: {}", serviceHost, mappedPort, MY_SQL_CONTAINER.getBoundPortNumbers());
184 
185 //      if (MY_SQL_CONTAINER instanceof MySQLContainer) {
186 //          String genJDBC = ((MySQLContainer)MY_SQL_CONTAINER).getJdbcUrl();
187 //          log.info( "generated connect url: {}", genJDBC);
188 //      }
189       String targetJDBC =
190       String.format("jdbc:mysql://%s:%d/%s?loggerLevel=OFF", serviceHost,
191                     mappedPort, DATABASE_NAME);
192       // changing the jdbc string prefix to  jdbc:tc:mysql does handle the test database setup,
193       // https://www.testcontainers.org/modules/databases/jdbc/
194       log.info( "used connect url: {}", targetJDBC);
195       return targetJDBC;
196    }
197 
198 }