diff options
author | Jon Marius Venstad <jonmv@users.noreply.github.com> | 2022-04-19 16:18:59 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-04-19 16:18:59 +0200 |
commit | 5cf282748f208c6c30e4943484042679185334b9 (patch) | |
tree | 271bcaefd3863bf4c7f29a6e597e9690bbc08ee6 /configserver | |
parent | 926dec1639b088d7a33a88ea3e03e679e7a618d0 (diff) | |
parent | ecc1d05760fedf9c49a24a949160aa7930838dcc (diff) |
Merge pull request #22119 from vespa-engine/jonmv/no-more-serviceview/v1
Jonmv/no more serviceview/v1
Diffstat (limited to 'configserver')
10 files changed, 0 insertions, 1022 deletions
diff --git a/configserver/pom.xml b/configserver/pom.xml index 110099421d1..1a06362e42a 100644 --- a/configserver/pom.xml +++ b/configserver/pom.xml @@ -184,11 +184,6 @@ </dependency> <dependency> <groupId>com.yahoo.vespa</groupId> - <artifactId>serviceview</artifactId> - <version>${project.version}</version> - </dependency> - <dependency> - <groupId>com.yahoo.vespa</groupId> <artifactId>zookeeper-server-common</artifactId> <version>${project.version}</version> <scope>provided</scope> @@ -246,52 +241,6 @@ <version>${project.version}</version> </dependency> - <!-- Jersey, needed by serviceview --> - <dependency> - <groupId>javax.ws.rs</groupId> - <artifactId>javax.ws.rs-api</artifactId> - <scope>provided</scope> <!-- TODO: Vespa 8: Set to compile if we get rid of the javax.ws.rs-api bundle --> - </dependency> - <dependency> - <groupId>org.glassfish.jersey.core</groupId> - <artifactId>jersey-client</artifactId> - </dependency> - <dependency> - <groupId>org.glassfish.jersey.core</groupId> - <artifactId>jersey-server</artifactId> - <exclusions> - <exclusion> - <groupId>org.glassfish.jersey.media</groupId> - <artifactId>jersey-media-jaxb</artifactId> - </exclusion> - </exclusions> - </dependency> - <dependency> - <groupId>org.glassfish.jersey.ext</groupId> - <artifactId>jersey-proxy-client</artifactId> - </dependency> - <dependency> - <groupId>org.glassfish.jersey.media</groupId> - <artifactId>jersey-media-json-jackson</artifactId> - <exclusions> - <!-- Prevent embedding deps provided by jdisc --> - <exclusion> - <groupId>com.fasterxml.jackson.core</groupId> - <artifactId>jackson-annotations</artifactId> - </exclusion> - <exclusion> - <groupId>com.fasterxml.jackson.core</groupId> - <artifactId>jackson-core</artifactId> - </exclusion> - </exclusions> - </dependency> - <dependency> - <!-- Not needed by configserver, but by controller. Also pulls in mimepull. --> - <groupId>org.glassfish.jersey.media</groupId> - <artifactId>jersey-media-multipart</artifactId> - </dependency> - <!-- Jersey END --> - </dependencies> <build> <plugins> diff --git a/configserver/src/main/java/com/yahoo/vespa/serviceview/Cluster.java b/configserver/src/main/java/com/yahoo/vespa/serviceview/Cluster.java deleted file mode 100644 index 7e2a83b6b9a..00000000000 --- a/configserver/src/main/java/com/yahoo/vespa/serviceview/Cluster.java +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.serviceview; - -import java.util.Arrays; -import java.util.List; - -import com.google.common.collect.ImmutableList; - -/** - * Model a single cluster of services in the Vespa model. - * - * @author Steinar Knutsen - */ -public final class Cluster implements Comparable<Cluster> { - - public final String name; - public final String type; - /** - * An ordered list of the service instances in this cluster. - */ - public final ImmutableList<Service> services; - - public Cluster(String name, String type, List<Service> services) { - this.name = name; - this.type = type; - ImmutableList.Builder<Service> builder = ImmutableList.builder(); - Service[] sortingBuffer = services.toArray(new Service[0]); - Arrays.sort(sortingBuffer); - builder.add(sortingBuffer); - this.services = builder.build(); - } - - @Override - public int compareTo(Cluster other) { - int nameOrder = name.compareTo(other.name); - if (nameOrder != 0) { - return nameOrder; - } - return type.compareTo(other.type); - } - - @Override - public int hashCode() { - final int prime = 761; - int result = 1; - result = prime * result + name.hashCode(); - result = prime * result + type.hashCode(); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - Cluster other = (Cluster) obj; - if (!name.equals(other.name)) { - return false; - } - return type.equals(other.type); - } - - @Override - public String toString() { - final int maxLen = 3; - StringBuilder builder = new StringBuilder(); - builder.append("Cluster [name=").append(name).append(", type=").append(type).append(", services=") - .append(services.subList(0, Math.min(services.size(), maxLen))).append("]"); - return builder.toString(); - } - -} diff --git a/configserver/src/main/java/com/yahoo/vespa/serviceview/ProxyErrorMapper.java b/configserver/src/main/java/com/yahoo/vespa/serviceview/ProxyErrorMapper.java deleted file mode 100644 index 545e9b3ddc8..00000000000 --- a/configserver/src/main/java/com/yahoo/vespa/serviceview/ProxyErrorMapper.java +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.serviceview; - -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Response; -import javax.ws.rs.ext.ExceptionMapper; -import javax.ws.rs.ext.Provider; - -/** - * Convert exceptions thrown by the internal REST client into a little more helpful responses. - * - * @author Steinar Knutsen - */ -@Provider -public class ProxyErrorMapper implements ExceptionMapper<WebApplicationException> { - - @Override - public Response toResponse(WebApplicationException exception) { - StringBuilder msg = new StringBuilder("Invoking (external) web service failed: "); - msg.append(exception.getMessage()); - return Response.status(500).entity(msg.toString()).type("text/plain").build(); - } - -} diff --git a/configserver/src/main/java/com/yahoo/vespa/serviceview/Service.java b/configserver/src/main/java/com/yahoo/vespa/serviceview/Service.java deleted file mode 100644 index 280ae1fa6c1..00000000000 --- a/configserver/src/main/java/com/yahoo/vespa/serviceview/Service.java +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.serviceview; - -import java.math.BigInteger; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.List; - -import com.google.common.collect.ImmutableList; -import com.yahoo.text.Utf8; - -/** - * Model a single service instance as a sortable object. - * - * @author Steinar Knutsen - */ -public final class Service implements Comparable<Service> { - - public final String serviceType; - public final String host; - public final int statePort; - public final String configId; - public final List<Integer> ports; - public final String name; - - public Service(String serviceType, String host, int statePort, String clusterName, String clusterType, - String configId, List<Integer> ports, String name) { - this.serviceType = serviceType; - this.host = host.toLowerCase(); - this.statePort = statePort; - this.configId = configId; - ImmutableList.Builder<Integer> portsBuilder = new ImmutableList.Builder<>(); - portsBuilder.addAll(ports); - this.ports = portsBuilder.build(); - this.name = name; - } - - @Override - public int compareTo(Service other) { - int serviceTypeOrder = serviceType.compareTo(other.serviceType); - if (serviceTypeOrder != 0) { - return serviceTypeOrder; - } - int hostOrder = host.compareTo(other.host); - if (hostOrder != 0) { - return hostOrder; - } - return Integer.compare(statePort, other.statePort); - } - - /** - * Generate an identifier string for one of the ports of this service - * suitable for using in an URL. - * - * @param port - * port which this identifier pertains to - * @return an opaque identifier string for this service - */ - public String getIdentifier(int port) { - StringBuilder b = new StringBuilder(serviceType); - b.append("-"); - MessageDigest md5; - try { - md5 = MessageDigest.getInstance("MD5"); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException("MD5 should by definition always be available in the JVM.", e); - } - md5.update(Utf8.toBytes(serviceType)); - md5.update(Utf8.toBytes(configId)); - md5.update(Utf8.toBytes(host)); - for (int i = 3; i >= 0; --i) { - md5.update((byte) (port >>> i)); - } - byte[] digest = md5.digest(); - BigInteger digestMarshal = new BigInteger(1, digest); - b.append(digestMarshal.toString(36)); - return b.toString(); - } - - /** - * All valid identifiers for this object. - * - * @return a list with a unique ID for each of this service's ports - */ - public List<String> getIdentifiers() { - List<String> ids = new ArrayList<>(ports.size()); - for (int port : ports) { - ids.add(getIdentifier(port)); - } - return ids; - } - - /** - * Find which port number a hash code pertains to. - * - * @param identifier a string generated from {@link #getIdentifier(int)} - * @return a port number, or 0 if no match is found - */ - public int matchIdentifierWithPort(String identifier) { - for (int port : ports) { - if (identifier.equals(getIdentifier(port))) { - return port; - } - } - throw new IllegalArgumentException("Identifier " + identifier + " matches no ports in " + this); - } - - @Override - public String toString() { - final int maxLen = 3; - StringBuilder builder = new StringBuilder(); - builder.append("Service [serviceType=").append(serviceType).append(", host=").append(host).append(", statePort=") - .append(statePort).append(", configId=").append(configId).append(", ports=") - .append(ports.subList(0, Math.min(ports.size(), maxLen))).append(", name=").append(name) - .append("]"); - return builder.toString(); - } - - @Override - public int hashCode() { - final int prime = 131; - int result = 1; - result = prime * result + configId.hashCode(); - result = prime * result + host.hashCode(); - result = prime * result + name.hashCode(); - result = prime * result + ports.hashCode(); - result = prime * result + serviceType.hashCode(); - result = prime * result + statePort; - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - Service other = (Service) obj; - if (!configId.equals(other.configId)) { - return false; - } - if (!host.equals(other.host)) { - return false; - } - if (!name.equals(other.name)) { - return false; - } - if (!ports.equals(other.ports)) { - return false; - } - if (!serviceType.equals(other.serviceType)) { - return false; - } - return statePort == other.statePort; - } - -} diff --git a/configserver/src/main/java/com/yahoo/vespa/serviceview/ServiceModel.java b/configserver/src/main/java/com/yahoo/vespa/serviceview/ServiceModel.java deleted file mode 100644 index f3e327ead32..00000000000 --- a/configserver/src/main/java/com/yahoo/vespa/serviceview/ServiceModel.java +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.serviceview; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import com.google.common.collect.HashBasedTable; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Table; -import com.google.common.collect.Table.Cell; -import com.yahoo.vespa.serviceview.bindings.ApplicationView; -import com.yahoo.vespa.serviceview.bindings.ClusterView; -import com.yahoo.vespa.serviceview.bindings.HostService; -import com.yahoo.vespa.serviceview.bindings.ModelResponse; -import com.yahoo.vespa.serviceview.bindings.ServicePort; -import com.yahoo.vespa.serviceview.bindings.ServiceView; - -import static com.yahoo.config.model.api.container.ContainerServiceType.CLUSTERCONTROLLER_CONTAINER; - -/** - * A transposed view for cloud.config.model. - * - * @author Steinar Knutsen - */ -public final class ServiceModel { - - private static final String CONTENT_CLUSTER_TYPENAME = "content"; - - private final Map<String, Service> servicesMap; - - /** - * An ordered list of the clusters in this config model. - */ - public final ImmutableList<Cluster> clusters; - - ServiceModel(ModelResponse modelConfig) { - Table<String, String, List<Service>> services = HashBasedTable.create(); - for (HostService h : modelConfig.hosts) { - String hostName = h.name; - for (com.yahoo.vespa.serviceview.bindings.Service s : h.services) { - addService(services, hostName, s); - } - } - List<Cluster> sortingBuffer = new ArrayList<>(); - for (Cell<String, String, List<Service>> c : services.cellSet()) { - sortingBuffer.add(new Cluster(c.getRowKey(), c.getColumnKey(), c.getValue())); - } - Collections.sort(sortingBuffer); - ImmutableList.Builder<Cluster> clustersBuilder = new ImmutableList.Builder<>(); - clustersBuilder.addAll(sortingBuffer); - clusters = clustersBuilder.build(); - Map<String, Service> seenIdentifiers = new HashMap<>(); - for (Cluster c : clusters) { - for (Service s : c.services) { - List<String> identifiers = s.getIdentifiers(); - for (String identifier : identifiers) { - if (seenIdentifiers.containsKey(identifier)) { - throw new RuntimeException("Hash collision" + " between " + - seenIdentifiers.get(identifier) + " and " + s + "."); - } - seenIdentifiers.put(identifier, s); - } - } - } - ImmutableMap.Builder<String, Service> servicesBuilder = new ImmutableMap.Builder<>(); - servicesBuilder.putAll(seenIdentifiers); - servicesMap = servicesBuilder.build(); - } - - private static void addService(Table<String, String, List<Service>> services, String hostName, - com.yahoo.vespa.serviceview.bindings.Service s) { - boolean hasStateApi = false; - int statePort = 0; - List<Integer> ports = new ArrayList<>(s.ports.size()); - for (ServicePort port : s.ports) { - ports.add(port.number); - if (!hasStateApi && port.hasTags("http", "state")) { - hasStateApi = true; - statePort = port.number; - } - } - // ignore hosts without state API - if (hasStateApi) { - Service service = new Service(s.type, hostName, statePort, s.clustername, s.clustertype, s.configid, ports, s.name); - getAndSetEntry(services, s.clustername, s.clustertype).add(service); - } - } - - private static List<Service> getAndSetEntry(Table<String, String, List<Service>> services, String clusterName, String clusterType) { - List<Service> serviceList = services.get(clusterName, clusterType); - if (serviceList == null) { - serviceList = new ArrayList<>(); - services.put(clusterName, clusterType, serviceList); - } - return serviceList; - } - - /** - * The top level view of a given application. - * - * @return a top level view of the entire application in a form suitable for - * consumption by a REST API - */ - public ApplicationView showAllClusters(String uriBase, String applicationIdentifier) { - ApplicationView response = new ApplicationView(); - List<ClusterView> clusterViews = new ArrayList<>(); - for (Cluster c : clusters) { - clusterViews.add(showCluster(c, uriBase, applicationIdentifier)); - } - response.clusters = clusterViews; - return response; - } - - private ClusterView showCluster(Cluster c, String uriBase, String applicationIdentifier) { - List<ServiceView> services = new ArrayList<>(); - for (Service s : c.services) { - ServiceView service = new ServiceView(); - StringBuilder buffer = getLinkBuilder(uriBase).append(applicationIdentifier).append('/'); - service.url = buffer.append("service/").append(s.getIdentifier(s.statePort)).append("/state/v1/").toString(); - service.serviceType = s.serviceType; - service.serviceName = s.name; - service.configId = s.configId; - service.host = s.host; - addLegacyLink(uriBase, applicationIdentifier, s, service); - services.add(service); - } - ClusterView v = new ClusterView(); - v.services = services; - v.name = c.name; - v.type = c.type; - if (CONTENT_CLUSTER_TYPENAME.equals(c.type)) { - Service s = getFirstClusterController(); - StringBuilder buffer = getLinkBuilder(uriBase).append(applicationIdentifier).append('/'); - buffer.append("service/").append(s.getIdentifier(s.statePort)).append("/cluster/v2/").append(c.name); - v.url = buffer.toString(); - } else { - v.url = null; - } - return v; - } - - private void addLegacyLink(String uriBase, String applicationIdentifier, Service s, ServiceView service) { - if (s.serviceType.equals("storagenode") || s.serviceType.equals("distributor")) { - StringBuilder legacyBuffer = getLinkBuilder(uriBase); - legacyBuffer.append("legacy/").append(applicationIdentifier).append('/'); - legacyBuffer.append("service/").append(s.getIdentifier(s.statePort)).append('/'); - service.legacyStatusPages = legacyBuffer.toString(); - } - } - - private Service getFirstServiceInstanceByType(String typeName) { - for (Cluster c : clusters) { - for (Service s : c.services) { - if (typeName.equals(s.serviceType)) { - return s; - } - } - } - throw new IllegalStateException("This installation has but no service of required type: " - + typeName + "."); - } - - private Service getFirstClusterController() { - // This is used assuming all cluster controllers know of all fleet controllers in an application - return getFirstServiceInstanceByType(CLUSTERCONTROLLER_CONTAINER.serviceName); - } - - private StringBuilder getLinkBuilder(String uriBase) { - StringBuilder buffer = new StringBuilder(uriBase); - if (!uriBase.endsWith("/")) { - buffer.append('/'); - } - return buffer; - } - - @Override - public String toString() { - final int maxLen = 3; - StringBuilder builder = new StringBuilder(); - builder.append("ServiceModel [clusters=") - .append(clusters.subList(0, Math.min(clusters.size(), maxLen))).append("]"); - return builder.toString(); - } - - - /** - * Match an identifier with a service for this cluster. - * - * @param identifier - * an opaque service identifier generated by the service - * @return the corresponding Service instance - */ - public Service getService(String identifier) { - return servicesMap.get(identifier); - } - - /** - * Find a service based on host and port. - * - * @param host - * the name of the host running the service - * @param port - * a port owned by the service - * @param self - * the service which generated the host data - * @return a service instance fullfilling the criteria - * @throws IllegalArgumentException - * if no matching service is found - */ - public Service resolve(String host, int port, Service self) { - Integer portAsObject = port; - String realHost; - if ("localhost".equals(host)) { - realHost = self.host; - } else { - realHost = host; - } - for (Cluster c : clusters) { - for (Service s : c.services) { - if (s.host.equals(realHost) && s.ports.contains(portAsObject)) { - return s; - } - } - } - throw new IllegalArgumentException("No registered service owns port " + port + " on host " + realHost + "."); - } - -} diff --git a/configserver/src/main/java/com/yahoo/vespa/serviceview/StateRequestHandler.java b/configserver/src/main/java/com/yahoo/vespa/serviceview/StateRequestHandler.java deleted file mode 100644 index 59b91a52791..00000000000 --- a/configserver/src/main/java/com/yahoo/vespa/serviceview/StateRequestHandler.java +++ /dev/null @@ -1,253 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.serviceview; - -import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; -import com.google.inject.Inject; -import com.yahoo.cloud.config.ConfigserverConfig; -import com.yahoo.container.jdisc.ThreadedHttpRequestHandler; -import ai.vespa.http.DomainName; -import ai.vespa.http.HttpURL; -import ai.vespa.http.HttpURL.Path; -import ai.vespa.http.HttpURL.Query; -import ai.vespa.http.HttpURL.Scheme; -import com.yahoo.restapi.RestApi; -import com.yahoo.restapi.RestApiRequestHandler; -import com.yahoo.vespa.serviceview.bindings.ApplicationView; -import com.yahoo.vespa.serviceview.bindings.ConfigClient; -import com.yahoo.vespa.serviceview.bindings.HealthClient; -import com.yahoo.vespa.serviceview.bindings.ModelResponse; -import org.glassfish.jersey.client.ClientProperties; -import org.glassfish.jersey.client.proxy.WebResourceFactory; - -import javax.ws.rs.client.Client; -import javax.ws.rs.client.ClientRequestFilter; -import javax.ws.rs.client.WebTarget; -import javax.ws.rs.core.HttpHeaders; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.HashMap; -import java.util.List; -import java.util.ListIterator; -import java.util.Map; - -/** - * A web service to discover and proxy Vespa service state info. - * - * @author Steinar Knutsen - * @author bjorncs - */ -public class StateRequestHandler extends RestApiRequestHandler<StateRequestHandler> { - - private static final String USER_AGENT = "service-view-config-server-client"; - private static final String SINGLE_API_LINK = "url"; - - @SuppressWarnings("removal") - private final Client client = new ai.vespa.util.http.VespaClientBuilderFactory() - .newBuilder() - .property(ClientProperties.CONNECT_TIMEOUT, 10000) - .property(ClientProperties.READ_TIMEOUT, 10000) - .register(JacksonJsonProvider.class) - .register((ClientRequestFilter) ctx -> ctx.getHeaders().put(HttpHeaders.USER_AGENT, List.of(USER_AGENT))) - .build(); - - private final int restApiPort; - - private static class GiveUpLinkRetargetingException extends Exception { - public GiveUpLinkRetargetingException(Throwable reason) { - super(reason); - } - - public GiveUpLinkRetargetingException(String message) { - super(message); - } - } - - @Inject - public StateRequestHandler(ThreadedHttpRequestHandler.Context context, - ConfigserverConfig configserverConfig) { - super(context, StateRequestHandler::createRestApiDefinition); - this.restApiPort = configserverConfig.httpport(); - } - - @Override - protected void destroy() { - client.close(); - super.destroy(); - } - - private static RestApi createRestApiDefinition(StateRequestHandler self) { - return RestApi.builder() - .addRoute(RestApi.route("/serviceview/v1") - .get(self::getDefaultUserInfo)) - .addRoute(RestApi.route("/serviceview/v1/") - .get(self::getDefaultUserInfo)) - .addRoute(RestApi.route("/serviceview/v1/tenant/{tenantName}/application/{applicationName}/environment/{environmentName}/region/{regionName}/instance/{instanceName}") - .get(self::getUserInfo)) - .addRoute(RestApi.route("/serviceview/v1/tenant/{tenantName}/application/{applicationName}/environment/{environmentName}/region/{regionName}/instance/{instanceName}/service/{serviceIdentifier}/{*}") - .get(self::singleService)) - .registerJacksonResponseEntity(HashMap.class) - .registerJacksonResponseEntity(ApplicationView.class) - .build(); - } - - private ApplicationView getDefaultUserInfo(RestApi.RequestContext context) { - return getUserInfo(context.baseRequestURL(), "default", "default", "default", "default", "default"); - } - - private ApplicationView getUserInfo(RestApi.RequestContext context) { - String tenantName = context.pathParameters().getStringOrThrow("tenantName"); - String applicationName = context.pathParameters().getStringOrThrow("applicationName"); - String environmentName = context.pathParameters().getStringOrThrow("environmentName"); - String regionName = context.pathParameters().getStringOrThrow("regionName"); - String instanceName = context.pathParameters().getStringOrThrow("instanceName"); - return getUserInfo(context.baseRequestURL(), tenantName, applicationName, environmentName, regionName, instanceName); - } - - public HashMap<?, ?> singleService(RestApi.RequestContext context) { - String tenantName = context.pathParameters().getStringOrThrow("tenantName"); - String applicationName = context.pathParameters().getStringOrThrow("applicationName"); - String environmentName = context.pathParameters().getStringOrThrow("environmentName"); - String regionName = context.pathParameters().getStringOrThrow("regionName"); - String instanceName = context.pathParameters().getStringOrThrow("instanceName"); - String identifier = context.pathParameters().getStringOrThrow("serviceIdentifier"); - Path apiParams = context.pathParameters().getRest().orElse(Path.empty()); - Query apiQuery = context.queryParameters().getFullQuery(); - return singleService(context.baseRequestURL(), tenantName, applicationName, environmentName, regionName, instanceName, identifier, apiParams, apiQuery); - } - - protected ApplicationView getUserInfo(HttpURL url, String tenantName, String applicationName, String environmentName, String regionName, String instanceName) { - ServiceModel model = new ServiceModel( - getModelConfig(tenantName, applicationName, environmentName, regionName, instanceName)); - return model.showAllClusters( - baseUri(url).toString(), - applicationIdentifier(tenantName, applicationName, environmentName, regionName, instanceName)); - } - - protected ModelResponse getModelConfig(String tenant, String application, String environment, String region, String instance) { - WebTarget target = client.target("http://localhost:" + restApiPort + "/"); - ConfigClient resource = WebResourceFactory.newResource(ConfigClient.class, target); - return resource.getServiceModel(tenant, application, environment, region, instance); - } - - protected HashMap<?, ?> singleService( - HttpURL url, String tenantName, String applicationName, String environmentName, String regionName, String instanceName, String identifier, Path path, Query query) { - ServiceModel model = new ServiceModel(getModelConfig(tenantName, applicationName, environmentName, regionName, instanceName)); - Service s = model.getService(identifier); - int requestedPort = s.matchIdentifierWithPort(identifier); - HealthClient resource = getHealthClient(path, s, requestedPort, query, client); - HashMap<?, ?> apiResult = resource.getHealthInfo(); - rewriteResourceLinks(url, apiResult, model, s, applicationIdentifier(tenantName, applicationName, environmentName, regionName, instanceName), identifier); - return apiResult; - } - - protected HealthClient getHealthClient(Path apiParams, Service s, int requestedPort, Query query, Client client) { - URI uri = HttpURL.create(Scheme.http, DomainName.of(s.host), requestedPort, apiParams, query).asURI(); - WebTarget target = client.target(uri); - return WebResourceFactory.newResource(HealthClient.class, target); - } - - private String applicationIdentifier(String tenant, String application, String environment, String region, String instance) { - return "tenant/" + tenant - + "/application/" + application - + "/environment/" + environment - + "/region/" + region - + "/instance/" + instance; - } - - private void rewriteResourceLinks(HttpURL url, - Object apiResult, - ServiceModel model, - Service self, - String applicationIdentifier, - String incomingIdentifier) { - if (apiResult instanceof List) { - for (@SuppressWarnings("unchecked") ListIterator<Object> i = ((List<Object>) apiResult).listIterator(); i.hasNext();) { - Object resource = i.next(); - if (resource instanceof String) { - try { - StringBuilder buffer = linkBuffer(url, applicationIdentifier); - // if it points to a port and host not part of the application, rewriting will not occur, so this is kind of safe - retarget(model, self, buffer, (String) resource); - i.set(buffer.toString()); - } catch (GiveUpLinkRetargetingException e) { - break; // assume relatively homogenous lists when doing rewrites to avoid freezing up on scanning long lists - } - } else { - rewriteResourceLinks(url, resource, model, self, applicationIdentifier, incomingIdentifier); - } - } - } else if (apiResult instanceof Map) { - @SuppressWarnings("unchecked") - Map<Object, Object> api = (Map<Object, Object>) apiResult; - for (Map.Entry<Object, Object> entry : api.entrySet()) { - if (SINGLE_API_LINK.equals(entry.getKey()) && entry.getValue() instanceof String) { - try { - rewriteSingleLink(entry, model, self, linkBuffer(url, applicationIdentifier)); - } catch (GiveUpLinkRetargetingException e) { - // NOP - } - } else if ("link".equals(entry.getKey()) && entry.getValue() instanceof String) { - buildSingleLink(entry, linkBuffer(url, applicationIdentifier), incomingIdentifier); - } else { - rewriteResourceLinks(url, entry.getValue(), model, self, applicationIdentifier, incomingIdentifier); - } - } - } - } - - private void buildSingleLink(Map.Entry<Object, Object> entry, - StringBuilder newUri, - String incomingIdentifier) { - newUri.append("/service/") - .append(incomingIdentifier); - newUri.append(entry.getValue()); - entry.setValue(newUri.toString()); - } - - private void addQuery(String query, StringBuilder newUri) { - if (query != null && query.length() > 0) { - newUri.append('?').append(query); - } - } - - private StringBuilder linkBuffer(HttpURL url, String applicationIdentifier) { - return new StringBuilder(baseUri(url).appendPath(Path.parse(applicationIdentifier)).toString()); - } - - private void rewriteSingleLink(Map.Entry<Object, Object> entry, - ServiceModel model, - Service self, - StringBuilder newUri) throws GiveUpLinkRetargetingException { - String url = (String) entry.getValue(); - retarget(model, self, newUri, url); - entry.setValue(newUri.toString()); - } - - private void retarget(ServiceModel model, Service self, StringBuilder newUri, String url) throws GiveUpLinkRetargetingException { - URI link; - try { - link = new URI(url); - } catch (URISyntaxException e) { - throw new GiveUpLinkRetargetingException(e); - } - if (!link.isAbsolute()) { - throw new GiveUpLinkRetargetingException("This rewriting only supports absolute URIs."); - } - int linkPort = link.getPort(); - if (linkPort == -1) { - linkPort = 80; - } - Service s; - try { - s = model.resolve(link.getHost(), linkPort, self); - } catch (IllegalArgumentException e) { - throw new GiveUpLinkRetargetingException(e); - } - newUri.append("/service/").append(s.getIdentifier(linkPort)); - newUri.append(link.getRawPath()); - } - - private static HttpURL baseUri(HttpURL url) { - return url.withPath(Path.parse("/serviceview/v1/")); - } -} diff --git a/configserver/src/main/java/com/yahoo/vespa/serviceview/package-info.java b/configserver/src/main/java/com/yahoo/vespa/serviceview/package-info.java deleted file mode 100644 index 72cfaf0d05d..00000000000 --- a/configserver/src/main/java/com/yahoo/vespa/serviceview/package-info.java +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -/** - * Home of the centralised service view implementation. The service view is a - * REST API for discovering and accessing the state API for any service in a - * Vespa cluster. - * - * <p>Do note this package is in its prototyping stage and classes <i>will</i> - * be renamed and moved around a little.</p> - */ -@ExportPackage -package com.yahoo.vespa.serviceview; - -import com.yahoo.osgi.annotation.ExportPackage; diff --git a/configserver/src/main/resources/configserver-app/services.xml b/configserver/src/main/resources/configserver-app/services.xml index b3a37c6f669..3536cfc7942 100644 --- a/configserver/src/main/resources/configserver-app/services.xml +++ b/configserver/src/main/resources/configserver-app/services.xml @@ -83,10 +83,6 @@ <binding>http://*/orchestrator/v1/instances</binding> <binding>http://*/orchestrator/v1/instances/*</binding> </handler> - <handler id="com.yahoo.vespa.serviceview.StateRequestHandler" bundle="configserver"> - <binding>http://*/serviceview/v1</binding> - <binding>http://*/serviceview/v1/*</binding> - </handler> <handler id='com.yahoo.vespa.config.server.http.HttpGetConfigHandler' bundle='configserver'> <binding>http://*/config/v1/*/*</binding> <binding>http://*/config/v1/*</binding> diff --git a/configserver/src/test/java/com/yahoo/vespa/serviceview/ServiceModelTest.java b/configserver/src/test/java/com/yahoo/vespa/serviceview/ServiceModelTest.java deleted file mode 100644 index 44bc2c15e8e..00000000000 --- a/configserver/src/test/java/com/yahoo/vespa/serviceview/ServiceModelTest.java +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.serviceview; - -import com.yahoo.vespa.defaults.Defaults; -import com.yahoo.vespa.serviceview.bindings.ApplicationView; -import com.yahoo.vespa.serviceview.bindings.HostService; -import com.yahoo.vespa.serviceview.bindings.ModelResponse; -import com.yahoo.vespa.serviceview.bindings.ServicePort; -import com.yahoo.vespa.serviceview.bindings.ServiceView; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import java.util.Arrays; -import java.util.Collections; - -import static com.yahoo.config.model.api.container.ContainerServiceType.CLUSTERCONTROLLER_CONTAINER; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -/** - * Functional tests for the programmatic view of cloud.config.model. - * - * @author Steinar Knutsen - */ -public class ServiceModelTest { - - private ServiceModel model; - - @Before - public void setUp() { - ModelResponse model = syntheticModelResponse(); - this.model = new ServiceModel(model); - } - - static ModelResponse syntheticModelResponse() { - ModelResponse model = new ModelResponse(); - HostService h = new HostService(); - h.name = "vespa.yahoo.com"; - com.yahoo.vespa.serviceview.bindings.Service service0 = new com.yahoo.vespa.serviceview.bindings.Service(); - { - service0.clustername = "examplecluster"; - service0.clustertype = "somethingservers"; - service0.index = 1L; - service0.type = "something"; - service0.name = "examplename"; - service0.configid = "blblb/lbl.0"; - ServicePort port = new ServicePort(); - port.number = Defaults.getDefaults().vespaWebServicePort(); - port.tags = "state http"; - service0.ports = Collections.singletonList(port); - } - com.yahoo.vespa.serviceview.bindings.Service service1 = new com.yahoo.vespa.serviceview.bindings.Service(); - { - service1.clustername = "examplecluster"; - service1.clustertype = "somethingservers"; - service1.index = 2L; - service1.type = CLUSTERCONTROLLER_CONTAINER.serviceName; - service1.name = "clustercontroller"; - service1.configid = "clustercontroller/lbl.0"; - ServicePort port = new ServicePort(); - port.number = 4090; - port.tags = "state http"; - service1.ports = Collections.singletonList(port); - } - com.yahoo.vespa.serviceview.bindings.Service service2 = new com.yahoo.vespa.serviceview.bindings.Service(); - { - service2.clustername = "tralala"; - service2.clustertype = "admin"; - service2.index = 3L; - service2.type = "configserver"; - service2.name = "configservername"; - service2.configid = "clustercontroller/lbl.0"; - ServicePort port = new ServicePort(); - port.number = 5000; - port.tags = "state http"; - service2.ports = Collections.singletonList(port); - } - h.services = Arrays.asList(service0, service1, service2); - model.hosts = Collections.singletonList(h); - return model; - } - - @After - public void tearDown() { - model = null; - } - - @Test - public final void test() { - final String uriBase = "http://configserver:5000/"; - ApplicationView x = model.showAllClusters(uriBase, "/tenant/default/application/default"); - assertEquals(2, x.clusters.size()); - String urlTracking = null; - for (com.yahoo.vespa.serviceview.bindings.ClusterView c : x.clusters) { - for (ServiceView s : c.services) { - if ("examplename".equals(s.serviceName)) { - assertEquals("something", s.serviceType); - urlTracking = s.url; - break; - } - } - } - assertNotNull(urlTracking); - final String serviceIdentifier = urlTracking.substring(urlTracking.indexOf("something"), - urlTracking.length() - "/state/v1/".length()); - Service y = model.getService(serviceIdentifier); - assertEquals("examplename", y.name); - } - -} diff --git a/configserver/src/test/java/com/yahoo/vespa/serviceview/StateRequestHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/serviceview/StateRequestHandlerTest.java deleted file mode 100644 index 79ed38b2025..00000000000 --- a/configserver/src/test/java/com/yahoo/vespa/serviceview/StateRequestHandlerTest.java +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.serviceview; - -import ai.vespa.http.HttpURL; -import ai.vespa.http.HttpURL.Query; -import com.yahoo.cloud.config.ConfigserverConfig; -import com.yahoo.jdisc.test.MockMetric; -import ai.vespa.http.HttpURL.Path; -import com.yahoo.vespa.serviceview.bindings.ApplicationView; -import com.yahoo.vespa.serviceview.bindings.HealthClient; -import com.yahoo.vespa.serviceview.bindings.ModelResponse; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import javax.ws.rs.client.Client; -import java.net.URI; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Executors; - -import static org.junit.Assert.assertEquals; - -/** - * Functional test for {@link StateRequestHandler}. - * - * @author Steinar Knutsen - * @author bjorncs - */ -public class StateRequestHandlerTest { - - private static final String EXTERNAL_BASE_URI = "http://someserver:8080/serviceview/v1/"; - - private static class TestHandler extends StateRequestHandler { - private static final String BASE_URI = "http://vespa.yahoo.com:8080/state/v1"; - - TestHandler(ConfigserverConfig config) { - super(new Context(Executors.newSingleThreadExecutor(), new MockMetric()), config); - } - - @Override - protected ModelResponse getModelConfig(String tenant, String application, String environment, String region, String instance) { - return ServiceModelTest.syntheticModelResponse(); - } - - @Override - protected HealthClient getHealthClient(Path apiParams, Service s, int requestedPort, Query query, Client client) { - HealthClient healthClient = Mockito.mock(HealthClient.class); - HashMap<Object, Object> dummyHealthData = new HashMap<>(); - HashMap<String, String> dummyLink = new HashMap<>(); - dummyLink.put("url", BASE_URI); - dummyHealthData.put("resources", Collections.singletonList(dummyLink)); - Mockito.when(healthClient.getHealthInfo()).thenReturn(dummyHealthData); - return healthClient; - } - } - - private StateRequestHandler testHandler; - private ServiceModel correspondingModel; - - @Before - public void setUp() throws Exception { - testHandler = new TestHandler(new ConfigserverConfig(new ConfigserverConfig.Builder())); - correspondingModel = new ServiceModel(ServiceModelTest.syntheticModelResponse()); - } - - @After - public void tearDown() { - testHandler = null; - correspondingModel = null; - } - - @Test - public final void test() { - Service s = correspondingModel.resolve("vespa.yahoo.com", 8080, null); - String api = "/state/v1"; - HashMap<?, ?> boom = testHandler.singleService(HttpURL.from(URI.create("http://someserver:8080")), "default", "default", "default", "default", "default", s.getIdentifier(8080), Path.parse(api), Query.empty().add("foo", "bar")); - assertEquals(EXTERNAL_BASE_URI + "tenant/default/application/default/environment/default/region/default/instance/default/service/" + s.getIdentifier(8080) + api, - ((Map<?, ?>) ((List<?>) boom.get("resources")).get(0)).get("url")); - } - - @Test - public final void testLinkEquality() { - ApplicationView explicitParameters = testHandler.getUserInfo(HttpURL.from(URI.create("http://someserver:8080")), "default", "default", "default", "default", "default"); - assertEquals(EXTERNAL_BASE_URI + "tenant/default/application/default/environment/default/region/default/instance" + - "/default/service/container-clustercontroller-2ul67p8psr451t3w8kdd0qwgg/state/v1/", - explicitParameters.clusters.get(0).services.get(0).url); - } - -} |