aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJon Marius Venstad <jvenstad@yahoo-inc.com>2017-10-25 11:28:41 +0200
committerJon Marius Venstad <jvenstad@yahoo-inc.com>2017-10-25 11:28:41 +0200
commit613ffaeca6ee5de1e6284468a7ce043ce6c73fd4 (patch)
treeb141791c09e9558a6cef604f5066fae7d841f156
parent8c34ea2b1e8b7e255571f12fd5c85942edd4503a (diff)
Expose property relevant URIs and contact lists at tenant/{tenant}/property
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/MockOrganization.java13
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/api/Tenant.java21
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java31
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java21
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant-list.json2
5 files changed, 73 insertions, 15 deletions
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/MockOrganization.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/MockOrganization.java
index 51bba7bb52c..dc3589a69ae 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/MockOrganization.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/MockOrganization.java
@@ -1,5 +1,6 @@
package com.yahoo.vespa.hosted.controller.api.integration.organization;
+import com.google.inject.Inject;
import com.yahoo.vespa.hosted.controller.api.identifiers.PropertyId;
import java.net.URI;
@@ -20,6 +21,12 @@ public class MockOrganization implements Organization {
private final HashMap<IssueId, WrappedIssue> issues;
private final HashMap<PropertyId, PropertyInfo> properties;
+ @Inject
+ @SuppressWarnings("unused")
+ public MockOrganization() {
+ this(Clock.systemUTC());
+ }
+
public MockOrganization(Clock clock) {
this.clock = clock;
@@ -82,17 +89,17 @@ public class MockOrganization implements Organization {
@Override
public URI issueCreationUri(PropertyId propertyId) {
- return null;
+ return URI.create("www.issues.com/" + propertyId.id());
}
@Override
public URI contactsUri(PropertyId propertyId) {
- return null;
+ return URI.create("www.contacts.com/" + propertyId.id());
}
@Override
public URI propertyUri(PropertyId propertyId) {
- return null;
+ return URI.create("www.properties.com/" + propertyId.id());
}
public void close(IssueId issueId) {
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/api/Tenant.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/api/Tenant.java
index 4889f789819..9b8643c7167 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/api/Tenant.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/api/Tenant.java
@@ -29,24 +29,25 @@ public class Tenant {
public Tenant(TenantId id, Optional<UserGroup> userGroup, Optional<Property> property, Optional<AthenzDomain> athenzDomain, Optional<PropertyId> propertyId) {
if (id.isUser()) {
- require(!userGroup.isPresent(), "User tenant '%s' cannot have a user group.", id);
- require(!property.isPresent(), "User tenant '%s' cannot have a property.", id);
- require(!propertyId.isPresent(), "User tenant '%s' cannot have a property ID.", id);
- require(!athenzDomain.isPresent(), "User tenant '%s' cannot have an athens domain.", id);
+ require( ! userGroup.isPresent(), "User tenant '%s' cannot have a user group.", id);
+ require( ! property.isPresent(), "User tenant '%s' cannot have a property.", id);
+ require( ! propertyId.isPresent(), "User tenant '%s' cannot have a property ID.", id);
+ require( ! athenzDomain.isPresent(), "User tenant '%s' cannot have an athens domain.", id);
} else if (athenzDomain.isPresent()) {
- require(property.isPresent(), "Athens tenant '%s' must have a property.", id);
- require(!userGroup.isPresent(), "Athens tenant '%s' cannot have a user group.", id);
- require(athenzDomain.isPresent(), "Athens tenant '%s' must have an athens domain.", id);
+ require( property.isPresent(), "Athens tenant '%s' must have a property.", id);
+ require( ! userGroup.isPresent(), "Athens tenant '%s' cannot have a user group.", id);
+ require( athenzDomain.isPresent(), "Athens tenant '%s' must have an athens domain.", id);
} else {
- require(property.isPresent(), "OpsDB tenant '%s' must have a property.", id);
- require(userGroup.isPresent(), "OpsDb tenant '%s' must have a user group.", id);
- require(!athenzDomain.isPresent(), "OpsDb tenant '%s' cannot have an athens domain.", id);
+ require( property.isPresent(), "OpsDB tenant '%s' must have a property.", id);
+ require( userGroup.isPresent(), "OpsDb tenant '%s' must have a user group.", id);
+ require( ! athenzDomain.isPresent(), "OpsDb tenant '%s' cannot have an athens domain.", id);
}
this.id = id;
this.userGroup = userGroup;
this.property = property;
this.athenzDomain = athenzDomain;
this.propertyId = propertyId; // TODO: Check validity after TODO@14. OpsDb tenants have this set in Sherpa, while athens tenants do not.
+ // TODO: Require PropertyId for non-users, and fetch Property from EntityService (which will be moved to Organization) in the controller.
}
public boolean isAthensTenant() { return athenzDomain.isPresent(); }
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
index c50f1464be7..30444f8bc93 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
@@ -53,6 +53,7 @@ import com.yahoo.vespa.hosted.controller.api.identifiers.UserId;
import com.yahoo.vespa.hosted.controller.api.integration.MetricsService;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServerException;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.Log;
+import com.yahoo.vespa.hosted.controller.api.integration.organization.User;
import com.yahoo.vespa.hosted.controller.api.integration.routing.RotationStatus;
import com.yahoo.vespa.hosted.controller.application.ApplicationPackage;
import com.yahoo.vespa.hosted.controller.application.ApplicationRevision;
@@ -164,6 +165,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
if (path.matches("/application/v4/property")) return properties();
if (path.matches("/application/v4/cookiefreshness")) return cookieFreshness(request);
if (path.matches("/application/v4/tenant/{tenant}")) return tenant(path.get("tenant"), request);
+ if (path.matches("/application/v4/tenant/{tenant}/property")) return property(path.get("tenant"));
if (path.matches("/application/v4/tenant/{tenant}/application")) return applications(path.get("tenant"), request);
if (path.matches("/application/v4/tenant/{tenant}/application/{application}")) return application(path.get("tenant"), path.get("application"), path, request);
if (path.matches("/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{instance}")) return deployment(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region"), request);
@@ -305,6 +307,20 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
return ErrorResponse.notFoundError("Tenant '" + tenantName + "' does not exist");
return new SlimeJsonResponse(toSlime(tenant.get(), request, true));
}
+
+ private HttpResponse property(String tenantName) {
+ Optional<Tenant> tenant = controller.tenants().tenant(new TenantId(tenantName));
+ if ( ! tenant.isPresent())
+ return ErrorResponse.notFoundError("Tenant '" + tenantName + "' does not exist");
+ if ( ! tenant.get().getPropertyId().isPresent())
+ return ErrorResponse.notFoundError("Tenant '" + tenantName + "' does not have the required property id");
+
+ PropertyId propertyId = tenant.get().getPropertyId().get();
+ return new SlimeJsonResponse(toSlime(controller.organization().propertyUri(propertyId),
+ controller.organization().contactsUri(propertyId),
+ controller.organization().issueCreationUri(propertyId),
+ controller.organization().contactsFor(propertyId)));
+ }
private HttpResponse applications(String tenantName, HttpRequest request) {
TenantName tenant = TenantName.from(tenantName);
@@ -990,6 +1006,21 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
return slime;
}
+ private Slime toSlime(URI propertyUri, URI contactsUri, URI issueCreationUri, List<? extends List<? extends User>> contacts) {
+ Slime slime = new Slime();
+ Cursor root = slime.setObject();
+ root.setString("propertyUri", propertyUri.toString());
+ root.setString("contactsUri", contactsUri.toString());
+ root.setString("issueCreationUri", issueCreationUri.toString());
+ Cursor lists = root.setArray("contacts");
+ for (List<? extends User> contactList : contacts) {
+ Cursor list = lists.addArray();
+ for (User contact : contactList)
+ list.addString(contact.toString());
+ }
+ return slime;
+ }
+
private void toSlime(Application application, Cursor object, HttpRequest request) {
object.setString("application", application.id().application().value());
object.setString("instance", application.id().instance().value());
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java
index 1ac5dfeb58a..13c6306a88e 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java
@@ -9,8 +9,11 @@ import com.yahoo.vespa.curator.Lock;
import com.yahoo.vespa.hosted.controller.Application;
import com.yahoo.vespa.hosted.controller.ConfigServerClientMock;
import com.yahoo.vespa.hosted.controller.api.identifiers.AthenzDomain;
+import com.yahoo.vespa.hosted.controller.api.identifiers.PropertyId;
import com.yahoo.vespa.hosted.controller.api.identifiers.UserId;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServerException;
+import com.yahoo.vespa.hosted.controller.api.integration.organization.MockOrganization;
+import com.yahoo.vespa.hosted.controller.api.integration.organization.User;
import com.yahoo.vespa.hosted.controller.application.ApplicationPackage;
import com.yahoo.vespa.hosted.controller.application.ClusterInfo;
import com.yahoo.vespa.hosted.controller.application.ClusterUtilization;
@@ -37,6 +40,8 @@ import java.io.UncheckedIOException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -79,7 +84,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
new File("cookiefreshness.json"));
// POST (add) a tenant without property ID
tester.assertResponse(request("/application/v4/tenant/tenant1",
- "{\"athensDomain\":\"domain1\", \"property\":\"property1\"}",
+ "{\"athensDomain\":\"domain1\", \"property\":\"property1\"}",
Request.Method.POST),
new File("tenant-without-applications.json"));
// PUT (modify) a tenant
@@ -101,6 +106,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
// GET a tenant
tester.assertResponse(request("/application/v4/tenant/tenant1", "", Request.Method.GET),
new File("tenant-with-application.json"));
+
// GET tenant applications
tester.assertResponse(request("/application/v4/tenant/tenant1/application/", "", Request.Method.GET),
new File("application-list.json"));
@@ -250,6 +256,11 @@ public class ApplicationApiTest extends ControllerContainerTest {
tester.assertResponse(request("/application/v4/tenant/tenant2", "", Request.Method.GET),
new File("tenant-without-applications-with-id.json"));
+ // GET property info for a tenant with property ID
+ addPropertyData((MockOrganization) controllerTester.controller().organization(), "1234");
+ tester.assertResponse(request("/application/v4/tenant/tenant2/property", "", Request.Method.GET),
+ new File("property-info.json"));
+
// Test legacy OpsDB tenants
// POST (add) an OpsDB tenant with property ID
tester.assertResponse(request("/application/v4/tenant/tenant3",
@@ -286,6 +297,13 @@ public class ApplicationApiTest extends ControllerContainerTest {
controllerTester.controller().deconstruct();
}
+ private void addPropertyData(MockOrganization organization, String propertyIdValue) {
+ PropertyId propertyId = new PropertyId(propertyIdValue);
+ organization.addProperty(propertyId);
+ organization.setContactsFor(propertyId, Arrays.asList(Collections.singletonList(User.from("alice")),
+ Collections.singletonList(User.from("bob"))));
+ }
+
@Test
public void testDeployDirectly() throws Exception {
// Setup
@@ -755,4 +773,5 @@ public class ApplicationApiTest extends ControllerContainerTest {
}
}
}
+
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant-list.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant-list.json
index a9d9cd33ae8..d7ec9a738f2 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant-list.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant-list.json
@@ -8,4 +8,4 @@
},
"url": "http://localhost:8080/application/v4/tenant/tenant1"
}
-] \ No newline at end of file
+]