azkaban-aplcache

Refactored Solo server launch. Also added a unit test for Solo

5/5/2017 4:27:56 PM

Details

diff --git a/azkaban-common/src/main/java/azkaban/database/AzkabanDatabaseSetup.java b/azkaban-common/src/main/java/azkaban/database/AzkabanDatabaseSetup.java
index eb59246..d24422f 100644
--- a/azkaban-common/src/main/java/azkaban/database/AzkabanDatabaseSetup.java
+++ b/azkaban-common/src/main/java/azkaban/database/AzkabanDatabaseSetup.java
@@ -166,8 +166,7 @@ public class AzkabanDatabaseSetup {
     File dbPropsFile = new File(this.scriptPath, "database.properties");
 
     if (!dbPropsFile.exists()) {
-      throw new IOException("Cannot find 'database.properties' file in "
-          + dbPropsFile.getPath());
+      throw new IOException("Cannot find 'database.properties' file in " + dbPropsFile.getAbsolutePath());
     }
 
     return new Props(null, dbPropsFile);
diff --git a/azkaban-exec-server/src/main/java/azkaban/execapp/AzkabanExecutorServer.java b/azkaban-exec-server/src/main/java/azkaban/execapp/AzkabanExecutorServer.java
index a669d25..2ce08b5 100644
--- a/azkaban-exec-server/src/main/java/azkaban/execapp/AzkabanExecutorServer.java
+++ b/azkaban-exec-server/src/main/java/azkaban/execapp/AzkabanExecutorServer.java
@@ -42,6 +42,7 @@ import azkaban.utils.Utils;
 import com.google.common.base.Throwables;
 import com.google.inject.Guice;
 import com.google.inject.Inject;
+import com.google.inject.Injector;
 import java.io.BufferedWriter;
 import java.io.File;
 import java.io.FileWriter;
@@ -339,28 +340,16 @@ public class AzkabanExecutorServer {
       return;
     }
 
-
     /* Initialize Guice Injector */
-    SERVICE_PROVIDER.setInjector(Guice.createInjector(
-        new AzkabanCommonModule(props),
-        new AzkabanExecServerModule()
-    ));
+    final Injector injector = Guice.createInjector(new AzkabanCommonModule(props), new AzkabanExecServerModule());
+    SERVICE_PROVIDER.setInjector(injector);
 
-    launch(props);
+    launch(injector.getInstance(AzkabanExecutorServer.class));
   }
 
