001package io.ebean.docker.commands;
002
003import io.ebean.docker.commands.process.ProcessHandler;
004import io.ebean.docker.commands.process.ProcessResult;
005import io.ebean.docker.container.Container;
006
007import java.util.ArrayList;
008import java.util.List;
009import java.util.Properties;
010
011/**
012 * Commands for controlling a DB2 docker container.
013 */
014public class Db2Container extends JdbcBaseDbContainer implements Container {
015
016  public static Db2Container create(String Db2Version, Properties properties) {
017    return new Db2Container(new Db2Config(Db2Version, properties));
018  }
019
020  public Db2Container(Db2Config config) {
021    super(config);
022    this.waitForConnectivityAttempts = 2000;
023  }
024
025  @Override
026  void createDatabase() {
027    if (checkConnectivity(false)) {
028      return; // container with DB & userName already running - do not recreate DB
029    }
030    // #1 Do user management (as root)
031    try {
032      dockerSu("root", "useradd -g db2iadm1 " + dbConfig.getUsername());
033    } catch (CommandException e) {
034      e.getResult().getOutLines().forEach(log::warn);
035    }
036    dockerSu("root", "echo \"" + dbConfig.getUsername() + ":" + dbConfig.getPassword() + "\" | chpasswd");
037
038    // #2 create database (with optional create options)
039    String cmd = "db2 create database " + dbConfig.getDbName();
040    if (defined(((Db2Config) dbConfig).getCreateOptions())) {
041      cmd = cmd + " " + ((Db2Config) dbConfig).getCreateOptions();
042    }
043    dockerSu(cmd);
044
045    // #3 set optional config options
046    if (defined(((Db2Config) dbConfig).getConfigOptions())) {
047      cmd = "db2 update database config for " + dbConfig.getDbName() + " " + ((Db2Config) dbConfig).getConfigOptions();
048      dockerSu(dbConfig.getAdminUsername(), cmd);
049    }
050
051    // #4 activate database
052    cmd = "db2 activate database " + dbConfig.getDbName();
053    dockerSu(cmd);
054
055    // #5 set recommended values for database
056    cmd = "/var/db2_setup/lib/set_rec_values.sh " + dbConfig.getDbName();
057    dockerSu(cmd);
058  }
059
060  @Override
061  void dropCreateDatabase() {
062    try {
063      dockerSu("db2 drop database " + dbConfig.getDbName());
064    } catch (CommandException e) {
065      // may fail, if database does not exist
066      e.getResult().getOutLines().forEach(log::warn);
067    }
068    createDatabase();
069  }
070
071  @Override
072  protected ProcessBuilder runProcess() {
073
074    List<String> args = dockerRun();
075    args.add("--privileged"); // this container needs extended permissions
076    args.add("-e");
077    args.add("LICENSE=accept");
078
079    args.add("-e");
080    args.add("SAMPLEDB=false");
081
082    args.add("-e");
083    args.add("ARCHIVE_LOGS=false");
084    // (default: true) Specify false to not configure log archiving (reduces start
085    // up time)
086
087    args.add("-e");
088    args.add("AUTOCONFIG=false");
089    // (default: true) Specify false to not run auto configuration on the instance
090    // and database (reduces start up time)
091
092    // set admin user & pw - but do not create a database
093    args.add("-e");
094    args.add("DB2INSTANCE=" + dbConfig.getAdminUsername());
095
096    if (defined(dbConfig.getAdminPassword())) {
097      args.add("-e");
098      args.add("DB2INST1_PASSWORD=" + dbConfig.getAdminPassword());
099    }
100    args.add("-e");
101    args.add("DBNAME=");
102    args.add(config.getImage());
103    return createProcessBuilder(args);
104  }
105
106  /**
107   * Runs the given (linux) command inside the container as given user.
108   */
109  protected List<String> dockerSu(String user, String cmd) {
110    List<String> args = new ArrayList<>();
111    args.add(config.docker);
112    args.add("exec");
113    args.add("-i");
114    args.add(config.containerName());
115    args.add("su");
116    args.add("-");
117    args.add(user);
118    args.add("-c");
119    args.add(cmd);
120    ProcessBuilder pb = createProcessBuilder(args);
121    ProcessResult pr = ProcessHandler.process(pb);
122    pr.getOutLines().forEach(log::debug);
123    return pr.getOutLines();
124  }
125
126  /**
127   * Executes the (linux) command as DB-admin user.
128   */
129  protected List<String> dockerSu(String cmd) {
130    return dockerSu(dbConfig.getAdminUsername(), cmd);
131  }
132
133  /**
134   * used to detect, when the container is up. This does not mean, you can already
135   * connect with JDBC. You must create the database first.
136   */
137  @Override
138  boolean checkConnectivity() {
139    try {
140      List<String> result = dockerSu("db2pd -");
141
142      for (String outLine : result) {
143        if (outLine.contains("-- Active --")) {
144          return true;
145        }
146      }
147      return false;
148    } catch (Throwable t) {
149      return false;
150    }
151  }
152}