/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 gobblin.cluster;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.UUID;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.lang.StringUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.helix.ControllerChangeListener;
import org.apache.helix.Criteria;
import org.apache.helix.HelixDataAccessor;
import org.apache.helix.HelixManager;
import org.apache.helix.HelixManagerFactory;
import org.apache.helix.HelixProperty;
import org.apache.helix.InstanceType;
import org.apache.helix.LiveInstanceChangeListener;
import org.apache.helix.NotificationContext;
import org.apache.helix.messaging.handling.HelixTaskResult;
import org.apache.helix.messaging.handling.MessageHandler;
import org.apache.helix.messaging.handling.MessageHandlerFactory;
import org.apache.helix.model.LiveInstance;
import org.apache.helix.model.Message;
import org.apache.helix.task.TaskDriver;
import org.apache.helix.task.TaskUtil;
import org.apache.helix.task.WorkflowConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.base.Optional;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.Service;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import com.typesafe.config.ConfigValueFactory;
import gobblin.annotation.Alpha;
import gobblin.cluster.event.ClusterManagerShutdownRequest;
import gobblin.configuration.ConfigurationKeys;
import gobblin.metrics.Tag;
import gobblin.runtime.api.MutableJobCatalog;
import gobblin.runtime.app.ApplicationException;
import gobblin.runtime.app.ApplicationLauncher;
import gobblin.runtime.app.ServiceBasedAppLauncher;
import gobblin.scheduler.SchedulerService;
import gobblin.util.ConfigUtils;
import gobblin.util.JvmUtils;
import gobblin.util.logs.Log4jConfigurationHelper;
import gobblin.util.reflection.GobblinConstructorUtils;
/**
* The central cluster manager for Gobblin Clusters.
*
* <p>
* This class runs the {@link GobblinHelixJobScheduler} for scheduling and running Gobblin jobs.
* This class serves as the Helix controller and it uses a {@link HelixManager} to work with Helix.
* </p>
*
* <p>
* This class will initiates a graceful shutdown of the cluster in the following conditions:
*
* <ul>
* <li>A shutdown request is received via a Helix message of subtype
* {@link HelixMessageSubTypes#APPLICATION_MASTER_SHUTDOWN}. Upon receiving such a message,
* it will call {@link #stop()} to initiate a graceful shutdown of the cluster</li>
* <li>The shutdown hook gets called. The shutdown hook will call {@link #stop()}, which will
* start a graceful shutdown of the cluster.</li>
* </ul>
* </p>
*
* @author Yinan Li
*/
@Alpha
public class GobblinClusterManager implements ApplicationLauncher {
private static final Logger LOGGER = LoggerFactory.getLogger(GobblinClusterManager.class);
private HelixManager helixManager;
private volatile boolean stopInProgress = false;
protected ServiceBasedAppLauncher applicationLauncher;
// An EventBus used for communications between services running in the ApplicationMaster
protected final EventBus eventBus = new EventBus(GobblinClusterManager.class.getSimpleName());
protected final Path appWorkDir;
protected final FileSystem fs;
protected final String applicationId;
// thread used to keep process up for an idle controller
private Thread idleProcessThread;
// set to true to stop the idle process thread
private volatile boolean stopIdleProcessThread = false;
// flag to keep track of leader and avoid processing duplicate leadership change notifications
private boolean isLeader = false;
private final boolean isStandaloneMode;
private MutableJobCatalog jobCatalog;
private final String clusterName;
private final Config config;
public GobblinClusterManager(String clusterName, String applicationId, Config config,
Optional<Path> appWorkDirOptional) throws Exception {
this.clusterName = clusterName;
this.config = config;
this.isStandaloneMode = ConfigUtils.getBoolean(config, GobblinClusterConfigurationKeys.STANDALONE_CLUSTER_MODE_KEY,
GobblinClusterConfigurationKeys.DEFAULT_STANDALONE_CLUSTER_MODE);
this.applicationId = applicationId;
initializeHelixManager();
this.fs = buildFileSystem(config);
this.appWorkDir = appWorkDirOptional.isPresent() ? appWorkDirOptional.get()
: GobblinClusterUtils.getAppWorkDirPath(this.fs, clusterName, applicationId);
initializeAppLauncherAndServices();
}
/**
* Create the service based application launcher and other associated services
* @throws Exception
*/
private void initializeAppLauncherAndServices() throws Exception {
// Done to preserve backwards compatibility with the previously hard-coded timeout of 5 minutes
Properties properties = ConfigUtils.configToProperties(this.config);
if (!properties.contains(ServiceBasedAppLauncher.APP_STOP_TIME_SECONDS)) {
properties.setProperty(ServiceBasedAppLauncher.APP_STOP_TIME_SECONDS, Long.toString(300));
}
this.applicationLauncher = new ServiceBasedAppLauncher(properties, this.clusterName);
// create a job catalog for keeping track of received jobs if a job config path is specified
if (this.config.hasPath(GobblinClusterConfigurationKeys.GOBBLIN_CLUSTER_PREFIX
+ ConfigurationKeys.JOB_CONFIG_FILE_GENERAL_PATH_KEY)) {
String jobCatalogClassName = ConfigUtils.getString(config, GobblinClusterConfigurationKeys.JOB_CATALOG_KEY,
GobblinClusterConfigurationKeys.DEFAULT_JOB_CATALOG);
this.jobCatalog =
(MutableJobCatalog) GobblinConstructorUtils.invokeFirstConstructor(Class.forName(jobCatalogClassName),
ImmutableList.<Object>of(config.getConfig(
StringUtils.removeEnd(GobblinClusterConfigurationKeys.GOBBLIN_CLUSTER_PREFIX, "."))));
} else {
this.jobCatalog = null;
}
SchedulerService schedulerService = new SchedulerService(properties);
this.applicationLauncher.addService(schedulerService);
this.applicationLauncher.addService(
buildGobblinHelixJobScheduler(config, this.appWorkDir, getMetadataTags(clusterName, applicationId),
schedulerService));
this.applicationLauncher.addService(buildJobConfigurationManager(config));
}
/**
* Start any services required by the application launcher then start the application launcher
*/
private void startAppLauncherAndServices() {
// other services such as the job configuration manager have a dependency on the job catalog, so it has be be
// started first
if (this.jobCatalog instanceof Service) {
((Service) this.jobCatalog).startAsync().awaitRunning();
}
this.applicationLauncher.start();
}
/**
* Stop the application launcher then any services that were started outside of the application launcher
*/
private void stopAppLauncherAndServices() {
try {
this.applicationLauncher.stop();
} catch (ApplicationException ae) {
LOGGER.error("Error while stopping Gobblin Cluster application launcher", ae);
}
if (this.jobCatalog instanceof Service) {
((Service) this.jobCatalog).stopAsync().awaitTerminated();
}
}
/**
* Handle leadership change.
* The applicationLauncher is only started on the leader.
* The leader cleans up existing jobs before starting the applicationLauncher.
* @param changeContext notification context
*/
@VisibleForTesting
void handleLeadershipChange(NotificationContext changeContext) {
if (this.helixManager.isLeader()) {
// can get multiple notifications on a leadership change, so only start the application launcher the first time
// the notification is received
LOGGER.info("Leader notification for {} isLeader {} HM.isLeader {}", this.helixManager.getInstanceName(),
isLeader, this.helixManager.isLeader());
if (!isLeader) {
LOGGER.info("New Helix Controller leader {}", this.helixManager.getInstanceName());
// Clean up existing jobs
TaskDriver taskDriver = new TaskDriver(this.helixManager);
GobblinHelixTaskDriver gobblinHelixTaskDriver = new GobblinHelixTaskDriver(this.helixManager);
Map<String, WorkflowConfig> workflows = taskDriver.getWorkflows();
for (Map.Entry<String, WorkflowConfig> entry : workflows.entrySet()) {
String queueName = entry.getKey();
WorkflowConfig workflowConfig = entry.getValue();
for (String namespacedJobName : workflowConfig.getJobDag().getAllNodes()) {
String jobName = TaskUtil.getDenamespacedJobName(queueName, namespacedJobName);
LOGGER.info("job {} found for queue {} ", jobName, queueName);
// #HELIX-0.6.7-WORKAROUND
// working around 0.6.7 delete job issue for queues with IN_PROGRESS state
gobblinHelixTaskDriver.deleteJob(queueName, jobName);
LOGGER.info("deleted job {} from queue {}", jobName, queueName);
}
}
startAppLauncherAndServices();
isLeader = true;
}
} else {
// stop and reinitialize services since they are not restartable
// this prepares them to start when this cluster manager becomes a leader
if (isLeader) {
isLeader = false;
stopAppLauncherAndServices();
try {
initializeAppLauncherAndServices();
} catch (Exception e) {
throw new RuntimeException("Exception reinitializing app launcher services ", e);
}
}
}
}
/**
* Start the Gobblin Cluster Manager.
*/
@Override
public synchronized void start() {
LOGGER.info("Starting the Gobblin Cluster Manager");
this.eventBus.register(this);
connectHelixManager();
if (this.isStandaloneMode) {
// standalone mode starts non-daemon threads later, so need to have this thread to keep process up
this.idleProcessThread = new Thread(new Runnable() {
@Override
public void run() {
while (!GobblinClusterManager.this.stopInProgress && !GobblinClusterManager.this.stopIdleProcessThread) {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
});
this.idleProcessThread.start();
// Need this in case a kill is issued to the process so that the idle thread does not keep the process up
// since GobblinClusterManager.stop() is not called this case.
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
GobblinClusterManager.this.stopIdleProcessThread = true;
}
});
} else {
startAppLauncherAndServices();
}
}
/**
* Stop the Gobblin Cluster Manager.
*/
@Override
public synchronized void stop() {
if (this.stopInProgress) {
return;
}
this.stopInProgress = true;
LOGGER.info("Stopping the Gobblin Cluster Manager");
if (this.idleProcessThread != null) {
try {
this.idleProcessThread.join();
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}
}
// Send a shutdown request to all GobblinTaskRunners unless running in standalone mode.
// In standalone mode a failing manager should not stop the whole cluster.
if (!this.isStandaloneMode) {
sendShutdownRequest();
}
stopAppLauncherAndServices();
disconnectHelixManager();
}
/**
* Get additional {@link Tag}s required for any type of reporting.
*/
private List<? extends Tag<?>> getMetadataTags(String applicationName, String applicationId) {
return Tag.fromMap(
new ImmutableMap.Builder<String, Object>().put(GobblinClusterMetricTagNames.APPLICATION_NAME, applicationName)
.put(GobblinClusterMetricTagNames.APPLICATION_ID, applicationId).build());
}
/**
* Build the {@link HelixManager} for the Application Master.
*/
private HelixManager buildHelixManager(Config config, String zkConnectionString) {
String helixInstanceName = ConfigUtils.getString(config, GobblinClusterConfigurationKeys.HELIX_INSTANCE_NAME_KEY,
GobblinClusterManager.class.getSimpleName());
return HelixManagerFactory.getZKHelixManager(
config.getString(GobblinClusterConfigurationKeys.HELIX_CLUSTER_NAME_KEY), helixInstanceName,
InstanceType.CONTROLLER, zkConnectionString);
}
/**
* Build the {@link FileSystem} for the Application Master.
*/
private FileSystem buildFileSystem(Config config) throws IOException {
return config.hasPath(ConfigurationKeys.FS_URI_KEY) ? FileSystem
.get(URI.create(config.getString(ConfigurationKeys.FS_URI_KEY)), new Configuration())
: FileSystem.get(new Configuration());
}
/**
* Build the {@link GobblinHelixJobScheduler} for the Application Master.
*/
private GobblinHelixJobScheduler buildGobblinHelixJobScheduler(Config config, Path appWorkDir,
List<? extends Tag<?>> metadataTags, SchedulerService schedulerService) throws Exception {
Properties properties = ConfigUtils.configToProperties(config);
return new GobblinHelixJobScheduler(properties, this.helixManager, this.eventBus, appWorkDir, metadataTags,
schedulerService, this.jobCatalog);
}
/**
* Build the {@link JobConfigurationManager} for the Application Master.
*/
private JobConfigurationManager buildJobConfigurationManager(Config config) {
return create(config);
}
private JobConfigurationManager create(Config config) {
try {
if (config.hasPath(GobblinClusterConfigurationKeys.JOB_CONFIGURATION_MANAGER_KEY)) {
return (JobConfigurationManager) GobblinConstructorUtils.invokeFirstConstructor(Class.forName(
config.getString(GobblinClusterConfigurationKeys.JOB_CONFIGURATION_MANAGER_KEY)),
ImmutableList.<Object>of(this.eventBus, config, this.jobCatalog),
ImmutableList.<Object>of(this.eventBus, config));
} else {
return new JobConfigurationManager(this.eventBus, config);
}
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException |
ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
@SuppressWarnings("unused")
@Subscribe
public void handleApplicationMasterShutdownRequest(ClusterManagerShutdownRequest shutdownRequest) {
stop();
}
@VisibleForTesting
EventBus getEventBus() {
return this.eventBus;
}
@VisibleForTesting
void connectHelixManager() {
try {
this.isLeader = false;
this.helixManager.connect();
this.helixManager.addLiveInstanceChangeListener(new GobblinLiveInstanceChangeListener());
this.helixManager.getMessagingService()
.registerMessageHandlerFactory(GobblinHelixConstants.SHUTDOWN_MESSAGE_TYPE, new ControllerShutdownMessageHandlerFactory());
this.helixManager.getMessagingService()
.registerMessageHandlerFactory(Message.MessageType.USER_DEFINE_MSG.toString(),
getUserDefinedMessageHandlerFactory());
// standalone mode listens for controller change
if (this.isStandaloneMode) {
// Subscribe to leadership changes
this.helixManager.addControllerListener(new ControllerChangeListener() {
@Override
public void onControllerChange(NotificationContext changeContext) {
handleLeadershipChange(changeContext);
}
});
}
} catch (Exception e) {
LOGGER.error("HelixManager failed to connect", e);
throw Throwables.propagate(e);
}
}
/**
* Creates and returns a {@link MessageHandlerFactory} for handling of Helix
* {@link org.apache.helix.model.Message.MessageType#USER_DEFINE_MSG}s.
*
* @returns a {@link MessageHandlerFactory}.
*/
protected MessageHandlerFactory getUserDefinedMessageHandlerFactory() {
return new ControllerUserDefinedMessageHandlerFactory();
}
@VisibleForTesting
void disconnectHelixManager() {
if (isHelixManagerConnected()) {
this.helixManager.disconnect();
}
}
@VisibleForTesting
boolean isHelixManagerConnected() {
return this.helixManager.isConnected();
}
@VisibleForTesting
void initializeHelixManager() {
String zkConnectionString = this.config.getString(GobblinClusterConfigurationKeys.ZK_CONNECTION_STRING_KEY);
LOGGER.info("Using ZooKeeper connection string: " + zkConnectionString);
// This will create and register a Helix controller in ZooKeeper
this.helixManager = buildHelixManager(this.config, zkConnectionString);
}
@VisibleForTesting
void sendShutdownRequest() {
Criteria criteria = new Criteria();
criteria.setInstanceName("%");
criteria.setResource("%");
criteria.setPartition("%");
criteria.setPartitionState("%");
criteria.setRecipientInstanceType(InstanceType.PARTICIPANT);
// #HELIX-0.6.7-WORKAROUND
// Add this back when messaging to instances is ported to 0.6 branch
//criteria.setDataSource(Criteria.DataSource.LIVEINSTANCES);
criteria.setSessionSpecific(true);
Message shutdownRequest = new Message(GobblinHelixConstants.SHUTDOWN_MESSAGE_TYPE,
HelixMessageSubTypes.WORK_UNIT_RUNNER_SHUTDOWN.toString().toLowerCase() + UUID.randomUUID().toString());
shutdownRequest.setMsgSubType(HelixMessageSubTypes.WORK_UNIT_RUNNER_SHUTDOWN.toString());
shutdownRequest.setMsgState(Message.MessageState.NEW);
// Wait for 5 minutes
final int timeout = 300000;
// #HELIX-0.6.7-WORKAROUND
// Temporarily bypass the default messaging service to allow upgrade to 0.6.7 which is missing support
// for messaging to instances
//int messagesSent = this.helixManager.getMessagingService().send(criteria, shutdownRequest,
// new NoopReplyHandler(), timeout);
GobblinHelixMessagingService messagingService = new GobblinHelixMessagingService(this.helixManager);
int messagesSent = messagingService.send(criteria, shutdownRequest,
new NoopReplyHandler(), timeout);
if (messagesSent == 0) {
LOGGER.error(String.format("Failed to send the %s message to the participants", shutdownRequest.getMsgSubType()));
}
}
@Override
public void close() throws IOException {
this.applicationLauncher.close();
}
/**
* A custom implementation of {@link LiveInstanceChangeListener}.
*/
private static class GobblinLiveInstanceChangeListener implements LiveInstanceChangeListener {
@Override
public void onLiveInstanceChange(List<LiveInstance> liveInstances, NotificationContext changeContext) {
for (LiveInstance liveInstance : liveInstances) {
LOGGER.info("Live Helix participant instance: " + liveInstance.getInstanceName());
}
}
}
/**
* A custom {@link MessageHandlerFactory} for {@link MessageHandler}s that handle messages of type
* "SHUTDOWN" for shutting down the controller.
*/
private class ControllerShutdownMessageHandlerFactory implements MessageHandlerFactory {
@Override
public MessageHandler createHandler(Message message, NotificationContext context) {
return new ControllerShutdownMessageHandler(message, context);
}
@Override
public String getMessageType() {
return GobblinHelixConstants.SHUTDOWN_MESSAGE_TYPE;
}
public List<String> getMessageTypes() {
return Collections.singletonList(getMessageType());
}
@Override
public void reset() {
}
/**
* A custom {@link MessageHandler} for handling messages of sub type
* {@link HelixMessageSubTypes#APPLICATION_MASTER_SHUTDOWN}.
*/
private class ControllerShutdownMessageHandler extends MessageHandler {
public ControllerShutdownMessageHandler(Message message, NotificationContext context) {
super(message, context);
}
@Override
public HelixTaskResult handleMessage() throws InterruptedException {
String messageSubType = this._message.getMsgSubType();
Preconditions.checkArgument(
messageSubType.equalsIgnoreCase(HelixMessageSubTypes.APPLICATION_MASTER_SHUTDOWN.toString()),
String.format("Unknown %s message subtype: %s", GobblinHelixConstants.SHUTDOWN_MESSAGE_TYPE, messageSubType));
HelixTaskResult result = new HelixTaskResult();
if (stopInProgress) {
result.setSuccess(true);
return result;
}
LOGGER.info("Handling message " + HelixMessageSubTypes.APPLICATION_MASTER_SHUTDOWN.toString());
ScheduledExecutorService shutdownMessageHandlingCompletionWatcher =
MoreExecutors.getExitingScheduledExecutorService(new ScheduledThreadPoolExecutor(1));
// Schedule the task for watching on the removal of the shutdown message, which indicates that
// the message has been successfully processed and it's safe to disconnect the HelixManager.
// This is a hacky way of watching for the completion of processing the shutdown message and
// should be replaced by a fix to https://issues.apache.org/jira/browse/HELIX-611.
shutdownMessageHandlingCompletionWatcher.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
HelixManager helixManager = _notificationContext.getManager();
HelixDataAccessor helixDataAccessor = helixManager.getHelixDataAccessor();
HelixProperty helixProperty = helixDataAccessor
.getProperty(_message.getKey(helixDataAccessor.keyBuilder(), helixManager.getInstanceName()));
// The absence of the shutdown message indicates it has been removed
if (helixProperty == null) {
eventBus.post(new ClusterManagerShutdownRequest());
}
}
}, 0, 1, TimeUnit.SECONDS);
result.setSuccess(true);
return result;
}
@Override
public void onError(Exception e, ErrorCode code, ErrorType type) {
LOGGER.error(
String.format("Failed to handle message with exception %s, error code %s, error type %s", e, code, type));
}
}
}
/**
* A custom {@link MessageHandlerFactory} for {@link ControllerUserDefinedMessageHandler}s that
* handle messages of type {@link org.apache.helix.model.Message.MessageType#USER_DEFINE_MSG}.
*/
private static class ControllerUserDefinedMessageHandlerFactory implements MessageHandlerFactory {
@Override
public MessageHandler createHandler(Message message, NotificationContext context) {
return new ControllerUserDefinedMessageHandler(message, context);
}
@Override
public String getMessageType() {
return Message.MessageType.USER_DEFINE_MSG.toString();
}
public List<String> getMessageTypes() {
return Collections.singletonList(getMessageType());
}
@Override
public void reset() {
}
/**
* A custom {@link MessageHandler} for handling user-defined messages to the controller.
*
* <p>
* Currently does not handle any user-defined messages. If this class is passed a custom message, it will simply
* print out a warning and return successfully. Sub-classes of {@link GobblinClusterManager} should override
* {@link #getUserDefinedMessageHandlerFactory}.
* </p>
*/
private static class ControllerUserDefinedMessageHandler extends MessageHandler {
public ControllerUserDefinedMessageHandler(Message message, NotificationContext context) {
super(message, context);
}
@Override
public HelixTaskResult handleMessage() throws InterruptedException {
LOGGER.warn(String
.format("No handling setup for %s message of subtype: %s", Message.MessageType.USER_DEFINE_MSG.toString(),
this._message.getMsgSubType()));
HelixTaskResult helixTaskResult = new HelixTaskResult();
helixTaskResult.setSuccess(true);
return helixTaskResult;
}
@Override
public void onError(Exception e, ErrorCode code, ErrorType type) {
LOGGER.error(
String.format("Failed to handle message with exception %s, error code %s, error type %s", e, code, type));
}
}
}
/**
* TODO for now the cluster id is hardcoded to 1 both here and in the {@link GobblinTaskRunner}. In the future, the
* cluster id should be created by the {@link GobblinClusterManager} and passed to each {@link GobblinTaskRunner} via
* Helix (at least that would be the easiest approach, there are certainly others ways to do it).
*/
private static String getApplicationId() {
return "1";
}
private static Options buildOptions() {
Options options = new Options();
options.addOption("a", GobblinClusterConfigurationKeys.APPLICATION_NAME_OPTION_NAME, true, "Gobblin application name");
options.addOption("s", GobblinClusterConfigurationKeys.STANDALONE_CLUSTER_MODE, true, "Standalone cluster mode");
options.addOption("i", GobblinClusterConfigurationKeys.HELIX_INSTANCE_NAME_OPTION_NAME, true, "Helix instance name");
return options;
}
private static void printUsage(Options options) {
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp(GobblinClusterManager.class.getSimpleName(), options);
}
public static void main(String[] args) throws Exception {
Options options = buildOptions();
try {
CommandLine cmd = new DefaultParser().parse(options, args);
if (!cmd.hasOption(GobblinClusterConfigurationKeys.APPLICATION_NAME_OPTION_NAME)) {
printUsage(options);
System.exit(1);
}
boolean isStandaloneClusterManager = false;
if (cmd.hasOption(GobblinClusterConfigurationKeys.STANDALONE_CLUSTER_MODE)) {
isStandaloneClusterManager = Boolean.parseBoolean(cmd.getOptionValue(GobblinClusterConfigurationKeys.STANDALONE_CLUSTER_MODE, "false"));
}
Log4jConfigurationHelper.updateLog4jConfiguration(GobblinClusterManager.class,
GobblinClusterConfigurationKeys.GOBBLIN_CLUSTER_LOG4J_CONFIGURATION_FILE,
GobblinClusterConfigurationKeys.GOBBLIN_CLUSTER_LOG4J_CONFIGURATION_FILE);
LOGGER.info(JvmUtils.getJvmInputArguments());
Config config = ConfigFactory.load();
if (cmd.hasOption(GobblinClusterConfigurationKeys.HELIX_INSTANCE_NAME_OPTION_NAME)) {
config = config.withValue(GobblinClusterConfigurationKeys.HELIX_INSTANCE_NAME_KEY,
ConfigValueFactory.fromAnyRef(cmd.getOptionValue(
GobblinClusterConfigurationKeys.HELIX_INSTANCE_NAME_OPTION_NAME)));
}
if (isStandaloneClusterManager) {
config = config.withValue(GobblinClusterConfigurationKeys.STANDALONE_CLUSTER_MODE_KEY,
ConfigValueFactory.fromAnyRef(true));
}
try (GobblinClusterManager gobblinClusterManager = new GobblinClusterManager(
cmd.getOptionValue(GobblinClusterConfigurationKeys.APPLICATION_NAME_OPTION_NAME), getApplicationId(),
config, Optional.<Path>absent())) {
// In AWS / Yarn mode, the cluster Launcher takes care of setting up Helix cluster
/// .. but for Standalone mode, we go via this main() method, so setup the cluster here
if (isStandaloneClusterManager) {
// Create Helix cluster and connect to it
String zkConnectionString = config.getString(GobblinClusterConfigurationKeys.ZK_CONNECTION_STRING_KEY);
String helixClusterName = config.getString(GobblinClusterConfigurationKeys.HELIX_CLUSTER_NAME_KEY);
HelixUtils.createGobblinHelixCluster(zkConnectionString, helixClusterName, false);
LOGGER.info("Created Helix cluster " + helixClusterName);
}
gobblinClusterManager.start();
}
} catch (ParseException pe) {
printUsage(options);
System.exit(1);
}
}
}