Skip to content

Commit

Permalink
fix(content data for vespa version >=8.323.45): content data for vesp…
Browse files Browse the repository at this point in the history
…a version >=8.323.45 (#76)

* Use appPackage to create content nodes

* Ignore jenv

* Ignore jenv

* Clean

* Remove jenv
  • Loading branch information
eoctodolphin authored May 21, 2024
1 parent 9dff0b9 commit 25f3188
Show file tree
Hide file tree
Showing 18 changed files with 449 additions and 8 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ package-lock.json
org.eclipse.*
settings.json
.DS_Store
.java-version
4 changes: 4 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
Expand Down
17 changes: 17 additions & 0 deletions src/main/java/com/vispana/api/model/apppackage/Content.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.vispana.api.model.apppackage;

import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import java.util.List;

public class Content {

@JacksonXmlProperty(isAttribute = true)
private String id;

@JacksonXmlProperty(localName = "group")
private TopGroup topGroup;

protected List<Group> getContentGroups() {
return topGroup != null ? topGroup.getGroups() : List.of();
}
}
30 changes: 30 additions & 0 deletions src/main/java/com/vispana/api/model/apppackage/Group.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.vispana.api.model.apppackage;

import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import java.util.List;

public class Group {

@JacksonXmlProperty(localName = "name", isAttribute = true)
private String name;

@JacksonXmlProperty(localName = "distribution-key", isAttribute = true)
private String distributionKey;

@JacksonXmlElementWrapper(useWrapping = false)
@JacksonXmlProperty(localName = "node")
List<Node> nodes;

public String getName() {
return name;
}

public String getDistributionKey() {
return distributionKey;
}

public List<Node> getNodes() {
return nodes != null ? nodes : List.of();
}
}
21 changes: 21 additions & 0 deletions src/main/java/com/vispana/api/model/apppackage/Host.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.vispana.api.model.apppackage;

import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;

public class Host {

@JacksonXmlProperty(localName = "name", isAttribute = true)
private String name;

@JacksonXmlProperty(localName = "alias")
private String alias;

// getters and setters
public String getName() {
return name;
}

public String getAlias() {
return alias;
}
}
29 changes: 29 additions & 0 deletions src/main/java/com/vispana/api/model/apppackage/Hosts.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.vispana.api.model.apppackage;

import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import java.io.IOException;
import java.util.List;

@JacksonXmlRootElement(localName = "hosts")
public class Hosts {
@JacksonXmlElementWrapper(useWrapping = false)
@JacksonXmlProperty(localName = "host")
List<Host> hosts;

public static Hosts fromXml(String xml) {
XmlMapper xmlMapper = new XmlMapper();

try {
return xmlMapper.readValue(xml, Hosts.class);
} catch (IOException e) {
throw new RuntimeException("Failed to parse Hosts xml", e);
}
}

public List<Host> getHosts() {
return hosts == null ? List.of() : hosts.stream().toList();
}
}
20 changes: 20 additions & 0 deletions src/main/java/com/vispana/api/model/apppackage/Node.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.vispana.api.model.apppackage;

import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;

public class Node {

@JacksonXmlProperty(localName = "hostalias", isAttribute = true)
private String hostAlias;

@JacksonXmlProperty(localName = "distribution-key", isAttribute = true)
private String distributionKey;

public String getHostAlias() {
return hostAlias;
}

public String getDistributionKey() {
return distributionKey;
}
}
31 changes: 31 additions & 0 deletions src/main/java/com/vispana/api/model/apppackage/Services.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.vispana.api.model.apppackage;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import java.io.IOException;
import java.util.List;

@JacksonXmlRootElement(localName = "services")
public class Services {

@JacksonXmlProperty(localName = "content")
private Content content;

public static Services fromXml(String xml) {
XmlMapper xmlMapper = new XmlMapper();
// To ignore parts off service xml that we don't need
xmlMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

try {
return xmlMapper.readValue(xml, Services.class);
} catch (IOException e) {
throw new RuntimeException("Failed to parse Services xml", e);
}
}

public List<Group> getContentGroups() {
return content != null ? content.getContentGroups() : List.of();
}
}
19 changes: 19 additions & 0 deletions src/main/java/com/vispana/api/model/apppackage/TopGroup.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.vispana.api.model.apppackage;

import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import java.util.List;

public class TopGroup {

@JacksonXmlProperty(isAttribute = true)
private String name;

@JacksonXmlElementWrapper(useWrapping = false)
@JacksonXmlProperty(localName = "group")
List<Group> groups;

public List<Group> getGroups() {
return groups;
}
}
14 changes: 11 additions & 3 deletions src/main/java/com/vispana/vespa/state/VespaStateClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

@Component
public class VespaStateClient {

public VispanaRoot vespaState(String configHost) {

try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Expand All @@ -29,13 +30,21 @@ public VispanaRoot vespaState(String configHost) {
var vespaVersion = vespaVersionFork.get();
var appUrl = appUrlFork.get();

var appPackageScope = scope.fork(() -> AppPackageAssembler.assemble(appUrl));
scope
.join()
.throwIfFailed(
throwable -> new RuntimeException("Failed to get app data from Vespa", throwable));
var appPackage = appPackageScope.get();

// fetch and build Vispana components concurrently and block until tasks are done
var configFork = scope.fork(() -> ConfigNodesAssembler.assemble(configHost, vespaMetrics));
var containerFork = scope.fork(() -> ContainerAssembler.assemble(configHost, vespaMetrics));
var contentFork =
scope.fork(
() -> ContentAssembler.assemble(configHost, vespaVersion, vespaMetrics, appUrl));
var appPackageScope = scope.fork(() -> AppPackageAssembler.assemble(appUrl));
() ->
ContentAssembler.assemble(
configHost, vespaVersion, vespaMetrics, appUrl, appPackage));
scope
.join()
.throwIfFailed(
Expand All @@ -44,7 +53,6 @@ public VispanaRoot vespaState(String configHost) {
var configNodes = configFork.get();
var containerNodes = containerFork.get();
var contentNodes = contentFork.get();
var appPackage = appPackageScope.get();

return new VispanaRoot(configNodes, containerNodes, contentNodes, appPackage);
} catch (Throwable t) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package com.vispana.vespa.state.assemblers;

import static com.vispana.vespa.state.helpers.ContentNodesExtractor.contentNodesFromAppPackage;
import static com.vispana.vespa.state.helpers.ProcessStatus.processStatus;
import static com.vispana.vespa.state.helpers.Request.requestGet;
import static com.vispana.vespa.state.helpers.SystemMetrics.systemMetrics;
import static java.util.stream.Collectors.groupingBy;

import com.vispana.api.model.Host;
import com.vispana.api.model.apppackage.ApplicationPackage;
import com.vispana.api.model.content.ContentCluster;
import com.vispana.api.model.content.ContentData;
import com.vispana.api.model.content.ContentNode;
Expand Down Expand Up @@ -38,14 +40,17 @@ public static ContentNodes assemble(
String configHost,
String vespaVersion,
Map<String, MetricsNode> vespaMetrics,
String appUrl) {
String appUrl,
ApplicationPackage appPackage) {
var contentDistributionUrl = configHost + "/config/v1/vespa.config.content.distribution/";

var contentClusters =
requestGet(contentDistributionUrl, ContentDistributionSchema.class).getConfigs().stream()
.map(NameExtractorFromUrl::nameFromUrl)
.map(
clusterName -> {
var dispatcher = fetchDispatcherData(configHost, clusterName, vespaVersion);
var dispatcher =
fetchDispatcherData(configHost, clusterName, vespaVersion, appPackage);
var schemas = fetchSchemas(configHost, clusterName);
var contentDistribution = fetchContentDistributionData(configHost, clusterName);
var distribution =
Expand Down Expand Up @@ -146,18 +151,34 @@ private static List<String> fetchSchemas(String configHost, String clusterName)
}

private static List<Node> fetchDispatcherData(
String configHost, String clusterName, String vespaVersion) {
if (vespaVersion.startsWith("7")) {
String configHost,
String clusterName,
String vespaVersion,
final ApplicationPackage appPackage) {

String[] version = vespaVersion.split("\\.");

// Assume semantic versioning major.minor.patch
if (version.length != 3) {
throw new RuntimeException("Failed to parse vespa version");
}

int majorVersion = Integer.parseInt(version[0]);
int minorVersion = Integer.parseInt(version[1]);

if (majorVersion == 7) {
var dispatcherUrl =
configHost + "/config/v1/vespa.config.search.dispatch/" + clusterName + "/search";
return requestGet(dispatcherUrl, SearchDispatchSchema.class).getNode();
} else {
} else if (majorVersion == 8 && minorVersion < 323) {
var dispatcherUrl =
configHost
+ "/config/v2/tenant/default/application/default/vespa.config.search.dispatch-nodes/"
+ clusterName
+ "/search";
return requestGet(dispatcherUrl, SearchDispatchNodesSchema.class).getNode();
} else {
return contentNodesFromAppPackage(appPackage);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.vispana.vespa.state.helpers;

import com.vispana.api.model.apppackage.ApplicationPackage;
import com.vispana.api.model.apppackage.Hosts;
import com.vispana.api.model.apppackage.Services;
import com.vispana.client.vespa.model.content.Node;
import java.util.List;
import java.util.stream.Collectors;

public class ContentNodesExtractor {
public static final long DEFAULT_RPC_ADMIN_PORT = 19103L;

public static List<Node> contentNodesFromAppPackage(final ApplicationPackage appPackage) {
Services services = Services.fromXml(appPackage.servicesContent());
Hosts hosts = Hosts.fromXml(appPackage.hostsContent());

return services.getContentGroups().stream()
.flatMap(group -> group.getNodes().stream().map(n -> createNode(group, n, hosts)))
.collect(Collectors.toList());
}

private static Node createNode(
final com.vispana.api.model.apppackage.Group group,
final com.vispana.api.model.apppackage.Node appPackNode,
final Hosts hosts) {
Node node = new Node();
node.setPort(DEFAULT_RPC_ADMIN_PORT);
node.setKey(Long.parseLong(appPackNode.getDistributionKey()));
node.setGroup(Long.parseLong(group.getDistributionKey()));

String hostAlias = appPackNode.getHostAlias();
String hostName =
hosts.getHosts().stream()
.filter(host -> hostAlias.equals(host.getAlias()))
.map(com.vispana.api.model.apppackage.Host::getName)
.findFirst()
.orElseThrow(() -> new RuntimeException("Failed to find host for alias: " + hostAlias));
node.setHost(hostName);
return node;
}
}
29 changes: 29 additions & 0 deletions src/test/java/com/vispana/Helper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.vispana;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import org.apache.commons.io.FileUtils;

public class Helper {

public static String defaultHostsXmlString() {
ClassLoader loader = Helper.class.getClassLoader();
File file = new File(loader.getResource("xml/hosts.xml").getFile());
try {
return FileUtils.readFileToString(file, StandardCharsets.UTF_8);
} catch (IOException e) {
throw new RuntimeException(e);
}
}

public static String defaultServicesXmlString() {
ClassLoader loader = Helper.class.getClassLoader();
File file = new File(loader.getResource("xml/services.xml").getFile());
try {
return FileUtils.readFileToString(file, StandardCharsets.UTF_8);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Loading

0 comments on commit 25f3188

Please sign in to comment.