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(PlatformAutoConfig.class);
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  }
038
039  private final DatabaseConfig config;
040
041  private final Properties properties;
042
043  private String db;
044
045  private String platform;
046
047  private PlatformSetup platformSetup;
048
049  private String databaseName;
050
051  public PlatformAutoConfig(String db, DatabaseConfig config) {
052    this.db = db;
053    this.config = config;
054    this.properties = config.getProperties();
055  }
056
057  /**
058   * Configure the DataSource for the extra database.
059   */
060  public void configExtraDataSource() {
061    determineTestPlatform();
062    if (isKnownPlatform()) {
063      databaseName = config.getName();
064      db = config.getName();
065
066      Config config = new Config(db, platform, databaseName, this.config);
067      platformSetup.setupExtraDbDataSource(config);
068      log.debug("configured dataSource for extraDb name:{} url:{}", db, this.config.getDataSourceConfig().getUrl());
069    }
070  }
071
072  /**
073   * Run setting up for testing.
074   */
075  public void run() {
076    determineTestPlatform();
077    if (isKnownPlatform()) {
078      readDbName();
079      setupForTesting();
080    }
081  }
082
083  private void setupForTesting() {
084    // start containers in parallel
085    RedisSetup.run(properties);
086    allOf(runAsync(this::setupElasticSearch), runAsync(this::setupDatabase)).join();
087  }
088
089  private void setupElasticSearch() {
090    new ElasticSearchSetup(properties).run();
091  }
092
093  private void setupDatabase() {
094    Config config = new Config(db, platform, databaseName, this.config);
095    Properties dockerProperties = platformSetup.setup(config);
096    if (!dockerProperties.isEmpty()) {
097      if (isDebug()) {
098        log.info("Docker properties: {}", dockerProperties);
099      } else {
100        log.debug("Docker properties: {}", dockerProperties);
101      }
102      // start the docker container with appropriate configuration
103      new ContainerFactory(dockerProperties, config.getDockerPlatform()).startContainers();
104    }
105  }
106
107  private boolean isDebug() {
108    String val = properties.getProperty("ebean.test.debug");
109    return (val != null && val.equalsIgnoreCase("true"));
110  }
111
112  private void readDbName() {
113    databaseName = properties.getProperty("ebean.test.dbName");
114    if (databaseName == null) {
115      if (inMemoryDb()) {
116        databaseName = "test_db";
117      } else {
118        throw new IllegalStateException("ebean.test.dbName is not set but required for testing configuration with platform " + platform);
119      }
120    }
121  }
122
123  private boolean inMemoryDb() {
124    return platformSetup.isLocal();
125  }
126
127  /**
128   * Return true if we match a known platform and know how to set it up for testing (via docker usually).
129   */
130  private boolean isKnownPlatform() {
131    if (platform == null) {
132      return false;
133    }
134    this.platformSetup = KNOWN_PLATFORMS.get(platform);
135    if (platformSetup == null) {
136      log.warn("unknown platform {} - skipping platform setup", platform);
137    }
138    return platformSetup != null;
139  }
140
141  /**
142   * Determine the platform we are going to use to run testing.
143   */
144  private void determineTestPlatform() {
145    String testPlatform = properties.getProperty("ebean.test.platform");
146    if (testPlatform != null && !testPlatform.isEmpty()) {
147      if (db == null) {
148        platform = testPlatform.trim();
149        db = "db";
150      } else {
151        // using command line system property to test alternate platform
152        // and we expect db to match a platform name
153        platform = db;
154      }
155    }
156  }
157}