aboutsummaryrefslogtreecommitdiffstats
path: root/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java
diff options
context:
space:
mode:
Diffstat (limited to 'controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java')
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java271
1 files changed, 239 insertions, 32 deletions
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java
index 3b74fea2b9c..32f0247b3bc 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java
@@ -1,4 +1,4 @@
-// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.controller.restapi.application;
import ai.vespa.hosted.api.MultiPartStreamer;
@@ -6,14 +6,19 @@ 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.HostName;
import com.yahoo.config.provision.InstanceName;
import com.yahoo.config.provision.TenantName;
import com.yahoo.restapi.RestApiException;
+import com.yahoo.slime.Cursor;
+import com.yahoo.slime.SlimeUtils;
import com.yahoo.vespa.flags.InMemoryFlagSource;
import com.yahoo.vespa.flags.PermanentFlags;
import com.yahoo.vespa.hosted.controller.ControllerTester;
import com.yahoo.vespa.hosted.controller.LockedTenant;
import com.yahoo.vespa.hosted.controller.api.integration.billing.PlanId;
+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.ApplicationVersion;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
import com.yahoo.vespa.hosted.controller.api.integration.secrets.TenantSecretStore;
@@ -35,14 +40,15 @@ import org.junit.jupiter.api.Test;
import java.io.File;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
import static com.yahoo.application.container.handler.Request.Method.DELETE;
import static com.yahoo.application.container.handler.Request.Method.GET;
import static com.yahoo.application.container.handler.Request.Method.POST;
import static com.yahoo.application.container.handler.Request.Method.PUT;
-import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -72,7 +78,7 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest {
void tenant_info_profile() {
var request = request("/application/v4/tenant/scoober/info/profile", GET)
.roles(Set.of(Role.reader(tenantName)));
- tester.assertResponse(request, "{}", 200);
+ tester.assertResponse(request, "{\"contact\":{\"name\":\"\",\"email\":\"\",\"emailVerified\":false},\"tenant\":{\"company\":\"\",\"website\":\"\"}}", 200);
var updateRequest = request("/application/v4/tenant/scoober/info/profile", PUT)
.data("{\"contact\":{\"name\":\"Some Name\",\"email\":\"foo@example.com\"},\"tenant\":{\"company\":\"Scoober, Inc.\",\"website\":\"https://example.com/\"}}")
@@ -92,34 +98,103 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest {
@Test
void tenant_info_billing() {
+ var expectedResponse = """
+ {
+ "contact": {
+ "name":"",
+ "email":"",
+ "emailVerified":false,
+ "phone":""
+ },
+ "taxId":"",
+ "purchaseOrder":"",
+ "invoiceEmail":""
+ }
+ """;
var request = request("/application/v4/tenant/scoober/info/billing", GET)
.roles(Set.of(Role.reader(tenantName)));
- tester.assertResponse(request, "{}", 200);
-
- var fullAddress = "{\"addressLines\":\"addressLines\",\"postalCodeOrZip\":\"postalCodeOrZip\",\"city\":\"city\",\"stateRegionProvince\":\"stateRegionProvince\",\"country\":\"country\"}";
- var fullBillingContact = "{\"contact\":{\"name\":\"name\",\"email\":\"foo@example\",\"phone\":\"phone\"},\"address\":" + fullAddress + "}";
-
+ tester.assertJsonResponse(request, expectedResponse, 200);
+
+ var fullBillingContact = """
+ {
+ "contact": {
+ "name":"name",
+ "email":"foo@example",
+ "phone":"phone"
+ },
+ "taxId":"1234L",
+ "purchaseOrder":"PO9001",
+ "invoiceEmail":"billing@mycomp.any",
+ "address": {
+ "addressLines":"addressLines",
+ "postalCodeOrZip":"postalCodeOrZip",
+ "city":"city",
+ "stateRegionProvince":"stateRegionProvince",
+ "country":"country"
+ }
+ }
+ """;
var updateRequest = request("/application/v4/tenant/scoober/info/billing", PUT)
.data(fullBillingContact)
.roles(Set.of(Role.administrator(tenantName)));
tester.assertResponse(updateRequest, "{\"message\":\"Tenant info updated\"}", 200);
- tester.assertResponse(request, "{\"contact\":{\"name\":\"name\",\"email\":\"foo@example\",\"phone\":\"phone\"},\"address\":{\"addressLines\":\"addressLines\",\"postalCodeOrZip\":\"postalCodeOrZip\",\"city\":\"city\",\"stateRegionProvince\":\"stateRegionProvince\",\"country\":\"country\"}}", 200);
+ expectedResponse = """
+ {
+ "contact": {
+ "name":"name",
+ "email":"foo@example",
+ "emailVerified": false,
+ "phone":"phone"
+ },
+ "taxId":"1234L",
+ "purchaseOrder":"PO9001",
+ "invoiceEmail":"billing@mycomp.any",
+ "address": {
+ "addressLines":"addressLines",
+ "postalCodeOrZip":"postalCodeOrZip",
+ "city":"city",
+ "stateRegionProvince":"stateRegionProvince",
+ "country":"country"
+ }
+ }
+ """;
+ tester.assertJsonResponse(request, expectedResponse, 200);
}
@Test
void tenant_info_contacts() {
var request = request("/application/v4/tenant/scoober/info/contacts", GET)
.roles(Set.of(Role.reader(tenantName)));
- tester.assertResponse(request, "{\"contacts\":[]}", 200);
-
-
- var fullContacts = "{\"contacts\":[{\"audiences\":[\"tenant\"],\"email\":\"contact1@example.com\",\"emailVerified\":false},{\"audiences\":[\"notifications\"],\"email\":\"contact2@example.com\",\"emailVerified\":false},{\"audiences\":[\"tenant\",\"notifications\"],\"email\":\"contact3@example.com\",\"emailVerified\":false}]}";
+ tester.assertResponse(request, "{\"contacts\":[{\"audiences\":[\"tenant\",\"notifications\"],\"email\":\"developer@scoober\",\"emailVerified\":true}]}", 200);
+
+
+ var fullContacts = """
+ {
+ "contacts":[
+ {
+ "audiences":["tenant"]
+ ,"email":"contact1@example.com",
+ "emailVerified":false
+ },
+ {
+ "audiences":["notifications"],
+ "email":"contact2@example.com",
+ "emailVerified":false
+ },
+ {
+ "audiences":["tenant","notifications"],
+ "email":"contact3@example.com",
+ "emailVerified":false
+ }
+ ]
+ }
+ """;
var updateRequest = request("/application/v4/tenant/scoober/info/contacts", PUT)
.data(fullContacts)
.roles(Set.of(Role.administrator(tenantName)));
tester.assertResponse(updateRequest, "{\"message\":\"Tenant info updated\"}", 200);
- tester.assertResponse(request, fullContacts, 200);
+ tester.assertJsonResponse(request, fullContacts, 200);
}
@Test
@@ -127,7 +202,7 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest {
var infoRequest =
request("/application/v4/tenant/scoober/info", GET)
.roles(Set.of(Role.reader(tenantName)));
- tester.assertResponse(infoRequest, "{}", 200);
+ tester.assertResponse(infoRequest, "{\"name\":\"\",\"email\":\"\",\"website\":\"\",\"contactName\":\"\",\"contactEmail\":\"\",\"contactEmailVerified\":false,\"contacts\":[{\"audiences\":[\"tenant\",\"notifications\"],\"email\":\"developer@scoober\",\"emailVerified\":true}]}", 200);
String partialInfo = "{\"contactName\":\"newName\", \"contactEmail\": \"foo@example.com\", \"billingContact\":{\"name\":\"billingName\"}}";
var postPartial =
@@ -144,13 +219,79 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest {
tester.assertResponse(postPartialContacts, "{\"message\":\"Tenant info updated\"}", 200);
// Read back the updated info
- tester.assertResponse(infoRequest, "{\"name\":\"\",\"email\":\"\",\"website\":\"\",\"contactName\":\"newName\",\"contactEmail\":\"foo@example.com\",\"contactEmailVerified\":false,\"billingContact\":{\"name\":\"billingName\",\"email\":\"\",\"phone\":\"\"},\"contacts\":[{\"audiences\":[\"tenant\"],\"email\":\"contact1@example.com\",\"emailVerified\":false}]}", 200);
-
- String fullAddress = "{\"addressLines\":\"addressLines\",\"postalCodeOrZip\":\"postalCodeOrZip\",\"city\":\"city\",\"stateRegionProvince\":\"stateRegionProvince\",\"country\":\"country\"}";
- String fullBillingContact = "{\"name\":\"name\",\"email\":\"foo@example\",\"phone\":\"phone\",\"address\":" + fullAddress + "}";
- String fullContacts = "[{\"audiences\":[\"tenant\"],\"email\":\"contact1@example.com\",\"emailVerified\":false},{\"audiences\":[\"notifications\"],\"email\":\"contact2@example.com\",\"emailVerified\":false},{\"audiences\":[\"tenant\",\"notifications\"],\"email\":\"contact3@example.com\",\"emailVerified\":false}]";
- String fullInfo = "{\"name\":\"name\",\"email\":\"foo@example\",\"website\":\"https://yahoo.com\",\"contactName\":\"contactName\",\"contactEmail\":\"contact@example.com\",\"contactEmailVerified\":false,\"address\":" + fullAddress + ",\"billingContact\":" + fullBillingContact + ",\"contacts\":" + fullContacts + "}";
-
+ var expectedResponse = """
+ {
+ "name":"",
+ "email":"",
+ "website":"",
+ "contactName":"newName",
+ "contactEmail":"foo@example.com",
+ "contactEmailVerified":false,
+ "billingContact": {
+ "name":"billingName",
+ "email":"","emailVerified":false,
+ "phone":"",
+ "taxId":"",
+ "purchaseOrder":"",
+ "invoiceEmail":""
+ },
+ "contacts": [
+ {"audiences":["tenant"],"email":"contact1@example.com","emailVerified":false}
+ ]
+ }
+ """;
+ tester.assertJsonResponse(infoRequest, expectedResponse, 200);
+
+ var fullInfo = """
+ {
+ "name":"name",
+ "email":"foo@example",
+ "website":"https://yahoo.com",
+ "contactName":"contactName",
+ "contactEmail":"contact@example.com",
+ "contactEmailVerified":false,
+ "address": {
+ "addressLines":"addressLines",
+ "postalCodeOrZip":"postalCodeOrZip",
+ "city":"city",
+ "stateRegionProvince":"stateRegionProvince",
+ "country":"country"
+ },
+ "billingContact": {
+ "name":"name",
+ "email":"foo@example",
+ "emailVerified":false,
+ "phone":"phone",
+ "taxId":"",
+ "purchaseOrder":"",
+ "invoiceEmail":"",
+ "address": {
+ "addressLines":"addressLines",
+ "postalCodeOrZip":"postalCodeOrZip",
+ "city":"city",
+ "stateRegionProvince":"stateRegionProvince",
+ "country":"country"
+ }
+ },
+ "contacts": [
+ {
+ "audiences":["tenant"],
+ "email":"contact1@example.com",
+ "emailVerified":false
+ },
+ {
+ "audiences":["notifications"],
+ "email":"contact2@example.com",
+ "emailVerified":false
+ },
+ {
+ "audiences":["tenant","notifications"]
+ ,"email":"contact3@example.com",
+ "emailVerified":false
+ }
+ ]
+ }
+ """;
// Now set all fields
var postFull =
request("/application/v4/tenant/scoober/info", PUT)
@@ -159,7 +300,7 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest {
tester.assertResponse(postFull, "{\"message\":\"Tenant info updated\"}", 200);
// Now compare the updated info with the full info we sent
- tester.assertResponse(infoRequest, fullInfo, 200);
+ tester.assertJsonResponse(infoRequest, fullInfo, 200);
var invalidBody = "{\"mail\":\"contact1@example.com\", \"mailType\":\"blurb\"}";
var resendMailRequest =
@@ -182,7 +323,7 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest {
var infoRequest =
request("/application/v4/tenant/scoober/info", GET)
.roles(Set.of(Role.reader(tenantName)));
- tester.assertResponse(infoRequest, "{}", 200);
+ tester.assertResponse(infoRequest, "{\"name\":\"\",\"email\":\"\",\"website\":\"\",\"contactName\":\"\",\"contactEmail\":\"\",\"contactEmailVerified\":false,\"contacts\":[{\"audiences\":[\"tenant\",\"notifications\"],\"email\":\"developer@scoober\",\"emailVerified\":true}]}", 200);
// name needs to be present and not blank
var partialInfoMissingName = "{\"contactName\": \" \"}";
@@ -470,17 +611,83 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest {
.roles(Role.developer(tenantName)),
"{\"tokens\":[]}", 200);
- String regexGenerateToken = "\\{\"id\":\"myTokenId\",\"token\":\"vespa_cloud_.*\",\"fingerprint\":\".*\"}";
+ AtomicReference<String> tokenValue = new AtomicReference<>();
+ AtomicReference<String> fingerprint = new AtomicReference<>();
tester.assertResponse(request("/application/v4/tenant/scoober/token/myTokenId", POST).roles(Role.developer(tenantName)),
- (response) -> assertTrue(new String(response.getBody(), UTF_8).matches(regexGenerateToken)),
- 200);
-
- String regexListTokens = "\\{\"tokens\":\\[\\{\"id\":\"myTokenId\",\"versions\":\\[\\{\"fingerprint\":\".*\",\"created\":\".*\",\"author\":\"user@test\",\"expiration\":\".*\"}]}]}";
- tester.assertResponse(request("/application/v4/tenant/scoober/token", GET)
- .roles(Role.developer(tenantName)),
- (response) -> assertTrue(new String(response.getBody(), UTF_8).matches(regexListTokens)),
+ (response) -> {
+ Cursor root = SlimeUtils.jsonToSlimeOrThrow(response.getBody()).get();
+ tokenValue.set(root.field("token").asString());
+ fingerprint.set(root.field("fingerprint").asString());
+ assertEquals("""
+ {
+ "id": "myTokenId",
+ "token": "%s",
+ "fingerprint": "%s",
+ "expiration": "2020-10-13T12:26:40Z"
+ }
+ """.formatted(tokenValue.get(), fingerprint.get()),
+ SlimeUtils.toJson(root, false));
+ },
200);
+ tester.assertJsonResponse(request("/application/v4/tenant/scoober/token", GET)
+ .roles(Role.developer(tenantName)),
+ """
+ {
+ "tokens": [
+ {
+ "id": "myTokenId",
+ "lastUpdatedMillis": 1600000000000,
+ "versions": [
+ {
+ "fingerprint": "%s",
+ "created": "2020-09-13T12:26:40Z",
+ "author": "user@test",
+ "expiration": "2020-10-13T12:26:40Z",
+ "state": "unused"
+ }
+ ]
+ }
+ ]
+ }
+ """.formatted(fingerprint.get()),
+ 200);
+
+ ControllerTester wrapped = new ControllerTester(tester);
+ wrapped.upgradeSystem(Version.fromString("7.1"));
+ new DeploymentTester(wrapped).newDeploymentContext(ApplicationId.from(tenantName, applicationName, InstanceName.defaultName()))
+ .submit()
+ .deploy();
+ wrapped.serviceRegistry().configServer().activeTokenFingerprints(null)
+ .put(HostName.of("host1"), Map.of(TokenId.of("myTokenId"), List.of(FingerPrint.of(fingerprint.get()), FingerPrint.of("ff:01"))));
+
+ tester.assertJsonResponse(request("/application/v4/tenant/scoober/token", GET)
+ .roles(Role.developer(tenantName)),
+ """
+ {
+ "tokens": [
+ {
+ "id": "myTokenId",
+ "lastUpdatedMillis": 1600000000000,
+ "versions": [
+ {
+ "fingerprint": "%s",
+ "created": "2020-09-13T12:26:40Z",
+ "author": "user@test",
+ "expiration": "2020-10-13T12:26:40Z",
+ "state": "active"
+ },
+ {
+ "fingerprint": "ff:01",
+ "state": "revoking"
+ }
+ ]
+ }
+ ]
+ }
+ """.formatted(fingerprint.get()),
+ 200);
+
// Rejects invalid tokenIds on create
tester.assertResponse(request("/application/v4/tenant/scoober/token/foo+bar", POST).roles(Role.developer(tenantName)),
"{\"error-code\":\"BAD_REQUEST\",\"message\":\"tokenId must match '[A-Za-z][A-Za-z0-9_-]{0,59}', but got: 'foo bar'\"}",