aboutsummaryrefslogtreecommitdiffstats
path: root/configserver/src/main/java/com/yahoo
diff options
context:
space:
mode:
Diffstat (limited to 'configserver/src/main/java/com/yahoo')
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java7
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClient.java41
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileDirectory.java31
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileServer.java84
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ApplicationPackageMaintainer.java8
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/rpc/RpcServer.java7
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/Session.java54
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionData.java87
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java37
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java70
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionSerializer.java53
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionZooKeeperClient.java45
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/tenant/DataplaneTokenSerializer.java7
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/tenant/OperatorCertificateSerializer.java15
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantSecretStoreSerializer.java6
15 files changed, 336 insertions, 216 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 3502ece9cb7..c7e4022c668 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
@@ -167,7 +167,6 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
ConfigserverConfig configserverConfig,
Orchestrator orchestrator,
TesterClient testerClient,
- Zone zone,
HealthCheckerProvider healthCheckers,
Metric metric,
SecretStore secretStore,
@@ -698,10 +697,10 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
Optional<String> applicationPackage = Optional.empty();
Optional<Session> session = getActiveSession(applicationId);
if (session.isPresent()) {
- FileReference applicationPackageReference = session.get().getApplicationPackageReference();
+ Optional<FileReference> applicationPackageReference = session.get().getApplicationPackageReference();
File downloadDirectory = new File(Defaults.getDefaults().underVespaHome(configserverConfig().fileReferencesDir()));
- if (applicationPackageReference != null && ! fileReferenceExistsOnDisk(downloadDirectory, applicationPackageReference))
- applicationPackage = Optional.of(applicationPackageReference.value());
+ if (applicationPackageReference.isPresent() && ! fileReferenceExistsOnDisk(downloadDirectory, applicationPackageReference.get()))
+ applicationPackage = Optional.of(applicationPackageReference.get().value());
}
return applicationPackage;
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClient.java b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClient.java
index 0acf32d79a7..efa62625159 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClient.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClient.java
@@ -96,8 +96,8 @@ public class ZooKeeperClient {
Path zkPath = getZooKeeperAppPath(USERAPP_ZK_SUBPATH).append(SCHEMAS_DIR);
curator.create(zkPath);
// Ensures that ranking expressions and other files are also written
- writeDir(app.getFile(ApplicationPackage.SEARCH_DEFINITIONS_DIR), zkPath, true);
- writeDir(app.getFile(ApplicationPackage.SCHEMAS_DIR), zkPath, true);
+ writeDir(app.getFile(ApplicationPackage.SEARCH_DEFINITIONS_DIR), zkPath);
+ writeDir(app.getFile(ApplicationPackage.SCHEMAS_DIR), zkPath);
for (NamedReader sd : schemas) {
curator.set(zkPath.append(sd.getName()), Utf8.toBytes(com.yahoo.io.IOUtils.readAll(sd.getReader())));
sd.getReader().close();
@@ -105,7 +105,7 @@ public class ZooKeeperClient {
}
/**
- * Puts some of the application package files into ZK - see write(app).
+ * Writes some application package files into ZK - see write(app).
*
* @param app the application package to use as input.
* @throws java.io.IOException if not able to write to Zookeeper
@@ -118,45 +118,40 @@ public class ZooKeeperClient {
writeFile(app.getFile(Path.fromString(VALIDATION_OVERRIDES.getName())), getZooKeeperAppPath(USERAPP_ZK_SUBPATH));
writeDir(app.getFile(RULES_DIR),
getZooKeeperAppPath(USERAPP_ZK_SUBPATH).append(RULES_DIR),
- (path) -> path.getName().endsWith(ApplicationPackage.RULES_NAME_SUFFIX),
- true);
+ (path) -> path.getName().endsWith(ApplicationPackage.RULES_NAME_SUFFIX));
writeDir(app.getFile(QUERY_PROFILES_DIR),
getZooKeeperAppPath(USERAPP_ZK_SUBPATH).append(QUERY_PROFILES_DIR),
- xmlFilter, true);
+ xmlFilter);
writeDir(app.getFile(PAGE_TEMPLATES_DIR),
getZooKeeperAppPath(USERAPP_ZK_SUBPATH).append(PAGE_TEMPLATES_DIR),
- xmlFilter, true);
+ xmlFilter);
writeDir(app.getFile(Path.fromString(SEARCHCHAINS_DIR)),
getZooKeeperAppPath(USERAPP_ZK_SUBPATH).append(SEARCHCHAINS_DIR),
- xmlFilter, true);
+ xmlFilter);
writeDir(app.getFile(Path.fromString(DOCPROCCHAINS_DIR)),
getZooKeeperAppPath(USERAPP_ZK_SUBPATH).append(DOCPROCCHAINS_DIR),
- xmlFilter, true);
+ xmlFilter);
writeDir(app.getFile(Path.fromString(ROUTINGTABLES_DIR)),
getZooKeeperAppPath(USERAPP_ZK_SUBPATH).append(ROUTINGTABLES_DIR),
- xmlFilter, true);
+ xmlFilter);
writeDir(app.getFile(MODELS_GENERATED_REPLICATED_DIR),
- getZooKeeperAppPath(USERAPP_ZK_SUBPATH).append(MODELS_GENERATED_REPLICATED_DIR),
- true);
+ getZooKeeperAppPath(USERAPP_ZK_SUBPATH).append(MODELS_GENERATED_REPLICATED_DIR));
writeDir(app.getFile(SECURITY_DIR),
- getZooKeeperAppPath(USERAPP_ZK_SUBPATH).append(SECURITY_DIR),
- true);
+ getZooKeeperAppPath(USERAPP_ZK_SUBPATH).append(SECURITY_DIR));
}
- private void writeDir(ApplicationFile file, Path zooKeeperAppPath, boolean recurse) throws IOException {
- writeDir(file, zooKeeperAppPath, (__) -> true, recurse);
+ private void writeDir(ApplicationFile file, Path zooKeeperAppPath) throws IOException {
+ writeDir(file, zooKeeperAppPath, (__) -> true);
}
- private void writeDir(ApplicationFile dir, Path path, ApplicationFile.PathFilter filenameFilter, boolean recurse) throws IOException {
+ private void writeDir(ApplicationFile dir, Path path, ApplicationFile.PathFilter filenameFilter) throws IOException {
if ( ! dir.isDirectory()) return;
for (ApplicationFile file : listFiles(dir, filenameFilter)) {
String name = file.getPath().getName();
if (name.startsWith(".")) continue; //.svn , .git ...
if (file.isDirectory()) {
curator.create(path.append(name));
- if (recurse) {
- writeDir(file, path.append(name), filenameFilter, recurse);
- }
+ writeDir(file, path.append(name), filenameFilter);
} else {
writeFile(file, path);
}
@@ -202,9 +197,7 @@ public class ZooKeeperClient {
if (files == null || files.isEmpty()) {
curator.create(getZooKeeperAppPath(USERAPP_ZK_SUBPATH + "/" + userInclude));
}
- writeDir(dir,
- getZooKeeperAppPath(USERAPP_ZK_SUBPATH + "/" + userInclude),
- xmlFilter, true);
+ writeDir(dir, getZooKeeperAppPath(USERAPP_ZK_SUBPATH + "/" + userInclude), xmlFilter);
}
}
@@ -249,7 +242,7 @@ public class ZooKeeperClient {
.forEach(path -> curator.delete(getZooKeeperAppPath(path)));
} catch (Exception e) {
logger.log(Level.WARNING, "Could not clean up in zookeeper: " + Exceptions.toMessageString(e));
- //Might be called in an exception handler before re-throw, so do not throw here.
+ // Might be called in an exception handler before re-throw, so do not throw here.
}
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileDirectory.java b/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileDirectory.java
index da18c4e4fcc..6fe133958f5 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileDirectory.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileDirectory.java
@@ -24,12 +24,14 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.time.Clock;
+import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.logging.Logger;
import static com.yahoo.yolean.Exceptions.uncheck;
+import static java.util.logging.Level.INFO;
/**
* Global file directory, holding files for file distribution for all deployed applications.
@@ -40,7 +42,6 @@ public class FileDirectory extends AbstractComponent {
private static final Logger log = Logger.getLogger(FileDirectory.class.getName());
private final Locks<FileReference> locks = new Locks<>(1, TimeUnit.MINUTES);
-
private final File root;
@Inject
@@ -67,7 +68,7 @@ public class FileDirectory extends AbstractComponent {
}
}
- static private class Filter implements FilenameFilter {
+ private static class Filter implements FilenameFilter {
@Override
public boolean accept(File dir, String name) {
return !".".equals(name) && !"..".equals(name) ;
@@ -78,17 +79,23 @@ public class FileDirectory extends AbstractComponent {
return root.getAbsolutePath() + "/" + ref.value();
}
- public File getFile(FileReference reference) {
+ public Optional<File> getFile(FileReference reference) {
ensureRootExist();
File dir = new File(getPath(reference));
- if (!dir.exists())
- throw new IllegalArgumentException("File reference '" + reference.value() + "' with absolute path '" + dir.getAbsolutePath() + "' does not exist.");
- if (!dir.isDirectory())
- throw new IllegalArgumentException("File reference '" + reference.value() + "' with absolute path '" + dir.getAbsolutePath() + "' is not a directory.");
- File [] files = dir.listFiles(new Filter());
- if (files == null || files.length == 0)
- throw new IllegalArgumentException("File reference '" + reference.value() + "' with absolute path '" + dir.getAbsolutePath() + " does not contain any files");
- return files[0];
+ if (!dir.exists()) {
+ log.log(INFO, "File reference '" + reference.value() + "' ('" + dir.getAbsolutePath() + "') does not exist.");
+ return Optional.empty();
+ }
+ if (!dir.isDirectory()) {
+ log.log(INFO, "File reference '" + reference.value() + "' ('" + dir.getAbsolutePath() + ")' is not a directory.");
+ return Optional.empty();
+ }
+ File[] files = dir.listFiles(new Filter());
+ if (files == null || files.length == 0) {
+ log.log(INFO, "File reference '" + reference.value() + "' ('" + dir.getAbsolutePath() + "') does not contain any files");
+ return Optional.empty();
+ }
+ return Optional.of(files[0]);
}
public File getRoot() { return root; }
@@ -136,7 +143,7 @@ public class FileDirectory extends AbstractComponent {
private void deleteDirRecursively(File dir) {
log.log(Level.FINE, "Will delete dir " + dir);
if ( ! IOUtils.recursiveDeleteDir(dir))
- log.log(Level.INFO, "Failed to delete " + dir);
+ log.log(INFO, "Failed to delete " + dir);
}
// Check if we should add file, it might already exist
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileServer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileServer.java
index 57d57d16d2f..e45c3a8e380 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileServer.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileServer.java
@@ -12,7 +12,6 @@ import com.yahoo.jrt.StringValue;
import com.yahoo.jrt.Supervisor;
import com.yahoo.jrt.Transport;
import com.yahoo.vespa.config.ConnectionPool;
-import com.yahoo.vespa.filedistribution.EmptyFileReferenceData;
import com.yahoo.vespa.filedistribution.FileDistributionConnectionPool;
import com.yahoo.vespa.filedistribution.FileDownloader;
import com.yahoo.vespa.filedistribution.FileReferenceCompressor;
@@ -20,14 +19,16 @@ import com.yahoo.vespa.filedistribution.FileReferenceData;
import com.yahoo.vespa.filedistribution.FileReferenceDownload;
import com.yahoo.vespa.filedistribution.LazyFileReferenceData;
import com.yahoo.vespa.filedistribution.LazyTemporaryStorageFileReferenceData;
-import com.yahoo.yolean.Exceptions;
+
import java.io.File;
import java.io.IOException;
+import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -35,6 +36,10 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import static com.yahoo.vespa.config.server.filedistribution.FileDistributionUtil.getOtherConfigServersInCluster;
+import static com.yahoo.vespa.config.server.filedistribution.FileServer.FileApiErrorCodes.NOT_FOUND;
+import static com.yahoo.vespa.config.server.filedistribution.FileServer.FileApiErrorCodes.OK;
+import static com.yahoo.vespa.config.server.filedistribution.FileServer.FileApiErrorCodes.TIMEOUT;
+import static com.yahoo.vespa.config.server.filedistribution.FileServer.FileApiErrorCodes.TRANSFER_FAILED;
import static com.yahoo.vespa.filedistribution.FileReferenceData.CompressionType;
import static com.yahoo.vespa.filedistribution.FileReferenceData.CompressionType.gzip;
import static com.yahoo.vespa.filedistribution.FileReferenceData.Type;
@@ -54,10 +59,11 @@ public class FileServer {
private final List<CompressionType> compressionTypes; // compression types to use, in preferred order
// TODO: Move to filedistribution module, so that it can be used by both clients and servers
- private enum FileApiErrorCodes {
+ enum FileApiErrorCodes {
OK(0, "OK"),
NOT_FOUND(1, "File reference not found"),
- TIMEOUT(2, "Timeout");
+ TIMEOUT(2, "Timeout"),
+ TRANSFER_FAILED(3, "Failed transferring file");
private final int code;
private final String description;
FileApiErrorCodes(int code, String description) {
@@ -103,40 +109,33 @@ public class FileServer {
}
private boolean hasFile(FileReference reference) {
- try {
- return fileDirectory.getFile(reference).exists();
- } catch (IllegalArgumentException e) {
- log.log(Level.FINE, () -> "Failed locating " + reference + ": " + e.getMessage());
- }
+ Optional<File> file = fileDirectory.getFile(reference);
+ if (file.isPresent())
+ return file.get().exists();
+
+ log.log(Level.FINE, () -> "Failed locating " + reference);
return false;
}
FileDirectory getRootDir() { return fileDirectory; }
- void startFileServing(FileReference reference, Receiver target, Set<CompressionType> acceptedCompressionTypes) {
- if ( ! fileDirectory.getFile(reference).exists()) return;
-
- File file = this.fileDirectory.getFile(reference);
- log.log(Level.FINE, () -> "Start serving " + reference + " with file '" + file.getAbsolutePath() + "'");
- FileReferenceData fileData = EmptyFileReferenceData.empty(reference, file.getName());
- try {
- fileData = readFileReferenceData(reference, acceptedCompressionTypes);
+ void startFileServing(FileReference reference, File file, Receiver target, Set<CompressionType> acceptedCompressionTypes) {
+ var absolutePath = file.getAbsolutePath();
+ try (FileReferenceData fileData = fileReferenceData(reference, acceptedCompressionTypes, file)) {
+ log.log(Level.FINE, () -> "Start serving " + reference.value() + " with file '" + absolutePath + "'");
target.receive(fileData, new ReplayStatus(0, "OK"));
- log.log(Level.FINE, () -> "Done serving " + reference.value() + " with file '" + file.getAbsolutePath() + "'");
- } catch (IOException e) {
- String errorDescription = "For" + reference.value() + ": failed reading file '" + file.getAbsolutePath() + "'";
- log.warning(errorDescription + " for sending to '" + target.toString() + "'. " + e.getMessage());
- target.receive(fileData, new ReplayStatus(1, errorDescription));
+ log.log(Level.FINE, () -> "Done serving " + reference.value() + " with file '" + absolutePath + "'");
+ } catch (IOException ioe) {
+ throw new UncheckedIOException("For " + reference.value() + ": failed reading file '" + absolutePath + "'" +
+ " for sending to '" + target.toString() + "'. ", ioe);
} catch (Exception e) {
- log.log(Level.WARNING, "Failed serving " + reference + ": " + Exceptions.toMessageString(e));
- } finally {
- fileData.close();
+ throw new RuntimeException("Failed serving " + reference.value() + " to '" + target + "': ", e);
}
}
- private FileReferenceData readFileReferenceData(FileReference reference, Set<CompressionType> acceptedCompressionTypes) throws IOException {
- File file = this.fileDirectory.getFile(reference);
-
+ private FileReferenceData fileReferenceData(FileReference reference,
+ Set<CompressionType> acceptedCompressionTypes,
+ File file) throws IOException {
if (file.isDirectory()) {
Path tempFile = Files.createTempFile("filereferencedata", reference.value());
CompressionType compressionType = chooseCompressionType(acceptedCompressionTypes);
@@ -172,20 +171,21 @@ public class FileServer {
Set<CompressionType> acceptedCompressionTypes) {
if (Instant.now().isAfter(deadline)) {
log.log(Level.INFO, () -> "Deadline exceeded for request for file reference '" + fileReference + "' from " + client);
- return FileApiErrorCodes.TIMEOUT;
+ return TIMEOUT;
}
- boolean fileExists;
try {
var fileReferenceDownload = new FileReferenceDownload(fileReference, client, downloadFromOtherSourceIfNotFound);
- fileExists = hasFileDownloadIfNeeded(fileReferenceDownload);
- if (fileExists) startFileServing(fileReference, receiver, acceptedCompressionTypes);
- } catch (IllegalArgumentException e) {
- fileExists = false;
+ var file = getFileDownloadIfNeeded(fileReferenceDownload);
+ if (file.isEmpty()) return NOT_FOUND;
+
+ startFileServing(fileReference, file.get(), receiver, acceptedCompressionTypes);
+ } catch (Exception e) {
log.warning("Failed serving file reference '" + fileReference + "', request from " + client + " failed with: " + e.getMessage());
+ return TRANSFER_FAILED;
}
- return (fileExists ? FileApiErrorCodes.OK : FileApiErrorCodes.NOT_FOUND);
+ return OK;
}
/* Choose the first compression type (list is in preferred order) that matches an accepted compression type, or fail */
@@ -198,9 +198,11 @@ public class FileServer {
acceptedCompressionTypes + ", compression types server can use: " + compressionTypes);
}
- boolean hasFileDownloadIfNeeded(FileReferenceDownload fileReferenceDownload) {
+ public Optional<File> getFileDownloadIfNeeded(FileReferenceDownload fileReferenceDownload) {
FileReference fileReference = fileReferenceDownload.fileReference();
- if (hasFile(fileReference)) return true;
+ Optional<File> file = fileDirectory.getFile(fileReference);
+ if (file.isPresent())
+ return file;
if (fileReferenceDownload.downloadFromOtherSourceIfNotFound()) {
log.log(Level.FINE, "File not found, downloading from another source");
@@ -209,13 +211,13 @@ public class FileServer {
FileReferenceDownload newDownload = new FileReferenceDownload(fileReference,
fileReferenceDownload.client(),
false);
- boolean fileExists = downloader.getFile(newDownload).isPresent();
- if ( ! fileExists)
+ file = downloader.getFile(newDownload);
+ if (file.isEmpty())
log.log(Level.INFO, "Failed downloading '" + fileReferenceDownload + "'");
- return fileExists;
+ return file;
} else {
log.log(Level.FINE, "File not found, will not download from another source");
- return false;
+ return Optional.empty();
}
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ApplicationPackageMaintainer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ApplicationPackageMaintainer.java
index 22ef6cc2547..031574bec77 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ApplicationPackageMaintainer.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ApplicationPackageMaintainer.java
@@ -66,15 +66,15 @@ public class ApplicationPackageMaintainer extends ConfigServerMaintainer {
Optional<Session> session = applicationRepository.getActiveSession(applicationId);
if (session.isEmpty()) continue; // App might be deleted after call to listApplications() or not activated yet (bootstrap phase)
- FileReference appFileReference = session.get().getApplicationPackageReference();
- if (appFileReference != null) {
+ Optional<FileReference> appFileReference = session.get().getApplicationPackageReference();
+ if (appFileReference.isPresent()) {
long sessionId = session.get().getSessionId();
attempts++;
- if (!fileReferenceExistsOnDisk(downloadDirectory, appFileReference)) {
+ if (!fileReferenceExistsOnDisk(downloadDirectory, appFileReference.get())) {
log.fine(() -> "Downloading application package with file reference " + appFileReference +
" for " + applicationId + " (session " + sessionId + ")");
- FileReferenceDownload download = new FileReferenceDownload(appFileReference,
+ FileReferenceDownload download = new FileReferenceDownload(appFileReference.get(),
this.getClass().getSimpleName(),
false);
if (fileDownloader.getFile(download).isEmpty()) {
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/RpcServer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/RpcServer.java
index eee7d6ec63d..d26a22284c0 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/RpcServer.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/RpcServer.java
@@ -518,7 +518,7 @@ public class RpcServer implements Runnable, ConfigActivationListener, TenantList
request.parameters().add(new StringValue(fileData.filename()));
request.parameters().add(new StringValue(fileData.type().name()));
request.parameters().add(new Int64Value(fileData.size()));
- // Only add paramter if not gzip, this is default and old clients will not handle the extra parameter
+ // Only add parameter if not gzip, this is default and old clients will not handle the extra parameter
if (fileData.compressionType() != CompressionType.gzip)
request.parameters().add(new StringValue(fileData.compressionType().name()));
return request;
@@ -532,7 +532,7 @@ public class RpcServer implements Runnable, ConfigActivationListener, TenantList
request.parameters().add(new DataValue(buf));
invokeRpcIfValidConnection(request);
if (request.isError()) {
- throw new IllegalArgumentException("Failed delivering reference '" + ref.value() + "' to " +
+ throw new IllegalArgumentException("Failed delivering part of reference '" + ref.value() + "' to " +
target.toString() + " with error: '" + request.errorMessage() + "'.");
} else {
if (request.returnValues().get(0).asInt32() != 0) {
@@ -550,7 +550,8 @@ public class RpcServer implements Runnable, ConfigActivationListener, TenantList
request.parameters().add(new StringValue(status.getDescription()));
invokeRpcIfValidConnection(request);
if (request.isError()) {
- throw new IllegalArgumentException("Failed delivering reference '" + fileData.fileReference().value() + "' with file '" + fileData.filename() + "' to " +
+ throw new IllegalArgumentException("Failed delivering eof for reference '" + fileData.fileReference().value() +
+ "' with file '" + fileData.filename() + "' to " +
target.toString() + " with error: '" + request.errorMessage() + "'.");
} else {
if (request.returnValues().get(0).asInt32() != 0) {
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 b627fe9ba3b..eb359f9ffc6 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
@@ -94,14 +94,10 @@ public abstract class Session implements Comparable<Session> {
* @return log preamble
*/
public String logPre() {
- Optional<ApplicationId> applicationId;
+ Optional<ApplicationId> applicationId = getOptionalApplicationId();
+
// We might not be able to read application id from zookeeper
// e.g. when the app has been deleted. Use tenant name in that case.
- try {
- applicationId = Optional.of(getApplicationId());
- } catch (Exception e) {
- applicationId = Optional.empty();
- }
return applicationId
.filter(appId -> ! appId.equals(ApplicationId.defaultId()))
.map(TenantRepository::logPre)
@@ -116,46 +112,6 @@ public abstract class Session implements Comparable<Session> {
return sessionZooKeeperClient.readActivatedTime();
}
- public void setApplicationId(ApplicationId applicationId) {
- sessionZooKeeperClient.writeApplicationId(applicationId);
- }
-
- void setApplicationPackageReference(FileReference applicationPackageReference) {
- sessionZooKeeperClient.writeApplicationPackageReference(Optional.ofNullable(applicationPackageReference));
- }
-
- public void setVespaVersion(Version version) {
- sessionZooKeeperClient.writeVespaVersion(version);
- }
-
- public void setDockerImageRepository(Optional<DockerImage> dockerImageRepository) {
- sessionZooKeeperClient.writeDockerImageRepository(dockerImageRepository);
- }
-
- public void setAthenzDomain(Optional<AthenzDomain> athenzDomain) {
- sessionZooKeeperClient.writeAthenzDomain(athenzDomain);
- }
-
- public void setQuota(Optional<Quota> quota) {
- sessionZooKeeperClient.writeQuota(quota);
- }
-
- public void setTenantSecretStores(List<TenantSecretStore> tenantSecretStores) {
- sessionZooKeeperClient.writeTenantSecretStores(tenantSecretStores);
- }
-
- public void setOperatorCertificates(List<X509Certificate> operatorCertificates) {
- sessionZooKeeperClient.writeOperatorCertificates(operatorCertificates);
- }
-
- public void setCloudAccount(Optional<CloudAccount> cloudAccount) {
- sessionZooKeeperClient.writeCloudAccount(cloudAccount);
- }
-
- public void setDataplaneTokens(List<DataplaneToken> dataplaneTokens) {
- sessionZooKeeperClient.writeDataplaneTokens(dataplaneTokens);
- }
-
/** Returns application id read from ZooKeeper. Will throw RuntimeException if not found */
public ApplicationId getApplicationId() { return sessionZooKeeperClient.readApplicationId(); }
@@ -168,7 +124,7 @@ public abstract class Session implements Comparable<Session> {
}
}
- public FileReference getApplicationPackageReference() {return sessionZooKeeperClient.readApplicationPackageReference(); }
+ public Optional<FileReference> getApplicationPackageReference() { return sessionZooKeeperClient.readApplicationPackageReference(); }
public Optional<DockerImage> getDockerImageRepository() { return sessionZooKeeperClient.readDockerImageRepository(); }
@@ -202,6 +158,8 @@ public abstract class Session implements Comparable<Session> {
return sessionZooKeeperClient.readDataplaneTokens();
}
+ public SessionZooKeeperClient getSessionZooKeeperClient() { return sessionZooKeeperClient; }
+
private Transaction createSetStatusTransaction(Status status) {
return sessionZooKeeperClient.createWriteStatusTransaction(status);
}
@@ -226,7 +184,7 @@ public abstract class Session implements Comparable<Session> {
return getApplicationPackage().getFile(relativePath);
}
- Optional<ApplicationSet> applicationSet() { return Optional.empty(); };
+ Optional<ApplicationSet> applicationSet() { return Optional.empty(); }
private void markSessionEdited() {
setStatus(Session.Status.NEW);
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionData.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionData.java
new file mode 100644
index 00000000000..1fb72e1253e
--- /dev/null
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionData.java
@@ -0,0 +1,87 @@
+package com.yahoo.vespa.config.server.session;
+
+import com.yahoo.component.Version;
+import com.yahoo.config.FileReference;
+import com.yahoo.config.model.api.Quota;
+import com.yahoo.config.model.api.TenantSecretStore;
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.AthenzDomain;
+import com.yahoo.config.provision.CloudAccount;
+import com.yahoo.config.provision.DataplaneToken;
+import com.yahoo.config.provision.DockerImage;
+import com.yahoo.slime.Cursor;
+import com.yahoo.slime.Slime;
+import com.yahoo.slime.SlimeUtils;
+import com.yahoo.vespa.config.server.tenant.DataplaneTokenSerializer;
+import com.yahoo.vespa.config.server.tenant.OperatorCertificateSerializer;
+import com.yahoo.vespa.config.server.tenant.TenantSecretStoreSerializer;
+
+import java.io.IOException;
+import java.security.cert.X509Certificate;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Data class for session information, typically parameters supplied in a deployment request that needs
+ * to be persisted in ZooKeeper. These will be used when creating a new session based on an existing one.
+ *
+ * @author hmusum
+ */
+public record SessionData(ApplicationId applicationId,
+ Optional<FileReference> applicationPackageReference,
+ Version version,
+ Optional<DockerImage> dockerImageRepository,
+ Optional<AthenzDomain> athenzDomain,
+ Optional<Quota> quota,
+ List<TenantSecretStore> tenantSecretStores,
+ List<X509Certificate> operatorCertificates,
+ Optional<CloudAccount> cloudAccount,
+ List<DataplaneToken> dataplaneTokens) {
+
+ // NOTE: Any state added here MUST also be propagated in com.yahoo.vespa.config.server.deploy.Deployment.prepare()
+ static final String APPLICATION_ID_PATH = "applicationId";
+ static final String APPLICATION_PACKAGE_REFERENCE_PATH = "applicationPackageReference";
+ static final String VERSION_PATH = "version";
+ static final String CREATE_TIME_PATH = "createTime";
+ static final String DOCKER_IMAGE_REPOSITORY_PATH = "dockerImageRepository";
+ static final String ATHENZ_DOMAIN = "athenzDomain";
+ static final String QUOTA_PATH = "quota";
+ static final String TENANT_SECRET_STORES_PATH = "tenantSecretStores";
+ static final String OPERATOR_CERTIFICATES_PATH = "operatorCertificates";
+ static final String CLOUD_ACCOUNT_PATH = "cloudAccount";
+ static final String DATAPLANE_TOKENS_PATH = "dataplaneTokens";
+ static final String SESSION_DATA_PATH = "sessionData";
+
+ public byte[] toJson() {
+ try {
+ Slime slime = new Slime();
+ toSlime(slime.setObject());
+ return SlimeUtils.toJsonBytes(slime);
+ }
+ catch (IOException e) {
+ throw new RuntimeException("Serialization of session data to json failed", e);
+ }
+ }
+
+ private void toSlime(Cursor object) {
+ object.setString(APPLICATION_ID_PATH, applicationId.serializedForm());
+ applicationPackageReference.ifPresent(ref -> object.setString(APPLICATION_PACKAGE_REFERENCE_PATH, ref.value()));
+ object.setString(VERSION_PATH, version.toString());
+ object.setLong(CREATE_TIME_PATH, System.currentTimeMillis());
+ dockerImageRepository.ifPresent(image -> object.setString(DOCKER_IMAGE_REPOSITORY_PATH, image.asString()));
+ athenzDomain.ifPresent(domain -> object.setString(ATHENZ_DOMAIN, domain.value()));
+ quota.ifPresent(q -> q.toSlime(object.setObject(QUOTA_PATH)));
+
+ Cursor tenantSecretStoresArray = object.setArray(TENANT_SECRET_STORES_PATH);
+ TenantSecretStoreSerializer.toSlime(tenantSecretStores, tenantSecretStoresArray);
+
+ Cursor operatorCertificatesArray = object.setArray(OPERATOR_CERTIFICATES_PATH);
+ OperatorCertificateSerializer.toSlime(operatorCertificates, operatorCertificatesArray);
+
+ cloudAccount.ifPresent(account -> object.setString(CLOUD_ACCOUNT_PATH, account.value()));
+
+ Cursor dataplaneTokensArray = object.setArray(DATAPLANE_TOKENS_PATH);
+ DataplaneTokenSerializer.toSlime(dataplaneTokens, dataplaneTokensArray);
+ }
+
+}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java
index ae87a0dd182..8d45ac7e8f1 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java
@@ -36,6 +36,7 @@ import com.yahoo.vespa.config.server.ConfigServerSpec;
import com.yahoo.vespa.config.server.TimeoutBudget;
import com.yahoo.vespa.config.server.application.ApplicationSet;
import com.yahoo.vespa.config.server.configchange.ConfigChangeActions;
+import com.yahoo.vespa.config.server.deploy.ZooKeeperClient;
import com.yahoo.vespa.config.server.deploy.ZooKeeperDeployer;
import com.yahoo.vespa.config.server.filedistribution.FileDistributionFactory;
import com.yahoo.vespa.config.server.host.HostValidator;
@@ -49,7 +50,9 @@ import com.yahoo.vespa.config.server.tenant.EndpointCertificateMetadataStore;
import com.yahoo.vespa.config.server.tenant.EndpointCertificateRetriever;
import com.yahoo.vespa.config.server.tenant.TenantRepository;
import com.yahoo.vespa.curator.Curator;
+import com.yahoo.vespa.flags.BooleanFlag;
import com.yahoo.vespa.flags.FlagSource;
+import com.yahoo.vespa.flags.Flags;
import com.yahoo.vespa.model.application.validation.BundleValidator;
import org.xml.sax.SAXException;
import javax.xml.parsers.ParserConfigurationException;
@@ -71,6 +74,8 @@ import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.zip.ZipException;
+import static com.yahoo.vespa.config.server.session.SessionZooKeeperClient.getSessionPath;
+
/**
* A SessionPreparer is responsible for preparing a session given an application package.
*
@@ -90,6 +95,7 @@ public class SessionPreparer {
private final SecretStore secretStore;
private final FlagSource flagSource;
private final ExecutorService executor;
+ private final BooleanFlag writeSessionData;
public SessionPreparer(ModelFactoryRegistry modelFactoryRegistry,
FileDistributionFactory fileDistributionFactory,
@@ -111,6 +117,7 @@ public class SessionPreparer {
this.secretStore = secretStore;
this.flagSource = flagSource;
this.executor = executor;
+ this.writeSessionData = Flags.WRITE_CONFIG_SERVER_SESSION_DATA_AS_ONE_BLOB.bindTo(flagSource);
}
ExecutorService getExecutor() { return executor; }
@@ -335,7 +342,7 @@ public class SessionPreparer {
writeStateToZooKeeper(sessionZooKeeperClient,
preprocessedApplicationPackage,
applicationId,
- filereference,
+ Optional.of(filereference),
dockerImageRepository,
vespaVersion,
logger,
@@ -377,7 +384,7 @@ public class SessionPreparer {
private void writeStateToZooKeeper(SessionZooKeeperClient zooKeeperClient,
ApplicationPackage applicationPackage,
ApplicationId applicationId,
- FileReference fileReference,
+ Optional<FileReference> fileReference,
Optional<DockerImage> dockerImageRepository,
Version vespaVersion,
DeployLogger deployLogger,
@@ -389,20 +396,22 @@ public class SessionPreparer {
List<X509Certificate> operatorCertificates,
Optional<CloudAccount> cloudAccount,
List<DataplaneToken> dataplaneTokens) {
- ZooKeeperDeployer zkDeployer = zooKeeperClient.createDeployer(deployLogger);
+ Path sessionPath = getSessionPath(applicationId.tenant(), zooKeeperClient.sessionId());
+ ZooKeeperDeployer zkDeployer = new ZooKeeperDeployer(new ZooKeeperClient(curator, deployLogger, sessionPath));
try {
zkDeployer.deploy(applicationPackage, fileRegistryMap, allocatedHosts);
- // Note: When changing the below you need to also change similar calls in SessionRepository.createSessionFromExisting()
- zooKeeperClient.writeApplicationId(applicationId);
- zooKeeperClient.writeApplicationPackageReference(Optional.of(fileReference));
- zooKeeperClient.writeVespaVersion(vespaVersion);
- zooKeeperClient.writeDockerImageRepository(dockerImageRepository);
- zooKeeperClient.writeAthenzDomain(athenzDomain);
- zooKeeperClient.writeQuota(quota);
- zooKeeperClient.writeTenantSecretStores(tenantSecretStores);
- zooKeeperClient.writeOperatorCertificates(operatorCertificates);
- zooKeeperClient.writeCloudAccount(cloudAccount);
- zooKeeperClient.writeDataplaneTokens(dataplaneTokens);
+ new SessionSerializer().write(zooKeeperClient,
+ applicationId,
+ fileReference,
+ dockerImageRepository,
+ vespaVersion,
+ athenzDomain,
+ quota,
+ tenantSecretStores,
+ operatorCertificates,
+ cloudAccount,
+ dataplaneTokens,
+ writeSessionData);
} catch (RuntimeException | IOException e) {
zkDeployer.cleanup();
throw new RuntimeException("Error preparing session", e);
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 f82aa405380..1af728919d9 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
@@ -6,7 +6,6 @@ import com.google.common.collect.Multiset;
import com.yahoo.cloud.config.ConfigserverConfig;
import com.yahoo.concurrent.DaemonThreadFactory;
import com.yahoo.concurrent.StripedExecutor;
-import com.yahoo.config.FileReference;
import com.yahoo.config.application.api.ApplicationPackage;
import com.yahoo.config.application.api.DeployLogger;
import com.yahoo.config.model.api.ConfigDefinitionRepo;
@@ -27,7 +26,6 @@ import com.yahoo.vespa.config.server.application.ApplicationSet;
import com.yahoo.vespa.config.server.application.TenantApplications;
import com.yahoo.vespa.config.server.configchange.ConfigChangeActions;
import com.yahoo.vespa.config.server.deploy.TenantFileSystemDirs;
-import com.yahoo.vespa.config.server.filedistribution.FileDirectory;
import com.yahoo.vespa.config.server.filedistribution.FileDistributionFactory;
import com.yahoo.vespa.config.server.http.InvalidApplicationException;
import com.yahoo.vespa.config.server.http.UnknownVespaVersionException;
@@ -41,7 +39,9 @@ import com.yahoo.vespa.config.server.tenant.TenantRepository;
import com.yahoo.vespa.config.server.zookeeper.SessionCounter;
import com.yahoo.vespa.config.server.zookeeper.ZKApplication;
import com.yahoo.vespa.curator.Curator;
+import com.yahoo.vespa.flags.BooleanFlag;
import com.yahoo.vespa.flags.FlagSource;
+import com.yahoo.vespa.flags.Flags;
import com.yahoo.vespa.flags.LongFlag;
import com.yahoo.vespa.flags.PermanentFlags;
import com.yahoo.vespa.flags.UnboundStringFlag;
@@ -127,6 +127,7 @@ public class SessionRepository {
private final ConfigDefinitionRepo configDefinitionRepo;
private final int maxNodeSize;
private final LongFlag expiryTimeFlag;
+ private final BooleanFlag writeSessionData;
public SessionRepository(TenantName tenantName,
TenantApplications applicationRepo,
@@ -168,7 +169,8 @@ public class SessionRepository {
this.modelFactoryRegistry = modelFactoryRegistry;
this.configDefinitionRepo = configDefinitionRepo;
this.maxNodeSize = maxNodeSize;
- expiryTimeFlag = PermanentFlags.CONFIG_SERVER_SESSION_EXPIRY_TIME.bindTo(flagSource);
+ this.expiryTimeFlag = PermanentFlags.CONFIG_SERVER_SESSION_EXPIRY_TIME.bindTo(flagSource);
+ this.writeSessionData = Flags.WRITE_CONFIG_SERVER_SESSION_DATA_AS_ONE_BLOB.bindTo(flagSource);
loadSessions(); // Needs to be done before creating cache below
this.directoryCache = curator.createDirectoryCache(sessionsPath.getAbsolute(), false, false, zkCacheExecutor);
@@ -266,24 +268,14 @@ public class SessionRepository {
boolean internalRedeploy,
TimeoutBudget timeoutBudget,
DeployLogger deployLogger) {
- ApplicationId existingApplicationId = existingSession.getApplicationId();
+ ApplicationId applicationId = existingSession.getApplicationId();
File existingApp = getSessionAppDir(existingSession.getSessionId());
LocalSession session = createSessionFromApplication(existingApp,
- existingApplicationId,
+ applicationId,
internalRedeploy,
timeoutBudget,
deployLogger);
- // Note: Setters below need to be kept in sync with calls in SessionPreparer.writeStateToZooKeeper()
- session.setApplicationId(existingApplicationId);
- session.setApplicationPackageReference(existingSession.getApplicationPackageReference());
- session.setVespaVersion(existingSession.getVespaVersion());
- session.setDockerImageRepository(existingSession.getDockerImageRepository());
- session.setAthenzDomain(existingSession.getAthenzDomain());
- session.setQuota(existingSession.getQuota());
- session.setTenantSecretStores(existingSession.getTenantSecretStores());
- session.setOperatorCertificates(existingSession.getOperatorCertificates());
- session.setCloudAccount(existingSession.getCloudAccount());
- session.setDataplaneTokens(existingSession.getDataplaneTokens());
+ write(existingSession, session, applicationId);
return session;
}
@@ -534,7 +526,6 @@ public class SessionRepository {
private ApplicationSet loadApplication(Session session, Optional<ApplicationSet> previousApplicationSet) {
log.log(Level.FINE, () -> "Loading application for " + session);
SessionZooKeeperClient sessionZooKeeperClient = createSessionZooKeeperClient(session.getSessionId());
- ApplicationPackage applicationPackage = sessionZooKeeperClient.loadApplicationPackage();
ActivatedModelsBuilder builder = new ActivatedModelsBuilder(session.getTenantName(),
session.getSessionId(),
sessionZooKeeperClient,
@@ -550,9 +541,9 @@ public class SessionRepository {
modelFactoryRegistry,
configDefinitionRepo);
return ApplicationSet.fromList(builder.buildModels(session.getApplicationId(),
- sessionZooKeeperClient.readDockerImageRepository(),
- sessionZooKeeperClient.readVespaVersion(),
- applicationPackage,
+ session.getDockerImageRepository(),
+ session.getVespaVersion(),
+ sessionZooKeeperClient.loadApplicationPackage(),
new AllocatedHostsFromAllModels(),
clock.instant()));
}
@@ -578,6 +569,24 @@ public class SessionRepository {
});
}
+ // ---------------- Serialization ----------------------------------------------------------------
+
+ private void write(Session existingSession, LocalSession session, ApplicationId applicationId) {
+ SessionSerializer sessionSerializer = new SessionSerializer();
+ sessionSerializer.write(session.getSessionZooKeeperClient(),
+ applicationId,
+ existingSession.getApplicationPackageReference(),
+ existingSession.getDockerImageRepository(),
+ existingSession.getVespaVersion(),
+ existingSession.getAthenzDomain(),
+ existingSession.getQuota(),
+ existingSession.getTenantSecretStores(),
+ existingSession.getOperatorCertificates(),
+ existingSession.getCloudAccount(),
+ existingSession.getDataplaneTokens(),
+ writeSessionData);
+ }
+
// ---------------- Common stuff ----------------------------------------------------------------
public void deleteExpiredSessions(Map<ApplicationId, Long> activeSessions) {
@@ -854,23 +863,18 @@ public class SessionRepository {
}
SessionZooKeeperClient sessionZKClient = createSessionZooKeeperClient(sessionId);
- FileReference fileReference = sessionZKClient.readApplicationPackageReference();
+ var fileReference = sessionZKClient.readApplicationPackageReference();
log.log(Level.FINE, () -> "File reference for session id " + sessionId + ": " + fileReference);
- if (fileReference == null) return;
+ if (fileReference.isEmpty()) return;
+
+ Optional<File> sessionDir = fileDistributionFactory.fileDirectory().getFile(fileReference.get());
+ // We cannot be guaranteed that the file reference exists (it could be that it has not
+ // been downloaded yet), and e.g. when bootstrapping we cannot throw an exception in that case
+ if (sessionDir.isEmpty()) return;
- File sessionDir;
- FileDirectory fileDirectory = fileDistributionFactory.fileDirectory();
- try {
- sessionDir = fileDirectory.getFile(fileReference);
- } catch (IllegalArgumentException e) {
- // We cannot be guaranteed that the file reference exists (it could be that it has not
- // been downloaded yet), and e.g. when bootstrapping we cannot throw an exception in that case
- log.log(Level.FINE, () -> "File reference for session id " + sessionId + ": " + fileReference + " not found");
- return;
- }
ApplicationId applicationId = sessionZKClient.readApplicationId();
log.log(Level.FINE, () -> "Creating local session for tenant '" + tenantName + "' with session id " + sessionId);
- createLocalSession(sessionDir, applicationId, sessionId);
+ createLocalSession(sessionDir.get(), applicationId, sessionId);
}
private Optional<Long> getActiveSessionId(ApplicationId applicationId) {
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionSerializer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionSerializer.java
new file mode 100644
index 00000000000..1202b2bd08b
--- /dev/null
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionSerializer.java
@@ -0,0 +1,53 @@
+package com.yahoo.vespa.config.server.session;
+
+import com.yahoo.component.Version;
+import com.yahoo.config.FileReference;
+import com.yahoo.config.model.api.Quota;
+import com.yahoo.config.model.api.TenantSecretStore;
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.AthenzDomain;
+import com.yahoo.config.provision.CloudAccount;
+import com.yahoo.config.provision.DataplaneToken;
+import com.yahoo.config.provision.DockerImage;
+import com.yahoo.vespa.flags.BooleanFlag;
+
+import java.security.cert.X509Certificate;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Serialization and deserialization of session data to/from ZooKeeper.
+ * @author hmusum
+ */
+public class SessionSerializer {
+
+ void write(SessionZooKeeperClient zooKeeperClient, ApplicationId applicationId,
+ Optional<FileReference> fileReference, Optional<DockerImage> dockerImageRepository,
+ Version vespaVersion, Optional<AthenzDomain> athenzDomain, Optional<Quota> quota,
+ List<TenantSecretStore> tenantSecretStores, List<X509Certificate> operatorCertificates,
+ Optional<CloudAccount> cloudAccount, List<DataplaneToken> dataplaneTokens,
+ BooleanFlag writeSessionData) {
+ zooKeeperClient.writeApplicationId(applicationId);
+ zooKeeperClient.writeApplicationPackageReference(fileReference);
+ zooKeeperClient.writeVespaVersion(vespaVersion);
+ zooKeeperClient.writeDockerImageRepository(dockerImageRepository);
+ zooKeeperClient.writeAthenzDomain(athenzDomain);
+ zooKeeperClient.writeQuota(quota);
+ zooKeeperClient.writeTenantSecretStores(tenantSecretStores);
+ zooKeeperClient.writeOperatorCertificates(operatorCertificates);
+ zooKeeperClient.writeCloudAccount(cloudAccount);
+ zooKeeperClient.writeDataplaneTokens(dataplaneTokens);
+ if (writeSessionData.value())
+ zooKeeperClient.writeSessionData(new SessionData(applicationId,
+ fileReference,
+ vespaVersion,
+ dockerImageRepository,
+ athenzDomain,
+ quota,
+ tenantSecretStores,
+ operatorCertificates,
+ cloudAccount,
+ dataplaneTokens));
+ }
+
+} \ No newline at end of file
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionZooKeeperClient.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionZooKeeperClient.java
index 23b6fe075fa..7d1a7ceae4e 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionZooKeeperClient.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionZooKeeperClient.java
@@ -6,7 +6,6 @@ import com.yahoo.component.Version;
import com.yahoo.component.Vtag;
import com.yahoo.config.FileReference;
import com.yahoo.config.application.api.ApplicationPackage;
-import com.yahoo.config.application.api.DeployLogger;
import com.yahoo.config.model.api.ConfigDefinitionRepo;
import com.yahoo.config.model.api.Quota;
import com.yahoo.config.model.api.TenantSecretStore;
@@ -23,8 +22,6 @@ import com.yahoo.text.Utf8;
import com.yahoo.transaction.Transaction;
import com.yahoo.vespa.config.server.NotFoundException;
import com.yahoo.vespa.config.server.UserConfigDefinitionRepo;
-import com.yahoo.vespa.config.server.deploy.ZooKeeperClient;
-import com.yahoo.vespa.config.server.deploy.ZooKeeperDeployer;
import com.yahoo.vespa.config.server.filedistribution.AddFileInterface;
import com.yahoo.vespa.config.server.filedistribution.MockFileManager;
import com.yahoo.vespa.config.server.tenant.CloudAccountSerializer;
@@ -45,6 +42,18 @@ import java.util.List;
import java.util.Optional;
import java.util.logging.Level;
+import static com.yahoo.vespa.config.server.session.SessionData.APPLICATION_ID_PATH;
+import static com.yahoo.vespa.config.server.session.SessionData.APPLICATION_PACKAGE_REFERENCE_PATH;
+import static com.yahoo.vespa.config.server.session.SessionData.ATHENZ_DOMAIN;
+import static com.yahoo.vespa.config.server.session.SessionData.CLOUD_ACCOUNT_PATH;
+import static com.yahoo.vespa.config.server.session.SessionData.CREATE_TIME_PATH;
+import static com.yahoo.vespa.config.server.session.SessionData.DATAPLANE_TOKENS_PATH;
+import static com.yahoo.vespa.config.server.session.SessionData.DOCKER_IMAGE_REPOSITORY_PATH;
+import static com.yahoo.vespa.config.server.session.SessionData.OPERATOR_CERTIFICATES_PATH;
+import static com.yahoo.vespa.config.server.session.SessionData.QUOTA_PATH;
+import static com.yahoo.vespa.config.server.session.SessionData.SESSION_DATA_PATH;
+import static com.yahoo.vespa.config.server.session.SessionData.TENANT_SECRET_STORES_PATH;
+import static com.yahoo.vespa.config.server.session.SessionData.VERSION_PATH;
import static com.yahoo.vespa.config.server.zookeeper.ZKApplication.USER_DEFCONFIGS_ZK_SUBPATH;
import static com.yahoo.vespa.curator.Curator.CompletionWaiter;
import static com.yahoo.yolean.Exceptions.uncheck;
@@ -61,18 +70,6 @@ public class SessionZooKeeperClient {
// NOTE: Any state added here MUST also be propagated in com.yahoo.vespa.config.server.deploy.Deployment.prepare()
- static final String APPLICATION_ID_PATH = "applicationId";
- static final String APPLICATION_PACKAGE_REFERENCE_PATH = "applicationPackageReference";
- private static final String VERSION_PATH = "version";
- private static final String CREATE_TIME_PATH = "createTime";
- private static final String DOCKER_IMAGE_REPOSITORY_PATH = "dockerImageRepository";
- private static final String ATHENZ_DOMAIN = "athenzDomain";
- private static final String QUOTA_PATH = "quota";
- private static final String TENANT_SECRET_STORES_PATH = "tenantSecretStores";
- private static final String OPERATOR_CERTIFICATES_PATH = "operatorCertificates";
- private static final String CLOUD_ACCOUNT_PATH = "cloudAccount";
- private static final String DATAPLANE_TOKENS_PATH = "dataplaneTokens";
-
private final Curator curator;
private final TenantName tenantName;
private final long sessionId;
@@ -180,11 +177,8 @@ public class SessionZooKeeperClient {
reference -> curator.set(applicationPackageReferencePath(), Utf8.toBytes(reference.value())));
}
- FileReference readApplicationPackageReference() {
- Optional<byte[]> data = curator.getData(applicationPackageReferencePath());
- if (data.isEmpty()) return null; // This should not happen.
-
- return new FileReference(Utf8.toString(data.get()));
+ Optional<FileReference> readApplicationPackageReference() {
+ return curator.getData(applicationPackageReferencePath()).map(d -> new FileReference(Utf8.toString(d)));
}
private Path applicationPackageReferencePath() {
@@ -227,6 +221,10 @@ public class SessionZooKeeperClient {
curator.set(versionPath(), Utf8.toBytes(version.toString()));
}
+ public void writeSessionData(SessionData sessionData) {
+ curator.set(sessionPath.append(SESSION_DATA_PATH), sessionData.toJson());
+ }
+
public Version readVespaVersion() {
Optional<byte[]> data = curator.getData(versionPath());
// TODO: Empty version should not be possible any more - verify and remove
@@ -261,11 +259,6 @@ public class SessionZooKeeperClient {
.orElseThrow(() -> new IllegalStateException("Allocated hosts does not exists"));
}
- public ZooKeeperDeployer createDeployer(DeployLogger logger) {
- ZooKeeperClient zkClient = new ZooKeeperClient(curator, logger, sessionPath);
- return new ZooKeeperDeployer(zkClient);
- }
-
public Transaction createWriteStatusTransaction(Session.Status status) {
CuratorTransaction transaction = new CuratorTransaction(curator);
if (curator.exists(sessionStatusPath)) {
@@ -368,7 +361,7 @@ public class SessionZooKeeperClient {
transaction.commit();
}
- private static Path getSessionPath(TenantName tenantName, long sessionId) {
+ static Path getSessionPath(TenantName tenantName, long sessionId) {
return TenantRepository.getSessionsPath(tenantName).append(String.valueOf(sessionId));
}
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/DataplaneTokenSerializer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/DataplaneTokenSerializer.java
index ef41512f979..3b819da6237 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/DataplaneTokenSerializer.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/DataplaneTokenSerializer.java
@@ -54,6 +54,11 @@ public class DataplaneTokenSerializer {
public static Slime toSlime(List<DataplaneToken> dataplaneTokens) {
Slime slime = new Slime();
Cursor root = slime.setArray();
+ toSlime(dataplaneTokens, root);
+ return slime;
+ }
+
+ public static void toSlime(List<DataplaneToken> dataplaneTokens, Cursor root) {
for (DataplaneToken token : dataplaneTokens) {
Cursor cursor = root.addObject();
cursor.setString(ID_FIELD, token.tokenId());
@@ -65,6 +70,6 @@ public class DataplaneTokenSerializer {
val.setString(EXPIRATION_FIELD, v.expiration().map(Instant::toString).orElse("<none>"));
});
}
- return slime;
}
+
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/OperatorCertificateSerializer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/OperatorCertificateSerializer.java
index 232dd2e5fe7..e5a969bb948 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/OperatorCertificateSerializer.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/OperatorCertificateSerializer.java
@@ -1,8 +1,6 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
package com.yahoo.vespa.config.server.tenant;
-import com.yahoo.config.model.api.ApplicationRoles;
import com.yahoo.security.X509CertificateUtils;
import com.yahoo.slime.Cursor;
import com.yahoo.slime.Inspector;
@@ -11,21 +9,28 @@ import com.yahoo.slime.SlimeUtils;
import java.security.cert.X509Certificate;
import java.util.List;
-import java.util.stream.Collectors;
+/**
+ * Serializer for operator certificates.
+ * The certificates are serialized as a list of PEM strings.
+ * @author tokle
+ */
public class OperatorCertificateSerializer {
private final static String certificateField = "certificates";
-
public static Slime toSlime(List<X509Certificate> certificateList) {
Slime slime = new Slime();
var root = slime.setObject();
Cursor array = root.setArray(certificateField);
+ toSlime(certificateList, array);
+ return slime;
+ }
+
+ public static void toSlime(List<X509Certificate> certificateList, Cursor array) {
certificateList.stream()
.map(X509CertificateUtils::toPem)
.forEach(array::addString);
- return slime;
}
public static List<X509Certificate> fromSlime(Inspector object) {
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantSecretStoreSerializer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantSecretStoreSerializer.java
index 262192ad6c4..b8df5073a3e 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantSecretStoreSerializer.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantSecretStoreSerializer.java
@@ -30,10 +30,14 @@ public class TenantSecretStoreSerializer {
public static Slime toSlime(List<TenantSecretStore> tenantSecretStores) {
Slime slime = new Slime();
Cursor cursor = slime.setArray();
- tenantSecretStores.forEach(tenantSecretStore -> toSlime(tenantSecretStore, cursor.addObject()));
+ toSlime(tenantSecretStores, cursor);
return slime;
}
+ public static void toSlime(List<TenantSecretStore> tenantSecretStores, Cursor cursor) {
+ tenantSecretStores.forEach(tenantSecretStore -> toSlime(tenantSecretStore, cursor.addObject()));
+ }
+
public static void toSlime(TenantSecretStore tenantSecretStore, Cursor object) {
object.setString(awsIdField, tenantSecretStore.getAwsId());
object.setString(nameField, tenantSecretStore.getName());