summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjørn Meland <bjormel@users.noreply.github.com>2017-10-05 11:59:57 +0200
committerGitHub <noreply@github.com>2017-10-05 11:59:57 +0200
commitcf86d459aa9d3d136fd7b4d5de91429165b9a8c3 (patch)
tree0878f31d717e79465523ec138d60796683543e54
parent7de6a28f74be7515a8fabe1a778b9c0728725b9e (diff)
parentc206adf7dfde86c32653937a43e0ef08de67ed29 (diff)
Merge pull request #3654 from vespa-engine/arnej/cpp-logforwarder-start-2
Arnej/cpp logforwarder start 2
-rw-r--r--CMakeLists.txt1
-rw-r--r--config-model/pom.xml2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/LogForwarder.java64
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminBuilderBase.java17
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminV2Builder.java3
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminV4Builder.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/ModelElement.java14
-rw-r--r--config-model/src/main/resources/schema/admin.rnc13
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/admin/AdminTestCase.java28
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/admin/DedicatedAdminV4Test.java54
-rw-r--r--config-model/src/test/schema-test-files/services.xml3
-rw-r--r--configdefinitions/src/vespa/CMakeLists.txt2
-rw-r--r--configdefinitions/src/vespa/logforwarder.def6
-rw-r--r--logforwarder/CMakeLists.txt10
-rw-r--r--logforwarder/OWNERS1
-rw-r--r--logforwarder/README5
-rw-r--r--logforwarder/src/apps/vespa-logforwarder-start/.gitignore3
-rw-r--r--logforwarder/src/apps/vespa-logforwarder-start/CMakeLists.txt12
-rw-r--r--logforwarder/src/apps/vespa-logforwarder-start/cf-handler.cpp91
-rw-r--r--logforwarder/src/apps/vespa-logforwarder-start/cf-handler.h20
-rw-r--r--logforwarder/src/apps/vespa-logforwarder-start/main.cpp38
-rw-r--r--logforwarder/src/apps/vespa-logforwarder-start/sig-catch.cpp41
-rw-r--r--logforwarder/src/apps/vespa-logforwarder-start/sig-catch.h8
23 files changed, 430 insertions, 8 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f7752d5915e..db6bbde423a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -71,6 +71,7 @@ add_subdirectory(jrt_test)
add_subdirectory(juniper)
add_subdirectory(logd)
add_subdirectory(logserver)
+add_subdirectory(logforwarder)
add_subdirectory(lowercasing_test)
add_subdirectory(memfilepersistence)
add_subdirectory(messagebus)
diff --git a/config-model/pom.xml b/config-model/pom.xml
index c1c08e6e702..4e79da3279d 100644
--- a/config-model/pom.xml
+++ b/config-model/pom.xml
@@ -279,7 +279,7 @@
<artifactId>filedistribution</artifactId>
<version>${project.version}</version>
</dependency>
- <dependency>
+ <dependency>
<groupId>com.yahoo.vespa</groupId>
<artifactId>searchsummary</artifactId>
<version>${project.version}</version>
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/LogForwarder.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/LogForwarder.java
new file mode 100644
index 00000000000..46f5807b350
--- /dev/null
+++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/LogForwarder.java
@@ -0,0 +1,64 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.model.admin;
+
+import com.yahoo.cloud.config.LogforwarderConfig;
+import com.yahoo.config.model.producer.AbstractConfigProducer;
+import com.yahoo.vespa.model.AbstractService;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class LogForwarder extends AbstractService implements LogforwarderConfig.Producer {
+
+ public static class Config {
+ public final String deploymentServer;
+ public final String clientName;
+
+ private Config(String ds, String cn) {
+ this.deploymentServer = ds;
+ this.clientName = cn;
+ }
+ public Config withDeploymentServer(String ds) {
+ return new Config(ds, clientName);
+ }
+ public Config withClientName(String cn) {
+ return new Config(deploymentServer, cn);
+ }
+ }
+
+ private final Config config;
+
+ /**
+ * Creates a new LogForwarder instance.
+ */
+ // TODO: Use proper types?
+ public LogForwarder(AbstractConfigProducer parent, int index, Config config) {
+ super(parent, "logforwarder." + index);
+ this.config = config;
+ setProp("clustertype", "hosts");
+ setProp("clustername", "admin");
+ }
+
+ public static Config cfg() {
+ return new Config(null, null);
+ }
+
+ /**
+ * LogForwarder does not need any ports.
+ *
+ * @return The number of ports reserved by the LogForwarder
+ */
+ public int getPortCount() { return 0; }
+
+ /**
+ * @return The command used to start LogForwarder
+ */
+ public String getStartupCommand() { return "exec $ROOT/bin/vespa-logforwarder-start -c " + getConfigId(); }
+
+ @Override
+ public void getConfig(LogforwarderConfig.Builder builder) {
+ builder.deploymentServer(config.deploymentServer);
+ builder.clientName(config.clientName);
+ }
+
+}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminBuilderBase.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminBuilderBase.java
index 908481aad63..3049112ac0a 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminBuilderBase.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminBuilderBase.java
@@ -111,4 +111,21 @@ public abstract class DomAdminBuilderBase extends VespaDomBuilder.DomConfigProdu
return minutes;
}
+ void addLogForwarders(ModelElement logForwardingElement, Admin admin) {
+ if (logForwardingElement == null) return;
+
+ int i = 0;
+ for (ModelElement e : logForwardingElement.getChildren("splunk")) {
+ LogForwarder.Config cfg = LogForwarder.cfg()
+ .withDeploymentServer(e.getStringAttribute("deployment-server"))
+ .withClientName(e.getStringAttribute("client-name"));
+ for (HostResource host : admin.getHostSystem().getHosts()) {
+ LogForwarder logForwarder = new LogForwarder(admin, i, cfg);
+ logForwarder.setHostResource(host);
+ logForwarder.initService();
+ i++;
+ }
+ }
+ }
+
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminV2Builder.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminV2Builder.java
index 180cf4eadec..dd1d4e36255 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminV2Builder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminV2Builder.java
@@ -49,6 +49,9 @@ public class DomAdminV2Builder extends DomAdminBuilderBase {
admin.addSlobroks(getSlobroks(admin, XML.getChild(adminE, "slobroks")));
if ( ! admin.multitenant())
admin.setClusterControllers(addConfiguredClusterControllers(admin, adminE));
+
+ ModelElement adminElement = new ModelElement(adminE);
+ addLogForwarders(adminElement.getChild("logforwarding"), admin);
}
private List<Configserver> parseConfigservers(Admin admin, Element adminE) {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminV4Builder.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminV4Builder.java
index 5e22ba3961f..cb8ec205395 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminV4Builder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminV4Builder.java
@@ -51,6 +51,8 @@ public class DomAdminV4Builder extends DomAdminBuilderBase {
assignSlobroks(requestedSlobroks.orElse(NodesSpecification.nonDedicated(3, version)), admin);
assignLogserver(requestedLogservers.orElse(NodesSpecification.nonDedicated(1, version)), admin);
+
+ addLogForwarders(adminElement.getChild("logforwarding"), admin);
}
private void assignSlobroks(NodesSpecification nodesSpecification, Admin admin) {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/ModelElement.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/ModelElement.java
index c5bc275a9e1..1fc70e343f3 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/ModelElement.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/ModelElement.java
@@ -11,7 +11,7 @@ import java.util.List;
import java.util.StringTokenizer;
/**
- * A w3c Element wrapper whith a better API.
+ * A w3c Element wrapper with a better API.
*
* Author unknown.
*/
@@ -46,6 +46,18 @@ public class ModelElement {
return null;
}
+ /**
+ * If not found, return empty list
+ */
+ public List<ModelElement> getChildren(String name) {
+ List<Element> e = XML.getChildren(xml, name);
+
+ List<ModelElement> list = new ArrayList<>();
+ e.forEach(element -> list.add(new ModelElement(element)));
+
+ return list;
+ }
+
public ModelElement getChildByPath(String path) {
StringTokenizer tokenizer = new StringTokenizer(path, ".");
ModelElement curElem = this;
diff --git a/config-model/src/main/resources/schema/admin.rnc b/config-model/src/main/resources/schema/admin.rnc
index 26705784a34..3ab02af3efd 100644
--- a/config-model/src/main/resources/schema/admin.rnc
+++ b/config-model/src/main/resources/schema/admin.rnc
@@ -13,7 +13,8 @@ AdminV2 =
AdminSlobroks? &
(LegacyAdminMonitoring | AdminMonitoring)? &
(LegacyMetricConsumers | Metrics)? &
- ClusterControllers?
+ ClusterControllers? &
+ LogForwarding?
}
AdminV3 =
@@ -32,7 +33,8 @@ AdminV4 =
AdminV4LogServers? &
GenericConfig* &
(LegacyAdminMonitoring | AdminMonitoring)? &
- (LegacyMetricConsumers | Metrics)?
+ (LegacyMetricConsumers | Metrics)? &
+ LogForwarding?
}
AdminV4Slobroks =
@@ -112,3 +114,10 @@ ClusterControllers = element cluster-controllers {
service.attlist
}+
}
+
+LogForwarding = element logforwarding {
+ element splunk {
+ attribute deployment-server { xsd:string } &
+ attribute client-name { xsd:string }
+ }
+}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/admin/AdminTestCase.java b/config-model/src/test/java/com/yahoo/vespa/model/admin/AdminTestCase.java
index 2dfef135425..b5375ccbce4 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/admin/AdminTestCase.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/admin/AdminTestCase.java
@@ -8,6 +8,7 @@ import com.yahoo.cloud.config.SentinelConfig;
import com.yahoo.config.model.ApplicationConfigProducerRoot;
import com.yahoo.config.model.deploy.DeployProperties;
import com.yahoo.config.model.deploy.DeployState;
+import com.yahoo.config.model.test.MockApplicationPackage;
import com.yahoo.config.model.test.TestDriver;
import com.yahoo.config.model.test.TestRoot;
import com.yahoo.config.provision.ApplicationId;
@@ -22,9 +23,12 @@ import com.yahoo.vespa.model.container.ContainerCluster;
import com.yahoo.vespa.model.container.component.Component;
import com.yahoo.vespa.model.container.component.StatisticsComponent;
import com.yahoo.vespa.model.test.utils.VespaModelCreatorWithFilePkg;
+import com.yahoo.vespa.model.test.utils.VespaModelCreatorWithMockPkg;
+import org.junit.Ignore;
import org.junit.Test;
import java.util.Set;
+import java.util.stream.IntStream;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertEquals;
@@ -246,7 +250,29 @@ public class AdminTestCase {
assertEquals(sc.values(0).operations(0).name(), StatisticsConfig.Values.Operations.Name.REGULAR);
assertEquals(sc.values(0).operations(0).arguments(0).key(), "limits");
assertEquals(sc.values(0).operations(0).arguments(0).value(), "25,50,100,500");
-
}
+ @Test
+ public void testLogForwarding() throws Exception {
+ String hosts = "<hosts>"
+ + " <host name=\"myhost0\">"
+ + " <alias>node0</alias>"
+ + " </host>"
+ + "</hosts>";
+
+ String services = "<services>" +
+ " <admin version='2.0'>" +
+ " <adminserver hostalias='node0' />" +
+ " <logforwarding>" +
+ " <splunk deployment-server='foo:123' client-name='foocli'/>" +
+ " </logforwarding>" +
+ " </admin>" +
+ "</services>";
+
+ VespaModel vespaModel = new VespaModelCreatorWithMockPkg(hosts, services).create();
+
+ Set<String> configIds = vespaModel.getConfigIds();
+ // 1 logforwarder on each host
+ assertTrue(configIds.toString(), configIds.contains("admin/logforwarder.0"));
+ }
}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/admin/DedicatedAdminV4Test.java b/config-model/src/test/java/com/yahoo/vespa/model/admin/DedicatedAdminV4Test.java
index 5b5094a9c43..e3f773a64c2 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/admin/DedicatedAdminV4Test.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/admin/DedicatedAdminV4Test.java
@@ -1,6 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model.admin;
+import com.yahoo.cloud.config.LogforwarderConfig;
import com.yahoo.cloud.config.SentinelConfig;
import com.yahoo.config.model.NullConfigModelRegistry;
import com.yahoo.config.application.api.ApplicationPackage;
@@ -16,13 +17,13 @@ import org.junit.Test;
import org.xml.sax.SAXException;
import java.io.IOException;
+import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
+import java.util.stream.IntStream;
import static com.yahoo.vespa.model.admin.monitoring.DefaultMetricsConsumer.VESPA_CONSUMER_ID;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.*;
/**
* @author lulf
@@ -141,6 +142,53 @@ public class DedicatedAdminV4Test {
"slobrok", "logd", "filedistributorservice", "qrserver");
}
+ @Test
+ public void testLogForwarding() throws IOException, SAXException {
+ String services = "<services>" +
+ " <admin version='4.0'>" +
+ " <slobroks><nodes count='2' dedicated='true'/></slobroks>" +
+ " <logservers><nodes count='1' dedicated='true'/></logservers>" +
+ " <logforwarding>" +
+ " <splunk deployment-server='foo:123' client-name='foocli'/>" +
+ " </logforwarding>" +
+ " </admin>" +
+ "</services>";
+
+ VespaModel model = createModel(hosts, services);
+ assertEquals(3, model.getHosts().size());
+
+ assertHostContainsServices(model, "hosts/myhost0",
+ "filedistributorservice", "logd", "logforwarder", "slobrok");
+ assertHostContainsServices(model, "hosts/myhost1",
+ "filedistributorservice", "logd", "logforwarder", "slobrok");
+ assertHostContainsServices(model, "hosts/myhost2",
+ "filedistributorservice", "logd", "logforwarder", "logserver");
+
+ Set<String> configIds = model.getConfigIds();
+ // 1 logforwarder on each host
+ IntStream.of(0, 1, 2).forEach(i -> assertTrue(configIds.toString(), configIds.contains("admin/logforwarder." + i)));
+
+ // First forwarder
+ {
+ LogforwarderConfig.Builder builder = new LogforwarderConfig.Builder();
+ model.getConfig(builder, "admin/logforwarder.0");
+ LogforwarderConfig config = new LogforwarderConfig(builder);
+
+ assertEquals("foo:123", config.deploymentServer());
+ assertEquals("foocli", config.clientName());
+ }
+
+ // Other host's forwarder
+ {
+ LogforwarderConfig.Builder builder = new LogforwarderConfig.Builder();
+ model.getConfig(builder, "admin/logforwarder.2");
+ LogforwarderConfig config = new LogforwarderConfig(builder);
+
+ assertEquals("foo:123", config.deploymentServer());
+ assertEquals("foocli", config.clientName());
+ }
+ }
+
private Set<String> serviceNames(VespaModel model, String hostname) {
SentinelConfig config = model.getConfig(SentinelConfig.class, hostname);
return config.service().stream().map(SentinelConfig.Service::name).collect(Collectors.toSet());
diff --git a/config-model/src/test/schema-test-files/services.xml b/config-model/src/test/schema-test-files/services.xml
index 322f4ed8356..4b6eb12208c 100644
--- a/config-model/src/test/schema-test-files/services.xml
+++ b/config-model/src/test/schema-test-files/services.xml
@@ -26,6 +26,9 @@
<metric-set id="my-set2" />
</consumer>
</metrics>
+ <logforwarding>
+ <splunk deployment-server="foo:8989" client-name="foobar"/>
+ </logforwarding>
</admin>
<config name="bar">
diff --git a/configdefinitions/src/vespa/CMakeLists.txt b/configdefinitions/src/vespa/CMakeLists.txt
index 4ed4dc06d41..9297383c53f 100644
--- a/configdefinitions/src/vespa/CMakeLists.txt
+++ b/configdefinitions/src/vespa/CMakeLists.txt
@@ -28,6 +28,8 @@ vespa_generate_config(configdefinitions lb-services.def)
install_config_definition(lb-services.def cloud.config.lb-services.def)
vespa_generate_config(configdefinitions load-type.def)
install_config_definition(load-type.def vespa.config.content.load-type.def)
+vespa_generate_config(configdefinitions logforwarder.def)
+install_config_definition(logforwarder.def cloud.config.logforwarder.def)
vespa_generate_config(configdefinitions messagetyperouteselectorpolicy.def)
install_config_definition(messagetyperouteselectorpolicy.def vespa.config.content.messagetyperouteselectorpolicy.def)
vespa_generate_config(configdefinitions model.def)
diff --git a/configdefinitions/src/vespa/logforwarder.def b/configdefinitions/src/vespa/logforwarder.def
new file mode 100644
index 00000000000..205e8ad3b8c
--- /dev/null
+++ b/configdefinitions/src/vespa/logforwarder.def
@@ -0,0 +1,6 @@
+namespace=cloud.config
+
+# only splunk type config for now
+
+deploymentServer string default=""
+clientName string default=""
diff --git a/logforwarder/CMakeLists.txt b/logforwarder/CMakeLists.txt
new file mode 100644
index 00000000000..bd1e480a074
--- /dev/null
+++ b/logforwarder/CMakeLists.txt
@@ -0,0 +1,10 @@
+# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_define_module(
+ DEPENDS
+ vespalog
+ vespalib
+ config_cloudconfig
+
+ APPS
+ src/apps/vespa-logforwarder-start
+)
diff --git a/logforwarder/OWNERS b/logforwarder/OWNERS
new file mode 100644
index 00000000000..67cd2820bb8
--- /dev/null
+++ b/logforwarder/OWNERS
@@ -0,0 +1 @@
+arnej27959
diff --git a/logforwarder/README b/logforwarder/README
new file mode 100644
index 00000000000..30f5992157b
--- /dev/null
+++ b/logforwarder/README
@@ -0,0 +1,5 @@
+Utility for log forwarding management
+
+currently this only supports "splunk", and
+just saves splunk config in a file using the
+appropriate format.
diff --git a/logforwarder/src/apps/vespa-logforwarder-start/.gitignore b/logforwarder/src/apps/vespa-logforwarder-start/.gitignore
new file mode 100644
index 00000000000..64d78991a4c
--- /dev/null
+++ b/logforwarder/src/apps/vespa-logforwarder-start/.gitignore
@@ -0,0 +1,3 @@
+vespa-logforwarder-start
+config-logforwarder.cpp
+config-logforwarder.h
diff --git a/logforwarder/src/apps/vespa-logforwarder-start/CMakeLists.txt b/logforwarder/src/apps/vespa-logforwarder-start/CMakeLists.txt
new file mode 100644
index 00000000000..f26edd8eee8
--- /dev/null
+++ b/logforwarder/src/apps/vespa-logforwarder-start/CMakeLists.txt
@@ -0,0 +1,12 @@
+# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(logforwarder-start_app
+ SOURCES
+ main.cpp
+ cf-handler.cpp
+ sig-catch.cpp
+ OUTPUT_NAME vespa-logforwarder-start
+ INSTALL bin
+ DEPENDS
+ config_cloudconfig
+ configdefinitions
+)
diff --git a/logforwarder/src/apps/vespa-logforwarder-start/cf-handler.cpp b/logforwarder/src/apps/vespa-logforwarder-start/cf-handler.cpp
new file mode 100644
index 00000000000..e34a83030e8
--- /dev/null
+++ b/logforwarder/src/apps/vespa-logforwarder-start/cf-handler.cpp
@@ -0,0 +1,91 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "cf-handler.h"
+#include <dirent.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <vespa/defaults.h>
+#include <vespa/config/common/configsystem.h>
+#include <vespa/config/common/exceptions.h>
+
+#include <vespa/log/log.h>
+LOG_SETUP(".cf-handler");
+
+CfHandler::CfHandler() : _subscriber() {}
+
+CfHandler::~CfHandler()
+{
+}
+
+void
+CfHandler::subscribe(const std::string & configId, uint64_t timeoutMS)
+{
+ _handle = _subscriber.subscribe<LogforwarderConfig>(configId, timeoutMS);
+}
+
+namespace {
+std::string
+cfFilePath() {
+ std::string path = vespa::Defaults::underVespaHome("var/db/vespa/splunk");
+ DIR *dp = opendir(path.c_str());
+ if (dp == NULL) {
+ if (errno != ENOENT || mkdir(path.c_str(), 0755) != 0) {
+ perror(path.c_str());
+ }
+ } else {
+ closedir(dp);
+ }
+ path += "/deploymentclient.conf";
+ return path;
+}
+}
+
+void
+CfHandler::doConfigure()
+{
+ std::unique_ptr<LogforwarderConfig> cfg(_handle->getConfig());
+ const LogforwarderConfig& config(*cfg);
+
+ std::string path = cfFilePath();
+ std::string tmpPath = path + ".new";
+ FILE *fp = fopen(tmpPath.c_str(), "w");
+ if (fp == NULL) return;
+
+ fprintf(fp, "[deployment-client]\n");
+ fprintf(fp, "clientName = %s\n", config.clientName.c_str());
+ fprintf(fp, "\n");
+ fprintf(fp, "[target-broker:deploymentServer]\n");
+ fprintf(fp, "targetUri = %s\n", config.deploymentServer.c_str());
+
+ fclose(fp);
+ rename(tmpPath.c_str(), path.c_str());
+}
+
+void
+CfHandler::check()
+{
+ if (_subscriber.nextConfig(0)) {
+ doConfigure();
+ }
+}
+
+constexpr uint64_t CONFIG_TIMEOUT_MS = 30 * 1000;
+
+void
+CfHandler::start(const char *configId)
+{
+ LOG(debug, "Reading configuration with id '%s'", configId);
+ try {
+ subscribe(configId, CONFIG_TIMEOUT_MS);
+ } catch (config::ConfigTimeoutException & ex) {
+ LOG(warning, "Timout getting config, please check your setup. Will exit and restart: %s", ex.getMessage().c_str());
+ exit(EXIT_FAILURE);
+ } catch (config::InvalidConfigException& ex) {
+ LOG(error, "Fatal: Invalid configuration, please check your setup: %s", ex.getMessage().c_str());
+ exit(EXIT_FAILURE);
+ } catch (config::ConfigRuntimeException& ex) {
+ LOG(error, "Fatal: Could not get config, please check your setup: %s", ex.getMessage().c_str());
+ exit(EXIT_FAILURE);
+ }
+}
diff --git a/logforwarder/src/apps/vespa-logforwarder-start/cf-handler.h b/logforwarder/src/apps/vespa-logforwarder-start/cf-handler.h
new file mode 100644
index 00000000000..99f0a6cd6d5
--- /dev/null
+++ b/logforwarder/src/apps/vespa-logforwarder-start/cf-handler.h
@@ -0,0 +1,20 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include <vespa/config/config.h>
+#include <vespa/config-logforwarder.h>
+
+using cloud::config::LogforwarderConfig;
+
+class CfHandler {
+private:
+ config::ConfigSubscriber _subscriber;
+ config::ConfigHandle<LogforwarderConfig>::UP _handle;
+ void subscribe(const std::string & configId, uint64_t timeoutMS);
+ void doConfigure();
+public:
+ CfHandler();
+ virtual ~CfHandler();
+ void start(const char *configId);
+ void check();
+};
diff --git a/logforwarder/src/apps/vespa-logforwarder-start/main.cpp b/logforwarder/src/apps/vespa-logforwarder-start/main.cpp
new file mode 100644
index 00000000000..e06d3dd6d8d
--- /dev/null
+++ b/logforwarder/src/apps/vespa-logforwarder-start/main.cpp
@@ -0,0 +1,38 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <csignal>
+#include <unistd.h>
+
+#include <vespa/log/log.h>
+LOG_SETUP("vespa-logforwarder-start");
+
+#include "cf-handler.h"
+#include "sig-catch.h"
+
+class Wrapper {
+ const char *_configId;
+public:
+ Wrapper(const char *cfid) : _configId(cfid) {}
+ void run() {
+ SigCatch catcher;
+ CfHandler handler;
+ handler.start(_configId);
+ while (! catcher.receivedStopSignal()) {
+ handler.check();
+ usleep(12500); // Avoid busy looping;
+ }
+ }
+};
+
+int
+main(int argc, char** argv)
+{
+ int c = getopt(argc, argv, "c:");
+ if (c != 'c') {
+ LOG(error, "Usage: %s -c <config-id>", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ Wrapper wrapper(optarg);
+ wrapper.run();
+ return 0;
+}
diff --git a/logforwarder/src/apps/vespa-logforwarder-start/sig-catch.cpp b/logforwarder/src/apps/vespa-logforwarder-start/sig-catch.cpp
new file mode 100644
index 00000000000..b10275d515e
--- /dev/null
+++ b/logforwarder/src/apps/vespa-logforwarder-start/sig-catch.cpp
@@ -0,0 +1,41 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "sig-catch.h"
+#include <csignal>
+#include <unistd.h>
+#include <string.h>
+
+static int sigPermanent(int sig, void(*handler)(int));
+static void setStopFlag(int sig);
+sig_atomic_t stop = 0;
+
+SigCatch::SigCatch()
+{
+ sigPermanent(SIGPIPE, SIG_IGN);
+ sigPermanent(SIGTERM, setStopFlag);
+ sigPermanent(SIGINT, setStopFlag);
+}
+
+bool
+SigCatch::receivedStopSignal() {
+ return stop != 0;
+}
+
+static void
+setStopFlag(int sig)
+{
+ (void)sig;
+ stop = 1;
+}
+
+static int
+sigPermanent(int sig, void(*handler)(int))
+{
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0; // no SA_RESTART!
+ sa.sa_handler = handler;
+ return sigaction(sig, &sa, nullptr);
+}
diff --git a/logforwarder/src/apps/vespa-logforwarder-start/sig-catch.h b/logforwarder/src/apps/vespa-logforwarder-start/sig-catch.h
new file mode 100644
index 00000000000..905f37103ec
--- /dev/null
+++ b/logforwarder/src/apps/vespa-logforwarder-start/sig-catch.h
@@ -0,0 +1,8 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+class SigCatch
+{
+public:
+ SigCatch();
+ bool receivedStopSignal();
+};