diff options
author | olaaun <ola.aunroe@gmail.com> | 2018-10-16 12:32:51 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-10-16 12:32:51 +0200 |
commit | 13c06dda80a8229991ddab2b8579baefb5f08f65 (patch) | |
tree | 377f8f82f6ecff7e8bad55ffa3645bec90cd85a5 /controller-server | |
parent | 2cc64e617ff11f763d9fa165b37d19df0ced4114 (diff) |
Revert 7315 revert 7274 olaaun/contact info maintainer feed to api (#7320)
* Revert "Revert "ContactInformationMaintainer now feeds to ContactInfoAPI instead of d… (#7274)" (#7315)"
This reverts commit 0a9e36c185cf555dd851b065c16ef720665dd906.
* Changed authorities config from string to list. Shortened namespace.
Diffstat (limited to 'controller-server')
7 files changed, 145 insertions, 43 deletions
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ContactInformationMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ContactInformationMaintainer.java index bf5743e2d3c..3cf3c47a6f3 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ContactInformationMaintainer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ContactInformationMaintainer.java @@ -1,17 +1,31 @@ // Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.hosted.controller.maintenance; +import com.yahoo.vespa.hosted.controller.authority.config.ApiAuthorityConfig; import com.yahoo.config.provision.SystemName; import com.yahoo.log.LogLevel; +import com.yahoo.slime.ArrayTraverser; +import com.yahoo.slime.Cursor; +import com.yahoo.slime.Inspector; +import com.yahoo.slime.Slime; +import com.yahoo.vespa.config.SlimeUtils; import com.yahoo.vespa.hosted.controller.Controller; +import com.yahoo.vespa.hosted.controller.api.identifiers.PropertyId; import com.yahoo.vespa.hosted.controller.api.integration.organization.Organization; import com.yahoo.vespa.hosted.controller.api.integration.organization.User; -import com.yahoo.vespa.hosted.controller.tenant.AthenzTenant; import com.yahoo.vespa.hosted.controller.tenant.Contact; -import com.yahoo.vespa.hosted.controller.tenant.Tenant; import com.yahoo.yolean.Exceptions; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ByteArrayEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.util.EntityUtils; +import java.io.IOException; import java.time.Duration; +import java.util.ArrayList; import java.util.EnumSet; import java.util.List; import java.util.Objects; @@ -29,44 +43,107 @@ public class ContactInformationMaintainer extends Maintainer { private static final Logger log = Logger.getLogger(ContactInformationMaintainer.class.getName()); private final Organization organization; + private final List<String> baseUris; + private CloseableHttpClient httpClient = HttpClientBuilder.create().build(); - public ContactInformationMaintainer(Controller controller, Duration interval, JobControl jobControl, Organization organization) { + public ContactInformationMaintainer(Controller controller, Duration interval, JobControl jobControl, Organization organization, ApiAuthorityConfig apiAuthorityConfig) { super(controller, interval, jobControl, null, EnumSet.of(SystemName.cd, SystemName.main)); this.organization = Objects.requireNonNull(organization, "organization must be non-null"); + this.baseUris = apiAuthorityConfig.authorities(); } + // The maintainer will eventually feed contact info to systems other than its own, determined by the baseUris list. @Override protected void maintain() { - for (Tenant t : controller().tenants().asList()) { - if (!(t instanceof AthenzTenant)) continue; // No contact information for non-Athenz tenants - AthenzTenant tenant = (AthenzTenant) t; - if (!tenant.propertyId().isPresent()) continue; // Can only update contact information if property ID is known - try { - findContact(tenant).ifPresent(contact -> { - controller().tenants().lockIfPresent(t.name(), lockedTenant -> controller().tenants().store(lockedTenant.with(contact))); + for (String baseUri : baseUris) { + for (String tenantName : getTenantList(baseUri)) { + Optional<PropertyId> tenantPropertyId = getPropertyId(tenantName, baseUri); + if (!tenantPropertyId.isPresent()) + continue; + findContact(tenantPropertyId.get()).ifPresent(contact -> { + feedContact(tenantName, contact, baseUri); }); - } catch (Exception e) { - log.log(LogLevel.WARNING, "Failed to update contact information for " + tenant + ": " + - Exceptions.toMessageString(e) + ". Retrying in " + - maintenanceInterval()); } } } - /** Find contact information for given tenant */ - private Optional<Contact> findContact(AthenzTenant tenant) { - if (!tenant.propertyId().isPresent()) { - return Optional.empty(); + private void feedContact(String tenantName, Contact contact, String baseUri) { + try { + CloseableHttpClient httpClient = HttpClientBuilder.create().build(); + String uri = baseUri + "contactinfo/v1/tenant/" + tenantName; + HttpPost httpPost = new HttpPost(uri); + httpPost.setEntity(contactToByteArrayEntity(contact)); + httpClient.execute(httpPost); + } catch (Exception e) { + log.log(LogLevel.WARNING, "Failed to update contact information for " + tenantName + ": " + + Exceptions.toMessageString(e) + ". Retrying in " + + maintenanceInterval()); + } + } + + private ByteArrayEntity contactToByteArrayEntity(Contact contact) throws IOException { + Slime slime = new Slime(); + Cursor cursor = slime.setObject(); + cursor.setString("url", contact.url().toString()); + cursor.setString("issueTrackerUrl", contact.issueTrackerUrl().toString()); + cursor.setString("propertyUrl", contact.propertyUrl().toString()); + Cursor personsCursor = cursor.setArray("persons"); + for (List<String> personList : contact.persons()) { + Cursor sublist = personsCursor.addArray(); + for(String person : personList) { + sublist.addString(person); + } + } + return new ByteArrayEntity(SlimeUtils.toJsonBytes(slime)); + } + + private List<String> getTenantList(String baseUri) { + List<String> tenantList = new ArrayList<>(); + HttpGet getRequest = new HttpGet(baseUri + "application/v4/tenant/"); + try { + HttpResponse response = httpClient.execute(getRequest); + Slime slime = SlimeUtils.jsonToSlime(EntityUtils.toByteArray(response.getEntity())); + Inspector inspector = slime.get(); + inspector.traverse((ArrayTraverser) (index, tenant) -> { + tenantList.add(tenant.field("tenant").asString()); + }); + } catch (IOException e) { + log.log(LogLevel.WARNING, "Failed to get tenant list from base URI: " + baseUri.toString() + + Exceptions.toMessageString(e) + ". Retrying in " + + maintenanceInterval()); } - List<List<String>> persons = organization.contactsFor(tenant.propertyId().get()) + return tenantList; + } + + private Optional<PropertyId> getPropertyId(String tenantName, String baseUri) { + Optional<PropertyId> propertyId = Optional.empty(); + HttpGet getRequest = new HttpGet(baseUri + "application/v4/tenant/" + tenantName); + try { + HttpResponse response = httpClient.execute(getRequest); + Slime slime = SlimeUtils.jsonToSlime(EntityUtils.toByteArray(response.getEntity())); + Inspector inspector = slime.get(); + if (!inspector.field("propertyId").valid()) { + log.log(LogLevel.WARNING, "Unable to get property id for " + tenantName); + return Optional.empty(); + } + propertyId = Optional.of(new PropertyId(inspector.field("propertyId").asString())); + } catch (IOException e) { + log.log(LogLevel.WARNING, "Unable to get property idfor " + tenantName, e); + } + return propertyId; + } + + /** Find contact information for given tenant */ + private Optional<Contact> findContact(PropertyId propertyId) { + List<List<String>> persons = organization.contactsFor(propertyId) .stream() .map(personList -> personList.stream() .map(User::displayName) .collect(Collectors.toList())) .collect(Collectors.toList()); - return Optional.of(new Contact(organization.contactsUri(tenant.propertyId().get()), - organization.propertyUri(tenant.propertyId().get()), - organization.issueCreationUri(tenant.propertyId().get()), + return Optional.of(new Contact(organization.contactsUri(propertyId), + organization.propertyUri(propertyId), + organization.issueCreationUri(propertyId), persons)); } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java index c67eab8826e..33555c08a43 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java @@ -4,6 +4,7 @@ package com.yahoo.vespa.hosted.controller.maintenance; import com.yahoo.component.AbstractComponent; import com.yahoo.jdisc.Metric; import com.yahoo.vespa.hosted.controller.Controller; +import com.yahoo.vespa.hosted.controller.authority.config.ApiAuthorityConfig; import com.yahoo.vespa.hosted.controller.api.integration.chef.Chef; import com.yahoo.vespa.hosted.controller.api.integration.dns.NameService; import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeRepositoryClientInterface; @@ -49,7 +50,7 @@ public class ControllerMaintenance extends AbstractComponent { private final ContactInformationMaintainer contactInformationMaintainer; @SuppressWarnings("unused") // instantiated by Dependency Injection - public ControllerMaintenance(MaintainerConfig maintainerConfig, Controller controller, CuratorDb curator, + public ControllerMaintenance(MaintainerConfig maintainerConfig, ApiAuthorityConfig apiAuthorityConfig, Controller controller, CuratorDb curator, JobControl jobControl, Metric metric, Chef chefClient, DeploymentIssues deploymentIssues, OwnershipIssues ownershipIssues, NameService nameService, NodeRepositoryClientInterface nodeRepositoryClient, @@ -72,7 +73,7 @@ public class ControllerMaintenance extends AbstractComponent { jobRunner = new JobRunner(controller, Duration.ofSeconds(30), jobControl); osUpgraders = osUpgraders(controller, jobControl); osVersionStatusUpdater = new OsVersionStatusUpdater(controller, maintenanceInterval, jobControl); - contactInformationMaintainer = new ContactInformationMaintainer(controller, Duration.ofHours(12), jobControl, organization); + contactInformationMaintainer = new ContactInformationMaintainer(controller, Duration.ofHours(12), jobControl, organization, apiAuthorityConfig); } public Upgrader upgrader() { return upgrader; } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/contactinfo/ContactInfoHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/contactinfo/ContactInfoHandler.java index 5c4573e1ce7..7651381b810 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/contactinfo/ContactInfoHandler.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/contactinfo/ContactInfoHandler.java @@ -13,9 +13,6 @@ import com.yahoo.slime.Inspector; import com.yahoo.slime.Slime; import com.yahoo.vespa.config.SlimeUtils; import com.yahoo.vespa.hosted.controller.Controller; -import com.yahoo.vespa.hosted.controller.api.identifiers.PropertyId; -import com.yahoo.vespa.hosted.controller.api.integration.organization.Organization; -import com.yahoo.vespa.hosted.controller.api.integration.organization.User; import com.yahoo.vespa.hosted.controller.restapi.ErrorResponse; import com.yahoo.vespa.hosted.controller.restapi.SlimeJsonResponse; import com.yahoo.vespa.hosted.controller.restapi.StringResponse; @@ -24,16 +21,11 @@ import com.yahoo.vespa.hosted.controller.tenant.Contact; import com.yahoo.yolean.Exceptions; import java.io.IOException; -import java.io.ObjectInputStream; import java.net.URI; -import java.net.URISyntaxException; import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; import java.util.List; import java.util.Optional; import java.util.logging.Level; -import java.util.stream.Collectors; /** * This implements the contactinfo/v1 API which allows getting and feeding diff --git a/controller-server/src/main/resources/configdefinitions/api-authority.def b/controller-server/src/main/resources/configdefinitions/api-authority.def new file mode 100644 index 00000000000..9ab652ecc81 --- /dev/null +++ b/controller-server/src/main/resources/configdefinitions/api-authority.def @@ -0,0 +1,4 @@ +# Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +namespace=vespa.hosted.controller.authority.config + +authorities[] string
\ No newline at end of file diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ContactInformationMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ContactInformationMaintainerTest.java index cbaa37b15e3..a46215eaf22 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ContactInformationMaintainerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ContactInformationMaintainerTest.java @@ -1,13 +1,17 @@ // Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.hosted.controller.maintenance; +import com.github.tomakehurst.wiremock.junit.WireMockRule; +import com.github.tomakehurst.wiremock.verification.LoggedRequest; import com.yahoo.config.provision.TenantName; import com.yahoo.vespa.hosted.controller.ControllerTester; +import com.yahoo.vespa.hosted.controller.authority.config.ApiAuthorityConfig; import com.yahoo.vespa.hosted.controller.api.identifiers.PropertyId; import com.yahoo.vespa.hosted.controller.api.integration.organization.User; import com.yahoo.vespa.hosted.controller.tenant.AthenzTenant; import com.yahoo.vespa.hosted.controller.tenant.Contact; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import java.net.URI; @@ -18,9 +22,16 @@ import java.util.List; import java.util.function.Supplier; import java.util.stream.Collectors; +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.findAll; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.okJson; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; +import static com.github.tomakehurst.wiremock.client.WireMock.verify; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; /** * @author mpolden @@ -29,13 +40,27 @@ public class ContactInformationMaintainerTest { private ControllerTester tester; private ContactInformationMaintainer maintainer; + private String contactInfoPath = "/contactinfo/v1/tenant/tenant1"; + private String tenantPath = "/application/v4/tenant/"; + + @Rule + public WireMockRule wireMockRule = new WireMockRule(4443); + @Before public void before() { tester = new ControllerTester(); + ApiAuthorityConfig.Builder apiAuthorityConfigBuilder = new ApiAuthorityConfig.Builder().authorities("http://localhost:4443/"); + ApiAuthorityConfig apiAuthorityConfig = new ApiAuthorityConfig(apiAuthorityConfigBuilder); maintainer = new ContactInformationMaintainer(tester.controller(), Duration.ofDays(1), new JobControl(tester.controller().curator()), - tester.organization()); + tester.organization(), apiAuthorityConfig); + wireMockRule.stubFor(post(urlEqualTo(contactInfoPath)) + .willReturn(aResponse().withStatus(200))); + wireMockRule.stubFor(get(urlEqualTo(tenantPath)) + .willReturn(okJson("[{\"tenant\":\"tenant1\"}]"))); + wireMockRule.stubFor(get(urlEqualTo(tenantPath + "tenant1")) + .willReturn(okJson("{\"tenant\":\"tenant1\", \"athensDomain\":\"domain\", \"property\":\"property\", \"propertyId\":\"1\"}"))); } @Test @@ -44,13 +69,13 @@ public class ContactInformationMaintainerTest { TenantName name = tester.createTenant("tenant1", "domain1", propertyId); Supplier<AthenzTenant> tenant = () -> tester.controller().tenants().requireAthenzTenant(name); assertFalse("No contact information initially", tenant.get().contact().isPresent()); - Contact contact = testContact(); registerContact(propertyId, contact); maintainer.run(); - - assertTrue("Contact information added", tenant.get().contact().isPresent()); - assertEquals(contact, tenant.get().contact().get()); + verify(1, postRequestedFor(urlEqualTo(contactInfoPath))); + LoggedRequest request = findAll(postRequestedFor(urlEqualTo(contactInfoPath))).get(0); + String expectedBody = "{\"url\":\"http://contact1.test\",\"issueTrackerUrl\":\"http://issue-tracker1.test\",\"propertyUrl\":\"http://property1.test\",\"persons\":[[\"alice\"],[\"bob\"]]}"; + assertEquals(expectedBody, new String(request.getBody())); } private void registerContact(long propertyId, Contact contact) { diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java index 9574256c065..e47cdf887f9 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java @@ -80,7 +80,11 @@ public class ControllerContainerTest { " <component id='com.yahoo.vespa.hosted.controller.api.integration.stubs.MockBuildService'/>\n" + " <component id='com.yahoo.vespa.hosted.controller.integration.ConfigServerProxyMock'/>\n" + " <component id='com.yahoo.vespa.hosted.controller.integration.MetricsServiceMock'/>\n" + - " <component id='com.yahoo.vespa.hosted.controller.maintenance.ControllerMaintenance'/>\n" + + " <component id='com.yahoo.vespa.hosted.controller.maintenance.ControllerMaintenance'>\n" + + " <config name=\"vespa.hosted.controller.authority.config.api-authority\">\n" + + " <authorities><item>https://localhost:4443/</item></authorities>\n" + + " </config>" + + " </component>" + " <component id='com.yahoo.vespa.hosted.controller.maintenance.JobControl'/>\n" + " <component id='com.yahoo.vespa.hosted.controller.integration.RoutingGeneratorMock'/>\n" + " <component id='com.yahoo.vespa.hosted.controller.integration.ArtifactRepositoryMock'/>\n" + 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 f2e105f2c31..d620d78d3f0 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 @@ -47,13 +47,13 @@ import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder; import com.yahoo.vespa.hosted.controller.deployment.BuildJob; import com.yahoo.vespa.hosted.controller.integration.ConfigServerMock; import com.yahoo.vespa.hosted.controller.integration.MetricsServiceMock; -import com.yahoo.vespa.hosted.controller.maintenance.ContactInformationMaintainer; import com.yahoo.vespa.hosted.controller.maintenance.DeploymentMetricsMaintainer; import com.yahoo.vespa.hosted.controller.maintenance.JobControl; import com.yahoo.vespa.hosted.controller.restapi.ContainerControllerTester; import com.yahoo.vespa.hosted.controller.restapi.ContainerTester; import com.yahoo.vespa.hosted.controller.restapi.ControllerContainerTest; import com.yahoo.vespa.hosted.controller.tenant.AthenzTenant; +import com.yahoo.vespa.hosted.controller.tenant.Contact; import org.apache.http.HttpEntity; import org.apache.http.entity.ContentType; import org.apache.http.entity.mime.MultipartEntityBuilder; @@ -1269,9 +1269,8 @@ public class ApplicationApiTest extends ControllerContainerTest { } private void updateContactInformation() { - new ContactInformationMaintainer(tester.controller(), Duration.ofDays(1), - new JobControl(tester.controller().curator()), - organization()).run(); + Contact contact = new Contact(URI.create("www.contacts.tld/1234"), URI.create("www.properties.tld/1234"), URI.create("www.issues.tld/1234"), Arrays.asList(Arrays.asList("alice"), Arrays.asList("bob"))); + tester.controller().tenants().lockIfPresent(TenantName.from("tenant2"), lockedTenant -> tester.controller().tenants().store(lockedTenant.with(contact))); } private void registerContact(long propertyId) { |