diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..57f1cb2
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/.idea/
\ No newline at end of file
diff --git a/.idea/.gitignore b/.idea/.gitignore
deleted file mode 100644
index 26d3352..0000000
--- a/.idea/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-# Default ignored files
-/shelf/
-/workspace.xml
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
deleted file mode 100644
index 08de2d0..0000000
--- a/.idea/compiler.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
deleted file mode 100644
index aa00ffa..0000000
--- a/.idea/encodings.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
deleted file mode 100644
index 4a35cfe..0000000
--- a/.idea/jarRepositories.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
deleted file mode 100644
index d7270e9..0000000
--- a/.idea/misc.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml
deleted file mode 100644
index e96534f..0000000
--- a/.idea/uiDesigner.xml
+++ /dev/null
@@ -1,124 +0,0 @@
-
-
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
-
-
-
- -
-
-
-
-
-
- -
-
-
-
-
-
- -
-
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
-
-
- -
-
-
- -
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index 94a25f7..0000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 185093f..d12c620 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,19 +4,28 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
- me.aurium
- tick
+
+ xyz.auriium
+ insect
+ 0.3.0
+
+
+ tickbox-parent
1.0.0
+ TickBox
+ Testcontainers alternative
+
+
+ scm:git:https://github.com/Auriium/TickBox.git
+
+
- tick-full
- tick-rapid
- tick-api
+ tickbox-api
+ tickbox-containers-sql
- 1.8
-
${compiler.version}
${compiler.version}
@@ -24,7 +33,7 @@
3.6.0
3.6.3
- 1.15.2
+ 3.2.7
@@ -52,13 +61,23 @@
-
+
+
+
+ xyz.auriium
+ tickbox-api
+ 1.0.0
+
+
+
+
+
- repsy
- ElytraForceRepo
- https://repo.repsy.io/mvn/elytraforce/default
+ central-repo
+ https://repo.auriium.xyz/releases
-
+
+
+
-
\ No newline at end of file
diff --git a/readme.md b/readme.md
index 2d691eb..7b55542 100644
--- a/readme.md
+++ b/readme.md
@@ -1 +1,73 @@
-Powerful testcontainer alternative with actually object oriented code, no bullshitty static abuse, no lombok, no ResourceReaper, no piss in the cup
+
+# TickBox
+
+A dockerized Database Container creation system + maven plugins.
+
+# Description
+
+A tool meant for developers who really really don't want to use the mess that is TestContainers.
+
+Comes in both code-based (tickbox-api) and maven plugin (tickbox-plugin) formats depending on
+your use case (Whether you want a single container for all tests or need multiple containers for
+multiple tests.)
+
+# Warning
+
+TickBox is not TestContainers. It isn't really even meant to fill the same role that testcontainers fills.
+Tick is a general purpose container creation library fit for use with JDBC. TestContainers is explicitly for testing.
+
+While Tick is also very useful in the testing environment, Tick explicitly avoids hackery and "magic code"
+that applications like TestContainers may implement in order to smoothen the testing experience.
+In TestContainers, various reflective hacks are done in order to allow containers to stop at the end of a JUnit test.
+A separate docker container is deployed just to make sure resources do not escape. (ryuk)
+
+In TestContainers, I try to explicitly avoid magic code/hackery/static abuse while also offering you the option of choice.
+TestContainers is brittle and static in design, and if you want to change something you'll have to dig deep into
+the archaic, bloated source code and edit it in yourself. If you want to change something in TickBox, write a new
+implementation of a single interface, or change a value in a configuration object.
+
+The biggest part of TestContainers for me that I attempt to give choice with is the ResourceReaper,
+or in our case, the ResourceManager. In TestContainers, it is a static part of the program
+that is essential, forcing Ryuk and runtime shutdown hooks down your throat. Here, you may choose
+if you would like the HookResourceManager (Features both automatic closing of containers on jvm shutdown
+as well as closing of containers and images when the main Tick instance is closed) or the EmptyResourceManager
+(which allows you to manually and explicitly remove resources left behind)
+
+# Unix Info
+
+If you want to use TickBox on Unix (MacOS or Linux) make sure that Docker is set up so it does
+not need the sudo command to run. TickBox (And TestContainers) use console commands internally
+to interact with Docker and if access to Docker is limited you will get an error like so:
+
+```WARNING: Error loading config file: /home/user/.docker/config.json -
+stat /home/user/.docker/config.json: permission denied```
+```
+
+You can fix this by following the instructions of the site here:
+https://docs.docker.com/engine/install/linux-postinstall/
+
+A Docker-Machine module exists but is currently unusuable due to the various requirements to convince
+DockerMachine to port-forward external port access to the internal docker container. Therefore, please
+do not attempt to use the docker-machine strategy.
+
+
+# Maven Info
+
+```
+
+
+ xyz.auriium
+ tickbox-api
+ 1.0.0
+
+
+
+
+
+ central-repo
+ https://repo.auriium.xyz/releases
+
+
+```
+
+"TickBox" as in "Tick", a parasite that hooks onto something (docker) and "Box", as in a container.
diff --git a/tick-api/src/main/java/me/aurium/tick/DockerHolder.java b/tick-api/src/main/java/me/aurium/tick/DockerHolder.java
deleted file mode 100644
index 685d8ec..0000000
--- a/tick-api/src/main/java/me/aurium/tick/DockerHolder.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package me.aurium.tick;
-
-public interface DockerHolder {
-}
diff --git a/tick-api/src/main/java/me/aurium/tick/DockerSource.java b/tick-api/src/main/java/me/aurium/tick/DockerSource.java
deleted file mode 100644
index c6d9ff8..0000000
--- a/tick-api/src/main/java/me/aurium/tick/DockerSource.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package me.aurium.tick;
-
-import com.amihaiemil.docker.Docker;
-
-public interface DockerSource {
-
- Docker produceDocker();
-
-}
diff --git a/tick-api/tick-api.iml b/tick-api/tick-api.iml
deleted file mode 100644
index 78b2cc5..0000000
--- a/tick-api/tick-api.iml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
\ No newline at end of file
diff --git a/tick-full/pom.xml b/tick-full/pom.xml
deleted file mode 100644
index 8ff90fe..0000000
--- a/tick-full/pom.xml
+++ /dev/null
@@ -1,69 +0,0 @@
-
-
-
- tick
- me.aurium
- 1.0.0
-
- 4.0.0
-
- maven-plugin
-
- tick-full
-
- 1.0.1
-
-
- ${compiler.version}
- ${compiler.version}
-
-
-
-
-
- org.apache.maven.plugins
- maven-jar-plugin
- ${maven.jarplugin.version}
-
-
- org.apache.maven.plugins
- maven-plugin-plugin
- 3.6.0
-
- tick-full
- true
-
-
-
-
-
-
-
- org.testcontainers
- testcontainers
- ${testcontainers.version}
-
-
- org.testcontainers
- mariadb
- 1.11.4
-
-
-
-
- org.flywaydb
- flyway-core
- 7.5.0
-
-
- org.jooq
- jooq-codegen
- 3.14.3
-
-
-
-
-
-
\ No newline at end of file
diff --git a/tick-full/src/main/java/me/aurium/tick/full/AbstractTickMojo.java b/tick-full/src/main/java/me/aurium/tick/full/AbstractTickMojo.java
deleted file mode 100644
index 0096fd9..0000000
--- a/tick-full/src/main/java/me/aurium/tick/full/AbstractTickMojo.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package me.aurium.tick.full;
-
-import org.apache.maven.plugin.AbstractMojo;
-import org.apache.maven.plugins.annotations.Parameter;
-
-public abstract class AbstractTickMojo extends AbstractMojo {
- //encapsulation over inheritance... bah, i don't care, it's a shitty maven plugin (fix this later)
-
- public CommonInitializers getInitializer() {
- return initializer;
- }
-
- public String[] getLocations() {
- return locations;
- }
-
- public String getOutputDirectory() {
- return outputDirectory;
- }
-
- public String getPackageName() {
- return packageName;
- }
-
- @Parameter(defaultValue = "MARIADB")
- private CommonInitializers initializer;
-
- @Parameter(required = true)
- private String[] locations;
-
- @Parameter(defaultValue = "target/generated-sources")
- private String outputDirectory;
-
- @Parameter(defaultValue = "me.aurium.tick.sources")
- private String packageName;
-
-
-
-}
diff --git a/tick-full/src/main/java/me/aurium/tick/full/CommonInitializers.java b/tick-full/src/main/java/me/aurium/tick/full/CommonInitializers.java
deleted file mode 100644
index 8b0cc48..0000000
--- a/tick-full/src/main/java/me/aurium/tick/full/CommonInitializers.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package me.aurium.tick.full;
-
-import org.testcontainers.containers.JdbcDatabaseContainer;
-import org.testcontainers.containers.MariaDBContainer;
-
-public enum CommonInitializers implements JDBCInitializer {
- MARIADB(new MariaDBContainer<>(),"org.jooq.meta.mariadb.MariaDBDatabase");
-
- private final JdbcDatabaseContainer> consumer;
- private final String jooqClassName;
-
- CommonInitializers(JdbcDatabaseContainer> consumer, String jooqClassName) {
- this.consumer = consumer;
- this.jooqClassName = jooqClassName;
- }
-
- @Override
- public JdbcDatabaseContainer> initializeContainer(String username, String password, String databaseName) {
- return consumer.withDatabaseName(databaseName).withUsername(username).withPassword(password);
- }
-
- @Override
- public String correspondingJooqClassName() {
- return jooqClassName;
- }
-}
diff --git a/tick-full/src/main/java/me/aurium/tick/full/JDBCInitializer.java b/tick-full/src/main/java/me/aurium/tick/full/JDBCInitializer.java
deleted file mode 100644
index e09c097..0000000
--- a/tick-full/src/main/java/me/aurium/tick/full/JDBCInitializer.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package me.aurium.tick.full;
-
-import org.testcontainers.containers.JdbcDatabaseContainer;
-
-public interface JDBCInitializer {
-
- JdbcDatabaseContainer> initializeContainer(String username, String password, String databaseName);
-
- String correspondingJooqClassName();
-
-
-}
diff --git a/tick-full/src/main/java/me/aurium/tick/full/PopulateGoalMojo.java b/tick-full/src/main/java/me/aurium/tick/full/PopulateGoalMojo.java
deleted file mode 100644
index 883de36..0000000
--- a/tick-full/src/main/java/me/aurium/tick/full/PopulateGoalMojo.java
+++ /dev/null
@@ -1,95 +0,0 @@
-package me.aurium.tick.full;
-
-import org.apache.maven.plugin.MojoExecutionException;
-import org.apache.maven.plugin.MojoFailureException;
-import org.apache.maven.plugins.annotations.LifecyclePhase;
-import org.apache.maven.plugins.annotations.Mojo;
-import org.flywaydb.core.Flyway;
-import org.jooq.codegen.GenerationTool;
-import org.jooq.meta.jaxb.*;
-import org.testcontainers.containers.JdbcDatabaseContainer;
-import org.testcontainers.utility.ResourceReaper;
-
-@Mojo(name = "generate", defaultPhase = LifecyclePhase.INITIALIZE)
-public class PopulateGoalMojo extends AbstractTickMojo{
-
- @Override
- public void execute() throws MojoExecutionException,MojoFailureException {
-
- getLog().info("(TICK) Initializing TestContainer!");
-
- JdbcDatabaseContainer> construct = getInitializer().initializeContainer("username","password","sandbox");
-
- getLog().info("(TICK) Starting TestContainer!");
-
- construct.start();
-
- String url = construct.getJdbcUrl();
- String username = construct.getUsername();
- String password = construct.getPassword();
-
- getLog().info("URL: " + url + " USERNAME: " + username + " PASSWORD: " + password);
-
- getLog().info("(TICK) TestContainer successfully deployed! Loading Flyway!");
-
- if (getLocations() == null) throw new MojoFailureException("No locations to draw sources from!");
-
- Flyway flyway = Flyway.configure(getClass().getClassLoader())
- .dataSource(url,username,password)
- .locations(getLocations())
- .validateMigrationNaming(true).group(true)
- .load();
-
- getLog().info("(TICK) Flyway successfully loaded! Migrating to testcontainer now!");
-
- flyway.migrate();
-
- getLog().info("(TICK) Flyway successfully migrated! Now activating JOOQ configuration!");
-
- String output = getParsedOutput();
-
-
- Configuration configuration = new Configuration()
- .withJdbc(new Jdbc()
- .withDriver(construct.getDriverClassName())
- .withUrl(url)
- .withUser(username)
- .withPassword(password))
- .withGenerator(new Generator()
- .withDatabase(new Database()
- .withName(getInitializer().correspondingJooqClassName())
- .withIncludes(".*")
- .withExcludes("")
- .withInputSchema(construct.getDatabaseName())) //TODO testing
- .withTarget(new Target()
- .withPackageName(getPackageName())
- .withDirectory(output)));
-
- getLog().info("(TICK) Configuration successful! Activating JOOQ Code Generation!");
-
- try {
- GenerationTool.generate(configuration);
- } catch (Exception e) {
- throw new MojoFailureException(e.getMessage());
- }
-
- getLog().info("(TICK) Code generation finished! Attempting shutdown!");
-
- ResourceReaper.instance().stopAndRemoveContainer(construct.getContainerId());
-
- getLog().info("(TICK) Tick generation finished successfully! (?) Please check your target directory to ensure satisfaction!");
-
-
-
- }
-
- private final static String FILE_SYSTEM = "filesystem:";
-
- String getParsedOutput() throws MojoFailureException {
- if (getOutputDirectory().startsWith(FILE_SYSTEM)) {
- return getOutputDirectory().substring(FILE_SYSTEM.length());
- } else {
- throw new MojoFailureException("Output directory is not a correct directory type! (E.g. filesystem:)");
- }
- }
-}
diff --git a/tick-full/tick-full.iml b/tick-full/tick-full.iml
deleted file mode 100644
index 78b2cc5..0000000
--- a/tick-full/tick-full.iml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
\ No newline at end of file
diff --git a/tick-rapid/pom.xml b/tick-rapid/pom.xml
deleted file mode 100644
index 883ea0c..0000000
--- a/tick-rapid/pom.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-
-
-
- tick
- me.aurium
- 1.0.0
-
- 4.0.0
-
- tick-rapid
-
- maven-plugin
-
- 1.0.0
-
-
- ${compiler.version}
- ${compiler.version}
-
-
-
-
-
- org.apache.maven.plugins
- maven-jar-plugin
- ${maven.jarplugin.version}
-
-
- org.apache.maven.plugins
- maven-plugin-plugin
- 3.6.0
-
- tick-rapid
- true
-
-
-
-
-
-
-
- org.testcontainers
- testcontainers
- 1.15.2
-
-
- org.testcontainers
- mariadb
- 1.11.4
-
-
-
-
\ No newline at end of file
diff --git a/tick-rapid/src/main/java/me/aurium/tick/rapid/AbstractTickMojo.java b/tick-rapid/src/main/java/me/aurium/tick/rapid/AbstractTickMojo.java
deleted file mode 100644
index 473b896..0000000
--- a/tick-rapid/src/main/java/me/aurium/tick/rapid/AbstractTickMojo.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package me.aurium.tick.rapid;
-
-import org.apache.maven.plugin.AbstractMojo;
-import org.apache.maven.plugins.annotations.Parameter;
-import org.apache.maven.project.MavenProject;
-
-public abstract class AbstractTickMojo extends AbstractMojo {
-
- protected final DBSingleton singleton = DBSingleton.get();
-
- @Parameter(defaultValue = "MARIADB")
- protected CommonInitializers initializer;
- @Parameter(defaultValue = "${project}", readonly = true, required = true)
- protected MavenProject project;
-
- @Parameter(defaultValue = "tick.jdbc_port")
- protected String internalJDBCPort;
- @Parameter(defaultValue = "tick.docker_port")
- protected String externalDockerPort;
- @Parameter(defaultValue = "tick.jdbc_url")
- protected String internalJDBCUrl;
- @Parameter(defaultValue = "tick.docker_ip")
- protected String externalDockerAddress;
-
- @Parameter(defaultValue = "sandbox")
- protected String sandboxName;
- @Parameter(defaultValue = "sandboxUser")
- protected String sandboxPassword;
- @Parameter(defaultValue = "sandboxPassword")
- protected String sandboxUser;
-
- protected void setParameter(String param, String toSet) {
- assert param != null;
- assert toSet != null;
-
- project.getProperties().put( param, toSet );
- }
-
-}
diff --git a/tick-rapid/src/main/java/me/aurium/tick/rapid/CommonInitializers.java b/tick-rapid/src/main/java/me/aurium/tick/rapid/CommonInitializers.java
deleted file mode 100644
index 653004e..0000000
--- a/tick-rapid/src/main/java/me/aurium/tick/rapid/CommonInitializers.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package me.aurium.tick.rapid;
-
-import org.testcontainers.containers.JdbcDatabaseContainer;
-import org.testcontainers.containers.MariaDBContainer;
-
-public enum CommonInitializers implements JDBCInitializer {
- MARIADB(new MariaDBContainer<>(),"org.jooq.meta.mariadb.MariaDBDatabase");
-
- private final JdbcDatabaseContainer> consumer;
- private final String jooqClassName;
-
- CommonInitializers(JdbcDatabaseContainer> consumer, String jooqClassName) {
- this.consumer = consumer;
- this.jooqClassName = jooqClassName;
- }
-
- @Override
- public JdbcDatabaseContainer> initializeContainer(String username, String password, String databaseName) {
- return consumer.withDatabaseName(databaseName).withUsername(username).withPassword(password);
- }
-
- @Override
- public String correspondingJooqClassName() {
- return jooqClassName;
- }
-}
diff --git a/tick-rapid/src/main/java/me/aurium/tick/rapid/DBSingleton.java b/tick-rapid/src/main/java/me/aurium/tick/rapid/DBSingleton.java
deleted file mode 100644
index 1b82974..0000000
--- a/tick-rapid/src/main/java/me/aurium/tick/rapid/DBSingleton.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package me.aurium.tick.rapid;
-
-import org.testcontainers.containers.JdbcDatabaseContainer;
-
-import java.util.Optional;
-
-public class DBSingleton {
-
- private static DBSingleton instance;
-
- private JdbcDatabaseContainer> container;
-
- public void setContainer(JdbcDatabaseContainer> container) {
- this.container = container;
- }
-
- public Optional> getContainer() {
- return Optional.ofNullable(container);
- }
-
- public static DBSingleton get() {
- if (instance == null) {
- return instance = new DBSingleton();
- } else {
- return instance;
- }
- }
-}
diff --git a/tick-rapid/src/main/java/me/aurium/tick/rapid/InitializeGoalMojo.java b/tick-rapid/src/main/java/me/aurium/tick/rapid/InitializeGoalMojo.java
deleted file mode 100644
index 501a6d3..0000000
--- a/tick-rapid/src/main/java/me/aurium/tick/rapid/InitializeGoalMojo.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package me.aurium.tick.rapid;
-
-import org.apache.maven.plugin.MojoExecutionException;
-import org.apache.maven.plugin.MojoFailureException;
-import org.apache.maven.plugins.annotations.LifecyclePhase;
-import org.apache.maven.plugins.annotations.Mojo;
-import org.testcontainers.containers.JdbcDatabaseContainer;
-
-@Mojo(name = "initialize", defaultPhase = LifecyclePhase.INITIALIZE)
-public class InitializeGoalMojo extends AbstractTickMojo{
-
- @Override
- public void execute() throws MojoExecutionException, MojoFailureException {
- JdbcDatabaseContainer> container = initializer.initializeContainer(sandboxUser,sandboxPassword,sandboxName);
-
- getLog().info("(TICK) Starting container!");
-
- container.start();
-
- getLog().info("(TICK) Setting external access port: " + container.getFirstMappedPort());
-
- setParameter(externalDockerPort,container.getFirstMappedPort().toString());
-
- getLog().info("(TICK) Setting internal access ports: " + container.getExposedPorts());
-
- //TODO
-
- getLog().info("(TICK) Setting external docker ip: " + container.getContainerIpAddress());
-
- setParameter(externalDockerAddress,container.getContainerIpAddress());
-
- getLog().info("(TICK) Host: " + container.getHost());
-
- getLog().info("(TICK) Setting internal JDBC Url: " + container.getJdbcUrl());
-
- setParameter(internalJDBCUrl,container.getJdbcUrl());
-
- getLog().info("(TICK) Setting access");
-
- singleton.setContainer(container);
- }
-}
diff --git a/tick-rapid/src/main/java/me/aurium/tick/rapid/JDBCInitializer.java b/tick-rapid/src/main/java/me/aurium/tick/rapid/JDBCInitializer.java
deleted file mode 100644
index 2781aab..0000000
--- a/tick-rapid/src/main/java/me/aurium/tick/rapid/JDBCInitializer.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package me.aurium.tick.rapid;
-
-import org.testcontainers.containers.JdbcDatabaseContainer;
-
-public interface JDBCInitializer {
-
- JdbcDatabaseContainer> initializeContainer(String username, String password, String databaseName);
-
- String correspondingJooqClassName();
-
-}
diff --git a/tick-rapid/src/main/java/me/aurium/tick/rapid/TeardownGoalMojo.java b/tick-rapid/src/main/java/me/aurium/tick/rapid/TeardownGoalMojo.java
deleted file mode 100644
index b549f2e..0000000
--- a/tick-rapid/src/main/java/me/aurium/tick/rapid/TeardownGoalMojo.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package me.aurium.tick.rapid;
-
-import org.apache.maven.plugin.MojoExecutionException;
-import org.apache.maven.plugin.MojoFailureException;
-import org.apache.maven.plugins.annotations.LifecyclePhase;
-import org.apache.maven.plugins.annotations.Mojo;
-import org.testcontainers.containers.JdbcDatabaseContainer;
-import org.testcontainers.utility.ResourceReaper;
-
-import java.util.Optional;
-
-@Mojo(name = "teardown", defaultPhase = LifecyclePhase.GENERATE_SOURCES)
-public class TeardownGoalMojo extends AbstractTickMojo{
-
- @Override
- public void execute() throws MojoExecutionException, MojoFailureException {
-
- getLog().info("(TICK) Attempting to retrieve container!");
-
- Optional> optional = singleton.getContainer();
-
- if (optional.isPresent()) {
- getLog().info("(TICK) Retrieved optional, attempting teardown!");
-
- ResourceReaper.instance().stopAndRemoveContainer(optional.get().getContainerId());
-
- getLog().info("(TICK) Teardown successful!");
- } else {
- throw new MojoFailureException("No container found to teardown!");
- }
-
- }
-}
diff --git a/tick-rapid/tick-rapid.iml b/tick-rapid/tick-rapid.iml
deleted file mode 100644
index 78b2cc5..0000000
--- a/tick-rapid/tick-rapid.iml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
\ No newline at end of file
diff --git a/tick.iml b/tick.iml
deleted file mode 100644
index 78b2cc5..0000000
--- a/tick.iml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
\ No newline at end of file
diff --git a/tickbox-api/pom.xml b/tickbox-api/pom.xml
new file mode 100644
index 0000000..fde8bfc
--- /dev/null
+++ b/tickbox-api/pom.xml
@@ -0,0 +1,65 @@
+
+
+
+ tickbox-parent
+ xyz.auriium
+ 1.0.0
+
+ 4.0.0
+
+ tickbox-api
+
+
+ ${compiler.version}
+ ${compiler.version}
+
+
+
+
+ com.github.docker-java
+ docker-java-core
+ 3.2.11
+
+
+ com.github.docker-java
+ docker-java-transport-okhttp
+ 3.2.11
+
+
+ org.zeroturnaround
+ zt-exec
+ 1.12
+
+
+ org.slf4j
+ slf4j-api
+ 2.0.0-alpha1
+
+
+ com.google.code.gson
+ gson
+ 2.8.7
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ 5.7.0
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-params
+ 5.7.0
+ test
+
+
+ ch.qos.logback
+ logback-classic
+ 1.3.0-alpha5
+ test
+
+
+
+
\ No newline at end of file
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/PostCreationTestException.java b/tickbox-api/src/main/java/xyz/auriium/tick/PostCreationTestException.java
new file mode 100644
index 0000000..4a3d97f
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/PostCreationTestException.java
@@ -0,0 +1,8 @@
+package xyz.auriium.tick;
+
+public class PostCreationTestException extends TickException {
+
+ public PostCreationTestException(Throwable cause) {
+ super("An exception was thrown even though your docker provider reported valid! Please check your provider. Exception: " + cause);
+ }
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/TickException.java b/tickbox-api/src/main/java/xyz/auriium/tick/TickException.java
new file mode 100644
index 0000000..a682119
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/TickException.java
@@ -0,0 +1,10 @@
+package xyz.auriium.tick;
+
+/**
+ * Common parent exception
+ */
+public class TickException extends RuntimeException{
+ public TickException(String s) {
+ super(s);
+ }
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/centralized/CommonTick.java b/tickbox-api/src/main/java/xyz/auriium/tick/centralized/CommonTick.java
new file mode 100644
index 0000000..9b1f1ea
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/centralized/CommonTick.java
@@ -0,0 +1,142 @@
+package xyz.auriium.tick.centralized;
+
+import com.github.dockerjava.api.DockerClient;
+import com.github.dockerjava.api.command.CreateContainerCmd;
+import com.github.dockerjava.api.command.CreateContainerResponse;
+import com.github.dockerjava.api.command.InspectContainerResponse;
+import com.github.dockerjava.api.exception.BadRequestException;
+import com.github.dockerjava.api.exception.ConflictException;
+import com.github.dockerjava.api.exception.DockerClientException;
+import com.github.dockerjava.api.model.Bind;
+import com.github.dockerjava.api.model.HostConfig;
+import com.github.dockerjava.api.model.PortBinding;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import xyz.auriium.tick.container.CreationTerms;
+import xyz.auriium.tick.container.TickContainer;
+import xyz.auriium.tick.docker.image.PullStrategy;
+import xyz.auriium.tick.docker.source.DockerSource;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+public class CommonTick implements Tick{
+
+ private final Logger logger = LoggerFactory.getLogger("(TICK | MANAGER)");
+
+ private final DockerSource location;
+ private final DockerClient client;
+
+ private final PullStrategy strategy;
+ private final ResourceManager manager;
+
+ CommonTick(DockerSource location, PullStrategy strategy, ResourceManager manager) {
+ this.location = location;
+ this.client = location.getClient();
+ this.strategy = strategy;
+ this.manager = manager;
+ }
+
+ @Override
+ public T createContainer(CreationTerms terms) {
+
+ strategy.loadIfRequired(terms.getDockerImageName());
+
+
+ logger.info("Initializing container!");
+ logger.info("Using image: {}", terms.getDockerImageName());
+ logger.info("Using container name: {}", terms.getContainerName());
+ logger.info("Using params: {}", Arrays.toString(terms.getParameters()));
+
+ HostConfig config = new HostConfig();
+ terms.getPortBindings().ifPresent(config::withPortBindings);
+ terms.getBinds().ifPresent(config::withBinds);
+
+ CreateContainerCmd cmd = client.createContainerCmd(terms.getDockerImageName())
+ .withHostName("tick")
+ .withName(terms.getContainerName())
+ .withEnv(terms.getParameters())
+ .withHostConfig(config);
+
+ terms.getCommands().ifPresent(cmd::withCmd);
+
+
+ CreateContainerResponse response = execDirtyHandlingException(cmd);
+
+ String id = response.getId();
+
+ manager.submitContainer(id,false);
+ logger.info("Container created! Starting container now.");
+
+ try {
+ client.startContainerCmd(id).exec();
+ } catch (BadRequestException exception) {
+
+ //time to remove
+ logger.error("Error during startup of container. Exiting early. Printing stacktrace below.");
+ manager.destroyContainer(id);
+ logger.error("Removed bad container. Exiting now! Sorry!");
+
+ throw new IllegalStateException("An exception occurred while trying to start the container: " + exception);
+ }
+
+
+ InspectContainerResponse response1 = client.inspectContainerCmd(id).exec();
+
+ if (!response1.getState().getRunning()) {
+ logger.error(response1.toString());
+ throw new IllegalStateException("Executed container start command but container is not marked as started in docker! Including exception above.");
+ }
+
+ manager.submitContainer(id,true);
+ logger.info("Container has been started successfully!");
+
+ return terms.instantiateHolder(location,manager,response.getId());
+
+
+ }
+
+ @Override
+ public DockerSource expose() {
+ return location;
+ }
+
+ //this all sucks i hate it
+ //A try/catch is required here in order for container to not persist and be effectively removed.
+ //all of this is hacky and gross so if anyone finds a better way please make a PR
+ CreateContainerResponse execDirtyHandlingException(CreateContainerCmd cmd) {
+ try {
+ return cmd.exec();
+ } catch (ConflictException e) {
+ logger.error("Error during execution: Two containers of the same name exist. Destroying container and exiting early. Printing stacktrace below:");
+
+ return getCreateContainerResponse(e, e.getMessage());
+ }
+ }
+
+ //destroy an invalid container
+ CreateContainerResponse getCreateContainerResponse(ConflictException e2, String message) {
+ String s = e2.getMessage();
+
+ s = s.substring(s.indexOf("r \\\"") + 4);
+ s = s.substring(0, s.indexOf("\\\"."));
+
+ manager.destroyContainer(s);
+ logger.error("Removed bad container. Exiting now! Sorry!");
+
+ throw new IllegalStateException(message);
+ }
+
+ @Override
+ public void stop() {
+ logger.info("Stopping TickBox now! Goodnight!");
+
+ manager.stop();
+
+ try {
+ client.close();
+ } catch (IOException e) {
+ throw new ShutdownException("An exception occurred while closing the connection to DockerClient: " + e);
+ }
+ }
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/centralized/CommonTickFactory.java b/tickbox-api/src/main/java/xyz/auriium/tick/centralized/CommonTickFactory.java
new file mode 100644
index 0000000..098b98f
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/centralized/CommonTickFactory.java
@@ -0,0 +1,100 @@
+package xyz.auriium.tick.centralized;
+
+import com.github.dockerjava.api.DockerClient;
+import com.github.dockerjava.api.exception.DockerClientException;
+import com.github.dockerjava.api.model.Version;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import xyz.auriium.tick.PostCreationTestException;
+import xyz.auriium.tick.container.CreationOptions;
+import xyz.auriium.tick.docker.InvalidProviderException;
+import xyz.auriium.tick.docker.image.PullStrategyProvider;
+import xyz.auriium.tick.docker.source.*;
+
+/**
+ * Common implementation of the tick factory
+ */
+public class CommonTickFactory implements TickFactory{
+
+ private final DockerSourceProvider sourceProvider;
+ private final PullStrategyProvider strategyProvider;
+ private final ResourceManagerProvider resourceManager;
+ private final CreationOptions creationOptions;
+
+ private final Logger logger = LoggerFactory.getLogger("(TICK | FACTORY)");
+
+ /**
+ * Create a new TickFactory that initializes Ticks with default specs
+ * @param sourceProvider the docker source provider to use in order to get a DockerClient instance
+ * @param strategyProvider the pull strategy to use in order to handle pulling new images
+ * @param resourceManager the resource manager which handles destroying docker containers on tick shutdown.
+ * If you do not want to use one of these, use the {@link EmptyResourceManager}
+ * @param creationOptions client creation options that pertain to how {@param provider} can be called
+ */
+ public CommonTickFactory(DockerSourceProvider sourceProvider, PullStrategyProvider strategyProvider, ResourceManagerProvider resourceManager, CreationOptions creationOptions) {
+ this.sourceProvider = sourceProvider;
+ this.strategyProvider = strategyProvider;
+ this.resourceManager = resourceManager;
+ this.creationOptions = creationOptions;
+ }
+
+ /**
+ * Creates a new TickFactory with default settings
+ * @param manager the resource manager to use. If you are looking for the suggested default, use {@link HookResourceManager.Provider}
+ * However, if you want a resource manager that does not use a shutdown hook in order
+ * to produce a maven plugin, use {@link EmptyResourceManager.Provider}
+ *
+ * @param sourceProvider the docker source provider to use in order to get a DockerClient instance
+ * @param strategyProvider the pull strategy to use in order to handle pulling new images
+ */
+ public CommonTickFactory(ResourceManagerProvider manager, DockerSourceProvider sourceProvider, PullStrategyProvider strategyProvider) {
+ this(sourceProvider, strategyProvider, manager, CreationOptions.defaults());
+ }
+
+ @Override
+ public Tick produce() {
+ logger.info("Initializing tick startup, performing DockerSourceProvider pre-check!");
+
+ ApplicableResult result = sourceProvider.isApplicable();
+
+ if (!result.isApplicable()) {
+ logger.error(String.format("Attempt to check valid DockerClient using [%s] failed!", sourceProvider.name()));
+ logger.error(String.format("Reason: [%s]", result.getReason()));
+ throw new InvalidProviderException();
+ }
+
+ logger.info("Pre-check successful! Attempting to produce a DockerClient now.");
+
+ DockerSource source = sourceProvider.source(creationOptions);
+ DockerClient client = source.getClient();
+
+ if (creationOptions.isUsePostCreationTest()) {
+ try {
+ client.versionCmd().exec();
+ } catch (DockerClientException exception) {
+ throw new PostCreationTestException(exception);
+ }
+ }
+
+ logger.info("Client produced successfully! Executing final startup activities...");
+
+ Version dockerVersion = client.versionCmd().exec();
+
+ logger.info("(DockerClient startup successful!" + "\n" +
+ "API version: " + dockerVersion.getApiVersion() + "\n" +
+ "Docker version: " + dockerVersion.getVersion() + "\n" +
+ "OS: " + dockerVersion.getOperatingSystem());
+
+ logger.info("Starting up resource manager implementation!");
+
+ ResourceManager manager = resourceManager.make(source);
+
+ logger.info("Resource manager online!");
+
+ return new CommonTick(source, strategyProvider.provide(client, manager), manager);
+
+
+
+
+ }
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/centralized/EmptyResourceManager.java b/tickbox-api/src/main/java/xyz/auriium/tick/centralized/EmptyResourceManager.java
new file mode 100644
index 0000000..9058bb0
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/centralized/EmptyResourceManager.java
@@ -0,0 +1,41 @@
+package xyz.auriium.tick.centralized;
+
+import xyz.auriium.tick.docker.source.DockerSource;
+
+public class EmptyResourceManager implements ResourceManager{
+
+ EmptyResourceManager() {}
+
+ @Override
+ public void submitContainer(String id, boolean val) {
+
+ }
+
+ @Override
+ public void destroyContainer(String id) {
+
+ }
+
+ @Override
+ public void submitImage(String imageName) {
+
+ }
+
+ @Override
+ public void destroyImage(String imageName) {
+
+ }
+
+ @Override
+ public void stop() {
+
+ }
+
+ public static class Provider implements ResourceManagerProvider {
+
+ @Override
+ public ResourceManager make(DockerSource source) {
+ return new EmptyResourceManager();
+ }
+ }
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/centralized/HookResourceManager.java b/tickbox-api/src/main/java/xyz/auriium/tick/centralized/HookResourceManager.java
new file mode 100644
index 0000000..ad38aee
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/centralized/HookResourceManager.java
@@ -0,0 +1,101 @@
+package xyz.auriium.tick.centralized;
+
+import com.github.dockerjava.api.command.InspectContainerResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import xyz.auriium.tick.docker.source.DockerSource;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class HookResourceManager implements ResourceManager { //toInterface
+
+ private final Map containers = new ConcurrentHashMap<>();
+ private final Set images = ConcurrentHashMap.newKeySet();
+
+ private final Logger logger = LoggerFactory.getLogger("(TICK | RESOURCE MANAGER)");
+ private final DockerSource dockerSource;
+
+ HookResourceManager(DockerSource dockerSource) {
+ this.dockerSource = dockerSource;
+ }
+
+ /**
+ * Sets the state of a container into the manager
+ * @param id the id of the container
+ * @param val true if the container is started, false if it is not.
+ */
+ @Override
+ public void submitContainer(String id, boolean val) {
+ logger.debug("Added container with ID: " + id + " to manager!");
+ this.containers.put(id,val);
+ }
+
+ @Override
+ public void submitImage(String imageName) {
+ logger.debug("Added image with name: " + imageName + " to manager!");
+ this.images.add(imageName);
+ }
+
+ @Override
+ public void destroyImage(String imageName) {
+ logger.info("Removing image: {}", imageName);
+ dockerSource.getClient().removeImageCmd(imageName);
+ logger.debug("Removed image!");
+ }
+
+ /**
+ * Shuts down a container if it is not already shut down.
+ * @param id the identification number of the container to destroy
+ */
+ @Override
+ public void destroyContainer(String id) {
+ boolean running = this.containers.remove(id);
+
+ InspectContainerResponse.ContainerState state = dockerSource.getClient().inspectContainerCmd(id).exec().getState();
+
+ if (!state.getRunning() && running) {
+ logger.error("Container is marked as running in ResourceManager yet docker says it is not running! Please report this to tickbox!");
+ logger.error("Attempting to remove container anyways");
+ } else if (running) {
+ logger.info("Stopping container: {}", id);
+ dockerSource.getClient().killContainerCmd(id).exec();
+ logger.debug("Stopped container!");
+ }
+
+ logger.info("Removing container: {}", id);
+ dockerSource.getClient().removeContainerCmd(id).withRemoveVolumes(true).withForce(true).exec();
+ logger.debug("Removed container and associated volume(s)!");
+ }
+
+ public void stop() {
+ containers.keySet().forEach(this::destroyContainer);
+ images.forEach(this::destroyImage);
+ }
+
+ public static class Provider implements ResourceManagerProvider {
+
+ private final Logger logger = LoggerFactory.getLogger("(TICK | RESOURCE PROVIDER)");
+ private final boolean useShutdownHook;
+
+ public Provider(boolean useShutdownHook) {
+ this.useShutdownHook = useShutdownHook;
+ }
+
+ @Override
+ public ResourceManager make(DockerSource source) {
+ ResourceManager manager = new HookResourceManager(source);
+
+ if (useShutdownHook) {
+ logger.info("Attempting to attach shutdown hook now!");
+ Runtime.getRuntime().addShutdownHook(new Thread(manager::stop));
+ }
+
+ return manager;
+ }
+ }
+
+
+
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/centralized/ResourceManager.java b/tickbox-api/src/main/java/xyz/auriium/tick/centralized/ResourceManager.java
new file mode 100644
index 0000000..e162c9e
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/centralized/ResourceManager.java
@@ -0,0 +1,20 @@
+package xyz.auriium.tick.centralized;
+
+import xyz.auriium.tick.utils.Stoppable;
+
+/**
+ * Manages resources for the tick system
+ */
+public interface ResourceManager extends Stoppable {
+
+ void submitContainer(String id, boolean val);
+ void destroyContainer(String id);
+
+ /**
+ * Inserts an image into the manager for later cleanup
+ * @param imageName the name of the image
+ */
+ void submitImage(String imageName);
+ void destroyImage(String imageName);
+
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/centralized/ResourceManagerProvider.java b/tickbox-api/src/main/java/xyz/auriium/tick/centralized/ResourceManagerProvider.java
new file mode 100644
index 0000000..3852bfa
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/centralized/ResourceManagerProvider.java
@@ -0,0 +1,9 @@
+package xyz.auriium.tick.centralized;
+
+import xyz.auriium.tick.docker.source.DockerSource;
+
+public interface ResourceManagerProvider {
+
+ ResourceManager make(DockerSource source);
+
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/centralized/ShutdownException.java b/tickbox-api/src/main/java/xyz/auriium/tick/centralized/ShutdownException.java
new file mode 100644
index 0000000..01533f8
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/centralized/ShutdownException.java
@@ -0,0 +1,9 @@
+package xyz.auriium.tick.centralized;
+
+import xyz.auriium.tick.TickException;
+
+public class ShutdownException extends TickException {
+ public ShutdownException(String s) {
+ super(s);
+ }
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/centralized/Tick.java b/tickbox-api/src/main/java/xyz/auriium/tick/centralized/Tick.java
new file mode 100644
index 0000000..5a09774
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/centralized/Tick.java
@@ -0,0 +1,28 @@
+package xyz.auriium.tick.centralized;
+
+import xyz.auriium.tick.container.CreationTerms;
+import xyz.auriium.tick.container.TickContainer;
+import xyz.auriium.tick.docker.source.DockerSource;
+import xyz.auriium.tick.utils.Stoppable;
+
+/**
+ * Entry point into the tick api, allows for the creation of dockerized containers.
+ * Must be stopped manually if you don't use a resource manager
+ */
+public interface Tick extends Stoppable {
+
+ /**
+ * Creates and starts a new container according to the terms provided
+ * @param terms the terms of creation used to generate the tick
+ * @param the type of container
+ * @return a new container
+ */
+ T createContainer(CreationTerms terms);
+
+ /**
+ * Testing
+ * @return testing
+ */
+ DockerSource expose();
+
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/centralized/TickFactory.java b/tickbox-api/src/main/java/xyz/auriium/tick/centralized/TickFactory.java
new file mode 100644
index 0000000..6882ab7
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/centralized/TickFactory.java
@@ -0,0 +1,10 @@
+package xyz.auriium.tick.centralized;
+
+/**
+ * Represents something that can make Ticks
+ */
+public interface TickFactory {
+
+ Tick produce();
+
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/container/Arguments.java b/tickbox-api/src/main/java/xyz/auriium/tick/container/Arguments.java
new file mode 100644
index 0000000..7fc1c75
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/container/Arguments.java
@@ -0,0 +1,82 @@
+package xyz.auriium.tick.container;
+
+import com.github.dockerjava.api.model.PortBinding;
+
+import java.util.Objects;
+
+public class Arguments {
+
+ private final String creationName;
+ private final String dockerImageName;
+ private final String[] parameters;
+ private final PortBinding binding;
+
+ public Arguments(String creationName, String dockerImageName, String[] parameters, PortBinding binding) {
+ this.creationName = creationName;
+ this.dockerImageName = dockerImageName;
+ this.parameters = parameters;
+ this.binding = binding;
+ }
+
+ public String getDockerImageName() {
+ return dockerImageName;
+ }
+
+ public String[] getParameters() {
+ return parameters;
+ }
+
+ public PortBinding getBinding() {
+ return this.binding;
+ }
+
+ public String getContainerName() {
+ return this.creationName;
+ }
+
+ public static class Builder {
+
+ private String dockerImageName;
+ private String[] params;
+ private PortBinding biniding;
+ private String creationName;
+
+ public Builder withImage(String name) {
+ //todo validator
+
+ this.dockerImageName = name;
+
+ return this;
+ }
+
+ public Builder withParams(String... params) {
+ this.params = params;
+
+ return this;
+ }
+
+ public Builder withBinding(PortBinding binding) {
+ this.biniding = binding;
+
+ return this;
+ }
+
+ public Builder withCreationName(String name) {
+ creationName = name;
+
+ return this;
+ }
+
+ public Arguments build() {
+ Objects.requireNonNull(dockerImageName);
+ Objects.requireNonNull(params);
+ Objects.requireNonNull(biniding);
+ Objects.requireNonNull(creationName);
+
+ return new Arguments(creationName, dockerImageName,params, biniding);
+ }
+
+
+ }
+
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/container/CreationOptions.java b/tickbox-api/src/main/java/xyz/auriium/tick/container/CreationOptions.java
new file mode 100644
index 0000000..f3e3c17
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/container/CreationOptions.java
@@ -0,0 +1,27 @@
+package xyz.auriium.tick.container;
+
+public class CreationOptions {
+
+
+
+ private final boolean usePostCreationTest;
+ private final boolean withTLS;
+
+ public CreationOptions(boolean usePostCreationTest, boolean withTLS) {
+ this.usePostCreationTest = usePostCreationTest;
+ this.withTLS = withTLS;
+ }
+ public boolean isUsePostCreationTest() {
+ return usePostCreationTest;
+ }
+
+ public boolean isWithTLS() {
+ return withTLS;
+ }
+
+
+ public static CreationOptions defaults() {
+ return new CreationOptions(true, false);
+ }
+
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/container/CreationTerms.java b/tickbox-api/src/main/java/xyz/auriium/tick/container/CreationTerms.java
new file mode 100644
index 0000000..ce5d5fb
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/container/CreationTerms.java
@@ -0,0 +1,36 @@
+package xyz.auriium.tick.container;
+
+import com.github.dockerjava.api.DockerClient;
+import com.github.dockerjava.api.model.Bind;
+import com.github.dockerjava.api.model.PortBinding;
+import xyz.auriium.tick.centralized.ResourceManager;
+import xyz.auriium.tick.docker.source.DockerSource;
+
+import java.util.Optional;
+
+public interface CreationTerms {
+
+ String getDockerImageName();
+
+ String[] getParameters();
+
+ Optional getBinds();
+
+ Optional getPortBindings();
+
+ Optional getCommands();
+
+ String getContainerName();
+
+
+ /**
+ * Method used to describe the creation of the actual container
+ *
+ * Can also be used as a callback for post container startup
+ *
+ * @param location the dockersource used
+ * @param dockerID the identifier of the container
+ * @return a new container object
+ */
+ T instantiateHolder(DockerSource location, ResourceManager manager, String dockerID);
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/container/JDBCUrlBuilder.java b/tickbox-api/src/main/java/xyz/auriium/tick/container/JDBCUrlBuilder.java
new file mode 100644
index 0000000..e6f148a
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/container/JDBCUrlBuilder.java
@@ -0,0 +1,40 @@
+package xyz.auriium.tick.container;
+
+import java.util.Objects;
+
+public class JDBCUrlBuilder {
+
+ private String driverName;
+ private String ip;
+ private int port;
+ private String dbName;
+
+ public JDBCUrlBuilder withDriver(String name) {
+ this.driverName = name;
+ return this;
+ }
+
+ public JDBCUrlBuilder withIP(String ip) {
+ this.ip = ip;
+ return this;
+ }
+
+ public JDBCUrlBuilder withPort(int port) {
+ this.port = port;
+ return this;
+ }
+
+ public JDBCUrlBuilder withDBName(String name) {
+ this.dbName = name;
+ return this;
+ }
+
+ public String build() {
+ Objects.requireNonNull(dbName);
+ Objects.requireNonNull(ip);
+ Objects.requireNonNull(driverName);
+
+ return "jdbc:" + driverName + "://" + ip + ":" + port + "/" + dbName;
+ }
+
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/container/TickContainer.java b/tickbox-api/src/main/java/xyz/auriium/tick/container/TickContainer.java
new file mode 100644
index 0000000..2e79d74
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/container/TickContainer.java
@@ -0,0 +1,32 @@
+package xyz.auriium.tick.container;
+
+/**
+ * Represents an already created Container that can start and stop itself
+ *
+ * TODO add more methods to this barebones ass cringe bullshit
+ *
+ * some ideas:
+ *
+ * runShell(str) for a linux extension of this interface
+ * getStatus()
+ * get.. idk everything that dockerjava provides for us ;)
+ */
+public interface TickContainer extends AutoCloseable{
+
+ String containerName();
+ String containerID();
+
+ /**
+ * Calling this method will stop and then destroy this tick container docker-side.
+ * After the container is destroyed do not attempt to invoke it.
+ */
+ void destroy();
+
+ /**
+ * Invokes {@link TickContainer#destroy()}
+ */
+ default void close() {
+ destroy();
+ }
+
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/docker/InvalidProviderException.java b/tickbox-api/src/main/java/xyz/auriium/tick/docker/InvalidProviderException.java
new file mode 100644
index 0000000..43e0678
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/docker/InvalidProviderException.java
@@ -0,0 +1,11 @@
+package xyz.auriium.tick.docker;
+
+import xyz.auriium.tick.TickException;
+
+public class InvalidProviderException extends TickException {
+
+ public InvalidProviderException() {
+ super("A provider that was provided was unable to meet it's requirements for launch! See logs for details.");
+ }
+
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/docker/adapter/FutureAdapter.java b/tickbox-api/src/main/java/xyz/auriium/tick/docker/adapter/FutureAdapter.java
new file mode 100644
index 0000000..553fbe9
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/docker/adapter/FutureAdapter.java
@@ -0,0 +1,9 @@
+package xyz.auriium.tick.docker.adapter;
+
+import java.util.concurrent.CompletableFuture;
+
+public interface FutureAdapter {
+
+ CompletableFuture toFuture();
+
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/docker/adapter/FutureCallbackAdapter.java b/tickbox-api/src/main/java/xyz/auriium/tick/docker/adapter/FutureCallbackAdapter.java
new file mode 100644
index 0000000..770acf1
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/docker/adapter/FutureCallbackAdapter.java
@@ -0,0 +1,47 @@
+package xyz.auriium.tick.docker.adapter;
+
+import com.github.dockerjava.api.command.PullImageResultCallback;
+import com.github.dockerjava.api.model.PullResponseItem;
+
+import java.io.Closeable;
+import java.util.concurrent.CompletableFuture;
+
+//todo-switch to async-callback
+public class FutureCallbackAdapter extends PullImageResultCallback implements FutureAdapter {
+
+ private CompletableFuture response = new CompletableFuture<>();
+
+ private OrThrowable objectCached;
+
+ @Override
+ public void onNext(PullResponseItem item) {
+ super.onNext(item);
+
+ this.objectCached.assignObject(item);
+ }
+
+ @Override
+ public void onComplete() {
+ super.onComplete();
+
+ objectCached.complete(response);
+ }
+
+ @Override
+ public void onError(Throwable throwable) {
+ super.onError(throwable);
+
+ this.objectCached.assignThrowable(throwable); //will this cause thread safety concerns? e.g. onError and onNext vs onComplete?
+ //i'm assuming not cause everything will be on the same thread (This will be on another thread that is the same thread
+ // the orThrowable is on)
+ }
+
+ @Override
+ public void onStart(Closeable stream) {
+ super.onStart(stream); //what the fuck? please advise
+ }
+
+ public CompletableFuture toFuture() {
+ return response;
+ }
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/docker/adapter/OrThrowable.java b/tickbox-api/src/main/java/xyz/auriium/tick/docker/adapter/OrThrowable.java
new file mode 100644
index 0000000..e235b8e
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/docker/adapter/OrThrowable.java
@@ -0,0 +1,39 @@
+package xyz.auriium.tick.docker.adapter;
+
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * Shitty class that probably will break threadsafety
+ * @param
+ */
+public class OrThrowable {
+
+ private T object;
+ private Throwable throwable;
+
+ public void assignObject(T object) {
+ this.object = object;
+ this.throwable = null;
+ }
+
+ public void assignThrowable(Throwable throwable) {
+ this.throwable = throwable;
+ this.object = null; //YOU CAN ONLY HAVE ONE >:(
+ }
+
+ public void complete(CompletableFuture future) {
+ if (throwable != null) {
+ future.completeExceptionally(throwable);
+ } else if (object != null) {
+ future.complete(object);
+ } else {
+ future.completeExceptionally(new NoCompletionResultsException("Tried to complete a future without having an object or a throwable to complete it with!"));
+ }
+ }
+
+ public static class NoCompletionResultsException extends RuntimeException {
+ public NoCompletionResultsException(String aaaaa) {
+ super(aaaaa);
+ }
+ }
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/docker/image/DefaultPullCallback.java b/tickbox-api/src/main/java/xyz/auriium/tick/docker/image/DefaultPullCallback.java
new file mode 100644
index 0000000..6dec9bd
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/docker/image/DefaultPullCallback.java
@@ -0,0 +1,131 @@
+package xyz.auriium.tick.docker.image;
+
+import com.github.dockerjava.api.command.PullImageResultCallback;
+import com.github.dockerjava.api.model.PullResponseItem;
+import org.slf4j.Logger;
+
+import java.io.Closeable;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.*;
+
+import static java.lang.String.format;
+import static org.apache.commons.io.FileUtils.byteCountToDisplaySize;
+
+/**
+ * Credit where credit is due: this stuff is from TestContainers
+ */
+public class DefaultPullCallback extends PullImageResultCallback {
+
+ private Instant start;
+
+ private final Set allLayers = new HashSet<>();
+ private final Set downloadedLayers = new HashSet<>();
+ private final Set pulledLayers = new HashSet<>();
+ private final Map totalSizes = new HashMap<>();
+ private final Map currentSizes = new HashMap<>();
+
+ private final Logger logger;
+
+ private boolean completed = false;
+
+ public DefaultPullCallback(Logger logger) {
+ this.logger = logger;
+ }
+
+ @Override
+ public void onStart(Closeable stream) {
+ super.onStart(stream);
+
+ start = Instant.now();
+
+ logger.info("(TICK) Starting image pull callback!");
+ }
+
+ @Override
+ public void onComplete() {
+ super.onComplete();
+
+ final long downloadedLayerSize = downloadedLayerSize();
+ final long duration = Duration.between(start, Instant.now()).getSeconds();
+
+ if (completed) {
+ logger.info("Pull complete. {} layers, pulled in {}s (downloaded {} at {}/s)",
+ allLayers.size(),
+ duration,
+ byteCountToDisplaySize(downloadedLayerSize),
+ byteCountToDisplaySize(downloadedLayerSize / duration));
+ }
+ }
+
+ @Override
+ public void onNext(PullResponseItem item) {
+ super.onNext(item);
+
+ final String statusLowercase = item.getStatus() != null ? item.getStatus().toLowerCase() : "";
+ final String id = item.getId();
+
+ if (item.getProgressDetail() != null) {
+ allLayers.add(id);
+ }
+
+ if (statusLowercase.equalsIgnoreCase("download complete")) {
+ downloadedLayers.add(id);
+ }
+
+ if (statusLowercase.equalsIgnoreCase("pull complete")) {
+ pulledLayers.add(id);
+ }
+
+ if (item.getProgressDetail() != null) {
+ Long total = item.getProgressDetail().getTotal();
+ Long current = item.getProgressDetail().getCurrent();
+
+ if (total != null && total > totalSizes.getOrDefault(id, 0L)) {
+ totalSizes.put(id, total);
+ }
+ if (current != null && current > currentSizes.getOrDefault(id, 0L)) {
+ currentSizes.put(id, current);
+ }
+ }
+
+ if (statusLowercase.startsWith("pulling from" ) || statusLowercase.contains("complete" )) {
+
+ long totalSize = totalLayerSize();
+ long currentSize = downloadedLayerSize();
+
+ int pendingCount = allLayers.size() - downloadedLayers.size();
+ String friendlyTotalSize;
+ if (pendingCount > 0) {
+ friendlyTotalSize = "? MB";
+ } else {
+ friendlyTotalSize = byteCountToDisplaySize(totalSize);
+ }
+
+ logger.info("Pulling image layers: {} pending, {} downloaded, {} extracted, ({}/{})",
+ format("%2d", pendingCount),
+ format("%2d", downloadedLayers.size()),
+ format("%2d", pulledLayers.size()),
+ byteCountToDisplaySize(currentSize),
+ friendlyTotalSize);
+ }
+
+ if (statusLowercase.contains("complete")) {
+ completed = true;
+ }
+
+ }
+
+ private long downloadedLayerSize() {
+ return currentSizes.values().stream().filter(Objects::nonNull).mapToLong(it -> it).sum();
+ }
+
+ private long totalLayerSize() {
+ return totalSizes.values().stream().filter(Objects::nonNull).mapToLong(it -> it).sum();
+ }
+
+ @Override
+ public void onError(Throwable throwable) {
+ super.onError(throwable);
+ }
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/docker/image/DefaultPullStrategy.java b/tickbox-api/src/main/java/xyz/auriium/tick/docker/image/DefaultPullStrategy.java
new file mode 100644
index 0000000..1638d92
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/docker/image/DefaultPullStrategy.java
@@ -0,0 +1,68 @@
+package xyz.auriium.tick.docker.image;
+
+import com.github.dockerjava.api.DockerClient;
+import com.github.dockerjava.api.exception.NotFoundException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import xyz.auriium.tick.centralized.ResourceManager;
+
+import java.util.concurrent.TimeUnit;
+
+public class DefaultPullStrategy implements PullStrategy {
+
+ private final Logger logger = LoggerFactory.getLogger("(TICK | DefaultPullStrategy)");
+
+ private final DockerClient client;
+ private final ResourceManager manager;
+
+ public DefaultPullStrategy(DockerClient client, ResourceManager manager) {
+ this.client = client;
+ this.manager = manager;
+ }
+
+ @Override
+ public boolean shouldLoad(String dockerImageName) {
+
+ logger.debug("Testing whether image should be loaded...");
+
+ try {
+ client.inspectImageCmd(dockerImageName).exec();
+
+ logger.debug("Image is present!");
+
+ return false;
+ } catch (NotFoundException exception) {
+
+ logger.debug("Image is not present!");
+
+ return true;
+ }
+ }
+
+ @Override
+ public void load(String dockerImageName) {
+
+ logger.info("Attempting to pull image with name: " + dockerImageName);
+
+ try {
+
+ client.pullImageCmd(dockerImageName).exec(new DefaultPullCallback(logger))
+ .awaitCompletion(30, TimeUnit.SECONDS);
+
+ manager.submitImage(dockerImageName);
+
+ logger.info("Image load finished!");
+ } catch (InterruptedException exception) {
+ logger.error("An exception occurred while loading an image: ", exception);
+ }
+
+ }
+
+ public static class Provider implements PullStrategyProvider {
+ @Override
+ public DefaultPullStrategy provide(DockerClient client, ResourceManager manager) {
+ return new DefaultPullStrategy(client, manager);
+ }
+ }
+
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/docker/image/PullStrategy.java b/tickbox-api/src/main/java/xyz/auriium/tick/docker/image/PullStrategy.java
new file mode 100644
index 0000000..da02fc6
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/docker/image/PullStrategy.java
@@ -0,0 +1,17 @@
+package xyz.auriium.tick.docker.image;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Future;
+
+public interface PullStrategy {
+
+ boolean shouldLoad(String dockerImageName);
+ void load(String dockerImageName);
+
+ default void loadIfRequired(String name) {
+ if (shouldLoad(name)) {
+ load(name);
+ }
+ }
+
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/docker/image/PullStrategyProvider.java b/tickbox-api/src/main/java/xyz/auriium/tick/docker/image/PullStrategyProvider.java
new file mode 100644
index 0000000..e81afbc
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/docker/image/PullStrategyProvider.java
@@ -0,0 +1,14 @@
+package xyz.auriium.tick.docker.image;
+
+import com.github.dockerjava.api.DockerClient;
+import xyz.auriium.tick.centralized.ResourceManager;
+
+/**
+ * Provider that exists in order to allow user to specify a strategy without actually initializing one
+ * since client initialization is done tick-side
+ */
+public interface PullStrategyProvider {
+
+ DefaultPullStrategy provide(DockerClient client, ResourceManager manager);
+
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/ApplicableResult.java b/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/ApplicableResult.java
new file mode 100644
index 0000000..0ab7106
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/ApplicableResult.java
@@ -0,0 +1,34 @@
+package xyz.auriium.tick.docker.source;
+
+import java.util.Optional;
+
+public class ApplicableResult {
+
+ public boolean isApplicable() {
+ return result;
+ }
+
+ public String getReason() {
+ if (result) {
+ throw new IllegalStateException("The result is successful, there will not be a reason!");
+ } else {
+ return reason;
+ }
+ }
+
+ private final boolean result;
+ private final String reason;
+
+ ApplicableResult(boolean result, String reason) {
+ this.result = result;
+ this.reason = reason;
+ }
+
+ public static ApplicableResult success() {
+ return new ApplicableResult(true,null);
+ }
+
+ public static ApplicableResult fail(String reason) {
+ return new ApplicableResult(false,reason);
+ }
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/ConfigUtils.java b/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/ConfigUtils.java
new file mode 100644
index 0000000..0c7039a
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/ConfigUtils.java
@@ -0,0 +1,70 @@
+package xyz.auriium.tick.docker.source;
+
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.net.URI;
+import java.util.Optional;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+//TODO de-testcontainers this
+
+/*
+public class ConfigUtils {
+
+ public static final boolean IN_A_CONTAINER = new File("/.dockerenv").exists();
+
+ private static final Optional defaultGateway = Optional
+ .ofNullable(DockerClientFactory.instance().runInsideDocker(
+ cmd -> cmd.withCmd("sh", "-c", "ip route|awk '/default/ { print $3 }'"),
+ (client, id) -> {
+ try {
+ LogToStringContainerCallback loggingCallback = new LogToStringContainerCallback();
+ client.logContainerCmd(id).withStdOut(true)
+ .withFollowStream(true)
+ .exec(loggingCallback)
+ .awaitStarted();
+ loggingCallback.awaitCompletion(3, SECONDS);
+ return loggingCallback.toString();
+ } catch (Exception e) {
+ log.warn("Can't parse the default gateway IP", e);
+ return null;
+ }
+ }
+ ))
+ .map(StringUtils::trimToEmpty)
+ .filter(StringUtils::isNotBlank);
+ private static final Logger log = LoggerFactory.getLogger("(TICK | Experimental)");
+
+ */
+/**
+ * @deprecated use {@link DockerClientProviderStrategy#getDockerHostIpAddress()}
+ *//*
+
+ @Deprecated
+ public static String getDockerHostIpAddress(URI dockerHost) {
+ switch (dockerHost.getScheme()) {
+ case "http":
+ case "https":
+ case "tcp":
+ return dockerHost.getHost();
+ case "unix":
+ case "npipe":
+ if (IN_A_CONTAINER) {
+ return getDefaultGateway().orElse("localhost");
+ }
+ return "localhost";
+ default:
+ return null;
+ }
+ }
+
+ public static Optional getDefaultGateway() {
+ return DockerClientConfigUtils.defaultGateway;
+ }
+
+}
+*/
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/CreationException.java b/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/CreationException.java
new file mode 100644
index 0000000..bba200e
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/CreationException.java
@@ -0,0 +1,12 @@
+package xyz.auriium.tick.docker.source;
+
+public class CreationException extends RuntimeException{
+
+ public CreationException(String message) {
+ super(message);
+ }
+
+ public CreationException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/DockerHolder.java b/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/DockerHolder.java
new file mode 100644
index 0000000..46e5c6d
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/DockerHolder.java
@@ -0,0 +1,4 @@
+package xyz.auriium.tick.docker.source;
+
+public interface DockerHolder {
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/DockerLocation.java b/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/DockerLocation.java
new file mode 100644
index 0000000..c38df4a
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/DockerLocation.java
@@ -0,0 +1,12 @@
+package xyz.auriium.tick.docker.source;
+
+import com.github.dockerjava.transport.SSLConfig;
+
+public interface DockerLocation {
+
+ String getIp();
+ String getUrl();
+ SSLConfig getSSLConfig();
+
+
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/DockerSource.java b/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/DockerSource.java
new file mode 100644
index 0000000..d86346a
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/DockerSource.java
@@ -0,0 +1,36 @@
+package xyz.auriium.tick.docker.source;
+
+import com.github.dockerjava.api.DockerClient;
+
+import java.net.URI;
+
+/**
+ * Wrapper interface for DockerClient
+ */
+public interface DockerSource {
+
+ /**
+ * Gets the full connection URI (Think https://127.0.0.1/)
+ *
+ * This is NOT a mysql/postgresql/whatever the fuck you want/jdbc connection uri. Those are created elsewhere.
+ *
+ * @return the URI of this source
+ */
+ URI getSourceURI();
+
+ /**
+ * Get just the host address of this source (Think 127.0.0.1)
+ *
+ * Typically what gets plugged into
+ *
+ * @return the host address of this source.
+ */
+ String getSourceHost();
+
+ /**
+ * Get the client.
+ * @return cluwne
+ */
+ DockerClient getClient();
+
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/DockerSourceProvider.java b/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/DockerSourceProvider.java
new file mode 100644
index 0000000..13d19d8
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/DockerSourceProvider.java
@@ -0,0 +1,34 @@
+package xyz.auriium.tick.docker.source;
+
+import xyz.auriium.tick.container.CreationOptions;
+
+public interface DockerSourceProvider extends Comparable {
+
+ String name();
+
+ /**
+ * Priority to be tested by {@link xyz.auriium.tick.docker.source.impl.AutoSourceProvider}
+ * The higher the number the earlier it is tested.
+ *
+ * @return
+ */
+ Integer priority();
+
+ /**
+ * Attempts to generate a DockerSource. Do not call this without calling {@link #isApplicable()}
+ * @return a docker source.
+ * @throws IllegalStateException if an error occurs attempting to generate the dockersource outlined.
+ */
+ DockerSource source(CreationOptions options);
+
+ /**
+ * Checks whether or not the DockerSourceProvider can effectively provide a DockerSource
+ * @return Result containing whether or not a Source can be provided and a reason for it.
+ */
+ ApplicableResult isApplicable();
+
+ @Override
+ default int compareTo(DockerSourceProvider o) {
+ return this.priority().compareTo(o.priority());
+ }
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/JDBCUrlProvider.java b/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/JDBCUrlProvider.java
new file mode 100644
index 0000000..c377bc0
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/JDBCUrlProvider.java
@@ -0,0 +1,8 @@
+package xyz.auriium.tick.docker.source;
+
+//TODO
+public interface JDBCUrlProvider {
+
+ String provide();//args
+
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/impl/AutoSourceProvider.java b/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/impl/AutoSourceProvider.java
new file mode 100644
index 0000000..21d8be9
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/impl/AutoSourceProvider.java
@@ -0,0 +1,48 @@
+package xyz.auriium.tick.docker.source.impl;
+
+import xyz.auriium.tick.container.CreationOptions;
+import xyz.auriium.tick.docker.source.ApplicableResult;
+import xyz.auriium.tick.docker.source.DockerSource;
+import xyz.auriium.tick.docker.source.DockerSourceProvider;
+
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Automatic source provider that loops through a list of all existing providers in order to attempt delegation
+ */
+public class AutoSourceProvider {
+
+ private static final Set providers = new HashSet<>();
+
+ static {
+ providers.add(new WindowsSourceProvider());
+ providers.add(new RootlessSourceProvider());
+ providers.add(new UnixSourceProvider());
+ providers.add(new SystemEnvSourceProvider());
+ }
+
+
+ /**
+ * Attempts to get a docker source provider that will work with your machine.
+ * @return a docker source provider
+ * @throws NoProviderException if no valid provider can be found for use on your machine.
+ */
+ public DockerSourceProvider provide() {
+
+ List sorted = providers.stream().sorted((a, b) -> b.priority().compareTo(a.priority())).collect(Collectors.toList());
+
+ for (DockerSourceProvider provider : sorted) {
+ ApplicableResult result = provider.isApplicable(); {
+ if (!result.isApplicable()) continue;
+
+ return provider;
+ }
+ }
+
+ throw new NoProviderException();
+ }
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/impl/DockerSourceImpl.java b/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/impl/DockerSourceImpl.java
new file mode 100644
index 0000000..568e3e2
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/impl/DockerSourceImpl.java
@@ -0,0 +1,60 @@
+package xyz.auriium.tick.docker.source.impl;
+
+import com.github.dockerjava.api.DockerClient;
+import com.github.dockerjava.api.model.Network;
+import xyz.auriium.tick.docker.source.DockerSource;
+
+import java.net.URI;
+
+public class DockerSourceImpl implements DockerSource {
+
+ private final URI sourceURI;
+ private final DockerClient client;
+
+ public DockerSourceImpl(URI sourceURI, DockerClient client) {
+ this.sourceURI = sourceURI;
+ this.client = client;
+ }
+
+ @Override
+ public URI getSourceURI() {
+ return sourceURI;
+ }
+
+ @Override
+ public String getSourceHost() {
+ switch (sourceURI.getScheme()) {
+ case "http":
+ case "https":
+ case "tcp":
+ return sourceURI.getHost();
+ case "unix":
+ case "npipe":
+ /*if (DockerClientConfigUtils.IN_A_CONTAINER) { //TODO de-testcontainers this
+ return client.inspectNetworkCmd()
+ .withNetworkId("bridge")
+ .exec()
+ .getIpam()
+ .getConfig()
+ .stream()
+ .filter(it -> it.getGateway() != null)
+ .findAny()
+ .map(Network.Ipam.Config::getGateway)
+ .orElseGet(() -> {
+ return DockerClientConfigUtils.getDefaultGateway().orElse("localhost");
+ });
+ }*/
+ return "localhost";
+ default:
+ throw new IllegalStateException("Unusual scheme used: Cannot interpret scheme: " + sourceURI.getScheme());
+ }
+
+ }
+
+ @Override
+ public DockerClient getClient() {
+ return client;
+ }
+
+
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/impl/MachineSourceProvider.java b/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/impl/MachineSourceProvider.java
new file mode 100644
index 0000000..bf5ed0c
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/impl/MachineSourceProvider.java
@@ -0,0 +1,154 @@
+package xyz.auriium.tick.docker.source.impl;
+
+import org.apache.commons.lang.SystemUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.zeroturnaround.exec.ProcessExecutor;
+import org.zeroturnaround.exec.ProcessResult;
+import xyz.auriium.tick.container.CreationOptions;
+import xyz.auriium.tick.docker.source.*;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.TimeoutException;
+import java.util.regex.Pattern;
+
+/**
+ * DO NOT USE THIS as it does not currently work correctly lmao
+ *
+ * (Startup works, connection does not.)
+ */
+@Deprecated
+public class MachineSourceProvider extends SimpleSourceProvider {
+
+ private static final Logger logger = LoggerFactory.getLogger("(TICK | DOCKER-MACHINE PROVIDER)");
+ private static final String DEFAULT = "default";
+
+ private final String preferredName;
+
+ public MachineSourceProvider(String preferredName) {
+ this.preferredName = preferredName;
+ }
+
+ @Override
+ public String name() {
+ return "MachineSourceProvider";
+ }
+
+ @Override
+ public Integer priority() {
+ return -9999999;
+ }
+
+ @Override
+ public ApplicableResult isApplicable() {
+ String executableName = "docker-machine";
+ if (SystemUtils.IS_OS_WINDOWS) {
+ executableName="docker-machine.exe";
+ }
+
+ if (!executableExists(executableName)) return ApplicableResult.fail("DockerMachine is not present on this system!");
+
+ try {
+ ProcessResult result = new ProcessExecutor()
+ .command(executableName,"ls","-q", "--filter", "state=Running")
+ .readOutput(true)
+ .exitValueNormal()
+ .execute();
+
+ Optional toUse = getToUse("ignored", List.of(result.outputUTF8().split("\n")));
+
+ if (toUse.isEmpty()) return ApplicableResult.fail("No usable DockerMachine system is active. Please create one and try again.");
+
+ String url = new ProcessExecutor()
+ .command(executableName,"url", toUse.get())
+ .readOutput(true)
+ .exitValueNormal()
+ .execute().outputString().replaceAll("\n","");
+
+ String ip = new ProcessExecutor()
+ .command(executableName,"ip", toUse.get())
+ .readOutput(true)
+ .exitValueNormal()
+ .execute().outputString().replaceAll("\n","");
+
+ return ApplicableResult.success();
+
+
+ } catch (IOException | InterruptedException | TimeoutException e) {
+ return ApplicableResult.fail(e.getMessage());
+ }
+ }
+
+ @Override
+ public URI makeURI(CreationOptions options) {
+
+ logger.info("Attempting to produce DockerSource using docker machine commandline!");
+
+ String executableName = "docker-machine";
+ if (SystemUtils.IS_OS_WINDOWS) {
+ executableName="docker-machine.exe";
+ }
+
+ if (!executableExists(executableName)) throw new IllegalStateException("DockerMachine executable not found on this system!");
+
+ try {
+ ProcessResult result = new ProcessExecutor()
+ .command(executableName,"ls","-q", "--filter", "state=Running")
+ .readOutput(true)
+ .exitValueNormal()
+ .execute();
+
+ String toUse = getToUse(preferredName, List.of(result.outputUTF8().split("\n"))).orElseThrow(() -> new IllegalStateException("No default machine present on this system!"));
+
+ logger.info(String.format("Using docker-machine with system %s (selected machine %s). If these do not match machine %s was likely ineligible for use.", toUse, preferredName, preferredName));
+
+ String url = new ProcessExecutor()
+ .command(executableName,"url", toUse)
+ .readOutput(true)
+ .exitValueNormal()
+ .execute().outputString().replaceAll("\n","");
+
+ return URI.create(url);
+
+
+ } catch (IOException | InterruptedException | TimeoutException e) {
+ throw new IllegalStateException(e);
+ }
+
+ }
+
+ private Optional getToUse(String preferredProvider, Collection strings) {
+ if (strings.contains(preferredProvider)) {
+ return Optional.of(preferredProvider);
+ } else if (strings.contains(DEFAULT)) {
+ return Optional.of(DEFAULT);
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ private boolean executableExists(String executable) {
+
+ Path directFile = Path.of(executable);
+ if (Files.isExecutable(directFile)) {
+ return true;
+ }
+
+ for (String pathString : System.getenv("PATH").split(Pattern.quote(File.pathSeparator))) {
+ Path path = Paths.get(pathString);
+ if (Files.isExecutable(path.resolve(executable))) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/impl/ManualSourceProvider.java b/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/impl/ManualSourceProvider.java
new file mode 100644
index 0000000..d917b19
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/impl/ManualSourceProvider.java
@@ -0,0 +1,55 @@
+package xyz.auriium.tick.docker.source.impl;
+
+import xyz.auriium.tick.container.CreationOptions;
+import xyz.auriium.tick.docker.source.*;
+
+import java.net.URI;
+import java.util.function.Supplier;
+
+/**
+ * Manual provider that provides a source using a given URI.
+ */
+public class ManualSourceProvider extends SimpleSourceProvider {
+
+ private final URI hostURI;
+ private final Supplier success;
+
+ /**
+ * A manual provider that provides a source using given URI.
+ * @param hostURI the URI to use when creating the source handle
+ * @param success a lambda function that determines whether the URI is usable or not.
+ */
+ public ManualSourceProvider(URI hostURI, Supplier success) {
+ this.hostURI = hostURI;
+ this.success = success;
+ }
+
+ /**
+ * Default constructor for provider
+ * @param hostURI the URI to use when creating the source handle
+ */
+ public ManualSourceProvider(URI hostURI) {
+ this.hostURI = hostURI;
+ this.success = ApplicableResult::success;
+ }
+
+ @Override
+ public String name() {
+ return "ManualSourceProvider";
+ }
+
+ @Override
+ public Integer priority() {
+ return -100000;
+ }
+
+ @Override
+ public URI makeURI(CreationOptions options) {
+ return hostURI;
+ }
+
+ @Override
+ public ApplicableResult isApplicable() {
+ return success.get();
+ }
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/impl/NoProviderException.java b/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/impl/NoProviderException.java
new file mode 100644
index 0000000..8cde6f5
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/impl/NoProviderException.java
@@ -0,0 +1,14 @@
+package xyz.auriium.tick.docker.source.impl;
+
+import xyz.auriium.tick.TickException;
+
+/**
+ * Exception thrown when there is no valid provider for the {@link AutoSourceProvider}
+ */
+public class NoProviderException extends TickException {
+
+
+ public NoProviderException() {
+ super("No valid provider could be found for use with your system!");
+ }
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/impl/RootlessSourceProvider.java b/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/impl/RootlessSourceProvider.java
new file mode 100644
index 0000000..c27ed4f
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/impl/RootlessSourceProvider.java
@@ -0,0 +1,102 @@
+package xyz.auriium.tick.docker.source.impl;
+
+import com.sun.jna.Library;
+import com.sun.jna.Native;
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.SystemUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import xyz.auriium.tick.container.CreationOptions;
+import xyz.auriium.tick.docker.source.ApplicableResult;
+
+import java.net.URI;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Optional;
+
+/**
+ * UNIX only SourceProvider that attempts to run docker regardless of whether it has permission to or not
+ *
+ * This was copy pasted from testcontainers and adapted to the tickbox format. I have no idea whether
+ * or not it actually functions.
+ */
+public class RootlessSourceProvider extends SimpleSourceProvider{
+
+ private static final Logger logger = LoggerFactory.getLogger("(TICK | ROOTLESS DOCKER PROVIDER)");
+
+ private Path resolveSocketPath() {
+ return tryEnv().orElseGet(() -> {
+ Path homePath = Paths.get(System.getProperty("user.home")).resolve(".docker").resolve("run");
+ return tryFolder(homePath).orElseGet(() -> {
+ Path implicitPath = Paths.get("/run/user/" + LibC.INSTANCE.getUUID());
+ return tryFolder(implicitPath).orElse(null);
+ });
+ });
+ }
+
+ private Optional tryEnv() {
+ String xdgRuntimeDir = System.getenv("XDG_RUNTIME_DIR");
+ if (StringUtils.isBlank(xdgRuntimeDir)) {
+ logger.debug("$XDG_RUNTIME_DIR is not set.");
+ return Optional.empty();
+ }
+ Path path = Paths.get(xdgRuntimeDir);
+ if (!Files.exists(path)) {
+ logger.debug("$XDG_RUNTIME_DIR is set to '{}' but the folder does not exist.", path);
+ return Optional.empty();
+ }
+ Path socketPath = path.resolve("docker.sock");
+ if (!Files.exists(socketPath)) {
+ logger.debug("$XDG_RUNTIME_DIR is set but '{}' does not exist.", socketPath);
+ return Optional.empty();
+ }
+ return Optional.of(socketPath);
+ }
+
+ private Optional tryFolder(Path path) {
+ if (!Files.exists(path)) {
+ logger.debug("'{}' does not exist.", path);
+ return Optional.empty();
+ }
+ Path socketPath = path.resolve("docker.sock");
+ if (!Files.exists(socketPath)) {
+ logger.debug("'{}' does not exist.", socketPath);
+ return Optional.empty();
+ }
+ return Optional.of(socketPath);
+ }
+
+ @Override
+ public String name() {
+ return "RootlessSourceProvider";
+ }
+
+ @Override
+ public Integer priority() {
+ return 29;
+ }
+
+ @Override
+ public ApplicableResult isApplicable() {
+ if (!SystemUtils.IS_OS_LINUX) return ApplicableResult.fail("Must use linux!");
+ Path resolve = resolveSocketPath();
+
+ if (resolve == null || !Files.exists(resolve)) return ApplicableResult.fail("Cannot find library used to run rootless!");
+
+ return ApplicableResult.success();
+ }
+
+ @Override
+ public URI makeURI(CreationOptions options) {
+ return URI.create("unix://" + resolveSocketPath().toString());
+ }
+
+ private interface LibC extends Library {
+
+ LibC INSTANCE = Native.loadLibrary("c", LibC.class);
+
+ int getUUID();
+ }
+
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/impl/SimpleSourceProvider.java b/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/impl/SimpleSourceProvider.java
new file mode 100644
index 0000000..0b227a7
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/impl/SimpleSourceProvider.java
@@ -0,0 +1,39 @@
+package xyz.auriium.tick.docker.source.impl;
+
+import com.github.dockerjava.core.*;
+import com.github.dockerjava.okhttp.OkDockerHttpClient;
+import com.github.dockerjava.transport.DockerHttpClient;
+import xyz.auriium.tick.container.CreationOptions;
+import xyz.auriium.tick.docker.source.DockerSource;
+import xyz.auriium.tick.docker.source.DockerSourceProvider;
+
+import java.net.URI;
+import java.nio.file.Paths;
+
+/**
+ * Client abstraction to allow source providers to only provide a URI
+ */
+public abstract class SimpleSourceProvider implements DockerSourceProvider {
+
+ @Override
+ public DockerSource source(CreationOptions options) {
+
+ URI pair = makeURI(options);
+
+ DockerHttpClient client = new OkDockerHttpClient.Builder()
+ .dockerHost(pair)
+ .sslConfig(new LocalDirectorySSLConfig(Paths.get(System.getProperty("user.home") + "/.docker/machine/certs/").toString()))
+ .build();
+
+ DefaultDockerClientConfig.Builder configBuilder = DefaultDockerClientConfig.createDefaultConfigBuilder();
+
+ if (configBuilder.build().getApiVersion() == RemoteApiVersion.UNKNOWN_VERSION) {
+ configBuilder.withApiVersion(RemoteApiVersion.VERSION_1_30);
+ }
+
+ return new DockerSourceImpl(pair, DockerClientImpl.getInstance(configBuilder.withDockerHost(pair.toString()).build(),client));
+ }
+
+ public abstract URI makeURI(CreationOptions options);
+
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/impl/SystemEnvSourceProvider.java b/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/impl/SystemEnvSourceProvider.java
new file mode 100644
index 0000000..433700b
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/impl/SystemEnvSourceProvider.java
@@ -0,0 +1,35 @@
+package xyz.auriium.tick.docker.source.impl;
+
+import com.github.dockerjava.core.DefaultDockerClientConfig;
+import xyz.auriium.tick.container.CreationOptions;
+import xyz.auriium.tick.docker.source.*;
+
+import java.net.URI;
+
+/**
+ * Provider that attempts to use system variables in order to provide a docker source
+ *
+ * Analogous to TestContainer's EnvironmentAndSystemPropertyClientProviderStrategy (geez)
+ */
+public class SystemEnvSourceProvider extends SimpleSourceProvider {
+
+ @Override
+ public String name() {
+ return "SystemEnvSourceProvider";
+ }
+
+ @Override
+ public Integer priority() {
+ return 50;
+ }
+
+ @Override
+ public ApplicableResult isApplicable() {
+ return System.getenv("DOCKER_HOST") != null ? ApplicableResult.success() : ApplicableResult.fail("System Environment Variables for docker could not be located or are null! Please fill them out!");
+ }
+
+ @Override
+ public URI makeURI(CreationOptions options) {
+ return DefaultDockerClientConfig.createDefaultConfigBuilder().build().getDockerHost();
+ }
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/impl/UnixSourceProvider.java b/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/impl/UnixSourceProvider.java
new file mode 100644
index 0000000..b3f0000
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/impl/UnixSourceProvider.java
@@ -0,0 +1,56 @@
+package xyz.auriium.tick.docker.source.impl;
+
+import org.apache.commons.lang.SystemUtils;
+import xyz.auriium.tick.container.CreationOptions;
+import xyz.auriium.tick.docker.source.ApplicableResult;
+
+import java.io.IOException;
+import java.net.URI;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+/**
+ * Provider that uses unix's socket in order to provide a source handle
+ *
+ * Ripped from TestContainers
+ */
+public class UnixSourceProvider extends SimpleSourceProvider {
+
+ protected static final String DOCKER_SOCK_PATH = "/var/run/docker.sock";
+ private static final String SOCKET_LOCATION = "unix://" + DOCKER_SOCK_PATH;
+ private static final int SOCKET_FILE_MODE_MASK = 0xc000;
+
+
+ @Override
+ public String name() {
+ return "UnixSourceProvider";
+ }
+
+ @Override
+ public Integer priority() {
+ return 30;
+ }
+
+ @Override
+ public ApplicableResult isApplicable() {
+ if (!(SystemUtils.IS_OS_LINUX || SystemUtils.IS_OS_MAC)) return ApplicableResult.fail("System is not UNIX based!");
+
+ Integer mode;
+ try {
+ mode = (Integer) Files.getAttribute(Paths.get(DOCKER_SOCK_PATH), "unix:mode");
+ } catch (IOException e) {
+ return ApplicableResult.fail("Could not find unix domain socket!");
+ }
+
+ if ((mode & 0xc000) != SOCKET_FILE_MODE_MASK) {
+ return ApplicableResult.fail("Found docker unix domain socket but file mode was not as expected (expected: srwxr-xr-x). This problem is possibly due to occurrence of this issue in the past: https://github.com/docker/docker/issues/13121");
+ }
+
+ return ApplicableResult.success();
+ }
+
+ @Override
+ public URI makeURI(CreationOptions options) {
+ return URI.create(SOCKET_LOCATION);
+ }
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/impl/WindowsSourceProvider.java b/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/impl/WindowsSourceProvider.java
new file mode 100644
index 0000000..e0ded68
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/docker/source/impl/WindowsSourceProvider.java
@@ -0,0 +1,39 @@
+package xyz.auriium.tick.docker.source.impl;
+
+import org.apache.commons.lang3.SystemUtils;
+import xyz.auriium.tick.container.CreationOptions;
+import xyz.auriium.tick.docker.source.ApplicableResult;
+
+import java.net.URI;
+
+/**
+ * Windows only Source Provider that uses npipe socket to provide a docker source handle
+ *
+ * Literally copy and pasted from TestContainers
+ */
+
+public class WindowsSourceProvider extends SimpleSourceProvider {
+
+ protected static final String DOCKER_SOCK_PATH = "//./pipe/docker_engine";
+ private static final String SOCKET_LOCATION = "npipe://" + DOCKER_SOCK_PATH;
+
+ @Override
+ public String name() {
+ return "WindowsSourceProvider";
+ }
+
+ @Override
+ public Integer priority() {
+ return 20;
+ }
+
+ @Override
+ public ApplicableResult isApplicable() {
+ return SystemUtils.IS_OS_WINDOWS ? ApplicableResult.success() : ApplicableResult.fail("OS must be windows to use NPIPE!");
+ }
+
+ @Override
+ public URI makeURI(CreationOptions options) {
+ return URI.create(SOCKET_LOCATION);
+ }
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/utils/Optionals.java b/tickbox-api/src/main/java/xyz/auriium/tick/utils/Optionals.java
new file mode 100644
index 0000000..6bfd7a9
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/utils/Optionals.java
@@ -0,0 +1,24 @@
+package xyz.auriium.tick.utils;
+
+import java.util.Optional;
+
+/**
+ * lazy class for lazy developer
+ *
+ * please everything else is held to a higher standard than TestContainers just lemme get away with this one
+ */
+public class Optionals {
+
+
+ /**
+ * I hate my life
+ * @param vars unsafe varargs
+ * @param unsafe
+ * @return un safe
+ * i wish arrays[]{"weren't so annoying to create"}
+ */
+ @SafeVarargs
+ public static Optional supply(T... vars) {
+ return Optional.of(vars);
+ }
+}
diff --git a/tickbox-api/src/main/java/xyz/auriium/tick/utils/Stoppable.java b/tickbox-api/src/main/java/xyz/auriium/tick/utils/Stoppable.java
new file mode 100644
index 0000000..36de488
--- /dev/null
+++ b/tickbox-api/src/main/java/xyz/auriium/tick/utils/Stoppable.java
@@ -0,0 +1,26 @@
+package xyz.auriium.tick.utils;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+/**
+ * Simple interface that offers a more definite term than closeable
+ * (Stop offers more context to a running service than close)
+ *
+ * Implements closeable in order to provide autocloseable functionality.
+ */
+public interface Stoppable extends Closeable {
+
+ /**
+ * Stops the service
+ */
+ void stop();
+
+ /**
+ * auto invoke
+ */
+ @Override
+ default void close() throws IOException {
+ stop();
+ }
+}
diff --git a/tickbox-api/src/test/java/xyz/auriium/tick/BaseTest.java b/tickbox-api/src/test/java/xyz/auriium/tick/BaseTest.java
new file mode 100644
index 0000000..f7215d2
--- /dev/null
+++ b/tickbox-api/src/test/java/xyz/auriium/tick/BaseTest.java
@@ -0,0 +1,30 @@
+package xyz.auriium.tick;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.TestInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class BaseTest {
+
+ protected static final Logger logger = LoggerFactory.getLogger("(TICK TESTING)");
+
+ @BeforeEach
+ void beforeEach(TestInfo testInfo) {
+
+ testInfo.getTestClass().orElseThrow().getName();
+
+ logger.info(String.format("\n <<< Test started: %s", testInfo.getDisplayName() + ">>> \n"));
+ }
+
+ @AfterEach
+ void afterEach(TestInfo testInfo) {
+
+ testInfo.getTestClass().orElseThrow().getName();
+
+ logger.info(String.format("\n\n <<< Test stopped: %s", testInfo.getDisplayName() + ">>> \n"));
+ }
+
+}
diff --git a/tickbox-api/src/test/java/xyz/auriium/tick/container/BadContainerTest.java b/tickbox-api/src/test/java/xyz/auriium/tick/container/BadContainerTest.java
new file mode 100644
index 0000000..f722903
--- /dev/null
+++ b/tickbox-api/src/test/java/xyz/auriium/tick/container/BadContainerTest.java
@@ -0,0 +1,68 @@
+package xyz.auriium.tick.container;
+
+import com.github.dockerjava.api.exception.NotFoundException;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledOnOs;
+import org.junit.jupiter.api.condition.OS;
+import xyz.auriium.tick.BaseTest;
+import xyz.auriium.tick.centralized.CommonTickFactory;
+import xyz.auriium.tick.centralized.HookResourceManager;
+import xyz.auriium.tick.centralized.Tick;
+import xyz.auriium.tick.docker.image.DefaultPullStrategy;
+import xyz.auriium.tick.docker.source.DockerSource;
+import xyz.auriium.tick.docker.source.impl.WindowsSourceProvider;
+
+import java.io.IOException;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+@EnabledOnOs(OS.WINDOWS) //TODO autoprovider for all platforms
+public class BadContainerTest extends BaseTest {
+
+ private static volatile Tick testingTick;
+ private static volatile DockerSource notClosedSource;
+
+ @BeforeAll
+ public static void startup() {
+ testingTick = new CommonTickFactory(
+ new HookResourceManager.Provider(false),
+ new WindowsSourceProvider(),
+ new DefaultPullStrategy.Provider()).produce(); //safe
+
+ notClosedSource = new WindowsSourceProvider().source(CreationOptions.defaults());
+ }
+
+ @AfterAll
+ public static void teardown() throws IOException {
+ testingTick.stop();
+
+ notClosedSource.getClient().close();
+ }
+
+ @Test
+ public void When_ContainerNameDuplicates_ThrowCE() {
+
+
+ assertThrows(IllegalStateException.class, () -> {
+ testingTick.createContainer(new TinyImageTerms("the-same"));
+ testingTick.createContainer(new TinyImageTerms("the-same"));
+ });
+
+ assertThrows(NotFoundException.class, () -> {
+ testingTick.expose().getClient().inspectContainerCmd("the-same").exec(); //removed
+ });
+
+
+ }
+
+ public void When_BadImage_PurgesSafely() {
+
+ assertThrows(IllegalStateException.class, () -> {
+ testingTick.createContainer(new TinyImageTerms("the-same", "hello-world"));
+ });
+
+ }
+
+}
diff --git a/tickbox-api/src/test/java/xyz/auriium/tick/container/TinyContainer.java b/tickbox-api/src/test/java/xyz/auriium/tick/container/TinyContainer.java
new file mode 100644
index 0000000..545032e
--- /dev/null
+++ b/tickbox-api/src/test/java/xyz/auriium/tick/container/TinyContainer.java
@@ -0,0 +1,32 @@
+package xyz.auriium.tick.container;
+
+import com.github.dockerjava.api.command.InspectContainerResponse;
+import xyz.auriium.tick.centralized.ResourceManager;
+
+public class TinyContainer implements TickContainer{
+
+ private final ResourceManager manager;
+ private final String name;
+ private final String id;
+
+ public TinyContainer(ResourceManager manager, String name, String id) {
+ this.manager = manager;
+ this.name = name;
+ this.id = id;
+ }
+
+ @Override
+ public String containerName() {
+ return name;
+ }
+
+ @Override
+ public String containerID() {
+ return id;
+ }
+
+ @Override
+ public void destroy() {
+ manager.destroyContainer(id);
+ }
+}
diff --git a/tickbox-api/src/test/java/xyz/auriium/tick/container/TinyImageTerms.java b/tickbox-api/src/test/java/xyz/auriium/tick/container/TinyImageTerms.java
new file mode 100644
index 0000000..6047eb9
--- /dev/null
+++ b/tickbox-api/src/test/java/xyz/auriium/tick/container/TinyImageTerms.java
@@ -0,0 +1,60 @@
+package xyz.auriium.tick.container;
+
+import com.github.dockerjava.api.model.Bind;
+import com.github.dockerjava.api.model.PortBinding;
+import xyz.auriium.tick.centralized.ResourceManager;
+import xyz.auriium.tick.docker.source.DockerSource;
+import xyz.auriium.tick.utils.Optionals;
+
+import java.util.Optional;
+
+public class TinyImageTerms implements CreationTerms{
+
+ private final String name;
+ private final String image;
+
+ public TinyImageTerms(String name, String image) {
+ this.name = name;
+ this.image = image;
+ }
+
+ public TinyImageTerms(String name) {
+ this.name = name;
+ this.image = "alpine:latest";
+ }
+
+ @Override
+ public String getDockerImageName() {
+ return image;
+ }
+
+ @Override
+ public String[] getParameters() {
+ return new String[0];
+ }
+
+ @Override
+ public Optional getBinds() {
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional getPortBindings() {
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional getCommands() {
+ return Optionals.supply("top");
+ }
+
+ @Override
+ public String getContainerName() {
+ return name;
+ }
+
+ @Override
+ public TinyContainer instantiateHolder(DockerSource location, ResourceManager manager, String dockerID) {
+ return new TinyContainer(manager, name, dockerID);
+ }
+}
diff --git a/tickbox-api/src/test/java/xyz/auriium/tick/container/TinyMessedUpPorts.java b/tickbox-api/src/test/java/xyz/auriium/tick/container/TinyMessedUpPorts.java
new file mode 100644
index 0000000..f4cc6c6
--- /dev/null
+++ b/tickbox-api/src/test/java/xyz/auriium/tick/container/TinyMessedUpPorts.java
@@ -0,0 +1,17 @@
+package xyz.auriium.tick.container;
+
+import com.github.dockerjava.api.model.PortBinding;
+import xyz.auriium.tick.utils.Optionals;
+
+import java.util.Optional;
+
+public class TinyMessedUpPorts extends TinyImageTerms{
+ public TinyMessedUpPorts(String name) {
+ super(name);
+ }
+
+ @Override
+ public Optional getPortBindings() {
+ return Optionals.supply(PortBinding.parse("55:55"), PortBinding.parse("55:55"));
+ }
+}
diff --git a/tickbox-api/src/test/java/xyz/auriium/tick/provider/AutoProviderTest.java b/tickbox-api/src/test/java/xyz/auriium/tick/provider/AutoProviderTest.java
new file mode 100644
index 0000000..713254c
--- /dev/null
+++ b/tickbox-api/src/test/java/xyz/auriium/tick/provider/AutoProviderTest.java
@@ -0,0 +1,4 @@
+package xyz.auriium.tick.provider;
+
+public class AutoProviderTest {
+}
diff --git a/tickbox-api/src/test/java/xyz/auriium/tick/provider/SystemEnvProviderTest.java b/tickbox-api/src/test/java/xyz/auriium/tick/provider/SystemEnvProviderTest.java
new file mode 100644
index 0000000..6a8a12a
--- /dev/null
+++ b/tickbox-api/src/test/java/xyz/auriium/tick/provider/SystemEnvProviderTest.java
@@ -0,0 +1,22 @@
+package xyz.auriium.tick.provider;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import xyz.auriium.tick.centralized.CommonTickFactory;
+import xyz.auriium.tick.centralized.HookResourceManager;
+import xyz.auriium.tick.centralized.Tick;
+import xyz.auriium.tick.docker.image.DefaultPullStrategy;
+import xyz.auriium.tick.docker.source.impl.SystemEnvSourceProvider;
+
+@Disabled //TODO mocking system envs
+public class SystemEnvProviderTest {
+
+ @Test
+ public void startup() {
+ Tick tick = new CommonTickFactory(
+ new HookResourceManager.Provider(false),
+ new SystemEnvSourceProvider(),
+ new DefaultPullStrategy.Provider()).produce();
+ }
+
+}
diff --git a/tickbox-api/src/test/java/xyz/auriium/tick/provider/UnixProviderTest.java b/tickbox-api/src/test/java/xyz/auriium/tick/provider/UnixProviderTest.java
new file mode 100644
index 0000000..742cba9
--- /dev/null
+++ b/tickbox-api/src/test/java/xyz/auriium/tick/provider/UnixProviderTest.java
@@ -0,0 +1,39 @@
+package xyz.auriium.tick.provider;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledOnOs;
+import org.junit.jupiter.api.condition.OS;
+import xyz.auriium.tick.BaseTest;
+import xyz.auriium.tick.centralized.CommonTickFactory;
+import xyz.auriium.tick.centralized.HookResourceManager;
+import xyz.auriium.tick.centralized.Tick;
+import xyz.auriium.tick.container.TickContainer;
+import xyz.auriium.tick.container.TinyImageTerms;
+import xyz.auriium.tick.docker.image.DefaultPullStrategy;
+import xyz.auriium.tick.docker.source.impl.UnixSourceProvider;
+
+@EnabledOnOs(OS.LINUX)
+public class UnixProviderTest extends BaseTest {
+
+ private static volatile Tick tick;
+
+ @BeforeAll
+ public static void startup() {
+ tick = new CommonTickFactory(
+ new HookResourceManager.Provider(false),
+ new UnixSourceProvider(),
+ new DefaultPullStrategy.Provider()).produce();
+ }
+
+ @AfterAll
+ public static void teardown() {
+ tick.stop();
+ }
+
+ @Test
+ public void whenCreated_ContainerWorksFine() {
+ TickContainer container = tick.createContainer(new TinyImageTerms("unix-test"));
+ }
+}
diff --git a/tickbox-api/src/test/java/xyz/auriium/tick/provider/WindowsProviderTest.java b/tickbox-api/src/test/java/xyz/auriium/tick/provider/WindowsProviderTest.java
new file mode 100644
index 0000000..3ff6a2b
--- /dev/null
+++ b/tickbox-api/src/test/java/xyz/auriium/tick/provider/WindowsProviderTest.java
@@ -0,0 +1,43 @@
+package xyz.auriium.tick.provider;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledOnOs;
+import org.junit.jupiter.api.condition.OS;
+import xyz.auriium.tick.BaseTest;
+import xyz.auriium.tick.centralized.CommonTickFactory;
+import xyz.auriium.tick.centralized.HookResourceManager;
+import xyz.auriium.tick.centralized.Tick;
+import xyz.auriium.tick.container.TickContainer;
+import xyz.auriium.tick.container.TinyImageTerms;
+import xyz.auriium.tick.docker.image.DefaultPullStrategy;
+import xyz.auriium.tick.docker.source.impl.WindowsSourceProvider;
+
+@EnabledOnOs(OS.WINDOWS)
+public class WindowsProviderTest extends BaseTest {
+
+ private static volatile Tick tick;
+
+ @BeforeAll
+ public static void startup() {
+
+
+ tick = new CommonTickFactory(
+ new HookResourceManager.Provider(false),
+ new WindowsSourceProvider(),
+ new DefaultPullStrategy.Provider()).produce();
+ }
+
+ @AfterAll
+ public static void teardown() {
+ tick.stop();
+ }
+
+ @Test
+ public void whenCreated_ContainerWorksFine() {
+ TickContainer container = tick.createContainer(new TinyImageTerms("windows-test"));
+ }
+
+
+}
diff --git a/tickbox-api/src/test/java/xyz/auriium/tick/shutdown/ShutdownHookTest.java b/tickbox-api/src/test/java/xyz/auriium/tick/shutdown/ShutdownHookTest.java
new file mode 100644
index 0000000..7bef965
--- /dev/null
+++ b/tickbox-api/src/test/java/xyz/auriium/tick/shutdown/ShutdownHookTest.java
@@ -0,0 +1,32 @@
+package xyz.auriium.tick.shutdown;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.condition.EnabledOnOs;
+import org.junit.jupiter.api.condition.OS;
+import xyz.auriium.tick.centralized.CommonTickFactory;
+import xyz.auriium.tick.centralized.HookResourceManager;
+import xyz.auriium.tick.centralized.Tick;
+import xyz.auriium.tick.docker.image.DefaultPullStrategy;
+import xyz.auriium.tick.docker.source.impl.WindowsSourceProvider;
+
+@Disabled //TODO how do we mock system?
+public class ShutdownHookTest {
+
+ private static volatile Tick tick;
+
+ @BeforeAll
+ public static void startup() {
+ tick = new CommonTickFactory(
+ new HookResourceManager.Provider(true),
+ new WindowsSourceProvider(),
+ new DefaultPullStrategy.Provider()).produce();
+ }
+
+
+
+
+
+
+
+}
diff --git a/tickbox-api/src/test/java/xyz/auriium/tick/shutdown/TickClosureTest.java b/tickbox-api/src/test/java/xyz/auriium/tick/shutdown/TickClosureTest.java
new file mode 100644
index 0000000..d4c709a
--- /dev/null
+++ b/tickbox-api/src/test/java/xyz/auriium/tick/shutdown/TickClosureTest.java
@@ -0,0 +1,62 @@
+package xyz.auriium.tick.shutdown;
+
+import com.github.dockerjava.api.exception.NotFoundException;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledOnOs;
+import org.junit.jupiter.api.condition.OS;
+import xyz.auriium.tick.BaseTest;
+import xyz.auriium.tick.centralized.CommonTickFactory;
+import xyz.auriium.tick.centralized.HookResourceManager;
+import xyz.auriium.tick.centralized.Tick;
+import xyz.auriium.tick.container.CreationOptions;
+import xyz.auriium.tick.container.TinyImageTerms;
+import xyz.auriium.tick.docker.image.DefaultPullStrategy;
+import xyz.auriium.tick.docker.source.DockerSource;
+import xyz.auriium.tick.docker.source.impl.WindowsSourceProvider;
+
+import java.io.IOException;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+@EnabledOnOs(OS.WINDOWS)
+public class TickClosureTest extends BaseTest {
+
+ private static volatile DockerSource notClosedSource;
+
+ @BeforeAll
+ public static void startup() {
+ notClosedSource = new WindowsSourceProvider().source(CreationOptions.defaults());
+ }
+
+ @AfterAll
+ public static void teardown() throws IOException {
+ notClosedSource.getClient().close();
+ }
+
+ @Test
+ public void whenStopped_ContainerShouldNotExist() {
+ Tick tick = new CommonTickFactory(new HookResourceManager.Provider(false), new WindowsSourceProvider(), new DefaultPullStrategy.Provider()).produce();
+
+ tick.createContainer(new TinyImageTerms("should-not-exist"));
+
+ tick.stop();
+
+ assertThrows(NotFoundException.class, () -> {
+ notClosedSource.getClient().inspectContainerCmd("should-not-exist").exec();
+ });
+ }
+
+ @Test
+ public void whenStopped_ContainerShouldNotAllowExpose() {
+ Tick tick = new CommonTickFactory(new HookResourceManager.Provider(false), new WindowsSourceProvider(), new DefaultPullStrategy.Provider()).produce();
+
+ tick.createContainer(new TinyImageTerms("should-not-expose"));
+
+ tick.stop();
+ }
+
+
+}
diff --git a/tickbox-api/src/test/java/xyz/auriium/tick/startup/BadSourceProvider.java b/tickbox-api/src/test/java/xyz/auriium/tick/startup/BadSourceProvider.java
new file mode 100644
index 0000000..daa17dd
--- /dev/null
+++ b/tickbox-api/src/test/java/xyz/auriium/tick/startup/BadSourceProvider.java
@@ -0,0 +1,33 @@
+package xyz.auriium.tick.startup;
+
+import xyz.auriium.tick.container.CreationOptions;
+import xyz.auriium.tick.docker.source.ApplicableResult;
+import xyz.auriium.tick.docker.source.DockerSourceProvider;
+import xyz.auriium.tick.docker.source.impl.SimpleSourceProvider;
+
+import java.net.URI;
+
+/**
+ * Represents a malfunctioning source provider that cannot tell if it is accurately providing valid fields
+ */
+public class BadSourceProvider extends SimpleSourceProvider {
+ @Override
+ public String name() {
+ return "BadSourceProvider";
+ }
+
+ @Override
+ public Integer priority() {
+ return 1;
+ }
+
+ @Override
+ public ApplicableResult isApplicable() {
+ return ApplicableResult.success(); //always true to simulate fallacy
+ }
+
+ @Override
+ public URI makeURI(CreationOptions options) {
+ return URI.create("https://i.shit.my.pants.com");
+ }
+}
diff --git a/tickbox-api/src/test/java/xyz/auriium/tick/startup/BadStartupTest.java b/tickbox-api/src/test/java/xyz/auriium/tick/startup/BadStartupTest.java
new file mode 100644
index 0000000..469fdb3
--- /dev/null
+++ b/tickbox-api/src/test/java/xyz/auriium/tick/startup/BadStartupTest.java
@@ -0,0 +1,25 @@
+package xyz.auriium.tick.startup;
+
+import com.github.dockerjava.api.exception.DockerClientException;
+import org.apache.http.client.ClientProtocolException;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import xyz.auriium.tick.PostCreationTestException;
+import xyz.auriium.tick.centralized.CommonTickFactory;
+import xyz.auriium.tick.centralized.HookResourceManager;
+import xyz.auriium.tick.docker.image.DefaultPullStrategy;
+import xyz.auriium.tick.startup.BadSourceProvider;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class BadStartupTest {
+
+ @Test
+ public void shouldThrowWhenBadSourceProvided() {
+ assertThrows(DockerClientException.class, () -> {
+ new CommonTickFactory(new HookResourceManager.Provider(false), new BadSourceProvider(), new DefaultPullStrategy.Provider()).produce();
+ });
+
+ }
+
+}
diff --git a/tickbox-api/src/test/resources/logback-test.xml b/tickbox-api/src/test/resources/logback-test.xml
new file mode 100644
index 0000000..afaebf8
--- /dev/null
+++ b/tickbox-api/src/test/resources/logback-test.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tick-api/pom.xml b/tickbox-containers-sql/pom.xml
similarity index 69%
rename from tick-api/pom.xml
rename to tickbox-containers-sql/pom.xml
index e99b232..f78d1ec 100644
--- a/tick-api/pom.xml
+++ b/tickbox-containers-sql/pom.xml
@@ -3,15 +3,13 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- tick
- me.aurium
+ tickbox-parent
+ xyz.auriium
1.0.0
4.0.0
- tick-api
-
- 1.0.0
+ tickbox-containers-sql
${compiler.version}
@@ -20,9 +18,8 @@
- com.amihaiemil.web
- docker-java-api
- 0.0.13
+ xyz.auriium
+ tickbox-api
diff --git a/tickbox-containers-sql/src/main/java/xyz/auriium/tick/containers/sql/JDBCConfig.java b/tickbox-containers-sql/src/main/java/xyz/auriium/tick/containers/sql/JDBCConfig.java
new file mode 100644
index 0000000..a227ba3
--- /dev/null
+++ b/tickbox-containers-sql/src/main/java/xyz/auriium/tick/containers/sql/JDBCConfig.java
@@ -0,0 +1,46 @@
+package xyz.auriium.tick.containers.sql;
+
+public class JDBCConfig {
+
+ private final String rootPassword;
+ private final String databaseUsername;
+ private final String databasePassword;
+ private final String databaseName;
+ private final String containerName;
+
+ private final int portBinding;
+
+ public JDBCConfig(String rootPassword, String databaseUsername, String databasePassword, String databaseName, String containerName, int portBinding) {
+ this.rootPassword = rootPassword;
+ this.databaseUsername = databaseUsername;
+ this.databasePassword = databasePassword;
+ this.databaseName = databaseName;
+ this.containerName = containerName;
+ this.portBinding = portBinding;
+ }
+
+ public String getRootPassword() {
+ return rootPassword;
+ }
+
+ public String getDatabaseUsername() {
+ return databaseUsername;
+ }
+
+ public String getDatabasePassword() {
+ return databasePassword;
+ }
+
+ public String getDatabaseName() {
+ return databaseName;
+ }
+
+ public String getContainerName() {
+ return containerName;
+ }
+
+ public int getPortBinding() {
+ return portBinding;
+ }
+
+}
diff --git a/tickbox-containers-sql/src/main/java/xyz/auriium/tick/containers/sql/JDBCContainer.java b/tickbox-containers-sql/src/main/java/xyz/auriium/tick/containers/sql/JDBCContainer.java
new file mode 100644
index 0000000..085e103
--- /dev/null
+++ b/tickbox-containers-sql/src/main/java/xyz/auriium/tick/containers/sql/JDBCContainer.java
@@ -0,0 +1,12 @@
+package xyz.auriium.tick.containers.sql;
+
+import xyz.auriium.tick.container.TickContainer;
+
+public interface JDBCContainer extends TickContainer {
+
+ String getJDBCUrl();
+
+ //String driverClass;
+ //String
+
+}
diff --git a/tickbox-containers-sql/src/main/java/xyz/auriium/tick/containers/sql/JDBCTerms.java b/tickbox-containers-sql/src/main/java/xyz/auriium/tick/containers/sql/JDBCTerms.java
new file mode 100644
index 0000000..cddb6c7
--- /dev/null
+++ b/tickbox-containers-sql/src/main/java/xyz/auriium/tick/containers/sql/JDBCTerms.java
@@ -0,0 +1,12 @@
+package xyz.auriium.tick.containers.sql;
+
+import xyz.auriium.tick.container.CreationTerms;
+
+/**
+ * Marker interface for terms that use jdbc
+ */
+public interface JDBCTerms extends CreationTerms {
+
+
+
+}
diff --git a/tickbox-containers-sql/src/main/java/xyz/auriium/tick/containers/sql/mariadb/MariaDBContainer.java b/tickbox-containers-sql/src/main/java/xyz/auriium/tick/containers/sql/mariadb/MariaDBContainer.java
new file mode 100644
index 0000000..5bd9d53
--- /dev/null
+++ b/tickbox-containers-sql/src/main/java/xyz/auriium/tick/containers/sql/mariadb/MariaDBContainer.java
@@ -0,0 +1,49 @@
+package xyz.auriium.tick.containers.sql.mariadb;
+
+import xyz.auriium.tick.centralized.ResourceManager;
+import xyz.auriium.tick.container.JDBCUrlBuilder;
+import xyz.auriium.tick.containers.sql.JDBCConfig;
+import xyz.auriium.tick.containers.sql.JDBCContainer;
+import xyz.auriium.tick.docker.source.DockerSource;
+
+public class MariaDBContainer implements JDBCContainer {
+
+ private final ResourceManager manager;
+ private final DockerSource location;
+
+ private final String containerID;
+ private final JDBCConfig config;
+
+ public MariaDBContainer(ResourceManager manager, DockerSource location, String containerID, JDBCConfig config) {
+ this.manager = manager;
+ this.location = location;
+ this.containerID = containerID;
+ this.config = config;
+ }
+
+
+ @Override
+ public String getJDBCUrl() {
+ return new JDBCUrlBuilder()
+ .withDBName(config.getDatabaseName())
+ .withDriver("mariadb")
+ .withIP(location.getSourceHost())
+ .withPort(config.getPortBinding()).build();
+ }
+
+ @Override
+ public String containerName() {
+ return config.getContainerName();
+ }
+
+ @Override
+ public String containerID() {
+ return containerID;
+ }
+
+ @Override
+ public void destroy() {
+ manager.destroyContainer(containerID);
+ }
+
+}
diff --git a/tickbox-containers-sql/src/main/java/xyz/auriium/tick/containers/sql/mariadb/MariaDBTerms.java b/tickbox-containers-sql/src/main/java/xyz/auriium/tick/containers/sql/mariadb/MariaDBTerms.java
new file mode 100644
index 0000000..c14620c
--- /dev/null
+++ b/tickbox-containers-sql/src/main/java/xyz/auriium/tick/containers/sql/mariadb/MariaDBTerms.java
@@ -0,0 +1,81 @@
+package xyz.auriium.tick.containers.sql.mariadb;
+
+import com.github.dockerjava.api.model.Bind;
+import com.github.dockerjava.api.model.PortBinding;
+import xyz.auriium.tick.centralized.ResourceManager;
+import xyz.auriium.tick.containers.sql.JDBCConfig;
+import xyz.auriium.tick.containers.sql.JDBCContainer;
+import xyz.auriium.tick.containers.sql.JDBCTerms;
+import xyz.auriium.tick.container.Arguments;
+import xyz.auriium.tick.docker.source.DockerSource;
+
+import java.util.Optional;
+
+/**
+ * mariadb
+ *
+ * external port is the exposed port of the mariadb container, but still
+ *
+ *
+ * EVERYTHING HERE NEEDS A TON OF WORK. JDBC SUPPORT FOR TESTCONTAINERS IS SOMETHING I AM ADDING TOMORROW NOT TONIGHT.
+ *
+ * TODO: image searching and guaruntees
+ */
+public class MariaDBTerms implements JDBCTerms {
+
+ private final JDBCConfig config;
+ private final Arguments args;
+
+ public MariaDBTerms(String rootPassword, String databaseUsername, String databasePassword, String databaseName, String containerName, int portBinding) {
+ this.config = new JDBCConfig(rootPassword, databaseUsername, databasePassword, databaseName, containerName, portBinding);
+
+ this.args = new Arguments.Builder()
+ .withBinding(PortBinding.parse(portBinding + ":3306"))
+ .withImage("mariadb:10.5.9")
+ .withParams(
+ "MYSQL_ROOT=" + rootPassword,
+ "MYSQL_DATABASE=" + databaseName,
+ "MYSQL_USER=" + databaseUsername,
+ "MYSQL_PASSWORD=" + databasePassword
+ )
+ .withCreationName(containerName)
+ .build();
+ }
+
+
+ @Override
+ public String getDockerImageName() {
+ return args.getDockerImageName();
+ }
+
+ @Override
+ public String[] getParameters() {
+ return args.getParameters();
+ }
+
+ @Override
+ public Optional getBinds() {
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional getPortBindings() {
+ return Optional.of(new PortBinding[]{args.getBinding()});
+ }
+
+ @Override
+ public Optional getCommands() {
+ return Optional.empty();
+ }
+
+ @Override
+ public String getContainerName() {
+ return args.getContainerName();
+ }
+
+ @Override
+ public JDBCContainer instantiateHolder(DockerSource location, ResourceManager manager, String id) {
+ return new MariaDBContainer(manager, location, id, config);
+ }
+
+}