aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHenrik <henrik.hoiness@online.no>2018-08-06 15:29:18 +0200
committerHenrik <henrik.hoiness@online.no>2018-08-06 15:29:18 +0200
commita9ce32c11684e612d5bf68686ee21414677d01ee (patch)
treefe15f2cba213c33cc94e2aaba1cb866ebdbbd641
parentc31c1eee87a00774f34e985b70568af0eea0c82b (diff)
parent2764585ef3ab81e74af7aa7ca2709c6c4d8046a6 (diff)
Solve conflicting files
-rw-r--r--CMakeLists.txt2
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGenerator.java57
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGeneratorTest.java2
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidatorTest.java51
-rw-r--r--container-search/src/main/java/com/yahoo/search/Query.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/handler/SearchHandler.java19
-rw-r--r--dist/vespa.spec10
-rw-r--r--document/src/tests/fieldpathupdatetestcase.cpp13
-rw-r--r--document/src/vespa/document/update/fieldpathupdate.cpp12
-rw-r--r--functions.cmake8
-rw-r--r--node-admin/src/main/application/services.xml4
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/component/ConfigServerInfo.java6
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/component/TaskContext.java8
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/ConfigServerApiImpl.java43
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/HttpException.java50
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/StorageMaintainer.java7
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/StoredInteger.java58
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/process/ChildProcessException.java2
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/yum/Yum.java6
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/RebootTest.java4
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminImplTest.java5
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/file/MakeDirectoryTest.java2
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/process/CommandLineTest.java1
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/yum/YumTest.java25
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/ApplicationMaintainer.java56
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainer.java35
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockDeployer.java19
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainerTest.java136
-rw-r--r--searchcore/CMakeLists.txt1
-rw-r--r--searchcore/src/tests/proton/attribute/attribute_directory/attribute_directory_test.cpp4
-rw-r--r--searchcore/src/tests/proton/proton_disk_layout/CMakeLists.txt9
-rw-r--r--searchcore/src/tests/proton/proton_disk_layout/proton_disk_layout_test.cpp178
-rw-r--r--searchcore/src/tests/proton/reprocessing/attribute_reprocessing_initializer/attribute_reprocessing_initializer_test.cpp2
-rw-r--r--searchcore/src/vespa/searchcore/proton/attribute/attribute_directory.cpp6
-rw-r--r--searchcore/src/vespa/searchcore/proton/attribute/attributedisklayout.cpp1
-rw-r--r--searchcore/src/vespa/searchcore/proton/index/index_manager_initializer.cpp1
-rw-r--r--searchcore/src/vespa/searchcore/proton/metrics/trans_log_server_metrics.cpp1
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/fileconfigmanager.cpp4
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/proton_disk_layout.cpp94
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/storeonlydocsubdb.cpp1
-rw-r--r--searchcorespi/src/vespa/searchcorespi/index/diskindexcleaner.cpp21
-rw-r--r--searchcorespi/src/vespa/searchcorespi/index/indexmaintainer.cpp2
-rw-r--r--searchcorespi/src/vespa/searchcorespi/index/indexwriteutilities.cpp8
-rw-r--r--searchlib/src/vespa/searchlib/common/indexmetainfo.cpp3
-rw-r--r--searchlib/src/vespa/searchlib/diskindex/fusion.cpp1
-rw-r--r--searchlib/src/vespa/searchlib/diskindex/indexbuilder.cpp1
-rw-r--r--searchlib/src/vespa/searchlib/transactionlog/domain.cpp4
-rw-r--r--searchlib/src/vespa/searchlib/transactionlog/translogserver.cpp40
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/EntityBindingsMapper.java62
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/IdentityDocument.java52
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/SignedIdentityDocument.java29
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/IdentityDocumentEntity.java73
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/SignedIdentityDocumentEntity.java46
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/VespaUniqueInstanceIdEntity.java97
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/utils/SiaUtils.java24
-rw-r--r--vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSignerTest.java5
-rw-r--r--vespa-athenz/src/test/java/com/yahoo/vespa/athenz/utils/SiaUtilsTest.java40
-rwxr-xr-xvespabase/src/common-env.sh13
-rw-r--r--vespalib/src/vespa/vespalib/io/fileutil.cpp9
-rw-r--r--vespalib/src/vespa/vespalib/io/fileutil.h10
60 files changed, 856 insertions, 629 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6c557179750..3961fd2a4ef 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -20,6 +20,8 @@ include(build_settings.cmake)
# Enable CTest unit testing
enable_testing()
+vespa_install_data(valgrind-suppressions.txt etc/vespa)
+
# Include vespa config definitions in every target
include_directories(BEFORE ${CMAKE_BINARY_DIR}/configdefinitions/src)
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGenerator.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGenerator.java
index 0abbb5a64f5..950d2df9532 100644
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGenerator.java
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGenerator.java
@@ -5,7 +5,6 @@ import com.google.inject.Inject;
import com.yahoo.config.provision.Zone;
import com.yahoo.net.HostName;
import com.yahoo.vespa.athenz.api.AthenzService;
-import com.yahoo.vespa.athenz.identityprovider.api.IdentityDocument;
import com.yahoo.vespa.athenz.identityprovider.api.IdentityType;
import com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument;
import com.yahoo.vespa.athenz.identityprovider.api.VespaUniqueInstanceId;
@@ -17,7 +16,6 @@ import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeRepository;
import com.yahoo.vespa.hosted.provision.node.Allocation;
-import java.net.URI;
import java.security.PrivateKey;
import java.time.Instant;
import java.util.HashSet;
@@ -49,57 +47,46 @@ public class IdentityDocumentGenerator {
}
public SignedIdentityDocument generateSignedIdentityDocument(String hostname, IdentityType identityType) {
- Node node = nodeRepository.getNode(hostname).orElseThrow(() -> new RuntimeException("Unable to find node " + hostname));
try {
- IdentityDocument identityDocument = generateIdDocument(node, identityType);
+ Node node = nodeRepository.getNode(hostname).orElseThrow(() -> new RuntimeException("Unable to find node " + hostname));
+ Allocation allocation = node.allocation().orElseThrow(() -> new RuntimeException("No allocation for node " + node.hostname()));
+ VespaUniqueInstanceId providerUniqueId = new VespaUniqueInstanceId(
+ allocation.membership().index(),
+ allocation.membership().cluster().id().value(),
+ allocation.owner().instance().value(),
+ allocation.owner().application().value(),
+ allocation.owner().tenant().value(),
+ zone.region().value(),
+ zone.environment().value(),
+ identityType);
+
+ Set<String> ips = new HashSet<>(node.ipAddresses());
PrivateKey privateKey = keyProvider.getPrivateKey(zoneConfig.secretVersion());
AthenzService providerService = new AthenzService(zoneConfig.domain(), zoneConfig.serviceName());
+ String configServerHostname = HostName.getLocalhost();
+ Instant createdAt = Instant.now();
String signature = signer.generateSignature(
- identityDocument.providerUniqueId(), providerService, identityDocument.configServerHostname(),
- identityDocument.instanceHostname(), identityDocument.createdAt(), identityDocument.ipAddresses(), identityType, privateKey);
+ providerUniqueId, providerService, configServerHostname,
+ node.hostname(), createdAt, ips, identityType, privateKey);
return new SignedIdentityDocument(
- identityDocument,
signature,
SignedIdentityDocument.DEFAULT_KEY_VERSION,
- identityDocument.providerUniqueId(),
- toZoneDnsSuffix(zone, zoneConfig.certDnsSuffix()),
+ providerUniqueId,
providerService,
- URI.create(zoneConfig.ztsUrl()),
SignedIdentityDocument.DEFAULT_DOCUMENT_VERSION,
- identityDocument.configServerHostname(),
- identityDocument.instanceHostname(),
- identityDocument.createdAt(),
- identityDocument.ipAddresses(),
+ configServerHostname,
+ node.hostname(),
+ createdAt,
+ ips,
identityType);
} catch (Exception e) {
throw new RuntimeException("Exception generating identity document: " + e.getMessage(), e);
}
}
- private IdentityDocument generateIdDocument(Node node, IdentityType identityType) {
- Allocation allocation = node.allocation().orElseThrow(() -> new RuntimeException("No allocation for node " + node.hostname()));
- VespaUniqueInstanceId providerUniqueId = new VespaUniqueInstanceId(
- allocation.membership().index(),
- allocation.membership().cluster().id().value(),
- allocation.owner().instance().value(),
- allocation.owner().application().value(),
- allocation.owner().tenant().value(),
- zone.region().value(),
- zone.environment().value(),
- identityType);
-
- Set<String> ips = new HashSet<>(node.ipAddresses());
- return new IdentityDocument(
- providerUniqueId,
- HostName.getLocalhost(),
- node.hostname(),
- Instant.now(),
- ips);
- }
-
private static String toZoneDnsSuffix(Zone zone, String dnsSuffix) {
return zone.environment().value() + "-" + zone.region().value() + "." + dnsSuffix;
}
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGeneratorTest.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGeneratorTest.java
index a1839ec62a2..48e85f6047e 100644
--- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGeneratorTest.java
+++ b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGeneratorTest.java
@@ -87,8 +87,6 @@ public class IdentityDocumentGeneratorTest {
String environment = "dev";
String region = "us-north-1";
- String expectedZoneDnsSuffix = environment + "-" + region + "." + dnsSuffix;
- assertEquals(expectedZoneDnsSuffix, signedIdentityDocument.dnsSuffix());
VespaUniqueInstanceId expectedProviderUniqueId =
new VespaUniqueInstanceId(0, "default", "default", "application", "tenant", region, environment, IdentityType.TENANT);
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidatorTest.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidatorTest.java
index 04c4d4da51a..56777325231 100644
--- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidatorTest.java
+++ b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidatorTest.java
@@ -1,8 +1,6 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.athenz.instanceproviderservice.instanceconfirmation;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.common.collect.ImmutableSet;
import com.yahoo.config.model.api.ApplicationInfo;
import com.yahoo.config.model.api.HostInfo;
import com.yahoo.config.model.api.Model;
@@ -10,18 +8,8 @@ import com.yahoo.config.model.api.ServiceInfo;
import com.yahoo.config.model.api.SuperModel;
import com.yahoo.config.model.api.SuperModelProvider;
import com.yahoo.config.provision.ApplicationId;
-import com.yahoo.vespa.athenz.identityprovider.api.EntityBindingsMapper;
-import com.yahoo.vespa.athenz.identityprovider.api.bindings.IdentityDocumentEntity;
-import com.yahoo.vespa.athenz.identityprovider.api.bindings.SignedIdentityDocumentEntity;
-import com.yahoo.vespa.athenz.identityprovider.api.bindings.VespaUniqueInstanceIdEntity;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.Utils;
import org.junit.Test;
-import java.net.URI;
-import java.security.PrivateKey;
-import java.security.Signature;
-import java.time.Instant;
-import java.util.Base64;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -40,6 +28,7 @@ import static org.mockito.Mockito.when;
/**
* @author valerijf
+ * @author bjorncs
*/
public class InstanceValidatorTest {
@@ -93,44 +82,6 @@ public class InstanceValidatorTest {
assertTrue(instanceValidator.isSameIdentityAsInServicesXml(applicationId, domain, service));
}
- private static InstanceConfirmation createInstanceConfirmation(PrivateKey privateKey, ApplicationId applicationId,
- String domain, String service) {
- IdentityDocumentEntity identityDocument = new IdentityDocumentEntity(
- new VespaUniqueInstanceIdEntity(applicationId.tenant().value(), applicationId.application().value(),
- "environment", "region", applicationId.instance().value(), "cluster-id", 0),
- "hostname",
- "instance-hostname",
- Instant.now(),
- ImmutableSet.of("127.0.0.1", "::1"));
-
- try {
- ObjectMapper mapper = Utils.getMapper();
- String encodedIdentityDocument =
- Base64.getEncoder().encodeToString(mapper.writeValueAsString(identityDocument).getBytes());
- Signature sigGenerator = Signature.getInstance("SHA512withRSA");
- sigGenerator.initSign(privateKey);
- sigGenerator.update(encodedIdentityDocument.getBytes());
-
- return new InstanceConfirmation(
- "provider", domain, service,
- new SignedIdentityDocumentEntity(encodedIdentityDocument,
- Base64.getEncoder().encodeToString(sigGenerator.sign()),
- 0,
- EntityBindingsMapper.toVespaUniqueInstanceId(identityDocument.providerUniqueId).asDottedString(),
- "dnssuffix",
- "service",
- URI.create("http://localhost/zts"),
- 1,
- identityDocument.configServerHostname,
- identityDocument.instanceHostname,
- identityDocument.createdAt,
- identityDocument.ipAddresses,
- null)); // TODO Remove support for legacy representation without type
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
private SuperModelProvider mockSuperModelProvider(ApplicationInfo... appInfos) {
SuperModel superModel = new SuperModel(Stream.of(appInfos)
.collect(Collectors.groupingBy(
diff --git a/container-search/src/main/java/com/yahoo/search/Query.java b/container-search/src/main/java/com/yahoo/search/Query.java
index 4ec0b6e426c..cfb1c9a26be 100644
--- a/container-search/src/main/java/com/yahoo/search/Query.java
+++ b/container-search/src/main/java/com/yahoo/search/Query.java
@@ -193,6 +193,7 @@ public class Query extends com.yahoo.processing.Request implements Cloneable {
public static final CompoundName GROUPING_SESSION_CACHE = new CompoundName("groupingSessionCache");
public static final CompoundName TIMEOUT = new CompoundName("timeout");
+
private static QueryProfileType argumentType;
static {
argumentType = new QueryProfileType("native");
@@ -216,6 +217,7 @@ public class Query extends com.yahoo.processing.Request implements Cloneable {
}
public static QueryProfileType getArgumentType() { return argumentType; }
+
/** The aliases of query properties */
private static Map<String,CompoundName> propertyAliases;
static {
diff --git a/container-search/src/main/java/com/yahoo/search/handler/SearchHandler.java b/container-search/src/main/java/com/yahoo/search/handler/SearchHandler.java
index d9fa6d8097f..c9e723db6d0 100644
--- a/container-search/src/main/java/com/yahoo/search/handler/SearchHandler.java
+++ b/container-search/src/main/java/com/yahoo/search/handler/SearchHandler.java
@@ -283,13 +283,16 @@ public class SearchHandler extends LoggingRequestHandler {
private HttpSearchResponse handleBody(HttpRequest request){
- // Find query profile
- String queryProfileName = request.getProperty("queryProfile");
+
+ Map<String, String> requestMap = requestMapFromRequest(request);
+
+ // Get query profile
+ String queryProfileName = requestMap.getOrDefault("queryProfile", null);
CompiledQueryProfile queryProfile = queryProfileRegistry.findQueryProfile(queryProfileName);
- boolean benchmarkOutput = VespaHeaders.benchmarkOutput(request);
- Query query = queryFromRequest(request, queryProfile);
+ Query query = new Query(request, requestMap, queryProfile);
+ boolean benchmarkOutput = VespaHeaders.benchmarkOutput(request);
boolean benchmarkCoverage = VespaHeaders.benchmarkCoverage(benchmarkOutput, request.getJDiscRequest().headers());
// Find and execute search chain if we have a valid query
@@ -558,7 +561,8 @@ public class SearchHandler extends LoggingRequestHandler {
return searchChainRegistry;
}
- private Query queryFromRequest(HttpRequest request, CompiledQueryProfile queryProfile){
+ private Map<String, String> requestMapFromRequest(HttpRequest request) {
+
if (request.getMethod() == com.yahoo.jdisc.http.HttpRequest.Method.POST
&& JSON_CONTENT_TYPE.equals(request.getHeader(com.yahoo.jdisc.http.HttpHeaders.Names.CONTENT_TYPE))) {
Inspector inspector;
@@ -588,11 +592,10 @@ public class SearchHandler extends LoggingRequestHandler {
requestMap.remove("select.grouping");
}
- return new Query(request, requestMap, queryProfile);
-
+ return requestMap;
} else {
- return new Query(request, queryProfile);
+ return request.propertyMap();
}
}
diff --git a/dist/vespa.spec b/dist/vespa.spec
index 0e3cabcea80..8af24c019b1 100644
--- a/dist/vespa.spec
+++ b/dist/vespa.spec
@@ -56,7 +56,7 @@ BuildRequires: gtest-devel
BuildRequires: gmock-devel
%endif
%if 0%{?fc29}
-BuildRequires: llvm4.0-devel >= 4.0
+BuildRequires: llvm3.9-devel >= 3.9.1
BuildRequires: boost-devel >= 1.66
BuildRequires: gtest-devel
BuildRequires: gmock-devel
@@ -130,10 +130,10 @@ Requires: llvm4.0-libs >= 4.0
%define _vespa_llvm_include_directory /usr/include/llvm4.0
%endif
%if 0%{?fc29}
-Requires: llvm4.0-libs >= 4.0
-%define _vespa_llvm_version 4.0
-%define _vespa_llvm_link_directory /usr/lib64/llvm4.0/lib
-%define _vespa_llvm_include_directory /usr/include/llvm4.0
+Requires: llvm3.9-libs >= 3.9.1
+%define _vespa_llvm_version 3.9
+%define _vespa_llvm_link_directory /usr/lib64/llvm3.9/lib
+%define _vespa_llvm_include_directory /usr/include/llvm3.9
%endif
%define _extra_link_directory /opt/vespa-cppunit/lib%{?_vespa_llvm_link_directory:;%{_vespa_llvm_link_directory}}%{?_vespa_gtest_link_directory:;%{_vespa_gtest_link_directory}}
%define _extra_include_directory /opt/vespa-cppunit/include%{?_vespa_llvm_include_directory:;%{_vespa_llvm_include_directory}}%{?_vespa_gtest_include_directory:;%{_vespa_gtest_include_directory}}
diff --git a/document/src/tests/fieldpathupdatetestcase.cpp b/document/src/tests/fieldpathupdatetestcase.cpp
index fb3ba3f7e40..f5db5831912 100644
--- a/document/src/tests/fieldpathupdatetestcase.cpp
+++ b/document/src/tests/fieldpathupdatetestcase.cpp
@@ -33,6 +33,7 @@ struct FieldPathUpdateTestCase : public CppUnit::TestFixture {
void tearDown() override;
void testWhereClause();
+ void testBrokenWhereClause();
void testNoIterateMapValues();
void testRemoveField();
void testApplyRemoveEntireListField();
@@ -73,6 +74,7 @@ struct FieldPathUpdateTestCase : public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(FieldPathUpdateTestCase);
CPPUNIT_TEST(testWhereClause);
+ CPPUNIT_TEST(testBrokenWhereClause);
CPPUNIT_TEST(testNoIterateMapValues);
CPPUNIT_TEST(testRemoveField);
CPPUNIT_TEST(testApplyRemoveEntireListField);
@@ -356,6 +358,17 @@ FieldPathUpdateTestCase::testWhereClause()
}
void
+FieldPathUpdateTestCase::testBrokenWhereClause()
+{
+ DocumentTypeRepo repo(getRepoConfig());
+ Document::UP doc(createTestDocument(repo));
+ std::string where = "l1s1.structmap.value.smap{$x} == \"dicaprio\"";
+ TestFieldPathUpdate update("l1s1.structmap.value.smap{$x}", where);
+ update.applyTo(*doc);
+ CPPUNIT_ASSERT_EQUAL(std::string(""), update._str);
+}
+
+void
FieldPathUpdateTestCase::testNoIterateMapValues()
{
DocumentTypeRepo repo(getRepoConfig());
diff --git a/document/src/vespa/document/update/fieldpathupdate.cpp b/document/src/vespa/document/update/fieldpathupdate.cpp
index e7d5824b6e0..fa7a8b38aba 100644
--- a/document/src/vespa/document/update/fieldpathupdate.cpp
+++ b/document/src/vespa/document/update/fieldpathupdate.cpp
@@ -3,7 +3,9 @@
#include <vespa/document/datatype/datatype.h>
#include <vespa/document/fieldvalue/document.h>
#include <vespa/document/fieldvalue/iteratorhandler.h>
+#include <vespa/document/select/constant.h>
#include <vespa/document/select/parser.h>
+#include <vespa/document/select/parsing_failed_exception.h>
#include <vespa/document/util/serializableexceptions.h>
#include <vespa/vespalib/objects/nbostream.h>
#include <ostream>
@@ -11,6 +13,7 @@
#include <vespa/log/log.h>
LOG_SETUP(".document.update.fieldpathupdate");
+using document::select::ParsingFailedException;
using vespalib::make_string;
using vespalib::IllegalArgumentException;
@@ -26,8 +29,13 @@ std::unique_ptr<select::Node>
parseDocumentSelection(vespalib::stringref query, const DocumentTypeRepo& repo)
{
BucketIdFactory factory;
- select::Parser parser(repo, factory);
- return parser.parse(query);
+ try {
+ select::Parser parser(repo, factory);
+ return parser.parse(query);
+ } catch (const ParsingFailedException &e) {
+ LOG(warning, "Failed to parse selection for field path update: %s", e.getMessage().c_str());
+ return std::make_unique<select::Constant>(false);
+ }
}
} // namespace
diff --git a/functions.cmake b/functions.cmake
index c0e07c1d362..f0dd5a31949 100644
--- a/functions.cmake
+++ b/functions.cmake
@@ -422,6 +422,14 @@ function(vespa_install_script)
endif()
endfunction()
+function(vespa_install_data)
+ if(ARGC GREATER 2)
+ install(FILES ${ARGV0} RENAME ${ARGV1} PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ DESTINATION ${ARGV2})
+ else()
+ install(FILES ${ARGV0} PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ DESTINATION ${ARGV1})
+ endif()
+endfunction()
+
function(vespa_workaround_gcc_bug_67055 SOURCE_FILE)
if(CMAKE_COMPILER_IS_GNUCC)
execute_process(COMMAND ${CMAKE_CPP_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION)
diff --git a/node-admin/src/main/application/services.xml b/node-admin/src/main/application/services.xml
index 96fe82a5b94..284b356d2ca 100644
--- a/node-admin/src/main/application/services.xml
+++ b/node-admin/src/main/application/services.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -->
<services version="1.0" xmlns:preprocess="properties">
- <jdisc id="node-admin" jetty="true" version="1.0">
+ <container id="node-admin" version="1.0">
<!-- Please update container test when changing this file -->
<accesslog type="vespa" fileNamePattern="logs/vespa/node-admin/access.log.%Y%m%d%H%M%S" rotationScheme="date" symlinkName="access.log" />
<component id="docker-api" class="com.yahoo.vespa.hosted.dockerapi.DockerImpl" bundle="docker-api"/>
@@ -13,5 +13,5 @@
</config>
<preprocess:include file="variant.xml" required="false"/>
- </jdisc>
+ </container>
</services>
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/component/ConfigServerInfo.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/component/ConfigServerInfo.java
index 93243f8b8ed..9e94f6ed7e4 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/component/ConfigServerInfo.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/component/ConfigServerInfo.java
@@ -1,20 +1,14 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.node.admin.component;
-import com.google.common.base.Strings;
-import com.yahoo.vespa.athenz.api.AthenzIdentity;
import com.yahoo.vespa.athenz.api.AthenzService;
import com.yahoo.vespa.athenz.utils.AthenzIdentities;
import com.yahoo.vespa.hosted.node.admin.config.ConfigServerConfig;
-import com.yahoo.vespa.hosted.node.admin.util.KeyStoreOptions;
import java.net.URI;
-import java.nio.file.Paths;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import java.util.Map;
-import java.util.Optional;
import java.util.function.Function;
import static java.util.stream.Collectors.toMap;
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/component/TaskContext.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/component/TaskContext.java
index 0c49e478d6a..3c44186f78d 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/component/TaskContext.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/component/TaskContext.java
@@ -21,8 +21,8 @@ public interface TaskContext {
* to bob".
*/
void recordSystemModification(Logger logger, String message);
- default void recordSystemModification(Logger logger, String messageFormat, String... args) {
- recordSystemModification(logger, String.format(messageFormat, (Object[]) args));
+ default void recordSystemModification(Logger logger, String messageFormat, Object... args) {
+ recordSystemModification(logger, String.format(messageFormat, args));
}
/**
@@ -35,8 +35,8 @@ public interface TaskContext {
* Do not log a message that is also recorded with recordSystemModification.
*/
default void log(Logger logger, String message) {}
- default void log(Logger logger, String messageFormat, String... args) {
- log(logger, String.format(messageFormat, (Object[]) args));
+ default void log(Logger logger, String messageFormat, Object... args) {
+ log(logger, String.format(messageFormat, args));
}
/**
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/ConfigServerApiImpl.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/ConfigServerApiImpl.java
index 12ba777f018..aea44e728ad 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/ConfigServerApiImpl.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/ConfigServerApiImpl.java
@@ -110,41 +110,26 @@ public class ConfigServerApiImpl implements ConfigServerApi {
private <T> T tryAllConfigServers(CreateRequest requestFactory, Class<T> wantedReturnType) {
Exception lastException = null;
for (URI configServer : configServers) {
- final CloseableHttpResponse response;
- try {
- response = client.execute(requestFactory.createRequest(configServer));
- } catch (Exception e) {
- // Failure to communicate with a config server is not abnormal, as they are
- // upgraded at the same time as Docker hosts.
- if (e.getMessage().indexOf("(Connection refused)") > 0) {
- NODE_ADMIN_LOGGER.info("Connection refused to " + configServer + " (upgrading?), will try next");
- } else {
- NODE_ADMIN_LOGGER.warning("Failed to communicate with " + configServer + ", will try next: " + e.getMessage());
- }
- lastException = e;
- continue;
- }
-
- try {
- Optional<HttpException> retryableException = HttpException.handleStatusCode(
- response.getStatusLine().getStatusCode(),
- "Config server " + configServer);
- if (retryableException.isPresent()) {
- lastException = retryableException.get();
- continue;
- }
+ try (CloseableHttpResponse response = client.execute(requestFactory.createRequest(configServer))) {
+ HttpException.handleStatusCode(
+ response.getStatusLine().getStatusCode(), "Config server " + configServer);
try {
return mapper.readValue(response.getEntity().getContent(), wantedReturnType);
} catch (IOException e) {
- throw new RuntimeException("Response didn't contain nodes element, failed parsing?", e);
+ throw new RuntimeException("Failed parse response from config server", e);
}
- } finally {
- try {
- response.close();
- } catch (IOException e) {
- NODE_ADMIN_LOGGER.warning("Ignoring exception from closing response", e);
+ } catch (HttpException e) {
+ if (!e.isRetryable()) throw e;
+ lastException = e;
+ } catch (Exception e) {
+ // Failure to communicate with a config server is not abnormal during upgrades
+ if (e.getMessage().contains("(Connection refused)")) {
+ NODE_ADMIN_LOGGER.info("Connection refused to " + configServer + " (upgrading?), will try next");
+ } else {
+ NODE_ADMIN_LOGGER.warning("Failed to communicate with " + configServer + ", will try next: " + e.getMessage());
}
+ lastException = e;
}
}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/HttpException.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/HttpException.java
index d0f436d16b6..256fe38ec68 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/HttpException.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/HttpException.java
@@ -2,7 +2,6 @@
package com.yahoo.vespa.hosted.node.admin.configserver;
import javax.ws.rs.core.Response;
-import java.util.Optional;
/**
* @author hakonhall
@@ -10,35 +9,34 @@ import java.util.Optional;
@SuppressWarnings("serial")
public class HttpException extends RuntimeException {
- public static class NotFoundException extends HttpException {
-
- public NotFoundException(String message) {
- super(Response.Status.NOT_FOUND, message);
- }
+ private final boolean isRetryable;
+ private HttpException(int statusCode, String message, boolean isRetryable) {
+ super("HTTP status code " + statusCode + ": " + message);
+ this.isRetryable = isRetryable;
}
- public static class ForbiddenException extends HttpException {
-
- public ForbiddenException(String message) {
- super(Response.Status.FORBIDDEN, message);
- }
+ private HttpException(Response.Status status, String message, boolean isRetryable) {
+ super(status.toString() + " (" + status.getStatusCode() + "): " + message);
+ this.isRetryable = isRetryable;
+ }
+ public boolean isRetryable() {
+ return isRetryable;
}
/**
- * Returns empty on success.
- * Returns an exception if the error is retriable.
- * Throws an exception on a non-retriable error, like 404 Not Found.
+ * Returns on success.
+ * @throws HttpException for all non-expected status codes.
*/
- static Optional<HttpException> handleStatusCode(int statusCode, String message) {
+ static void handleStatusCode(int statusCode, String message) {
Response.Status status = Response.Status.fromStatusCode(statusCode);
if (status == null) {
- return Optional.of(new HttpException(statusCode, message));
+ throw new HttpException(statusCode, message, true);
}
switch (status.getFamily()) {
- case SUCCESSFUL: return Optional.empty();
+ case SUCCESSFUL: return;
case CLIENT_ERROR:
switch (status) {
case FORBIDDEN:
@@ -48,20 +46,24 @@ public class HttpException extends RuntimeException {
case CONFLICT:
// A response body is assumed to be present, and
// will later be interpreted as an error.
- return Optional.empty();
+ return;
}
- throw new HttpException(statusCode, message);
+ throw new HttpException(status, message, false);
}
// Other errors like server-side errors are assumed to be retryable.
- return Optional.of(new HttpException(status, message));
+ throw new HttpException(status, message, true);
}
- private HttpException(int statusCode, String message) {
- super("HTTP status code " + statusCode + ": " + message);
+ public static class NotFoundException extends HttpException {
+ public NotFoundException(String message) {
+ super(Response.Status.NOT_FOUND, message, false);
+ }
}
- private HttpException(Response.Status status, String message) {
- super(status.toString() + " (" + status.getStatusCode() + "): " + message);
+ public static class ForbiddenException extends HttpException {
+ public ForbiddenException(String message) {
+ super(Response.Status.FORBIDDEN, message, false);
+ }
}
}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/StorageMaintainer.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/StorageMaintainer.java
index ea92ca9b56f..383c025e2cb 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/StorageMaintainer.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/StorageMaintainer.java
@@ -89,6 +89,13 @@ public class StorageMaintainer {
"129600", "--crit", "1", "--coredir", environment.pathInNodeUnderVespaHome("var/crash/processing").toString());
configs.add(annotatedCheck(node, coredumpSchedule));
+ // athenz certificate check
+ Path athenzCertExpiryCheckPath = environment.pathInNodeUnderVespaHome("libexec64/yms/yms_check_athenz_certs");
+ SecretAgentCheckConfig athenzCertExpirySchedule = new SecretAgentCheckConfig("athenz-certificate-expiry", 60,
+ athenzCertExpiryCheckPath, "--threshold", "20")
+ .withRunAsUser("root");
+ configs.add(annotatedCheck(node, athenzCertExpirySchedule));
+
if (node.getNodeType() != NodeType.config) {
// vespa-health
Path vespaHealthCheckPath = environment.pathInNodeUnderVespaHome("libexec/yms/yms_check_vespa_health");
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/StoredInteger.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/StoredInteger.java
new file mode 100644
index 00000000000..a815515ac83
--- /dev/null
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/StoredInteger.java
@@ -0,0 +1,58 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.node.admin.task.util.file;
+
+import com.yahoo.vespa.hosted.node.admin.component.TaskContext;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.util.OptionalInt;
+import java.util.function.Supplier;
+import java.util.logging.Logger;
+
+/**
+ * Class wrapping an integer stored on disk
+ *
+ * @author freva
+ */
+public class StoredInteger implements Supplier<OptionalInt> {
+
+ private static final Logger logger = Logger.getLogger(StoredInteger.class.getName());
+
+ private final Path path;
+ private OptionalInt value;
+ private boolean hasBeenRead = false;
+
+ public StoredInteger(Path path) {
+ this.path = path;
+ }
+
+ @Override
+ public OptionalInt get() {
+ if (!hasBeenRead) {
+ try {
+ String value = new String(Files.readAllBytes(path));
+ this.value = OptionalInt.of(Integer.valueOf(value));
+ } catch (NoSuchFileException e) {
+ this.value = OptionalInt.empty();
+ } catch (IOException e) {
+ throw new UncheckedIOException("Failed to read integer in " + path, e);
+ }
+ hasBeenRead = true;
+ }
+ return value;
+ }
+
+ public void write(TaskContext taskContext, int value) {
+ try {
+ Files.write(path, Integer.toString(value).getBytes());
+ this.value = OptionalInt.of(value);
+ this.hasBeenRead = true;
+ taskContext.log(logger, "Stored new integer in %s: %d", path, value);
+ } catch (IOException e) {
+ throw new UncheckedIOException("Failed to store integer in " + path, e);
+ }
+ }
+}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/process/ChildProcessException.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/process/ChildProcessException.java
index 9f7aaab2060..cbc8ffbf1b7 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/process/ChildProcessException.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/process/ChildProcessException.java
@@ -65,7 +65,7 @@ public abstract class ChildProcessException extends RuntimeException {
if (possiblyHugeOutput.length() <= maxOutputPrefix + maxOutputSuffix + maxOutputSlack) {
stringBuilder.append(possiblyHugeOutput);
} else {
- stringBuilder.append(possiblyHugeOutput.substring(0, maxOutputPrefix))
+ stringBuilder.append(possiblyHugeOutput, 0, maxOutputPrefix)
.append("... [")
.append(possiblyHugeOutput.length() - maxOutputPrefix - maxOutputSuffix)
.append(" chars omitted] ...")
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/yum/Yum.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/yum/Yum.java
index d88c6f4ab33..5d60823d1c5 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/yum/Yum.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/yum/Yum.java
@@ -36,6 +36,10 @@ public class Yum {
return newYumCommand("install", packages, INSTALL_NOOP_PATTERN);
}
+ /**
+ * @param packages A list of packages, each package being of the form name-1.2.3-1.el7.noarch,
+ * if no packages are given, will upgrade all installed packages
+ */
public GenericYumCommand upgrade(String... packages) {
return newYumCommand("upgrade", packages, UPGRADE_NOOP_PATTERN);
}
@@ -70,7 +74,7 @@ public class Yum {
this.packages = packages;
this.commandOutputNoopPattern = commandOutputNoopPattern;
- if (packages.isEmpty()) {
+ if (packages.isEmpty() && ! "upgrade".equals(yumCommand)) {
throw new IllegalArgumentException("No packages specified");
}
}
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/RebootTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/RebootTest.java
index db14efdd5d2..a1af36f9c21 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/RebootTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/RebootTest.java
@@ -11,10 +11,6 @@ import com.yahoo.vespa.hosted.provision.Node;
import org.junit.Ignore;
import org.junit.Test;
-import java.util.Optional;
-
-import static org.hamcrest.core.Is.is;
-import static org.hamcrest.junit.MatcherAssert.assertThat;
import static org.junit.Assert.assertTrue;
/**
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminImplTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminImplTest.java
index 91c61623ee7..c348dc4c8b5 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminImplTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminImplTest.java
@@ -25,6 +25,7 @@ import java.util.stream.Collectors;
import static java.util.Arrays.asList;
import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
@@ -152,7 +153,7 @@ public class NodeAdminImplTest {
assertTrue(nodeAdmin.isFrozen());
assertTrue(nodeAdmin.subsystemFreezeDuration().isZero());
clock.advance(Duration.ofSeconds(1));
- assertTrue(nodeAdmin.subsystemFreezeDuration().equals(Duration.ofSeconds(1)));
+ assertEquals(Duration.ofSeconds(1), nodeAdmin.subsystemFreezeDuration());
// Unfreezing floors freeze duration
assertTrue(nodeAdmin.setFrozen(false)); // Unfreeze everything
@@ -164,7 +165,7 @@ public class NodeAdminImplTest {
assertTrue(nodeAdmin.setFrozen(true));
assertTrue(nodeAdmin.subsystemFreezeDuration().isZero());
clock.advance(Duration.ofSeconds(1));
- assertTrue(nodeAdmin.subsystemFreezeDuration().equals(Duration.ofSeconds(1)));
+ assertEquals(Duration.ofSeconds(1), nodeAdmin.subsystemFreezeDuration());
}
@Test
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/file/MakeDirectoryTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/file/MakeDirectoryTest.java
index a83f3bbe7d4..b714ab539f6 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/file/MakeDirectoryTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/file/MakeDirectoryTest.java
@@ -25,7 +25,7 @@ public class MakeDirectoryTest {
private final FileSystem fileSystem = TestFileSystem.create();
private final TestTaskContext context = new TestTaskContext();
- private String path = "/parent/dir";
+ private final String path = "/parent/dir";
private String permissions = "rwxr----x";
private String owner = "test-owner";
private String group = "test-group";
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/process/CommandLineTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/process/CommandLineTest.java
index 5bc45d7540e..a5eb0ab059b 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/process/CommandLineTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/process/CommandLineTest.java
@@ -104,7 +104,6 @@ public class CommandLineTest {
@Test
public void programFails() {
- TestChildProcess2 child = new TestChildProcess2(0, "");
terminal.expectCommand("foo 2>&1", 1, "");
try {
commandLine.add("foo").execute();
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/yum/YumTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/yum/YumTest.java
index d29d8741438..7f37336db70 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/yum/YumTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/yum/YumTest.java
@@ -9,6 +9,7 @@ import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
@@ -16,6 +17,7 @@ import static org.mockito.Mockito.mock;
public class YumTest {
private final TaskContext taskContext = mock(TaskContext.class);
private final TestTerminal terminal = new TestTerminal();
+ private final Yum yum = new Yum(terminal);
@Before
public void tearDown() {
@@ -29,7 +31,6 @@ public class YumTest {
0,
"foobar\nNothing to do\n");
- Yum yum = new Yum(terminal);
assertFalse(yum
.install("package-1", "package-2")
.enableRepo("repo-name")
@@ -43,7 +44,7 @@ public class YumTest {
0,
"foobar\nNo packages marked for update\n");
- assertFalse(new Yum(terminal)
+ assertFalse(yum
.upgrade("package-1", "package-2")
.converge(taskContext));
}
@@ -55,7 +56,7 @@ public class YumTest {
0,
"foobar\nNo Packages marked for removal\n");
- assertFalse(new Yum(terminal)
+ assertFalse(yum
.remove("package-1", "package-2")
.converge(taskContext));
}
@@ -67,7 +68,6 @@ public class YumTest {
0,
"installing, installing");
- Yum yum = new Yum(terminal);
assertTrue(yum
.install("package-1", "package-2")
.converge(taskContext));
@@ -80,7 +80,6 @@ public class YumTest {
0,
"installing, installing");
- Yum yum = new Yum(terminal);
assertTrue(yum
.install("package-1", "package-2")
.enableRepo("repo-name")
@@ -94,7 +93,6 @@ public class YumTest {
1,
"error");
- Yum yum = new Yum(terminal);
yum.install("package-1", "package-2")
.enableRepo("repo-name")
.converge(taskContext);
@@ -112,15 +110,26 @@ public class YumTest {
"No package package-2 available.\n" +
"Nothing to do\n");
- Yum yum = new Yum(terminal);
Yum.GenericYumCommand install = yum.install("package-1", "package-2", "package-3");
try {
install.converge(taskContext);
fail();
} catch (Exception e) {
- assertTrue(e.getCause() != null);
+ assertNotNull(e.getCause());
assertEquals("Unknown package: package-1", e.getCause().getMessage());
}
}
+
+ @Test(expected = IllegalArgumentException.class)
+ public void throwIfNoPackagesSpecified() {
+ yum.install();
+ }
+
+ @Test
+ public void allowToCallUpgradeWithNoPackages() {
+ terminal.expectCommand("yum upgrade --assumeyes 2>&1", 0, "OK");
+
+ yum.upgrade().converge(taskContext);
+ }
} \ No newline at end of file
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/ApplicationMaintainer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/ApplicationMaintainer.java
index 3b7c4857f48..62dc9c2395f 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/ApplicationMaintainer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/ApplicationMaintainer.java
@@ -11,12 +11,15 @@ import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeRepository;
import java.time.Duration;
+import java.time.Instant;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
@@ -26,11 +29,15 @@ import java.util.stream.Collectors;
public abstract class ApplicationMaintainer extends Maintainer {
private final Deployer deployer;
+ private final List<ApplicationId> pendingDeployments = new CopyOnWriteArrayList<>();
// Use a fixed thread pool to avoid overload on config servers. Resource usage when deploying varies
// a lot between applications, so doing one by one avoids issues where one or more resource-demanding
// deployments happen simultaneously
- private final Executor deploymentExecutor = Executors.newSingleThreadExecutor(new DaemonThreadFactory("node repo application maintainer"));
+ private final ThreadPoolExecutor deploymentExecutor = new ThreadPoolExecutor(1, 1,
+ 0L, TimeUnit.MILLISECONDS,
+ new LinkedBlockingQueue<>(),
+ new DaemonThreadFactory("node repo application maintainer"));
protected ApplicationMaintainer(Deployer deployer, NodeRepository nodeRepository, Duration interval, JobControl jobControl) {
super(nodeRepository, interval, jobControl);
@@ -39,12 +46,15 @@ public abstract class ApplicationMaintainer extends Maintainer {
@Override
protected final void maintain() {
- Set<ApplicationId> applications = applicationsNeedingMaintenance();
- for (ApplicationId application : applications) {
- deploy(application);
- }
+ applicationsNeedingMaintenance().forEach(this::deploy);
+ }
+
+ /** Returns the number of deployments that are pending execution */
+ public int pendingDeployments() {
+ return pendingDeployments.size();
}
+ /** Returns whether given application should be deployed at this moment in time */
protected boolean canDeployNow(ApplicationId application) {
return true;
}
@@ -56,16 +66,21 @@ public abstract class ApplicationMaintainer extends Maintainer {
* even when deployments are slow.
*/
protected void deploy(ApplicationId application) {
+ if (pendingDeployments.contains(application)) {
+ return;// Avoid queuing multiple deployments for same application
+ }
+ log.log(LogLevel.INFO, application + " will be deployed, last deploy time " +
+ getLastDeployTime(application));
deploymentExecutor.execute(() -> deployWithLock(application));
+ pendingDeployments.add(application);
}
protected Deployer deployer() { return deployer; }
-
protected Set<ApplicationId> applicationsNeedingMaintenance() {
return nodesNeedingMaintenance().stream()
- .map(node -> node.allocation().get().owner())
- .collect(Collectors.toCollection(LinkedHashSet::new));
+ .map(node -> node.allocation().get().owner())
+ .collect(Collectors.toCollection(LinkedHashSet::new));
}
/**
@@ -75,7 +90,7 @@ public abstract class ApplicationMaintainer extends Maintainer {
protected abstract List<Node> nodesNeedingMaintenance();
/** Redeploy this application. A lock will be taken for the duration of the deployment activation */
- final void deployWithLock(ApplicationId application) {
+ protected final void deployWithLock(ApplicationId application) {
// An application might change its state between the time the set of applications is retrieved and the
// time deployment happens. Lock the application and check if it's still active.
//
@@ -89,12 +104,31 @@ public abstract class ApplicationMaintainer extends Maintainer {
deployment.get().activate();
} catch (RuntimeException e) {
log.log(LogLevel.WARNING, "Exception on maintenance redeploy", e);
+ } finally {
+ pendingDeployments.remove(application);
}
}
+ /** Returns the last time application was deployed. Epoch is returned if the application has never been deployed. */
+ protected final Instant getLastDeployTime(ApplicationId application) {
+ return deployer.lastDeployTime(application).orElse(Instant.EPOCH);
+ }
+
/** Returns true when application has at least one active node */
private boolean isActive(ApplicationId application) {
return ! nodeRepository().getNodes(application, Node.State.active).isEmpty();
}
+ @Override
+ public void deconstruct() {
+ super.deconstruct();
+ this.deploymentExecutor.shutdownNow();
+ try {
+ // Give deployments in progress some time to complete
+ this.deploymentExecutor.awaitTermination(1, TimeUnit.MINUTES);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainer.java
index ee5d6a04ddc..8b2d0a55cd8 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainer.java
@@ -3,17 +3,18 @@ package com.yahoo.vespa.hosted.provision.maintenance;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.Deployer;
-import com.yahoo.log.LogLevel;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeRepository;
+import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.util.Collections;
import java.util.Comparator;
+import java.util.LinkedHashSet;
import java.util.List;
-import java.util.Optional;
import java.util.Set;
+import java.util.stream.Collectors;
/**
* The application maintainer regularly redeploys all applications to make sure the node repo and application
@@ -23,14 +24,17 @@ import java.util.Set;
* @author bratseth
*/
public class PeriodicApplicationMaintainer extends ApplicationMaintainer {
+
private final Duration minTimeBetweenRedeployments;
+ private final Clock clock;
private final Instant start;
public PeriodicApplicationMaintainer(Deployer deployer, NodeRepository nodeRepository,
Duration interval, Duration minTimeBetweenRedeployments, JobControl jobControl) {
super(deployer, nodeRepository, interval, jobControl);
this.minTimeBetweenRedeployments = minTimeBetweenRedeployments;
- this.start = Instant.now();
+ this.clock = nodeRepository.clock();
+ this.start = clock.instant();
}
@Override
@@ -39,24 +43,17 @@ public class PeriodicApplicationMaintainer extends ApplicationMaintainer {
return getLastDeployTime(application).isBefore(nodeRepository().clock().instant().minus(minTimeBetweenRedeployments));
}
- // Returns the app that was deployed the longest time ago
+ // Returns the applications that need to be redeployed by this config server at this point in time.
@Override
protected Set<ApplicationId> applicationsNeedingMaintenance() {
if (waitInitially()) return Collections.emptySet();
- Optional<ApplicationId> app = (nodesNeedingMaintenance().stream()
- .map(node -> node.allocation().get().owner())
- .distinct()
- .filter(this::shouldBeDeployedOnThisServer)
- .min(Comparator.comparing(this::getLastDeployTime)))
- .filter(this::canDeployNow);
- app.ifPresent(applicationId -> log.log(LogLevel.INFO, applicationId + " will be deployed, last deploy time " +
- getLastDeployTime(applicationId)));
- return app.map(Collections::singleton).orElseGet(Collections::emptySet);
- }
-
- private Instant getLastDeployTime(ApplicationId application) {
- return deployer().lastDeployTime(application).orElse(Instant.EPOCH);
+ return nodesNeedingMaintenance().stream()
+ .map(node -> node.allocation().get().owner())
+ .filter(this::shouldBeDeployedOnThisServer)
+ .filter(this::canDeployNow)
+ .sorted(Comparator.comparing(this::getLastDeployTime))
+ .collect(Collectors.toCollection(LinkedHashSet::new));
}
// We only know last deploy time for applications that were deployed on this config server,
@@ -66,8 +63,8 @@ public class PeriodicApplicationMaintainer extends ApplicationMaintainer {
}
// TODO: Do not start deploying until some time has gone (ideally only until bootstrap of config server is finished)
- protected boolean waitInitially() {
- return Instant.now().isBefore(start.plus(minTimeBetweenRedeployments));
+ private boolean waitInitially() {
+ return clock.instant().isBefore(start.plus(minTimeBetweenRedeployments));
}
@Override
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockDeployer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockDeployer.java
index 99beab50e16..299dc66c547 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockDeployer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockDeployer.java
@@ -20,6 +20,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
/**
@@ -35,6 +36,7 @@ public class MockDeployer implements Deployer {
public int redeployments = 0;
private final Clock clock;
+ private final ReentrantLock lock = new ReentrantLock();
@Inject
@SuppressWarnings("unused")
@@ -54,6 +56,10 @@ public class MockDeployer implements Deployer {
this.applications = applications;
}
+ public ReentrantLock lock() {
+ return lock;
+ }
+
@Override
public Optional<Deployment> deployFromLocalActive(ApplicationId id, boolean bootstrap) {
return deployFromLocalActive(id, Duration.ofSeconds(60));
@@ -61,8 +67,17 @@ public class MockDeployer implements Deployer {
@Override
public Optional<Deployment> deployFromLocalActive(ApplicationId id, Duration timeout) {
- lastDeployTimes.put(id, clock.instant());
- return Optional.of(new MockDeployment(provisioner, applications.get(id)));
+ try {
+ lock.lockInterruptibly();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ try {
+ lastDeployTimes.put(id, clock.instant());
+ return Optional.of(new MockDeployment(provisioner, applications.get(id)));
+ } finally {
+ lock.unlock();
+ }
}
@Override
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainerTest.java
index 2ddb2e0d004..3a7bca801a9 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainerTest.java
@@ -29,7 +29,9 @@ import com.yahoo.vespa.hosted.provision.provisioning.FlavorConfigBuilder;
import com.yahoo.vespa.hosted.provision.provisioning.NodeRepositoryProvisioner;
import com.yahoo.vespa.hosted.provision.testutils.MockDeployer;
import com.yahoo.vespa.hosted.provision.testutils.MockNameResolver;
+import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import java.time.Duration;
@@ -41,36 +43,48 @@ import java.util.Map;
import java.util.Optional;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
/**
* @author bratseth
*/
+@Ignore
public class PeriodicApplicationMaintainerTest {
private static final NodeFlavors nodeFlavors = FlavorConfigBuilder.createDummies("default");
private NodeRepository nodeRepository;
private Fixture fixture;
+ private ManualClock clock;
@Before
public void before() {
Curator curator = new MockCurator();
Zone zone = new Zone(Environment.prod, RegionName.from("us-east"));
- this.nodeRepository = new NodeRepository(nodeFlavors, curator, new ManualClock(), zone,
+ this.clock = new ManualClock();
+ this.nodeRepository = new NodeRepository(nodeFlavors, curator, clock, zone,
new MockNameResolver().mockAnyLookup(),
new DockerImage("docker-registry.domain.tld:8080/dist/vespa"),
true);
this.fixture = new Fixture(zone, nodeRepository, nodeFlavors, curator);
- }
- @Test
- public void test_application_maintenance() {
createReadyNodes(15, nodeRepository, nodeFlavors);
createHostNodes(2, nodeRepository, nodeFlavors);
+ }
+
+ @After
+ public void after() {
+ this.fixture.maintainer.deconstruct();
+ }
+ @Test(timeout = 60_000)
+ public void test_application_maintenance() {
// Create applications
fixture.activate();
+ // Exhaust initial wait period
+ clock.advance(Duration.ofMinutes(30).plus(Duration.ofSeconds(1)));
+
// Fail and park some nodes
nodeRepository.fail(nodeRepository.getNodes(fixture.app1).get(3).hostname(), Agent.system, "Failing to unit test");
nodeRepository.fail(nodeRepository.getNodes(fixture.app2).get(0).hostname(), Agent.system, "Failing to unit test");
@@ -102,7 +116,7 @@ public class PeriodicApplicationMaintainerTest {
0, fixture.getNodes(Node.State.active).retired().size());
// Cause maintenance deployment which will update the applications with the re-activated nodes
- ((ManualClock)nodeRepository.clock()).advance(Duration.ofMinutes(35)); // Otherwise redeploys are inhibited
+ clock.advance(Duration.ofMinutes(35)); // Otherwise redeploys are inhibited
fixture.runApplicationMaintainer();
assertEquals("Superflous content nodes are retired",
reactivatedInApp2, fixture.getNodes(Node.State.active).retired().size());
@@ -110,11 +124,8 @@ public class PeriodicApplicationMaintainerTest {
reactivatedInApp1, fixture.getNodes(Node.State.inactive).size());
}
- @Test
+ @Test(timeout = 60_000)
public void deleted_application_is_not_reactivated() {
- createReadyNodes(15, nodeRepository, nodeFlavors);
- createHostNodes(2, nodeRepository, nodeFlavors);
-
// Create applications
fixture.activate();
@@ -126,36 +137,81 @@ public class PeriodicApplicationMaintainerTest {
assertEquals(fixture.wantedNodesApp2, nodeRepository.getNodes(fixture.app2, Node.State.inactive).size());
// Nodes belonging to app2 are inactive after maintenance
- fixture.runApplicationMaintainer(Optional.of(frozenActiveNodes));
+ fixture.maintainer.setOverriddenNodesNeedingMaintenance(frozenActiveNodes);
+ fixture.runApplicationMaintainer();
assertEquals("Inactive nodes were incorrectly activated after maintenance", fixture.wantedNodesApp2,
nodeRepository.getNodes(fixture.app2, Node.State.inactive).size());
}
- @Test
+ @Test(timeout = 60_000)
public void application_deploy_inhibits_redeploy_for_a_while() {
- ManualClock clock = (ManualClock)nodeRepository.clock();
- createReadyNodes(15, nodeRepository, nodeFlavors);
- createHostNodes(2, nodeRepository, nodeFlavors);
-
- // Create applications
fixture.activate();
+
+ // Holds off on deployments a while after starting
+ fixture.runApplicationMaintainer();
+ assertFalse("No deployment expected", fixture.deployer.lastDeployTime(fixture.app1).isPresent());
+ assertFalse("No deployment expected", fixture.deployer.lastDeployTime(fixture.app2).isPresent());
+ // Exhaust initial wait period
+ clock.advance(Duration.ofMinutes(30).plus(Duration.ofSeconds(1)));
+
+ // First deployment of applications
fixture.runApplicationMaintainer();
Instant firstDeployTime = clock.instant();
assertEquals(firstDeployTime, fixture.deployer.lastDeployTime(fixture.app1).get());
assertEquals(firstDeployTime, fixture.deployer.lastDeployTime(fixture.app2).get());
- ((ManualClock) nodeRepository.clock()).advance(Duration.ofMinutes(5));
+ clock.advance(Duration.ofMinutes(5));
fixture.runApplicationMaintainer();
// Too soon: Not redeployed:
assertEquals(firstDeployTime, fixture.deployer.lastDeployTime(fixture.app1).get());
assertEquals(firstDeployTime, fixture.deployer.lastDeployTime(fixture.app2).get());
- ((ManualClock) nodeRepository.clock()).advance(Duration.ofMinutes(30));
+ clock.advance(Duration.ofMinutes(30));
fixture.runApplicationMaintainer();
// Redeployed:
assertEquals(clock.instant(), fixture.deployer.lastDeployTime(fixture.app1).get());
assertEquals(clock.instant(), fixture.deployer.lastDeployTime(fixture.app2).get());
}
+ @Test(timeout = 60_000)
+ public void queues_all_eligible_applications_for_deployment() throws Exception {
+ fixture.activate();
+
+ // Exhaust initial wait period
+ clock.advance(Duration.ofMinutes(30).plus(Duration.ofSeconds(1)));
+
+ // Lock deployer to simulate slow deployments
+ fixture.deployer.lock().lockInterruptibly();
+
+ try {
+ // Queues all eligible applications
+ assertEquals(2, fixture.maintainer.applicationsNeedingMaintenance().size());
+ fixture.runApplicationMaintainer(false);
+ assertEquals(2, fixture.maintainer.pendingDeployments());
+
+ // Enough time passes to make applications eligible for another periodic deployment
+ clock.advance(Duration.ofMinutes(30).plus(Duration.ofSeconds(1)));
+ fixture.runApplicationMaintainer(false);
+
+ // Deployments are not re-queued as previous deployments are still pending
+ assertEquals(2, fixture.maintainer.pendingDeployments());
+
+ // Slow deployments complete
+ fixture.deployer.lock().unlock();
+ fixture.runApplicationMaintainer();
+ Instant deployTime = clock.instant();
+ assertEquals(deployTime, fixture.deployer.lastDeployTime(fixture.app1).get());
+ assertEquals(deployTime, fixture.deployer.lastDeployTime(fixture.app2).get());
+
+ // Too soon: Already deployed recently
+ clock.advance(Duration.ofMinutes(5));
+ assertEquals(0, fixture.maintainer.applicationsNeedingMaintenance().size());
+ } finally {
+ if (fixture.deployer.lock().isHeldByCurrentThread()) {
+ fixture.deployer.lock().unlock();
+ }
+ }
+ }
+
private void createReadyNodes(int count, NodeRepository nodeRepository, NodeFlavors nodeFlavors) {
List<Node> nodes = new ArrayList<>(count);
for (int i = 0; i < count; i++)
@@ -179,7 +235,7 @@ public class PeriodicApplicationMaintainerTest {
final NodeRepository nodeRepository;
final NodeRepositoryProvisioner provisioner;
final Curator curator;
- final Deployer deployer;
+ final MockDeployer deployer;
final ApplicationId app1 = ApplicationId.from(TenantName.from("foo1"), ApplicationName.from("bar"), InstanceName.from("fuz"));
final ApplicationId app2 = ApplicationId.from(TenantName.from("foo2"), ApplicationName.from("bar"), InstanceName.from("fuz"));
@@ -188,6 +244,8 @@ public class PeriodicApplicationMaintainerTest {
final int wantedNodesApp1 = 5;
final int wantedNodesApp2 = 7;
+ private final TestablePeriodicApplicationMaintainer maintainer;
+
Fixture(Zone zone, NodeRepository nodeRepository, NodeFlavors flavors, Curator curator) {
this.nodeRepository = nodeRepository;
this.curator = curator;
@@ -199,6 +257,8 @@ public class PeriodicApplicationMaintainerTest {
apps.put(app2, new MockDeployer.ApplicationContext(app2, clusterApp2,
Capacity.fromNodeCount(wantedNodesApp2, Optional.of("default"), false, true), 1));
this.deployer = new MockDeployer(provisioner, nodeRepository.clock(), apps);
+ this.maintainer = new TestablePeriodicApplicationMaintainer(deployer, nodeRepository, Duration.ofDays(1), // Long duration to prevent scheduled runs during test
+ Duration.ofMinutes(30));
}
void activate() {
@@ -222,16 +282,12 @@ public class PeriodicApplicationMaintainerTest {
}
void runApplicationMaintainer() {
- runApplicationMaintainer(Optional.empty());
+ runApplicationMaintainer(true);
}
- void runApplicationMaintainer(Optional<List<Node>> overriddenNodesNeedingMaintenance) {
- TestablePeriodicApplicationMaintainer maintainer =
- new TestablePeriodicApplicationMaintainer(deployer, nodeRepository, Duration.ofMinutes(1),
- Duration.ofMinutes(30), overriddenNodesNeedingMaintenance);
- // Need to run twice, as only one app is deployed per run
- maintainer.run();
+ void runApplicationMaintainer(boolean waitForDeployments) {
maintainer.run();
+ while (waitForDeployments && fixture.maintainer.pendingDeployments() != 0);
}
NodeList getNodes(Node.State ... states) {
@@ -240,36 +296,32 @@ public class PeriodicApplicationMaintainerTest {
}
- public static class TestablePeriodicApplicationMaintainer extends PeriodicApplicationMaintainer {
+ private static class TestablePeriodicApplicationMaintainer extends PeriodicApplicationMaintainer {
- private Optional<List<Node>> overriddenNodesNeedingMaintenance;
-
- TestablePeriodicApplicationMaintainer(Deployer deployer, NodeRepository nodeRepository, Duration interval,
- Duration minTimeBetweenRedeployments, Optional<List<Node>> overriddenNodesNeedingMaintenance) {
- super(deployer, nodeRepository, interval, minTimeBetweenRedeployments, new JobControl(nodeRepository.database()));
+ private List<Node> overriddenNodesNeedingMaintenance;
+
+ TestablePeriodicApplicationMaintainer setOverriddenNodesNeedingMaintenance(List<Node> overriddenNodesNeedingMaintenance) {
this.overriddenNodesNeedingMaintenance = overriddenNodesNeedingMaintenance;
+ return this;
}
- @Override
- protected void deploy(ApplicationId application) {
- deployWithLock(application);
+ TestablePeriodicApplicationMaintainer(Deployer deployer, NodeRepository nodeRepository, Duration interval,
+ Duration minTimeBetweenRedeployments) {
+ super(deployer, nodeRepository, interval, minTimeBetweenRedeployments, new JobControl(nodeRepository.database()));
}
@Override
protected List<Node> nodesNeedingMaintenance() {
- if (overriddenNodesNeedingMaintenance.isPresent())
- return overriddenNodesNeedingMaintenance.get();
- return super.nodesNeedingMaintenance();
+ return overriddenNodesNeedingMaintenance != null
+ ? overriddenNodesNeedingMaintenance
+ : super.nodesNeedingMaintenance();
}
+ @Override
protected boolean shouldBeDeployedOnThisServer(ApplicationId application) {
return true;
}
- protected boolean waitInitially() {
- return false;
- }
-
}
}
diff --git a/searchcore/CMakeLists.txt b/searchcore/CMakeLists.txt
index 89e6493cfbc..9d996d96dc7 100644
--- a/searchcore/CMakeLists.txt
+++ b/searchcore/CMakeLists.txt
@@ -129,6 +129,7 @@ vespa_define_module(
src/tests/proton/proton
src/tests/proton/proton_config_fetcher
src/tests/proton/proton_configurer
+ src/tests/proton/proton_disk_layout
src/tests/proton/reference/gid_to_lid_change_handler
src/tests/proton/reference/gid_to_lid_change_listener
src/tests/proton/reference/gid_to_lid_change_registrator
diff --git a/searchcore/src/tests/proton/attribute/attribute_directory/attribute_directory_test.cpp b/searchcore/src/tests/proton/attribute/attribute_directory/attribute_directory_test.cpp
index 37cf201e354..f95ea478ce1 100644
--- a/searchcore/src/tests/proton/attribute/attribute_directory/attribute_directory_test.cpp
+++ b/searchcore/src/tests/proton/attribute/attribute_directory/attribute_directory_test.cpp
@@ -137,6 +137,7 @@ struct Fixture : public DirectoryHandler
EXPECT_TRUE(hasAttributeDir(dir));
auto writer = dir->getWriter();
writer->createInvalidSnapshot(serialNum);
+ vespalib::mkdir(writer->getSnapshotDir(serialNum), false);
writer->markValidSnapshot(serialNum);
TEST_DO(assertAttributeDiskDir("foo"));
}
@@ -162,6 +163,7 @@ struct Fixture : public DirectoryHandler
auto dir = createFooAttrDir();
auto writer = dir->getWriter();
writer->createInvalidSnapshot(serialNum);
+ vespalib::mkdir(writer->getSnapshotDir(serialNum), false);
writer->markValidSnapshot(serialNum);
}
@@ -208,8 +210,10 @@ TEST_F("Test that we can prune attribute snapshots", Fixture)
TEST_DO(f.assertNotAttributeDiskDir("foo"));
auto writer = dir->getWriter();
writer->createInvalidSnapshot(2);
+ vespalib::mkdir(writer->getSnapshotDir(2), false);
writer->markValidSnapshot(2);
writer->createInvalidSnapshot(4);
+ vespalib::mkdir(writer->getSnapshotDir(4), false);
writer->markValidSnapshot(4);
writer.reset();
TEST_DO(f.assertAttributeDiskDir("foo"));
diff --git a/searchcore/src/tests/proton/proton_disk_layout/CMakeLists.txt b/searchcore/src/tests/proton/proton_disk_layout/CMakeLists.txt
new file mode 100644
index 00000000000..f63fa21a954
--- /dev/null
+++ b/searchcore/src/tests/proton/proton_disk_layout/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(searchcore_proton_disk_layout_test_app TEST
+ SOURCES
+ proton_disk_layout_test.cpp
+ DEPENDS
+ searchcore_server
+ searchcore_fconfig
+)
+vespa_add_test(NAME searchcore_proton_disk_layout_test_app COMMAND searchcore_proton_disk_layout_test_app)
diff --git a/searchcore/src/tests/proton/proton_disk_layout/proton_disk_layout_test.cpp b/searchcore/src/tests/proton/proton_disk_layout/proton_disk_layout_test.cpp
new file mode 100644
index 00000000000..edb4250ce76
--- /dev/null
+++ b/searchcore/src/tests/proton/proton_disk_layout/proton_disk_layout_test.cpp
@@ -0,0 +1,178 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/searchcore/proton/server/proton_disk_layout.h>
+#include <vespa/searchcore/proton/common/doctypename.h>
+#include <vespa/searchlib/index/dummyfileheadercontext.h>
+#include <vespa/searchlib/transactionlog/translogserver.h>
+#include <vespa/searchlib/transactionlog/translogclient.h>
+#include <vespa/vespalib/io/fileutil.h>
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vespa/vespalib/test/insertion_operators.h>
+#include <vespa/vespalib/util/stringfmt.h>
+
+using search::index::DummyFileHeaderContext;
+using search::transactionlog::TransLogClient;
+using search::transactionlog::TransLogServer;
+using proton::DocTypeName;
+using proton::ProtonDiskLayout;
+
+static constexpr unsigned int tlsPort = 9018;
+
+static const vespalib::string baseDir("testdb");
+static const vespalib::string documentsDir(baseDir + "/documents");
+
+struct FixtureBase
+{
+ FixtureBase() { vespalib::rmdir(baseDir, true); }
+ ~FixtureBase() { vespalib::rmdir(baseDir, true); }
+};
+
+struct DiskLayoutFixture {
+ DummyFileHeaderContext _fileHeaderContext;
+ TransLogServer _tls;
+ vespalib::string _tlsSpec;
+ ProtonDiskLayout _diskLayout;
+
+ DiskLayoutFixture();
+ ~DiskLayoutFixture();
+
+ void createDirs(const std::set<vespalib::string> &dirs) {
+ for (const auto &dir : dirs) {
+ vespalib::mkdir(documentsDir + "/" + dir, false);
+ }
+ }
+ void createDomains(const std::set<vespalib::string> &domains) {
+ TransLogClient tlc(_tlsSpec);
+ for (const auto &domain : domains) {
+ ASSERT_TRUE(tlc.create(domain));
+ }
+ }
+
+ std::set<vespalib::string> listDomains() {
+ std::vector<vespalib::string> domainVector;
+ TransLogClient tlc(_tlsSpec);
+ ASSERT_TRUE(tlc.listDomains(domainVector));
+ std::set<vespalib::string> domains;
+ for (const auto &domain : domainVector) {
+ domains.emplace(domain);
+ }
+ return domains;
+ }
+
+ std::set<vespalib::string> listDirs() {
+ std::set<vespalib::string> dirs;
+ auto names = vespalib::listDirectory(documentsDir);
+ for (const auto &name : names) {
+ if (vespalib::isDirectory(documentsDir + "/" + name)) {
+ dirs.emplace(name);
+ }
+ }
+ return dirs;
+ }
+
+ void initAndPruneUnused(const std::set<vespalib::string> names)
+ {
+ std::set<DocTypeName> docTypeNames;
+ for (const auto &name: names) {
+ docTypeNames.emplace(name);
+ }
+ _diskLayout.initAndPruneUnused(docTypeNames);
+ }
+
+ void assertDirs(const std::set<vespalib::string> &expDirs) {
+ EXPECT_EQUAL(expDirs, listDirs());
+ }
+
+ void assertDomains(const std::set<vespalib::string> &expDomains)
+ {
+ EXPECT_EQUAL(expDomains, listDomains());
+ }
+};
+
+DiskLayoutFixture::DiskLayoutFixture()
+ : _fileHeaderContext(),
+ _tls("tls", tlsPort, baseDir, _fileHeaderContext),
+ _tlsSpec(vespalib::make_string("tcp/localhost:%u", tlsPort)),
+ _diskLayout(baseDir, _tlsSpec)
+{
+}
+
+DiskLayoutFixture::~DiskLayoutFixture() = default;
+
+struct Fixture : public FixtureBase, public DiskLayoutFixture
+{
+ Fixture()
+ : FixtureBase(),
+ DiskLayoutFixture()
+ {
+ }
+};
+
+TEST_F("require that empty config is ok", Fixture) {
+ TEST_DO(f.assertDirs({}));
+ TEST_DO(f.assertDomains({}));
+}
+
+TEST_F("require that disk layout is preserved", FixtureBase)
+{
+ {
+ DiskLayoutFixture diskLayout;
+ diskLayout.createDirs({"foo", "bar"});
+ diskLayout.createDomains({"bar", "baz"});
+ }
+ {
+ DiskLayoutFixture diskLayout;
+ TEST_DO(diskLayout.assertDirs({"foo", "bar"}));
+ TEST_DO(diskLayout.assertDomains({"bar", "baz"}));
+ }
+}
+
+TEST_F("require that used dir is preserved", Fixture)
+{
+ f.createDirs({"foo"});
+ f.createDomains({"foo"});
+ f.initAndPruneUnused({"foo"});
+ TEST_DO(f.assertDirs({"foo"}));
+ TEST_DO(f.assertDomains({"foo"}));
+}
+
+TEST_F("require that unused dir is removed", Fixture)
+{
+ f.createDirs({"foo"});
+ f.createDomains({"foo"});
+ f.initAndPruneUnused({"bar"});
+ TEST_DO(f.assertDirs({}));
+ TEST_DO(f.assertDomains({}));
+}
+
+TEST_F("require that interrupted remove is completed", Fixture)
+{
+ f.createDirs({"foo.removed"});
+ f.createDomains({"foo"});
+ f.initAndPruneUnused({"foo"});
+ TEST_DO(f.assertDirs({}));
+ TEST_DO(f.assertDomains({}));
+}
+
+TEST_F("require that early interrupted remove is completed", Fixture)
+{
+ f.createDirs({"foo", "foo.removed"});
+ f.createDomains({"foo"});
+ f.initAndPruneUnused({"foo"});
+ TEST_DO(f.assertDirs({}));
+ TEST_DO(f.assertDomains({}));
+}
+
+TEST_F("require that live document db dir remove works", Fixture)
+{
+ f.createDirs({"foo"});
+ f.createDomains({"foo"});
+ f.initAndPruneUnused({"foo"});
+ TEST_DO(f.assertDirs({"foo"}));
+ TEST_DO(f.assertDomains({"foo"}));
+ f._diskLayout.remove(DocTypeName("foo"));
+ TEST_DO(f.assertDirs({}));
+ TEST_DO(f.assertDomains({}));
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/searchcore/src/tests/proton/reprocessing/attribute_reprocessing_initializer/attribute_reprocessing_initializer_test.cpp b/searchcore/src/tests/proton/reprocessing/attribute_reprocessing_initializer/attribute_reprocessing_initializer_test.cpp
index 7be774f7291..72e558fd25f 100644
--- a/searchcore/src/tests/proton/reprocessing/attribute_reprocessing_initializer/attribute_reprocessing_initializer_test.cpp
+++ b/searchcore/src/tests/proton/reprocessing/attribute_reprocessing_initializer/attribute_reprocessing_initializer_test.cpp
@@ -294,9 +294,9 @@ TEST("require that added attribute aspect with flushed attribute after interrupt
auto dir = diskLayout->createAttributeDir("a");
auto writer = dir->getWriter();
writer->createInvalidSnapshot(INIT_SERIAL_NUM);
- writer->markValidSnapshot(INIT_SERIAL_NUM);
auto snapshotdir = writer->getSnapshotDir(INIT_SERIAL_NUM);
vespalib::mkdir(snapshotdir);
+ writer->markValidSnapshot(INIT_SERIAL_NUM);
auto av = AttributeFactory::createAttribute(snapshotdir + "/a",
Config(BasicType::STRING));
av->save();
diff --git a/searchcore/src/vespa/searchcore/proton/attribute/attribute_directory.cpp b/searchcore/src/vespa/searchcore/proton/attribute/attribute_directory.cpp
index f2e4ac4905d..2bbc6c99dc0 100644
--- a/searchcore/src/vespa/searchcore/proton/attribute/attribute_directory.cpp
+++ b/searchcore/src/vespa/searchcore/proton/attribute/attribute_directory.cpp
@@ -112,6 +112,7 @@ AttributeDirectory::createInvalidSnapshot(SerialNum serialNum)
if (empty()) {
vespalib::string dirName(getDirName());
vespalib::mkdir(dirName, false);
+ vespalib::File::sync(vespalib::dirname(dirName));
}
{
std::lock_guard<std::mutex> guard(_mutex);
@@ -130,6 +131,9 @@ AttributeDirectory::markValidSnapshot(SerialNum serialNum)
assert(snap.syncToken == serialNum);
_snapInfo.validateSnapshot(serialNum);
}
+ vespalib::string snapshotDir(getSnapshotDir(serialNum));
+ vespalib::File::sync(snapshotDir);
+ vespalib::File::sync(dirname(snapshotDir));
saveSnapInfo();
}
@@ -178,6 +182,7 @@ AttributeDirectory::removeInvalidSnapshots()
vespalib::rmdir(subDir, true);
}
if (!toRemove.empty()) {
+ vespalib::File::sync(getDirName());
{
std::lock_guard<std::mutex> guard(_mutex);
for (const auto &serialNum : toRemove) {
@@ -194,6 +199,7 @@ AttributeDirectory::removeDiskDir()
if (empty()) {
vespalib::string dirName(getDirName());
vespalib::rmdir(dirName, true);
+ vespalib::File::sync(vespalib::dirname(dirName));
return true;
}
return false;
diff --git a/searchcore/src/vespa/searchcore/proton/attribute/attributedisklayout.cpp b/searchcore/src/vespa/searchcore/proton/attribute/attributedisklayout.cpp
index bb2f99d077b..a675927b85f 100644
--- a/searchcore/src/vespa/searchcore/proton/attribute/attributedisklayout.cpp
+++ b/searchcore/src/vespa/searchcore/proton/attribute/attributedisklayout.cpp
@@ -14,6 +14,7 @@ AttributeDiskLayout::AttributeDiskLayout(const vespalib::string &baseDir, Privat
_dirs()
{
vespalib::mkdir(_baseDir, false);
+ vespalib::File::sync(vespalib::dirname(_baseDir));
}
AttributeDiskLayout::~AttributeDiskLayout()
diff --git a/searchcore/src/vespa/searchcore/proton/index/index_manager_initializer.cpp b/searchcore/src/vespa/searchcore/proton/index/index_manager_initializer.cpp
index b8f2947c9b4..ad233a66d1f 100644
--- a/searchcore/src/vespa/searchcore/proton/index/index_manager_initializer.cpp
+++ b/searchcore/src/vespa/searchcore/proton/index/index_manager_initializer.cpp
@@ -45,6 +45,7 @@ IndexManagerInitializer::run()
LOG(debug, "About to create proton::IndexManager with %u index field(s)",
_schema.getNumIndexFields());
vespalib::mkdir(_baseDir, false);
+ vespalib::File::sync(vespalib::dirname(_baseDir));
*_indexManager = std::make_shared<proton::IndexManager>
(_baseDir,
_warmupCfg,
diff --git a/searchcore/src/vespa/searchcore/proton/metrics/trans_log_server_metrics.cpp b/searchcore/src/vespa/searchcore/proton/metrics/trans_log_server_metrics.cpp
index 00a7e9b9140..c2624719d81 100644
--- a/searchcore/src/vespa/searchcore/proton/metrics/trans_log_server_metrics.cpp
+++ b/searchcore/src/vespa/searchcore/proton/metrics/trans_log_server_metrics.cpp
@@ -44,6 +44,7 @@ TransLogServerMetrics::considerRemoveDomains(const DomainStats &stats)
for (auto itr = _domainMetrics.begin(); itr != _domainMetrics.end(); ) {
const vespalib::string &documentType = itr->first;
if (stats.find(documentType) == stats.end()) {
+ _parent->unregisterMetric(*itr->second);
itr = _domainMetrics.erase(itr);
} else {
++itr;
diff --git a/searchcore/src/vespa/searchcore/proton/server/fileconfigmanager.cpp b/searchcore/src/vespa/searchcore/proton/server/fileconfigmanager.cpp
index 76f2ffce93a..72f48df3295 100644
--- a/searchcore/src/vespa/searchcore/proton/server/fileconfigmanager.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/fileconfigmanager.cpp
@@ -229,6 +229,7 @@ FileConfigManager::FileConfigManager(const vespalib::string &baseDir,
_protonConfig()
{
vespalib::mkdir(baseDir, false);
+ vespalib::File::sync(vespalib::dirname(baseDir));
if (!_info.load())
_info.save();
removeInvalid();
@@ -297,6 +298,8 @@ FileConfigManager::saveConfig(const DocumentDBConfig &snapshot,
bool saveHistorySchemaRes = historySchema.saveToFile(snapDir + "/historyschema.txt");
assert(saveHistorySchemaRes);
(void) saveHistorySchemaRes;
+ vespalib::File::sync(snapDir);
+ vespalib::File::sync(_baseDir);
_info.validateSnapshot(serialNum);
@@ -402,6 +405,7 @@ FileConfigManager::removeInvalid()
LOG(warning, "Removing obsolete config directory '%s' failed due to %s", snapDir.c_str(), e.what());
}
}
+ vespalib::File::sync(_baseDir);
for (const auto &serial : toRem) {
_info.removeSnapshot(serial);
}
diff --git a/searchcore/src/vespa/searchcore/proton/server/proton_disk_layout.cpp b/searchcore/src/vespa/searchcore/proton/server/proton_disk_layout.cpp
index 1bc24d4a0e3..31fd44eec5e 100644
--- a/searchcore/src/vespa/searchcore/proton/server/proton_disk_layout.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/proton_disk_layout.cpp
@@ -3,15 +3,75 @@
#include "proton_disk_layout.h"
#include <vespa/vespalib/io/fileutil.h>
#include <vespa/fastos/file.h>
+#include <vespa/searchcore/proton/common/doctypename.h>
+#include <vespa/searchlib/transactionlog/translogclient.h>
#include <cassert>
+#include <vespa/log/log.h>
+LOG_SETUP(".proton.server.proton_disk_layout");
+
+using search::transactionlog::TransLogClient;
+
namespace proton {
+namespace {
+
+struct DocumentDBDirMeta
+{
+ bool normal;
+ bool removed;
+
+ DocumentDBDirMeta()
+ : normal(false),
+ removed(false)
+ {
+ }
+};
+
+using DocumentDBDirScan = std::map<DocTypeName, DocumentDBDirMeta>;
+
+vespalib::string getDocumentsDir(const vespalib::string &baseDir)
+{
+ return baseDir + "/documents";
+}
+
+vespalib::string removedSuffix(".removed");
+
+vespalib::string getNormalName(const vespalib::string removedName) {
+ return removedName.substr(0, removedName.size() - removedSuffix.size());
+}
+
+vespalib::string getRemovedName(const vespalib::string &normalName)
+{
+ return normalName + removedSuffix;
+}
+
+bool isRemovedName(const vespalib::string &dirName)
+{
+ return dirName.size() > removedSuffix.size() && dirName.substr(dirName.size() - removedSuffix.size()) == removedSuffix;
+}
+
+void scanDir(const vespalib::string documentsDir, DocumentDBDirScan &dirs)
+{
+ auto names = vespalib::listDirectory(documentsDir);
+ for (const auto &name : names) {
+ if (vespalib::isDirectory(documentsDir + "/" + name)) {
+ if (isRemovedName(name)) {
+ dirs[DocTypeName(getNormalName(name))].removed = true;
+ } else {
+ dirs[DocTypeName(name)].normal = true;
+ }
+ }
+ }
+}
+
+}
+
ProtonDiskLayout::ProtonDiskLayout(const vespalib::string &baseDir, const vespalib::string &tlsSpec)
: _baseDir(baseDir),
_tlsSpec(tlsSpec)
{
- vespalib::mkdir(_baseDir + "/documents", true);
+ vespalib::mkdir(getDocumentsDir(_baseDir), true);
}
ProtonDiskLayout::~ProtonDiskLayout() = default;
@@ -19,13 +79,41 @@ ProtonDiskLayout::~ProtonDiskLayout() = default;
void
ProtonDiskLayout::remove(const DocTypeName &docTypeName)
{
- (void) docTypeName;
+ vespalib::string documentsDir(getDocumentsDir(_baseDir));
+ vespalib::string name(docTypeName.toString());
+ vespalib::string normalDir(documentsDir + "/" + name);
+ vespalib::string removedDir(documentsDir + "/" + getRemovedName(name));
+ vespalib::rename(normalDir, removedDir, false, false);
+ vespalib::File::sync(documentsDir);
+ TransLogClient tlc(_tlsSpec);
+ if (!tlc.remove(name)) {
+ LOG(fatal, "Failed to remove tls domain %s", name.c_str());
+ LOG_ABORT("Failed to remove tls domain");
+ }
+ vespalib::rmdir(removedDir, true);
+ vespalib::File::sync(documentsDir);
}
void
ProtonDiskLayout::initAndPruneUnused(const std::set<DocTypeName> &docTypeNames)
{
- (void) docTypeNames;
+ vespalib::string documentsDir(getDocumentsDir(_baseDir));
+ DocumentDBDirScan dirs;
+ scanDir(documentsDir, dirs);
+ for (const auto &dir : dirs) {
+ if (dir.second.removed) {
+ // Complete interrupted removal
+ if (dir.second.normal) {
+ vespalib::string name(dir.first.toString());
+ vespalib::string normalDir(documentsDir + "/" + name);
+ vespalib::rmdir(normalDir, true);
+ }
+ remove(dir.first);
+ } else if (docTypeNames.count(dir.first) == 0) {
+ // Remove unused directory
+ remove(dir.first);
+ }
+ }
}
} // namespace proton
diff --git a/searchcore/src/vespa/searchcore/proton/server/storeonlydocsubdb.cpp b/searchcore/src/vespa/searchcore/proton/server/storeonlydocsubdb.cpp
index ad06525a5a3..2d3c204d259 100644
--- a/searchcore/src/vespa/searchcore/proton/server/storeonlydocsubdb.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/storeonlydocsubdb.cpp
@@ -139,6 +139,7 @@ StoreOnlyDocSubDB::StoreOnlyDocSubDB(const Config &cfg, const Context &ctx)
_gidToLidChangeHandler(std::make_shared<DummyGidToLidChangeHandler>())
{
vespalib::mkdir(_baseDir, false); // Assume parent is created.
+ vespalib::File::sync(vespalib::dirname(_baseDir));
}
StoreOnlyDocSubDB::~StoreOnlyDocSubDB()
diff --git a/searchcorespi/src/vespa/searchcorespi/index/diskindexcleaner.cpp b/searchcorespi/src/vespa/searchcorespi/index/diskindexcleaner.cpp
index c363a93815b..e0cb8548f0d 100644
--- a/searchcorespi/src/vespa/searchcorespi/index/diskindexcleaner.cpp
+++ b/searchcorespi/src/vespa/searchcorespi/index/diskindexcleaner.cpp
@@ -3,6 +3,7 @@
#include "diskindexcleaner.h"
#include "activediskindexes.h"
#include <vespa/fastos/file.h>
+#include <vespa/vespalib/io/fileutil.h>
#include <sstream>
#include <vector>
@@ -34,6 +35,11 @@ bool isValidIndex(const string &index_dir) {
return serial_file.OpenReadOnlyExisting();
}
+void invalidateIndex(const string &index_dir) {
+ vespalib::unlink(index_dir + "/serial.dat");
+ vespalib::File::sync(index_dir);
+}
+
uint32_t findLastFusionId(const string &base_dir,
const vector<string> &indexes) {
uint32_t fusion_id = 0;
@@ -56,7 +62,8 @@ uint32_t findLastFusionId(const string &base_dir,
void removeDir(const string &dir) {
LOG(debug, "Removing index dir '%s'", dir.c_str());
- FastOS_FileInterface::EmptyAndRemoveDirectory(dir.c_str());
+ invalidateIndex(dir);
+ vespalib::rmdir(dir, true);
}
bool isOldIndex(const string &index, uint32_t last_fusion_id) {
@@ -73,14 +80,18 @@ bool isOldIndex(const string &index, uint32_t last_fusion_id) {
}
void removeOld(const string &base_dir, const vector<string> &indexes,
- const ActiveDiskIndexes &active_indexes) {
+ const ActiveDiskIndexes &active_indexes, bool remove) {
uint32_t last_fusion_id = findLastFusionId(base_dir, indexes);
for (size_t i = 0; i < indexes.size(); ++i) {
const string index_dir = base_dir + "/" + indexes[i];
if (isOldIndex(indexes[i], last_fusion_id) &&
!active_indexes.isActive(index_dir))
{
- removeDir(index_dir);
+ if (remove) {
+ removeDir(index_dir);
+ } else {
+ invalidateIndex(index_dir);
+ }
}
}
}
@@ -99,14 +110,14 @@ void removeInvalid(const string &base_dir, const vector<string> &indexes) {
void DiskIndexCleaner::clean(const string &base_dir,
const ActiveDiskIndexes &active_indexes) {
vector<string> indexes = readIndexes(base_dir);
- removeOld(base_dir, indexes, active_indexes);
+ removeOld(base_dir, indexes, active_indexes, false);
removeInvalid(base_dir, indexes);
}
void DiskIndexCleaner::removeOldIndexes(
const string &base_dir, const ActiveDiskIndexes &active_indexes) {
vector<string> indexes = readIndexes(base_dir);
- removeOld(base_dir, indexes, active_indexes);
+ removeOld(base_dir, indexes, active_indexes, true);
}
}
diff --git a/searchcorespi/src/vespa/searchcorespi/index/indexmaintainer.cpp b/searchcorespi/src/vespa/searchcorespi/index/indexmaintainer.cpp
index a18d24931cb..1e63741084d 100644
--- a/searchcorespi/src/vespa/searchcorespi/index/indexmaintainer.cpp
+++ b/searchcorespi/src/vespa/searchcorespi/index/indexmaintainer.cpp
@@ -16,6 +16,7 @@
#include <vespa/vespalib/util/lambdatask.h>
#include <sstream>
#include <vespa/searchcorespi/flush/closureflushtask.h>
+#include <vespa/vespalib/io/fileutil.h>
#include <vespa/vespalib/util/exceptions.h>
#include <vespa/vespalib/util/array.hpp>
#include <vespa/fastos/file.h>
@@ -999,6 +1000,7 @@ IndexMaintainer::runFusion(const FusionSpec &fusion_spec)
_activeFusionSchema.reset();
_activeFusionPrunedSchema.reset();
}
+ vespalib::File::sync(vespalib::dirname(fail_dir));
return fusion_spec.last_fusion_id;
}
diff --git a/searchcorespi/src/vespa/searchcorespi/index/indexwriteutilities.cpp b/searchcorespi/src/vespa/searchcorespi/index/indexwriteutilities.cpp
index c8b2e81a9c0..ebb316610e1 100644
--- a/searchcorespi/src/vespa/searchcorespi/index/indexwriteutilities.cpp
+++ b/searchcorespi/src/vespa/searchcorespi/index/indexwriteutilities.cpp
@@ -5,6 +5,7 @@
#include <vespa/searchlib/common/serialnumfileheadercontext.h>
#include <vespa/searchlib/index/schemautil.h>
#include <vespa/fastlib/io/bufferedfile.h>
+#include <vespa/vespalib/io/fileutil.h>
#include <vespa/vespalib/util/exceptions.h>
#include <sstream>
#include <unistd.h>
@@ -57,6 +58,7 @@ IndexWriteUtilities::writeSerialNum(SerialNum serialNum,
tmpFileName.c_str());
}
file.Close();
+ vespalib::File::sync(dir);
if (ok) {
FastOS_File renameFile(tmpFileName.c_str());
@@ -67,6 +69,7 @@ IndexWriteUtilities::writeSerialNum(SerialNum serialNum,
msg << "Unable to write serial number to '" << dir << "'.";
throw IllegalStateException(msg.str());
}
+ vespalib::File::sync(dir);
}
bool
@@ -94,12 +97,14 @@ IndexWriteUtilities::copySerialNumFile(const vespalib::string &sourceDir,
return false;
}
file.Close();
+ vespalib::File::sync(destDir);
if (!file.Rename(dest.c_str())) {
LOG(error,
"Unable to rename file '%s' to '%s'",
tmpDest.c_str(), dest.c_str());
return false;
}
+ vespalib::File::sync(destDir);
return true;
}
@@ -151,6 +156,7 @@ IndexWriteUtilities::updateDiskIndexSchema(const vespalib::string &indexDir,
}
vespalib::string schemaTmpName = schemaName + ".tmp";
vespalib::string schemaOrigName = schemaName + ".orig";
+ vespalib::unlink(schemaTmpName);
if (!newSchema->saveToFile(schemaTmpName)) {
LOG(error, "Could not save schema to '%s'",
schemaTmpName.c_str());
@@ -172,6 +178,7 @@ IndexWriteUtilities::updateDiskIndexSchema(const vespalib::string &indexDir,
schemaName.c_str(),
FastOS_File::getLastErrorString().c_str());
}
+ vespalib::File::sync(indexDir);
}
// XXX: FastOS layer violation
int renameres = ::rename(schemaTmpName.c_str(), schemaName.c_str());
@@ -183,6 +190,7 @@ IndexWriteUtilities::updateDiskIndexSchema(const vespalib::string &indexDir,
schemaName.c_str(),
errString.c_str());
}
+ vespalib::File::sync(indexDir);
}
} // namespace index
diff --git a/searchlib/src/vespa/searchlib/common/indexmetainfo.cpp b/searchlib/src/vespa/searchlib/common/indexmetainfo.cpp
index 10015bb658a..837c38eb340 100644
--- a/searchlib/src/vespa/searchlib/common/indexmetainfo.cpp
+++ b/searchlib/src/vespa/searchlib/common/indexmetainfo.cpp
@@ -1,6 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "indexmetainfo.h"
+#include <vespa/vespalib/io/fileutil.h>
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/util/guard.h>
#include <cassert>
@@ -310,6 +311,7 @@ IndexMetaInfo::save(const vespalib::string &baseName)
{
vespalib::string fileName = makeFileName(baseName);
vespalib::string newName = fileName + ".new";
+ vespalib::unlink(newName);
vespalib::FilePointer f(fopen(newName.c_str(), "w"));
if (!f.valid()) {
LOG(warning, "could not open file for writing: %s", newName.c_str());
@@ -350,6 +352,7 @@ IndexMetaInfo::save(const vespalib::string &baseName)
newName.c_str(), fileName.c_str());
return false;
}
+ vespalib::File::sync(vespalib::dirname(fileName));
return true;
}
diff --git a/searchlib/src/vespa/searchlib/diskindex/fusion.cpp b/searchlib/src/vespa/searchlib/diskindex/fusion.cpp
index e61fe7bab17..2db6025d257 100644
--- a/searchlib/src/vespa/searchlib/diskindex/fusion.cpp
+++ b/searchlib/src/vespa/searchlib/diskindex/fusion.cpp
@@ -218,6 +218,7 @@ Fusion::mergeField(uint32_t id)
}
if (!FileKit::createStamp(indexDir + "/.mergeocc_done"))
return false;
+ vespalib::File::sync(indexDir);
if (!CleanTmpDirs())
return false;
diff --git a/searchlib/src/vespa/searchlib/diskindex/indexbuilder.cpp b/searchlib/src/vespa/searchlib/diskindex/indexbuilder.cpp
index b879e1f65a6..b744a68932d 100644
--- a/searchlib/src/vespa/searchlib/diskindex/indexbuilder.cpp
+++ b/searchlib/src/vespa/searchlib/diskindex/indexbuilder.cpp
@@ -700,6 +700,7 @@ IndexBuilder::close()
for (FieldHandle & fh : _fields) {
if (fh.getValid()) {
fh.close();
+ vespalib::File::sync(fh.getDir());
}
}
if (!docsummary::DocumentSummary::writeDocIdLimit(_prefix, _docIdLimit)) {
diff --git a/searchlib/src/vespa/searchlib/transactionlog/domain.cpp b/searchlib/src/vespa/searchlib/transactionlog/domain.cpp
index 88c2dd9ecc3..1caff132779 100644
--- a/searchlib/src/vespa/searchlib/transactionlog/domain.cpp
+++ b/searchlib/src/vespa/searchlib/transactionlog/domain.cpp
@@ -3,6 +3,7 @@
#include "domain.h"
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/util/closuretask.h>
+#include <vespa/vespalib/io/fileutil.h>
#include <vespa/fastos/file.h>
#include <algorithm>
#include <thread>
@@ -60,6 +61,7 @@ Domain::Domain(const string &domainName, const string & baseDir, Executor & comm
_sessionExecutor.sync();
if (_parts.empty() || _parts.crbegin()->second->isClosed()) {
_parts[lastPart].reset(new DomainPart(_name, dir(), lastPart, _defaultCrcType, _fileHeaderContext, false));
+ vespalib::File::sync(dir());
}
}
@@ -294,6 +296,7 @@ void Domain::commit(const Packet & packet)
_parts[entry.serial()] = dp;
}
dp = _parts.rbegin()->second;
+ vespalib::File::sync(dir());
}
dp->commit(entry.serial(), packet);
cleanSessions();
@@ -310,6 +313,7 @@ bool Domain::erase(SerialNum to)
_parts.erase(it);
}
retval = retval && dp->erase(to);
+ vespalib::File::sync(dir());
}
if (_parts.begin()->second->range().to() >= to) {
_parts.begin()->second->erase(to);
diff --git a/searchlib/src/vespa/searchlib/transactionlog/translogserver.cpp b/searchlib/src/vespa/searchlib/transactionlog/translogserver.cpp
index 4c3c5609a93..bfca137ba06 100644
--- a/searchlib/src/vespa/searchlib/transactionlog/translogserver.cpp
+++ b/searchlib/src/vespa/searchlib/transactionlog/translogserver.cpp
@@ -336,6 +336,29 @@ void TransLogServer::exportRPC(FRT_Supervisor & supervisor)
rb.ReturnDesc("syncedto", "Entry synced to");
}
+namespace {
+
+void
+writeDomainDir(std::lock_guard<std::mutex> &guard,
+ vespalib::string dir,
+ vespalib::string domainList,
+ std::map<vespalib::string, std::shared_ptr<Domain>> &domains)
+{
+ (void) guard;
+ vespalib::string domainListTmp(domainList + ".tmp");
+ vespalib::unlink(domainListTmp);
+ std::ofstream domainDir(domainListTmp.c_str(), std::ios::trunc);
+ for (const auto &domainEntry : domains) {
+ domainDir << domainEntry.first << std::endl;
+ }
+ domainDir.close();
+ vespalib::File::sync(domainListTmp);
+ vespalib::rename(domainListTmp, domainList, false, false);
+ vespalib::File::sync(dir);
+}
+
+}
+
void TransLogServer::createDomain(FRT_RPCRequest *req)
{
uint32_t retval(0);
@@ -351,12 +374,9 @@ void TransLogServer::createDomain(FRT_RPCRequest *req)
try {
domain = std::make_shared<Domain>(domainName, dir(), _commitExecutor, _sessionExecutor,
_domainPartSize, _defaultCrcType, _fileHeaderContext);
- {
- Guard domainGuard(_lock);
- _domains[domain->name()] = domain;
- }
- std::ofstream domainDir(domainList().c_str(), std::ios::app);
- domainDir << domain->name() << std::endl;
+ Guard domainGuard(_lock);
+ _domains[domain->name()] = domain;
+ writeDomainDir(domainGuard, dir(), domainList(), _domains);
} catch (const std::exception & e) {
LOG(warning, "Failed creating %s domain. Exception = %s", domainName, e.what());
retval = uint32_t(-1);
@@ -385,12 +405,10 @@ void TransLogServer::deleteDomain(FRT_RPCRequest *req)
Guard domainGuard(_lock);
_domains.erase(domainName);
}
- vespalib::rmdir(Domain::getDir(dir(), domainName).c_str(), true);
- std::ofstream domainDir(domainList().c_str(), std::ios::trunc);
+ vespalib::rmdir(Domain::getDir(dir(), domainName), true);
+ vespalib::File::sync(dir());
Guard domainGuard(_lock);
- for (DomainList::const_iterator it(_domains.begin()), mt(_domains.end()); it != mt; it++) {
- domainDir << it->first << std::endl;
- }
+ writeDomainDir(domainGuard, dir(), domainList(), _domains);
} catch (const std::exception & e) {
msg = make_string("Failed deleting %s domain. Exception = %s", domainName, e.what());
retval = -1;
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/EntityBindingsMapper.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/EntityBindingsMapper.java
index 3bfe492a125..a01bbe2fae1 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/EntityBindingsMapper.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/EntityBindingsMapper.java
@@ -5,14 +5,11 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.yahoo.vespa.athenz.api.AthenzService;
-import com.yahoo.vespa.athenz.identityprovider.api.bindings.IdentityDocumentEntity;
import com.yahoo.vespa.athenz.identityprovider.api.bindings.SignedIdentityDocumentEntity;
-import com.yahoo.vespa.athenz.identityprovider.api.bindings.VespaUniqueInstanceIdEntity;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Path;
-import java.util.Base64;
import static com.yahoo.vespa.athenz.identityprovider.api.VespaUniqueInstanceId.fromDottedString;
@@ -35,29 +32,12 @@ public class EntityBindingsMapper {
}
}
- public static VespaUniqueInstanceId toVespaUniqueInstanceId(VespaUniqueInstanceIdEntity entity) {
- return new VespaUniqueInstanceId(
- entity.clusterIndex, entity.clusterId, entity.instance, entity.application, entity.tenant, entity.region, entity.environment, entity.type != null ? IdentityType.fromId(entity.type) : null); // TODO Remove support for legacy representation without type
- }
-
- public static IdentityDocument toIdentityDocument(IdentityDocumentEntity entity) {
- return new IdentityDocument(
- toVespaUniqueInstanceId(entity.providerUniqueId),
- entity.configServerHostname,
- entity.instanceHostname,
- entity.createdAt,
- entity.ipAddresses);
- }
-
public static SignedIdentityDocument toSignedIdentityDocument(SignedIdentityDocumentEntity entity) {
return new SignedIdentityDocument(
- entity.identityDocument != null ? toIdentityDocument(entity.identityDocument) : null,
entity.signature,
entity.signingKeyVersion,
fromDottedString(entity.providerUniqueId),
- entity.dnsSuffix,
new AthenzService(entity.providerService),
- entity.ztsEndpoint,
entity.documentVersion,
entity.configServerHostname,
entity.instanceHostname,
@@ -66,42 +46,18 @@ public class EntityBindingsMapper {
entity.identityType != null ? IdentityType.fromId(entity.identityType) : null); // TODO Remove support for legacy representation without type
}
- public static VespaUniqueInstanceIdEntity toVespaUniqueInstanceIdEntity(VespaUniqueInstanceId model) {
- return new VespaUniqueInstanceIdEntity(
- model.tenant(), model.application(), model.environment(), model.region(),
- model.instance(), model.clusterId(), model.clusterIndex(), model.type() != null ? model.type().id() : null); // TODO Remove support for legacy representation without type
- }
-
- public static IdentityDocumentEntity toIdentityDocumentEntity(IdentityDocument model) {
- return new IdentityDocumentEntity(
- toVespaUniqueInstanceIdEntity(model.providerUniqueId()),
+ public static SignedIdentityDocumentEntity toSignedIdentityDocumentEntity(SignedIdentityDocument model) {
+ return new SignedIdentityDocumentEntity(
+ model.signature(),
+ model.signingKeyVersion(),
+ model.providerUniqueId().asDottedString(),
+ model.providerService().getFullName(),
+ model.documentVersion(),
model.configServerHostname(),
model.instanceHostname(),
model.createdAt(),
- model.ipAddresses());
- }
-
- public static SignedIdentityDocumentEntity toSignedIdentityDocumentEntity(SignedIdentityDocument model) {
- try {
- IdentityDocumentEntity identityDocumentEntity = model.identityDocument() != null ? toIdentityDocumentEntity(model.identityDocument()) : null;
- String rawDocument = Base64.getEncoder().encodeToString(mapper.writeValueAsString(identityDocumentEntity).getBytes());
- return new SignedIdentityDocumentEntity(
- rawDocument,
- model.signature(),
- model.signingKeyVersion(),
- model.providerUniqueId().asDottedString(),
- model.dnsSuffix(),
- model.providerService().getFullName(),
- model.ztsEndpoint(),
- model.documentVersion(),
- model.configServerHostname(),
- model.instanceHostname(),
- model.createdAt(),
- model.ipAddresses(),
- model.identityType() != null ? model.identityType().id() : null); // TODO Remove support for legacy representation without type
- } catch (JsonProcessingException e) {
- throw new RuntimeException(e);
- }
+ model.ipAddresses(),
+ model.identityType() != null ? model.identityType().id() : null); // TODO Remove support for legacy representation without type
}
public static SignedIdentityDocument readSignedIdentityDocumentFromFile(Path file) {
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/IdentityDocument.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/IdentityDocument.java
deleted file mode 100644
index 82d0a3d622c..00000000000
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/IdentityDocument.java
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.athenz.identityprovider.api;
-
-import java.time.Instant;
-import java.util.Set;
-
-/**
- * The identity document that contains the instance specific information
- *
- * @author bjorncs
- * @deprecated Will soon be inlined into {@link SignedIdentityDocument}
- */
-@Deprecated
-public class IdentityDocument {
- private final VespaUniqueInstanceId providerUniqueId;
- private final String configServerHostname;
- private final String instanceHostname;
- private final Instant createdAt;
- private final Set<String> ipAddresses;
-
- public IdentityDocument(VespaUniqueInstanceId providerUniqueId,
- String configServerHostname,
- String instanceHostname,
- Instant createdAt,
- Set<String> ipAddresses) {
- this.providerUniqueId = providerUniqueId;
- this.configServerHostname = configServerHostname;
- this.instanceHostname = instanceHostname;
- this.createdAt = createdAt;
- this.ipAddresses = ipAddresses;
- }
-
- public VespaUniqueInstanceId providerUniqueId() {
- return providerUniqueId;
- }
-
- public String configServerHostname() {
- return configServerHostname;
- }
-
- public String instanceHostname() {
- return instanceHostname;
- }
-
- public Instant createdAt() {
- return createdAt;
- }
-
- public Set<String> ipAddresses() {
- return ipAddresses;
- }
-}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/SignedIdentityDocument.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/SignedIdentityDocument.java
index aa1dbd4dac3..dc5dae9d516 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/SignedIdentityDocument.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/SignedIdentityDocument.java
@@ -3,12 +3,11 @@ package com.yahoo.vespa.athenz.identityprovider.api;
import com.yahoo.vespa.athenz.api.AthenzService;
-import java.net.URI;
import java.time.Instant;
import java.util.Set;
/**
- * A signed identity document which contains a {@link IdentityDocument}
+ * A signed identity document
*
* @author bjorncs
*/
@@ -16,13 +15,10 @@ public class SignedIdentityDocument {
public static final int DEFAULT_KEY_VERSION = 0;
public static final int DEFAULT_DOCUMENT_VERSION = 1;
- private final IdentityDocument identityDocument;
private final String signature;
private final int signingKeyVersion;
private final VespaUniqueInstanceId providerUniqueId;
- private final String dnsSuffix;
private final AthenzService providerService;
- private final URI ztsEndpoint;
private final int documentVersion;
private final String configServerHostname;
private final String instanceHostname;
@@ -30,26 +26,20 @@ public class SignedIdentityDocument {
private final Set<String> ipAddresses;
private final IdentityType identityType;
- public SignedIdentityDocument(IdentityDocument identityDocument,
- String signature,
+ public SignedIdentityDocument(String signature,
int signingKeyVersion,
VespaUniqueInstanceId providerUniqueId,
- String dnsSuffix,
AthenzService providerService,
- URI ztsEndpoint,
int documentVersion,
String configServerHostname,
String instanceHostname,
Instant createdAt,
Set<String> ipAddresses,
IdentityType identityType) {
- this.identityDocument = identityDocument;
this.signature = signature;
this.signingKeyVersion = signingKeyVersion;
this.providerUniqueId = providerUniqueId;
- this.dnsSuffix = dnsSuffix;
this.providerService = providerService;
- this.ztsEndpoint = ztsEndpoint;
this.documentVersion = documentVersion;
this.configServerHostname = configServerHostname;
this.instanceHostname = instanceHostname;
@@ -58,11 +48,6 @@ public class SignedIdentityDocument {
this.identityType = identityType;
}
- @Deprecated
- public IdentityDocument identityDocument() {
- return identityDocument;
- }
-
public String signature() {
return signature;
}
@@ -75,20 +60,10 @@ public class SignedIdentityDocument {
return providerUniqueId;
}
- @Deprecated
- public String dnsSuffix() {
- return dnsSuffix;
- }
-
public AthenzService providerService() {
return providerService;
}
- @Deprecated
- public URI ztsEndpoint() {
- return ztsEndpoint;
- }
-
public int documentVersion() {
return documentVersion;
}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/IdentityDocumentEntity.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/IdentityDocumentEntity.java
deleted file mode 100644
index b4b2e82ab0e..00000000000
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/IdentityDocumentEntity.java
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.athenz.identityprovider.api.bindings;
-
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-import java.time.Instant;
-import java.util.Objects;
-import java.util.Set;
-
-/**
- * @author bjorncs
- * @deprecated Will soon be inlined into {@link SignedIdentityDocumentEntity}
- */
-@JsonIgnoreProperties(ignoreUnknown = true)
-@Deprecated
-public class IdentityDocumentEntity {
-
- @JsonProperty("provider-unique-id")
- public final VespaUniqueInstanceIdEntity providerUniqueId;
- @JsonProperty("configserver-hostname")
- public final String configServerHostname;
- @JsonProperty("instance-hostname")
- public final String instanceHostname;
- @JsonProperty("created-at")
- public final Instant createdAt;
- @JsonProperty("ip-addresses")
- public final Set<String> ipAddresses;
-
- public IdentityDocumentEntity(
- @JsonProperty("provider-unique-id") VespaUniqueInstanceIdEntity providerUniqueId,
- @JsonProperty("configserver-hostname") String configServerHostname,
- @JsonProperty("instance-hostname") String instanceHostname,
- @JsonProperty("created-at") Instant createdAt,
- @JsonProperty("ip-addresses") Set<String> ipAddresses) {
- this.providerUniqueId = providerUniqueId;
- this.configServerHostname = configServerHostname;
- this.instanceHostname = instanceHostname;
- this.createdAt = createdAt;
- this.ipAddresses = ipAddresses;
- }
-
-
- @Override
- public String toString() {
- return "IdentityDocumentEntity{" +
- "providerUniqueId=" + providerUniqueId +
- ", configServerHostname='" + configServerHostname + '\'' +
- ", instanceHostname='" + instanceHostname + '\'' +
- ", createdAt=" + createdAt +
- ", ipAddresses=" + ipAddresses +
- '}';
- }
-
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- IdentityDocumentEntity that = (IdentityDocumentEntity) o;
- return Objects.equals(providerUniqueId, that.providerUniqueId) &&
- Objects.equals(configServerHostname, that.configServerHostname) &&
- Objects.equals(instanceHostname, that.instanceHostname) &&
- Objects.equals(createdAt, that.createdAt) &&
- Objects.equals(ipAddresses, that.ipAddresses);
- }
-
- @Override
- public int hashCode() {
-
- return Objects.hash(providerUniqueId, configServerHostname, instanceHostname, createdAt, ipAddresses);
- }
-}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/SignedIdentityDocumentEntity.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/SignedIdentityDocumentEntity.java
index 3d4872549d6..52d33f79c1d 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/SignedIdentityDocumentEntity.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/SignedIdentityDocumentEntity.java
@@ -2,17 +2,10 @@
package com.yahoo.vespa.athenz.identityprovider.api.bindings;
import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
-import java.io.IOException;
-import java.io.UncheckedIOException;
-import java.net.URI;
import java.time.Instant;
-import java.util.Base64;
import java.util.Objects;
import java.util.Set;
@@ -22,16 +15,10 @@ import java.util.Set;
@JsonIgnoreProperties(ignoreUnknown = true)
public class SignedIdentityDocumentEntity {
- private static final ObjectMapper mapper = createObjectMapper();
-
- @JsonProperty("identity-document")public final String rawIdentityDocument;
- @JsonIgnore @Deprecated public final IdentityDocumentEntity identityDocument;
@JsonProperty("signature") public final String signature;
@JsonProperty("signing-key-version") public final int signingKeyVersion;
@JsonProperty("provider-unique-id") public final String providerUniqueId; // String representation
- @JsonProperty("dns-suffix") public final String dnsSuffix;
@JsonProperty("provider-service") public final String providerService;
- @JsonProperty("zts-endpoint") public final URI ztsEndpoint;
@JsonProperty("document-version") public final int documentVersion;
@JsonProperty("configserver-hostname") public final String configServerHostname;
@JsonProperty("instance-hostname") public final String instanceHostname;
@@ -40,27 +27,20 @@ public class SignedIdentityDocumentEntity {
@JsonProperty("identity-type") public final String identityType;
@JsonCreator
- public SignedIdentityDocumentEntity(@JsonProperty("identity-document") String rawIdentityDocument,
- @JsonProperty("signature") String signature,
+ public SignedIdentityDocumentEntity(@JsonProperty("signature") String signature,
@JsonProperty("signing-key-version") int signingKeyVersion,
@JsonProperty("provider-unique-id") String providerUniqueId,
- @JsonProperty("dns-suffix") String dnsSuffix,
@JsonProperty("provider-service") String providerService,
- @JsonProperty("zts-endpoint") URI ztsEndpoint,
@JsonProperty("document-version") int documentVersion,
@JsonProperty("configserver-hostname") String configServerHostname,
@JsonProperty("instance-hostname") String instanceHostname,
@JsonProperty("created-at") Instant createdAt,
@JsonProperty("ip-addresses") Set<String> ipAddresses,
@JsonProperty("identity-type") String identityType) {
- this.rawIdentityDocument = rawIdentityDocument;
- this.identityDocument = parseIdentityDocument(rawIdentityDocument);
this.signature = signature;
this.signingKeyVersion = signingKeyVersion;
this.providerUniqueId = providerUniqueId;
- this.dnsSuffix = dnsSuffix;
this.providerService = providerService;
- this.ztsEndpoint = ztsEndpoint;
this.documentVersion = documentVersion;
this.configServerHostname = configServerHostname;
this.instanceHostname = instanceHostname;
@@ -69,31 +49,13 @@ public class SignedIdentityDocumentEntity {
this.identityType = identityType;
}
- private static IdentityDocumentEntity parseIdentityDocument(String rawIdentityDocument) {
- try {
- return mapper.readValue(Base64.getDecoder().decode(rawIdentityDocument), IdentityDocumentEntity.class);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- private static ObjectMapper createObjectMapper() {
- ObjectMapper mapper = new ObjectMapper();
- mapper.registerModule(new JavaTimeModule());
- return mapper;
- }
-
@Override
public String toString() {
return "SignedIdentityDocumentEntity{" +
- "rawIdentityDocument='" + rawIdentityDocument + '\'' +
- ", identityDocument=" + identityDocument +
", signature='" + signature + '\'' +
", signingKeyVersion=" + signingKeyVersion +
", providerUniqueId='" + providerUniqueId + '\'' +
- ", dnsSuffix='" + dnsSuffix + '\'' +
", providerService='" + providerService + '\'' +
- ", ztsEndpoint=" + ztsEndpoint +
", documentVersion=" + documentVersion +
", configServerHostname='" + configServerHostname + '\'' +
", instanceHostname='" + instanceHostname + '\'' +
@@ -110,13 +72,9 @@ public class SignedIdentityDocumentEntity {
SignedIdentityDocumentEntity that = (SignedIdentityDocumentEntity) o;
return signingKeyVersion == that.signingKeyVersion &&
documentVersion == that.documentVersion &&
- Objects.equals(rawIdentityDocument, that.rawIdentityDocument) &&
- Objects.equals(identityDocument, that.identityDocument) &&
Objects.equals(signature, that.signature) &&
Objects.equals(providerUniqueId, that.providerUniqueId) &&
- Objects.equals(dnsSuffix, that.dnsSuffix) &&
Objects.equals(providerService, that.providerService) &&
- Objects.equals(ztsEndpoint, that.ztsEndpoint) &&
Objects.equals(configServerHostname, that.configServerHostname) &&
Objects.equals(instanceHostname, that.instanceHostname) &&
Objects.equals(createdAt, that.createdAt) &&
@@ -126,6 +84,6 @@ public class SignedIdentityDocumentEntity {
@Override
public int hashCode() {
- return Objects.hash(rawIdentityDocument, identityDocument, signature, signingKeyVersion, providerUniqueId, dnsSuffix, providerService, ztsEndpoint, documentVersion, configServerHostname, instanceHostname, createdAt, ipAddresses, identityType);
+ return Objects.hash(signature, signingKeyVersion, providerUniqueId, providerService, documentVersion, configServerHostname, instanceHostname, createdAt, ipAddresses, identityType);
}
}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/VespaUniqueInstanceIdEntity.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/VespaUniqueInstanceIdEntity.java
deleted file mode 100644
index 3fdbb49b28e..00000000000
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/VespaUniqueInstanceIdEntity.java
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.athenz.identityprovider.api.bindings;
-
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-import java.util.Objects;
-
-/**
- * @author bjorncs
- */
-@JsonIgnoreProperties(ignoreUnknown = true)
-public class VespaUniqueInstanceIdEntity {
-
- @JsonProperty("tenant")
- public final String tenant;
- @JsonProperty("application")
- public final String application;
- @JsonProperty("environment")
- public final String environment;
- @JsonProperty("region")
- public final String region;
- @JsonProperty("instance")
- public final String instance;
- @JsonProperty("cluster-id")
- public final String clusterId;
- @JsonProperty("cluster-index")
- public final int clusterIndex;
- @JsonProperty("type")
- public final String type;
-
- @JsonCreator
- public VespaUniqueInstanceIdEntity(@JsonProperty("tenant") String tenant,
- @JsonProperty("application") String application,
- @JsonProperty("environment") String environment,
- @JsonProperty("region") String region,
- @JsonProperty("instance") String instance,
- @JsonProperty("cluster-id") String clusterId,
- @JsonProperty("cluster-index") int clusterIndex,
- @JsonProperty("type") String type) {
- this.tenant = tenant;
- this.application = application;
- this.environment = environment;
- this.region = region;
- this.instance = instance;
- this.clusterId = clusterId;
- this.clusterIndex = clusterIndex;
- this.type = type;
- }
-
- @Deprecated
- public VespaUniqueInstanceIdEntity(String tenant,
- String application,
- String environment,
- String region,
- String instance,
- String clusterId,
- int clusterIndex) {
- this(tenant, application, environment, region, instance, clusterId, clusterIndex, null);
- }
-
-
- @Override
- public String toString() {
- return "VespaUniqueInstanceIdEntity{" +
- "tenant='" + tenant + '\'' +
- ", application='" + application + '\'' +
- ", environment='" + environment + '\'' +
- ", region='" + region + '\'' +
- ", instance='" + instance + '\'' +
- ", clusterId='" + clusterId + '\'' +
- ", clusterIndex=" + clusterIndex +
- ", type='" + type + '\'' +
- '}';
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- VespaUniqueInstanceIdEntity that = (VespaUniqueInstanceIdEntity) o;
- return clusterIndex == that.clusterIndex &&
- Objects.equals(tenant, that.tenant) &&
- Objects.equals(application, that.application) &&
- Objects.equals(environment, that.environment) &&
- Objects.equals(region, that.region) &&
- Objects.equals(instance, that.instance) &&
- Objects.equals(clusterId, that.clusterId) &&
- Objects.equals(type, that.type);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(tenant, application, environment, region, instance, clusterId, clusterIndex, type);
- }
-}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/utils/SiaUtils.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/utils/SiaUtils.java
index 55e9103b040..05459e5488b 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/utils/SiaUtils.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/utils/SiaUtils.java
@@ -7,13 +7,18 @@ import com.yahoo.vespa.athenz.tls.X509CertificateUtils;
import java.io.IOException;
import java.io.UncheckedIOException;
+import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
+import java.util.List;
import java.util.Optional;
+import java.util.stream.StreamSupport;
+
+import static java.util.stream.Collectors.toList;
/**
* Misc utility methods for SIA provided credentials
@@ -105,6 +110,25 @@ public class SiaUtils {
}
}
+ public static List<AthenzService> findSiaServices() {
+ return findSiaServices(DEFAULT_SIA_DIRECTORY);
+ }
+
+ public static List<AthenzService> findSiaServices(Path root) {
+ String keyFileSuffix = ".key.pem";
+ Path keysDirectory = root.resolve("keys");
+ try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(keysDirectory)) {
+ return StreamSupport.stream(directoryStream.spliterator(), false)
+ .map(path -> path.getFileName().toString())
+ .filter(fileName -> fileName.endsWith(keyFileSuffix))
+ .map(fileName -> fileName.substring(0, fileName.length() - keyFileSuffix.length()))
+ .map(AthenzService::new)
+ .collect(toList());
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
private static Path toTempFile(Path file) {
return Paths.get(file.toAbsolutePath().toString() + ".tmp");
}
diff --git a/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSignerTest.java b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSignerTest.java
index 9cc500ee241..38483bdbaee 100644
--- a/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSignerTest.java
+++ b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSignerTest.java
@@ -9,7 +9,6 @@ import com.yahoo.vespa.athenz.tls.KeyAlgorithm;
import com.yahoo.vespa.athenz.tls.KeyUtils;
import org.junit.Test;
-import java.net.URI;
import java.security.KeyPair;
import java.time.Instant;
import java.util.Arrays;
@@ -18,7 +17,7 @@ import java.util.HashSet;
import static com.yahoo.vespa.athenz.identityprovider.api.IdentityType.TENANT;
import static com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument.DEFAULT_DOCUMENT_VERSION;
import static com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument.DEFAULT_KEY_VERSION;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertTrue;
/**
* @author bjorncs
@@ -41,7 +40,7 @@ public class IdentityDocumentSignerTest {
signer.generateSignature(id, providerService, configserverHostname, instanceHostname, createdAt, ipAddresses, identityType, keyPair.getPrivate());
SignedIdentityDocument signedIdentityDocument = new SignedIdentityDocument(
- null, signature, DEFAULT_KEY_VERSION, id, "dns-suffix", providerService, URI.create("https://zts"),
+ signature, DEFAULT_KEY_VERSION, id, providerService,
DEFAULT_DOCUMENT_VERSION, configserverHostname, instanceHostname, createdAt, ipAddresses, identityType);
assertTrue(signer.hasValidSignature(signedIdentityDocument, keyPair.getPublic()));
diff --git a/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/utils/SiaUtilsTest.java b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/utils/SiaUtilsTest.java
new file mode 100644
index 00000000000..0282373cdaf
--- /dev/null
+++ b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/utils/SiaUtilsTest.java
@@ -0,0 +1,40 @@
+package com.yahoo.vespa.athenz.utils;
+
+import com.yahoo.vespa.athenz.api.AthenzService;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author bjorncs
+ */
+public class SiaUtilsTest {
+
+ @Rule
+ public TemporaryFolder tempDirectory = new TemporaryFolder();
+
+ @Test
+ public void it_finds_all_identity_names_from_files_in_sia_keys_directory() throws IOException {
+ Path siaRoot = tempDirectory.getRoot().toPath();
+ Files.createDirectory(siaRoot.resolve("keys"));
+ AthenzService fooService = new AthenzService("my.domain.foo");
+ Files.createFile(SiaUtils.getPrivateKeyFile(siaRoot, fooService));
+ AthenzService barService = new AthenzService("my.domain.bar");
+ Files.createFile(SiaUtils.getPrivateKeyFile(siaRoot, barService));
+
+ List<AthenzService> siaIdentities = SiaUtils.findSiaServices(siaRoot);
+ assertThat(siaIdentities.size(), equalTo(2));
+ assertThat(siaIdentities, hasItem(fooService));
+ assertThat(siaIdentities, hasItem(barService));
+ }
+
+} \ No newline at end of file
diff --git a/vespabase/src/common-env.sh b/vespabase/src/common-env.sh
index 8dfcf8d2c4c..018630b0622 100755
--- a/vespabase/src/common-env.sh
+++ b/vespabase/src/common-env.sh
@@ -107,6 +107,13 @@ populate_environment () {
fi
}
+add_valgrind_suppressions_file() {
+ if [ -f "$1" ]
+ then
+ VESPA_VALGRIND_SUPPREESSIONS_OPT="$VESPA_VALGRIND_SUPPREESSIONS_OPT --suppressions=$1"
+ fi
+}
+
populate_environment
PATH=$VESPA_HOME/bin64:$VESPA_HOME/bin:/usr/local/bin:/usr/X11R6/bin:/sbin:/bin:/usr/sbin:/usr/bin
@@ -119,13 +126,17 @@ if [ "$JAVA_HOME" ] && [ -f "${JAVA_HOME}/bin/java" ]; then
PATH="${PATH}:${JAVA_HOME}/bin"
fi
+VESPA_VALGRIND_SUPPREESSIONS_OPT=""
+add_valgrind_suppressions_file ${VESPA_HOME}/etc/vespa/valgrind-suppressions.txt
+add_valgrind_suppressions_file ${VESPA_HOME}/etc/vespa/suppressions.txt
+
consider_fallback VESPA_VALGRIND_OPT "--num-callers=32 \
--run-libc-freeres=yes \
--track-origins=yes \
--freelist-vol=1000000000 \
--leak-check=full \
--show-reachable=yes \
---suppressions=${VESPA_HOME}/etc/vespa/suppressions.txt"
+${VESPA_VALGRIND_SUPPREESSIONS_OPT}"
consider_fallback VESPA_USE_HUGEPAGES_LIST $(get_var "hugepages_list")
consider_fallback VESPA_USE_HUGEPAGES_LIST "all"
diff --git a/vespalib/src/vespa/vespalib/io/fileutil.cpp b/vespalib/src/vespa/vespalib/io/fileutil.cpp
index e360c84f569..5ab5fb99a0d 100644
--- a/vespalib/src/vespa/vespalib/io/fileutil.cpp
+++ b/vespalib/src/vespa/vespalib/io/fileutil.cpp
@@ -408,6 +408,15 @@ File::sync()
}
}
+void
+File::sync(vespalib::stringref path)
+{
+ File file(path);
+ file.open(READONLY);
+ file.sync();
+ file.close();
+}
+
bool
File::close()
{
diff --git a/vespalib/src/vespa/vespalib/io/fileutil.h b/vespalib/src/vespa/vespalib/io/fileutil.h
index 187939ed412..1b493a9ebf1 100644
--- a/vespalib/src/vespa/vespalib/io/fileutil.h
+++ b/vespalib/src/vespa/vespalib/io/fileutil.h
@@ -190,6 +190,16 @@ public:
*/
static vespalib::string readAll(vespalib::stringref path);
+ /**
+ * Sync file or directory.
+ *
+ * This is a convenience function for the member functions open() and
+ * sync(), see there for more details.
+ *
+ * @throw IoException If we failed to sync the file.
+ */
+ static void sync(vespalib::stringref path);
+
virtual void sync();
virtual bool close();
virtual bool unlink();