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