aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorØyvind Grønnesby <oyving@verizonmedia.com>2019-05-27 16:00:58 +0200
committerØyvind Grønnesby <oyving@verizonmedia.com>2019-05-27 16:00:58 +0200
commitb4c7bc329f5400e241daf625f9926dd4c89705fb (patch)
treec96f5027efbaf138594f684a1525c7648a0e9d4e
parente823c91306d06af0ad8d77e6c6cafdfb5c5c2d32 (diff)
Make rotations be List<> instead of Optional<>
This allows us to have multiple rotations assigned to service, which in the future allows multiple rotations per application.
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Application.java21
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedApplication.java49
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializer.java30
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java7
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepository.java10
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java4
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepositoryTest.java12
9 files changed, 81 insertions, 56 deletions
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Application.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Application.java
index 84e15deea4c..1bae9e0ccd4 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Application.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Application.java
@@ -18,6 +18,7 @@ import com.yahoo.vespa.hosted.controller.application.ApplicationActivity;
import com.yahoo.vespa.hosted.controller.application.Change;
import com.yahoo.vespa.hosted.controller.application.Deployment;
import com.yahoo.vespa.hosted.controller.application.DeploymentJobs;
+import com.yahoo.vespa.hosted.controller.application.Endpoint;
import com.yahoo.vespa.hosted.controller.application.EndpointList;
import com.yahoo.vespa.hosted.controller.application.RotationStatus;
import com.yahoo.vespa.hosted.controller.rotation.RotationId;
@@ -56,7 +57,7 @@ public class Application {
private final OptionalInt majorVersion;
private final ApplicationMetrics metrics;
private final Optional<String> pemDeployKey;
- private final Optional<RotationId> rotation;
+ private final List<RotationId> rotations;
private final Map<HostName, RotationStatus> rotationStatus;
/** Creates an empty application */
@@ -65,7 +66,7 @@ public class Application {
new DeploymentJobs(OptionalLong.empty(), Collections.emptyList(), Optional.empty(), false),
Change.empty(), Change.empty(), Optional.empty(), Optional.empty(), OptionalInt.empty(),
new ApplicationMetrics(0, 0),
- Optional.empty(), Optional.empty(), Collections.emptyMap());
+ Optional.empty(), Collections.emptyList(), Collections.emptyMap());
}
/** Used from persistence layer: Do not use */
@@ -73,18 +74,18 @@ public class Application {
List<Deployment> deployments, DeploymentJobs deploymentJobs, Change change,
Change outstandingChange, Optional<IssueId> ownershipIssueId, Optional<User> owner,
OptionalInt majorVersion, ApplicationMetrics metrics, Optional<String> pemDeployKey,
- Optional<RotationId> rotation, Map<HostName, RotationStatus> rotationStatus) {
+ List<RotationId> rotations, Map<HostName, RotationStatus> rotationStatus) {
this(id, createdAt, deploymentSpec, validationOverrides,
deployments.stream().collect(Collectors.toMap(Deployment::zone, Function.identity())),
deploymentJobs, change, outstandingChange, ownershipIssueId, owner, majorVersion,
- metrics, pemDeployKey, rotation, rotationStatus);
+ metrics, pemDeployKey, rotations, rotationStatus);
}
Application(ApplicationId id, Instant createdAt, DeploymentSpec deploymentSpec, ValidationOverrides validationOverrides,
Map<ZoneId, Deployment> deployments, DeploymentJobs deploymentJobs, Change change,
Change outstandingChange, Optional<IssueId> ownershipIssueId, Optional<User> owner,
OptionalInt majorVersion, ApplicationMetrics metrics, Optional<String> pemDeployKey,
- Optional<RotationId> rotation, Map<HostName, RotationStatus> rotationStatus) {
+ List<RotationId> rotations, Map<HostName, RotationStatus> rotationStatus) {
this.id = Objects.requireNonNull(id, "id cannot be null");
this.createdAt = Objects.requireNonNull(createdAt, "instant of creation cannot be null");
this.deploymentSpec = Objects.requireNonNull(deploymentSpec, "deploymentSpec cannot be null");
@@ -98,7 +99,7 @@ public class Application {
this.majorVersion = Objects.requireNonNull(majorVersion, "majorVersion cannot be null");
this.metrics = Objects.requireNonNull(metrics, "metrics cannot be null");
this.pemDeployKey = pemDeployKey;
- this.rotation = Objects.requireNonNull(rotation, "rotation cannot be null");
+ this.rotations = Objects.requireNonNull(rotations, "rotations cannot be null");
this.rotationStatus = ImmutableMap.copyOf(Objects.requireNonNull(rotationStatus, "rotationStatus cannot be null"));
}
@@ -195,13 +196,15 @@ public class Application {
}
/** Returns the global rotation id of this, if present */
- public Optional<RotationId> rotation() {
- return rotation;
+ public List<RotationId> rotations() {
+ return Collections.unmodifiableList(rotations);
}
/** Returns the default global endpoints for this in given system */
public EndpointList endpointsIn(SystemName system) {
- if (rotation.isEmpty()) return EndpointList.EMPTY;
+ // TODO: Do we need to change something here? .defaultGlobalId seems like it is
+ // TODO: making some assumptions on naming.
+ if (rotations.isEmpty()) return EndpointList.EMPTY;
return EndpointList.defaultGlobal(id, system);
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java
index 8ca6373166b..5c80e12dab4 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java
@@ -328,7 +328,7 @@ public class ApplicationController {
// Include global DNS names
cnames = app.endpointsIn(controller.system()).asList().stream().map(Endpoint::dnsName).collect(Collectors.toSet());
// Include rotation ID to ensure that deployment can respond to health checks with rotation ID as Host header
- app.rotation().map(RotationId::asString).ifPresent(cnames::add);
+ app.rotations().stream().map(RotationId::asString).forEach(cnames::add);
// Update application with information from application package
if ( ! preferOldestVersion
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedApplication.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedApplication.java
index 943ede5197b..e8d69f8a577 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedApplication.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedApplication.java
@@ -27,6 +27,7 @@ import com.yahoo.vespa.hosted.controller.rotation.RotationId;
import java.time.Instant;
import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
@@ -55,7 +56,7 @@ public class LockedApplication {
private final OptionalInt majorVersion;
private final ApplicationMetrics metrics;
private final Optional<String> pemDeployKey;
- private final Optional<RotationId> rotation;
+ private final List<RotationId> rotations;
private final Map<HostName, RotationStatus> rotationStatus;
/**
@@ -70,7 +71,7 @@ public class LockedApplication {
application.deployments(),
application.deploymentJobs(), application.change(), application.outstandingChange(),
application.ownershipIssueId(), application.owner(), application.majorVersion(), application.metrics(),
- application.pemDeployKey(), application.rotation(), application.rotationStatus());
+ application.pemDeployKey(), application.rotations(), application.rotationStatus());
}
private LockedApplication(Lock lock, ApplicationId id, Instant createdAt,
@@ -78,7 +79,7 @@ public class LockedApplication {
Map<ZoneId, Deployment> deployments, DeploymentJobs deploymentJobs, Change change,
Change outstandingChange, Optional<IssueId> ownershipIssueId, Optional<User> owner,
OptionalInt majorVersion, ApplicationMetrics metrics, Optional<String> pemDeployKey,
- Optional<RotationId> rotation, Map<HostName, RotationStatus> rotationStatus) {
+ List<RotationId> rotations, Map<HostName, RotationStatus> rotationStatus) {
this.lock = lock;
this.id = id;
this.createdAt = createdAt;
@@ -93,7 +94,7 @@ public class LockedApplication {
this.majorVersion = majorVersion;
this.metrics = metrics;
this.pemDeployKey = pemDeployKey;
- this.rotation = rotation;
+ this.rotations = rotations;
this.rotationStatus = rotationStatus;
}
@@ -101,35 +102,35 @@ public class LockedApplication {
public Application get() {
return new Application(id, createdAt, deploymentSpec, validationOverrides, deployments, deploymentJobs, change,
outstandingChange, ownershipIssueId, owner, majorVersion, metrics, pemDeployKey,
- rotation, rotationStatus);
+ rotations, rotationStatus);
}
public LockedApplication withBuiltInternally(boolean builtInternally) {
return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments,
deploymentJobs.withBuiltInternally(builtInternally), change, outstandingChange,
ownershipIssueId, owner, majorVersion, metrics, pemDeployKey,
- rotation, rotationStatus);
+ rotations, rotationStatus);
}
public LockedApplication withProjectId(OptionalLong projectId) {
return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments,
deploymentJobs.withProjectId(projectId), change, outstandingChange,
ownershipIssueId, owner, majorVersion, metrics, pemDeployKey,
- rotation, rotationStatus);
+ rotations, rotationStatus);
}
public LockedApplication withDeploymentIssueId(IssueId issueId) {
return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments,
deploymentJobs.with(issueId), change, outstandingChange,
ownershipIssueId, owner, majorVersion, metrics, pemDeployKey,
- rotation, rotationStatus);
+ rotations, rotationStatus);
}
public LockedApplication withJobPause(JobType jobType, OptionalLong pausedUntil) {
return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments,
deploymentJobs.withPause(jobType, pausedUntil), change, outstandingChange,
ownershipIssueId, owner, majorVersion, metrics, pemDeployKey,
- rotation, rotationStatus);
+ rotations, rotationStatus);
}
public LockedApplication withJobCompletion(long projectId, JobType jobType, JobStatus.JobRun completion,
@@ -137,14 +138,14 @@ public class LockedApplication {
return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments,
deploymentJobs.withCompletion(projectId, jobType, completion, jobError),
change, outstandingChange, ownershipIssueId, owner, majorVersion, metrics,
- pemDeployKey, rotation, rotationStatus);
+ pemDeployKey, rotations, rotationStatus);
}
public LockedApplication withJobTriggering(JobType jobType, JobStatus.JobRun job) {
return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments,
deploymentJobs.withTriggering(jobType, job), change, outstandingChange,
ownershipIssueId, owner, majorVersion, metrics, pemDeployKey,
- rotation, rotationStatus);
+ rotations, rotationStatus);
}
public LockedApplication withNewDeployment(ZoneId zone, ApplicationVersion applicationVersion, Version version,
@@ -195,45 +196,45 @@ public class LockedApplication {
return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments,
deploymentJobs.without(jobType), change, outstandingChange,
ownershipIssueId, owner, majorVersion, metrics, pemDeployKey,
- rotation, rotationStatus);
+ rotations, rotationStatus);
}
public LockedApplication with(DeploymentSpec deploymentSpec) {
return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments,
deploymentJobs, change, outstandingChange,
ownershipIssueId, owner, majorVersion, metrics, pemDeployKey,
- rotation, rotationStatus);
+ rotations, rotationStatus);
}
public LockedApplication with(ValidationOverrides validationOverrides) {
return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments,
deploymentJobs, change, outstandingChange, ownershipIssueId, owner, majorVersion,
- metrics, pemDeployKey, rotation, rotationStatus);
+ metrics, pemDeployKey, rotations, rotationStatus);
}
public LockedApplication withChange(Change change) {
return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments,
deploymentJobs, change, outstandingChange, ownershipIssueId, owner, majorVersion,
- metrics, pemDeployKey, rotation, rotationStatus);
+ metrics, pemDeployKey, rotations, rotationStatus);
}
public LockedApplication withOutstandingChange(Change outstandingChange) {
return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments,
deploymentJobs, change, outstandingChange, ownershipIssueId, owner, majorVersion,
- metrics, pemDeployKey, rotation, rotationStatus);
+ metrics, pemDeployKey, rotations, rotationStatus);
}
public LockedApplication withOwnershipIssueId(IssueId issueId) {
return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments,
deploymentJobs, change, outstandingChange, Optional.ofNullable(issueId), owner,
- majorVersion, metrics, pemDeployKey, rotation, rotationStatus);
+ majorVersion, metrics, pemDeployKey, rotations, rotationStatus);
}
public LockedApplication withOwner(User owner) {
return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments,
deploymentJobs, change, outstandingChange, ownershipIssueId,
Optional.ofNullable(owner), majorVersion, metrics, pemDeployKey,
- rotation, rotationStatus);
+ rotations, rotationStatus);
}
/** Set a major version for this, or set to null to remove any major version override */
@@ -241,31 +242,31 @@ public class LockedApplication {
return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments,
deploymentJobs, change, outstandingChange, ownershipIssueId, owner,
majorVersion == null ? OptionalInt.empty() : OptionalInt.of(majorVersion),
- metrics, pemDeployKey, rotation, rotationStatus);
+ metrics, pemDeployKey, rotations, rotationStatus);
}
public LockedApplication with(MetricsService.ApplicationMetrics metrics) {
return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments,
deploymentJobs, change, outstandingChange, ownershipIssueId, owner, majorVersion,
- metrics, pemDeployKey, rotation, rotationStatus);
+ metrics, pemDeployKey, rotations, rotationStatus);
}
public LockedApplication withPemDeployKey(String pemDeployKey) {
return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments,
deploymentJobs, change, outstandingChange, ownershipIssueId, owner, majorVersion,
- metrics, Optional.ofNullable(pemDeployKey), rotation, rotationStatus);
+ metrics, Optional.ofNullable(pemDeployKey), rotations, rotationStatus);
}
public LockedApplication with(RotationId rotation) {
return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments,
deploymentJobs, change, outstandingChange, ownershipIssueId, owner, majorVersion,
- metrics, pemDeployKey, Optional.of(rotation), rotationStatus);
+ metrics, pemDeployKey, List.of(rotation), rotationStatus);
}
public LockedApplication withRotationStatus(Map<HostName, RotationStatus> rotationStatus) {
return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments,
deploymentJobs, change, outstandingChange, ownershipIssueId, owner, majorVersion,
- metrics, pemDeployKey, rotation, rotationStatus);
+ metrics, pemDeployKey, rotations, rotationStatus);
}
/** Don't expose non-leaf sub-objects. */
@@ -278,7 +279,7 @@ public class LockedApplication {
private LockedApplication with(Map<ZoneId, Deployment> deployments) {
return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments,
deploymentJobs, change, outstandingChange, ownershipIssueId, owner, majorVersion,
- metrics, pemDeployKey, rotation, rotationStatus);
+ metrics, pemDeployKey, rotations, rotationStatus);
}
@Override
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializer.java
index 3c2cbade606..a2754a57546 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializer.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializer.java
@@ -70,7 +70,8 @@ public class ApplicationSerializer {
private final String writeQualityField = "writeQuality";
private final String queryQualityField = "queryQuality";
private final String pemDeployKeyField = "pemDeployKey";
- private final String rotationField = "rotation";
+ private final String rotationsField = "rotations";
+ private final String deprecatedRotationField = "rotation";
private final String rotationStatusField = "rotationStatus";
// Deployment fields
@@ -162,7 +163,8 @@ public class ApplicationSerializer {
root.setDouble(queryQualityField, application.metrics().queryServiceQuality());
root.setDouble(writeQualityField, application.metrics().writeServiceQuality());
application.pemDeployKey().ifPresent(pemDeployKey -> root.setString(pemDeployKeyField, pemDeployKey));
- application.rotation().ifPresent(rotation -> root.setString(rotationField, rotation.asString()));
+ Cursor rotations = root.setArray(rotationsField);
+ application.rotations().forEach(rotation -> rotations.addString(rotation.asString()));
toSlime(application.rotationStatus(), root.setArray(rotationStatusField));
return slime;
}
@@ -329,12 +331,12 @@ public class ApplicationSerializer {
ApplicationMetrics metrics = new ApplicationMetrics(root.field(queryQualityField).asDouble(),
root.field(writeQualityField).asDouble());
Optional<String> pemDeployKey = optionalString(root.field(pemDeployKeyField));
- Optional<RotationId> rotation = rotationFromSlime(root.field(rotationField));
+ List<RotationId> rotations = rotationsFromSlime(root);
Map<HostName, RotationStatus> rotationStatus = rotationStatusFromSlime(root.field(rotationStatusField));
return new Application(id, createdAt, deploymentSpec, validationOverrides, deployments, deploymentJobs,
deploying, outstandingChange, ownershipIssueId, owner, majorVersion, metrics,
- pemDeployKey, rotation, rotationStatus);
+ pemDeployKey, rotations, rotationStatus);
}
private List<Deployment> deploymentsFromSlime(Inspector array) {
@@ -514,7 +516,25 @@ public class ApplicationSerializer {
Instant.ofEpochMilli(object.field(atField).asLong())));
}
- private Optional<RotationId> rotationFromSlime(Inspector field) {
+ private List<RotationId> rotationsFromSlime(Inspector root) {
+ final var rotations = rotationListFromSlime(root.field(rotationsField));
+ final var legacyRotation = legacyRotationFromSlime(root.field(deprecatedRotationField));
+ legacyRotation.ifPresent(rotations::add);
+ return rotations;
+ }
+
+ private List<RotationId> rotationListFromSlime(Inspector field) {
+ final var rotations = new ArrayList<RotationId>();
+
+ for (int i = 0; i < field.entries(); ++i) {
+ var entry = field.entry(i);
+ rotations.add(new RotationId(entry.asString()));
+ }
+
+ return rotations;
+ }
+
+ private Optional<RotationId> legacyRotationFromSlime(Inspector field) {
return field.valid() ? optionalString(field).map(RotationId::new) : Optional.empty();
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
index 872f4d5d1af..d605e7ceacc 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
@@ -496,7 +496,8 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
.map(URI::toString)
.forEach(globalRotationsArray::addString);
- application.rotation().ifPresent(rotation -> object.setString("rotationId", rotation.asString()));
+
+ application.rotations().stream().findFirst().ifPresent(rotation -> object.setString("rotationId", rotation.asString()));
// Per-cluster rotations
Set<RoutingPolicy> routingPolicies = controller.applications().routingPolicies(application.id());
@@ -515,7 +516,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
for (Deployment deployment : deployments) {
Cursor deploymentObject = instancesArray.addObject();
- if (application.rotation().isPresent() && deployment.zone().environment() == Environment.prod) {
+ if ((! application.rotations().isEmpty()) && deployment.zone().environment() == Environment.prod) {
toSlime(application.rotationStatus(deployment), deploymentObject);
}
@@ -707,7 +708,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
ApplicationId applicationId = ApplicationId.from(tenantName, applicationName, instanceName);
Application application = controller.applications().require(applicationId);
ZoneId zone = ZoneId.from(environment, region);
- if (!application.rotation().isPresent()) {
+ if (application.rotations().isEmpty()) {
throw new NotExistsException("global rotation does not exist for " + application);
}
Deployment deployment = application.deployments().get(zone);
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepository.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepository.java
index b3953c47c01..d2b16721503 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepository.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepository.java
@@ -47,7 +47,7 @@ public class RotationRepository {
/** Get rotation for given application */
public Optional<Rotation> getRotation(Application application) {
- return application.rotation().map(allRotations::get);
+ return application.rotations().stream().map(allRotations::get).findFirst();
}
/**
@@ -60,8 +60,8 @@ public class RotationRepository {
* @param lock Lock which must be acquired by the caller
*/
public Rotation getOrAssignRotation(Application application, RotationLock lock) {
- if (application.rotation().isPresent()) {
- return allRotations.get(application.rotation().get());
+ if (! application.rotations().isEmpty()) {
+ return allRotations.get(application.rotations().get(0));
}
if (application.deploymentSpec().globalServiceId().isEmpty()) {
throw new IllegalArgumentException("global-service-id is not set in deployment spec");
@@ -81,8 +81,8 @@ public class RotationRepository {
*/
public Map<RotationId, Rotation> availableRotations(@SuppressWarnings("unused") RotationLock lock) {
List<RotationId> assignedRotations = applications.asList().stream()
- .filter(application -> application.rotation().isPresent())
- .map(application -> application.rotation().get())
+ .filter(application -> ! application.rotations().isEmpty())
+ .flatMap(application -> application.rotations().stream())
.collect(Collectors.toList());
Map<RotationId, Rotation> unassignedRotations = new LinkedHashMap<>(this.allRotations);
assignedRotations.forEach(unassignedRotations::remove);
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java
index 7dcb4aba138..b89941b5d2d 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java
@@ -436,7 +436,7 @@ public class ControllerTest {
.build();
tester.deployCompletely(app1, applicationPackage);
app1 = tester.applications().require(app1.id());
- assertEquals("rotation-id-02", app1.rotation().get().asString());
+ assertEquals("rotation-id-02", app1.rotations().get(0).asString());
// Existing DNS records are updated to point to the newly assigned rotation
assertEquals(6, tester.controllerTester().nameService().records().size());
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java
index d7c0ac9fe9e..7b89b77d105 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java
@@ -115,7 +115,7 @@ public class ApplicationSerializerTest {
OptionalInt.of(7),
new MetricsService.ApplicationMetrics(0.5, 0.9),
Optional.of("-----BEGIN PUBLIC KEY-----\n∠( ᐛ 」∠)_\n-----END PUBLIC KEY-----"),
- Optional.of(new RotationId("my-rotation")),
+ List.of(new RotationId("my-rotation")),
rotationStatus);
Application serialized = applicationSerializer.fromSlime(applicationSerializer.toSlime(original));
@@ -151,7 +151,7 @@ public class ApplicationSerializerTest {
assertEquals(original.change(), serialized.change());
assertEquals(original.pemDeployKey(), serialized.pemDeployKey());
- assertEquals(original.rotation().get(), serialized.rotation().get());
+ assertEquals(original.rotations(), serialized.rotations());
assertEquals(original.rotationStatus(), serialized.rotationStatus());
// Test cluster utilization
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepositoryTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepositoryTest.java
index 8d1f40260e3..02a82e35f10 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepositoryTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepositoryTest.java
@@ -14,10 +14,11 @@ import org.junit.Test;
import org.junit.rules.ExpectedException;
import java.net.URI;
+import java.util.List;
import java.util.Optional;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
/**
* @author Oyvind Gronnesby
@@ -64,7 +65,7 @@ public class RotationRepositoryTest {
Rotation expected = new Rotation(new RotationId("foo-1"), "foo-1.com");
application = tester.applications().require(application.id());
- assertEquals(expected.id(), application.rotation().get());
+ assertEquals(List.of(expected.id()), application.rotations());
assertEquals(URI.create("https://app1--tenant1.global.vespa.oath.cloud:4443/"),
application.endpointsIn(SystemName.main).main().get().url());
try (RotationLock lock = repository.lock()) {
@@ -80,7 +81,7 @@ public class RotationRepositoryTest {
.searchDefinition("search foo { }") // Update application package so there is something to deploy
.build();
tester.deployCompletely(application, applicationPackage, 43);
- assertEquals(expected.id(), tester.applications().require(application.id()).rotation().get());
+ assertEquals(List.of(expected.id()), tester.applications().require(application.id()).rotations());
}
@Test
@@ -139,8 +140,7 @@ public class RotationRepositoryTest {
.build();
tester.deployCompletely(application, applicationPackage);
Application app = tester.applications().require(application.id());
- Optional<RotationId> rotation = app.rotation();
- assertFalse(rotation.isPresent());
+ assertTrue(app.rotations().isEmpty());
}
@Test
@@ -153,7 +153,7 @@ public class RotationRepositoryTest {
Application application = tester.createApplication("app2", "tenant2", 22L,
2L);
tester.deployCompletely(application, applicationPackage);
- assertEquals(new RotationId("foo-1"), tester.applications().require(application.id()).rotation().get());
+ assertEquals(List.of(new RotationId("foo-1")), tester.applications().require(application.id()).rotations());
assertEquals("https://cd--app2--tenant2.global.vespa.oath.cloud:4443/", tester.applications().require(application.id())
.endpointsIn(SystemName.cd).main().get().url().toString());
}