// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.config.server.session; import com.yahoo.component.Version; 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.Quota; 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; import com.yahoo.config.provision.CloudAccount; import com.yahoo.config.provision.DataplaneToken; import com.yahoo.config.provision.DockerImage; import com.yahoo.config.provision.TenantName; import com.yahoo.path.Path; import com.yahoo.transaction.Transaction; import com.yahoo.vespa.config.server.application.ApplicationVersions; import com.yahoo.vespa.config.server.tenant.TenantRepository; import java.security.cert.X509Certificate; import java.time.Instant; import java.util.List; import java.util.Optional; /** * A session represents an instance of an application that can be edited, prepared and activated. This * class represents the common stuff between sessions working on the local file * system ({@link LocalSession}s) and sessions working on zookeeper ({@link RemoteSession}s). * * @author Ulf Lilleengen * @author hmusum */ public abstract class Session implements Comparable { protected final long sessionId; protected final TenantName tenant; protected final SessionZooKeeperClient sessionZooKeeperClient; protected final Optional applicationPackage; protected Session(TenantName tenant, long sessionId, SessionZooKeeperClient sessionZooKeeperClient) { this(tenant, sessionId, sessionZooKeeperClient, Optional.empty()); } protected Session(TenantName tenant, long sessionId, SessionZooKeeperClient sessionZooKeeperClient, ApplicationPackage applicationPackage) { this(tenant, sessionId, sessionZooKeeperClient, Optional.of(applicationPackage)); } private Session(TenantName tenant, long sessionId, SessionZooKeeperClient sessionZooKeeperClient, Optional applicationPackage) { this.tenant = tenant; this.sessionId = sessionId; this.sessionZooKeeperClient = sessionZooKeeperClient; this.applicationPackage = applicationPackage; } public final long getSessionId() { return sessionId; } public Session.Status getStatus() { return sessionZooKeeperClient.readStatus(); } @Override public String toString() { return "Session,id=" + sessionId + ",status=" + getStatus(); } public long getActiveSessionAtCreate() { return getMetaData().getPreviousActiveGeneration(); } /** * The status of this session. */ public enum Status { NEW, PREPARE, ACTIVATE, DEACTIVATE, UNKNOWN, DELETE; public static Status parse(String data) { for (Status status : Status.values()) { if (status.name().equals(data)) { return status; } } return Status.NEW; } } public TenantName getTenantName() { return tenant; } /** * Helper to provide a log message preamble for code dealing with sessions * @return log preamble */ public String logPre() { Optional 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. return applicationId .filter(appId -> ! appId.equals(ApplicationId.defaultId())) .map(TenantRepository::logPre) .orElse(TenantRepository.logPre(getTenantName())); } public Instant getCreateTime() { return sessionZooKeeperClient.readCreateTime(); } public Instant getActivatedTime() { return sessionZooKeeperClient.readActivatedTime(); } /** Returns application id read from ZooKeeper. Will throw RuntimeException if not found */ public ApplicationId getApplicationId() { return sessionZooKeeperClient.readApplicationId(); } /** Returns application id read from ZooKeeper. Will return Optional.empty() if not found */ public Optional getOptionalApplicationId() { try { return Optional.of(getApplicationId()); } catch (RuntimeException e) { return Optional.empty(); } } public Optional getApplicationPackageReference() { return sessionZooKeeperClient.readApplicationPackageReference(); } public Optional getDockerImageRepository() { return sessionZooKeeperClient.readDockerImageRepository(); } public Version getVespaVersion() { return sessionZooKeeperClient.readVespaVersion(); } public Optional getAthenzDomain() { return sessionZooKeeperClient.readAthenzDomain(); } public Optional getQuota() { return sessionZooKeeperClient.readQuota(); } public AllocatedHosts getAllocatedHosts() { return sessionZooKeeperClient.getAllocatedHosts(); } public Transaction createDeactivateTransaction() { return createSetStatusTransaction(Status.DEACTIVATE); } public List getTenantSecretStores() { return sessionZooKeeperClient.readTenantSecretStores(); } public List getOperatorCertificates() { return sessionZooKeeperClient.readOperatorCertificates(); } public Optional getCloudAccount() { return sessionZooKeeperClient.readCloudAccount(); } public List getDataplaneTokens() { return sessionZooKeeperClient.readDataplaneTokens(); } public SessionZooKeeperClient getSessionZooKeeperClient() { return sessionZooKeeperClient; } private Transaction createSetStatusTransaction(Status status) { return sessionZooKeeperClient.createWriteStatusTransaction(status); } // Note: Assumes monotonically increasing session ids public boolean isNewerThan(long sessionId) { return getSessionId() > sessionId; } public ApplicationMetaData getMetaData() { return applicationPackage.isPresent() ? applicationPackage.get().getMetaData() : sessionZooKeeperClient.loadApplicationPackage().getMetaData(); } public ApplicationPackage getApplicationPackage() { return applicationPackage.orElseThrow(() -> new RuntimeException("No application package found for " + this)); } public ApplicationFile getApplicationFile(Path relativePath, LocalSession.Mode mode) { if (mode.equals(Session.Mode.WRITE)) { markSessionEdited(); } return getApplicationPackage().getFile(relativePath); } Optional applicationVersions() { return Optional.empty(); } private void markSessionEdited() { setStatus(Session.Status.NEW); } void setStatus(Session.Status newStatus) { sessionZooKeeperClient.writeStatus(newStatus); } @Override public int compareTo(Session rhs) { Long lhsId = getSessionId(); Long rhsId = rhs.getSessionId(); return lhsId.compareTo(rhsId); } public enum Mode { READ, WRITE } }