summaryrefslogtreecommitdiffstats
path: root/configserver
diff options
context:
space:
mode:
authorHarald Musum <musum@verizonmedia.com>2021-03-11 10:47:12 +0100
committerHarald Musum <musum@verizonmedia.com>2021-03-11 10:47:12 +0100
commit6fd7c4952a8fb3fa53de827a3731ca1dd9803cc1 (patch)
tree973304ccf5de51e11f5192c093de2fd07430432a /configserver
parent35390b007c102370bbad7490cc1cb5542dc1ad17 (diff)
parentb310bcb0d382dcb2f5c481902772c591a77197d8 (diff)
Merge branch 'master' into jonmv/cluster-controller-migration-cleanup-2
Diffstat (limited to 'configserver')
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java12
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/ConfigServerBootstrap.java12
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/application/Application.java5
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/application/ApplicationCuratorDatabase.java2
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/deploy/Deployment.java3
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/http/SecretStoreValidator.java26
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java26
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/PrepareParams.java7
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/Session.java10
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java20
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/tenant/SecretStoreExternalIdRetriever.java2
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantDebugger.java3
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/MockSecretStoreValidator.java7
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/deploy/HostedDeployTest.java16
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/http/SecretStoreValidatorTest.java29
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandlerTest.java10
16 files changed, 116 insertions, 74 deletions
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java b/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java
index 98d0c32c5d5..5d46f3dc240 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java
@@ -29,6 +29,7 @@ import com.yahoo.docproc.jdisc.metric.NullMetric;
import com.yahoo.io.IOUtils;
import com.yahoo.jdisc.Metric;
import com.yahoo.path.Path;
+import com.yahoo.slime.Slime;
import com.yahoo.transaction.NestedTransaction;
import com.yahoo.transaction.Transaction;
import com.yahoo.vespa.config.server.application.Application;
@@ -663,10 +664,10 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
}
}
- public Set<ApplicationId> listApplications() {
+ public List<ApplicationId> listApplications() {
return tenantRepository.getAllTenants().stream()
.flatMap(tenant -> tenant.getApplicationRepo().activeApplications().stream())
- .collect(Collectors.toSet());
+ .collect(Collectors.toList());
}
private boolean isFileLastModifiedBefore(File fileReference, Instant instant) {
@@ -702,9 +703,9 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
: applicationSet.get().getAllVersions(applicationId);
}
- public HttpResponse validateSecretStore(ApplicationId applicationId, TenantSecretStore tenantSecretStore, String tenantSecretName) {
+ public HttpResponse validateSecretStore(ApplicationId applicationId, SystemName systemName, Slime slime) {
Application application = getApplication(applicationId);
- return secretStoreValidator.validateSecretStore(application, tenantSecretStore, tenantSecretName);
+ return secretStoreValidator.validateSecretStore(application, systemName, slime);
}
// ---------------- Convergence ----------------------------------------------------------------
@@ -867,8 +868,7 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
public void deleteExpiredLocalSessions() {
Map<Tenant, Collection<LocalSession>> sessionsPerTenant = new HashMap<>();
tenantRepository.getAllTenants()
- .forEach(tenant -> sessionsPerTenant.put(tenant,
- List.copyOf(tenant.getSessionRepository().getLocalSessions())));
+ .forEach(tenant -> sessionsPerTenant.put(tenant, tenant.getSessionRepository().getLocalSessions()));
Set<ApplicationId> applicationIds = new HashSet<>();
sessionsPerTenant.values()
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/ConfigServerBootstrap.java b/configserver/src/main/java/com/yahoo/vespa/config/server/ConfigServerBootstrap.java
index 3275dc42477..be26e880440 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/ConfigServerBootstrap.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/ConfigServerBootstrap.java
@@ -17,10 +17,11 @@ import com.yahoo.yolean.Exceptions;
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Optional;
-import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -203,7 +204,8 @@ public class ConfigServerBootstrap extends AbstractComponent implements Runnable
private boolean redeployAllApplications() throws InterruptedException {
Instant end = Instant.now().plus(maxDurationOfRedeployment);
- Set<ApplicationId> applicationsNotRedeployed = applicationRepository.listApplications();
+ List<ApplicationId> applicationsNotRedeployed = applicationRepository.listApplications();
+ Collections.shuffle(applicationsNotRedeployed);
long failCount = 0;
do {
applicationsNotRedeployed = redeployApplications(applicationsNotRedeployed);
@@ -225,7 +227,7 @@ public class ConfigServerBootstrap extends AbstractComponent implements Runnable
}
// Returns the set of applications that failed to redeploy
- private Set<ApplicationId> redeployApplications(Set<ApplicationId> applicationIds) throws InterruptedException {
+ private List<ApplicationId> redeployApplications(List<ApplicationId> applicationIds) throws InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(configserverConfig.numParallelTenantLoaders(),
new DaemonThreadFactory("redeploy apps"));
// Keep track of deployment per application
@@ -235,12 +237,12 @@ public class ConfigServerBootstrap extends AbstractComponent implements Runnable
executor.submit(() -> applicationRepository.deployFromLocalActive(appId, true /* bootstrap */)
.ifPresent(Deployment::activate))));
- Set<ApplicationId> failedDeployments =
+ List<ApplicationId> failedDeployments =
deployments.entrySet().stream()
.map(entry -> checkDeployment(entry.getKey(), entry.getValue()))
.filter(Optional::isPresent)
.map(Optional::get)
- .collect(Collectors.toSet());
+ .collect(Collectors.toList());
executor.shutdown();
executor.awaitTermination(365, TimeUnit.DAYS); // Timeout should never happen
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/application/Application.java b/configserver/src/main/java/com/yahoo/vespa/config/server/application/Application.java
index 96dc334ba45..8e98bf5884b 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/application/Application.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/application/Application.java
@@ -146,7 +146,8 @@ public class Application implements ModelResult {
} catch (ConfigurationRuntimeException e) {
// This can happen in cases where services ask for config that no longer exist before they have been able
// to reconfigure themselves
- log.log(Level.INFO, "Error resolving instance for builder '" + builder.getClass().getName() +
+ log.log(Level.INFO, TenantRepository.logPre(getId()) +
+ ": Error resolving instance for builder '" + builder.getClass().getName() +
"', returning empty config: " + Exceptions.toMessageString(e));
payload = ConfigPayload.fromBuilder(new ConfigPayloadBuilder());
}
@@ -178,7 +179,7 @@ public class Application implements ModelResult {
}
private void debug(String message) {
- log.log(Level.FINE, TenantRepository.logPre(getId())+message);
+ log.log(Level.FINE, TenantRepository.logPre(getId()) + message);
}
private ConfigDefinition getTargetDef(GetConfigRequest req) {
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/application/ApplicationCuratorDatabase.java b/configserver/src/main/java/com/yahoo/vespa/config/server/application/ApplicationCuratorDatabase.java
index 2edd0d9fc2a..519e53272dc 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/application/ApplicationCuratorDatabase.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/application/ApplicationCuratorDatabase.java
@@ -77,6 +77,8 @@ public class ApplicationCuratorDatabase {
if ( ! id.tenant().equals(tenant))
throw new IllegalArgumentException("Cannot write application id '" + id + "' for tenant '" + tenant + "'");
try (Lock lock = lock(id)) {
+ if (curator.exists(applicationPath(id))) return;
+
curator.create(applicationPath(id));
modifyReindexing(id, ApplicationReindexing.empty(), UnaryOperator.identity());
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/Deployment.java b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/Deployment.java
index 980134f8884..05e73e7f454 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/Deployment.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/Deployment.java
@@ -219,7 +219,8 @@ public class Deployment implements com.yahoo.config.provision.Deployment {
.ignoreValidationErrors(ignoreValidationErrors)
.isBootstrap(isBootstrap)
.force(force)
- .waitForResourcesInPrepare(waitForResourcesInPrepare);
+ .waitForResourcesInPrepare(waitForResourcesInPrepare)
+ .tenantSecretStores(session.getTenantSecretStores());
session.getDockerImageRepository().ifPresent(params::dockerImageRepository);
session.getAthenzDomain().ifPresent(params::athenzDomain);
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/SecretStoreValidator.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/SecretStoreValidator.java
index 72f5747e15c..71eed450955 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/SecretStoreValidator.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/SecretStoreValidator.java
@@ -3,12 +3,16 @@ package com.yahoo.vespa.config.server.http;
import ai.vespa.util.http.VespaHttpClientBuilder;
import com.yahoo.config.model.api.HostInfo;
+import com.yahoo.config.provision.SystemName;
+import com.yahoo.config.provision.TenantName;
+import com.yahoo.container.jdisc.HttpRequest;
import com.yahoo.container.jdisc.HttpResponse;
import com.yahoo.container.jdisc.secretstore.SecretStore;
import com.yahoo.slime.Slime;
import com.yahoo.slime.SlimeUtils;
import com.yahoo.vespa.config.server.application.Application;
import com.yahoo.config.model.api.TenantSecretStore;
+import com.yahoo.vespa.config.server.tenant.SecretStoreExternalIdRetriever;
import com.yahoo.yolean.Exceptions;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ByteArrayEntity;
@@ -22,6 +26,7 @@ import static com.yahoo.yolean.Exceptions.uncheck;
/**
* @author olaa
+ * Takes the payload received from the controller, adds external ID and acts as a proxy for the AwsParameterStoreValidationHandler result
*/
public class SecretStoreValidator {
@@ -35,22 +40,12 @@ public class SecretStoreValidator {
this.secretStore = secretStore;
}
- public HttpResponse validateSecretStore(Application application, TenantSecretStore tenantSecretStore, String tenantSecretName) {
- var slime = toSlime(tenantSecretStore, tenantSecretName);
+ public HttpResponse validateSecretStore(Application application, SystemName system, Slime slime) {
+ addExternalId(application.getId().tenant(), system, slime);
var uri = getUri(application);
return postRequest(uri, slime);
}
- private Slime toSlime(TenantSecretStore tenantSecretStore, String tenantSecretName) {
- var slime = new Slime();
- var cursor = slime.setObject();
- cursor.setString("externalId", secretStore.getSecret(tenantSecretName));
- cursor.setString("awsId", tenantSecretStore.getAwsId());
- cursor.setString("name", tenantSecretStore.getName());
- cursor.setString("role", tenantSecretStore.getRole());
- return slime;
- }
-
private URI getUri(Application application) {
var hostname = application.getModel().getHosts()
.stream()
@@ -78,4 +73,11 @@ public class SecretStoreValidator {
}
}
+ private void addExternalId(TenantName tenantName, SystemName system, Slime slime) {
+ var data = slime.get();
+ var name = data.field("name").asString();
+ var secretName = SecretStoreExternalIdRetriever.secretName(tenantName, system, name);
+ data.setString("externalId", secretStore.getSecret(secretName));
+ }
+
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java
index ba2989164ee..3634a6825a3 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java
@@ -29,7 +29,6 @@ import com.yahoo.vespa.config.server.http.HttpHandler;
import com.yahoo.vespa.config.server.http.JSONResponse;
import com.yahoo.vespa.config.server.http.NotFoundException;
import com.yahoo.vespa.config.server.tenant.Tenant;
-import com.yahoo.config.model.api.TenantSecretStore;
import java.io.IOException;
import java.net.URLDecoder;
@@ -47,7 +46,6 @@ import java.util.stream.Stream;
import static com.yahoo.yolean.Exceptions.uncheck;
import static java.nio.charset.StandardCharsets.UTF_8;
-import static java.util.Map.Entry.comparingByKey;
import static java.util.stream.Collectors.toList;
/**
@@ -70,7 +68,7 @@ public class ApplicationHandler extends HttpHandler {
"http://*/application/v2/tenant/*/application/*/environment/*/region/*/instance/*/metrics/*",
"http://*/application/v2/tenant/*/application/*/environment/*/region/*/instance/*/metrics/*",
"http://*/application/v2/tenant/*/application/*/environment/*/region/*/instance/*/logs",
- "http://*/application/v2/tenant/*/application/*/environment/*/region/*/instance/*/validate-secret-store/*",
+ "http://*/application/v2/tenant/*/application/*/environment/*/region/*/instance/*/validate-secret-store",
"http://*/application/v2/tenant/*/application/*/environment/*/region/*/instance/*/tester/*/*",
"http://*/application/v2/tenant/*/application/*/environment/*/region/*/instance/*/tester/*",
"http://*/application/v2/tenant/*/application/*/environment/*/region/*/instance/*/quota",
@@ -231,9 +229,8 @@ public class ApplicationHandler extends HttpHandler {
}
if (isValidateSecretStoreRequest(request)) {
- var tenantSecretStore = tenantSecretStoreFromRequest(request);
- var tenantSecretName = tenantSecretNameFromRequest(request);
- return applicationRepository.validateSecretStore(applicationId, tenantSecretStore, tenantSecretName);
+ var slime = uncheck(() -> SlimeUtils.jsonToSlime(request.getData().readAllBytes()));
+ return applicationRepository.validateSecretStore(applicationId, zone.system(), slime);
}
throw new NotFoundException("Illegal POST request '" + request.getUri() + "'");
@@ -360,8 +357,8 @@ public class ApplicationHandler extends HttpHandler {
}
private static boolean isValidateSecretStoreRequest(HttpRequest request) {
- return getBindingMatch(request).groupCount() == 8 &&
- request.getUri().getPath().contains("/validate-secret-store/");
+ return getBindingMatch(request).groupCount() == 7 &&
+ request.getUri().getPath().endsWith("/validate-secret-store");
}
private static boolean isServiceConvergeListRequest(HttpRequest request) {
@@ -424,11 +421,6 @@ public class ApplicationHandler extends HttpHandler {
return bm.group(8);
}
- private static String tenantSecretNameFromRequest(HttpRequest req) {
- BindingMatch<?> bm = getBindingMatch(req);
- return bm.group(7);
- }
-
private static ApplicationId getApplicationIdFromRequest(HttpRequest req) {
// Two bindings for this: with full app id or only application name
BindingMatch<?> bm = getBindingMatch(req);
@@ -533,14 +525,6 @@ public class ApplicationHandler extends HttpHandler {
}
- private TenantSecretStore tenantSecretStoreFromRequest(HttpRequest httpRequest) {
- var data = uncheck(() -> SlimeUtils.jsonToSlime(httpRequest.getData().readAllBytes()).get());
- var awsId = data.field("awsId").asString();
- var name = data.field("name").asString();
- var role = data.field("role").asString();
- return new TenantSecretStore(name, awsId, role);
- }
-
private static JSONResponse createMessageResponse(String message) {
return new JSONResponse(Response.Status.OK) { { object.setString("message", message); } };
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/PrepareParams.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/PrepareParams.java
index fc8fbe4d7d5..5a3e0311db9 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/PrepareParams.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/PrepareParams.java
@@ -205,9 +205,14 @@ public final class PrepareParams {
}
public Builder tenantSecretStores(String serialized) {
- this.tenantSecretStores = (serialized == null)
+ List<TenantSecretStore> secretStores = (serialized == null)
? List.of()
: TenantSecretStoreSerializer.listFromSlime(SlimeUtils.jsonToSlime(serialized).get());
+ return tenantSecretStores(secretStores);
+ }
+
+ public Builder tenantSecretStores(List<TenantSecretStore> tenantSecretStores) {
+ this.tenantSecretStores = tenantSecretStores;
return this;
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/Session.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/Session.java
index 9b74eac5631..f1044b28049 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/Session.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/Session.java
@@ -6,6 +6,7 @@ import com.yahoo.config.FileReference;
import com.yahoo.config.application.api.ApplicationFile;
import com.yahoo.config.application.api.ApplicationMetaData;
import com.yahoo.config.application.api.ApplicationPackage;
+import com.yahoo.config.model.api.TenantSecretStore;
import com.yahoo.config.provision.AllocatedHosts;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.AthenzDomain;
@@ -17,6 +18,7 @@ import com.yahoo.vespa.config.server.application.ApplicationSet;
import com.yahoo.vespa.config.server.tenant.TenantRepository;
import java.time.Instant;
+import java.util.List;
import java.util.Optional;
/**
@@ -131,6 +133,10 @@ public abstract class Session implements Comparable<Session> {
sessionZooKeeperClient.writeAthenzDomain(athenzDomain);
}
+ public void setTenantSecretStores(List<TenantSecretStore> tenantSecretStores) {
+ sessionZooKeeperClient.writeTenantSecretStores(tenantSecretStores);
+ }
+
/** Returns application id read from ZooKeeper. Will throw RuntimeException if not found */
public ApplicationId getApplicationId() {
return sessionZooKeeperClient.readApplicationId()
@@ -162,6 +168,10 @@ public abstract class Session implements Comparable<Session> {
return createSetStatusTransaction(Status.DEACTIVATE);
}
+ public List<TenantSecretStore> getTenantSecretStores() {
+ return sessionZooKeeperClient.readTenantSecretStores();
+ }
+
private Transaction createSetStatusTransaction(Status status) {
return sessionZooKeeperClient.createWriteStatusTransaction(status);
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java
index d2260683495..f56e74c2869 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java
@@ -196,8 +196,9 @@ public class SessionRepository {
return localSessionCache.get(sessionId);
}
+ /** Returns a copy of local sessions */
public Collection<LocalSession> getLocalSessions() {
- return localSessionCache.values();
+ return List.copyOf(localSessionCache.values());
}
private void loadLocalSessions(ExecutorService executor) {
@@ -255,6 +256,7 @@ public class SessionRepository {
session.setVespaVersion(existingSession.getVespaVersion());
session.setDockerImageRepository(existingSession.getDockerImageRepository());
session.setAthenzDomain(existingSession.getAthenzDomain());
+ session.setTenantSecretStores(existingSession.getTenantSecretStores());
return session;
}
@@ -297,8 +299,7 @@ public class SessionRepository {
}
private void deleteAllSessions() {
- List<LocalSession> sessions = new ArrayList<>(localSessionCache.values());
- for (LocalSession session : sessions) {
+ for (LocalSession session : getLocalSessions()) {
deleteLocalSession(session);
}
}
@@ -309,6 +310,11 @@ public class SessionRepository {
return remoteSessionCache.get(sessionId);
}
+ /** Returns a copy of remote sessions */
+ public Collection<RemoteSession> getRemoteSessions() {
+ return List.copyOf(remoteSessionCache.values());
+ }
+
public List<Long> getRemoteSessionsFromZooKeeper() {
return getSessionList(curator.getChildren(sessionsPath));
}
@@ -524,9 +530,7 @@ public class SessionRepository {
private void nodeChanged() {
zkWatcherExecutor.execute(() -> {
Multiset<Session.Status> sessionMetrics = HashMultiset.create();
- for (Session session : remoteSessionCache.values()) {
- sessionMetrics.add(session.getStatus());
- }
+ getRemoteSessions().forEach(session -> sessionMetrics.add(session.getStatus()));
metricUpdater.setNewSessions(sessionMetrics.count(Session.Status.NEW));
metricUpdater.setPreparedSessions(sessionMetrics.count(Session.Status.PREPARE));
metricUpdater.setActivatedSessions(sessionMetrics.count(Session.Status.ACTIVATE));
@@ -556,7 +560,7 @@ public class SessionRepository {
log.log(Level.FINE, () -> "Purging old sessions for tenant '" + tenantName + "'");
Set<LocalSession> toDelete = new HashSet<>();
try {
- for (LocalSession candidate : List.copyOf(localSessionCache.values())) {
+ for (LocalSession candidate : getLocalSessions()) {
Instant createTime = candidate.getCreateTime();
log.log(Level.FINE, () -> "Candidate session for deletion: " + candidate.getSessionId() + ", created: " + createTime);
@@ -629,7 +633,7 @@ public class SessionRepository {
sessionZKClient.createNewSession(clock.instant());
Curator.CompletionWaiter waiter = sessionZKClient.getUploadWaiter();
LocalSession session = new LocalSession(tenantName, sessionId, app, sessionZKClient);
- waiter.awaitCompletion(timeoutBudget.timeLeft());
+ waiter.awaitCompletion(Duration.ofSeconds(Math.min(60, timeoutBudget.timeLeft().getSeconds())));
addLocalSession(session);
return session;
} catch (Exception e) {
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/SecretStoreExternalIdRetriever.java b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/SecretStoreExternalIdRetriever.java
index cd2ae9d9d0c..0c254606169 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/SecretStoreExternalIdRetriever.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/SecretStoreExternalIdRetriever.java
@@ -25,7 +25,7 @@ public class SecretStoreExternalIdRetriever {
.collect(Collectors.toList());
}
- private static String secretName(TenantName tenant, SystemName system, String storeName) {
+ public static String secretName(TenantName tenant, SystemName system, String storeName) {
return String.format(SECRET_NAME_FORMAT, tenantSecretGroup(system), tenant.value(), storeName);
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantDebugger.java b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantDebugger.java
index d378f6e9235..334e53ee11f 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantDebugger.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantDebugger.java
@@ -1,13 +1,13 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.config.server.tenant;
-import java.util.logging.Level;
import com.yahoo.vespa.curator.Curator;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.TreeCache;
import org.apache.curator.framework.recipes.cache.TreeCacheEvent;
import org.apache.curator.framework.recipes.cache.TreeCacheListener;
+import java.util.logging.Level;
import java.util.logging.Logger;
/**
@@ -19,6 +19,7 @@ public class TenantDebugger implements TreeCacheListener {
private static final Logger log = Logger.getLogger(TenantDebugger.class.getName());
+ @SuppressWarnings("deprecation") // TreeCache is deprecated, and recommended replacement is CuratorCache
public TenantDebugger(Curator curator) throws Exception {
TreeCache cache = new TreeCache(curator.framework(), "/config/v2/tenants");
cache.getListenable().addListener(this);
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/MockSecretStoreValidator.java b/configserver/src/test/java/com/yahoo/vespa/config/server/MockSecretStoreValidator.java
index c464af404d9..a02e8a3f3a6 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/MockSecretStoreValidator.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/MockSecretStoreValidator.java
@@ -1,9 +1,12 @@
// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.config.server;
+import com.yahoo.config.provision.SystemName;
import com.yahoo.container.jdisc.HttpResponse;
import com.yahoo.container.jdisc.SecretStoreProvider;
+import com.yahoo.restapi.SlimeJsonResponse;
import com.yahoo.restapi.StringResponse;
+import com.yahoo.slime.Slime;
import com.yahoo.vespa.config.server.application.Application;
import com.yahoo.config.model.api.TenantSecretStore;
import com.yahoo.vespa.config.server.http.SecretStoreValidator;
@@ -17,7 +20,7 @@ public class MockSecretStoreValidator extends SecretStoreValidator {
super(new SecretStoreProvider().get());
}
- public HttpResponse validateSecretStore(Application application, TenantSecretStore tenantSecretStore, String tenantSecretName) {
- return new StringResponse(tenantSecretStore.toString() + " - " + tenantSecretName);
+ public HttpResponse validateSecretStore(Application application, SystemName system, Slime slime) {
+ return new SlimeJsonResponse(slime);
}
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/HostedDeployTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/HostedDeployTest.java
index 11e14192c74..f1404b30f30 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/HostedDeployTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/HostedDeployTest.java
@@ -9,6 +9,7 @@ import com.yahoo.config.model.api.ModelContext;
import com.yahoo.config.model.api.ModelCreateResult;
import com.yahoo.config.model.api.ModelFactory;
import com.yahoo.config.model.api.ServiceInfo;
+import com.yahoo.config.model.api.TenantSecretStore;
import com.yahoo.config.model.api.ValidationParameters;
import com.yahoo.config.model.provision.Host;
import com.yahoo.config.model.provision.Hosts;
@@ -111,6 +112,21 @@ public class HostedDeployTest {
}
@Test
+ public void testRedeployWithTenantSecretStores() throws IOException {
+ List<TenantSecretStore> tenantSecretStores = List.of(new TenantSecretStore("foo", "123", "role"));
+ DeployTester tester = new DeployTester.Builder()
+ .modelFactory(createHostedModelFactory(Version.fromString("4.5.6"), Clock.systemUTC()))
+ .configserverConfig(createConfigserverConfig()).build();
+ tester.deployApp("src/test/apps/hosted/", Instant.now(), new PrepareParams.Builder()
+ .tenantSecretStores(tenantSecretStores));
+
+ Optional<com.yahoo.config.provision.Deployment> deployment = tester.redeployFromLocalActive(tester.applicationId());
+ assertTrue(deployment.isPresent());
+ deployment.get().activate();
+ assertEquals(tenantSecretStores, ((Deployment) deployment.get()).session().getTenantSecretStores());
+ }
+
+ @Test
public void testDeployMultipleVersions() throws IOException {
List<ModelFactory> modelFactories = List.of(createHostedModelFactory(Version.fromString("6.1.0")),
createHostedModelFactory(Version.fromString("6.2.0")),
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/SecretStoreValidatorTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/SecretStoreValidatorTest.java
index d308cd72b55..8e5dd16bc05 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/SecretStoreValidatorTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/SecretStoreValidatorTest.java
@@ -4,9 +4,13 @@ import com.github.tomakehurst.wiremock.junit.WireMockRule;
import com.yahoo.config.model.api.HostInfo;
import com.yahoo.config.model.api.Model;
import com.yahoo.config.model.api.ServiceInfo;
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.SystemName;
+import com.yahoo.config.provision.TenantName;
import com.yahoo.container.jdisc.secretstore.SecretStore;
+import com.yahoo.slime.SlimeUtils;
import com.yahoo.vespa.config.server.application.Application;
-import com.yahoo.config.model.api.TenantSecretStore;
+import com.yahoo.vespa.config.server.tenant.SecretStoreExternalIdRetriever;
import org.junit.Rule;
import org.junit.Test;
@@ -35,20 +39,26 @@ public class SecretStoreValidatorTest {
@Test
public void createsCorrectRequestData() throws IOException {
var app = mockApplication();
- var tenantSecretStore = new TenantSecretStore("store", "123", "role");
- var tenantSecretName = "some-secret";
- when(secretStore.getSecret(tenantSecretName)).thenReturn("some-secret-value");
-
+ var requestBody = SlimeUtils.jsonToSlime("{\"awsId\":\"123\"," +
+ "\"name\":\"store\"," +
+ "\"role\":\"role\"," +
+ "\"region\":\"some-region\"," +
+ "\"parameterName\":\"some-parameter\"" +
+ "}");
+ var expectedSecretName = SecretStoreExternalIdRetriever.secretName(TenantName.defaultName(), SystemName.PublicCd, "store");
+ when(secretStore.getSecret(expectedSecretName)).thenReturn("some-secret-value");
stubFor(post(urlEqualTo("/validate-secret-store"))
- .withRequestBody(equalToJson("{\"externalId\":\"some-secret-value\"," +
- "\"awsId\":\"123\"," +
+ .withRequestBody(equalToJson("{\"awsId\":\"123\"," +
"\"name\":\"store\"," +
- "\"role\":\"role\"" +
+ "\"role\":\"role\"," +
+ "\"region\":\"some-region\"," +
+ "\"parameterName\":\"some-parameter\"," +
+ "\"externalId\":\"some-secret-value\"" +
"}"))
.willReturn(aResponse()
.withStatus(200)
.withBody("is ok")));
- var response = secretStoreValidator.validateSecretStore(app, tenantSecretStore, tenantSecretName);
+ var response = secretStoreValidator.validateSecretStore(app, SystemName.PublicCd, requestBody);
var body = new ByteArrayOutputStream();
response.render(body);
assertEquals("is ok", body.toString());
@@ -60,6 +70,7 @@ public class SecretStoreValidatorTest {
var hostList = createHostList();
when(app.getModel()).thenReturn(model);
when(model.getHosts()).thenReturn(hostList);
+ when(app.getId()).thenReturn(ApplicationId.defaultId());
return app;
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandlerTest.java
index ab9457c5d2a..d364785f415 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandlerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandlerTest.java
@@ -401,17 +401,17 @@ public class ApplicationHandlerTest {
@Test
public void testValidateSecretStore() throws IOException {
applicationRepository.deploy(new File("src/test/apps/app-logserver-with-container"), prepareParams(applicationId));
- var url = toUrlPath(applicationId, Zone.defaultZone(), true) + "/validate-secret-store/some-secret-name";
+ var url = toUrlPath(applicationId, Zone.defaultZone(), true) + "/validate-secret-store";
var mockHandler = createApplicationHandler();
- var requestData = new ByteArrayInputStream("{\"name\": \"store\", \"awsId\":\"aws-id\", \"role\":\"role\"}".getBytes(StandardCharsets.UTF_8));
+ var requestString = "{\"name\":\"store\",\"awsId\":\"aws-id\",\"role\":\"role\",\"region\":\"us-west-1\",\"parameterName\":\"some-parameter\"}";
+ var requestData = new ByteArrayInputStream(requestString.getBytes(StandardCharsets.UTF_8));
var response = mockHandler.handle(createTestRequest(url, POST, requestData));
assertEquals(200, response.getStatus());
- // MockSecretStoreValidator returns response on format tenantSecretStore.toString() - tenantSecretName
- var expectedResponse = "TenantSecretStore{name='store', awsId='aws-id', role='role'} - some-secret-name";
- assertEquals(expectedResponse, getRenderedString(response));
+ // MockSecretStoreValidator simply returns the request body
+ assertEquals(requestString, getRenderedString(response));
}
@Test