summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMorten Tokle <mortent@verizonmedia.com>2020-05-15 13:15:41 +0200
committerMorten Tokle <mortent@verizonmedia.com>2020-05-15 13:26:58 +0200
commit662f00565c1181a3c0d0750a9ef0fbb746b5ec7a (patch)
treeb64de8324088c97b283dd3c08bd21f6d01078076
parentca294b0a05520dff341016d2fc75056ac75b0d8e (diff)
Persist application roles
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/model/api/ApplicationRoles.java51
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java2
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java8
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java10
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java9
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/PrepareParams.java22
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java17
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/tenant/ApplicationRolesSerializer.java27
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/tenant/ApplicationRolesStore.java64
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/ModelContextImplTest.java1
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/session/PrepareParamsTest.java14
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/tenant/ApplicationRolesStoreTest.java39
12 files changed, 255 insertions, 9 deletions
diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ApplicationRoles.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ApplicationRoles.java
new file mode 100644
index 00000000000..8492603e2e3
--- /dev/null
+++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ApplicationRoles.java
@@ -0,0 +1,51 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.config.model.api;
+
+import com.google.common.base.Strings;
+
+/**
+ * @author mortent
+ */
+public class ApplicationRoles {
+ private final String applicationHostRole;
+ private final String applicationContainerRole;
+
+ public ApplicationRoles(String applicationHostRole, String applicationContainerRole) {
+ this.applicationHostRole = applicationHostRole;
+ this.applicationContainerRole = applicationContainerRole;
+ }
+
+ /**
+ * @return an ApplicationRoles instance if both hostRole and containerRole is non-empty, <code>null</code> otherwise
+ */
+ public static ApplicationRoles fromString(String hostRole, String containerRole) {
+ if(Strings.isNullOrEmpty(hostRole) || Strings.isNullOrEmpty(containerRole)) {
+ return null;
+ }
+ return new ApplicationRoles(hostRole, containerRole);
+ }
+
+ public String applicationContainerRole() {
+ return applicationContainerRole;
+ }
+
+ public String applicationHostRole() {
+ return applicationHostRole;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ApplicationRoles that = (ApplicationRoles) o;
+ if (!applicationHostRole.equals(that.applicationHostRole)) return false;
+ return applicationContainerRole.equals(that.applicationContainerRole);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = applicationHostRole.hashCode();
+ result = 31 * result + applicationContainerRole.hashCode();
+ return result;
+ }
+}
diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java
index 7641f14b007..33369727703 100644
--- a/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java
+++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java
@@ -93,6 +93,8 @@ public interface ModelContext {
// TODO(mpolden): Remove after May 2020
default boolean useDedicatedNodesWhenUnspecified() { return true; }
+
+ Optional<ApplicationRoles> applicationRoles();
}
}
diff --git a/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java b/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java
index 4dbfc8f7a8f..54630426cb8 100644
--- a/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java
+++ b/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java
@@ -2,6 +2,7 @@
package com.yahoo.config.model.deploy;
import com.google.common.collect.ImmutableList;
+import com.yahoo.config.model.api.ApplicationRoles;
import com.yahoo.config.model.api.ConfigServerSpec;
import com.yahoo.config.model.api.ContainerEndpoint;
import com.yahoo.config.model.api.EndpointCertificateSecrets;
@@ -49,6 +50,7 @@ public class TestProperties implements ModelContext.Properties {
private int defaultNumResponseThreads = 0;
private Optional<EndpointCertificateSecrets> endpointCertificateSecrets = Optional.empty();
private AthenzDomain athenzDomain;
+ private ApplicationRoles applicationRoles;
@Override public boolean multitenant() { return multitenant; }
@Override public ApplicationId applicationId() { return applicationId; }
@@ -90,6 +92,7 @@ public class TestProperties implements ModelContext.Properties {
@Override public boolean useDistributorBtreeDb() { return useDistributorBtreeDb; }
@Override public boolean useThreePhaseUpdates() { return useThreePhaseUpdates; }
@Override public Optional<AthenzDomain> athenzDomain() { return Optional.ofNullable(athenzDomain); }
+ @Override public Optional<ApplicationRoles> applicationRoles() { return Optional.ofNullable(applicationRoles); }
public TestProperties setDefaultTermwiseLimit(double limit) {
defaultTermwiseLimit = limit;
@@ -174,6 +177,11 @@ public class TestProperties implements ModelContext.Properties {
return this;
}
+ public TestProperties setApplicationRoles(ApplicationRoles applicationRoles) {
+ this.applicationRoles = applicationRoles;
+ return this;
+ }
+
public static class Spec implements ConfigServerSpec {
private final String hostName;
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java
index fa7a107f953..0ecb608fe0c 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java
@@ -5,6 +5,7 @@ import com.yahoo.component.Version;
import com.yahoo.config.application.api.ApplicationPackage;
import com.yahoo.config.application.api.DeployLogger;
import com.yahoo.config.application.api.FileRegistry;
+import com.yahoo.config.model.api.ApplicationRoles;
import com.yahoo.config.model.api.ConfigDefinitionRepo;
import com.yahoo.config.model.api.ConfigServerSpec;
import com.yahoo.config.model.api.ContainerEndpoint;
@@ -156,6 +157,7 @@ public class ModelContextImpl implements ModelContext {
private final double queueSizefactor;
private final int defaultNumResponseThreads;
private final Optional<AthenzDomain> athenzDomain;
+ private final Optional<ApplicationRoles> applicationRoles;
public Properties(ApplicationId applicationId,
boolean multitenantFromConfig,
@@ -170,7 +172,8 @@ public class ModelContextImpl implements ModelContext {
boolean isFirstTimeDeployment,
FlagSource flagSource,
Optional<EndpointCertificateSecrets> endpointCertificateSecrets,
- Optional<AthenzDomain> athenzDomain) {
+ Optional<AthenzDomain> athenzDomain,
+ Optional<ApplicationRoles> applicationRoles) {
this.applicationId = applicationId;
this.multitenant = multitenantFromConfig || hostedVespa || Boolean.getBoolean("multitenant");
this.configServerSpecs = configServerSpecs;
@@ -202,6 +205,7 @@ public class ModelContextImpl implements ModelContext {
defaultNumResponseThreads = Flags.DEFAULT_NUM_RESPONSE_THREADS.bindTo(flagSource)
.with(FetchVector.Dimension.APPLICATION_ID, applicationId.serializedForm()).value();
this.athenzDomain = athenzDomain;
+ this.applicationRoles = applicationRoles;
}
@Override
@@ -287,6 +291,10 @@ public class ModelContextImpl implements ModelContext {
@Override
public Optional<AthenzDomain> athenzDomain() { return athenzDomain; }
+ @Override
+ public Optional<ApplicationRoles> applicationRoles() {
+ return applicationRoles;
+ }
}
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java b/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java
index 0b83e927c39..7828ce8963f 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java
@@ -16,7 +16,6 @@ import com.yahoo.config.provision.DockerImage;
import com.yahoo.config.provision.HostName;
import com.yahoo.config.provision.TenantName;
import com.yahoo.container.jdisc.secretstore.SecretStore;
-import java.util.logging.Level;
import com.yahoo.vespa.config.server.ConfigServerSpec;
import com.yahoo.vespa.config.server.GlobalComponentRegistry;
import com.yahoo.vespa.config.server.ServerCache;
@@ -28,10 +27,11 @@ import com.yahoo.vespa.config.server.monitoring.Metrics;
import com.yahoo.vespa.config.server.provision.HostProvisionerProvider;
import com.yahoo.vespa.config.server.session.SessionZooKeeperClient;
import com.yahoo.vespa.config.server.session.SilentDeployLogger;
+import com.yahoo.vespa.config.server.tenant.ApplicationRolesStore;
import com.yahoo.vespa.config.server.tenant.ContainerEndpointsCache;
+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.config.server.tenant.EndpointCertificateMetadataStore;
import com.yahoo.vespa.curator.Curator;
import com.yahoo.vespa.flags.FlagSource;
@@ -39,6 +39,7 @@ import java.net.URI;
import java.time.Instant;
import java.util.Map;
import java.util.Optional;
+import java.util.logging.Level;
import java.util.logging.Logger;
/**
@@ -147,7 +148,9 @@ public class ActivatedModelsBuilder extends ModelsBuilder<Application> {
new EndpointCertificateMetadataStore(curator, TenantRepository.getTenantPath(tenant))
.readEndpointCertificateMetadata(applicationId)
.flatMap(new EndpointCertificateRetriever(secretStore)::readEndpointCertificateSecrets),
- zkClient.readAthenzDomain());
+ zkClient.readAthenzDomain(),
+ new ApplicationRolesStore(curator, TenantRepository.getTenantPath(tenant))
+ .readApplicationRoles(applicationId));
}
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 ea84dee9fe7..a29b105f43f 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
@@ -2,6 +2,7 @@
package com.yahoo.vespa.config.server.session;
import com.yahoo.component.Version;
+import com.yahoo.config.model.api.ApplicationRoles;
import com.yahoo.config.model.api.ContainerEndpoint;
import com.yahoo.config.model.api.EndpointCertificateMetadata;
import com.yahoo.config.provision.ApplicationId;
@@ -38,6 +39,8 @@ public final class PrepareParams {
static final String ENDPOINT_CERTIFICATE_METADATA_PARAM_NAME = "endpointCertificateMetadata";
static final String DOCKER_IMAGE_REPOSITORY = "dockerImageRepository";
static final String ATHENZ_DOMAIN = "athenzDomain";
+ static final String APPLICATION_HOST_ROLE = "applicationHostRole";
+ static final String APPLICATION_CONTAINER_ROLE = "applicationContainerRole";
private final ApplicationId applicationId;
private final TimeoutBudget timeoutBudget;
@@ -51,12 +54,14 @@ public final class PrepareParams {
private final Optional<EndpointCertificateMetadata> endpointCertificateMetadata;
private final Optional<DockerImage> dockerImageRepository;
private final Optional<AthenzDomain> athenzDomain;
+ private final Optional<ApplicationRoles> applicationRoles;
private PrepareParams(ApplicationId applicationId, TimeoutBudget timeoutBudget, boolean ignoreValidationErrors,
boolean dryRun, boolean verbose, boolean isBootstrap, Optional<Version> vespaVersion,
List<ContainerEndpoint> containerEndpoints, Optional<String> tlsSecretsKeyName,
Optional<EndpointCertificateMetadata> endpointCertificateMetadata,
- Optional<DockerImage> dockerImageRepository, Optional<AthenzDomain> athenzDomain) {
+ Optional<DockerImage> dockerImageRepository, Optional<AthenzDomain> athenzDomain,
+ Optional<ApplicationRoles> applicationRoles) {
this.timeoutBudget = timeoutBudget;
this.applicationId = applicationId;
this.ignoreValidationErrors = ignoreValidationErrors;
@@ -69,6 +74,7 @@ public final class PrepareParams {
this.endpointCertificateMetadata = endpointCertificateMetadata;
this.dockerImageRepository = dockerImageRepository;
this.athenzDomain = athenzDomain;
+ this.applicationRoles = applicationRoles;
}
public static class Builder {
@@ -85,6 +91,7 @@ public final class PrepareParams {
private Optional<EndpointCertificateMetadata> endpointCertificateMetadata = Optional.empty();
private Optional<DockerImage> dockerImageRepository = Optional.empty();
private Optional<AthenzDomain> athenzDomain = Optional.empty();
+ private Optional<ApplicationRoles> applicationRoles = Optional.empty();
public Builder() { }
@@ -174,12 +181,17 @@ public final class PrepareParams {
return this;
}
+ public Builder applicationRoles(ApplicationRoles applicationRoles) {
+ this.applicationRoles = Optional.ofNullable(applicationRoles);
+ return this;
+ }
+
public PrepareParams build() {
return new PrepareParams(applicationId, timeoutBudget, ignoreValidationErrors, dryRun,
verbose, isBootstrap, vespaVersion, containerEndpoints, tlsSecretsKeyName,
- endpointCertificateMetadata, dockerImageRepository, athenzDomain);
+ endpointCertificateMetadata, dockerImageRepository, athenzDomain,
+ applicationRoles);
}
-
}
public static PrepareParams fromHttpRequest(HttpRequest request, TenantName tenant, Duration barrierTimeout) {
@@ -194,6 +206,7 @@ public final class PrepareParams {
.endpointCertificateMetadata(request.getProperty(ENDPOINT_CERTIFICATE_METADATA_PARAM_NAME))
.dockerImageRepository(request.getProperty(DOCKER_IMAGE_REPOSITORY))
.athenzDomain(request.getProperty(ATHENZ_DOMAIN))
+ .applicationRoles(ApplicationRoles.fromString(request.getProperty(APPLICATION_HOST_ROLE), request.getProperty(APPLICATION_CONTAINER_ROLE)))
.build();
}
@@ -261,4 +274,7 @@ public final class PrepareParams {
public Optional<AthenzDomain> athenzDomain() { return athenzDomain; }
+ public Optional<ApplicationRoles> applicationRoles() {
+ return applicationRoles;
+ }
}
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 54d4cc8654b..e63244cd57b 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
@@ -10,6 +10,7 @@ import com.yahoo.config.FileReference;
import com.yahoo.config.application.api.ApplicationPackage;
import com.yahoo.config.application.api.DeployLogger;
import com.yahoo.config.application.api.FileRegistry;
+import com.yahoo.config.model.api.ApplicationRoles;
import com.yahoo.config.model.api.ConfigDefinitionRepo;
import com.yahoo.config.model.api.ContainerEndpoint;
import com.yahoo.config.model.api.EndpointCertificateMetadata;
@@ -36,6 +37,7 @@ import com.yahoo.vespa.config.server.http.InvalidApplicationException;
import com.yahoo.vespa.config.server.modelfactory.ModelFactoryRegistry;
import com.yahoo.vespa.config.server.modelfactory.PreparedModelsBuilder;
import com.yahoo.vespa.config.server.provision.HostProvisionerProvider;
+import com.yahoo.vespa.config.server.tenant.ApplicationRolesStore;
import com.yahoo.vespa.config.server.tenant.ContainerEndpointsCache;
import com.yahoo.vespa.config.server.tenant.EndpointCertificateMetadataSerializer;
import com.yahoo.vespa.config.server.tenant.EndpointCertificateMetadataStore;
@@ -135,6 +137,7 @@ public class SessionPreparer {
} else {
preparation.legacyWriteContainerEndpointsZK();
}
+ preparation.writeApplicationRoles();
preparation.distribute();
}
log.log(Level.FINE, () -> "time used " + params.getTimeoutBudget().timesUsed() +
@@ -170,6 +173,8 @@ public class SessionPreparer {
private final Optional<EndpointCertificateMetadata> endpointCertificateMetadata;
private final Optional<EndpointCertificateSecrets> endpointCertificateSecrets;
private final Optional<AthenzDomain> athenzDomain;
+ private final ApplicationRolesStore applicationRolesStore;
+ private final Optional<ApplicationRoles> applicationRoles;
private ApplicationPackage applicationPackage;
private List<PreparedModelsBuilder.PreparedModelResult> modelResultList;
@@ -203,6 +208,9 @@ public class SessionPreparer {
this.containerEndpoints = getEndpoints(params.containerEndpoints());
}
this.athenzDomain = params.athenzDomain();
+ this.applicationRolesStore = new ApplicationRolesStore(curator, tenantPath);
+ this.applicationRoles = params.applicationRoles()
+ .or(() -> applicationRolesStore.readApplicationRoles(applicationId));
this.properties = new ModelContextImpl.Properties(params.getApplicationId(),
configserverConfig.multitenant(),
ConfigServerSpec.fromConfig(configserverConfig),
@@ -216,7 +224,7 @@ public class SessionPreparer {
currentActiveApplicationSet.isEmpty(),
context.getFlagSource(),
endpointCertificateSecrets,
- athenzDomain);
+ athenzDomain, applicationRoles);
this.fileDistributionProvider = fileDistributionFactory.createProvider(context.getServerDBSessionDir());
this.preparedModelsBuilder = new PreparedModelsBuilder(modelFactoryRegistry,
permanentApplicationPackage,
@@ -303,6 +311,12 @@ public class SessionPreparer {
}
}
+ void writeApplicationRoles() {
+ applicationRoles.ifPresent(roles ->
+ applicationRolesStore.writeApplicationRoles(applicationId, roles));
+ checkTimeout("write application roles to zookeeper");
+ }
+
void distribute() {
prepareResult.asList().forEach(modelResult -> modelResult.model
.distributeFiles(modelResult.fileDistributionProvider.getFileDistribution()));
@@ -326,7 +340,6 @@ public class SessionPreparer {
}
return List.copyOf(endpoints);
}
-
}
private void writeStateToZooKeeper(SessionZooKeeperClient zooKeeperClient,
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/ApplicationRolesSerializer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/ApplicationRolesSerializer.java
new file mode 100644
index 00000000000..132828de7b4
--- /dev/null
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/ApplicationRolesSerializer.java
@@ -0,0 +1,27 @@
+// 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.tenant;
+
+import com.yahoo.config.model.api.ApplicationRoles;
+import com.yahoo.slime.Cursor;
+import com.yahoo.slime.Inspector;
+
+/**
+ * @author mortent
+ */
+public class ApplicationRolesSerializer {
+
+ private final static String hostRoleField = "applicationHostRole";
+ private final static String containerRoleField = "applicationContainerRole";
+
+
+ public static void toSlime(ApplicationRoles applicationRoles, Cursor object) {
+ object.setString(hostRoleField, applicationRoles.applicationHostRole());
+ object.setString(containerRoleField, applicationRoles.applicationContainerRole());
+ }
+
+ public static ApplicationRoles fromSlime(Inspector inspector) {
+ return new ApplicationRoles(inspector.field(hostRoleField).asString(),
+ inspector.field(containerRoleField).asString());
+
+ }
+}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/ApplicationRolesStore.java b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/ApplicationRolesStore.java
new file mode 100644
index 00000000000..a41e5465509
--- /dev/null
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/ApplicationRolesStore.java
@@ -0,0 +1,64 @@
+// 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 com.yahoo.config.model.api.ApplicationRoles;
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.path.Path;
+import com.yahoo.slime.Slime;
+import com.yahoo.slime.SlimeUtils;
+import com.yahoo.vespa.curator.Curator;
+import com.yahoo.vespa.curator.transaction.CuratorOperations;
+import com.yahoo.vespa.curator.transaction.CuratorTransaction;
+
+import java.util.Optional;
+
+/**
+ * Stores application roles for an application.
+ *
+ * @author mortent
+ */
+public class ApplicationRolesStore {
+
+ private final Path path;
+ private final Curator curator;
+
+ public ApplicationRolesStore(Curator curator, Path tenantPath) {
+ this.curator = curator;
+ this.path = tenantPath.append("applicationRoles/");
+ }
+
+ /** Reads the application roles from ZooKeeper, if it exists */
+ public Optional<ApplicationRoles> readApplicationRoles(ApplicationId application) {
+ try {
+ Optional<byte[]> data = curator.getData(applicationRolesPath(application));
+ if (data.isEmpty() || data.get().length == 0) return Optional.empty();
+ Slime slime = SlimeUtils.jsonToSlime(data.get());
+ ApplicationRoles applicationRoles = ApplicationRolesSerializer.fromSlime(slime.get());
+ return Optional.of(applicationRoles);
+ } catch (Exception e) {
+ throw new RuntimeException("Error reading application roles of " + application, e);
+ }
+ }
+
+ /** Writes the application roles to ZooKeeper */
+ public void writeApplicationRoles(ApplicationId application, ApplicationRoles applicationRoles) {
+ try {
+ Slime slime = new Slime();
+ ApplicationRolesSerializer.toSlime(applicationRoles, slime.setObject());
+ curator.set(applicationRolesPath(application), SlimeUtils.toJsonBytes(slime));
+ } catch (Exception e) {
+ throw new RuntimeException("Could not write application roles of " + application, e);
+ }
+ }
+
+ /** Returns a transaction which deletes application roles if they exist */
+ public CuratorTransaction delete(ApplicationId application) {
+ if (!curator.exists(applicationRolesPath(application))) return CuratorTransaction.empty(curator);
+ return CuratorTransaction.from(CuratorOperations.delete(applicationRolesPath(application).getAbsolute()), curator);
+ }
+
+ /** Returns the path storing the application roles for an application */
+ private Path applicationRolesPath(ApplicationId application) {
+ return path.append(application.serializedForm());
+ }
+}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/ModelContextImplTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/ModelContextImplTest.java
index 20e8524b9b8..3b8343fc6a4 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/ModelContextImplTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/ModelContextImplTest.java
@@ -62,6 +62,7 @@ public class ModelContextImplTest {
false,
flagSource,
null,
+ Optional.empty(),
Optional.empty()),
Optional.empty(),
Optional.empty(),
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/session/PrepareParamsTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/session/PrepareParamsTest.java
index 3f5d06b7071..bf21f800815 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/session/PrepareParamsTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/session/PrepareParamsTest.java
@@ -1,6 +1,7 @@
// 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.session;
+import com.yahoo.config.model.api.ApplicationRoles;
import com.yahoo.config.model.api.ContainerEndpoint;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.TenantName;
@@ -70,6 +71,19 @@ public class PrepareParamsTest {
assertEquals(endpoints, prepareParams.containerEndpoints());
}
+ @Test
+ public void testCorrectParsingWithApplicationRoles() {
+ String req = request + "&" +
+ PrepareParams.APPLICATION_HOST_ROLE + "=hostRole&" +
+ PrepareParams.APPLICATION_CONTAINER_ROLE + "=containerRole";
+ var prepareParams = createParams(req, TenantName.from("foo"));
+
+ Optional<ApplicationRoles> applicationRoles = prepareParams.applicationRoles();
+ assertTrue(applicationRoles.isPresent());
+ assertEquals("hostRole", applicationRoles.get().applicationHostRole());
+ assertEquals("containerRole", applicationRoles.get().applicationContainerRole());
+ }
+
// Create PrepareParams from a request (based on uri and tenant name)
private static PrepareParams createParams(String uri, TenantName tenantName) {
return PrepareParams.fromHttpRequest(
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/ApplicationRolesStoreTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/ApplicationRolesStoreTest.java
new file mode 100644
index 00000000000..a03ea61ec54
--- /dev/null
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/ApplicationRolesStoreTest.java
@@ -0,0 +1,39 @@
+// 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.tenant;
+
+import com.yahoo.config.model.api.ApplicationRoles;
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.path.Path;
+import com.yahoo.vespa.curator.mock.MockCurator;
+import org.junit.Test;
+
+import java.util.Optional;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author mortent
+ */
+public class ApplicationRolesStoreTest {
+ @Test
+ public void persists_entry_correctly() {
+ // Persist
+ var applicationRolesStore = new ApplicationRolesStore(new MockCurator(), Path.createRoot());
+ var roles = new ApplicationRoles("hostRole", "containerRole");
+ applicationRolesStore.writeApplicationRoles(ApplicationId.defaultId(), roles);
+
+ // Read
+ Optional<ApplicationRoles> deserialized = applicationRolesStore.readApplicationRoles(ApplicationId.defaultId());
+ assertTrue(deserialized.isPresent());
+ assertEquals(roles, deserialized.get());
+ }
+
+ @Test
+ public void read_non_existent() {
+ var applicationRolesStore = new ApplicationRolesStore(new MockCurator(), Path.createRoot());
+ Optional<ApplicationRoles> applicationRoles = applicationRolesStore.readApplicationRoles(ApplicationId.defaultId());
+ assertTrue(applicationRoles.isEmpty());
+ }
+
+}