summaryrefslogtreecommitdiffstats
path: root/controller-server/src/main/java/com/yahoo/vespa/hosted/controller
diff options
context:
space:
mode:
Diffstat (limited to 'controller-server/src/main/java/com/yahoo/vespa/hosted/controller')
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedTenant.java65
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/TenantController.java7
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/TenantRoleMaintainer.java13
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializer.java8
4 files changed, 67 insertions, 26 deletions
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedTenant.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedTenant.java
index da40f63d543..f8135e27d74 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedTenant.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedTenant.java
@@ -41,11 +41,13 @@ public abstract class LockedTenant {
final TenantName name;
final Instant createdAt;
final LastLoginInfo lastLoginInfo;
+ final Instant tenantRolesLastMaintained;
- private LockedTenant(TenantName name, Instant createdAt, LastLoginInfo lastLoginInfo) {
+ private LockedTenant(TenantName name, Instant createdAt, LastLoginInfo lastLoginInfo, Instant tenantRolesLastMaintained) {
this.name = requireNonNull(name);
this.createdAt = requireNonNull(createdAt);
this.lastLoginInfo = requireNonNull(lastLoginInfo);
+ this.tenantRolesLastMaintained = requireNonNull(tenantRolesLastMaintained);
}
static LockedTenant of(Tenant tenant, Mutex lock) {
@@ -62,6 +64,8 @@ public abstract class LockedTenant {
public abstract LockedTenant with(LastLoginInfo lastLoginInfo);
+ public abstract LockedTenant with(Instant tenantRolesLastMaintained);
+
public Deleted deleted(Instant deletedAt) {
return new Deleted(new DeletedTenant(name, createdAt, deletedAt));
}
@@ -81,8 +85,8 @@ public abstract class LockedTenant {
private final Optional<Contact> contact;
private Athenz(TenantName name, AthenzDomain domain, Property property, Optional<PropertyId> propertyId,
- Optional<Contact> contact, Instant createdAt, LastLoginInfo lastLoginInfo) {
- super(name, createdAt, lastLoginInfo);
+ Optional<Contact> contact, Instant createdAt, LastLoginInfo lastLoginInfo, Instant tenantRolesLastMaintained) {
+ super(name, createdAt, lastLoginInfo, tenantRolesLastMaintained);
this.domain = domain;
this.property = property;
this.propertyId = propertyId;
@@ -90,33 +94,38 @@ public abstract class LockedTenant {
}
private Athenz(AthenzTenant tenant) {
- this(tenant.name(), tenant.domain(), tenant.property(), tenant.propertyId(), tenant.contact(), tenant.createdAt(), tenant.lastLoginInfo());
+ this(tenant.name(), tenant.domain(), tenant.property(), tenant.propertyId(), tenant.contact(), tenant.createdAt(), tenant.lastLoginInfo(), tenant.tenantRolesLastMaintained());
}
@Override
public AthenzTenant get() {
- return new AthenzTenant(name, domain, property, propertyId, contact, createdAt, lastLoginInfo);
+ return new AthenzTenant(name, domain, property, propertyId, contact, createdAt, lastLoginInfo, tenantRolesLastMaintained);
}
public Athenz with(AthenzDomain domain) {
- return new Athenz(name, domain, property, propertyId, contact, createdAt, lastLoginInfo);
+ return new Athenz(name, domain, property, propertyId, contact, createdAt, lastLoginInfo, tenantRolesLastMaintained);
}
public Athenz with(Property property) {
- return new Athenz(name, domain, property, propertyId, contact, createdAt, lastLoginInfo);
+ return new Athenz(name, domain, property, propertyId, contact, createdAt, lastLoginInfo, tenantRolesLastMaintained);
}
public Athenz with(PropertyId propertyId) {
- return new Athenz(name, domain, property, Optional.of(propertyId), contact, createdAt, lastLoginInfo);
+ return new Athenz(name, domain, property, Optional.of(propertyId), contact, createdAt, lastLoginInfo, tenantRolesLastMaintained);
}
public Athenz with(Contact contact) {
- return new Athenz(name, domain, property, propertyId, Optional.of(contact), createdAt, lastLoginInfo);
+ return new Athenz(name, domain, property, propertyId, Optional.of(contact), createdAt, lastLoginInfo, tenantRolesLastMaintained);
}
@Override
public LockedTenant with(LastLoginInfo lastLoginInfo) {
- return new Athenz(name, domain, property, propertyId, contact, createdAt, lastLoginInfo);
+ return new Athenz(name, domain, property, propertyId, contact, createdAt, lastLoginInfo, tenantRolesLastMaintained);
+ }
+
+ @Override
+ public LockedTenant with(Instant tenantRolesLastMaintained) {
+ return new Athenz(name, domain, property, propertyId, contact, createdAt, lastLoginInfo, tenantRolesLastMaintained);
}
}
@@ -134,8 +143,8 @@ public abstract class LockedTenant {
private Cloud(TenantName name, Instant createdAt, LastLoginInfo lastLoginInfo, Optional<SimplePrincipal> creator,
BiMap<PublicKey, SimplePrincipal> developerKeys, TenantInfo info,
- List<TenantSecretStore> tenantSecretStores, ArchiveAccess archiveAccess, Optional<Instant> invalidateUserSessionsBefore) {
- super(name, createdAt, lastLoginInfo);
+ List<TenantSecretStore> tenantSecretStores, ArchiveAccess archiveAccess, Optional<Instant> invalidateUserSessionsBefore, Instant tenantRolesLastMaintained) {
+ super(name, createdAt, lastLoginInfo, tenantRolesLastMaintained);
this.developerKeys = ImmutableBiMap.copyOf(developerKeys);
this.creator = creator;
this.info = info;
@@ -145,12 +154,12 @@ public abstract class LockedTenant {
}
private Cloud(CloudTenant tenant) {
- this(tenant.name(), tenant.createdAt(), tenant.lastLoginInfo(), tenant.creator(), tenant.developerKeys(), tenant.info(), tenant.tenantSecretStores(), tenant.archiveAccess(), tenant.invalidateUserSessionsBefore());
+ this(tenant.name(), tenant.createdAt(), tenant.lastLoginInfo(), tenant.creator(), tenant.developerKeys(), tenant.info(), tenant.tenantSecretStores(), tenant.archiveAccess(), tenant.invalidateUserSessionsBefore(), tenant.tenantRolesLastMaintained());
}
@Override
public CloudTenant get() {
- return new CloudTenant(name, createdAt, lastLoginInfo, creator, developerKeys, info, tenantSecretStores, archiveAccess, invalidateUserSessionsBefore);
+ return new CloudTenant(name, createdAt, lastLoginInfo, creator, developerKeys, info, tenantSecretStores, archiveAccess, invalidateUserSessionsBefore, tenantRolesLastMaintained);
}
public Cloud withDeveloperKey(PublicKey key, Principal principal) {
@@ -161,42 +170,47 @@ public abstract class LockedTenant {
if (keys.inverse().containsKey(simplePrincipal))
throw new IllegalArgumentException(principal + " is already associated with key " + KeyUtils.toPem(keys.inverse().get(simplePrincipal)));
keys.put(key, simplePrincipal);
- return new Cloud(name, createdAt, lastLoginInfo, creator, keys, info, tenantSecretStores, archiveAccess, invalidateUserSessionsBefore);
+ return new Cloud(name, createdAt, lastLoginInfo, creator, keys, info, tenantSecretStores, archiveAccess, invalidateUserSessionsBefore, tenantRolesLastMaintained);
}
public Cloud withoutDeveloperKey(PublicKey key) {
BiMap<PublicKey, SimplePrincipal> keys = HashBiMap.create(developerKeys);
keys.remove(key);
- return new Cloud(name, createdAt, lastLoginInfo, creator, keys, info, tenantSecretStores, archiveAccess, invalidateUserSessionsBefore);
+ return new Cloud(name, createdAt, lastLoginInfo, creator, keys, info, tenantSecretStores, archiveAccess, invalidateUserSessionsBefore, tenantRolesLastMaintained);
}
public Cloud withInfo(TenantInfo newInfo) {
- return new Cloud(name, createdAt, lastLoginInfo, creator, developerKeys, newInfo, tenantSecretStores, archiveAccess, invalidateUserSessionsBefore);
+ return new Cloud(name, createdAt, lastLoginInfo, creator, developerKeys, newInfo, tenantSecretStores, archiveAccess, invalidateUserSessionsBefore, tenantRolesLastMaintained);
}
@Override
public LockedTenant with(LastLoginInfo lastLoginInfo) {
- return new Cloud(name, createdAt, lastLoginInfo, creator, developerKeys, info, tenantSecretStores, archiveAccess, invalidateUserSessionsBefore);
+ return new Cloud(name, createdAt, lastLoginInfo, creator, developerKeys, info, tenantSecretStores, archiveAccess, invalidateUserSessionsBefore, tenantRolesLastMaintained);
}
public Cloud withSecretStore(TenantSecretStore tenantSecretStore) {
ArrayList<TenantSecretStore> secretStores = new ArrayList<>(tenantSecretStores);
secretStores.add(tenantSecretStore);
- return new Cloud(name, createdAt, lastLoginInfo, creator, developerKeys, info, secretStores, archiveAccess, invalidateUserSessionsBefore);
+ return new Cloud(name, createdAt, lastLoginInfo, creator, developerKeys, info, secretStores, archiveAccess, invalidateUserSessionsBefore, tenantRolesLastMaintained);
}
public Cloud withoutSecretStore(TenantSecretStore tenantSecretStore) {
ArrayList<TenantSecretStore> secretStores = new ArrayList<>(tenantSecretStores);
secretStores.remove(tenantSecretStore);
- return new Cloud(name, createdAt, lastLoginInfo, creator, developerKeys, info, secretStores, archiveAccess, invalidateUserSessionsBefore);
+ return new Cloud(name, createdAt, lastLoginInfo, creator, developerKeys, info, secretStores, archiveAccess, invalidateUserSessionsBefore, tenantRolesLastMaintained);
}
public Cloud withArchiveAccess(ArchiveAccess archiveAccess) {
- return new Cloud(name, createdAt, lastLoginInfo, creator, developerKeys, info, tenantSecretStores, archiveAccess, invalidateUserSessionsBefore);
+ return new Cloud(name, createdAt, lastLoginInfo, creator, developerKeys, info, tenantSecretStores, archiveAccess, invalidateUserSessionsBefore,tenantRolesLastMaintained);
}
public Cloud withInvalidateUserSessionsBefore(Instant invalidateUserSessionsBefore) {
- return new Cloud(name, createdAt, lastLoginInfo, creator, developerKeys, info, tenantSecretStores, archiveAccess, Optional.of(invalidateUserSessionsBefore));
+ return new Cloud(name, createdAt, lastLoginInfo, creator, developerKeys, info, tenantSecretStores, archiveAccess, Optional.of(invalidateUserSessionsBefore), tenantRolesLastMaintained);
+ }
+
+ @Override
+ public LockedTenant with(Instant tenantRolesLastMaintained) {
+ return new Cloud(name, createdAt, lastLoginInfo, creator, developerKeys, info, tenantSecretStores, archiveAccess, invalidateUserSessionsBefore, tenantRolesLastMaintained);
}
}
@@ -207,7 +221,7 @@ public abstract class LockedTenant {
private final Instant deletedAt;
private Deleted(DeletedTenant tenant) {
- super(tenant.name(), tenant.createdAt(), tenant.lastLoginInfo());
+ super(tenant.name(), tenant.createdAt(), tenant.lastLoginInfo(), Instant.EPOCH);
this.deletedAt = tenant.deletedAt();
}
@@ -220,6 +234,11 @@ public abstract class LockedTenant {
public LockedTenant with(LastLoginInfo lastLoginInfo) {
return this;
}
+
+ @Override
+ public LockedTenant with(Instant tenantRolesLastMaintained) {
+ return this;
+ }
}
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/TenantController.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/TenantController.java
index d2adf3d9b73..b6a645d96d2 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/TenantController.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/TenantController.java
@@ -158,6 +158,13 @@ public class TenantController {
}
}
+ public void updateLastTenantRolesMaintained(TenantName tenantName, Instant lastMaintained) {
+ try (Mutex lock = lock(tenantName)) {
+ var tenant = require(tenantName);
+ curator.writeTenant(LockedTenant.of(tenant, lock).with(lastMaintained).get());
+ }
+ }
+
/** Deletes the given tenant. */
public void delete(TenantName tenant, Optional<Credentials> credentials, boolean forget) {
try (Mutex lock = lock(tenant)) {
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/TenantRoleMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/TenantRoleMaintainer.java
index 19ea3232667..bd121871c7c 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/TenantRoleMaintainer.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/TenantRoleMaintainer.java
@@ -6,6 +6,8 @@ import com.yahoo.vespa.hosted.controller.Controller;
import com.yahoo.vespa.hosted.controller.tenant.Tenant;
import java.time.Duration;
+import java.time.Instant;
+import java.util.Comparator;
public class TenantRoleMaintainer extends ControllerMaintainer {
@@ -16,7 +18,10 @@ public class TenantRoleMaintainer extends ControllerMaintainer {
@Override
protected double maintain() {
var roleService = controller().serviceRegistry().roleService();
- var tenants = controller().tenants().asList();
+ var tenants = controller().tenants().asList().stream()
+ .sorted(Comparator.comparing(Tenant::tenantRolesLastMaintained))
+ .limit(5)
+ .toList();
// Create separate athenz service for all tenants
tenants.forEach(roleService::createTenantRole);
@@ -26,6 +31,12 @@ public class TenantRoleMaintainer extends ControllerMaintainer {
.toList();
roleService.maintainRoles(tenantsWithRoles);
+ // Update last maintained timestamp
+ var updated = Instant.now(controller().clock());
+ tenants.forEach(t -> {
+ controller().tenants().updateLastTenantRolesMaintained(t.name(), updated);
+ });
+
return 1.0;
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializer.java
index b6d0155b6ab..d4536da91fe 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializer.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializer.java
@@ -83,6 +83,7 @@ public class TenantSerializer {
private static final String awsArchiveAccessRoleField = "awsArchiveAccessRole";
private static final String gcpArchiveAccessMemberField = "gcpArchiveAccessMember";
private static final String invalidateUserSessionsBeforeField = "invalidateUserSessionsBefore";
+ private static final String tenantRolesLastMaintainedField = "tenantRolesLastMaintained";
private static final String awsIdField = "awsId";
private static final String roleField = "role";
@@ -94,6 +95,7 @@ public class TenantSerializer {
tenantObject.setString(typeField, valueOf(tenant.type()));
tenantObject.setLong(createdAtField, tenant.createdAt().toEpochMilli());
toSlime(tenant.lastLoginInfo(), tenantObject.setObject(lastLoginInfoField));
+ tenantObject.setLong(tenantRolesLastMaintainedField, tenant.tenantRolesLastMaintained().toEpochMilli());
switch (tenant.type()) {
case athenz: toSlime((AthenzTenant) tenant, tenantObject); break;
@@ -178,7 +180,8 @@ public class TenantSerializer {
Optional<Contact> contact = contactFrom(tenantObject.field(contactField));
Instant createdAt = SlimeUtils.instant(tenantObject.field(createdAtField));
LastLoginInfo lastLoginInfo = lastLoginInfoFromSlime(tenantObject.field(lastLoginInfoField));
- return new AthenzTenant(name, domain, property, propertyId, contact, createdAt, lastLoginInfo);
+ Instant tenantRolesLastMaintained = SlimeUtils.instant(tenantObject.field(tenantRolesLastMaintainedField));
+ return new AthenzTenant(name, domain, property, propertyId, contact, createdAt, lastLoginInfo, tenantRolesLastMaintained);
}
private CloudTenant cloudTenantFrom(Inspector tenantObject) {
@@ -191,7 +194,8 @@ public class TenantSerializer {
List<TenantSecretStore> tenantSecretStores = secretStoresFromSlime(tenantObject.field(secretStoresField));
ArchiveAccess archiveAccess = archiveAccessFromSlime(tenantObject);
Optional<Instant> invalidateUserSessionsBefore = SlimeUtils.optionalInstant(tenantObject.field(invalidateUserSessionsBeforeField));
- return new CloudTenant(name, createdAt, lastLoginInfo, creator, developerKeys, info, tenantSecretStores, archiveAccess, invalidateUserSessionsBefore);
+ Instant tenantRolesLastMaintained = SlimeUtils.instant(tenantObject.field(tenantRolesLastMaintainedField));
+ return new CloudTenant(name, createdAt, lastLoginInfo, creator, developerKeys, info, tenantSecretStores, archiveAccess, invalidateUserSessionsBefore, tenantRolesLastMaintained);
}
private DeletedTenant deletedTenantFrom(Inspector tenantObject) {