Skip to content

Commit

Permalink
Add stats endpoint to GeoIpDownloader (#70282)
Browse files Browse the repository at this point in the history
This change adds _geoip/stats endpoint that can be used to collect basic data about geoip downloader (successful, failed and skipped downloads, current db count and total time spent downloading).
It also fixes missing/wrong origins for clients that will break if used with security.

Relates to #68920
  • Loading branch information
probakowski authored Mar 23, 2021
1 parent d7144de commit f5b7aad
Show file tree
Hide file tree
Showing 21 changed files with 1,146 additions and 44 deletions.
5 changes: 5 additions & 0 deletions client/rest-high-level/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ dependencies {
testImplementation(project(':x-pack:plugin:core')) {
exclude group: 'org.elasticsearch', module: 'elasticsearch-rest-high-level-client'
}
testImplementation(project(':modules:ingest-geoip')) {
exclude group: 'com.fasterxml.jackson.core', module: 'jackson-annotations'
}
testImplementation(project(':x-pack:plugin:eql'))
}

Expand Down Expand Up @@ -84,6 +87,8 @@ tasks.named("check").configure {
testClusters.all {
testDistribution = 'DEFAULT'
systemProperty 'es.scripting.update.ctx_in_params', 'false'
systemProperty 'es.geoip_v2_feature_flag_enabled', 'true'
setting 'geoip.downloader.enabled', 'false'
setting 'reindex.remote.whitelist', '[ "[::1]:*", "127.0.0.1:*" ]'
setting 'xpack.license.self_generated.type', 'trial'
setting 'xpack.security.enabled', 'true'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

package org.elasticsearch.client;

import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;

import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg;

public class GeoIpStatsResponse implements ToXContentObject {

@SuppressWarnings("unchecked")
private static final ConstructingObjectParser<GeoIpStatsResponse, Void> PARSER = new ConstructingObjectParser<>("geoip_stats", a -> {
Map<String, Object> stats = (Map<String, Object>) a[0];
List<Tuple<String, NodeInfo>> nodes = (List<Tuple<String, NodeInfo>>) a[1];

return new GeoIpStatsResponse((int) stats.get("successful_downloads"), (int) stats.get("failed_downloads"),
((Number) stats.get("total_download_time")).longValue(), (int) stats.get("databases_count"), (int) stats.get("skipped_updates"),
nodes.stream().collect(Collectors.toMap(Tuple::v1, Tuple::v2)));
});

static {
PARSER.declareObject(constructorArg(), (p, c) -> p.map(), new ParseField("stats"));
PARSER.declareNamedObjects(constructorArg(), (p, c, name) -> Tuple.tuple(name, NodeInfo.PARSER.apply(p, c)),
new ParseField("nodes"));
}

private final int successfulDownloads;
private final int failedDownloads;
private final long totalDownloadTime;
private final int databasesCount;
private final int skippedDownloads;
private final Map<String, NodeInfo> nodes;

public GeoIpStatsResponse(int successfulDownloads, int failedDownloads, long totalDownloadTime, int databasesCount,
int skippedDownloads, Map<String, NodeInfo> nodes) {
this.successfulDownloads = successfulDownloads;
this.failedDownloads = failedDownloads;
this.totalDownloadTime = totalDownloadTime;
this.databasesCount = databasesCount;
this.skippedDownloads = skippedDownloads;
this.nodes = nodes;
}

public int getSuccessfulDownloads() {
return successfulDownloads;
}

public int getFailedDownloads() {
return failedDownloads;
}

public long getTotalDownloadTime() {
return totalDownloadTime;
}

public int getDatabasesCount() {
return databasesCount;
}

public int getSkippedDownloads() {
return skippedDownloads;
}

public Map<String, NodeInfo> getNodes() {
return nodes;
}

public static GeoIpStatsResponse fromXContent(XContentParser parser) {
return PARSER.apply(parser, null);
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
GeoIpStatsResponse that = (GeoIpStatsResponse) o;
return successfulDownloads == that.successfulDownloads
&& failedDownloads == that.failedDownloads
&& totalDownloadTime == that.totalDownloadTime
&& databasesCount == that.databasesCount
&& skippedDownloads == that.skippedDownloads
&& nodes.equals(that.nodes);
}

@Override
public int hashCode() {
return Objects.hash(successfulDownloads, failedDownloads, totalDownloadTime, databasesCount, skippedDownloads, nodes);
}

@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.startObject("stats");
{
builder.field("successful_downloads", successfulDownloads);
builder.field("failed_downloads", failedDownloads);
builder.field("skipped_updates", skippedDownloads);
builder.field("total_download_time", totalDownloadTime);
builder.field("databases_count", databasesCount);
}
builder.endObject();
builder.field("nodes", nodes);
builder.endObject();
return builder;
}

public static final class NodeInfo implements ToXContentObject {
@SuppressWarnings("unchecked")
private static final ConstructingObjectParser<NodeInfo, Void> PARSER = new ConstructingObjectParser<>("node_info", a -> {
List<DatabaseInfo> databases = (List<DatabaseInfo>) a[1];
return new NodeInfo((Collection<String>) a[0], databases.stream().collect(Collectors.toMap(DatabaseInfo::getName,
Function.identity())));
});

static {
PARSER.declareStringArray(optionalConstructorArg(), new ParseField("files_in_temp"));
PARSER.declareObjectArray(optionalConstructorArg(), DatabaseInfo.PARSER, new ParseField("databases"));
}

private final List<String> filesInTemp;
private final Map<String, DatabaseInfo> databases;

public NodeInfo(Collection<String> filesInTemp, Map<String, DatabaseInfo> databases) {
this.filesInTemp = List.copyOf(filesInTemp);
this.databases = Map.copyOf(databases);
}

public List<String> getFilesInTemp() {
return filesInTemp;
}

public Map<String, DatabaseInfo> getDatabases() {
return databases;
}

@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.field("files_in_temp", filesInTemp);
builder.field("databases", databases.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.map(Map.Entry::getValue)
.collect(Collectors.toList()));
builder.endObject();
return builder;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
NodeInfo nodeInfo = (NodeInfo) o;
return filesInTemp.equals(nodeInfo.filesInTemp) && databases.equals(nodeInfo.databases);
}

@Override
public int hashCode() {
return Objects.hash(filesInTemp, databases);
}
}

public static final class DatabaseInfo implements ToXContentObject {

private static final ConstructingObjectParser<DatabaseInfo, Void> PARSER = new ConstructingObjectParser<>("database_info",
a -> new DatabaseInfo((String) a[0]));

static {
PARSER.declareString(constructorArg(), new ParseField("name"));
}

private final String name;

public DatabaseInfo(String name) {
this.name = name;
}

public String getName() {
return name;
}

@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.field("name", name);
builder.endObject();
return builder;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DatabaseInfo that = (DatabaseInfo) o;
return name.equals(that.name);
}

@Override
public int hashCode() {
return Objects.hash(name);
}
}
}
Loading

0 comments on commit f5b7aad

Please sign in to comment.