001package io.ebean.test.config.platform;
002
003import io.ebean.config.DatabaseConfig;
004import io.ebean.docker.container.ContainerFactory;
005import org.slf4j.Logger;
006import org.slf4j.LoggerFactory;
007
008import java.util.HashMap;
009import java.util.Map;
010import java.util.Properties;
011
012import static java.util.concurrent.CompletableFuture.allOf;
013import static java.util.concurrent.CompletableFuture.runAsync;
014
015public class PlatformAutoConfig {
016
017  private static final Logger log = LoggerFactory.getLogger("io.ebean.test");
018
019  /**
020   * Known platforms we can setup locally or via docker container.
021   */
022  private static final Map<String, PlatformSetup> KNOWN_PLATFORMS = new HashMap<>();
023
024  static {
025    KNOWN_PLATFORMS.put("h2", new H2Setup());
026    KNOWN_PLATFORMS.put("sqlite", new SqliteSetup());
027    KNOWN_PLATFORMS.put("postgres", new PostgresSetup());
028    KNOWN_PLATFORMS.put("postgis", new PostgisSetup());
029    KNOWN_PLATFORMS.put("nuodb", new NuoDBSetup());
030    KNOWN_PLATFORMS.put("mysql", new MySqlSetup());
031    KNOWN_PLATFORMS.put("mariadb", new MariaDBSetup());
032    KNOWN_PLATFORMS.put("sqlserver", new SqlServerSetup());
033    KNOWN_PLATFORMS.put("oracle", new OracleSetup());
034    KNOWN_PLATFORMS.put("clickhouse", new ClickHouseSetup());
035    KNOWN_PLATFORMS.put("cockroach", new CockroachSetup());
036    KNOWN_PLATFORMS.put("hana", new HanaSetup());
037    KNOWN_PLATFORMS.put("db2", new Db2Setup());
038    KNOWN_PLATFORMS.put("yugabyte", new YugabyteSetup());
039  }
040
041  private final DatabaseConfig config;
042  private final Properties properties;
043  private String db;
044  private String platform;
045  private PlatformSetup platformSetup;
046  private String databaseName;
047
048  public PlatformAutoConfig(String db, DatabaseConfig config) {
049    this.db = db;
050    this.config = config;
051    this.properties = config.getProperties();
052  }
053
054  /**
055   * Configure the DataSource for the extra database.
056   */
057  public void configExtraDataSource() {
058    determineTestPlatform();
059    if (isKnownPlatform()) {
060      databaseName = config.getName();
061      db = config.getName();
062
063      Config config = new Config(db, platform, databaseName, this.config);
064      platformSetup.setupExtraDbDataSource(config);
065      log.debug("configured dataSource for extraDb name:{} url:{}", db, this.config.getDataSourceConfig().getUrl());
066    }
067  }
068
069  /**
070   * Run setting up for testing.
071   */
072  public void run() {
073    determineTestPlatform();
074    if (isKnownPlatform()) {
075      readDbName();
076      setupForTesting();
077    }
078  }
079
080  private void setupForTesting() {
081    // start containers in parallel
082    RedisSetup.run(properties);
083    allOf(runAsync(this::setupElasticSearch), runAsync(this::setupDatabase)).join();
084  }
085
086  private void setupElasticSearch() {
087    new ElasticSearchSetup(properties).run();
088  }
089
090  private void setupDatabase() {
091    Config config = new Config(db, platform, databaseName, this.config);
092    Properties dockerProperties = platformSetup.setup(config);
093    if (!dockerProperties.isEmpty()) {
094      if (isDebug()) {
095        log.info("Docker properties: {}", dockerProperties);
096      } else {
097        log.debug("Docker properties: {}", dockerProperties);
098      }
099      // start the docker container with appropriate configuration
100      new ContainerFactory(dockerProperties, config.getDockerPlatform()).startContainers();
101    }
102  }
103
104  private boolean isDebug() {
105    String val = properties.getProperty("ebean.test.debug");
106    return (val != null && val.equalsIgnoreCase("true"));
107  }
108
109  private void readDbName() {
110    databaseName = properties.getProperty("ebean.test.dbName");
111    if (databaseName == null) {
112      if (inMemoryDb()) {
113        databaseName = "test_db";
114      } else {
115        throw new IllegalStateException("ebean.test.dbName is not set but required for testing configuration with platform " + platform);
116      }
117    }
118  }
119
120  private boolean inMemoryDb() {
121    return platformSetup.isLocal();
122  }
123
124  /**
125   * Return true if we match a known platform and know how to set it up for testing (via docker usually).
126   */
127  private boolean isKnownPlatform() {
128    if (platform == null) {
129      return false;
130    }
131    this.platformSetup = KNOWN_PLATFORMS.get(platform);
132    if (platformSetup == null) {
133      log.warn("unknown platform {} - skipping platform setup", platform);
134    }
135    return platformSetup != null;
136  }
137
138  /**
139   * Determine the platform we are going to use to run testing.
140   */
141  private void determineTestPlatform() {
142    String testPlatform = properties.getProperty("ebean.test.platform");
143    if (testPlatform != null && !testPlatform.isEmpty()) {
144      if (db == null) {
145        platform = testPlatform.trim();
146        db = "db";
147      } else {
148        // using command line system property to test alternate platform
149        // and we expect db to match a platform name
150        platform = db;
151      }
152    }
153  }
154}