-  public static void launch(Props azkabanSettings) throws Exception {
-    // Setup time zone
-    if (azkabanSettings.containsKey(DEFAULT_TIMEZONE_ID)) {
-      String timezone = azkabanSettings.getString(DEFAULT_TIMEZONE_ID);
-      System.setProperty("user.timezone", timezone);
-      TimeZone.setDefault(TimeZone.getTimeZone(timezone));
-      DateTimeZone.setDefault(DateTimeZone.forID(timezone));
-
-      logger.info("Setting timezone to " + timezone);
-    }
-
-    app = SERVICE_PROVIDER.getInstance(AzkabanExecutorServer.class);
+  public static void launch(AzkabanExecutorServer azkabanExecutorServer) throws Exception {
+    setupTimeZone(azkabanExecutorServer.getAzkabanProps());
+    app = azkabanExecutorServer;
 
     Runtime.getRuntime().addShutdownHook(new Thread() {
 
@@ -413,6 +402,17 @@ public class AzkabanExecutorServer {
     });
   }
 
+  private static void setupTimeZone(Props azkabanSettings) {
+    if (azkabanSettings.containsKey(DEFAULT_TIMEZONE_ID)) {
+      String timezone = azkabanSettings.getString(DEFAULT_TIMEZONE_ID);
+      System.setProperty("user.timezone", timezone);
+      TimeZone.setDefault(TimeZone.getTimeZone(timezone));
+      DateTimeZone.setDefault(DateTimeZone.forID(timezone));
+
+      logger.info("Setting timezone to " + timezone);
+    }
+  }
+
   public FlowRunnerManager getFlowRunnerManager() {
     return runnerManager;
   }
diff --git a/azkaban-solo-server/src/main/java/azkaban/soloserver/AzkabanSingleServer.java b/azkaban-solo-server/src/main/java/azkaban/soloserver/AzkabanSingleServer.java
index 6d0b6ca..7a0c306 100644
--- a/azkaban-solo-server/src/main/java/azkaban/soloserver/AzkabanSingleServer.java
+++ b/azkaban-solo-server/src/main/java/azkaban/soloserver/AzkabanSingleServer.java
@@ -20,6 +20,8 @@ import azkaban.AzkabanCommonModule;
 import azkaban.execapp.AzkabanExecServerModule;
 import azkaban.webapp.AzkabanWebServerModule;
 import com.google.inject.Guice;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
 import org.apache.log4j.Logger;
 
 import azkaban.database.AzkabanDatabaseSetup;
@@ -33,15 +35,32 @@ import static azkaban.ServiceProvider.*;
 
 
 public class AzkabanSingleServer {
-  private static final Logger logger = Logger.getLogger(AzkabanWebServer.class);
+  private static final Logger log = Logger.getLogger(AzkabanWebServer.class);
+
+  private final AzkabanWebServer webServer;
+  private final AzkabanExecutorServer executor;
+
+  @Inject
+  public AzkabanSingleServer(AzkabanWebServer webServer, AzkabanExecutorServer executor) {
+    this.webServer = webServer;
+    this.executor = executor;
+  }
+
+  private void launch() throws Exception {
+    AzkabanWebServer.launch(webServer);
+    log.info("Azkaban Web Server started...");
+
+    AzkabanExecutorServer.launch(executor);
+    log.info("Azkaban Exec Server started...");
+  }
 
   public static void main(String[] args) throws Exception {
-    logger.info("Starting Azkaban Server");
+    log.info("Starting Azkaban Server");
 
     Props props = AzkabanServer.loadProps(args);
     if (props == null) {
-      logger.error("Properties not found. Need it to connect to the db.");
-      logger.error("Exiting...");
+      log.error("Properties not found. Need it to connect to the db.");
+      log.error("Exiting...");
       return;
     }
 
@@ -52,16 +71,14 @@ public class AzkabanSingleServer {
     }
 
     /* Initialize Guice Injector */
-    SERVICE_PROVIDER.setInjector(Guice.createInjector(
+    final Injector injector = Guice.createInjector(
         new AzkabanCommonModule(props),
         new AzkabanWebServerModule(),
         new AzkabanExecServerModule()
-    ));
-
-    AzkabanWebServer.launch(props);
-    logger.info("Azkaban Web Server started...");
+    );
+    SERVICE_PROVIDER.setInjector(injector);
 
-    AzkabanExecutorServer.launch(props);
-    logger.info("Azkaban Exec Server started...");
+    /* Launch server */
+    injector.getInstance(AzkabanSingleServer.class).launch();
   }
 }
diff --git a/azkaban-solo-server/src/test/java/azkaban/soloserver/AzkabanSingleServerTest.java b/azkaban-solo-server/src/test/java/azkaban/soloserver/AzkabanSingleServerTest.java
new file mode 100644
index 0000000..2cd1035
--- /dev/null
+++ b/azkaban-solo-server/src/test/java/azkaban/soloserver/AzkabanSingleServerTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2017 LinkedIn Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ *
+ */
+
+package azkaban.soloserver;
+
+import azkaban.AzkabanCommonModule;
+import azkaban.database.AzkabanDatabaseSetup;
+import azkaban.database.AzkabanDatabaseUpdater;
+import azkaban.execapp.AzkabanExecServerModule;
+import azkaban.utils.Props;
+import azkaban.webapp.AzkabanWebServerModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.apache.log4j.Logger;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.Test;
+
+import static azkaban.ServiceProvider.*;
+import static azkaban.executor.ExecutorManager.*;
+import static java.util.Objects.*;
+import static org.apache.commons.io.FileUtils.*;
+import static org.junit.Assert.*;
+
+
+public class AzkabanSingleServerTest {
+  private static final Logger log = Logger.getLogger(AzkabanSingleServerTest.class);
+  public static final String AZKABAN_DB_SQL_PATH = "azkaban-db/src/main/sql";
+
+  private static final Props props = new Props();
+
+  private static String getConfPath() {
+    final URL resource = AzkabanSingleServerTest.class.getClassLoader().getResource("conf");
+    return requireNonNull(resource).getPath();
+  }
+
+  private static String getSqlScriptsDir() throws IOException {
+    // Dummy because any resource file works.
+    Path resources = Paths.get(getConfPath()).getParent();
+    Path azkabanRoot = resources.getParent().getParent().getParent().getParent();
+
+    File sqlScriptDir = Paths.get(azkabanRoot.toString(), AZKABAN_DB_SQL_PATH).toFile();
+    return sqlScriptDir.getCanonicalPath();
+  }
+
+  @Before
+  public void setUp() throws Exception {
+    tearDown();
+
+    final String confPath = getConfPath();
+
+    props.put("database.type", "h2");
+    props.put("h2.path", "./h2");
+
+    props.put(AZKABAN_USE_MULTIPLE_EXECUTORS, "false");
+    props.put("server.port", "0");
+    props.put("jetty.port", "0");
+    props.put("server.useSSL", "true");
+    props.put("jetty.use.ssl", "false");
+    props.put("user.manager.xml.file", new File(confPath, "azkaban-users.xml").getPath());
+    props.put("executor.port", "12321");
+
+    String sqlScriptsDir = getSqlScriptsDir();
+    assertTrue(new File(sqlScriptsDir).isDirectory());
+    props.put(AzkabanDatabaseSetup.DATABASE_SQL_SCRIPT_DIR, sqlScriptsDir);
+    AzkabanDatabaseUpdater.runDatabaseUpdater(props, sqlScriptsDir, true);
+  }
+
+  @AfterClass
+  public static void tearDown() throws Exception {
+    deleteQuietly(new File("h2.mv.db"));
+    deleteQuietly(new File("h2.trace.db"));
+    deleteQuietly(new File("executor.port"));
+    deleteQuietly(new File("executions"));
+    deleteQuietly(new File("projects"));
+  }
+
+  @Test
+  public void testInjection() throws Exception {
+    SERVICE_PROVIDER.unsetInjector();
+    /* Initialize Guice Injector */
+    final Injector injector = Guice.createInjector(
+        new AzkabanCommonModule(props),
+        new AzkabanWebServerModule(),
+        new AzkabanExecServerModule()
+    );
+    SERVICE_PROVIDER.setInjector(injector);
+
+    /* Launch server */
+    assertNotNull(injector.getInstance(AzkabanSingleServer.class));
+
+    SERVICE_PROVIDER.unsetInjector();
+  }
+}
diff --git a/azkaban-solo-server/src/test/resources/conf/azkaban-users.xml b/azkaban-solo-server/src/test/resources/conf/azkaban-users.xml
new file mode 100644
index 0000000..a13035d
--- /dev/null
+++ b/azkaban-solo-server/src/test/resources/conf/azkaban-users.xml
@@ -0,0 +1,7 @@
+<azkaban-users>
+	<user username="azkaban" password="azkaban" roles="admin" groups="azkaban" />
+	<user username="metrics" password="metrics" roles="metrics"/>
+
+	<role name="admin" permissions="ADMIN" />
+	<role name="metrics" permissions="METRICS"/>
+</azkaban-users>
diff --git a/azkaban-solo-server/src/test/resources/log4j.properties b/azkaban-solo-server/src/test/resources/log4j.properties
new file mode 100644
index 0000000..705b927
--- /dev/null
+++ b/azkaban-solo-server/src/test/resources/log4j.properties
@@ -0,0 +1,13 @@
+log4j.rootLogger=INFO, Console
+log4j.logger.azkaban=INFO, server
+
+log4j.appender.server=org.apache.log4j.RollingFileAppender
+log4j.appender.server.layout=org.apache.log4j.PatternLayout
+log4j.appender.server.File=azkaban-webserver.log
+log4j.appender.server.layout.ConversionPattern=%d{yyyy/MM/dd HH:mm:ss.SSS Z} %p [%c{1}] [Azkaban] %m%n
+log4j.appender.server.MaxFileSize=102400MB
+log4j.appender.server.MaxBackupIndex=2
+
+log4j.appender.Console=org.apache.log4j.ConsoleAppender
+log4j.appender.Console.layout=org.apache.log4j.PatternLayout
+log4j.appender.Console.layout.ConversionPattern=%d{yyyy/MM/dd HH:mm:ss.SSS Z} %p [%c{1}] [Azkaban] %m%n
diff --git a/azkaban-web-server/src/main/java/azkaban/webapp/AzkabanWebServer.java b/azkaban-web-server/src/main/java/azkaban/webapp/AzkabanWebServer.java
index 012f08c..3e301e7 100644
--- a/azkaban-web-server/src/main/java/azkaban/webapp/AzkabanWebServer.java
+++ b/azkaban-web-server/src/main/java/azkaban/webapp/AzkabanWebServer.java
@@ -21,6 +21,7 @@ import com.codahale.metrics.MetricRegistry;
 
 import com.google.inject.Guice;
 import com.google.inject.Inject;
+import com.google.inject.Injector;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
@@ -683,20 +684,18 @@ public class AzkabanWebServer extends AzkabanServer {
     }
 
     /* Initialize Guice Injector */
-    SERVICE_PROVIDER.setInjector(Guice.createInjector(
-        new AzkabanCommonModule(props),
-        new AzkabanWebServerModule()
-    ));
+    final Injector injector = Guice.createInjector(new AzkabanCommonModule(props), new AzkabanWebServerModule());
+    SERVICE_PROVIDER.setInjector(injector);
 
-    launch(props);
+    launch(injector.getInstance(AzkabanWebServer.class));
   }
 
-  public static void launch(Props azkabanSettings) throws Exception {
+  public static void launch(AzkabanWebServer webServer) throws Exception {
     /* This creates the Web Server instance */
-    app = SERVICE_PROVIDER.getInstance(AzkabanWebServer.class);
+    app = webServer;
 
     // TODO refactor code into ServerProvider
-    prepareAndStartServer(azkabanSettings, app.server);
+    prepareAndStartServer(webServer.getServerProps(), app.server);
 
     Runtime.getRuntime().addShutdownHook(new Thread() {