diff options
author | Jon Marius Venstad <jonmv@users.noreply.github.com> | 2023-08-28 15:52:07 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-08-28 15:52:07 +0200 |
commit | bd5fa1d77691b408a52de516657dc7e7410be81d (patch) | |
tree | 37c40e5fb8971d26ab4e60de3fcd34f0885428eb | |
parent | ffbc28f7cad786f78236aef0248f3543dcf25f4e (diff) | |
parent | d506e48f27d0907308238271ca75de9159911d8c (diff) |
Merge pull request #28158 from vespa-engine/jonmv/account-id-owners
Use JIRA account owners instead of usernames
22 files changed, 144 insertions, 87 deletions
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/AccountId.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/AccountId.java new file mode 100644 index 00000000000..34438c2dd1e --- /dev/null +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/AccountId.java @@ -0,0 +1,12 @@ +package com.yahoo.vespa.hosted.controller.api.integration.organization; + +import ai.vespa.validation.StringWrapper; + +public class AccountId extends StringWrapper<AccountId> { + + public AccountId(String value) { + super(value); + if (value.isBlank()) throw new IllegalArgumentException("id must be non-blank"); + } + +} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/DeploymentIssues.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/DeploymentIssues.java index 75866b68ab1..3989d4bbae6 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/DeploymentIssues.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/DeploymentIssues.java @@ -15,7 +15,7 @@ import java.util.Optional; */ public interface DeploymentIssues { - IssueId fileUnlessOpen(Optional<IssueId> issueId, ApplicationId applicationId, User asignee, Contact contact); + IssueId fileUnlessOpen(Optional<IssueId> issueId, ApplicationId applicationId, AccountId assigneeId, User assignee, Contact contact); IssueId fileUnlessOpen(Collection<ApplicationId> applicationIds, Version version); diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/Issue.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/Issue.java index 7db85da1dbb..57696e0649d 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/Issue.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/Issue.java @@ -18,11 +18,13 @@ public class Issue { private final String description; private final List<String> labels; private final User assignee; + private final AccountId assigneeId; private final Type type; private final String queue; private final Optional<String> component; - private Issue(String summary, String description, List<String> labels, User assignee, Type type, String queue, Optional<String> component) { + private Issue(String summary, String description, List<String> labels, User assignee, + AccountId assigneeId, Type type, String queue, Optional<String> component) { if (summary.isEmpty()) throw new IllegalArgumentException("Issue summary can not be empty!"); if (description.isEmpty()) throw new IllegalArgumentException("Issue description can not be empty!"); @@ -30,45 +32,50 @@ public class Issue { this.description = description; this.labels = List.copyOf(labels); this.assignee = assignee; + this.assigneeId = assigneeId; this.type = type; this.queue = queue; this.component = component; } public Issue(String summary, String description, String queue, Optional<String> component) { - this(summary, description, Collections.emptyList(), null, Type.defect, queue, component); + this(summary, description, Collections.emptyList(), null, null, Type.defect, queue, component); } public Issue append(String appendage) { - return new Issue(summary, description + appendage, labels, assignee, type, queue, component); + return new Issue(summary, description + appendage, labels, assignee, assigneeId, type, queue, component); } public Issue with(String label) { List<String> labels = new ArrayList<>(this.labels); labels.add(label); - return new Issue(summary, description, labels, assignee, type, queue, component); + return new Issue(summary, description, labels, assignee, assigneeId, type, queue, component); } public Issue with(List<String> labels) { List<String> newLabels = new ArrayList<>(this.labels); newLabels.addAll(labels); - return new Issue(summary, description, newLabels, assignee, type, queue, component); + return new Issue(summary, description, newLabels, assignee, assigneeId, type, queue, component); + } + + public Issue with(AccountId assigneeId) { + return new Issue(summary, description, labels, null, assigneeId, type, queue, component); } public Issue with(User assignee) { - return new Issue(summary, description, labels, assignee, type, queue, component); + return new Issue(summary, description, labels, assignee, null, type, queue, component); } public Issue with(Type type) { - return new Issue(summary, description, labels, assignee, type, queue, component); + return new Issue(summary, description, labels, assignee, assigneeId, type, queue, component); } public Issue in(String queue) { - return new Issue(summary, description, labels, assignee, type, queue, Optional.empty()); + return new Issue(summary, description, labels, assignee, assigneeId, type, queue, Optional.empty()); } public Issue withoutComponent() { - return new Issue(summary, description, labels, assignee, type, queue, Optional.empty()); + return new Issue(summary, description, labels, assignee, assigneeId, type, queue, Optional.empty()); } public String summary() { @@ -83,8 +90,11 @@ public class Issue { return labels; } - public Optional<User> assignee() { - return Optional.ofNullable(assignee); + public Optional<User> assignee() { return Optional.ofNullable(assignee); + } + + public Optional<AccountId> assigneeId() { + return Optional.ofNullable(assigneeId); } public Type type() { @@ -98,6 +108,7 @@ public class Issue { public Optional<String> component() { return component; } + public enum Type { defect, // A defect which needs fixing. diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/IssueHandler.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/IssueHandler.java index 8123b6f2ce6..9b9c3df8104 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/IssueHandler.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/IssueHandler.java @@ -82,6 +82,14 @@ public interface IssueHandler { Optional<User> assigneeOf(IssueId issueId); /** + * Returns the account id assigned to the given issue, if any. + * + * @param issueId ID of the issue for which to find the assignee. + * @return The account id of the user responsible for fixing the given issue, if found. + */ + Optional<AccountId> assigneeIdOf(IssueId issueId); + + /** * Reassign the issue with the given ID to the given user, and returns the outcome of this. * * @param issueId ID of the issue to be reassigned. diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/IssueInfo.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/IssueInfo.java index 52c022bebdf..f71e6b6507d 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/IssueInfo.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/IssueInfo.java @@ -16,13 +16,13 @@ public class IssueInfo { private final IssueId id; private final Instant updated; private final Status status; - private final User assignee; + private final AccountId assigneeId; - public IssueInfo(IssueId id, Instant updated, Status status, User assignee) { + public IssueInfo(IssueId id, Instant updated, Status status, AccountId assigneeId) { this.id = id; this.updated = updated; this.status = status; - this.assignee = assignee; + this.assigneeId = assigneeId; } public IssueId id() { @@ -37,11 +37,10 @@ public class IssueInfo { return status; } - public Optional<User> assignee() { - return Optional.ofNullable(assignee); + public Optional<AccountId> assigneeId() { + return Optional.ofNullable(assigneeId); } - public enum Status { toDo("To Do"), diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/MockIssueHandler.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/MockIssueHandler.java index d60c4d196e2..fb4986d0061 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/MockIssueHandler.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/MockIssueHandler.java @@ -42,7 +42,7 @@ public class MockIssueHandler implements IssueHandler { @Override public IssueId file(Issue issue) { - if (!issue.assignee().isPresent()) throw new RuntimeException(); + if (issue.assignee().isEmpty() && issue.assigneeId().isEmpty()) throw new RuntimeException(); IssueId issueId = IssueId.from("" + counter.incrementAndGet()); issues.put(issueId, new MockIssue(issue)); return issueId; @@ -55,7 +55,7 @@ public class MockIssueHandler implements IssueHandler { .map(entry -> new IssueInfo(entry.getKey(), entry.getValue().updated, entry.getValue().isOpen() ? Status.toDo : Status.done, - entry.getValue().assignee)) + entry.getValue().assigneeId)) .toList(); } @@ -85,6 +85,11 @@ public class MockIssueHandler implements IssueHandler { } @Override + public Optional<AccountId> assigneeIdOf(IssueId issueId) { + return Optional.ofNullable(issues.get(issueId).assigneeId); + } + + @Override public boolean reassign(IssueId issueId, User assignee) { issues.get(issueId).assignee = assignee; touch(issueId); @@ -159,21 +164,13 @@ public class MockIssueHandler implements IssueHandler { projects.put(projectKey, projectInfo); } - private static class PropertyInfo { - - private List<List<User>> contacts = Collections.emptyList(); - private URI issueUrl = URI.create("issues.tld"); - private URI contactsUrl = URI.create("contacts.tld"); - private URI propertyUrl = URI.create("properties.tld"); - - } - public class MockIssue { private Issue issue; private Instant updated; private boolean open; private User assignee; + private AccountId assigneeId; private List<String> watchers; private MockIssue(Issue issue) { @@ -181,11 +178,13 @@ public class MockIssueHandler implements IssueHandler { this.updated = clock.instant(); this.open = true; this.assignee = issue.assignee().orElse(null); + this.assigneeId = issue.assigneeId().orElse(null); this.watchers = new ArrayList<>(); } public Issue issue() { return issue; } public User assignee() { return assignee; } + public AccountId assigneeId() { return assigneeId; } public boolean isOpen() { return open; } public List<String> watchers() { return List.copyOf(watchers); } public void addWatcher(String watcher) { watchers.add(watcher); } diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/OwnershipIssues.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/OwnershipIssues.java index df128f18193..6822b4b432f 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/OwnershipIssues.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/OwnershipIssues.java @@ -20,11 +20,12 @@ public interface OwnershipIssues { * * @param issueId ID of the previous ownership issue filed for the given application. * @param summary Summary of an application for which to file an issue. + * @param assigneeId Issue assignee id * @param assignee Issue assignee * @param contact Contact info for the application tenant * @return ID of the created issue, if one was created. */ - Optional<IssueId> confirmOwnership(Optional<IssueId> issueId, ApplicationSummary summary, User assignee, Contact contact); + Optional<IssueId> confirmOwnership(Optional<IssueId> issueId, ApplicationSummary summary, AccountId assigneeId, User assignee, Contact contact); /** * Make sure the given ownership confirmation request is acted upon, unless it is already acknowledged. @@ -38,6 +39,6 @@ public interface OwnershipIssues { * @param issueId ID of the ownership issue. * @return The owner of the application, if it has been confirmed. */ - Optional<User> getConfirmedOwner(IssueId issueId); + Optional<AccountId> getConfirmedOwner(IssueId issueId); } diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/DummyOwnershipIssues.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/DummyOwnershipIssues.java index d3d5ba96781..caff9460628 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/DummyOwnershipIssues.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/DummyOwnershipIssues.java @@ -1,6 +1,7 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.hosted.controller.api.integration.stubs; +import com.yahoo.vespa.hosted.controller.api.integration.organization.AccountId; import com.yahoo.vespa.hosted.controller.api.integration.organization.ApplicationSummary; import com.yahoo.vespa.hosted.controller.api.integration.organization.Contact; import com.yahoo.vespa.hosted.controller.api.integration.organization.IssueId; @@ -15,7 +16,7 @@ import java.util.Optional; public class DummyOwnershipIssues implements OwnershipIssues { @Override - public Optional<IssueId> confirmOwnership(Optional<IssueId> issueId, ApplicationSummary summary, User assignee, Contact contact) { + public Optional<IssueId> confirmOwnership(Optional<IssueId> issueId, ApplicationSummary summary, AccountId assigneeId, User assignee, Contact contact) { return Optional.empty(); } @@ -24,7 +25,7 @@ public class DummyOwnershipIssues implements OwnershipIssues { } @Override - public Optional<User> getConfirmedOwner(IssueId issueId) { + public Optional<AccountId> getConfirmedOwner(IssueId issueId) { return Optional.empty(); } diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/LoggingDeploymentIssues.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/LoggingDeploymentIssues.java index 20178f300d2..1de3418bd93 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/LoggingDeploymentIssues.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/LoggingDeploymentIssues.java @@ -5,6 +5,7 @@ package com.yahoo.vespa.hosted.controller.api.integration.stubs; import com.yahoo.component.annotation.Inject; import com.yahoo.component.Version; import com.yahoo.config.provision.ApplicationId; +import com.yahoo.vespa.hosted.controller.api.integration.organization.AccountId; import com.yahoo.vespa.hosted.controller.api.integration.organization.Contact; import com.yahoo.vespa.hosted.controller.api.integration.organization.DeploymentIssues; import com.yahoo.vespa.hosted.controller.api.integration.organization.IssueId; @@ -52,7 +53,7 @@ public class LoggingDeploymentIssues implements DeploymentIssues { } @Override - public IssueId fileUnlessOpen(Optional<IssueId> issueId, ApplicationId applicationId, User assignee, Contact contact) { + public IssueId fileUnlessOpen(Optional<IssueId> issueId, ApplicationId applicationId, AccountId assigneeId, User assignee, Contact contact) { return fileUnlessPresent(issueId, applicationId); } 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 66e62ff7b95..f7eb7cdde0d 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 @@ -8,6 +8,7 @@ import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.InstanceName; import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.RevisionId; +import com.yahoo.vespa.hosted.controller.api.integration.organization.AccountId; import com.yahoo.vespa.hosted.controller.api.integration.organization.IssueId; import com.yahoo.vespa.hosted.controller.api.integration.organization.User; import com.yahoo.vespa.hosted.controller.application.ApplicationActivity; @@ -52,7 +53,8 @@ public class Application { private final OptionalLong projectId; private final Optional<IssueId> deploymentIssueId; private final Optional<IssueId> ownershipIssueId; - private final Optional<User> owner; + private final Optional<User> userOwner; + private final Optional<AccountId> issueOwner; private final OptionalInt majorVersion; private final ApplicationMetrics metrics; private final Set<PublicKey> deployKeys; @@ -60,14 +62,14 @@ public class Application { /** Creates an empty application. */ public Application(TenantAndApplicationId id, Instant now) { - this(id, now, DeploymentSpec.empty, ValidationOverrides.empty, Optional.empty(), + this(id, now, DeploymentSpec.empty, ValidationOverrides.empty, Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), OptionalInt.empty(), new ApplicationMetrics(0, 0), Set.of(), OptionalLong.empty(), RevisionHistory.empty(), List.of()); } // Do not use directly - edit through LockedApplication. public Application(TenantAndApplicationId id, Instant createdAt, DeploymentSpec deploymentSpec, ValidationOverrides validationOverrides, - Optional<IssueId> deploymentIssueId, Optional<IssueId> ownershipIssueId, Optional<User> owner, + Optional<IssueId> deploymentIssueId, Optional<IssueId> ownershipIssueId, Optional<User> userOwner, Optional<AccountId> issueOwner, OptionalInt majorVersion, ApplicationMetrics metrics, Set<PublicKey> deployKeys, OptionalLong projectId, RevisionHistory revisions, Collection<Instance> instances) { this.id = Objects.requireNonNull(id, "id cannot be null"); @@ -76,7 +78,8 @@ public class Application { this.validationOverrides = Objects.requireNonNull(validationOverrides, "validationOverrides cannot be null"); this.deploymentIssueId = Objects.requireNonNull(deploymentIssueId, "deploymentIssueId cannot be null"); this.ownershipIssueId = Objects.requireNonNull(ownershipIssueId, "ownershipIssueId cannot be null"); - this.owner = Objects.requireNonNull(owner, "owner cannot be null"); + this.userOwner = Objects.requireNonNull(userOwner, "owner cannot be null"); + this.issueOwner = Objects.requireNonNull(issueOwner, "issueOwner cannot be null"); this.majorVersion = Objects.requireNonNull(majorVersion, "majorVersion cannot be null"); this.metrics = Objects.requireNonNull(metrics, "metrics cannot be null"); this.deployKeys = Objects.requireNonNull(deployKeys, "deployKeys cannot be null"); @@ -143,8 +146,12 @@ public class Application { return ownershipIssueId; } - public Optional<User> owner() { - return owner; + public Optional<User> userOwner() { + return userOwner; + } + + public Optional<AccountId> issueOwner() { + return issueOwner; } /** 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 b99c52d1533..066d10041c2 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 @@ -5,6 +5,7 @@ import com.yahoo.config.application.api.DeploymentSpec; import com.yahoo.config.application.api.ValidationOverrides; import com.yahoo.config.provision.InstanceName; import com.yahoo.transaction.Mutex; +import com.yahoo.vespa.hosted.controller.api.integration.organization.AccountId; import com.yahoo.vespa.hosted.controller.api.integration.organization.IssueId; import com.yahoo.vespa.hosted.controller.api.integration.organization.User; import com.yahoo.vespa.hosted.controller.application.TenantAndApplicationId; @@ -37,7 +38,8 @@ public class LockedApplication { private final ValidationOverrides validationOverrides; private final Optional<IssueId> deploymentIssueId; private final Optional<IssueId> ownershipIssueId; - private final Optional<User> owner; + private final Optional<User> userOwner; + private final Optional<AccountId> issueOwner; private final OptionalInt majorVersion; private final ApplicationMetrics metrics; private final Set<PublicKey> deployKeys; @@ -53,15 +55,14 @@ public class LockedApplication { */ LockedApplication(Application application, Mutex lock) { this(Objects.requireNonNull(lock, "lock cannot be null"), application.id(), application.createdAt(), - application.deploymentSpec(), application.validationOverrides(), - application.deploymentIssueId(), application.ownershipIssueId(), - application.owner(), application.majorVersion(), application.metrics(), application.deployKeys(), + application.deploymentSpec(), application.validationOverrides(), application.deploymentIssueId(), application.ownershipIssueId(), + application.userOwner(), application.issueOwner(), application.majorVersion(), application.metrics(), application.deployKeys(), application.projectId(), application.instances(), application.revisions()); } private LockedApplication(Mutex lock, TenantAndApplicationId id, Instant createdAt, DeploymentSpec deploymentSpec, - ValidationOverrides validationOverrides, - Optional<IssueId> deploymentIssueId, Optional<IssueId> ownershipIssueId, Optional<User> owner, + ValidationOverrides validationOverrides, Optional<IssueId> deploymentIssueId, + Optional<IssueId> ownershipIssueId, Optional<User> userOwner, Optional<AccountId> issueOwner, OptionalInt majorVersion, ApplicationMetrics metrics, Set<PublicKey> deployKeys, OptionalLong projectId, Map<InstanceName, Instance> instances, RevisionHistory revisions) { this.lock = lock; @@ -71,7 +72,8 @@ public class LockedApplication { this.validationOverrides = validationOverrides; this.deploymentIssueId = deploymentIssueId; this.ownershipIssueId = ownershipIssueId; - this.owner = owner; + this.userOwner = userOwner; + this.issueOwner = issueOwner; this.majorVersion = majorVersion; this.metrics = metrics; this.deployKeys = deployKeys; @@ -83,7 +85,7 @@ public class LockedApplication { /** Returns a read-only copy of this */ public Application get() { return new Application(id, createdAt, deploymentSpec, validationOverrides, - deploymentIssueId, ownershipIssueId, owner, majorVersion, metrics, deployKeys, + deploymentIssueId, ownershipIssueId, userOwner, issueOwner, majorVersion, metrics, deployKeys, projectId, revisions, instances.values()); } @@ -91,7 +93,7 @@ public class LockedApplication { var instances = new HashMap<>(this.instances); instances.put(instance, new Instance(id.instance(instance))); return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, - deploymentIssueId, ownershipIssueId, owner, majorVersion, metrics, deployKeys, + deploymentIssueId, ownershipIssueId, userOwner, issueOwner, majorVersion, metrics, deployKeys, projectId, instances, revisions); } @@ -99,7 +101,7 @@ public class LockedApplication { var instances = new HashMap<>(this.instances); instances.put(instance, modification.apply(instances.get(instance))); return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, - deploymentIssueId, ownershipIssueId, owner, majorVersion, metrics, deployKeys, + deploymentIssueId, ownershipIssueId, userOwner, issueOwner, majorVersion, metrics, deployKeys, projectId, instances, revisions); } @@ -107,57 +109,57 @@ public class LockedApplication { var instances = new HashMap<>(this.instances); instances.remove(instance); return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, - deploymentIssueId, ownershipIssueId, owner, majorVersion, metrics, deployKeys, + deploymentIssueId, ownershipIssueId, userOwner, issueOwner, majorVersion, metrics, deployKeys, projectId, instances, revisions); } public LockedApplication withProjectId(OptionalLong projectId) { return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, - deploymentIssueId, ownershipIssueId, owner, majorVersion, metrics, deployKeys, + deploymentIssueId, ownershipIssueId, userOwner, issueOwner, majorVersion, metrics, deployKeys, projectId, instances, revisions); } public LockedApplication withDeploymentIssueId(IssueId issueId) { return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, - Optional.ofNullable(issueId), ownershipIssueId, owner, majorVersion, metrics, deployKeys, + Optional.ofNullable(issueId), ownershipIssueId, userOwner, issueOwner, majorVersion, metrics, deployKeys, projectId, instances, revisions); } public LockedApplication with(DeploymentSpec deploymentSpec) { return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, - deploymentIssueId, ownershipIssueId, owner, majorVersion, metrics, deployKeys, + deploymentIssueId, ownershipIssueId, userOwner, issueOwner, majorVersion, metrics, deployKeys, projectId, instances, revisions); } public LockedApplication with(ValidationOverrides validationOverrides) { return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, - deploymentIssueId, ownershipIssueId, owner, majorVersion, metrics, deployKeys, + deploymentIssueId, ownershipIssueId, userOwner, issueOwner, majorVersion, metrics, deployKeys, projectId, instances, revisions); } public LockedApplication withOwnershipIssueId(IssueId issueId) { return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, - deploymentIssueId, Optional.of(issueId), owner, majorVersion, metrics, deployKeys, + deploymentIssueId, Optional.of(issueId), userOwner, issueOwner, majorVersion, metrics, deployKeys, projectId, instances, revisions); } - public LockedApplication withOwner(User owner) { + public LockedApplication withOwner(AccountId issueOwner) { return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, - deploymentIssueId, ownershipIssueId, Optional.of(owner), majorVersion, metrics, deployKeys, + deploymentIssueId, ownershipIssueId, userOwner, Optional.of(issueOwner), majorVersion, metrics, deployKeys, projectId, instances, revisions); } /** Set a major version for this, or set to null to remove any major version override */ public LockedApplication withMajorVersion(Integer majorVersion) { return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, - deploymentIssueId, ownershipIssueId, owner, - majorVersion == null ? OptionalInt.empty() : OptionalInt.of(majorVersion), + deploymentIssueId, ownershipIssueId, userOwner, + issueOwner, majorVersion == null ? OptionalInt.empty() : OptionalInt.of(majorVersion), metrics, deployKeys, projectId, instances, revisions); } public LockedApplication with(ApplicationMetrics metrics) { return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, - deploymentIssueId, ownershipIssueId, owner, majorVersion, metrics, deployKeys, + deploymentIssueId, ownershipIssueId, userOwner, issueOwner, majorVersion, metrics, deployKeys, projectId, instances, revisions); } @@ -165,7 +167,7 @@ public class LockedApplication { Set<PublicKey> keys = new LinkedHashSet<>(deployKeys); keys.add(pemDeployKey); return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, - deploymentIssueId, ownershipIssueId, owner, majorVersion, metrics, keys, + deploymentIssueId, ownershipIssueId, userOwner, issueOwner, majorVersion, metrics, keys, projectId, instances, revisions); } @@ -173,13 +175,13 @@ public class LockedApplication { Set<PublicKey> keys = new LinkedHashSet<>(deployKeys); keys.remove(pemDeployKey); return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, - deploymentIssueId, ownershipIssueId, owner, majorVersion, metrics, keys, + deploymentIssueId, ownershipIssueId, userOwner, issueOwner, majorVersion, metrics, keys, projectId, instances, revisions); } public LockedApplication withRevisions(UnaryOperator<RevisionHistory> change) { return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, - deploymentIssueId, ownershipIssueId, owner, majorVersion, metrics, + deploymentIssueId, ownershipIssueId, userOwner, issueOwner, majorVersion, metrics, deployKeys, projectId, instances, change.apply(revisions)); } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmer.java index cbdfcf70123..1a944cfd5d7 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmer.java @@ -6,6 +6,7 @@ import com.yahoo.vespa.hosted.controller.ApplicationController; import com.yahoo.vespa.hosted.controller.Controller; import com.yahoo.vespa.hosted.controller.Instance; import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId; +import com.yahoo.vespa.hosted.controller.api.integration.organization.AccountId; import com.yahoo.vespa.hosted.controller.api.integration.organization.ApplicationSummary; import com.yahoo.vespa.hosted.controller.api.integration.organization.IssueId; import com.yahoo.vespa.hosted.controller.api.integration.organization.OwnershipIssues; @@ -70,7 +71,8 @@ public class ApplicationOwnershipConfirmer extends ControllerMaintainer { tenantOf(application.id()).contact().flatMap(contact -> { return ownershipIssues.confirmOwnership(application.ownershipIssueId(), summaryOf(application.id()), - determineAssignee(application), + application.issueOwner().orElse(null), + application.userOwner().orElse(null), contact); }).ifPresent(newIssueId -> store(newIssueId, application.id())); } @@ -156,8 +158,12 @@ public class ApplicationOwnershipConfirmer extends ControllerMaintainer { return ApplicationList.from(controller().applications().readable()); } - private User determineAssignee(Application application) { - return application.owner().orElse(null); + private AccountId determineAssignee(Application application) { + return application.issueOwner().orElse(null); + } + + private User determineLegacyAssignee(Application application) { + return application.userOwner().orElse(null); } private Tenant tenantOf(TenantAndApplicationId applicationId) { diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentIssueReporter.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentIssueReporter.java index c352fb053dc..cd0f4be7a48 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentIssueReporter.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentIssueReporter.java @@ -117,9 +117,10 @@ public class DeploymentIssueReporter extends ControllerMaintainer { try { Tenant tenant = ownerOf(application.id()); tenant.contact().ifPresent(contact -> { - User assignee = application.owner().orElse(null); Optional<IssueId> ourIssueId = application.deploymentIssueId(); - IssueId issueId = deploymentIssues.fileUnlessOpen(ourIssueId, application.id().defaultInstance(), assignee, contact); + IssueId issueId = deploymentIssues.fileUnlessOpen(ourIssueId, application.id().defaultInstance(), + application.issueOwner().orElse(null), application.userOwner().orElse(null), + contact); store(application.id(), issueId); }); } 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 41b40f447d8..e6b3dd74abc 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 @@ -24,6 +24,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType; import com.yahoo.vespa.hosted.controller.api.integration.deployment.RevisionId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.SourceRevision; +import com.yahoo.vespa.hosted.controller.api.integration.organization.AccountId; import com.yahoo.vespa.hosted.controller.api.integration.organization.IssueId; import com.yahoo.vespa.hosted.controller.api.integration.organization.User; import com.yahoo.vespa.hosted.controller.application.AssignedRotation; @@ -86,7 +87,8 @@ public class ApplicationSerializer { private static final String revisionPinnedField = "revisionPinned"; private static final String deploymentIssueField = "deploymentIssueId"; private static final String ownershipIssueIdField = "ownershipIssueId"; - private static final String ownerField = "confirmedOwner"; + private static final String userOwnerField = "confirmedOwner"; + private static final String issueOwnerField = "confirmedOwnerId"; private static final String majorVersionField = "majorVersion"; private static final String writeQualityField = "writeQuality"; private static final String queryQualityField = "queryQuality"; @@ -174,7 +176,8 @@ public class ApplicationSerializer { application.projectId().ifPresent(projectId -> root.setLong(projectIdField, projectId)); application.deploymentIssueId().ifPresent(jiraIssueId -> root.setString(deploymentIssueField, jiraIssueId.value())); application.ownershipIssueId().ifPresent(issueId -> root.setString(ownershipIssueIdField, issueId.value())); - application.owner().ifPresent(owner -> root.setString(ownerField, owner.username())); + application.userOwner().ifPresent(owner -> root.setString(userOwnerField, owner.username())); + application.issueOwner().ifPresent(owner -> root.setString(issueOwnerField, owner.value())); application.majorVersion().ifPresent(majorVersion -> root.setLong(majorVersionField, majorVersion)); root.setDouble(queryQualityField, application.metrics().queryServiceQuality()); root.setDouble(writeQualityField, application.metrics().writeServiceQuality()); @@ -349,7 +352,8 @@ public class ApplicationSerializer { ValidationOverrides validationOverrides = ValidationOverrides.fromXml(root.field(validationOverridesField).asString()); Optional<IssueId> deploymentIssueId = SlimeUtils.optionalString(root.field(deploymentIssueField)).map(IssueId::from); Optional<IssueId> ownershipIssueId = SlimeUtils.optionalString(root.field(ownershipIssueIdField)).map(IssueId::from); - Optional<User> owner = SlimeUtils.optionalString(root.field(ownerField)).map(User::from); + Optional<User> userOwner = SlimeUtils.optionalString(root.field(userOwnerField)).map(User::from); + Optional<AccountId> issueOwner = SlimeUtils.optionalString(root.field(issueOwnerField)).map(AccountId::new); OptionalInt majorVersion = SlimeUtils.optionalInteger(root.field(majorVersionField)); ApplicationMetrics metrics = new ApplicationMetrics(root.field(queryQualityField).asDouble(), root.field(writeQualityField).asDouble()); @@ -359,7 +363,7 @@ public class ApplicationSerializer { RevisionHistory revisions = revisionsFromSlime(root.field(prodVersionsField), root.field(devVersionsField), id); return new Application(id, createdAt, deploymentSpec, validationOverrides, - deploymentIssueId, ownershipIssueId, owner, majorVersion, metrics, + deploymentIssueId, ownershipIssueId, userOwner, issueOwner, majorVersion, metrics, deployKeys, projectId, revisions, instances); } 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 94c7829a851..9394a1fcbe2 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 @@ -1739,7 +1739,7 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler { application.activity().lastWritesPerSecond().ifPresent(value -> activity.setDouble("lastWritesPerSecond", value)); application.ownershipIssueId().ifPresent(issueId -> object.setString("ownershipIssueId", issueId.value())); - application.owner().ifPresent(owner -> object.setString("owner", owner.username())); + application.issueOwner().ifPresent(owner -> object.setString("owner", owner.value())); application.deploymentIssueId().ifPresent(issueId -> object.setString("deploymentIssueId", issueId.value())); } @@ -1931,7 +1931,7 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler { application.activity().lastWritesPerSecond().ifPresent(value -> activity.setDouble("lastWritesPerSecond", value)); application.ownershipIssueId().ifPresent(issueId -> object.setString("ownershipIssueId", issueId.value())); - application.owner().ifPresent(owner -> object.setString("owner", owner.username())); + application.issueOwner().ifPresent(owner -> object.setString("owner", owner.value())); application.deploymentIssueId().ifPresent(issueId -> object.setString("deploymentIssueId", issueId.value())); } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/DeploymentQuotaCalculatorTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/DeploymentQuotaCalculatorTest.java index cb9c1c2fa13..f2897c14ffe 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/DeploymentQuotaCalculatorTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/DeploymentQuotaCalculatorTest.java @@ -63,7 +63,7 @@ public class DeploymentQuotaCalculatorTest { void quota_is_divided_among_prod_and_manual_instances() { var existing_dev_deployment = new Application(TenantAndApplicationId.from(ApplicationId.defaultId()), Instant.EPOCH, DeploymentSpec.empty, ValidationOverrides.empty, Optional.empty(), - Optional.empty(), Optional.empty(), OptionalInt.empty(), new ApplicationMetrics(1, 1), Set.of(), OptionalLong.empty(), RevisionHistory.empty(), + Optional.empty(), Optional.empty(), Optional.empty(), OptionalInt.empty(), new ApplicationMetrics(1, 1), Set.of(), OptionalLong.empty(), RevisionHistory.empty(), List.of(new Instance(ApplicationId.defaultId()).withNewDeployment(ZoneId.from(Environment.dev, RegionName.defaultName()), RevisionId.forProduction(1), Version.emptyVersion, Instant.EPOCH, Map.of(), QuotaUsage.create(0.53d), CloudAccount.empty))); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmerTest.java index b643d3e90d2..142210843ff 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmerTest.java @@ -3,6 +3,7 @@ package com.yahoo.vespa.hosted.controller.maintenance; import com.yahoo.config.provision.InstanceName; import com.yahoo.vespa.hosted.controller.LockedTenant; +import com.yahoo.vespa.hosted.controller.api.integration.organization.AccountId; import com.yahoo.vespa.hosted.controller.api.integration.organization.ApplicationSummary; import com.yahoo.vespa.hosted.controller.api.integration.organization.Contact; import com.yahoo.vespa.hosted.controller.api.integration.organization.IssueId; @@ -80,10 +81,10 @@ public class ApplicationOwnershipConfirmerTest { assertEquals(issueId2, app.application().ownershipIssueId(), "A new confirmation issue id is stored when something is returned to the maintainer."); - assertFalse(app.application().owner().isPresent(), "No owner is stored for application"); - issues.owner = Optional.of(User.from("username")); + assertFalse(app.application().issueOwner().isPresent(), "No owner is stored for application"); + issues.owner = Optional.of(new AccountId("username")); confirmer.maintain(); - assertEquals(app.application().owner().get().username(), "username", "Owner has been added to application"); + assertEquals(app.application().issueOwner().get().value(), "username", "Owner has been added to application"); // The app deletes all production deployments — see that the issue is forgotten. assertEquals(issueId2, app.application().ownershipIssueId(), "Confirmation issue for application is still open."); @@ -103,10 +104,10 @@ public class ApplicationOwnershipConfirmerTest { private Optional<IssueId> response; private boolean escalated = false; - private Optional<User> owner = Optional.empty(); + private Optional<AccountId> owner = Optional.empty(); @Override - public Optional<IssueId> confirmOwnership(Optional<IssueId> issueId, ApplicationSummary summary, User assignee, Contact contact) { + public Optional<IssueId> confirmOwnership(Optional<IssueId> issueId, ApplicationSummary summary, AccountId assigneeId, User assignee, Contact contact) { return response; } @@ -116,7 +117,7 @@ public class ApplicationOwnershipConfirmerTest { } @Override - public Optional<User> getConfirmedOwner(IssueId issueId) { + public Optional<AccountId> getConfirmedOwner(IssueId issueId) { return owner; } } 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 7e10d481f0f..13f7ec2a4ec 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 @@ -17,6 +17,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationV import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.RevisionId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.SourceRevision; +import com.yahoo.vespa.hosted.controller.api.integration.organization.AccountId; import com.yahoo.vespa.hosted.controller.api.integration.organization.IssueId; import com.yahoo.vespa.hosted.controller.api.integration.organization.User; import com.yahoo.vespa.hosted.controller.application.AssignedRotation; @@ -159,6 +160,7 @@ public class ApplicationSerializerTest { Optional.of(IssueId.from("4321")), Optional.of(IssueId.from("1234")), Optional.of(User.from("by-username")), + Optional.of(new AccountId("foo8ar")), OptionalInt.of(7), new ApplicationMetrics(0.5, 0.9), Set.of(publicKey, otherPublicKey), @@ -211,7 +213,8 @@ public class ApplicationSerializerTest { serialized.require(id1.instance()).jobPause(DeploymentContext.stagingTest)); assertEquals(original.ownershipIssueId(), serialized.ownershipIssueId()); - assertEquals(original.owner(), serialized.owner()); + assertEquals(original.userOwner(), serialized.userOwner()); + assertEquals(original.issueOwner(), serialized.issueOwner()); assertEquals(original.majorVersion(), serialized.majorVersion()); assertEquals(original.deployKeys(), serialized.deployKeys()); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java index 8e73c26a2b2..345f164f8da 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java @@ -44,6 +44,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServ import com.yahoo.vespa.hosted.controller.api.integration.configserver.Node; import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion; import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId; +import com.yahoo.vespa.hosted.controller.api.integration.organization.AccountId; import com.yahoo.vespa.hosted.controller.api.integration.organization.Contact; import com.yahoo.vespa.hosted.controller.api.integration.organization.IssueId; import com.yahoo.vespa.hosted.controller.api.integration.organization.User; @@ -1032,7 +1033,7 @@ public class ApplicationApiTest extends ControllerContainerTest { tester.applications().lockApplicationOrThrow(id, application -> tester.controller().applications().store(application.withDeploymentIssueId(IssueId.from("123")) .withOwnershipIssueId(IssueId.from("321")) - .withOwner(User.from("owner-username")))); + .withOwner(new AccountId("owner-account-id")))); } @Test diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance1-recursive.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance1-recursive.json index 32b091a92ca..e5ee9157792 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance1-recursive.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance1-recursive.json @@ -182,6 +182,6 @@ "lastWritesPerSecond": 2.0 }, "ownershipIssueId": "321", - "owner": "owner-username", + "owner": "owner-account-id", "deploymentIssueId": "123" } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/recursive-root.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/recursive-root.json index 6dc58cc2800..f1aba622fcf 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/recursive-root.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/recursive-root.json @@ -189,7 +189,7 @@ "lastWritesPerSecond": 2.0 }, "ownershipIssueId": "321", - "owner": "owner-username", + "owner": "owner-account-id", "deploymentIssueId": "123" } ], diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant1-recursive.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant1-recursive.json index 210a637ece8..e252e042e43 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant1-recursive.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant1-recursive.json @@ -188,7 +188,7 @@ "lastWritesPerSecond": 2.0 }, "ownershipIssueId": "321", - "owner": "owner-username", + "owner": "owner-account-id", "deploymentIssueId": "123" } ], |