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