summaryrefslogtreecommitdiffstats
path: root/controller-server
diff options
context:
space:
mode:
authorTorbjørn Smørgrav <smorgrav@users.noreply.github.com>2020-11-02 11:54:42 +0100
committerGitHub <noreply@github.com>2020-11-02 11:54:42 +0100
commit65d0a37a01a4b1f2420b8f4cae3ea9091b01b189 (patch)
tree5fd7698abf8240801cd1a549d0efc0c55c152713 /controller-server
parentac8b4ebae4796b275ff71cc15eb259a22797a913 (diff)
parent426fe805f7520932367eb24e557ad0af5c5a1795 (diff)
Merge pull request #15095 from vespa-engine/smorgrav/tenantinfo
Smorgrav/tenantinfo
Diffstat (limited to 'controller-server')
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedTenant.java14
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/TenantController.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializer.java76
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/CloudTenant.java12
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TenantInfo.java127
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TenantInfoAddress.java95
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TenantInfoBillingContact.java74
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializerTest.java65
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/SignatureFilterTest.java8
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()),