auto create apikey

This commit is contained in:
Jonathan Shook 2020-11-22 02:55:51 -06:00
parent cfa92eabcc
commit 3d2ff55f1c
5 changed files with 122 additions and 59 deletions

View File

@ -21,17 +21,28 @@ public class GrafanaClientConfig {
@JsonProperty("baseuri") @JsonProperty("baseuri")
private URI baseUrl; private URI baseUrl;
@JsonProperty("timeoutms") @JsonProperty("timeoutms" )
private int timeoutms; private int timeoutms;
private final List<Authenticator> authenticators = new ArrayList<>(); private final List<Authenticator> authenticators = new ArrayList<>();
// private LinkedHashMap<String,String> headers = new LinkedHashMap<>();
private final List<Supplier<Map<String, String>>> headerSources = new ArrayList<>(); private final List<Supplier<Map<String, String>>> headerSources = new ArrayList<>();
public GrafanaClientConfig() { public GrafanaClientConfig() {
} }
public void basicAuth(String username, String pw) { private GrafanaClientConfig(URI baseUrl, int timeoutms, List<Authenticator> authenticators,
List<Supplier<Map<String, String>>> headerSources) {
this.baseUrl = baseUrl;
this.timeoutms = timeoutms;
this.authenticators.addAll(authenticators);
this.headerSources.addAll(headerSources);
}
public GrafanaClientConfig copy() {
return new GrafanaClientConfig(baseUrl, timeoutms, authenticators, headerSources);
}
public GrafanaClientConfig basicAuth(String username, String pw) {
Objects.requireNonNull(username); Objects.requireNonNull(username);
String authPw = pw != null ? pw : ""; String authPw = pw != null ? pw : "";
@ -44,9 +55,9 @@ public class GrafanaClientConfig {
addAuthenticator(basicAuth); addAuthenticator(basicAuth);
addHeader("Authorization", encodeBasicAuth(username, authPw)); addHeader("Authorization", encodeBasicAuth(username, authPw));
return this;
} }
public GrafanaClientConfig addAuthenticator(Authenticator authenticator) { public GrafanaClientConfig addAuthenticator(Authenticator authenticator) {
authenticators.add(authenticator); authenticators.add(authenticator);
return this; return this;
@ -87,7 +98,9 @@ public class GrafanaClientConfig {
public HttpClient newClient() { public HttpClient newClient() {
HttpClient.Builder cb = HttpClient.newBuilder(); HttpClient.Builder cb = HttpClient.newBuilder();
cb.connectTimeout(Duration.ofMillis(timeoutms)); if (timeoutms > 0) {
cb.connectTimeout(Duration.ofMillis(timeoutms));
}
for (Authenticator authenticator : authenticators) { for (Authenticator authenticator : authenticators) {
cb.authenticator(authenticator); cb.authenticator(authenticator);
} }
@ -106,8 +119,11 @@ public class GrafanaClientConfig {
public HttpRequest.Builder newRequest(String path) { public HttpRequest.Builder newRequest(String path) {
URI requestUri = makeUri(path); URI requestUri = makeUri(path);
HttpRequest.Builder rqb = HttpRequest.newBuilder(requestUri); HttpRequest.Builder rqb = HttpRequest.newBuilder(requestUri);
rqb.timeout(Duration.ofMillis(timeoutms)); if (timeoutms > 0) {
rqb.timeout(Duration.ofMillis(timeoutms));
}
getHeaders().forEach(rqb::setHeader); getHeaders().forEach(rqb::setHeader);
return rqb; return rqb;
} }

View File

@ -1,4 +1,4 @@
package io.nosqlbench.engine.core.metrics; package io.nosqlbench.engine.clients.grafana;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
@ -10,15 +10,18 @@ import java.nio.file.Path;
import java.util.function.Supplier; import java.util.function.Supplier;
public class GrafanaKeyFileReader implements Supplier<String> { public class GrafanaKeyFileReader implements Supplier<String> {
private final static Logger logger = LogManager.getLogger("ANNOTATORS"); private final static Logger logger = LogManager.getLogger("ANNOTATORS" );
private final Path keyfilePath; private final Path keyfilePath;
public GrafanaKeyFileReader(Path path) {
this.keyfilePath = path;
}
public GrafanaKeyFileReader(String sourcePath) { public GrafanaKeyFileReader(String sourcePath) {
this.keyfilePath = Path.of(sourcePath); this.keyfilePath = Path.of(sourcePath);
} }
@Override @Override
public String get() { public String get() {
if (!Files.exists(keyfilePath)) { if (!Files.exists(keyfilePath)) {
@ -27,6 +30,7 @@ public class GrafanaKeyFileReader implements Supplier<String> {
} else { } else {
try { try {
String apikey = Files.readString(keyfilePath, StandardCharsets.UTF_8); String apikey = Files.readString(keyfilePath, StandardCharsets.UTF_8);
apikey = apikey.trim();
return apikey; return apikey;
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);

View File

@ -1,26 +1,29 @@
package io.nosqlbench.engine.core.metrics; package io.nosqlbench.engine.clients.grafana;
import io.nosqlbench.engine.clients.grafana.GrafanaClient;
import io.nosqlbench.engine.clients.grafana.GrafanaClientConfig;
import io.nosqlbench.engine.clients.grafana.transfer.GrafanaAnnotation; import io.nosqlbench.engine.clients.grafana.transfer.GrafanaAnnotation;
import io.nosqlbench.nb.annotations.Service; import io.nosqlbench.nb.annotations.Service;
import io.nosqlbench.nb.api.Environment;
import io.nosqlbench.nb.api.OnError;
import io.nosqlbench.nb.api.SystemId;
import io.nosqlbench.nb.api.annotations.Annotation; import io.nosqlbench.nb.api.annotations.Annotation;
import io.nosqlbench.nb.api.annotations.Annotator; import io.nosqlbench.nb.api.annotations.Annotator;
import io.nosqlbench.nb.api.config.*; import io.nosqlbench.nb.api.config.*;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
@Service(value = Annotator.class, selector = "grafana") @Service(value = Annotator.class, selector = "grafana" )
public class GrafanaMetricsAnnotator implements Annotator, ConfigAware { public class GrafanaMetricsAnnotator implements Annotator, ConfigAware {
private final static Logger logger = LogManager.getLogger("ANNOTATORS"); private final static Logger logger = LogManager.getLogger("ANNOTATORS" );
private final static Logger annotationsLog = LogManager.getLogger("ANNOTATIONS"); private final static Logger annotationsLog = LogManager.getLogger("ANNOTATIONS" );
private OnError onError = OnError.Warn; private OnError onError = OnError.Warn;
private GrafanaClient client; private GrafanaClient client;
@ -38,43 +41,43 @@ public class GrafanaMetricsAnnotator implements Annotator, ConfigAware {
ga.setTimeEnd(annotation.getEnd()); ga.setTimeEnd(annotation.getEnd());
annotation.getLabels().forEach((k, v) -> { annotation.getLabels().forEach((k, v) -> {
ga.getTags().put(k, v); ga.getTags().add(k + ":" + v);
}); });
ga.getTags().put("layer", annotation.getLayer().toString()); ga.getTags().add("layer:" + annotation.getLayer().toString());
Map<String, String> labels = annotation.getLabels(); Map<String, String> labels = annotation.getLabels();
Optional.ofNullable(labels.get("alertId")) Optional.ofNullable(labels.get("alertId" ))
.map(Integer::parseInt).ifPresent(ga::setAlertId); .map(Integer::parseInt).ifPresent(ga::setAlertId);
ga.setData(annotation.toString()); ga.setText(annotation.toString());
annotation.getSession(); annotation.getSession();
// Target // Target
Optional.ofNullable(labels.get("type")) Optional.ofNullable(labels.get("type" ))
.ifPresent(ga::setType); .ifPresent(ga::setType);
Optional.ofNullable(labels.get("id")).map(Integer::valueOf) Optional.ofNullable(labels.get("id" )).map(Integer::valueOf)
.ifPresent(ga::setId); .ifPresent(ga::setId);
Optional.ofNullable(labels.get("alertId")).map(Integer::valueOf) Optional.ofNullable(labels.get("alertId" )).map(Integer::valueOf)
.ifPresent(ga::setAlertId); .ifPresent(ga::setAlertId);
Optional.ofNullable(labels.get("dashboardId")).map(Integer::valueOf) Optional.ofNullable(labels.get("dashboardId" )).map(Integer::valueOf)
.ifPresent(ga::setDashboardId); .ifPresent(ga::setDashboardId);
Optional.ofNullable(labels.get("panelId")).map(Integer::valueOf) Optional.ofNullable(labels.get("panelId" )).map(Integer::valueOf)
.ifPresent(ga::setPanelId); .ifPresent(ga::setPanelId);
Optional.ofNullable(labels.get("userId")).map(Integer::valueOf) Optional.ofNullable(labels.get("userId" )).map(Integer::valueOf)
.ifPresent(ga::setUserId); .ifPresent(ga::setUserId);
Optional.ofNullable(labels.get("userName")) Optional.ofNullable(labels.get("userName" ))
.ifPresent(ga::setUserName); .ifPresent(ga::setUserName);
Optional.ofNullable(labels.get("metric")) Optional.ofNullable(labels.get("metric" ))
.ifPresent(ga::setMetric); .ifPresent(ga::setMetric);
// Details // Details
@ -107,60 +110,81 @@ public class GrafanaMetricsAnnotator implements Annotator, ConfigAware {
GrafanaClientConfig gc = new GrafanaClientConfig(); GrafanaClientConfig gc = new GrafanaClientConfig();
gc.setBaseUri(cfg.param("baseurl", String.class)); gc.setBaseUri(cfg.param("baseurl", String.class));
if (cfg.containsKey("tags")) { if (cfg.containsKey("tags" )) {
this.tags = ParamsParser.parse(cfg.param("tags", String.class), false); this.tags = ParamsParser.parse(cfg.param("tags", String.class), false);
} }
if (cfg.containsKey("apikeyfile")) { if (cfg.containsKey("username" )) {
String apikeyfile = cfg.paramEnv("apikeyfile", String.class); if (cfg.containsKey("password" )) {
AuthWrapper authHeaderSupplier = new AuthWrapper(
"Authorization",
new GrafanaKeyFileReader(apikeyfile),
s -> "Bearer " + s + ";"
);
gc.addHeaderSource(authHeaderSupplier);
}
if (cfg.containsKey("apikey")) {
gc.addHeaderSource(() -> Map.of("Authorization", "Bearer " + cfg.param("apikey", String.class)));
}
if (cfg.containsKey("username")) {
if (cfg.containsKey("password")) {
gc.basicAuth( gc.basicAuth(
cfg.param("username", String.class), cfg.param("username", String.class),
cfg.param("password", String.class) cfg.param("password", String.class)
); );
} else { } else {
gc.basicAuth(cfg.param("username", String.class), ""); gc.basicAuth(cfg.param("username", String.class), "" );
} }
} }
this.onError = OnError.valueOfName(cfg.get("onerror").toString()); Path keyfilePath = null;
if (cfg.containsKey("apikeyfile" )) {
String apikeyfile = cfg.paramEnv("apikeyfile", String.class);
keyfilePath = Path.of(apikeyfile);
} else if (cfg.containsKey("apikey" )) {
gc.addHeaderSource(() -> Map.of("Authorization", "Bearer " + cfg.param("apikey", String.class)));
} else {
Optional<String> apikeyLocation = Environment.INSTANCE.interpolate("$NBSTATEDIR/grafana_apikey" );
keyfilePath = apikeyLocation.map(Path::of).orElseThrow();
}
if (!Files.exists(keyfilePath)) {
logger.info("Auto-configuring grafana apikey." );
GrafanaClientConfig apiClientConf = gc.copy().basicAuth("admin", "admin" );
GrafanaClient apiClient = new GrafanaClient(apiClientConf);
try {
String nodeId = SystemId.getNodeId();
String keyName = "nosqlbench-" + nodeId + "-" + System.currentTimeMillis();
ApiToken apiToken = apiClient.createApiToken(keyName, "Admin", Long.MAX_VALUE);
Files.writeString(keyfilePath, apiToken.getKey());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
AuthWrapper authHeaderSupplier = new AuthWrapper(
"Authorization",
new GrafanaKeyFileReader(keyfilePath),
s -> "Bearer " + s
);
gc.addHeaderSource(authHeaderSupplier);
this.onError = OnError.valueOfName(cfg.get("onerror" ).toString());
this.client = new GrafanaClient(gc); this.client = new GrafanaClient(gc);
} }
@Override @Override
public ConfigModel getConfigModel() { public ConfigModel getConfigModel() {
return new MutableConfigModel(this) return new MutableConfigModel(this)
.required("baseurl", String.class, .required("baseurl", String.class,
"The base url of the grafana node, like http://localhost:3000/") "The base url of the grafana node, like http://localhost:3000/" )
.defaultto("apikeyfile", "$NBSTATEDIR/grafana_key", .defaultto("apikeyfile", "$NBSTATEDIR/grafana_apikey",
"The file that contains the api key, supersedes apikey") "The file that contains the api key, supersedes apikey" )
.optional("apikey", String.class, .optional("apikey", String.class,
"The api key to use, supersedes basic username and password") "The api key to use, supersedes basic username and password" )
.optional("username", String.class, .optional("username", String.class,
"The username to use for basic auth") "The username to use for basic auth" )
.optional("password", String.class, .optional("password", String.class,
"The password to use for basic auth") "The password to use for basic auth" )
.defaultto("tags", "source:nosqlbench", .defaultto("tags", "source:nosqlbench",
"The tags that identify the annotations, in k:v,... form") "The tags that identify the annotations, in k:v,... form" )
// .defaultto("onerror", OnError.Warn) // .defaultto("onerror", OnError.Warn)
.defaultto("onerror", "warn", .defaultto("onerror", "warn",
"What to do when an error occurs while posting an annotation") "What to do when an error occurs while posting an annotation" )
.defaultto("timeoutms", 5000, .defaultto("timeoutms", 5000,
"connect and transport timeout for the HTTP client") "connect and transport timeout for the HTTP client" )
.asReadOnly(); .asReadOnly();
} }

View File

@ -17,7 +17,8 @@ public class GrafanaAnnotation {
private String text; private String text;
private String metric; private String metric;
private String type; private String type;
private Map<String, String> tags = new LinkedHashMap<>(); private final List<String> tags = new ArrayList<>();
// private Map<String, String> tags = new LinkedHashMap<>();
private Object data; private Object data;
public Integer getId() { public Integer getId() {
@ -124,12 +125,22 @@ public class GrafanaAnnotation {
this.type = type; this.type = type;
} }
public Map<String, String> getTags() { public List<String> getTags() {
return tags; return tags;
} }
public void addTag(String tag) {
this.tags.add(tag);
}
public void setTags(List<String> tags) {
tags.forEach(this::addTag);
}
public void setTags(Map<String, String> tags) { public void setTags(Map<String, String> tags) {
this.tags = tags; tags.forEach((k, v) -> {
this.addTag(k + ":" + v);
});
} }
public Object getData() { public Object getData() {

View File

@ -31,10 +31,18 @@ public class ConfigReader extends LinkedHashMap<String, Object> {
Object o = get(name); Object o = get(name);
ConfigElement<?> elem = configModel.getElements().get(name); ConfigElement<?> elem = configModel.getElements().get(name);
if (elem == null) { if (elem == null) {
throw new RuntimeException("Invalid config element named '" + name + "'"); throw new RuntimeException("Invalid config element named '" + name + "'" );
} }
Class<T> type = (Class<T>) elem.getType(); Class<T> type = (Class<T>) elem.getType();
T typeCastedValue = type.cast(o); T typeCastedValue = type.cast(o);
return typeCastedValue; return typeCastedValue;
} }
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(this.configModel.getOf().getSimpleName()).append(":" );
sb.append(this.configModel.toString());
return sb.toString();
}
} }