/* * Copyright 2016 ANI Technologies Pvt. Ltd. * * 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 com.olacabs.fabric.executor; import com.codahale.metrics.ConsoleReporter; import com.codahale.metrics.MetricFilter; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.SharedMetricRegistries; import com.codahale.metrics.jvm.FileDescriptorRatioGauge; import com.codahale.metrics.jvm.GarbageCollectorMetricSet; import com.codahale.metrics.jvm.MemoryUsageGaugeSet; import com.codahale.metrics.jvm.ThreadStatesGaugeSet; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.ObjectMapper; import com.github.sps.metrics.OpenTsdbReporter; import com.github.sps.metrics.opentsdb.OpenTsdb; import com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.olacabs.fabric.common.util.MesosDnsResolver; import com.olacabs.fabric.compute.builder.ComponentUrlResolver; import com.olacabs.fabric.compute.builder.Linker; import com.olacabs.fabric.compute.builder.impl.DownloadingLoader; import com.olacabs.fabric.compute.pipeline.ComputationPipeline; import com.olacabs.fabric.executor.impl.FileMetadataSource; import com.olacabs.fabric.executor.impl.HttpMetadataSource; import com.olacabs.fabric.model.common.ComponentSource; import com.olacabs.fabric.model.computation.ComputationSpec; import io.undertow.Undertow; import io.undertow.util.Headers; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import org.apache.http.HttpStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.Inet4Address; import java.util.Collection; import java.util.concurrent.TimeUnit; /** * TODO doc. */ public class Executor { private static final Logger LOGGER = LoggerFactory.getLogger(Executor.class); static { java.security.Security.setProperty("networkaddress.cache.ttl", "60"); } private final MesosDnsResolver dnsResolver; private final ObjectMapper objectMapper; private Undertow healthcheckMonitor; public Executor() { objectMapper = new ObjectMapper(); objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); dnsResolver = new MesosDnsResolver(); } public static void main(String[] args) throws Exception { try { new Executor().run(args); } catch (Throwable t) { LOGGER.error("Executor will exit...", t); } } public void startMonitor(final ComputationPipeline pipeline) { healthcheckMonitor = Undertow.builder().addHttpListener(8080, "0.0.0.0").setHandler(exchange -> { boolean result = pipeline.healthcheck(); exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "text/plain"); exchange.setStatusCode(result ? HttpStatus.SC_OK : HttpStatus.SC_INTERNAL_SERVER_ERROR); exchange.getResponseSender().send(result ? "alive" : "dead"); }) .build(); healthcheckMonitor.start(); } public void stopMonitor() { healthcheckMonitor.stop(); } public void run(String[] args) throws Exception { Options options = new Options(); options.addOption("m", "opentsdb-endpoint", true, "OpenTSDB endpoint host:port"); options.addOption("d", "disable-metrics", true, "Disable metric completely"); options.addOption("h", "help", false, "Print help"); options.addOption(Option.builder("s") .longOpt("spec") .desc("Computation spec URL") .hasArg() .build()); options.addOption(Option.builder("f") .longOpt("spec-file") .desc("Computation spec file [JSON]") .hasArg() .build()); String opentsdbHost = null; int opentsdbPort = 4242; CommandLineParser commandLineParser = new DefaultParser(); CommandLine commandLine = commandLineParser.parse(options, args); if (commandLine.hasOption("h")) { HelpFormatter formatter = new HelpFormatter(); formatter.printHelp("fabric-executor", options); return; } if (!commandLine.hasOption("spec") && !commandLine.hasOption("spec-file")) { System.err.println("No computation spec is present"); HelpFormatter formatter = new HelpFormatter(); formatter.printHelp("fabric-executor", options); return; } if (commandLine.hasOption("m")) { if (!commandLine.getOptionValue("m").equalsIgnoreCase("NIL")) { String[] tokens = commandLine.getOptionValue("m").split(":"); if (tokens.length > 2) { throw new Exception("Cannot parse opentsdb connection string"); } else { opentsdbHost = tokens[0].trim(); if (tokens.length == 2) { opentsdbPort = Integer.valueOf(tokens[1]); } } LOGGER.info("Setting opentsdb endpoint to {}:{}", opentsdbHost, opentsdbPort); } } if (Strings.isNullOrEmpty(opentsdbHost)) { LOGGER.warn("No metrics data available"); } MetricRegistry registry = SharedMetricRegistries.getOrCreate("metrics-registry"); MetadataSource metadataSource = metadataSource(commandLine); ComputationSpec spec = metadataSource.load(specPath(commandLine)); DownloadingLoader loader = new DownloadingLoader(); ImmutableSet.Builder<ComponentSource> componentSourceSetBuilder = ImmutableSet.builder(); spec.getSources().forEach(sourceMeta -> componentSourceSetBuilder.add(sourceMeta.getMeta().getSource())); spec.getProcessors() .forEach(processorMeta -> componentSourceSetBuilder.add(processorMeta.getMeta().getSource())); Collection<String> resolvedUrls = ComponentUrlResolver.urls(componentSourceSetBuilder.build()); LOGGER.info("Component Jar URLs: {}", resolvedUrls); loader.loadJars(resolvedUrls, Thread.currentThread().getContextClassLoader()); Linker linker = new Linker(loader, registry); LOGGER.info(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(spec)); ComputationPipeline pipeline = linker.build(spec); String host; if (null != System.getenv("HOST")) { host = System.getenv("HOST"); } else { host = Inet4Address.getLocalHost().getHostAddress(); } LOGGER.info("Setting container host to: " + host); boolean metricsDisabled = true; if (commandLine.hasOption("d")) { metricsDisabled = Boolean.valueOf(commandLine.getOptionValue("d")); } if (!metricsDisabled) { registry.register("gc", new GarbageCollectorMetricSet()); registry.register("memory", new MemoryUsageGaugeSet()); registry.register("threads", new ThreadStatesGaugeSet()); registry.register("fd", new FileDescriptorRatioGauge()); if (!Strings.isNullOrEmpty(opentsdbHost)) { long interval = (null != System.getenv("OPENTSDB_REPORTER_INTERVAL")) ? Long .valueOf(System.getenv("OPENTSDB_REPORTER_INTERVAL")) : 60L; OpenTsdbReporter.forRegistry(registry) .prefixedWith(spec.getName()) .convertRatesTo(TimeUnit.SECONDS) .filter(MetricFilter.ALL) .withTags(ImmutableMap.of("host", host, "topologyName", spec.getName())) .build(OpenTsdb.forService(String.format("http://%s:%d", opentsdbHost, opentsdbPort)).create()) .start(interval, TimeUnit.SECONDS); } else { LOGGER.warn("Using console reporter"); ConsoleReporter.forRegistry(registry) .convertDurationsTo(TimeUnit.MILLISECONDS) .convertRatesTo(TimeUnit.SECONDS) .filter(MetricFilter.ALL) .build() .start(60L, TimeUnit.SECONDS); } } else { LOGGER.warn("Metrics disabled..."); } try { pipeline.initialize(spec.getProperties()) .start(); } catch (Throwable t) { LOGGER.error("Couldn't start computation...", t); System.exit(-1); } startMonitor(pipeline); Runtime.getRuntime().addShutdownHook(new Thread(() -> { LOGGER.info("Got shutdown signal, shutting down the computation"); pipeline.stop(); stopMonitor(); })); } private MetadataSource metadataSource(CommandLine commandLine) { if (commandLine.hasOption("s")) { return new HttpMetadataSource(objectMapper, dnsResolver); } if (commandLine.hasOption("f")) { return new FileMetadataSource(objectMapper); } throw new IllegalArgumentException("No spec source defined. Use either -s or -f"); } private String specPath(CommandLine commandLine) { if (commandLine.hasOption("s")) { return commandLine.getOptionValue("s"); } if (commandLine.hasOption("f")) { return commandLine.getOptionValue("f"); } throw new IllegalArgumentException("No spec source defined. Use either -s or -f"); } }