diff options
author | bjormel <bjormel@yahooinc.com> | 2023-10-01 12:23:12 +0000 |
---|---|---|
committer | bjormel <bjormel@yahooinc.com> | 2023-10-01 12:23:12 +0000 |
commit | e9058b555d4dfea2f6c872d9a677e8678b569569 (patch) | |
tree | fa1b67c6e39712c1e0d9f308b0dd55573b43f913 /controller-api/src/main/java/com/yahoo/vespa/hosted | |
parent | 0ad931fa86658904fe9212b014d810236b0e00e4 (diff) | |
parent | 16030193ec04ee41e98779a3d7ee6a6c1d0d0d6f (diff) |
Merge branch 'master' into bjormel/aws-main-controller
Diffstat (limited to 'controller-api/src/main/java/com/yahoo/vespa/hosted')
15 files changed, 125 insertions, 26 deletions
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/archive/ArchiveService.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/archive/ArchiveService.java index ed965f4331e..66cf3eef954 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/archive/ArchiveService.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/archive/ArchiveService.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.archive; +import com.yahoo.component.Version; import com.yahoo.config.provision.CloudAccount; import com.yahoo.config.provision.TenantName; import com.yahoo.config.provision.zone.ZoneId; @@ -10,6 +11,7 @@ import java.net.URI; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.stream.Stream; /** * Service that manages archive storage URIs for tenant nodes. @@ -28,4 +30,19 @@ public interface ArchiveService { Optional<String> findEnclaveArchiveBucket(ZoneId zoneId, CloudAccount cloudAccount); URI bucketURI(ZoneId zoneId, String bucketName); + + /** + * @return the version of the template that was used during the last apply for the given cloud account, + * or {@link Version#emptyVersion} if the version tag was not present or invalid, + * or {@link Optional#empty()} if the we have no access to the cloud account (template probably not applied yet) + */ + Optional<Version> getEnclaveTemplateVersion(CloudAccount cloudAccount); + + static Stream<Version> parseVersion(String versionString) { + try { + return Stream.of(Version.fromString(versionString)); + } catch (IllegalArgumentException e) { + return Stream.empty(); + } + } } diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/archive/MockArchiveService.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/archive/MockArchiveService.java index 7461d3aa47e..4e6e71ca855 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/archive/MockArchiveService.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/archive/MockArchiveService.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.archive; +import com.yahoo.component.Version; import com.yahoo.config.provision.CloudAccount; import com.yahoo.config.provision.TenantName; import com.yahoo.config.provision.zone.ZoneId; @@ -59,6 +60,11 @@ public class MockArchiveService implements ArchiveService { return URI.create(String.format("s3://%s/", bucketName)); } + @Override + public Optional<Version> getEnclaveTemplateVersion(CloudAccount cloudAccount) { + return Optional.of(new Version(1, 2, 3)); + } + public void setEnclaveArchiveBucket(ZoneId zoneId, CloudAccount cloudAccount, String bucketName) { removeEnclaveArchiveBucket(zoneId, cloudAccount); diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/BillingController.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/BillingController.java index 5c6e5c9542a..dfa0d4dccf2 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/BillingController.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/BillingController.java @@ -2,6 +2,7 @@ package com.yahoo.vespa.hosted.controller.api.integration.billing; import com.yahoo.config.provision.TenantName; +import com.yahoo.vespa.hosted.controller.tenant.CloudTenant; import java.math.BigDecimal; import java.time.LocalDate; @@ -109,6 +110,9 @@ public interface BillingController { /** Get all bills from the system */ List<Bill> getBills(); + /** Get the bill with the given id */ + Bill getBill(Bill.Id billId); + /** Get the bill collection method for the given tenant */ default CollectionMethod getCollectionMethod(TenantName tenant) { return CollectionMethod.NONE; @@ -125,4 +129,8 @@ public interface BillingController { } default void updateCache(List<TenantName> tenants) {} -}
\ No newline at end of file + + default String exportBill(Bill bill, String exportMethod, CloudTenant tenant) { + return "NOT_IMPLEMENTED"; + } +} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/BillingReporter.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/BillingReporter.java index a4b3abf3bf9..719d22429b8 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/BillingReporter.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/BillingReporter.java @@ -1,5 +1,8 @@ package com.yahoo.vespa.hosted.controller.api.integration.billing; +import com.yahoo.vespa.hosted.controller.tenant.BillingReference; +import com.yahoo.vespa.hosted.controller.tenant.CloudTenant; + public interface BillingReporter { - double maintain(); + BillingReference maintainTenant(CloudTenant tenant); } diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/BillingReporterMock.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/BillingReporterMock.java new file mode 100644 index 00000000000..34599f83a8c --- /dev/null +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/BillingReporterMock.java @@ -0,0 +1,21 @@ +// 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.billing; + +import com.yahoo.vespa.hosted.controller.tenant.BillingReference; +import com.yahoo.vespa.hosted.controller.tenant.CloudTenant; + +import java.time.Clock; +import java.util.UUID; + +public class BillingReporterMock implements BillingReporter { + private final Clock clock; + + public BillingReporterMock(Clock clock) { + this.clock = clock; + } + + @Override + public BillingReference maintainTenant(CloudTenant tenant) { + return new BillingReference(UUID.randomUUID().toString(), clock.instant()); + } +} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/MockBillingController.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/MockBillingController.java index 671739bacab..eb20126304e 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/MockBillingController.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/MockBillingController.java @@ -171,6 +171,15 @@ public class MockBillingController implements BillingController { } @Override + public Bill getBill(Bill.Id billId) { + return committedBills.values().stream() + .flatMap(Collection::stream) + .filter(bill -> bill.id().equals(billId)) + .findFirst() + .orElseThrow(); + } + + @Override public CollectionMethod getCollectionMethod(TenantName tenant) { return collectionMethod.getOrDefault(tenant, CollectionMethod.AUTO); } diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ConfigServer.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ConfigServer.java index 19bfc84db7a..31fdc9d1b64 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ConfigServer.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ConfigServer.java @@ -6,6 +6,7 @@ import com.yahoo.component.Version; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.EndpointsChecker.Availability; import com.yahoo.config.provision.EndpointsChecker.Endpoint; +import com.yahoo.config.provision.HostName; import com.yahoo.config.provision.zone.ZoneId; import ai.vespa.http.DomainName; import ai.vespa.http.HttpURL.Path; @@ -16,6 +17,8 @@ import com.yahoo.vespa.hosted.controller.api.application.v4.model.EndpointStatus import com.yahoo.vespa.hosted.controller.api.application.v4.model.SearchNodeMetrics; import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId; import com.yahoo.vespa.hosted.controller.api.integration.LogEntry; +import com.yahoo.vespa.hosted.controller.api.integration.dataplanetoken.FingerPrint; +import com.yahoo.vespa.hosted.controller.api.integration.dataplanetoken.TokenId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.TestReport; import com.yahoo.vespa.hosted.controller.api.integration.deployment.TesterCloud; import com.yahoo.vespa.hosted.controller.api.integration.noderepository.RestartFilter; @@ -158,4 +161,7 @@ public interface ConfigServer { /** Validates secret store configuration. */ String validateSecretStore(DeploymentId deploymentId, TenantSecretStore tenantSecretStore, String region, String parameterName); + /** Fingerprints of active data plane tokens, per healthy host with token auth, in the given deployment. */ + Map<HostName, Map<TokenId, List<FingerPrint>>> activeTokenFingerprints(DeploymentId deploymentId); + } diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/MockVpcEndpointService.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/MockVpcEndpointService.java index 39975138140..48cdc6ee053 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/MockVpcEndpointService.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/MockVpcEndpointService.java @@ -29,7 +29,7 @@ public class MockVpcEndpointService implements VpcEndpointService { } @Override - public synchronized Optional<DnsChallenge> setPrivateDns(DomainName privateDnsName, ClusterId clusterId, Optional<CloudAccount> account) { + public synchronized Optional<DnsChallenge> setPrivateDns(DomainName privateDnsName, ClusterId clusterId, Optional<CloudAccount> account, boolean isGenerated) { DnsChallenge challenge = new DnsChallenge(RecordName.from("challenge--" + privateDnsName.value()), RecordData.from(account.map(CloudAccount::value).orElse("system")), clusterId, diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/VpcEndpointService.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/VpcEndpointService.java index a3ee7681e2a..97e1b88b25c 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/VpcEndpointService.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/VpcEndpointService.java @@ -38,7 +38,7 @@ public interface VpcEndpointService { enum ChallengeState { pending, ready, running, done } /** Sets the private DNS name for any VPC endpoint for the given cluster, potentially guarded by a challenge. */ - Optional<DnsChallenge> setPrivateDns(DomainName privateDnsName, ClusterId clusterId, Optional<CloudAccount> account); + Optional<DnsChallenge> setPrivateDns(DomainName privateDnsName, ClusterId clusterId, Optional<CloudAccount> account, boolean isGenerated); /** Attempts to complete the challenge, and returns the updated challenge state. */ ChallengeState process(DnsChallenge challenge); diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/systemflags/v1/SystemFlagsDataArchive.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/systemflags/v1/SystemFlagsDataArchive.java index e661c88e117..8f47ac68cda 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/systemflags/v1/SystemFlagsDataArchive.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/systemflags/v1/SystemFlagsDataArchive.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.TextNode; import com.yahoo.component.Version; import com.yahoo.config.provision.ApplicationId; +import com.yahoo.config.provision.ApplicationName; import com.yahoo.config.provision.CloudAccount; import com.yahoo.config.provision.CloudName; import com.yahoo.config.provision.ClusterSpec; @@ -191,8 +192,8 @@ public class SystemFlagsDataArchive { flagData.rules().forEach(rule -> rule.conditions().forEach(condition -> { int force_switch_expression_dummy = switch (condition.type()) { case RELATIONAL -> switch (condition.dimension()) { - case INSTANCE_ID, CLOUD, CLOUD_ACCOUNT, CLUSTER_ID, CLUSTER_TYPE, CONSOLE_USER_EMAIL, - ENVIRONMENT, HOSTNAME, NODE_TYPE, SYSTEM, TENANT_ID, ZONE_ID -> + case APPLICATION_ID, CLOUD, CLOUD_ACCOUNT, CLUSTER_ID, CLUSTER_TYPE, CONSOLE_USER_EMAIL, + ENVIRONMENT, HOSTNAME, INSTANCE_ID, NODE_TYPE, SYSTEM, TENANT_ID, ZONE_ID -> throw new FlagValidationException(condition.type().toWire() + " " + DimensionHelper.toWire(condition.dimension()) + " condition is not supported"); @@ -206,7 +207,7 @@ public class SystemFlagsDataArchive { }; case WHITELIST, BLACKLIST -> switch (condition.dimension()) { - case INSTANCE_ID -> validateConditionValues(condition, ApplicationId::fromSerializedForm); + case APPLICATION_ID -> validateConditionValues(condition, SystemFlagsDataArchive::validateTenantApplication); case CONSOLE_USER_EMAIL -> validateConditionValues(condition, email -> { if (!email.contains("@")) throw new FlagValidationException("Invalid email address: " + email); @@ -220,6 +221,7 @@ public class SystemFlagsDataArchive { case CLUSTER_TYPE -> validateConditionValues(condition, ClusterSpec.Type::from); case ENVIRONMENT -> validateConditionValues(condition, Environment::from); case HOSTNAME -> validateConditionValues(condition, HostName::of); + case INSTANCE_ID -> validateConditionValues(condition, ApplicationId::fromSerializedForm); case NODE_TYPE -> validateConditionValues(condition, NodeType::valueOf); case SYSTEM -> throw new IllegalStateException("Flag data contains system dimension"); case TENANT_ID -> validateConditionValues(condition, TenantName::from); @@ -250,23 +252,20 @@ public class SystemFlagsDataArchive { return 0; // dummy to force switch expression } + private static void validateTenantApplication(String application) { + String[] parts = application.split(":"); + if (parts.length != 2) + throw new IllegalArgumentException("Applications must be on the form tenant:application, but was %s".formatted(application)); + TenantName.from(parts[0]); + ApplicationName.from(parts[1]); + } + private static FlagData parseFlagData(FlagId flagId, String fileContent, ZoneRegistry zoneRegistry, boolean inController) { if (fileContent.isBlank()) return new FlagData(flagId); final JsonNode root; try { root = mapper.readTree(fileContent); - // TODO (mortent): Remove this after completing migration of APPLICATION_ID dimension - // replace "application" with "instance" for all dimension fields -// List<JsonNode> dimensionParents = root.findParents("dimension"); -// for (JsonNode parentNode : dimensionParents) { -// JsonNode dimension = parentNode.get("dimension"); -// if (dimension.isTextual() && "application".equals(dimension.textValue())) { -// ObjectNode parent = (ObjectNode) parentNode; -// parent.remove("dimension"); -// parent.put("dimension", "instance"); -// } -// } } catch (JsonProcessingException e) { throw new FlagValidationException("Invalid JSON: " + e.getMessage()); } diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/AthenzTenant.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/AthenzTenant.java index 53a3f431de7..0754a5ed49f 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/AthenzTenant.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/AthenzTenant.java @@ -8,6 +8,7 @@ import com.yahoo.vespa.hosted.controller.api.identifiers.PropertyId; import com.yahoo.vespa.hosted.controller.api.integration.organization.Contact; import java.time.Instant; +import java.util.List; import java.util.Objects; import java.util.Optional; @@ -27,8 +28,9 @@ public class AthenzTenant extends Tenant { * Use {@link #create(TenantName, AthenzDomain, Property, Optional, Instant)}. * */ public AthenzTenant(TenantName name, AthenzDomain domain, Property property, Optional<PropertyId> propertyId, - Optional<Contact> contact, Instant createdAt, LastLoginInfo lastLoginInfo, Instant tenantRolesLastMaintained) { - super(name, createdAt, lastLoginInfo, contact, tenantRolesLastMaintained); + Optional<Contact> contact, Instant createdAt, LastLoginInfo lastLoginInfo, Instant tenantRolesLastMaintained, + List<CloudAccountInfo> cloudAccounts) { + super(name, createdAt, lastLoginInfo, contact, tenantRolesLastMaintained, cloudAccounts); this.domain = Objects.requireNonNull(domain, "domain must be non-null"); this.property = Objects.requireNonNull(property, "property must be non-null"); this.propertyId = Objects.requireNonNull(propertyId, "propertyId must be non-null"); @@ -62,7 +64,7 @@ public class AthenzTenant extends Tenant { /** Create a new Athenz tenant */ public static AthenzTenant create(TenantName name, AthenzDomain domain, Property property, Optional<PropertyId> propertyId, Instant createdAt) { - return new AthenzTenant(requireName(name), domain, property, propertyId, Optional.empty(), createdAt, LastLoginInfo.EMPTY, Instant.EPOCH); + return new AthenzTenant(requireName(name), domain, property, propertyId, Optional.empty(), createdAt, LastLoginInfo.EMPTY, Instant.EPOCH, List.of()); } @Override diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/CloudAccountInfo.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/CloudAccountInfo.java new file mode 100644 index 00000000000..430f5770165 --- /dev/null +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/CloudAccountInfo.java @@ -0,0 +1,19 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.tenant; + +import com.yahoo.component.Version; +import com.yahoo.config.provision.CloudAccount; + +import java.util.Objects; + +/** + * @author freva + */ +public record CloudAccountInfo(CloudAccount cloudAccount, Version templateVersion) { + + public CloudAccountInfo { + Objects.requireNonNull(cloudAccount, "cloudAccount must be non-null"); + Objects.requireNonNull(templateVersion, "templateVersion must be non-null"); + } + +} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/CloudTenant.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/CloudTenant.java index 4d7aee7b604..173d3e1950e 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/CloudTenant.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/CloudTenant.java @@ -34,8 +34,8 @@ public class CloudTenant extends Tenant { BiMap<PublicKey, SimplePrincipal> developerKeys, TenantInfo info, List<TenantSecretStore> tenantSecretStores, ArchiveAccess archiveAccess, Optional<Instant> invalidateUserSessionsBefore, Instant tenantRoleLastMaintained, - Optional<BillingReference> billingReference) { - super(name, createdAt, lastLoginInfo, Optional.empty(), tenantRoleLastMaintained); + List<CloudAccountInfo> cloudAccounts, Optional<BillingReference> billingReference) { + super(name, createdAt, lastLoginInfo, Optional.empty(), tenantRoleLastMaintained, cloudAccounts); this.creator = creator; this.developerKeys = developerKeys; this.info = Objects.requireNonNull(info); @@ -51,7 +51,8 @@ public class CloudTenant extends Tenant { createdAt, LastLoginInfo.EMPTY, Optional.ofNullable(creator).map(SimplePrincipal::of), - ImmutableBiMap.of(), TenantInfo.empty(), List.of(), new ArchiveAccess(), Optional.empty(), Instant.EPOCH, Optional.empty()); + ImmutableBiMap.of(), TenantInfo.empty(), List.of(), new ArchiveAccess(), Optional.empty(), + Instant.EPOCH, List.of(), Optional.empty()); } /** The user that created the tenant */ diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/DeletedTenant.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/DeletedTenant.java index b58fdf81278..30ce5d5a3b2 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/DeletedTenant.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/DeletedTenant.java @@ -4,6 +4,7 @@ package com.yahoo.vespa.hosted.controller.tenant; import com.yahoo.config.provision.TenantName; import java.time.Instant; +import java.util.List; import java.util.Objects; import java.util.Optional; @@ -17,7 +18,7 @@ public class DeletedTenant extends Tenant { private final Instant deletedAt; public DeletedTenant(TenantName name, Instant createdAt, Instant deletedAt) { - super(name, createdAt, LastLoginInfo.EMPTY, Optional.empty(), Instant.EPOCH); + super(name, createdAt, LastLoginInfo.EMPTY, Optional.empty(), Instant.EPOCH, List.of()); this.deletedAt = Objects.requireNonNull(deletedAt, "deletedAt must be non-null"); } diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/Tenant.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/Tenant.java index a4500991bf2..8b1c6b3ebde 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/Tenant.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/Tenant.java @@ -5,6 +5,7 @@ import com.yahoo.config.provision.TenantName; import com.yahoo.vespa.hosted.controller.api.integration.organization.Contact; import java.time.Instant; +import java.util.List; import java.util.Objects; import java.util.Optional; @@ -20,13 +21,15 @@ public abstract class Tenant { private final LastLoginInfo lastLoginInfo; private final Optional<Contact> contact; private final Instant tenantRolesLastMaintained; + private final List<CloudAccountInfo> cloudAccounts; - Tenant(TenantName name, Instant createdAt, LastLoginInfo lastLoginInfo, Optional<Contact> contact, Instant tenantRolesLastMaintained) { + Tenant(TenantName name, Instant createdAt, LastLoginInfo lastLoginInfo, Optional<Contact> contact, Instant tenantRolesLastMaintained, List<CloudAccountInfo> cloudAccounts) { this.name = name; this.createdAt = createdAt; this.lastLoginInfo = lastLoginInfo; this.contact = contact; this.tenantRolesLastMaintained = tenantRolesLastMaintained; + this.cloudAccounts = cloudAccounts; } /** Name of this tenant */ @@ -53,6 +56,10 @@ public abstract class Tenant { return tenantRolesLastMaintained; } + public List<CloudAccountInfo> cloudAccounts() { + return cloudAccounts; + } + public abstract Type type(); @Override |