diff options
66 files changed, 1132 insertions, 259 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index eba69f6ed02..379b6c5830f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,7 @@ include_directories(BEFORE ${CMAKE_BINARY_DIR}/configdefinitions/src) add_subdirectory(application-model) add_subdirectory(application-preprocessor) +add_subdirectory(athenz-identity-provider-service) add_subdirectory(chain) add_subdirectory(component) add_subdirectory(config-bundle) diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java index 959e08d18fa..d0ec98e3297 100644 --- a/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java +++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java @@ -56,7 +56,8 @@ public interface ModelContext { boolean useFdispatchByDefault(); boolean dispatchWithProtobuf(); boolean useAdaptiveDispatch(); - boolean useSeparateServiceTypeForLogserverContainer(); + // TODO: Remove when 7.33 is the oldest model in use + default boolean useSeparateServiceTypeForLogserverContainer() { return true; } boolean enableMetricsProxyContainer(); } diff --git a/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java b/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java index c5837ae73f3..fe6d683adf8 100644 --- a/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java +++ b/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java @@ -37,7 +37,6 @@ public class TestProperties implements ModelContext.Properties { private boolean useFdispatchByDefault = true; private boolean dispatchWithProtobuf = true; private boolean useAdaptiveDispatch = false; - private boolean useSeparateServiceTypeForLogserverContainer = false; private boolean enableMetricsProxyContainer = false; @@ -56,7 +55,6 @@ public class TestProperties implements ModelContext.Properties { @Override public boolean useDedicatedNodeForLogserver() { return useDedicatedNodeForLogserver; } @Override public boolean useFdispatchByDefault() { return useFdispatchByDefault; } @Override public boolean dispatchWithProtobuf() { return dispatchWithProtobuf; } - @Override public boolean useSeparateServiceTypeForLogserverContainer() { return useSeparateServiceTypeForLogserverContainer; } @Override public boolean enableMetricsProxyContainer() { return enableMetricsProxyContainer; } public TestProperties setApplicationId(ApplicationId applicationId) { @@ -89,11 +87,6 @@ public class TestProperties implements ModelContext.Properties { return this; } - public TestProperties setUseSeparateServiceTypeForLogserverContainer(boolean useSeparateServiceTypeForLogserverContainer) { - this.useSeparateServiceTypeForLogserverContainer = useSeparateServiceTypeForLogserverContainer; - return this; - } - public TestProperties setEnableMetricsProxyContainer(boolean enableMetricsProxyContainer) { this.enableMetricsProxyContainer = enableMetricsProxyContainer; return this; diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/LogserverContainer.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/LogserverContainer.java index 3d8773dcc2a..09bc29b181a 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/admin/LogserverContainer.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/LogserverContainer.java @@ -5,6 +5,7 @@ import com.yahoo.config.model.producer.AbstractConfigProducer; import com.yahoo.vespa.model.container.Container; import com.yahoo.vespa.model.container.ContainerCluster; import com.yahoo.config.model.api.container.ContainerServiceType; +import com.yahoo.vespa.model.container.component.AccessLogComponent; import com.yahoo.vespa.model.container.component.Handler; /** @@ -18,6 +19,7 @@ public class LogserverContainer extends Container { public LogserverContainer(AbstractConfigProducer parent, boolean useSeparateServiceTypeForLogserverContainer) { super(parent, "" + 0, 0); this.useSeparateServiceTypeForLogserverContainer = useSeparateServiceTypeForLogserverContainer; + addComponent(new AccessLogComponent(AccessLogComponent.AccessLogType.jsonAccessLog, ((LogserverContainerCluster) parent).getName(), true)); } @Override @@ -25,5 +27,4 @@ public class LogserverContainer extends Container { return useSeparateServiceTypeForLogserverContainer ? ContainerServiceType.LOGSERVER_CONTAINER : ContainerServiceType.CONTAINER; } - } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/LogserverContainerCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/LogserverContainerCluster.java index f8f515cb609..184b75ceb2e 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/admin/LogserverContainerCluster.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/LogserverContainerCluster.java @@ -16,6 +16,7 @@ public class LogserverContainerCluster extends ContainerCluster<LogserverContain addDefaultHandlersWithVip(); addLogHandler(); + addDefaultSearchAccessLog(); } @Override diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java index 7325105c8f0..6663dc0bbc0 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java @@ -96,7 +96,7 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { /** * Path to vip status file for container in Hosted Vespa. Only used if set, else use HOSTED_VESPA_STATUS_FILE */ - private static final String HOSTED_VESPA_STATUS_FILE_INSTALL_SETTING = "cloudconfig_server__tenant_vip_status_file"; + private static final String HOSTED_VESPA_STATUS_FILE_SETTING = "VESPA_LB_STATUS_FILE"; private static final String ENVIRONMENT_VARIABLES_ELEMENT = "environment-variables"; public enum Networking { disable, enable } @@ -268,7 +268,7 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { protected void addStatusHandlers(ApplicationContainerCluster cluster, boolean isHostedVespa) { if (isHostedVespa) { String name = "status.html"; - Optional<String> statusFile = Optional.ofNullable(System.getenv(HOSTED_VESPA_STATUS_FILE_INSTALL_SETTING)); + Optional<String> statusFile = Optional.ofNullable(System.getenv(HOSTED_VESPA_STATUS_FILE_SETTING)); cluster.addComponent( new FileStatusHandlerComponent(name + "-status-handler", statusFile.orElse(HOSTED_VESPA_STATUS_FILE), "http://*/" + name, "https://*/" + name)); diff --git a/config-model/src/main/javacc/SDParser.jj b/config-model/src/main/javacc/SDParser.jj index 291532ff958..de5146ec7d2 100644 --- a/config-model/src/main/javacc/SDParser.jj +++ b/config-model/src/main/javacc/SDParser.jj @@ -570,6 +570,7 @@ void bodycfg(SDDocumentType document) : { } */ void compression(SDDocumentType document, String name) : { + deployLogger.log(Level.WARNING, "'compression' for a document is deprecated and ignored"); CompressionConfig cfg = new CompressionConfig(CompressionType.LZ4); } { diff --git a/config-model/src/test/java/com/yahoo/config/model/provision/ModelProvisioningTest.java b/config-model/src/test/java/com/yahoo/config/model/provision/ModelProvisioningTest.java index f395ef680da..6c9b9fdc084 100644 --- a/config-model/src/test/java/com/yahoo/config/model/provision/ModelProvisioningTest.java +++ b/config-model/src/test/java/com/yahoo/config/model/provision/ModelProvisioningTest.java @@ -956,27 +956,7 @@ public class ModelProvisioningTest { " </container>" + "</services>"; boolean useDedicatedNodeForLogserver = false; - boolean useSeparateServiceTypeForLogserverContainer = false; - testContainerOnLogserverHost(services, useDedicatedNodeForLogserver, useSeparateServiceTypeForLogserverContainer); - } - - @Test - public void testLogserverContainerWhenDedicatedLogserverSeparateServiceType() { - String services = - "<?xml version='1.0' encoding='utf-8' ?>\n" + - "<services>" + - " <admin version='4.0'>" + - " <logservers>" + - " <nodes count='1' dedicated='true'/>" + - " </logservers>" + - " </admin>" + - " <container version='1.0' id='foo'>" + - " <nodes count='1'/>" + - " </container>" + - "</services>"; - boolean useDedicatedNodeForLogserver = false; - boolean useSeparateServiceTypeForLogserverContainer = true; - testContainerOnLogserverHost(services, useDedicatedNodeForLogserver, useSeparateServiceTypeForLogserverContainer); + testContainerOnLogserverHost(services, useDedicatedNodeForLogserver); } @Test @@ -989,8 +969,7 @@ public class ModelProvisioningTest { " </container>" + "</services>"; boolean useDedicatedNodeForLogserver = true; - boolean useSeparateServiceTypeForLogserverContainer = false; - testContainerOnLogserverHost(services, useDedicatedNodeForLogserver, useSeparateServiceTypeForLogserverContainer); + testContainerOnLogserverHost(services, useDedicatedNodeForLogserver); } @Test @@ -1833,11 +1812,10 @@ public class ModelProvisioningTest { // Tests that a container is allocated on logserver host and that // it is able to get config - private void testContainerOnLogserverHost(String services, boolean useDedicatedNodeForLogserver, boolean useSeparateServiceTypeForLogserverContainer) { + private void testContainerOnLogserverHost(String services, boolean useDedicatedNodeForLogserver) { int numberOfHosts = 2; VespaModelTester tester = new VespaModelTester(); tester.useDedicatedNodeForLogserver(useDedicatedNodeForLogserver); - tester.useSeparateServiceTypeForLogserverContainer(useSeparateServiceTypeForLogserverContainer); tester.addHosts(numberOfHosts); VespaModel model = tester.createModel(Zone.defaultZone(), services, true); @@ -1847,9 +1825,7 @@ public class ModelProvisioningTest { Logserver logserver = admin.getLogserver(); HostResource hostResource = logserver.getHostResource(); assertNotNull(hostResource.getService("logserver")); - String containerServiceType = useSeparateServiceTypeForLogserverContainer - ? ContainerServiceType.LOGSERVER_CONTAINER.serviceName - : ContainerServiceType.CONTAINER.serviceName; + String containerServiceType = ContainerServiceType.LOGSERVER_CONTAINER.serviceName; assertNotNull(hostResource.getService(containerServiceType)); // Test that the container gets config 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 f004cc8ff2a..3896b6d799a 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 @@ -28,6 +28,7 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; import static com.yahoo.config.model.api.container.ContainerServiceType.CONTAINER; +import static com.yahoo.config.model.api.container.ContainerServiceType.LOGSERVER_CONTAINER; import static com.yahoo.config.model.api.container.ContainerServiceType.METRICS_PROXY_CONTAINER; import static com.yahoo.config.model.api.container.ContainerServiceType.QRSERVER; import static org.junit.Assert.assertEquals; @@ -77,7 +78,7 @@ public class DedicatedAdminV4Test { assertHostContainsServices(model, "hosts/myhost0", "slobrok", "logd"); assertHostContainsServices(model, "hosts/myhost1", "slobrok", "logd"); // Note: A container is always added on logserver host - assertHostContainsServices(model, "hosts/myhost2", "logserver", "logd", CONTAINER.serviceName); + assertHostContainsServices(model, "hosts/myhost2", "logserver", "logd", LOGSERVER_CONTAINER.serviceName); Monitoring monitoring = model.getAdmin().getMonitoring(); assertEquals("vespa.routing", monitoring.getClustername()); @@ -157,7 +158,7 @@ public class DedicatedAdminV4Test { assertHostContainsServices(model, "hosts/myhost0", "logd", "logforwarder", "slobrok"); assertHostContainsServices(model, "hosts/myhost1", "logd", "logforwarder", "slobrok"); // Note: A container is always added on logserver host - assertHostContainsServices(model, "hosts/myhost2", "logd", "logforwarder", "logserver", CONTAINER.serviceName); + assertHostContainsServices(model, "hosts/myhost2", "logd", "logforwarder", "logserver", LOGSERVER_CONTAINER.serviceName); Set<String> configIds = model.getConfigIds(); // 1 logforwarder on each host @@ -200,7 +201,7 @@ public class DedicatedAdminV4Test { .properties(new TestProperties().setHostedVespa(true))); assertEquals(1, model.getHosts().size()); // Should create a container on the same node as logserver - assertHostContainsServices(model, "hosts/myhost0", "slobrok", "logd", "logserver", CONTAINER.serviceName); + assertHostContainsServices(model, "hosts/myhost0", "slobrok", "logd", "logserver", LOGSERVER_CONTAINER.serviceName); } private Set<String> serviceNames(VespaModel model, String hostname) { diff --git a/config-model/src/test/java/com/yahoo/vespa/model/test/VespaModelTester.java b/config-model/src/test/java/com/yahoo/vespa/model/test/VespaModelTester.java index 00bc82bbe02..801e138f3c7 100644 --- a/config-model/src/test/java/com/yahoo/vespa/model/test/VespaModelTester.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/test/VespaModelTester.java @@ -47,7 +47,6 @@ public class VespaModelTester { private Map<String, Collection<Host>> hostsByFlavor = new HashMap<>(); private ApplicationId applicationId = ApplicationId.defaultId(); private boolean useDedicatedNodeForLogserver = false; - private boolean useSeparateServiceTypeForLogserverContainer = false; private boolean enableMetricsProxyContainer = false; public VespaModelTester() { @@ -99,10 +98,6 @@ public class VespaModelTester { this.useDedicatedNodeForLogserver = useDedicatedNodeForLogserver; } - public void useSeparateServiceTypeForLogserverContainer(boolean useSeparateServiceTypeForLogserverContainer) { - this.useSeparateServiceTypeForLogserverContainer = useSeparateServiceTypeForLogserverContainer; - } - public void enableMetricsProxyContainer(boolean enableMetricsProxyContainer) { this.enableMetricsProxyContainer = enableMetricsProxyContainer; } @@ -148,7 +143,6 @@ public class VespaModelTester { .setHostedVespa(hosted) .setApplicationId(applicationId) .setUseDedicatedNodeForLogserver(useDedicatedNodeForLogserver) - .setUseSeparateServiceTypeForLogserverContainer(useSeparateServiceTypeForLogserverContainer) .setEnableMetricsProxyContainer(enableMetricsProxyContainer); DeployState deployState = new DeployState.Builder() diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java index 38886b4f656..0279d175488 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java @@ -132,7 +132,6 @@ public class ModelContextImpl implements ModelContext { private final boolean useFdispatchByDefault; private final boolean useAdaptiveDispatch; private final boolean dispatchWithProtobuf; - private final boolean useSeparateServiceTypeForLogserverContainer; private final boolean enableMetricsProxyContainer; public Properties(ApplicationId applicationId, @@ -166,8 +165,6 @@ public class ModelContextImpl implements ModelContext { .with(FetchVector.Dimension.APPLICATION_ID, applicationId.serializedForm()).value(); this.useAdaptiveDispatch = Flags.USE_ADAPTIVE_DISPATCH.bindTo(flagSource) .with(FetchVector.Dimension.APPLICATION_ID, applicationId.serializedForm()).value(); - this.useSeparateServiceTypeForLogserverContainer = Flags.USE_SEPARATE_SERVICE_TYPE_FOR_LOGSERVER_CONTAINER.bindTo(flagSource) - .with(FetchVector.Dimension.APPLICATION_ID, applicationId.serializedForm()).value(); this.enableMetricsProxyContainer = Flags.ENABLE_METRICS_PROXY_CONTAINER.bindTo(flagSource) .with(FetchVector.Dimension.APPLICATION_ID, applicationId.serializedForm()).value(); } @@ -222,9 +219,6 @@ public class ModelContextImpl implements ModelContext { public boolean useAdaptiveDispatch() { return useAdaptiveDispatch; } @Override - public boolean useSeparateServiceTypeForLogserverContainer() { return useSeparateServiceTypeForLogserverContainer; } - - @Override public boolean enableMetricsProxyContainer() { return enableMetricsProxyContainer; } } diff --git a/container-core/CMakeLists.txt b/container-core/CMakeLists.txt index 1a2bbabaed3..43225e38aee 100644 --- a/container-core/CMakeLists.txt +++ b/container-core/CMakeLists.txt @@ -12,3 +12,5 @@ install_config_definition(src/main/resources/configdefinitions/qr.def container. install_config_definition(src/main/resources/configdefinitions/servlet-config.def container.servlet.servlet-config.def) install_config_definition(src/main/resources/configdefinitions/threadpool.def container.handler.threadpool.def) install_config_definition(src/main/resources/configdefinitions/vip-status.def container.core.vip-status.def) + +vespa_install_script(src/main/sh/vespa-load-balancer-status libexec/vespa) diff --git a/container-core/src/main/java/com/yahoo/container/handler/LogReader.java b/container-core/src/main/java/com/yahoo/container/handler/LogReader.java index 95a0e9a6766..663741f9bef 100644 --- a/container-core/src/main/java/com/yahoo/container/handler/LogReader.java +++ b/container-core/src/main/java/com/yahoo/container/handler/LogReader.java @@ -57,13 +57,10 @@ class LogReader { void writeLogs(OutputStream outputStream, Instant earliestLogThreshold, Instant latestLogThreshold) { try { for (Path file : getMatchingFiles(earliestLogThreshold, latestLogThreshold)) { - if (file.toString().endsWith(".gz")) { - Files.copy(file, outputStream); - } else { - OutputStream zip = new GZIPOutputStream(outputStream); - Files.copy(file, zip); - zip.close(); + if (!file.toString().endsWith(".gz") && !(outputStream instanceof GZIPOutputStream)) { + outputStream = new GZIPOutputStream(outputStream); } + Files.copy(file, outputStream); } outputStream.close(); } catch (IOException e) { diff --git a/container-core/src/main/sh/vespa-load-balancer-status b/container-core/src/main/sh/vespa-load-balancer-status new file mode 100755 index 00000000000..e93337333f3 --- /dev/null +++ b/container-core/src/main/sh/vespa-load-balancer-status @@ -0,0 +1,214 @@ +#!/bin/bash +# +# Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +# BEGIN environment bootstrap section +# Do not edit between here and END as this section should stay identical in all scripts + +findpath () { + myname=${0} + mypath=${myname%/*} + myname=${myname##*/} + if [ "$mypath" ] && [ -d "$mypath" ]; then + return + fi + mypath=$(pwd) + if [ -f "${mypath}/${myname}" ]; then + return + fi + echo "FATAL: Could not figure out the path where $myname lives from $0" + exit 1 +} + +COMMON_ENV=libexec/vespa/common-env.sh + +source_common_env () { + if [ "$VESPA_HOME" ] && [ -d "$VESPA_HOME" ]; then + export VESPA_HOME + common_env=$VESPA_HOME/$COMMON_ENV + if [ -f "$common_env" ]; then + . $common_env + return + fi + fi + return 1 +} + +findroot () { + source_common_env && return + if [ "$VESPA_HOME" ]; then + echo "FATAL: bad VESPA_HOME value '$VESPA_HOME'" + exit 1 + fi + if [ "$ROOT" ] && [ -d "$ROOT" ]; then + VESPA_HOME="$ROOT" + source_common_env && return + fi + findpath + while [ "$mypath" ]; do + VESPA_HOME=${mypath} + source_common_env && return + mypath=${mypath%/*} + done + echo "FATAL: missing VESPA_HOME environment variable" + echo "Could not locate $COMMON_ENV anywhere" + exit 1 +} + +findhost () { + if [ "${VESPA_HOSTNAME}" = "" ]; then + VESPA_HOSTNAME=$(vespa-detect-hostname || hostname -f || hostname || echo "localhost") || exit 1 + fi + validate="${VESPA_HOME}/bin/vespa-validate-hostname" + if [ -f "$validate" ]; then + "$validate" "${VESPA_HOSTNAME}" || exit 1 + fi + export VESPA_HOSTNAME +} + +findroot +findhost + +# END environment bootstrap section + +set -eu + +declare LB_STATUS_DIR="$VESPA_HOME"/var/vespa/load-balancer +declare LB_STATUS_FILE="$LB_STATUS_DIR"/status.html +declare LB_OPERATOR_LOG="$LB_STATUS_DIR"/operator.log + +function Usage { + cat <<EOF +Usage: ${0##*/} COMMAND [-u USER] [-f] +Make jdisc container stop serving /status.html. + +Useful when jdisc container is behind a load balancer: The load balancer can be +set up to monitor the health of /status.html requests, and remove bad backends +from serving. + +Command: + get Return info on the current in/out status. + in Undo 'out'. This command is a no-op if 1. status is already in, or 2. + if the the user that set it out is different from USER and -f was NOT + specified. + out Stop answering OK on /status.html requests against jdisc container. + Note: The jdisc container may not answer OK for other reasons too. + +Options: + -u USER Set the user agent. The user setting the status in, must match + the user that set it out. Defaults to current user. + -f Force-set status: Ignore any mismatch on user. +EOF + + exit 0 +} + +function PrintPair { + printf "%-19s %s\n" "$1:" "$2" +} + +function IsIn { + if [ -r "$LB_STATUS_FILE" ]; then + return 0 + else + return 1 + fi +} + +function DifferentUserSetOut { + local user="$1" + + if [ -r "$LB_OPERATOR_LOG" ]; then + local out_user + out_user=$(< "$LB_OPERATOR_LOG") + if [ "$user" != "$out_user" ]; then + return 0 + fi + fi + + return 1 +} + +function GetCommand { + if IsIn; then + PrintPair "VIP status" IN + else + PrintPair "VIP status" OUT + fi + PrintPair "Status file" "$LB_STATUS_FILE" + + if [ -r "$LB_OPERATOR_LOG" ]; then + PrintPair "Last modified" "$(stat -c %y "$LB_OPERATOR_LOG")" + PrintPair "Last modified by" "$(< "$LB_OPERATOR_LOG")" + fi +} + +function InCommand { + local user="$1" + local force="$2" + + if ! $force; then + if IsIn || DifferentUserSetOut "$user"; then + return + fi + fi + + mkdir -p "$LB_STATUS_DIR" + echo "$user" > "$LB_OPERATOR_LOG" + echo OK > "$LB_STATUS_FILE" +} + +function OutCommand { + local user="$1" + local force="$2" + + if ! $force && ! IsIn; then + return + fi + + mkdir -p "$LB_STATUS_DIR" + echo "$user" > "$LB_OPERATOR_LOG" + rm -f "$LB_STATUS_FILE" +} + +function Main { + if (($# == 0)); then + Usage + fi + + local command= + local user="${SUDO_USER:-${USER:-$(id -nu)}}" + local force=false + + # Supports placement of options both before and after command. + while (($# > 0)); do + case "$1" in + -f) + force=true + shift + ;; + -u) + user="$2" + shift 2 + ;; + -*) Usage "Unknown option '$1'" ;; + *) + case "$1" in + get) command="GetCommand" ;; + in) command="InCommand" ;; + out) command="OutCommand" ;; + *) Usage ;; + esac + shift + ;; + esac + done + + if [ -z "$command" ]; then + Usage + fi + + "$command" "$user" "$force" +} + +Main "$@" diff --git a/container-core/src/test/java/com/yahoo/container/handler/LogReaderTest.java b/container-core/src/test/java/com/yahoo/container/handler/LogReaderTest.java index 1bd68ad0c48..464d6f772eb 100644 --- a/container-core/src/test/java/com/yahoo/container/handler/LogReaderTest.java +++ b/container-core/src/test/java/com/yahoo/container/handler/LogReaderTest.java @@ -69,14 +69,6 @@ public class LogReaderTest { @Test public void testZippedStreaming() throws IOException { - // Add some more files - Files.setLastModifiedTime( - Files.write(logDirectory.resolve("log3.gz"), compress("Three\n")), - FileTime.from(Instant.ofEpochMilli(324))); - Files.setLastModifiedTime( - Files.write(logDirectory.resolve("log4"), "Four\n".getBytes()), - FileTime.from(Instant.ofEpochMilli(432))); - ByteArrayOutputStream zippedBaos = new ByteArrayOutputStream(); LogReader logReader = new LogReader(logDirectory, Pattern.compile(".*")); logReader.writeLogs(zippedBaos, Instant.ofEpochMilli(21), Instant.now()); @@ -85,7 +77,7 @@ public class LogReaderTest { Scanner s = new Scanner(unzippedIs).useDelimiter("\\A"); String actual = s.hasNext() ? s.next() : ""; - String expected = "This is one log file\nThis is another log file\nThree\nFour\n"; + String expected = "This is one log file\nThis is another log file\n"; assertEquals(expected, actual); } diff --git a/container-search/src/main/java/com/yahoo/fs4/BasicPacket.java b/container-search/src/main/java/com/yahoo/fs4/BasicPacket.java index 6f87e45af25..f87721dc503 100644 --- a/container-search/src/main/java/com/yahoo/fs4/BasicPacket.java +++ b/container-search/src/main/java/com/yahoo/fs4/BasicPacket.java @@ -26,9 +26,7 @@ public abstract class BasicPacket { private static int DEFAULT_WRITE_BUFFER_SIZE = (10 * 1024); public static final int CODE_MASK = 0x00ff_ffff; // Reserve upper byte for flags. - protected byte[] encodedBody; - - protected ByteBuffer encodingBuffer; + private byte[] encodedBody; /** The length of this packet in bytes or -1 if not known */ protected int length = -1; @@ -199,7 +197,7 @@ public abstract class BasicPacket { throw new UnsupportedOperationException("Encoding of " + this + " is not implemented"); } - protected void setEncodedBody(ByteBuffer b, int start, int length) { + private void setEncodedBody(ByteBuffer b, int start, int length) { encodedBody = new byte[length]; b.position(start); b.get(encodedBody); @@ -222,18 +220,7 @@ public abstract class BasicPacket { * * If this packet does not use a channel ID, the ID will be ignored. */ - public final void allocateAndEncode(int channelId) { - allocateAndEncode(channelId, DEFAULT_WRITE_BUFFER_SIZE); - } - - private void allocateAndEncode(int channelId, int initialSize) { - if (encodingBuffer != null) { - patchChannelId(encodingBuffer, channelId); - return; - } - - int size = initialSize; - ByteBuffer buffer = ByteBuffer.allocate(size); + private ByteBuffer allocateAndEncode(int channelId, ByteBuffer buffer) { while (true) { try { if (hasChannelId()) { @@ -242,43 +229,25 @@ public abstract class BasicPacket { encode(buffer); } buffer.flip(); - encodingBuffer = buffer; break; } catch (BufferTooSmallException e) { - size *= 2; - buffer = ByteBuffer.allocate(size); + buffer = ByteBuffer.allocate(buffer.capacity()*2); } } + return buffer; } - // No channel ID for BasicPacket instances, so it's a NOP - protected void patchChannelId(ByteBuffer buf, int channelId) {} - /** * Return buffer containing the encoded form of this package and * remove internal reference to it. */ public final ByteBuffer grantEncodingBuffer(int channelId) { - if (encodingBuffer == null) { - allocateAndEncode(channelId); - } else { - patchChannelId(encodingBuffer, channelId); - } - ByteBuffer b = encodingBuffer; - encodingBuffer = null; - return b; + return allocateAndEncode(channelId, ByteBuffer.allocate(DEFAULT_WRITE_BUFFER_SIZE)); } - public final ByteBuffer grantEncodingBuffer(int channelId, int initialSize) { - if (encodingBuffer == null) { - allocateAndEncode(channelId, initialSize); - } else { - patchChannelId(encodingBuffer, channelId); - } - ByteBuffer b = encodingBuffer; - encodingBuffer = null; - return b; + public final ByteBuffer grantEncodingBuffer(int channelId, ByteBuffer buffer) { + return allocateAndEncode(channelId, buffer); } /** Returns the code of this package */ diff --git a/container-search/src/main/java/com/yahoo/fs4/Packet.java b/container-search/src/main/java/com/yahoo/fs4/Packet.java index 78d5083c25f..1e9deede59d 100644 --- a/container-search/src/main/java/com/yahoo/fs4/Packet.java +++ b/container-search/src/main/java/com/yahoo/fs4/Packet.java @@ -19,8 +19,6 @@ public abstract class Packet extends BasicPacket { */ protected int channel = -1; - private static final int CHANNEL_ID_OFFSET = 8; - /** * Fills this package from a byte buffer positioned at the first * byte of the package @@ -109,17 +107,6 @@ public abstract class Packet extends BasicPacket { return true; } - /** - * Only for use with encodingBuffer magic. - * - * This is only called from allocateAndEncode and grantEncodingBuffer, - * therefore an assumption about the packet starting at the beginning of the - * buffer is made. - */ - protected void patchChannelId(ByteBuffer buf, int channelId) { - buf.putInt(CHANNEL_ID_OFFSET, channelId); - } - public String toString() { return "packet with code " + getCode() + ", channelId=" + getChannel(); } diff --git a/container-search/src/main/java/com/yahoo/fs4/mplex/Backend.java b/container-search/src/main/java/com/yahoo/fs4/mplex/Backend.java index 202ee94383f..12f8e9e387d 100644 --- a/container-search/src/main/java/com/yahoo/fs4/mplex/Backend.java +++ b/container-search/src/main/java/com/yahoo/fs4/mplex/Backend.java @@ -1,19 +1,29 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.fs4.mplex; - -import com.yahoo.fs4.*; +import com.yahoo.fs4.BasicPacket; +import com.yahoo.fs4.Packet; +import com.yahoo.fs4.PacketDumper; +import com.yahoo.fs4.PacketListener; +import com.yahoo.fs4.PacketNotificationsBroadcaster; +import com.yahoo.fs4.PacketQueryTracer; import com.yahoo.io.Connection; import com.yahoo.io.ConnectionFactory; import com.yahoo.io.Listener; import com.yahoo.vespa.defaults.Defaults; import com.yahoo.yolean.Exceptions; +import com.yahoo.yolean.concurrent.ConcurrentResourcePool; +import com.yahoo.yolean.concurrent.ResourceFactory; +import com.yahoo.yolean.concurrent.ResourcePool; import java.io.File; import java.io.IOException; import java.net.InetSocketAddress; +import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; -import java.util.*; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; import java.util.logging.Logger; @@ -23,6 +33,8 @@ import java.util.logging.Logger; */ public class Backend implements ConnectionFactory { + private static int DEFAULT_BUFFER_SIZE = 0x8000; + public static final class BackendStatistics { public final int activeConnections; @@ -61,6 +73,12 @@ public class Backend implements ConnectionFactory { private final ConnectionPool connectionPool; private final PacketDumper packetDumper; private final AtomicInteger connectionCount = new AtomicInteger(0); + private final ConcurrentResourcePool<ByteBuffer> byteBufferResourcePool = new ConcurrentResourcePool<>(new ResourceFactory<>() { + @Override + public ByteBuffer create() { + return ByteBuffer.allocate(DEFAULT_BUFFER_SIZE); + } + }); /** * For unit testing. do not use @@ -116,6 +134,10 @@ public class Backend implements ConnectionFactory { return connection; } + ConcurrentResourcePool<ByteBuffer> getBufferPool() { + return byteBufferResourcePool; + } + /** * Return a connection to the connection pool. If the * connection is not valid anymore we drop it, ie. do not @@ -416,10 +438,6 @@ public class Backend implements ConnectionFactory { } } - public void dumpPackets(final PacketDumper.PacketType packetType, final boolean on) throws IOException { - packetDumper.dumpPackets(packetType, on); - } - public String getHost() { return host; } diff --git a/container-search/src/main/java/com/yahoo/fs4/mplex/FS4Connection.java b/container-search/src/main/java/com/yahoo/fs4/mplex/FS4Connection.java index 69267f4a6b2..7dcbefde9fa 100644 --- a/container-search/src/main/java/com/yahoo/fs4/mplex/FS4Connection.java +++ b/container-search/src/main/java/com/yahoo/fs4/mplex/FS4Connection.java @@ -36,7 +36,6 @@ public class FS4Connection implements Connection private static int idCounter = 1; private int idNumber; - private int maxInitialSize = 1024; // outbound data private ByteBuffer writeBuffer; @@ -69,7 +68,7 @@ public class FS4Connection implements Connection * Packet sending interface. */ public void sendPacket (BasicPacket packet, Integer channelId) throws IOException { - ByteBuffer buffer = packet.grantEncodingBuffer(channelId.intValue(), maxInitialSize); + ByteBuffer buffer = packet.grantEncodingBuffer(channelId.intValue(), backend.getBufferPool().alloc()); ByteBuffer viewForPacketListener = buffer.slice(); synchronized (this) { if (!(valid && channel.isOpen())) { @@ -79,9 +78,6 @@ public class FS4Connection implements Connection ", isOpen = " + channel.isOpen()); } - if (buffer.capacity() > maxInitialSize) { - maxInitialSize = buffer.limit(); - } if (writeBuffer == null) { writeBuffer = buffer; } else { @@ -131,6 +127,8 @@ public class FS4Connection implements Connection // buffer drained so we forget it and see what happens when we // go around. if indeed we go around if (!writeBuffer.hasRemaining()) { + writeBuffer.clear(); + backend.getBufferPool().free(writeBuffer); writeBuffer = null; } } while (bytesWritten > 0); diff --git a/container-search/src/test/java/com/yahoo/fs4/test/QueryTestCase.java b/container-search/src/test/java/com/yahoo/fs4/test/QueryTestCase.java index fc39c1d8fe0..911831e3a65 100644 --- a/container-search/src/test/java/com/yahoo/fs4/test/QueryTestCase.java +++ b/container-search/src/test/java/com/yahoo/fs4/test/QueryTestCase.java @@ -192,6 +192,15 @@ public class QueryTestCase { } @Test + public void testBufferExpands() throws BufferTooSmallException { + Query query = new Query("/?query=chain&sortspec=%2Ba+-b&timeout=0"); + QueryPacket packet = QueryPacket.create("container.0", query); + + ByteBuffer buffer = packet.grantEncodingBuffer(0, ByteBuffer.allocate(2)); + assertEquals(64, buffer.capacity()); + } + + @Test public void testPhraseEqualsPhraseWithPhraseSegment() throws BufferTooSmallException { Query query = new Query(); PhraseItem p = new PhraseItem(); @@ -258,7 +267,6 @@ public class QueryTestCase { assertEqualArrays(correctBuffer,encoded); - packet.allocateAndEncode(0x07070707); buffer = packet.grantEncodingBuffer(0x09090909); correctBuffer = new byte[] {0,0,0,46,0,0,0,-38,9,9,9,9, // Header 0,0,0,6, // Features diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceAllocation.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceAllocation.java new file mode 100644 index 00000000000..3b86b7b8219 --- /dev/null +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceAllocation.java @@ -0,0 +1,50 @@ +// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.api.integration.resource; + +import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeRepositoryNode; + +import java.util.List; + +/** + * Stores the total amount of resources allocated to a list of nodes + * + * @author leandroalves + */ +public class ResourceAllocation { + + private final double cpuCores; + private final double memoryGb; + private final double diskGb; + + private ResourceAllocation(double cpuCores, double memoryGb, double diskGb) { + this.cpuCores = cpuCores; + this.memoryGb = memoryGb; + this.diskGb = diskGb; + } + + public static ResourceAllocation from(List<NodeRepositoryNode> nodes) { + return new ResourceAllocation( + nodes.stream().mapToDouble(NodeRepositoryNode::getMinCpuCores).sum(), + nodes.stream().mapToDouble(NodeRepositoryNode::getMinMainMemoryAvailableGb).sum(), + nodes.stream().mapToDouble(NodeRepositoryNode::getMinDiskAvailableGb).sum() + ); + } + + public double usageFraction(ResourceAllocation total) { + return (cpuCores / total.cpuCores + memoryGb / total.memoryGb + diskGb / total.diskGb) / 3; + } + + public double getCpuCores() { + return cpuCores; + } + + public double getMemoryGb() { + return memoryGb; + } + + public double getDiskGb() { + return diskGb; + } + +} + diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshot.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshot.java new file mode 100644 index 00000000000..7f7a6b758d5 --- /dev/null +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshot.java @@ -0,0 +1,27 @@ +// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.api.integration.resource; + +import java.time.Instant; + +/** + * @author olaa + */ +public class ResourceSnapshot { + + private final ResourceAllocation resourceAllocation; + private final Instant timestamp; + + public ResourceSnapshot(ResourceAllocation resourceAllocation, Instant timestamp) { + this.resourceAllocation = resourceAllocation; + this.timestamp = timestamp; + } + + public ResourceAllocation getResourceAllocation() { + return resourceAllocation; + } + + public Instant getTimestamp() { + return timestamp; + } + +} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshotConsumer.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshotConsumer.java new file mode 100644 index 00000000000..f7f3eddb482 --- /dev/null +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshotConsumer.java @@ -0,0 +1,16 @@ +// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.api.integration.resource; + +import com.yahoo.config.provision.ApplicationId; + +import java.util.Map; + +/** + * Consumes a snapshot of resourses allocated/used per application. + * + * @author olaa + */ +public interface ResourceSnapshotConsumer { + + public void consume(Map<ApplicationId, ResourceSnapshot> resources); +} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockResourceSnapshotConsumer.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockResourceSnapshotConsumer.java new file mode 100644 index 00000000000..d5d7b63e933 --- /dev/null +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockResourceSnapshotConsumer.java @@ -0,0 +1,25 @@ +// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.api.integration.stubs; + +import com.yahoo.config.provision.ApplicationId; +import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceSnapshot; +import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceSnapshotConsumer; + +import java.util.Map; + +/** + * @author olaa + */ +public class MockResourceSnapshotConsumer implements ResourceSnapshotConsumer { + + private Map<ApplicationId, ResourceSnapshot> resources; + + @Override + public void consume(Map<ApplicationId, ResourceSnapshot> resources){ + this.resources = resources; + } + + public Map<ApplicationId, ResourceSnapshot> consumedResources() { + return resources; + } +} diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java index cc9e4020dab..506231f086b 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java @@ -5,6 +5,7 @@ import com.yahoo.component.AbstractComponent; import com.yahoo.jdisc.Metric; import com.yahoo.vespa.hosted.controller.Controller; import com.yahoo.vespa.hosted.controller.api.integration.organization.ContactRetriever; +import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceSnapshotConsumer; import com.yahoo.vespa.hosted.controller.authority.config.ApiAuthorityConfig; import com.yahoo.vespa.hosted.controller.api.integration.chef.Chef; import com.yahoo.vespa.hosted.controller.api.integration.dns.NameService; @@ -53,6 +54,7 @@ public class ControllerMaintenance extends AbstractComponent { private final ContactInformationMaintainer contactInformationMaintainer; private final CostReportMaintainer costReportMaintainer; private final RoutingPolicyMaintainer routingPolicyMaintainer; + private final ResourceMeterMaintainer resourceMeterMaintainer; @SuppressWarnings("unused") // instantiated by Dependency Injection public ControllerMaintenance(MaintainerConfig maintainerConfig, ApiAuthorityConfig apiAuthorityConfig, Controller controller, CuratorDb curator, @@ -61,6 +63,7 @@ public class ControllerMaintenance extends AbstractComponent { NameService nameService, NodeRepositoryClientInterface nodeRepositoryClient, ContactRetriever contactRetriever, CostReportConsumer reportConsumer, + ResourceSnapshotConsumer resourceSnapshotConsumer, SelfHostedCostConfig selfHostedCostConfig) { Duration maintenanceInterval = Duration.ofMinutes(maintainerConfig.intervalMinutes()); this.jobControl = jobControl; @@ -83,6 +86,7 @@ public class ControllerMaintenance extends AbstractComponent { contactInformationMaintainer = new ContactInformationMaintainer(controller, Duration.ofHours(12), jobControl, contactRetriever); costReportMaintainer = new CostReportMaintainer(controller, Duration.ofHours(2), reportConsumer, jobControl, nodeRepositoryClient, Clock.systemUTC(), selfHostedCostConfig); routingPolicyMaintainer = new RoutingPolicyMaintainer(controller, Duration.ofMinutes(5), jobControl, nameService, curator); + resourceMeterMaintainer = new ResourceMeterMaintainer(controller, Duration.ofMinutes(5), jobControl, nodeRepositoryClient, Clock.systemUTC(), metric, resourceSnapshotConsumer); } public Upgrader upgrader() { return upgrader; } @@ -111,6 +115,7 @@ public class ControllerMaintenance extends AbstractComponent { contactInformationMaintainer.deconstruct(); costReportMaintainer.deconstruct(); routingPolicyMaintainer.deconstruct(); + resourceMeterMaintainer.deconstruct(); } /** Create one OS upgrader per cloud found in the zone registry of controller */ diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/CostReportMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/CostReportMaintainer.java index 2298c3c92fe..a76d472cc89 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/CostReportMaintainer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/CostReportMaintainer.java @@ -46,6 +46,6 @@ public class CostReportMaintainer extends Maintainer { @Override protected void maintain() { - consumer.Consume(CostCalculator.toCsv(CostCalculator.calculateCost(nodeRepository, controller(), clock, selfHostedCostConfig))); + consumer.Consume(CostCalculator.resourceShareByPropertyToCsv(nodeRepository, controller(), clock, selfHostedCostConfig)); } } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainer.java new file mode 100644 index 00000000000..5ed7a14836e --- /dev/null +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainer.java @@ -0,0 +1,104 @@ +// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.maintenance; + +import com.yahoo.config.provision.SystemName; +import com.yahoo.jdisc.Metric; +import com.yahoo.vespa.hosted.controller.Controller; +import com.yahoo.config.provision.ApplicationId; +import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeOwner; +import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeRepositoryClientInterface; +import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeRepositoryNode; +import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceSnapshot; +import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceSnapshotConsumer; +import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceAllocation; + +import java.time.Clock; +import java.time.Duration; +import java.time.Instant; +import java.util.*; +import java.util.stream.Collectors; + +import static com.yahoo.yolean.Exceptions.uncheck; + +/** + * Creates a ResourceSnapshot per application, which is then passed on to a ResourceSnapshotConsumer + * TODO: Write JSON blob of node repo somewhere + * + * @author olaa + */ +public class ResourceMeterMaintainer extends Maintainer { + + private final Clock clock; + private final Metric metric; + private final NodeRepositoryClientInterface nodeRepository; + private final ResourceSnapshotConsumer resourceSnapshotConsumer; + + private static final String metering_last_reported = "metering_last_reported"; + private static final String metering_total_reported = "metering_total_reported"; + + @SuppressWarnings("WeakerAccess") + public ResourceMeterMaintainer(Controller controller, + Duration interval, + JobControl jobControl, + NodeRepositoryClientInterface nodeRepository, + Clock clock, + Metric metric, + ResourceSnapshotConsumer resourceSnapshotConsumer) { + super(controller, interval, jobControl, ResourceMeterMaintainer.class.getSimpleName(), Set.of(SystemName.cd)); + this.clock = clock; + this.nodeRepository = nodeRepository; + this.metric = metric; + this.resourceSnapshotConsumer = resourceSnapshotConsumer; + } + + @Override + protected void maintain() { + List<NodeRepositoryNode> nodes = getNodes(); + Map<ApplicationId, ResourceAllocation> resourceAllocationByApplication = getResourceAllocationByApplication(nodes); + + // For now, we're only interested in resource allocation + Instant timeStamp = clock.instant(); + Map<ApplicationId, ResourceSnapshot> resourceSnapshots = resourceAllocationByApplication.entrySet().stream() + .collect(Collectors.toMap( + e -> e.getKey(), + e -> new ResourceSnapshot(e.getValue(), timeStamp)) + ); + + + resourceSnapshotConsumer.consume(resourceSnapshots); + + metric.set(metering_last_reported, clock.millis() / 1000, metric.createContext(Collections.emptyMap())); + metric.set(metering_total_reported, resourceSnapshots.values().stream() + .map(ResourceSnapshot::getResourceAllocation) + .mapToDouble(r -> r.getCpuCores() + r.getMemoryGb() + r.getDiskGb()) // total metered resource usage, for alerting on drastic changes + .sum() + , metric.createContext(Collections.emptyMap())); + } + + private List<NodeRepositoryNode> getNodes() { + return controller().zoneRegistry().zones() + .reachable().ids().stream() + .flatMap(zoneId -> uncheck(() -> nodeRepository.listNodes(zoneId, true).nodes().stream())) + .filter(node -> node.getOwner() != null && !node.getOwner().getTenant().equals("hosted-vespa")) + .collect(Collectors.toList()); + } + + private Map<ApplicationId, ResourceAllocation> getResourceAllocationByApplication(List<NodeRepositoryNode> nodes) { + Map<ApplicationId, List<NodeRepositoryNode>> applicationNodes = new HashMap<>(); + + nodes.stream().forEach(node -> applicationNodes.computeIfAbsent(applicationIdFromNodeOwner(node.getOwner()), n -> new ArrayList<>()).add(node)); + + return applicationNodes.entrySet().stream() + .collect( + Collectors.toMap( + entry -> entry.getKey(), + entry -> ResourceAllocation.from(entry.getValue()) + ) + ); + } + + private ApplicationId applicationIdFromNodeOwner(NodeOwner owner) { + return ApplicationId.from(owner.getTenant(), owner.getApplication(), owner.getInstance()); + } + +} diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java index 69a4216d221..402f91f1a14 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java @@ -110,6 +110,8 @@ import static java.util.stream.Collectors.joining; @SuppressWarnings("unused") // created by injection public class ApplicationApiHandler extends LoggingRequestHandler { + private static final String OPTIONAL_PREFIX = "/api"; + private final Controller controller; private final AccessControlRequests accessControlRequests; @@ -162,7 +164,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler { } private HttpResponse handleGET(HttpRequest request) { - Path path = new Path(request.getUri().getPath()); + Path path = new Path(request.getUri().getPath(), OPTIONAL_PREFIX); if (path.matches("/application/v4/")) return root(request); if (path.matches("/application/v4/user")) return authenticatedUser(request); if (path.matches("/application/v4/tenant")) return tenants(request); @@ -185,7 +187,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler { } private HttpResponse handlePUT(HttpRequest request) { - Path path = new Path(request.getUri().getPath()); + Path path = new Path(request.getUri().getPath(), OPTIONAL_PREFIX); if (path.matches("/application/v4/user")) return createUser(request); if (path.matches("/application/v4/tenant/{tenant}")) return updateTenant(path.get("tenant"), request); if (path.matches("/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{instance}/global-rotation/override")) @@ -194,7 +196,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler { } private HttpResponse handlePOST(HttpRequest request) { - Path path = new Path(request.getUri().getPath()); + Path path = new Path(request.getUri().getPath(), OPTIONAL_PREFIX); if (path.matches("/application/v4/tenant/{tenant}")) return createTenant(path.get("tenant"), request); if (path.matches("/application/v4/tenant/{tenant}/application/{application}")) return createApplication(path.get("tenant"), path.get("application"), request); if (path.matches("/application/v4/tenant/{tenant}/application/{application}/promote")) return promoteApplication(path.get("tenant"), path.get("application"), request); @@ -213,14 +215,14 @@ public class ApplicationApiHandler extends LoggingRequestHandler { } private HttpResponse handlePATCH(HttpRequest request) { - Path path = new Path(request.getUri().getPath()); + Path path = new Path(request.getUri().getPath(), OPTIONAL_PREFIX); if (path.matches("/application/v4/tenant/{tenant}/application/{application}")) return setMajorVersion(path.get("tenant"), path.get("application"), request); return ErrorResponse.notFoundError("Nothing at " + path); } private HttpResponse handleDELETE(HttpRequest request) { - Path path = new Path(request.getUri().getPath()); + Path path = new Path(request.getUri().getPath(), OPTIONAL_PREFIX); if (path.matches("/application/v4/tenant/{tenant}")) return deleteTenant(path.get("tenant"), request); if (path.matches("/application/v4/tenant/{tenant}/application/{application}")) return deleteApplication(path.get("tenant"), path.get("application"), request); if (path.matches("/application/v4/tenant/{tenant}/application/{application}/deploying")) return cancelDeploy(path.get("tenant"), path.get("application"), "all"); diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/cost/CostApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/cost/CostApiHandler.java index 6d599d32cc6..444153089da 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/cost/CostApiHandler.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/cost/CostApiHandler.java @@ -36,7 +36,7 @@ public class CostApiHandler extends LoggingRequestHandler { Path path = new Path(request.getUri().getPath()); if (path.matches("/cost/v1/csv")) { - return new StringResponse(CostCalculator.toCsv(CostCalculator.calculateCost(nodeRepository, controller, Clock.systemUTC(), selfHostedCostConfig))); + return new StringResponse(CostCalculator.resourceShareByPropertyToCsv(nodeRepository, controller, Clock.systemUTC(), selfHostedCostConfig)); } return ErrorResponse.notFoundError("Nothing at " + path); diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/cost/CostCalculator.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/cost/CostCalculator.java index 88fe28a3613..fc30ecc97bb 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/cost/CostCalculator.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/cost/CostCalculator.java @@ -3,16 +3,20 @@ package com.yahoo.vespa.hosted.controller.restapi.cost; import com.yahoo.config.provision.CloudName; import com.yahoo.config.provision.Environment; import com.yahoo.vespa.hosted.controller.Controller; +import com.yahoo.vespa.hosted.controller.api.identifiers.ApplicationId; import com.yahoo.vespa.hosted.controller.api.identifiers.Property; import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeOwner; import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeRepositoryClientInterface; import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeRepositoryNode; +import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceAllocation; import com.yahoo.vespa.hosted.controller.restapi.cost.config.SelfHostedCostConfig; import com.yahoo.vespa.hosted.controller.tenant.AthenzTenant; import java.time.Clock; import java.time.LocalDate; +import java.util.ArrayList; import java.util.Comparator; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -24,7 +28,7 @@ public class CostCalculator { private static final double SELF_HOSTED_DISCOUNT = .5; - public static Map<Property, ResourceAllocation> calculateCost(NodeRepositoryClientInterface nodeRepository, + public static String resourceShareByPropertyToCsv(NodeRepositoryClientInterface nodeRepository, Controller controller, Clock clock, SelfHostedCostConfig selfHostedCostConfig) { @@ -50,7 +54,7 @@ public class CostCalculator { return selfHostedNode; }).forEach(nodes::add); - ResourceAllocation total = ResourceAllocation.from(date, nodes, null); + ResourceAllocation totalResourceAllocation = ResourceAllocation.from(nodes); Map<String, Property> propertyByTenantName = controller.tenants().asList().stream() .filter(AthenzTenant.class::isInstance) @@ -63,58 +67,29 @@ public class CostCalculator { .map(SelfHostedCostConfig.Properties::name) .forEach(name -> propertyByTenantName.put(name, new Property(name))); - return nodes.stream() + Map<Property, ResourceAllocation> resourceShareByProperty = nodes.stream() .filter(node -> propertyByTenantName.containsKey(node.getOwner().tenant)) .collect(Collectors.groupingBy( node -> propertyByTenantName.get(node.getOwner().tenant), Collectors.collectingAndThen( Collectors.toList(), - (tenantNodes) -> ResourceAllocation.from(date, tenantNodes, total) + (tenantNodes) -> ResourceAllocation.from(tenantNodes) ) )); - } - static class ResourceAllocation { - final double cpuCores; - final double memoryGb; - final double diskGb; - final String date; - final ResourceAllocation total; - - private ResourceAllocation(String date, double cpuCores, double memoryGb, double diskGb, ResourceAllocation total) { - this.date = date; - this.cpuCores = cpuCores; - this.memoryGb = memoryGb; - this.diskGb = diskGb; - this.total = total; - } - - private static ResourceAllocation from(String date, List<NodeRepositoryNode> nodes, ResourceAllocation total) { - return new ResourceAllocation( - date, - nodes.stream().mapToDouble(NodeRepositoryNode::getMinCpuCores).sum(), - nodes.stream().mapToDouble(NodeRepositoryNode::getMinMainMemoryAvailableGb).sum(), - nodes.stream().mapToDouble(NodeRepositoryNode::getMinDiskAvailableGb).sum(), - total - ); - } - - private double usageFraction() { - return (cpuCores / total.cpuCores + memoryGb / total.memoryGb + diskGb / total.diskGb) / 3; - } + return toCsv(resourceShareByProperty, date, totalResourceAllocation); } - public static String toCsv(Map<Property, ResourceAllocation> resourceShareByProperty) { + private static String toCsv(Map<Property, ResourceAllocation> resourceShareByProperty, String date, ResourceAllocation totalResourceAllocation) { String header = "Date,Property,Reserved Cpu Cores,Reserved Memory GB,Reserved Disk Space GB,Usage Fraction\n"; String entries = resourceShareByProperty.entrySet().stream() - .sorted((Comparator.comparingDouble(entry -> entry.getValue().usageFraction()))) + .sorted((Comparator.comparingDouble(entry -> entry.getValue().usageFraction(totalResourceAllocation)))) .map(propertyEntry -> { ResourceAllocation r = propertyEntry.getValue(); - return Stream.of(r.date, propertyEntry.getKey(), r.cpuCores, r.memoryGb, r.diskGb, r.usageFraction()) + return Stream.of(date, propertyEntry.getKey(), r.getCpuCores(), r.getMemoryGb(), r.getDiskGb(), r.usageFraction(totalResourceAllocation)) .map(Object::toString).collect(Collectors.joining(",")); }) .collect(Collectors.joining("\n")); return header + entries; } - } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryClientMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryClientMock.java index daddc46589d..ccd09cb9261 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryClientMock.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryClientMock.java @@ -56,8 +56,8 @@ public class NodeRepositoryClientMock implements NodeRepositoryClientInterface { node.setMinDiskAvailableGb(500d); node.setMinMainMemoryAvailableGb(24d); NodeOwner owner = new NodeOwner(); - owner.tenant = "lsbe"; - owner.application = "local-search"; + owner.tenant = "tenant1"; + owner.application = "app1"; owner.instance = "default"; node.setOwner(owner); NodeMembership membership = new NodeMembership(); @@ -76,8 +76,8 @@ public class NodeRepositoryClientMock implements NodeRepositoryClientInterface { node.setMinDiskAvailableGb(500d); node.setMinMainMemoryAvailableGb(24d); NodeOwner owner = new NodeOwner(); - owner.tenant = "mediasearch"; - owner.application = "imagesearch"; + owner.tenant = "tenant2"; + owner.application = "app2"; owner.instance = "default"; node.setOwner(owner); NodeMembership membership = new NodeMembership(); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/CostReportMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/CostReportMaintainerTest.java index 01f3f55c679..890af974a0e 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/CostReportMaintainerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/CostReportMaintainerTest.java @@ -34,8 +34,8 @@ public class CostReportMaintainerTest { .build(); - tester.createTenant("lsbe", "local-search", 1L); - tester.createTenant("mediasearch", "msbe", 2L); + tester.createTenant("tenant1", "app1", 1L); + tester.createTenant("tenant2", "app2", 2L); CostReportMaintainer maintainer = new CostReportMaintainer( tester.controller(), Duration.ofDays(1), diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainerTest.java new file mode 100644 index 00000000000..df2a4b5ca7f --- /dev/null +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainerTest.java @@ -0,0 +1,50 @@ +// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.maintenance; + +import com.yahoo.vespa.hosted.controller.ControllerTester; +import com.yahoo.config.provision.ApplicationId; +import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceSnapshot; +import com.yahoo.vespa.hosted.controller.api.integration.stubs.MockResourceSnapshotConsumer; +import com.yahoo.vespa.hosted.controller.integration.NodeRepositoryClientMock; +import com.yahoo.vespa.hosted.controller.integration.MetricsMock; +import org.junit.Test; + +import java.time.Duration; +import java.util.Map; + +import static org.junit.Assert.*; + +/** + * @author olaa + */ +public class ResourceMeterMaintainerTest { + + private final double DELTA = Double.MIN_VALUE; + private NodeRepositoryClientMock nodeRepository = new NodeRepositoryClientMock(); + private MockResourceSnapshotConsumer snapshotConsumer = new MockResourceSnapshotConsumer(); + private MetricsMock metrics = new MetricsMock(); + + @Test + public void testMaintainer() { + ControllerTester tester = new ControllerTester(); + ResourceMeterMaintainer resourceMeterMaintainer = new ResourceMeterMaintainer(tester.controller(), Duration.ofMinutes(5), new JobControl(tester.curator()), nodeRepository, tester.clock(), metrics, snapshotConsumer); + resourceMeterMaintainer.maintain(); + Map<ApplicationId, ResourceSnapshot> consumedResources = snapshotConsumer.consumedResources(); + + assertEquals(2, consumedResources.size()); + + ResourceSnapshot app1 = consumedResources.get(ApplicationId.from("tenant1", "app1", "default")); + ResourceSnapshot app2 = consumedResources.get(ApplicationId.from("tenant2", "app2", "default")); + + assertEquals(96, app1.getResourceAllocation().getCpuCores(), DELTA); + assertEquals(96, app1.getResourceAllocation().getMemoryGb(), DELTA); + assertEquals(2000, app1.getResourceAllocation().getDiskGb(), DELTA); + + assertEquals(160, app2.getResourceAllocation().getCpuCores(), DELTA); + assertEquals(96, app2.getResourceAllocation().getMemoryGb(), DELTA); + assertEquals(2000, app2.getResourceAllocation().getDiskGb(), DELTA); + + assertEquals(tester.clock().millis()/1000, metrics.getMetric("metering_last_reported")); + assertEquals(4448.0d, (Double) metrics.getMetric("metering_total_reported"), DELTA); + } +}
\ No newline at end of file diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java index 71b65770b1e..331a6ba9ac8 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java @@ -75,6 +75,7 @@ public class ControllerContainerTest { " <component id='com.yahoo.vespa.hosted.controller.api.integration.stubs.MockRunDataStore'/>\n" + " <component id='com.yahoo.vespa.hosted.controller.api.integration.organization.MockContactRetriever'/>\n" + " <component id='com.yahoo.vespa.hosted.controller.api.integration.organization.MockIssueHandler'/>\n" + + " <component id='com.yahoo.vespa.hosted.controller.api.integration.stubs.MockResourceSnapshotConsumer'/>\n" + " <component id='com.yahoo.vespa.hosted.controller.integration.ConfigServerMock'/>\n" + " <component id='com.yahoo.vespa.hosted.controller.integration.NodeRepositoryClientMock'/>\n" + " <component id='com.yahoo.vespa.hosted.controller.integration.ZoneRegistryMock'/>\n" + diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json index dd64d480453..0b5d3912214 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json @@ -46,6 +46,9 @@ "name": "ReadyJobsTrigger" }, { + "name": "ResourceMeterMaintainer" + }, + { "name": "RoutingPolicyMaintainer" }, { diff --git a/document/src/tests/CMakeLists.txt b/document/src/tests/CMakeLists.txt index b78fd66b687..bb668287a8c 100644 --- a/document/src/tests/CMakeLists.txt +++ b/document/src/tests/CMakeLists.txt @@ -54,9 +54,7 @@ vespa_add_executable(document_testrunner_app TEST document_documentconfig ) -# TODO: Test with a larger chunk size to parallelize test suite runs vespa_add_test( NAME document_testrunner_app - COMMAND python ${PROJECT_SOURCE_DIR}/cppunit-parallelize.py --chunks 1 $<TARGET_FILE:document_testrunner_app> - DEPENDS document_testrunner_app + COMMAND document_testrunner_app ) diff --git a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java index dc50d237ab7..be564098121 100644 --- a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java +++ b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java @@ -113,12 +113,6 @@ public class Flags { "Takes effect on next node agent tick (but does not clear existing failure reports)", HOSTNAME); - public static final UnboundBooleanFlag USE_SEPARATE_SERVICE_TYPE_FOR_LOGSERVER_CONTAINER = defineFeatureFlag( - "use-separate-service-type-for-logserver-container", true, - "Use separate service type for Logserver container, resulting in logserver container not being an application endpoint", - "Takes effect at redeployment", - APPLICATION_ID); - public static final UnboundBooleanFlag ENABLE_METRICS_PROXY_CONTAINER = defineFeatureFlag( "enable-metrics-proxy-container", false, "Start a container for metrics-proxy on every vespa node", diff --git a/logd/CMakeLists.txt b/logd/CMakeLists.txt index 9a5fdf32841..e16d3bd5179 100644 --- a/logd/CMakeLists.txt +++ b/logd/CMakeLists.txt @@ -17,6 +17,7 @@ vespa_define_module( src/tests/legacy_forwarder src/tests/proto_converter src/tests/rotate + src/tests/rpc_forwarder ) vespa_install_script(src/apps/retention/retention-enforcer.sh vespa-retention-enforcer sbin) diff --git a/logd/src/logd/CMakeLists.txt b/logd/src/logd/CMakeLists.txt index baf52f1d5d8..629c7b1637a 100644 --- a/logd/src/logd/CMakeLists.txt +++ b/logd/src/logd/CMakeLists.txt @@ -16,6 +16,7 @@ vespa_add_library(logd STATIC legacy_forwarder.cpp metrics.cpp proto_converter.cpp + rpc_forwarder.cpp state_reporter.cpp watcher.cpp ${logd_PROTOBUF_SRCS} diff --git a/logd/src/logd/exceptions.h b/logd/src/logd/exceptions.h index b2ff3516b69..82e8b570c3b 100644 --- a/logd/src/logd/exceptions.h +++ b/logd/src/logd/exceptions.h @@ -18,7 +18,12 @@ public: class ConnectionException : public MsgException { public: - ConnectionException(const char *s) : MsgException(s) {} + ConnectionException(const std::string& msg) : MsgException(msg) {} +}; + +class DecodeException : public MsgException { +public: + DecodeException(const std::string& msg) : MsgException(msg) {} }; class SigTermException : public MsgException diff --git a/logd/src/logd/forwarder.h b/logd/src/logd/forwarder.h index a0a1c5f1ea5..93cfdb3de9f 100644 --- a/logd/src/logd/forwarder.h +++ b/logd/src/logd/forwarder.h @@ -2,8 +2,15 @@ #pragma once +#include <vespa/log/log.h> +#include <map> +#include <string_view> + namespace logdemon { +// Mapping saying if a level should be forwarded or not +using ForwardMap = std::map<ns_log::Logger::LogLevel, bool>; + /** * Interface used to forward log lines to something. */ @@ -11,7 +18,8 @@ class Forwarder { public: virtual ~Forwarder() {} virtual void sendMode() = 0; - virtual void forwardLine(const char *line, const char *eol) = 0; + virtual void forwardLine(std::string_view log_line) = 0; + virtual void flush() = 0; virtual int badLines() const = 0; virtual void resetBadLines() = 0; }; diff --git a/logd/src/logd/legacy_forwarder.cpp b/logd/src/logd/legacy_forwarder.cpp index b8b93a03530..6b2f430d388 100644 --- a/logd/src/logd/legacy_forwarder.cpp +++ b/logd/src/logd/legacy_forwarder.cpp @@ -58,26 +58,24 @@ LegacyForwarder::sendMode() } void -LegacyForwarder::forwardLine(const char *line, const char *eol) +LegacyForwarder::forwardLine(std::string_view line) { - int linelen = eol - line; - assert(_logserverfd >= 0); - assert (linelen > 0); - assert (linelen < 1024*1024); - assert (line[linelen - 1] == '\n'); + assert (line.size() > 0); + assert (line.size() < 1024*1024); + assert (line[line.size() - 1] == '\n'); - if (parseline(line, eol)) { - forwardText(line, linelen); + if (parseLine(line)) { + forwardText(line.data(), line.size()); } } bool -LegacyForwarder::parseline(const char *linestart, const char *lineend) +LegacyForwarder::parseLine(std::string_view line) { LogMessage message; try { - message.parse_log_line(std::string_view(linestart, lineend - linestart)); + message.parse_log_line(line); } catch (BadLogLineException &e) { LOG(spam, "bad logline: %s", e.what()); ++_badLines; diff --git a/logd/src/logd/legacy_forwarder.h b/logd/src/logd/legacy_forwarder.h index 81a93ce1d50..db3bf84fd4f 100644 --- a/logd/src/logd/legacy_forwarder.h +++ b/logd/src/logd/legacy_forwarder.h @@ -2,15 +2,9 @@ #pragma once #include "forwarder.h" -#include <vespa/log/log.h> -#include <map> -#include <unordered_set> namespace logdemon { -// Mapping saying if a level should be forwarded or not -using ForwardMap = std::map<ns_log::Logger::LogLevel, bool>; - struct Metrics; /** @@ -29,12 +23,13 @@ private: ret[len] = '\0'; return ret; } - bool parseline(const char *linestart, const char *lineend); + bool parseLine(std::string_view line); public: LegacyForwarder(Metrics &metrics); ~LegacyForwarder(); void forwardText(const char *text, int len); - void forwardLine(const char *line, const char *eol) override; + void forwardLine(std::string_view line) override; + void flush() override {} void setForwardMap(const ForwardMap & forwardMap) { _forwardMap = forwardMap; } void setLogserverFD(int fd) { _logserverfd = fd; } int getLogserverFD() { return _logserverfd; } diff --git a/logd/src/logd/proto_converter.h b/logd/src/logd/proto_converter.h index 688648b99de..88749100736 100644 --- a/logd/src/logd/proto_converter.h +++ b/logd/src/logd/proto_converter.h @@ -1,5 +1,7 @@ // Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#pragma once + #include "log_protocol_proto.h" #include <vespa/log/log_message.h> #include <vector> @@ -11,6 +13,7 @@ namespace logdemon { */ struct ProtoConverter { using ProtoLogRequest = logserver::protocol::protobuf::LogRequest; + using ProtoLogResponse = logserver::protocol::protobuf::LogResponse; using ProtoLogMessage = logserver::protocol::protobuf::LogMessage; static void log_messages_to_proto(const std::vector<ns_log::LogMessage>& messages, ProtoLogRequest& proto); diff --git a/logd/src/logd/rpc_forwarder.cpp b/logd/src/logd/rpc_forwarder.cpp new file mode 100644 index 00000000000..e515f463db4 --- /dev/null +++ b/logd/src/logd/rpc_forwarder.cpp @@ -0,0 +1,151 @@ +// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include "exceptions.h" +#include "metrics.h" +#include "proto_converter.h" +#include "rpc_forwarder.h" +#include <vespa/log/exceptions.h> +#include <vespa/vespalib/util/buffer.h> +#include <vespa/vespalib/util/stringfmt.h> + +#include <vespa/log/log.h> +LOG_SETUP(".logd.rpc_forwarder"); + +using ns_log::BadLogLineException; +using ns_log::LogMessage; +using vespalib::make_string; + +namespace logdemon { + +RpcForwarder::RpcForwarder(Metrics& metrics, FRT_Supervisor& supervisor, + const vespalib::string &hostname, int rpc_port, + double rpc_timeout_secs, size_t max_messages_per_request) + : _metrics(metrics), + _connection_spec(make_string("tcp/%s:%d", hostname.c_str(), rpc_port)), + _rpc_timeout_secs(rpc_timeout_secs), + _max_messages_per_request(max_messages_per_request), + _target(), + _messages(), + _bad_lines(0), + _forward_filter() +{ + _target = supervisor.GetTarget(_connection_spec.c_str()); +} + +RpcForwarder::~RpcForwarder() +{ + _target->SubRef(); +} + +namespace { + +void +encode_log_request(const ProtoConverter::ProtoLogRequest& src, FRT_RPCRequest& dst) +{ + dst.SetMethodName("vespa.logserver.archiveLogMessages"); + auto buf = src.SerializeAsString(); + auto& params = *dst.GetParams(); + params.AddInt8(0); // '0' indicates no compression + params.AddInt32(buf.size()); + params.AddData(buf.data(), buf.size()); +} + +bool +decode_log_response(FRT_RPCRequest& src, ProtoConverter::ProtoLogResponse& dst) +{ + auto& values = *src.GetReturn(); + uint8_t encoding = values[0]._intval8; + assert(encoding == 0); // Not using compression + uint32_t uncompressed_size = values[1]._intval32; + (void) uncompressed_size; + return dst.ParseFromArray(values[2]._data._buf, values[2]._data._len); +} + +bool +should_forward_log_message(const LogMessage& message, const ForwardMap& filter) +{ + auto found = filter.find(message.level()); + if (found != filter.end()) { + return found->second; + } + return false; +} + +} + +void +RpcForwarder::forwardLine(std::string_view line) +{ + LogMessage message; + try { + message.parse_log_line(line); + } catch (BadLogLineException &e) { + LOG(spam, "Skipping bad logline: %s", e.what()); + ++_bad_lines; + return; + } + _metrics.countLine(ns_log::Logger::logLevelNames[message.level()], message.service()); + if (should_forward_log_message(message, _forward_filter)) { + _messages.push_back(std::move(message)); + if (_messages.size() == _max_messages_per_request) { + flush(); + } + } +} + +namespace { + +class GuardedRequest { +private: + FRT_RPCRequest* _request; +public: + GuardedRequest() + : _request(new FRT_RPCRequest()) + {} + ~GuardedRequest() { + _request->SubRef(); + } + FRT_RPCRequest& operator*() const { return *_request; } + FRT_RPCRequest* get() const { return _request; } + FRT_RPCRequest* operator->() const { return get(); } +}; + +} + +void +RpcForwarder::flush() +{ + if (_messages.empty()) { + return; + } + ProtoConverter::ProtoLogRequest proto_request; + ProtoConverter::log_messages_to_proto(_messages, proto_request); + GuardedRequest request; + encode_log_request(proto_request, *request); + _target->InvokeSync(request.get(), _rpc_timeout_secs); + if (!request->CheckReturnTypes("bix")) { + auto error_msg = make_string("Error in rpc reply from '%s': '%s'", + _connection_spec.c_str(), request->GetErrorMessage()); + throw ConnectionException(error_msg); + } + ProtoConverter::ProtoLogResponse proto_response; + if (!decode_log_response(*request, proto_response)) { + auto error_msg = make_string("Error during decoding of protobuf response from '%s'", _connection_spec.c_str()); + throw DecodeException(error_msg); + } + _messages.clear(); +} + +int +RpcForwarder::badLines() const +{ + return _bad_lines; +} + +void +RpcForwarder::resetBadLines() +{ + _bad_lines = 0; +} + +} diff --git a/logd/src/logd/rpc_forwarder.h b/logd/src/logd/rpc_forwarder.h new file mode 100644 index 00000000000..3212da08195 --- /dev/null +++ b/logd/src/logd/rpc_forwarder.h @@ -0,0 +1,45 @@ +// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#pragma once + +#include "forwarder.h" +#include "proto_converter.h" +#include <vespa/log/log_message.h> +#include <vespa/fnet/frt/frt.h> +#include <vector> + +namespace logdemon { + +struct Metrics; + +/** + * Implementation of the Forwarder interface that uses RPC to send protobuf encoded log messages to the logserver. + */ +class RpcForwarder : public Forwarder { +private: + Metrics& _metrics; + vespalib::string _connection_spec; + double _rpc_timeout_secs; + size_t _max_messages_per_request; + FRT_Target* _target; + std::vector<ns_log::LogMessage> _messages; + int _bad_lines; + ForwardMap _forward_filter; + +public: + RpcForwarder(Metrics& metrics, FRT_Supervisor& supervisor, + const vespalib::string& logserver_host, int logserver_rpc_port, + double rpc_timeout_secs, size_t max_messages_per_request); + ~RpcForwarder() override; + void set_forward_filter(const ForwardMap& forward_filter) { _forward_filter = forward_filter; } + + // Implements Forwarder + void sendMode() override {} + void forwardLine(std::string_view line) override; + void flush() override; + int badLines() const override; + void resetBadLines() override; +}; + +} + diff --git a/logd/src/logd/watcher.cpp b/logd/src/logd/watcher.cpp index a047c110f32..c505d2dd235 100644 --- a/logd/src/logd/watcher.cpp +++ b/logd/src/logd/watcher.cpp @@ -220,7 +220,7 @@ Watcher::watchfile() } while (nnl != nullptr && elapsed(tickStart) < 1) { ++nnl; - _forwarder.forwardLine(l, nnl); + _forwarder.forwardLine(std::string_view(l, (nnl - l))); ssize_t wsize = nnl - l; offset += wsize; l = nnl; diff --git a/logd/src/tests/legacy_forwarder/legacy_forwarder_test.cpp b/logd/src/tests/legacy_forwarder/legacy_forwarder_test.cpp index 3af35f9aa09..c6702e8bc67 100644 --- a/logd/src/tests/legacy_forwarder/legacy_forwarder_test.cpp +++ b/logd/src/tests/legacy_forwarder/legacy_forwarder_test.cpp @@ -40,13 +40,12 @@ struct ForwardFixture { } void verifyForward(bool doForward) { - const std::string & line(logLine); - forwarder.forwardLine(line.c_str(), line.c_str() + line.length()); + forwarder.forwardLine(logLine); fsync(fd); int rfd = open(fname.c_str(), O_RDONLY); char *buffer[2048]; ssize_t bytes = read(rfd, buffer, 2048); - ssize_t expected = doForward ? line.length() : 0; + ssize_t expected = doForward ? logLine.length() : 0; EXPECT_EQUAL(expected, bytes); close(rfd); } diff --git a/logd/src/tests/rpc_forwarder/CMakeLists.txt b/logd/src/tests/rpc_forwarder/CMakeLists.txt new file mode 100644 index 00000000000..66a30777b41 --- /dev/null +++ b/logd/src/tests/rpc_forwarder/CMakeLists.txt @@ -0,0 +1,9 @@ +# Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(logd_rpc_forwarder_test_app TEST + SOURCES + rpc_forwarder_test.cpp + DEPENDS + logd + gtest +) +vespa_add_test(NAME logd_rpc_forwarder_test_app COMMAND logd_rpc_forwarder_test_app) diff --git a/logd/src/tests/rpc_forwarder/rpc_forwarder_test.cpp b/logd/src/tests/rpc_forwarder/rpc_forwarder_test.cpp new file mode 100644 index 00000000000..30ca5e19d44 --- /dev/null +++ b/logd/src/tests/rpc_forwarder/rpc_forwarder_test.cpp @@ -0,0 +1,248 @@ +// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <logd/exceptions.h> +#include <logd/metrics.h> +#include <logd/rpc_forwarder.h> +#include <vespa/vespalib/gtest/gtest.h> +#include <vespa/vespalib/metrics/dummy_metrics_manager.h> + +using namespace logdemon; +using vespalib::metrics::DummyMetricsManager; + +void +encode_log_response(const ProtoConverter::ProtoLogResponse& src, FRT_Values& dst) +{ + auto buf = src.SerializeAsString(); + dst.AddInt8(0); + dst.AddInt32(buf.size()); + dst.AddData(buf.data(), buf.size()); +} + +bool +decode_log_request(FRT_Values& src, ProtoConverter::ProtoLogRequest& dst) +{ + uint8_t encoding = src[0]._intval8; + assert(encoding == 0); + uint32_t uncompressed_size = src[1]._intval32; + assert(uncompressed_size == src[2]._data._len); + return dst.ParseFromArray(src[2]._data._buf, src[2]._data._len); +} + +std::string garbage("garbage"); + +struct RpcServer : public FRT_Invokable { + FRT_Supervisor supervisor; + int request_count; + std::vector<std::string> messages; + bool reply_with_error; + bool reply_with_proto_response; + +public: + RpcServer() + : supervisor(), + request_count(0), + messages(), + reply_with_error(false), + reply_with_proto_response(true) + { + supervisor.Listen(0); + FRT_ReflectionBuilder builder(&supervisor); + builder.DefineMethod("vespa.logserver.archiveLogMessages", "bix", "bix", + FRT_METHOD(RpcServer::rpc_archive_log_messages), this); + supervisor.Start(); + } + ~RpcServer() { + supervisor.ShutDown(true); + } + int get_listen_port() { + return supervisor.GetListenPort(); + } + void rpc_archive_log_messages(FRT_RPCRequest* request) { + ProtoConverter::ProtoLogRequest proto_request; + ASSERT_TRUE(decode_log_request(*request->GetParams(), proto_request)); + ++request_count; + for (const auto& message : proto_request.log_messages()) { + messages.push_back(message.payload()); + } + if (reply_with_error) { + request->SetError(123, "This is a server error"); + return; + } + if (reply_with_proto_response) { + ProtoConverter::ProtoLogResponse proto_response; + encode_log_response(proto_response, *request->GetReturn()); + } else { + auto& dst = *request->GetReturn(); + dst.AddInt8(0); + dst.AddInt32(garbage.size()); + dst.AddData(garbage.data(), garbage.size()); + } + } +}; + +std::string +make_log_line(const std::string& level, const std::string& payload) +{ + return "1234.5678\tmy_host\t10/20\tmy_service\tmy_component\t" + level + "\t" + payload; +} + +struct MockMetricsManager : public DummyMetricsManager { + int add_count; + MockMetricsManager() : DummyMetricsManager(), add_count(0) {} + void add(Counter::Increment) override { + ++add_count; + } +}; + +class ClientSupervisor { +private: + FRT_Supervisor _supervisor; +public: + ClientSupervisor() + : _supervisor() + { + _supervisor.Start(); + } + ~ClientSupervisor() { + _supervisor.ShutDown(true); + } + FRT_Supervisor& get() { return _supervisor; } + +}; + +struct RpcForwarderTest : public ::testing::Test { + RpcServer server; + std::shared_ptr<MockMetricsManager> metrics_mgr; + Metrics metrics; + ClientSupervisor supervisor; + RpcForwarder forwarder; + RpcForwarderTest() + : server(), + metrics_mgr(std::make_shared<MockMetricsManager>()), + metrics(metrics_mgr), + forwarder(metrics, supervisor.get(), "localhost", server.get_listen_port(), 60.0, 3) + { + ForwardMap forward_filter; + forward_filter[ns_log::Logger::error] = true; + forward_filter[ns_log::Logger::warning] = false; + forward_filter[ns_log::Logger::info] = true; + // all other log levels are implicit false + forwarder.set_forward_filter(forward_filter); + } + void forward_line(const std::string& payload) { + forwarder.forwardLine(make_log_line("info", payload)); + } + void forward_line(const std::string& level, const std::string& payload) { + forwarder.forwardLine(make_log_line(level, payload)); + } + void forward_bad_line() { + forwarder.forwardLine("badline"); + } + void flush() { + forwarder.flush(); + } + void expect_messages() { + expect_messages(0, {}); + } + void expect_messages(int exp_request_count, const std::vector<std::string>& exp_messages) { + EXPECT_EQ(exp_request_count, server.request_count); + EXPECT_EQ(exp_messages, server.messages); + } +}; + +TEST_F(RpcForwarderTest, does_not_send_rpc_with_no_log_messages) +{ + expect_messages(); + flush(); + expect_messages(); +} + +TEST_F(RpcForwarderTest, can_send_rpc_with_single_log_message) +{ + forward_line("a"); + expect_messages(); + flush(); + expect_messages(1, {"a"}); +} + +TEST_F(RpcForwarderTest, can_send_rpc_with_multiple_log_messages) +{ + forward_line("a"); + forward_line("b"); + expect_messages(); + flush(); + expect_messages(1, {"a", "b"}); +} + +TEST_F(RpcForwarderTest, automatically_sends_rpc_when_max_messages_limit_is_reached) +{ + forward_line("a"); + forward_line("b"); + expect_messages(); + forward_line("c"); + expect_messages(1, {"a", "b", "c"}); + forward_line("d"); + expect_messages(1, {"a", "b", "c"}); + forward_line("e"); + expect_messages(1, {"a", "b", "c"}); + forward_line("f"); + expect_messages(2, {"a", "b", "c", "d", "e", "f"}); +} + +TEST_F(RpcForwarderTest, bad_log_lines_are_counted_but_not_sent) +{ + forward_line("a"); + forward_bad_line(); + EXPECT_EQ(1, forwarder.badLines()); + flush(); + expect_messages(1, {"a"}); +} + +TEST_F(RpcForwarderTest, bad_log_lines_count_can_be_reset) +{ + forward_bad_line(); + EXPECT_EQ(1, forwarder.badLines()); + forwarder.resetBadLines(); + EXPECT_EQ(0, forwarder.badLines()); +} + +TEST_F(RpcForwarderTest, metrics_are_updated_for_each_log_message) +{ + forward_line("a"); + EXPECT_EQ(1, metrics_mgr->add_count); + forward_line("b"); + EXPECT_EQ(2, metrics_mgr->add_count); +} + +TEST_F(RpcForwarderTest, log_messages_are_filtered_on_log_level) +{ + forward_line("fatal", "a"); + forward_line("error", "b"); + forward_line("warning", "c"); + forward_line("config", "d"); + forward_line("info", "e"); + forward_line("event", "f"); + forward_line("debug", "g"); + forward_line("spam", "h"); + forward_line("null", "i"); + flush(); + expect_messages(1, {"b", "e"}); + EXPECT_EQ(9, metrics_mgr->add_count); +} + +TEST_F(RpcForwarderTest, throws_when_rpc_reply_contains_errors) +{ + server.reply_with_error = true; + forward_line("a"); + EXPECT_THROW(flush(), logdemon::ConnectionException); +} + +TEST_F(RpcForwarderTest, throws_when_rpc_reply_does_not_contain_proto_response) +{ + server.reply_with_proto_response = false; + forward_line("a"); + EXPECT_THROW(flush(), logdemon::DecodeException); +} + +GTEST_MAIN_RUN_ALL_TESTS() + diff --git a/metrics/src/tests/CMakeLists.txt b/metrics/src/tests/CMakeLists.txt index afb56757bdd..2c69b83d4b7 100644 --- a/metrics/src/tests/CMakeLists.txt +++ b/metrics/src/tests/CMakeLists.txt @@ -38,6 +38,5 @@ vespa_add_executable(metrics_testrunner_app TEST # TODO: Test with a larger chunk size to parallelize test suite runs vespa_add_test( NAME metrics_testrunner_app - COMMAND python ${PROJECT_SOURCE_DIR}/cppunit-parallelize.py --chunks 1 $<TARGET_FILE:metrics_testrunner_app> - DEPENDS metrics_testrunner_app + COMMAND metrics_testrunner_app ) diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/identity/AthenzCredentialsMaintainer.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/identity/AthenzCredentialsMaintainer.java index abac16d242e..4fe0f420f05 100644 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/identity/AthenzCredentialsMaintainer.java +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/identity/AthenzCredentialsMaintainer.java @@ -17,8 +17,8 @@ import com.yahoo.vespa.athenz.identity.ServiceIdentityProvider; import com.yahoo.vespa.athenz.identityprovider.api.EntityBindingsMapper; import com.yahoo.vespa.athenz.identityprovider.api.IdentityDocumentClient; import com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument; -import com.yahoo.vespa.athenz.identityprovider.client.DefaultIdentityDocumentClient; import com.yahoo.vespa.athenz.identityprovider.client.CsrGenerator; +import com.yahoo.vespa.athenz.identityprovider.client.DefaultIdentityDocumentClient; import com.yahoo.vespa.athenz.tls.AthenzIdentityVerifier; import com.yahoo.vespa.athenz.utils.SiaUtils; import com.yahoo.vespa.hosted.dockerapi.ContainerName; @@ -51,7 +51,7 @@ import static java.util.Collections.singleton; * * @author bjorncs */ -public class AthenzCredentialsMaintainer { +public class AthenzCredentialsMaintainer implements CredentialsMaintainer { private static final Logger logger = Logger.getLogger(AthenzCredentialsMaintainer.class.getName()); @@ -89,7 +89,7 @@ public class AthenzCredentialsMaintainer { this.clock = Clock.systemUTC(); } - public void converge(NodeAgentContext context) { + public boolean converge(NodeAgentContext context) { try { context.log(logger, LogLevel.DEBUG, "Checking certificate"); Path containerSiaDirectory = context.pathOnHostFromPathInNode(CONTAINER_SIA_DIRECTORY); @@ -102,7 +102,7 @@ public class AthenzCredentialsMaintainer { Files.createDirectories(certificateFile.getParent()); Files.createDirectories(identityDocumentFile.getParent()); registerIdentity(context, privateKeyFile, certificateFile, identityDocumentFile); - return; + return true; } X509Certificate certificate = readCertificateFromFile(certificateFile); @@ -111,7 +111,7 @@ public class AthenzCredentialsMaintainer { if (isCertificateExpired(expiry, now)) { context.log(logger, "Certificate has expired (expiry=%s)", expiry.toString()); registerIdentity(context, privateKeyFile, certificateFile, identityDocumentFile); - return; + return true; } Duration age = Duration.between(certificate.getNotBefore().toInstant(), now); @@ -121,14 +121,15 @@ public class AthenzCredentialsMaintainer { context.log(logger, LogLevel.WARNING, String.format( "Skipping refresh attempt as last refresh was on %s (less than %s ago)", lastRefreshAttempt.get(context.containerName()).toString(), REFRESH_BACKOFF.toString())); - return; + return false; } else { lastRefreshAttempt.put(context.containerName(), now); refreshIdentity(context, privateKeyFile, certificateFile, identityDocumentFile); - return; + return true; } } context.log(logger, LogLevel.DEBUG, "Certificate is still valid"); + return false; } catch (IOException e) { throw new UncheckedIOException(e); } diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/identity/CredentialsMaintainer.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/identity/CredentialsMaintainer.java new file mode 100644 index 00000000000..58c3585a48f --- /dev/null +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/identity/CredentialsMaintainer.java @@ -0,0 +1,21 @@ +// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.node.admin.maintenance.identity; + +import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentContext; + +/** + * A maintainer that is responsible for providing and refreshing credentials for a container. + * + * @author freva + */ +public interface CredentialsMaintainer { + + /** + * Creates/refreshes credentials for the given NodeAgentContext. Called for every NodeAgent tick. + * @return false if already converged, i.e. was a no-op. + */ + boolean converge(NodeAgentContext context); + + /** Remove any existing credentials. This method is called just before container data is archived. */ + void clearCredentials(NodeAgentContext context); +} diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentImpl.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentImpl.java index fc4162a0f73..57e504a6ffd 100644 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentImpl.java +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentImpl.java @@ -27,7 +27,7 @@ import com.yahoo.vespa.hosted.node.admin.configserver.orchestrator.OrchestratorE import com.yahoo.vespa.hosted.node.admin.docker.DockerOperations; import com.yahoo.vespa.hosted.node.admin.maintenance.StorageMaintainer; import com.yahoo.vespa.hosted.node.admin.maintenance.acl.AclMaintainer; -import com.yahoo.vespa.hosted.node.admin.maintenance.identity.AthenzCredentialsMaintainer; +import com.yahoo.vespa.hosted.node.admin.maintenance.identity.CredentialsMaintainer; import com.yahoo.vespa.hosted.node.admin.nodeadmin.ConvergenceException; import com.yahoo.vespa.hosted.node.admin.util.SecretAgentCheckConfig; @@ -63,7 +63,7 @@ public class NodeAgentImpl implements NodeAgent { private final Orchestrator orchestrator; private final DockerOperations dockerOperations; private final StorageMaintainer storageMaintainer; - private final Optional<AthenzCredentialsMaintainer> athenzCredentialsMaintainer; + private final Optional<CredentialsMaintainer> credentialsMaintainer; private final Optional<AclMaintainer> aclMaintainer; private final Optional<HealthChecker> healthChecker; @@ -105,7 +105,7 @@ public class NodeAgentImpl implements NodeAgent { final DockerOperations dockerOperations, final StorageMaintainer storageMaintainer, final FlagSource flagSource, - final Optional<AthenzCredentialsMaintainer> athenzCredentialsMaintainer, + final Optional<CredentialsMaintainer> credentialsMaintainer, final Optional<AclMaintainer> aclMaintainer, final Optional<HealthChecker> healthChecker) { this.contextSupplier = contextSupplier; @@ -113,7 +113,7 @@ public class NodeAgentImpl implements NodeAgent { this.orchestrator = orchestrator; this.dockerOperations = dockerOperations; this.storageMaintainer = storageMaintainer; - this.athenzCredentialsMaintainer = athenzCredentialsMaintainer; + this.credentialsMaintainer = credentialsMaintainer; this.aclMaintainer = aclMaintainer; this.healthChecker = healthChecker; @@ -443,7 +443,7 @@ public class NodeAgentImpl implements NodeAgent { return; } container = removeContainerIfNeededUpdateContainerState(context, container); - athenzCredentialsMaintainer.ifPresent(maintainer -> maintainer.converge(context)); + credentialsMaintainer.ifPresent(maintainer -> maintainer.converge(context)); if (! container.isPresent()) { containerState = STARTING; startContainer(context); @@ -481,7 +481,7 @@ public class NodeAgentImpl implements NodeAgent { case dirty: removeContainerIfNeededUpdateContainerState(context, container); context.log(logger, "State is " + node.getState() + ", will delete application storage and mark node as ready"); - athenzCredentialsMaintainer.ifPresent(maintainer -> maintainer.clearCredentials(context)); + credentialsMaintainer.ifPresent(maintainer -> maintainer.clearCredentials(context)); storageMaintainer.archiveNodeStorage(context); updateNodeRepoWithCurrentAttributes(context); nodeRepository.setNodeState(context.hostname().value(), NodeState.ready); diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentImplTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentImplTest.java index 15786f2ed34..dcbbaf792a8 100644 --- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentImplTest.java +++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentImplTest.java @@ -25,7 +25,7 @@ import com.yahoo.vespa.hosted.node.admin.configserver.orchestrator.OrchestratorE import com.yahoo.vespa.hosted.node.admin.docker.DockerOperations; import com.yahoo.vespa.hosted.node.admin.maintenance.StorageMaintainer; import com.yahoo.vespa.hosted.node.admin.maintenance.acl.AclMaintainer; -import com.yahoo.vespa.hosted.node.admin.maintenance.identity.AthenzCredentialsMaintainer; +import com.yahoo.vespa.hosted.node.admin.maintenance.identity.CredentialsMaintainer; import com.yahoo.vespa.hosted.node.admin.nodeadmin.ConvergenceException; import org.junit.Test; import org.mockito.InOrder; @@ -84,7 +84,7 @@ public class NodeAgentImplTest { private final HealthChecker healthChecker = mock(HealthChecker.class); private final ContainerStats emptyContainerStats = new ContainerStats(Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap()); - private final AthenzCredentialsMaintainer athenzCredentialsMaintainer = mock(AthenzCredentialsMaintainer.class); + private final CredentialsMaintainer credentialsMaintainer = mock(CredentialsMaintainer.class); private final InMemoryFlagSource flagSource = new InMemoryFlagSource(); @@ -763,7 +763,7 @@ public class NodeAgentImplTest { doNothing().when(storageMaintainer).writeMetricsConfig(any()); return new NodeAgentImpl(contextSupplier, nodeRepository, orchestrator, dockerOperations, - storageMaintainer, flagSource, Optional.of(athenzCredentialsMaintainer), Optional.of(aclMaintainer), + storageMaintainer, flagSource, Optional.of(credentialsMaintainer), Optional.of(aclMaintainer), Optional.of(healthChecker)); } diff --git a/persistence/src/tests/CMakeLists.txt b/persistence/src/tests/CMakeLists.txt index d6f6db07f9d..396e9f99655 100644 --- a/persistence/src/tests/CMakeLists.txt +++ b/persistence/src/tests/CMakeLists.txt @@ -7,9 +7,7 @@ vespa_add_executable(persistence_testrunner_app TEST persistence_testspi ) -# TODO: Test with a larger chunk size to parallelize test suite runs vespa_add_test( NAME persistence_testrunner_app - COMMAND python ${PROJECT_SOURCE_DIR}/cppunit-parallelize.py --chunks 1 $<TARGET_FILE:persistence_testrunner_app> - DEPENDS persistence_testrunner_app + COMMAND persistence_testrunner_app ) diff --git a/searchlib/src/vespa/searchlib/attribute/attribute_blueprint_factory.cpp b/searchlib/src/vespa/searchlib/attribute/attribute_blueprint_factory.cpp index f09f48363db..07689e5ffec 100644 --- a/searchlib/src/vespa/searchlib/attribute/attribute_blueprint_factory.cpp +++ b/searchlib/src/vespa/searchlib/attribute/attribute_blueprint_factory.cpp @@ -115,6 +115,13 @@ public: return _search_context->createIterator(tfmda[0], strict); } + SearchIterator::UP + createSearch(fef::MatchData &md, bool strict) const override { + const State &state = getState(); + assert(state.numFields() == 1); + return _search_context->createIterator(state.field(0).resolve(md), strict); + } + void fetchPostings(bool strict) override { _search_context->fetchPostings(strict); diff --git a/searchlib/src/vespa/searchlib/queryeval/weighted_set_term_blueprint.cpp b/searchlib/src/vespa/searchlib/queryeval/weighted_set_term_blueprint.cpp index fc68c48a247..cec72129475 100644 --- a/searchlib/src/vespa/searchlib/queryeval/weighted_set_term_blueprint.cpp +++ b/searchlib/src/vespa/searchlib/queryeval/weighted_set_term_blueprint.cpp @@ -44,8 +44,7 @@ WeightedSetTermBlueprint::addTerm(Blueprint::UP term, int32_t weight) SearchIterator::UP -WeightedSetTermBlueprint::createLeafSearch(const search::fef::TermFieldMatchDataArray &tfmda, - bool) const +WeightedSetTermBlueprint::createLeafSearch(const fef::TermFieldMatchDataArray &tfmda, bool) const { assert(tfmda.size() == 1); fef::MatchData::UP md = _layout.createMatchData(); diff --git a/staging_vespalib/src/vespa/vespalib/metrics/dummy_metrics_manager.h b/staging_vespalib/src/vespa/vespalib/metrics/dummy_metrics_manager.h index e4ca1e0ec49..6aeb1137732 100644 --- a/staging_vespalib/src/vespa/vespalib/metrics/dummy_metrics_manager.h +++ b/staging_vespalib/src/vespa/vespalib/metrics/dummy_metrics_manager.h @@ -20,7 +20,7 @@ namespace metrics { **/ class DummyMetricsManager : public MetricsManager { -private: +protected: DummyMetricsManager() {} public: ~DummyMetricsManager(); diff --git a/storageapi/src/tests/CMakeLists.txt b/storageapi/src/tests/CMakeLists.txt index 2045cca89e1..ebbf3b8357a 100644 --- a/storageapi/src/tests/CMakeLists.txt +++ b/storageapi/src/tests/CMakeLists.txt @@ -26,9 +26,7 @@ vespa_add_executable(storageapi_testrunner_app TEST storageapi ) -# TODO: Test with a larger chunk size to parallelize test suite runs vespa_add_test( NAME storageapi_testrunner_app - COMMAND python ${PROJECT_SOURCE_DIR}/cppunit-parallelize.py --chunks 1 $<TARGET_FILE:storageapi_testrunner_app> - DEPENDS storageapi_testrunner_app + COMMAND storageapi_testrunner_app ) diff --git a/storageframework/src/tests/CMakeLists.txt b/storageframework/src/tests/CMakeLists.txt index 49c25a275f2..aa4be0783fa 100644 --- a/storageframework/src/tests/CMakeLists.txt +++ b/storageframework/src/tests/CMakeLists.txt @@ -24,9 +24,7 @@ vespa_add_executable(storageframework_testrunner_app TEST storageframework_testthread ) -# TODO: Test with a larger chunk size to parallelize test suite runs vespa_add_test( NAME storageframework_testrunner_app - COMMAND python ${PROJECT_SOURCE_DIR}/cppunit-parallelize.py --chunks 1 $<TARGET_FILE:storageframework_testrunner_app> - DEPENDS storageframework_testrunner_app + COMMAND storageframework_testrunner_app ) diff --git a/storageserver/src/tests/CMakeLists.txt b/storageserver/src/tests/CMakeLists.txt index a78aea8aea3..21412e4bc33 100644 --- a/storageserver/src/tests/CMakeLists.txt +++ b/storageserver/src/tests/CMakeLists.txt @@ -11,9 +11,7 @@ vespa_add_executable(storageserver_testrunner_app TEST searchlib_searchlib_uca ) -# TODO: Test with a larger chunk size to parallelize test suite runs vespa_add_test( NAME storageserver_testrunner_app - COMMAND python ${PROJECT_SOURCE_DIR}/cppunit-parallelize.py --chunks 1 $<TARGET_FILE:storageserver_testrunner_app> - DEPENDS storageserver_testrunner_app + COMMAND storageserver_testrunner_app ) diff --git a/vdslib/src/tests/CMakeLists.txt b/vdslib/src/tests/CMakeLists.txt index e1853ab05bd..1fdc45a3163 100644 --- a/vdslib/src/tests/CMakeLists.txt +++ b/vdslib/src/tests/CMakeLists.txt @@ -29,6 +29,5 @@ vespa_add_executable(vdslib_testrunner_app TEST # TODO: Test with a larger chunk size to parallelize test suite runs vespa_add_test( NAME vdslib_testrunner_app - COMMAND python ${PROJECT_SOURCE_DIR}/cppunit-parallelize.py --chunks 1 $<TARGET_FILE:vdslib_testrunner_app> - DEPENDS vdslib_testrunner_app + COMMAND vdslib_testrunner_app ) diff --git a/vdstestlib/src/tests/cppunit/CMakeLists.txt b/vdstestlib/src/tests/cppunit/CMakeLists.txt index 5cb577ce70e..1b8e857fe30 100644 --- a/vdstestlib/src/tests/cppunit/CMakeLists.txt +++ b/vdstestlib/src/tests/cppunit/CMakeLists.txt @@ -7,10 +7,8 @@ vespa_add_executable(vdstestlib_testrunner_app TEST vdstestlib ) -# TODO: Test with a larger chunk size to parallelize test suite runs vespa_add_test( NAME vdstestlib_testrunner_app NO_VALGRIND - COMMAND python ${PROJECT_SOURCE_DIR}/cppunit-parallelize.py --chunks 1 $<TARGET_FILE:vdstestlib_testrunner_app> - DEPENDS vdstestlib_testrunner_app + COMMAND vdstestlib_testrunner_app ) diff --git a/yolean/src/main/java/com/yahoo/yolean/concurrent/ConcurrentResourcePool.java b/yolean/src/main/java/com/yahoo/yolean/concurrent/ConcurrentResourcePool.java index 6f5fb591ba6..24d4cfe4318 100644 --- a/yolean/src/main/java/com/yahoo/yolean/concurrent/ConcurrentResourcePool.java +++ b/yolean/src/main/java/com/yahoo/yolean/concurrent/ConcurrentResourcePool.java @@ -1,8 +1,6 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.yolean.concurrent; -import com.yahoo.yolean.concurrent.ResourceFactory; - import java.util.Iterator; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; diff --git a/yolean/src/main/java/com/yahoo/yolean/concurrent/ResourcePool.java b/yolean/src/main/java/com/yahoo/yolean/concurrent/ResourcePool.java index 62d5d749604..40c5ca3b6c2 100644 --- a/yolean/src/main/java/com/yahoo/yolean/concurrent/ResourcePool.java +++ b/yolean/src/main/java/com/yahoo/yolean/concurrent/ResourcePool.java @@ -1,8 +1,6 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.yolean.concurrent; -import com.yahoo.yolean.concurrent.ResourceFactory; - import java.util.ArrayDeque; import java.util.Deque; import java.util.Iterator; |