diff options
author | Torbjørn Smørgrav <smorgrav@users.noreply.github.com> | 2020-11-02 11:54:42 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-11-02 11:54:42 +0100 |
commit | 65d0a37a01a4b1f2420b8f4cae3ea9091b01b189 (patch) | |
tree | 5fd7698abf8240801cd1a549d0efc0c55c152713 /controller-server | |
parent | ac8b4ebae4796b275ff71cc15eb259a22797a913 (diff) | |
parent | 426fe805f7520932367eb24e557ad0af5c5a1795 (diff) |
Merge pull request #15095 from vespa-engine/smorgrav/tenantinfo
Smorgrav/tenantinfo
Diffstat (limited to 'controller-server')
9 files changed, 456 insertions, 17 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 f2e6cd0e15f..9e54d887952 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 @@ -12,9 +12,9 @@ import com.yahoo.vespa.hosted.controller.api.identifiers.Property; import com.yahoo.vespa.hosted.controller.api.identifiers.PropertyId; import com.yahoo.vespa.hosted.controller.api.integration.organization.Contact; import com.yahoo.vespa.hosted.controller.tenant.AthenzTenant; -import com.yahoo.vespa.hosted.controller.api.integration.organization.BillingInfo; import com.yahoo.vespa.hosted.controller.tenant.CloudTenant; import com.yahoo.vespa.hosted.controller.tenant.Tenant; +import com.yahoo.vespa.hosted.controller.tenant.TenantInfo; import java.security.Principal; import java.security.PublicKey; @@ -102,20 +102,22 @@ public abstract class LockedTenant { private final Optional<Principal> creator; private final BiMap<PublicKey, Principal> developerKeys; + private final TenantInfo info; - private Cloud(TenantName name, Optional<Principal> creator, BiMap<PublicKey, Principal> developerKeys) { + private Cloud(TenantName name, Optional<Principal> creator, BiMap<PublicKey, Principal> developerKeys, TenantInfo info) { super(name); this.developerKeys = ImmutableBiMap.copyOf(developerKeys); this.creator = creator; + this.info = info; } private Cloud(CloudTenant tenant) { - this(tenant.name(), Optional.empty(), tenant.developerKeys()); + this(tenant.name(), Optional.empty(), tenant.developerKeys(), tenant.info()); } @Override public CloudTenant get() { - return new CloudTenant(name, creator, developerKeys); + return new CloudTenant(name, creator, developerKeys, info); } public Cloud withDeveloperKey(PublicKey key, Principal principal) { @@ -123,13 +125,13 @@ public abstract class LockedTenant { if (keys.containsKey(key)) throw new IllegalArgumentException("Key " + KeyUtils.toPem(key) + " is already owned by " + keys.get(key)); keys.put(key, principal); - return new Cloud(name, creator, keys); + return new Cloud(name, creator, keys, info); } public Cloud withoutDeveloperKey(PublicKey key) { BiMap<PublicKey, Principal> keys = HashBiMap.create(developerKeys); keys.remove(key); - return new Cloud(name, creator, keys); + return new Cloud(name, creator, keys, info); } } 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 f64d79a2b80..bc6886eae58 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 @@ -133,7 +133,7 @@ public class TenantController { } private void requireNonExistent(TenantName name) { - if ( "hosted-vespa".equals(name.value()) + if ("hosted-vespa".equals(name.value()) || get(name).isPresent() // Underscores are allowed in existing tenant names, but tenants with - and _ cannot co-exist. E.g. // my-tenant cannot be created if my_tenant exists. 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 2ed71b91850..8b7590e88a9 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 @@ -14,11 +14,14 @@ import com.yahoo.slime.SlimeUtils; import com.yahoo.vespa.hosted.controller.api.identifiers.Property; import com.yahoo.vespa.hosted.controller.api.identifiers.PropertyId; import com.yahoo.vespa.hosted.controller.api.role.SimplePrincipal; -import com.yahoo.vespa.hosted.controller.tenant.AthenzTenant; import com.yahoo.vespa.hosted.controller.api.integration.organization.Contact; import com.yahoo.vespa.hosted.controller.api.integration.organization.BillingInfo; +import com.yahoo.vespa.hosted.controller.tenant.AthenzTenant; import com.yahoo.vespa.hosted.controller.tenant.CloudTenant; import com.yahoo.vespa.hosted.controller.tenant.Tenant; +import com.yahoo.vespa.hosted.controller.tenant.TenantInfo; +import com.yahoo.vespa.hosted.controller.tenant.TenantInfoAddress; +import com.yahoo.vespa.hosted.controller.tenant.TenantInfoBillingContact; import java.net.URI; import java.security.Principal; @@ -60,6 +63,7 @@ public class TenantSerializer { private static final String customerIdField = "customerId"; private static final String productCodeField = "productCode"; private static final String pemDeveloperKeysField = "pemDeveloperKeys"; + private static final String tenantInfoField = "info"; public Slime toSlime(Tenant tenant) { Slime slime = new Slime(); @@ -93,6 +97,7 @@ public class TenantSerializer { tenant.creator().ifPresent(creator -> root.setString(creatorField, creator.getName())); developerKeysToSlime(tenant.developerKeys(), root.setArray(pemDeveloperKeysField)); toSlime(legacyBillingInfo, root.setObject(billingInfoField)); + toSlime(tenant.info(), root); } private void developerKeysToSlime(BiMap<PublicKey, Principal> keys, Cursor array) { @@ -133,7 +138,8 @@ public class TenantSerializer { TenantName name = TenantName.from(tenantObject.field(nameField).asString()); Optional<Principal> creator = SlimeUtils.optionalString(tenantObject.field(creatorField)).map(SimplePrincipal::new); BiMap<PublicKey, Principal> developerKeys = developerKeysFromSlime(tenantObject.field(pemDeveloperKeysField)); - return new CloudTenant(name, creator, developerKeys); + TenantInfo info = tenantInfoFromSlime(tenantObject.field(tenantInfoField)); + return new CloudTenant(name, creator, developerKeys, info); } private BiMap<PublicKey, Principal> developerKeysFromSlime(Inspector array) { @@ -145,6 +151,71 @@ public class TenantSerializer { return keys.build(); } + TenantInfo tenantInfoFromSlime(Inspector infoObject) { + if (!infoObject.valid()) return TenantInfo.EMPTY; + + return TenantInfo.EMPTY + .withName(infoObject.field("name").asString()) + .withEmail(infoObject.field("email").asString()) + .withWebsite(infoObject.field("website").asString()) + .withContactName(infoObject.field("contactName").asString()) + .withContactEmail(infoObject.field("contactEmail").asString()) + .withInvoiceEmail(infoObject.field("invoiceEmail").asString()) + .withAddress(tenantInfoAddressFromSlime(infoObject.field("address"))) + .withBillingContact(tenantInfoBillingContactFromSlime(infoObject.field("billingContact"))); + } + + private TenantInfoAddress tenantInfoAddressFromSlime(Inspector addressObject) { + return TenantInfoAddress.EMPTY + .withAddressLines(addressObject.field("addressLines").asString()) + .withPostalCodeOrZip(addressObject.field("postalCodeOrZip").asString()) + .withCity(addressObject.field("city").asString()) + .withStateRegionProvince(addressObject.field("stateRegionProvince").asString()) + .withCountry(addressObject.field("country").asString()); + } + + private TenantInfoBillingContact tenantInfoBillingContactFromSlime(Inspector billingObject) { + return TenantInfoBillingContact.EMPTY + .withName(billingObject.field("name").asString()) + .withEmail(billingObject.field("email").asString()) + .withPhone(billingObject.field("phone").asString()) + .withAddress(tenantInfoAddressFromSlime(billingObject.field("address"))); + } + + void toSlime(TenantInfo info, Cursor parentCursor) { + if (info.isEmpty()) return; + Cursor infoCursor = parentCursor.setObject("info"); + infoCursor.setString("name", info.name()); + infoCursor.setString("email", info.email()); + infoCursor.setString("website", info.website()); + infoCursor.setString("invoiceEmail", info.invoiceEmail()); + infoCursor.setString("contactName", info.contactName()); + infoCursor.setString("contactEmail", info.contactEmail()); + toSlime(info.address(), infoCursor); + toSlime(info.billingContact(), infoCursor); + } + + private void toSlime(TenantInfoAddress address, Cursor parentCursor) { + if (address.isEmpty()) return; + + Cursor addressCursor = parentCursor.setObject("address"); + addressCursor.setString("addressLines", address.addressLines()); + addressCursor.setString("postalCodeOrZip", address.postalCodeOrZip()); + addressCursor.setString("city", address.city()); + addressCursor.setString("stateRegionProvince", address.stateRegionProvince()); + addressCursor.setString("country", address.country()); + } + + private void toSlime(TenantInfoBillingContact billingContact, Cursor parentCursor) { + if (billingContact.isEmpty()) return; + + Cursor addressCursor = parentCursor.setObject("billingContact"); + addressCursor.setString("name", billingContact.name()); + addressCursor.setString("email", billingContact.email()); + addressCursor.setString("phone", billingContact.phone()); + toSlime(billingContact.address(), addressCursor); + } + private Optional<Contact> contactFrom(Inspector object) { if ( ! object.valid()) return Optional.empty(); @@ -208,5 +279,4 @@ public class TenantSerializer { default: throw new IllegalArgumentException("Unexpected tenant type '" + type + "'."); } } - } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/CloudTenant.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/CloudTenant.java index a74c39dc362..67b285bb24f 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/CloudTenant.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/CloudTenant.java @@ -7,6 +7,7 @@ import com.yahoo.config.provision.TenantName; import java.security.Principal; import java.security.PublicKey; +import java.util.Objects; import java.util.Optional; /** @@ -18,19 +19,21 @@ public class CloudTenant extends Tenant { private final Optional<Principal> creator; private final BiMap<PublicKey, Principal> developerKeys; + private final TenantInfo info; /** Public for the serialization layer — do not use! */ - public CloudTenant(TenantName name, Optional<Principal> creator, BiMap<PublicKey, Principal> developerKeys) { + public CloudTenant(TenantName name, Optional<Principal> creator, BiMap<PublicKey, Principal> developerKeys, TenantInfo info) { super(name, Optional.empty()); this.creator = creator; this.developerKeys = developerKeys; + this.info = Objects.requireNonNull(info); } /** Creates a tenant with the given name, provided it passes validation. */ public static CloudTenant create(TenantName tenantName, Principal creator) { return new CloudTenant(requireName(tenantName), Optional.ofNullable(creator), - ImmutableBiMap.of()); + ImmutableBiMap.of(), TenantInfo.EMPTY); } /** The user that created the tenant */ @@ -38,6 +41,11 @@ public class CloudTenant extends Tenant { return creator; } + /** Legal name, addresses etc */ + public TenantInfo info() { + return info; + } + /** Returns the set of developer keys and their corresponding developers for this tenant. */ public BiMap<PublicKey, Principal> developerKeys() { return developerKeys; } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TenantInfo.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TenantInfo.java new file mode 100644 index 00000000000..81c08e1083b --- /dev/null +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TenantInfo.java @@ -0,0 +1,127 @@ +// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.tenant; + +import java.util.Objects; + +/** + * Tenant information beyond technical tenant id and user authorizations. + * + * This info is used to capture generic support information and invoiced billing information. + * + * All fields are non null but strings can be empty + * + * @author smorgrav + */ +public class TenantInfo { + private final String name; + private final String email; + private final String website; + private final String contactName; + private final String contactEmail; + private final String invoiceEmail; + private final TenantInfoAddress address; + private final TenantInfoBillingContact billingContact; + + TenantInfo(String name, String email, String website, String contactName, String contactEmail, + String invoiceEmail, TenantInfoAddress address, TenantInfoBillingContact billingContact) { + this.name = Objects.requireNonNull(name); + this.email = Objects.requireNonNull(email); + this.website = Objects.requireNonNull(website); + this.contactName = Objects.requireNonNull(contactName); + this.contactEmail = Objects.requireNonNull(contactEmail); + this.invoiceEmail = Objects.requireNonNull(invoiceEmail); + this.address = Objects.requireNonNull(address); + this.billingContact = Objects.requireNonNull(billingContact); + } + + public static final TenantInfo EMPTY = new TenantInfo("","","", "", "", "", + TenantInfoAddress.EMPTY, TenantInfoBillingContact.EMPTY); + + public String name() { + return name; + } + + public String email() { + return email; + } + + public String website() { + return website; + } + + public String contactName() { + return contactName; + } + + public String contactEmail() { + return contactEmail; + } + + public String invoiceEmail() { + return invoiceEmail; + } + + public TenantInfoAddress address() { + return address; + } + + public TenantInfoBillingContact billingContact() { + return billingContact; + } + + public TenantInfo withName(String newName) { + return new TenantInfo(newName, email, website, contactName, contactEmail, invoiceEmail, address, billingContact); + } + + public TenantInfo withEmail(String newEmail) { + return new TenantInfo(name, newEmail, website, contactName, contactEmail, invoiceEmail, address, billingContact); + } + + public TenantInfo withWebsite(String newWebsite) { + return new TenantInfo(name, email, newWebsite, contactName, contactEmail, invoiceEmail, address, billingContact); + } + + public TenantInfo withContactName(String newContactName) { + return new TenantInfo(name, email, website, newContactName, contactEmail, invoiceEmail, address, billingContact); + } + + public TenantInfo withContactEmail(String newContactEmail) { + return new TenantInfo(name, email, website, contactName, newContactEmail, invoiceEmail, address, billingContact); + } + + public TenantInfo withInvoiceEmail(String newInvoiceEmail) { + return new TenantInfo(name, email, website, contactName, contactEmail, newInvoiceEmail, address, billingContact); + } + + public TenantInfo withAddress(TenantInfoAddress newAddress) { + return new TenantInfo(name, email, website, contactName, contactEmail, invoiceEmail, newAddress, billingContact); + } + + public TenantInfo withBillingContact(TenantInfoBillingContact newBillingContact) { + return new TenantInfo(name, email, website, contactName, contactEmail, invoiceEmail, address, newBillingContact); + } + + public boolean isEmpty() { + return this.equals(EMPTY); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TenantInfo that = (TenantInfo) o; + return name.equals(that.name) && + email.equals(that.email) && + website.equals(that.website) && + contactName.equals(that.contactName) && + contactEmail.equals(that.contactEmail) && + invoiceEmail.equals(that.invoiceEmail) && + address.equals(that.address) && + billingContact.equals(that.billingContact); + } + + @Override + public int hashCode() { + return Objects.hash(name, email, website, contactName, contactEmail, invoiceEmail, address, billingContact); + } +} diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TenantInfoAddress.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TenantInfoAddress.java new file mode 100644 index 00000000000..a12f351abd6 --- /dev/null +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TenantInfoAddress.java @@ -0,0 +1,95 @@ +// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.tenant; + +import java.util.Objects; + +/** + * Address formats are quite diverse across the world both in therms of what fields are used, named and + * the order of them. + * + * To be generic a little future proof the address fields here are a mix of free text (address lines) and fixed fields. + * The address lines can be street address, P.O box, c/o name, apartment, suite, unit, building floor etc etc. + * + * All fields are mandatory but can be an empty string (ie. not null) + * + * @author smorgrav + */ +public class TenantInfoAddress { + + private final String addressLines; + private final String postalCodeOrZip; + private final String city; + private final String stateRegionProvince; + private final String country; + + TenantInfoAddress(String addressLines, String postalCodeOrZip, String city, String country, String stateRegionProvince) { + this.addressLines = Objects.requireNonNull(addressLines);; + this.city = Objects.requireNonNull(city); + this.postalCodeOrZip = Objects.requireNonNull(postalCodeOrZip); + this.country = Objects.requireNonNull(country); + this.stateRegionProvince = Objects.requireNonNull(stateRegionProvince); + } + + public static final TenantInfoAddress EMPTY = new TenantInfoAddress("","","", "", ""); + + public String addressLines() { + return addressLines; + } + + public String postalCodeOrZip() { + return postalCodeOrZip; + } + + public String city() { + return city; + } + + public String country() { + return country; + } + + public String stateRegionProvince() { + return stateRegionProvince; + } + + public TenantInfoAddress withAddressLines(String newAddressLines) { + return new TenantInfoAddress(newAddressLines, postalCodeOrZip, city, country, stateRegionProvince); + } + + public TenantInfoAddress withPostalCodeOrZip(String newPostalCodeOrZip) { + return new TenantInfoAddress(addressLines, newPostalCodeOrZip, city, country, stateRegionProvince); + } + + public TenantInfoAddress withCity(String newCity) { + return new TenantInfoAddress(addressLines, postalCodeOrZip, newCity, country, stateRegionProvince); + } + + public TenantInfoAddress withCountry(String newCountry) { + return new TenantInfoAddress(addressLines, postalCodeOrZip, city, newCountry, stateRegionProvince); + } + + public TenantInfoAddress withStateRegionProvince(String newStateRegionProvince) { + return new TenantInfoAddress(addressLines, postalCodeOrZip, city, country, newStateRegionProvince); + } + + public boolean isEmpty() { + return this.equals(EMPTY); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TenantInfoAddress that = (TenantInfoAddress) o; + return addressLines.equals(that.addressLines) && + postalCodeOrZip.equals(that.postalCodeOrZip) && + city.equals(that.city) && + stateRegionProvince.equals(that.stateRegionProvince) && + country.equals(that.country); + } + + @Override + public int hashCode() { + return Objects.hash(addressLines, postalCodeOrZip, city, stateRegionProvince, country); + } +} diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TenantInfoBillingContact.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TenantInfoBillingContact.java new file mode 100644 index 00000000000..a00dd626f0a --- /dev/null +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TenantInfoBillingContact.java @@ -0,0 +1,74 @@ +// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.tenant; + +import java.util.Objects; + +/** + * @author smorgrav + */ +public class TenantInfoBillingContact { + private final String name; + private final String email; + private final String phone; + private final TenantInfoAddress address; + + TenantInfoBillingContact(String name, String email, String phone, TenantInfoAddress address) { + this.name = Objects.requireNonNull(name); + this.email = Objects.requireNonNull(email); + this.phone = Objects.requireNonNull(phone); + this.address = Objects.requireNonNull(address); + } + + public static final TenantInfoBillingContact EMPTY = + new TenantInfoBillingContact("","", "", TenantInfoAddress.EMPTY); + + public String name() { + return name; + } + + public String email() { return email; } + + public String phone() { + return phone; + } + + public TenantInfoAddress address() { + return address; + } + + public TenantInfoBillingContact withName(String newName) { + return new TenantInfoBillingContact(newName, email, phone, address); + } + + public TenantInfoBillingContact withEmail(String newEmail) { + return new TenantInfoBillingContact(name, newEmail, phone, address); + } + + public TenantInfoBillingContact withPhone(String newPhone) { + return new TenantInfoBillingContact(name, email, newPhone, address); + } + + public TenantInfoBillingContact withAddress(TenantInfoAddress newAddress) { + return new TenantInfoBillingContact(name, email, phone, newAddress); + } + + public boolean isEmpty() { + return this.equals(EMPTY); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TenantInfoBillingContact that = (TenantInfoBillingContact) o; + return name.equals(that.name) && + email.equals(that.email) && + phone.equals(that.phone) && + address.equals(that.address); + } + + @Override + public int hashCode() { + return Objects.hash(name, email, phone, address); + } +} diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializerTest.java index 9f36275e196..20caceee097 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializerTest.java @@ -4,14 +4,18 @@ package com.yahoo.vespa.hosted.controller.persistence;// Copyright 2018 Yahoo Ho import com.google.common.collect.ImmutableBiMap; import com.yahoo.config.provision.TenantName; import com.yahoo.security.KeyUtils; +import com.yahoo.slime.Cursor; +import com.yahoo.slime.Slime; import com.yahoo.vespa.athenz.api.AthenzDomain; import com.yahoo.vespa.hosted.controller.api.identifiers.Property; import com.yahoo.vespa.hosted.controller.api.identifiers.PropertyId; -import com.yahoo.vespa.hosted.controller.api.integration.organization.BillingInfo; import com.yahoo.vespa.hosted.controller.api.integration.organization.Contact; import com.yahoo.vespa.hosted.controller.api.role.SimplePrincipal; import com.yahoo.vespa.hosted.controller.tenant.AthenzTenant; import com.yahoo.vespa.hosted.controller.tenant.CloudTenant; +import com.yahoo.vespa.hosted.controller.tenant.TenantInfo; +import com.yahoo.vespa.hosted.controller.tenant.TenantInfoAddress; +import com.yahoo.vespa.hosted.controller.tenant.TenantInfoBillingContact; import org.junit.Test; import java.net.URI; @@ -80,13 +84,70 @@ public class TenantSerializerTest { CloudTenant tenant = new CloudTenant(TenantName.from("elderly-lady"), Optional.of(new SimplePrincipal("foobar-user")), ImmutableBiMap.of(publicKey, new SimplePrincipal("joe"), - otherPublicKey, new SimplePrincipal("jane"))); + otherPublicKey, new SimplePrincipal("jane")), + TenantInfo.EMPTY); CloudTenant serialized = (CloudTenant) serializer.tenantFrom(serializer.toSlime(tenant)); assertEquals(tenant.name(), serialized.name()); assertEquals(tenant.creator(), serialized.creator()); assertEquals(tenant.developerKeys(), serialized.developerKeys()); } + @Test + public void cloud_tenant_with_info() { + CloudTenant tenant = new CloudTenant(TenantName.from("elderly-lady"), + Optional.of(new SimplePrincipal("foobar-user")), + ImmutableBiMap.of(publicKey, new SimplePrincipal("joe"), + otherPublicKey, new SimplePrincipal("jane")), + TenantInfo.EMPTY.withName("Ofni Tnanet")); + CloudTenant serialized = (CloudTenant) serializer.tenantFrom(serializer.toSlime(tenant)); + assertEquals(tenant.info(), serialized.info()); + } + + + @Test + public void cloud_tenant_with_tenant_info_partial() { + TenantInfo partialInfo = TenantInfo.EMPTY + .withAddress(TenantInfoAddress.EMPTY.withCity("Hønefoss")); + + Slime slime = new Slime(); + Cursor parentObject = slime.setObject(); + serializer.toSlime(partialInfo, parentObject); + assertEquals("{\"info\":{\"name\":\"\",\"email\":\"\",\"website\":\"\",\"invoiceEmail\":\"\",\"contactName\":\"\",\"contactEmail\":\"\",\"address\":{\"addressLines\":\"\",\"postalCodeOrZip\":\"\",\"city\":\"Hønefoss\",\"stateRegionProvince\":\"\",\"country\":\"\"}}}", slime.toString()); + } + + @Test + public void cloud_tenant_with_tenant_info_full() { + TenantInfo fullInfo = TenantInfo.EMPTY + .withName("My Company") + .withEmail("email@mycomp.any") + .withWebsite("http://mycomp.any") + .withContactEmail("ceo@mycomp.any") + .withContactName("My Name") + .withInvoiceEmail("invoice@mycomp.any") + .withAddress(TenantInfoAddress.EMPTY + .withCity("Hønefoss") + .withAddressLines("Riperbakken 2") + .withCountry("Norway") + .withPostalCodeOrZip("3510") + .withStateRegionProvince("Viken")) + .withBillingContact(TenantInfoBillingContact.EMPTY + .withEmail("thomas@sodor.com") + .withName("Thomas The Tank Engine") + .withPhone("NA") + .withAddress(TenantInfoAddress.EMPTY + .withCity("Suddery") + .withCountry("Sodor") + .withAddressLines("Central Station") + .withStateRegionProvince("Irish Sea"))); + + Slime slime = new Slime(); + Cursor parentCursor = slime.setObject(); + serializer.toSlime(fullInfo, parentCursor); + TenantInfo roundTripInfo = serializer.tenantInfoFromSlime(parentCursor.field("info")); + + assertEquals(fullInfo, roundTripInfo); + } + private Contact contact() { return new Contact( URI.create("http://contact1.test"), diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/SignatureFilterTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/SignatureFilterTest.java index 28db2cf2224..26c2e2d1175 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/SignatureFilterTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/SignatureFilterTest.java @@ -11,13 +11,13 @@ import com.yahoo.security.KeyUtils; import com.yahoo.vespa.hosted.controller.Application; import com.yahoo.vespa.hosted.controller.ApplicationController; import com.yahoo.vespa.hosted.controller.ControllerTester; -import com.yahoo.vespa.hosted.controller.api.integration.organization.BillingInfo; import com.yahoo.vespa.hosted.controller.api.role.Role; import com.yahoo.vespa.hosted.controller.api.role.SecurityContext; import com.yahoo.vespa.hosted.controller.api.role.SimplePrincipal; import com.yahoo.vespa.hosted.controller.application.TenantAndApplicationId; import com.yahoo.vespa.hosted.controller.restapi.ApplicationRequestToDiscFilterRequestWrapper; import com.yahoo.vespa.hosted.controller.tenant.CloudTenant; +import com.yahoo.vespa.hosted.controller.tenant.TenantInfo; import org.junit.Before; import org.junit.Test; @@ -68,7 +68,8 @@ public class SignatureFilterTest { tester.curator().writeTenant(new CloudTenant(appId.tenant(), Optional.empty(), - ImmutableBiMap.of())); + ImmutableBiMap.of(), + TenantInfo.EMPTY)); tester.curator().writeApplication(new Application(appId, tester.clock().instant())); } @@ -107,7 +108,8 @@ public class SignatureFilterTest { // Signed request gets a developer role when a matching developer key is stored for the tenant. tester.curator().writeTenant(new CloudTenant(appId.tenant(), Optional.empty(), - ImmutableBiMap.of(publicKey, () -> "user"))); + ImmutableBiMap.of(publicKey, () -> "user"), + TenantInfo.EMPTY)); verifySecurityContext(requestOf(signer.signed(request.copy(), Method.POST, () -> new ByteArrayInputStream(hiBytes)), hiBytes), new SecurityContext(new SimplePrincipal("user"), Set.of(Role.reader(id.tenant()), |