Compare commits

..
7 Commits
Author SHA1 Message Date
Dave Marion af44743104 Update Accumulo 4 dependencies 2026-08-31 18:54:08 +00:00
Dave Marion 6272cd69eb Add runtime dependency for missing class with JDK 17 2026-01-08 17:46:37 +00:00
Dave Marion 8ca430d9c3 Updates for Accumulo 4.0.0-SNAPSHOT 2026-01-07 13:38:54 +00:00
Dave Marion 870bd7c2f9 Protect against period in tag name when setting iterator name 2024-07-01 18:18:26 +00:00
Dave Marion 2daedbf880 Modified API to accept timestamps in seconds
This change assumes that the timestamp is sent in seconds,
and multiplies it by 1000 to create ms, if the timestamp
is older than a year from the start of the Timely JVM. This
means that loading older data into Timely will require it
to be sent in seconds instead of milliseconds.
2024-06-28 19:04:20 +00:00
Dave Marion 27dff4dd42 Change version to 3.0.0-SNAPSHOT in all poms 2024-06-17 18:32:16 +00:00
Dave Marion 6d8c2e4c84 Bump Accumulo dependency to 4.0.0-SNAPSHOT
This commit changes the Accumulo and in-memory-accumulo versions.
The compaction strategies were removed as part of this commit and
need to be redone as the API have changed since Accumulo 2.1
2024-06-17 17:23:20 +00:00
262 changed files with 161163 additions and 37595 deletions

No files matched your search

-1
View File
@@ -8,5 +8,4 @@ logs/
server/pki/
client/src/main/python/setup.py
grafana/timely-app/yarn-error.log
grafana/timely-app/data/
+21 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>2.2.6-SNAPSHOT</version>
<version>3.0.0-SNAPSHOT</version>
</parent>
<artifactId>timely-analytics</artifactId>
<!--
@@ -64,6 +64,26 @@
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<compilerId>jdt</compilerId>
<source>11</source>
<target>11</target>
</configuration>
<dependencies>
<dependency>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-compiler-jdt</artifactId>
<version>1.4.0</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
+32 -51
View File
@@ -48,47 +48,40 @@ timely:
server:
ip: ${HOST_IP}
tcp-base-port: ${TCP_BASE_PORT}
num-tcp-pools: 1
tcp-buffer-size: 100
tcp-client-pool:
min-idle-per-key: 5
max-idle-per-key: 5
max-total-per-key: 5
min-idle-per-key: 0
max-idle-per-key: 40
max-total-per-key: 40
max-total: -1
lifo: false
fairness: true
max-wait-millis: 60000
min-evictable-idle-time-millis: -1
lifo: true
fairness: false
max-wait-millis: -1
min-evictable-idle-time-millis: 1800000
evictor-shutdown-timeout-millis: 10000
soft-min-evictable-idle-time-millis: 300000
soft-min-evictable-idle-time-millis: -1
num-tests-per-eviction-run: 3
time-between-eviction-runs-millis: 30000
time-between-eviction-runs-millis: -1
block-when-exhausted: true
remove-abandoned-on-borrow: false
remove-abandoned-on-maintenance: true
remove-abandoned-timeout-millis: 600000
remove-abandoned-logging: true
jmx-enabled: true
test-on-borrow: false
test-on-return: false
udp-base-port: ${UDP_BASE_PORT}
udp-client-pool:
min-idle-per-key: 5
max-idle-per-key: 5
max-total-per-key: 5
min-idle-per-key: 0
max-idle-per-key: 40
max-total-per-key: 40
max-total: -1
lifo: false
fairness: true
max-wait-millis: 60000
min-evictable-idle-time-millis: -1
lifo: true
fairness: false
max-wait-millis: -1
min-evictable-idle-time-millis: 1800000
evictor-shutdown-timeout-millis: 10000
soft-min-evictable-idle-time-millis: 300000
soft-min-evictable-idle-time-millis: -1
num-tests-per-eviction-run: 3
time-between-eviction-runs-millis: 30000
time-between-eviction-runs-millis: -1
block-when-exhausted: true
remove-abandoned-on-borrow: false
remove-abandoned-on-maintenance: true
remove-abandoned-timeout-millis: 600000
remove-abandoned-logging: true
jmx-enabled: true
test-on-borrow: false
test-on-return: false
@@ -98,10 +91,6 @@ timely:
base-port: ${HTTP_BASE_PORT}
redirect-path: /secure-me
strict-transport-max-age: 604800
connection-request-timeout: 5000
connect-timeout: 5000
socketTimeout: 300000
request-timeout: 300000
cors:
allow-any-origin: false
allow-null-origin: false
@@ -110,23 +99,19 @@ timely:
allowed-methods: DELETE,GET,HEAD,OPTIONS,PUT,POST
allowed-headers: content-type
httpClientPool:
min-idle-per-key: 10
min-idle-per-key: 0
max-idle-per-key: 40
max-total-per-key: 40
max-total: -1
lifo: false
fairness: true
max-wait-millis: 60000
min-evictable-idle-time-millis: -1
lifo: true
fairness: false
max-wait-millis: -1
min-evictable-idle-time-millis: 1800000
evictor-shutdown-timeout-millis: 10000
soft-min-evictable-idle-time-millis: 300000
soft-min-evictable-idle-time-millis: -1
num-tests-per-eviction-run: 3
time-between-eviction-runs-millis: 30000
time-between-eviction-runs-millis: -1
block-when-exhausted: true
remove-abandoned-on-borrow: false
remove-abandoned-on-maintenance: true
remove-abandoned-timeout-millis: 600000
remove-abandoned-logging: true
jmx-enabled: true
test-on-borrow: false
test-on-return: false
@@ -145,19 +130,15 @@ timely:
max-idle-per-key: 40
max-total-per-key: 40
max-total: -1
lifo: false
fairness: true
max-wait-millis: 60000
min-evictable-idle-time-millis: -1
lifo: true
fairness: false
max-wait-millis: -1
min-evictable-idle-time-millis: 1800000
evictor-shutdown-timeout-millis: 10000
soft-min-evictable-idle-time-millis: 300000
soft-min-evictable-idle-time-millis: -1
num-tests-per-eviction-run: 3
time-between-eviction-runs-millis: 30000
time-between-eviction-runs-millis: -1
block-when-exhausted: true
remove-abandoned-on-borrow: false
remove-abandoned-on-maintenance: true
remove-abandoned-timeout-millis: 600000
remove-abandoned-logging: true
jmx-enabled: true
test-on-borrow: false
test-on-return: false
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>2.2.6-SNAPSHOT</version>
<version>3.0.0-SNAPSHOT</version>
</parent>
<artifactId>timely-balancer</artifactId>
<description>Timely Balancer</description>
+7
View File
@@ -46,5 +46,12 @@
<include>*</include>
</includes>
</fileSet>
<fileSet>
<directory>${project.basedir}/src/main/resources/webapp</directory>
<outputDirectory>bin/webapp</outputDirectory>
<includes>
<include>*</include>
</includes>
</fileSet>
</fileSets>
</assembly>
@@ -83,10 +83,8 @@ import timely.common.configuration.SecurityProperties;
import timely.common.configuration.SslClientProperties;
import timely.common.configuration.SslServerProperties;
import timely.common.configuration.ZookeeperProperties;
import timely.netty.http.HttpStrictTransportSecurityHandler;
import timely.netty.http.HttpVersionRequestHandler;
import timely.netty.http.NonSslForbiddenHandler;
import timely.netty.http.NotFoundHandler;
import timely.netty.http.HttpStaticFileServerHandler;
import timely.netty.http.NonSslRedirectHandler;
import timely.netty.http.TimelyExceptionHandler;
import timely.netty.http.auth.X509LoginRequestHandler;
import timely.netty.tcp.MetricsBufferDecoder;
@@ -219,12 +217,16 @@ public class Balancer {
}
log.info("Using channel class {}", channelClass.getSimpleName());
List<TcpClientPool> tcpClientPools = new ArrayList<>();
for (int x = 0; x < balancerServerProperties.getNumTcpPools(); x++) {
tcpClientPools.add(new TcpClientPool(balancerServerProperties));
}
final ServerBootstrap tcpServer = new ServerBootstrap();
tcpServer.group(tcpBossGroup, tcpWorkerGroup);
tcpServer.channel(channelClass);
tcpServer.handler(new LoggingHandler());
TcpClientPool tcpClientPool = new TcpClientPool(balancerServerProperties);
tcpServer.childHandler(setupTcpChannel(metricResolver, tcpClientPool));
tcpServer.childHandler(setupTcpChannel(metricResolver, tcpClientPools));
tcpServer.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
tcpServer.option(ChannelOption.SO_BACKLOG, 128);
tcpServer.childOption(ChannelOption.SO_KEEPALIVE, true);
@@ -419,9 +421,8 @@ public class Balancer {
@Override
protected void initChannel(SocketChannel ch) {
ch.pipeline().addLast("nonsslforbidden", new NonSslForbiddenHandler(sslCtx));
ch.pipeline().addLast("ssl", new NonSslRedirectHandler(balancerHttpProperties, sslCtx));
ch.pipeline().addLast("encoder", new HttpResponseEncoder());
ch.pipeline().addLast("strict", new HttpStrictTransportSecurityHandler(balancerHttpProperties));
ch.pipeline().addLast("decoder", new HttpRequestDecoder(4096, 32768, 8192, true, 128));
ch.pipeline().addLast("compressor", new HttpContentCompressor());
ch.pipeline().addLast("decompressor", new HttpContentDecompressor());
@@ -429,10 +430,10 @@ public class Balancer {
ch.pipeline().addLast("chunker", new ChunkedWriteHandler());
ch.pipeline().addLast("queryDecoder",
new timely.netty.http.HttpRequestDecoder(authenticationService, securityProperties, balancerHttpProperties));
ch.pipeline().addLast("notfound", new NotFoundHandler());
ch.pipeline().addLast("fileServer",
new HttpStaticFileServerHandler().setIgnoreSslHandshakeErrors(sslServerProperties.isIgnoreSslHandshakeErrors()));
ch.pipeline().addLast("login", new X509LoginRequestHandler(authenticationService, securityProperties, balancerHttpProperties));
ch.pipeline().addLast("version", new HttpVersionRequestHandler());
ch.pipeline().addLast("httpRelay", new HttpRelayHandler(balancerHttpProperties, metricResolver, httpClientPool));
ch.pipeline().addLast("httpRelay", new HttpRelayHandler(metricResolver, httpClientPool));
ch.pipeline().addLast("error", new TimelyExceptionHandler().setIgnoreSslHandshakeErrors(sslServerProperties.isIgnoreSslHandshakeErrors()));
}
};
@@ -453,7 +454,7 @@ public class Balancer {
};
}
protected ChannelHandler setupTcpChannel(MetricResolver metricResolver, TcpClientPool tcpClientPool) {
protected ChannelHandler setupTcpChannel(MetricResolver metricResolver, List<TcpClientPool> tcpClientPools) {
return new ChannelInitializer<SocketChannel>() {
@Override
@@ -461,7 +462,7 @@ public class Balancer {
ch.pipeline().addLast("buffer", new MetricsBufferDecoder());
ch.pipeline().addLast("frame", new DelimiterBasedFrameDecoder(65536, true, Delimiters.lineDelimiter()));
ch.pipeline().addLast("putDecoder", new TcpDecoder());
ch.pipeline().addLast("tcpRelayHandler", new TcpRelayHandler(metricResolver, tcpClientPool));
ch.pipeline().addLast("tcpRelayHandler", new TcpRelayHandler(metricResolver, tcpClientPools));
ch.pipeline().addLast("versionHandler", new TcpVersionHandler());
}
};
@@ -2,6 +2,7 @@ package timely.balancer.configuration;
import javax.validation.Valid;
import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
@@ -13,48 +14,11 @@ import timely.common.configuration.HttpProperties;
@ConfigurationProperties(prefix = "timely.balancer.http")
public class BalancerHttpProperties extends HttpProperties {
private int connectionRequestTimeout = 5000;
private int connectTimeout = 5000;
private int socketTimeout = 300000;
private int requestTimeout = 300000;
@Valid
@NestedConfigurationProperty
private GenericKeyedObjectPoolConfiguration<CloseableHttpClient> httpClientPool = new GenericKeyedObjectPoolConfiguration<>();
private GenericKeyedObjectPoolConfig<CloseableHttpClient> httpClientPool = new GenericKeyedObjectPoolConfig<>();
public GenericKeyedObjectPoolConfiguration<CloseableHttpClient> getHttpClientPool() {
public GenericKeyedObjectPoolConfig<CloseableHttpClient> getHttpClientPool() {
return httpClientPool;
}
public void setConnectionRequestTimeout(final int connectionRequestTimeout) {
this.connectionRequestTimeout = connectionRequestTimeout;
}
public int getConnectionRequestTimeout() {
return connectionRequestTimeout;
}
public void setConnectTimeout(final int connectTimeout) {
this.connectTimeout = connectTimeout;
}
public int getConnectTimeout() {
return connectTimeout;
}
public void setSocketTimeout(final int socketTimeout) {
this.socketTimeout = socketTimeout;
}
public int getSocketTimeout() {
return socketTimeout;
}
public void setRequestTimeout(int requestTimeout) {
this.requestTimeout = requestTimeout;
}
public int getRequestTimeout() {
return requestTimeout;
}
}
@@ -2,6 +2,7 @@ package timely.balancer.configuration;
import javax.validation.Valid;
import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.cloud.context.config.annotation.RefreshScope;
@@ -16,22 +17,32 @@ public class BalancerServerProperties extends ServerProperties {
@Valid
@NestedConfigurationProperty
private GenericKeyedObjectPoolConfiguration<UdpClient> udpClientPool = new GenericKeyedObjectPoolConfiguration<>();
private GenericKeyedObjectPoolConfig<UdpClient> udpClientPool = new GenericKeyedObjectPoolConfig<>();
@Valid
@NestedConfigurationProperty
private GenericKeyedObjectPoolConfiguration<TcpClient> tcpClientPool = new GenericKeyedObjectPoolConfiguration<>();
private GenericKeyedObjectPoolConfig<TcpClient> tcpClientPool = new GenericKeyedObjectPoolConfig<>();
private int numTcpPools = 1;
private int tcpBufferSize = -1;
public GenericKeyedObjectPoolConfiguration<UdpClient> getUdpClientPool() {
public GenericKeyedObjectPoolConfig<UdpClient> getUdpClientPool() {
return udpClientPool;
}
public GenericKeyedObjectPoolConfiguration<TcpClient> getTcpClientPool() {
public GenericKeyedObjectPoolConfig<TcpClient> getTcpClientPool() {
return tcpClientPool;
}
public int getNumTcpPools() {
return numTcpPools;
}
public void setNumTcpPools(int numTcpPools) {
this.numTcpPools = numTcpPools;
}
public int getTcpBufferSize() {
return tcpBufferSize;
}
@@ -2,6 +2,7 @@ package timely.balancer.configuration;
import javax.validation.Valid;
import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.cloud.context.config.annotation.RefreshScope;
@@ -15,9 +16,9 @@ public class BalancerWebsocketProperties extends WebsocketProperties {
@Valid
@NestedConfigurationProperty
private GenericKeyedObjectPoolConfiguration<WebSocketSubscriptionClient> wsClientPool = new GenericKeyedObjectPoolConfiguration<>();
private GenericKeyedObjectPoolConfig<WebSocketSubscriptionClient> wsClientPool = new GenericKeyedObjectPoolConfig<>();
public GenericKeyedObjectPoolConfiguration<WebSocketSubscriptionClient> getWsClientPool() {
public GenericKeyedObjectPoolConfig<WebSocketSubscriptionClient> getWsClientPool() {
return wsClientPool;
}
@@ -1,83 +0,0 @@
package timely.balancer.configuration;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import org.apache.commons.pool2.impl.AbandonedConfig;
import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
public class GenericKeyedObjectPoolConfiguration<T> extends GenericKeyedObjectPoolConfig<T> {
private Duration removeAbandonedTimeout = null;
private boolean removeAbandonedLogging = false;
private boolean removeAbandonedOnBorrow = false;
private boolean removeAbandonedOnMaintenance = false;
public void setMaxWaitMillis(long maxWaitMillis) {
super.setMaxWait(Duration.ofMillis(maxWaitMillis));
}
public void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) {
super.setMinEvictableIdleTime(Duration.ofMillis(minEvictableIdleTimeMillis));
}
public void setEvictorShutdownTimeoutMillis(long evictorShutdownTimeoutMillis) {
super.setEvictorShutdownTimeout(Duration.ofMillis(evictorShutdownTimeoutMillis));
}
public void setSoftMinEvictableIdleTimeMillis(long softMinEvictableIdleTimeMillis) {
super.setSoftMinEvictableIdleTime(Duration.ofMillis(softMinEvictableIdleTimeMillis));
}
public void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) {
super.setTimeBetweenEvictionRuns(Duration.ofMillis(timeBetweenEvictionRunsMillis));
}
public void setRemoveAbandonedTimeoutMillis(long removeAbandonedTimeoutMillis) {
this.removeAbandonedTimeout = Duration.ofMillis(removeAbandonedTimeoutMillis);
}
public Duration getRemoveAbandonedTimeout() {
return removeAbandonedTimeout;
}
public void setRemoveAbandonedOnBorrow(boolean removeAbandonedOnBorrow) {
this.removeAbandonedOnBorrow = removeAbandonedOnBorrow;
}
public boolean getRemoveAbandonedOnBorrow() {
return removeAbandonedOnBorrow;
}
public void setRemoveAbandonedOnMaintenance(boolean removeAbandonedOnMaintenance) {
this.removeAbandonedOnMaintenance = removeAbandonedOnMaintenance;
}
public boolean getRemoveAbandonedOnMaintenance() {
return removeAbandonedOnMaintenance;
}
public void setRemoveAbandonedLogging(boolean removeAbandonedLogging) {
this.removeAbandonedLogging = removeAbandonedLogging;
}
public boolean getRemoveAbandonedLogging() {
return removeAbandonedLogging;
}
public AbandonedConfig getAbandonedConfig() {
AbandonedConfig abandonedConfig = null;
if (removeAbandonedTimeout != null) {
abandonedConfig = new AbandonedConfig();
abandonedConfig.setRemoveAbandonedTimeout(removeAbandonedTimeout);
if (removeAbandonedLogging) {
abandonedConfig.setLogAbandoned(removeAbandonedLogging);
abandonedConfig.setLogWriter(new PrintWriter(System.out, true, StandardCharsets.UTF_8));
}
abandonedConfig.setRemoveAbandonedOnBorrow(removeAbandonedOnBorrow);
abandonedConfig.setRemoveAbandonedOnMaintenance(removeAbandonedOnMaintenance);
}
return abandonedConfig;
}
}
@@ -11,7 +11,7 @@ import timely.common.configuration.SslClientProperties;
public class HttpClientPool extends GenericKeyedObjectPool<TimelyBalancedHost,CloseableHttpClient> {
public HttpClientPool(BalancerHttpProperties config, SSLContext sslContext, SslClientProperties sslClientProperties) {
super(new HttpClientFactory(sslContext, sslClientProperties), config.getHttpClientPool(), config.getHttpClientPool().getAbandonedConfig());
public HttpClientPool(BalancerHttpProperties http, SSLContext sslContext, SslClientProperties sslClientProperties) {
super(new HttpClientFactory(sslContext, sslClientProperties), http.getHttpClientPool());
}
}
@@ -8,7 +8,7 @@ import timely.client.tcp.TcpClient;
public class TcpClientPool extends GenericKeyedObjectPool<TimelyBalancedHost,TcpClient> {
public TcpClientPool(BalancerServerProperties config) {
super(new TcpClientFactory(config.getTcpBufferSize()), config.getTcpClientPool(), config.getTcpClientPool().getAbandonedConfig());
public TcpClientPool(BalancerServerProperties balancerServerProperties) {
super(new TcpClientFactory(balancerServerProperties.getTcpBufferSize()), balancerServerProperties.getTcpClientPool());
}
}
@@ -8,7 +8,7 @@ import timely.client.udp.UdpClient;
public class UdpClientPool extends GenericKeyedObjectPool<TimelyBalancedHost,UdpClient> {
public UdpClientPool(BalancerServerProperties config) {
super(new UdpClientFactory(), config.getUdpClientPool(), config.getUdpClientPool().getAbandonedConfig());
public UdpClientPool(BalancerServerProperties serverProperties) {
super(new UdpClientFactory(), serverProperties.getUdpClientPool());
}
}
@@ -12,9 +12,9 @@ import timely.common.configuration.SslClientProperties;
public class WsClientPool extends GenericKeyedObjectPool<TimelyBalancedHost,WebSocketSubscriptionClient> {
public WsClientPool(BalancerProperties balancerProperties, BalancerWebsocketProperties config, SslClientProperties sslClientProperties,
public WsClientPool(BalancerProperties balancerProperties, BalancerWebsocketProperties balancerWebsocketProperties, SslClientProperties sslClientProperties,
SSLContext sslContext) {
super(new WsClientFactory(balancerProperties, config, sslClientProperties, sslContext), config.getWsClientPool(),
config.getWsClientPool().getAbandonedConfig());
super(new WsClientFactory(balancerProperties, balancerWebsocketProperties, sslClientProperties, sslContext),
balancerWebsocketProperties.getWsClientPool());
}
}
@@ -3,7 +3,6 @@ package timely.balancer.netty.http;
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
@@ -11,19 +10,12 @@ import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.apache.http.Header;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.message.BasicHeader;
@@ -31,7 +23,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StreamUtils;
import com.google.common.base.Throwables;
import com.google.common.collect.Multimap;
import io.netty.buffer.Unpooled;
@@ -43,6 +34,7 @@ import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.util.ReferenceCountUtil;
import timely.api.request.AuthenticatedRequest;
import timely.api.request.HttpRequest;
import timely.api.request.MetricRequest;
@@ -52,7 +44,6 @@ import timely.auth.TimelyAuthenticationToken;
import timely.auth.util.HttpHeaderUtils;
import timely.auth.util.ProxiedEntityUtils;
import timely.balancer.MetricResolver;
import timely.balancer.configuration.BalancerHttpProperties;
import timely.balancer.connection.TimelyBalancedHost;
import timely.balancer.connection.http.HttpClientPool;
import timely.netty.http.TimelyHttpHandler;
@@ -60,13 +51,10 @@ import timely.netty.http.TimelyHttpHandler;
public class HttpRelayHandler extends SimpleChannelInboundHandler<HttpRequest> implements TimelyHttpHandler {
private static final Logger log = LoggerFactory.getLogger(HttpRelayHandler.class);
private BalancerHttpProperties balancerHttpProperties;
private final HttpClientPool httpClientPool;
private MetricResolver metricResolver;
private ExecutorService requestExecutor = Executors.newCachedThreadPool();
public HttpRelayHandler(BalancerHttpProperties balancerHttpProperties, MetricResolver metricResolver, HttpClientPool httpClientPool) {
this.balancerHttpProperties = balancerHttpProperties;
public HttpRelayHandler(MetricResolver metricResolver, HttpClientPool httpClientPool) {
this.metricResolver = metricResolver;
this.httpClientPool = httpClientPool;
}
@@ -79,7 +67,6 @@ public class HttpRelayHandler extends SimpleChannelInboundHandler<HttpRequest> i
CloseableHttpResponse relayedResponse = null;
FullHttpRequest request = null;
try {
String relayURI = "";
try {
request = msg.getHttpRequest();
String originalURI = request.uri();
@@ -122,39 +109,45 @@ public class HttpRelayHandler extends SimpleChannelInboundHandler<HttpRequest> i
}
Header[] headerArray = new Header[relayedHeaderList.size()];
relayedHeaderList.toArray(headerArray);
relayURI = "https://" + k.getHost() + ":" + k.getHttpPort() + originalURI;
HttpRequestBase relayedRequest = null;
String relayURI = "https://" + k.getHost() + ":" + k.getHttpPort() + originalURI;
if (request.method().equals(HttpMethod.GET)) {
relayedRequest = new HttpGet(relayURI);
HttpUriRequest relayedRequest = new HttpGet(relayURI);
if (headerArray.length > 0) {
relayedRequest.setHeaders(headerArray);
}
relayedResponse = client.execute(relayedRequest);
} else if (request.method().equals(HttpMethod.POST)) {
relayedRequest = new HttpPost(relayURI);
HttpPost relayedRequest = new HttpPost(relayURI);
String content = request.content().toString(StandardCharsets.UTF_8);
StringEntity entity = new StringEntity(content);
((HttpPost) relayedRequest).setEntity(entity);
} else {
throw new IllegalArgumentException("Unsupported HTTP method: " + request.method());
relayedRequest.setEntity(entity);
if (headerArray.length > 0) {
relayedRequest.setHeaders(headerArray);
}
relayedResponse = client.execute(relayedRequest);
}
if (headerArray.length > 0) {
relayedRequest.setHeaders(headerArray);
}
relayedResponse = executeHttpRequest(client, relayedRequest, balancerHttpProperties.getRequestTimeout());
} catch (IOException e) {
String message = String.format("%s calling %s", e.getMessage() == null ? "" : e.getMessage(), relayURI);
log.error(message, e);
this.sendHttpError(ctx, new TimelyException(HttpResponseStatus.GATEWAY_TIMEOUT.code(), e.getMessage(), e.getLocalizedMessage(), e));
return;
} catch (Exception e) {
String message = e.getMessage();
if (message != null && message.contains("No matching tags")) {
log.trace(message);
if (message == null) {
log.error("", e);
} else {
message = String.format("%s calling %s", e.getMessage() == null ? "" : e.getMessage(), relayURI);
log.error(message, e);
if (message.contains("No matching tags")) {
log.trace(message);
} else {
log.error(message, e);
}
}
this.sendHttpError(ctx, new TimelyException(HttpResponseStatus.INTERNAL_SERVER_ERROR.code(), e.getMessage(), e.getLocalizedMessage(), e));
return;
} finally {
if (request != null) {
ReferenceCountUtil.release(request);
}
}
FullHttpResponse response;
FullHttpResponse response = null;
if (relayedResponse == null) {
this.sendHttpError(ctx, new TimelyException(HttpResponseStatus.METHOD_NOT_ALLOWED.code(), "Method not allowed", "Method not allowed"));
} else {
@@ -169,8 +162,6 @@ public class HttpRelayHandler extends SimpleChannelInboundHandler<HttpRequest> i
response.headers().set(CONTENT_LENGTH, response.content().readableBytes());
sendResponse(ctx, response);
}
} catch (Exception e) {
log.error(e.getMessage(), e);
} finally {
if (relayedResponse != null) {
relayedResponse.close();
@@ -209,22 +200,4 @@ public class HttpRelayHandler extends SimpleChannelInboundHandler<HttpRequest> i
}
return s;
}
private CloseableHttpResponse executeHttpRequest(CloseableHttpClient httpClient, HttpRequestBase relayedRequest, long timeout) throws Exception {
try {
RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
requestConfigBuilder.setConnectionRequestTimeout(this.balancerHttpProperties.getConnectionRequestTimeout());
requestConfigBuilder.setConnectTimeout(this.balancerHttpProperties.getConnectTimeout());
requestConfigBuilder.setSocketTimeout(this.balancerHttpProperties.getSocketTimeout());
RequestConfig requestConfig = requestConfigBuilder.build();
relayedRequest.setConfig(requestConfig);
Future<CloseableHttpResponse> f = requestExecutor.submit(() -> httpClient.execute(relayedRequest));
return f.get(timeout, TimeUnit.MILLISECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
Throwable cause = Throwables.getRootCause(e);
throw new IOException(cause.getMessage(), cause);
} catch (Exception e) {
throw e;
}
}
}
@@ -1,6 +1,8 @@
package timely.balancer.netty.tcp;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
@@ -25,11 +27,16 @@ public class TcpRelayHandler extends SimpleChannelInboundHandler<TcpRequest> {
private static final String ERR_MSG = "Error storing put metric: ";
private MetricResolver metricResolver;
private TcpClientPool tcpClientPool;
private List<TcpClientPool> tcpClientPools;
public TcpRelayHandler(MetricResolver metricResolver, TcpClientPool tcpClientPool) {
public TcpRelayHandler(MetricResolver metricResolver, List<TcpClientPool> tcpClientPools) {
this.metricResolver = metricResolver;
this.tcpClientPool = tcpClientPool;
this.tcpClientPools = tcpClientPools;
}
private TcpClientPool tcpClientPool() {
int x = Math.toIntExact(Thread.currentThread().getId() % tcpClientPools.size());
return tcpClientPools.get(x);
}
@Override
@@ -51,17 +58,15 @@ public class TcpRelayHandler extends SimpleChannelInboundHandler<TcpRequest> {
keyClientPair.getRight().write(line + "\n");
} finally {
if (keyClientPair != null && keyClientPair.getLeft() != null && keyClientPair.getRight() != null) {
tcpClientPool.returnObject(keyClientPair.getLeft(), keyClientPair.getRight());
tcpClientPool().returnObject(keyClientPair.getLeft(), keyClientPair.getRight());
}
}
} catch (Exception e) {
} catch (IOException e) {
log.error(LOG_ERR_MSG, msg, e);
ChannelFuture cf = ctx.writeAndFlush(Unpooled.copiedBuffer((ERR_MSG + e.getMessage() + "\n").getBytes(StandardCharsets.UTF_8)));
if (!cf.isSuccess()) {
Throwable t = cf.cause();
String message = (t == null) ? "" : t.getMessage();
log.error(String.format(Constants.ERR_WRITING_RESPONSE, message), cf.cause());
log.error(Constants.ERR_WRITING_RESPONSE, cf.cause());
}
}
}
@@ -73,7 +78,7 @@ public class TcpRelayHandler extends SimpleChannelInboundHandler<TcpRequest> {
while (client == null) {
try {
k = (metricRequest == true) ? metricResolver.getHostPortKeyIngest(metric) : metricResolver.getHostPortKey(metric);
client = tcpClientPool.borrowObject(k);
client = tcpClientPool().borrowObject(k);
} catch (Exception e1) {
failures++;
client = null;
@@ -53,9 +53,7 @@ public class UdpRelayHandler extends SimpleChannelInboundHandler<UdpRequest> {
log.error(LOG_ERR_MSG, msg, e);
ChannelFuture cf = ctx.writeAndFlush(Unpooled.copiedBuffer((ERR_MSG + e.getMessage() + "\n").getBytes(StandardCharsets.UTF_8)));
if (!cf.isSuccess()) {
Throwable t = cf.cause();
String message = (t == null) ? "" : t.getMessage();
log.error(String.format(Constants.ERR_WRITING_RESPONSE, message), cf.cause());
log.error(Constants.ERR_WRITING_RESPONSE, cf.cause());
}
}
}
@@ -1,95 +0,0 @@
package timely.balancer.integration;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import java.net.HttpURLConnection;
import java.net.URL;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import timely.balancer.configuration.BalancerHttpProperties;
import timely.netty.http.HttpStrictTransportSecurityHandler;
import timely.test.IntegrationTest;
import timely.test.TimelyTestRule;
@Category(IntegrationTest.class)
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class HTTPStrictTransportSecurityIT {
@Autowired
@Rule
public TimelyTestRule testRule;
@Autowired
private BalancerHttpProperties httpProperties;
@Autowired
protected SSLContext clientSslContext;
private String baseHttpUrl;
private String baseHttpsUrl;
private String HSTS_HEADER_VALUE;
@Before
public void setup() {
baseHttpUrl = "http://" + httpProperties.getHost() + ":" + httpProperties.getPort();
baseHttpsUrl = "https://" + httpProperties.getHost() + ":" + httpProperties.getPort();
HSTS_HEADER_VALUE = String.format("max-age=%d; includeSubDomains; preload", httpProperties.getStrictTransportMaxAge());
}
protected SSLSocketFactory getSSLSocketFactory() throws Exception {
return clientSslContext.getSocketFactory();
}
protected HttpsURLConnection getUrlConnection(URL url) throws Exception {
HttpsURLConnection.setDefaultSSLSocketFactory(getSSLSocketFactory());
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
con.setHostnameVerifier((host, session) -> true);
return con;
}
@Test
public void testHttpRequest() throws Exception {
URL url = new URL(baseHttpUrl + "/api/metrics");
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET");
int responseCode = con.getResponseCode();
assertEquals(403, responseCode);
// should not have HSTS header on HTTP response
assertNull(con.getHeaderField(HttpStrictTransportSecurityHandler.HSTS_HEADER_NAME));
con.disconnect();
}
@Test
public void testHttpsRequestHSTS() throws Exception {
URL url = new URL(baseHttpsUrl + "/version");
HttpsURLConnection con = getUrlConnection(url);
int responseCode = con.getResponseCode();
assertEquals(200, responseCode);
assertEquals(HSTS_HEADER_VALUE, con.getHeaderField(HttpStrictTransportSecurityHandler.HSTS_HEADER_NAME));
con.disconnect();
}
@Test
public void testHttpsRequestNotFound() throws Exception {
URL url = new URL(baseHttpsUrl);
HttpsURLConnection con = getUrlConnection(url);
int responseCode = con.getResponseCode();
assertEquals(404, responseCode);
assertEquals(HSTS_HEADER_VALUE, con.getHeaderField(HttpStrictTransportSecurityHandler.HSTS_HEADER_NAME));
con.disconnect();
}
}
@@ -62,61 +62,50 @@ timely:
assignment-file: file://${java.io.tmpdir}/timely/balancer/assignments.csv
server:
ip: 127.0.0.1
tcp-base-port: 9242
tcp-base-port: 4242
num-tcp-pools: 1
tcp-buffer-size: 100
tcp-client-pool:
min-idle-per-key: 5
max-idle-per-key: 5
max-total-per-key: 5
min-idle-per-key: 0
max-idle-per-key: 40
max-total-per-key: 40
max-total: -1
lifo: false
fairness: true
max-wait-millis: 60000
min-evictable-idle-time-millis: -1
lifo: true
fairness: false
max-wait-millis: -1
min-evictable-idle-time-millis: 1800000
evictor-shutdown-timeout-millis: 10000
soft-min-evictable-idle-time-millis: 300000
soft-min-evictable-idle-time-millis: -1
num-tests-per-eviction-run: 3
time-between-eviction-runs-millis: 30000
time-between-eviction-runs-millis: -1
block-when-exhausted: true
remove-abandoned-on-borrow: false
remove-abandoned-on-maintenance: true
remove-abandoned-timeout-millis: 600000
remove-abandoned-logging: true
jmx-enabled: true
test-on-borrow: false
test-on-return: false
udp-base-port: 9245
udp-base-port: 4245
udp-client-pool:
min-idle-per-key: 5
max-idle-per-key: 5
max-total-per-key: 5
min-idle-per-key: 0
max-idle-per-key: 40
max-total-per-key: 40
max-total: -1
lifo: false
fairness: true
max-wait-millis: 60000
min-evictable-idle-time-millis: -1
lifo: true
fairness: false
max-wait-millis: -1
min-evictable-idle-time-millis: 1800000
evictor-shutdown-timeout-millis: 10000
soft-min-evictable-idle-time-millis: 300000
soft-min-evictable-idle-time-millis: -1
num-tests-per-eviction-run: 3
time-between-eviction-runs-millis: 30000
time-between-eviction-runs-millis: -1
block-when-exhausted: true
remove-abandoned-on-borrow: false
remove-abandoned-on-maintenance: true
remove-abandoned-timeout-millis: 600000
remove-abandoned-logging: true
jmx-enabled: true
test-on-borrow: false
test-on-return: false
http:
host: localhost
ip: 127.0.0.1
base-port: 9243
base-port: 4243
redirect-path: /secure-me
strict-transport-max-age: 604800
connection-request-timeout: 5000
connect-timeout: 5000
socket-timeout: 300000
request-timeout: 300000
cors:
allow-any-origin: false
allow-null-origin: false
@@ -125,29 +114,25 @@ timely:
allowed-methods: DELETE,GET,HEAD,OPTIONS,PUT,POST
allowed-headers: content-type
http-client-pool:
min-idle-per-key: 10
min-idle-per-key: 0
max-idle-per-key: 40
max-total-per-key: 40
max-total: -1
lifo: false
fairness: true
max-wait-millis: 60000
min-evictable-idle-time-millis: -1
lifo: true
fairness: false
max-wait-millis: -1
min-evictable-idle-time-millis: 1800000
evictor-shutdown-timeout-millis: 10000
soft-min-evictable-idle-time-millis: 300000
soft-min-evictable-idle-time-millis: -1
num-tests-per-eviction-run: 3
time-between-eviction-runs-millis: 30000
time-between-eviction-runs-millis: -1
block-when-exhausted: true
remove-abandoned-on-borrow: false
remove-abandoned-on-maintenance: true
remove-abandoned-timeout-millis: 600000
remove-abandoned-logging: true
jmx-enabled: true
test-on-borrow: false
test-on-return: false
websocket:
ip: 127.0.0.1
base-port: 9244
base-port: 5244
timeout: 60
subscription-lag: 120
scanner-batch-size: 10000
@@ -160,19 +145,15 @@ timely:
max-idle-per-key: 40
max-total-per-key: 40
max-total: -1
lifo: false
fairness: true
max-wait-millis: 60000
min-evictable-idle-time-millis: -1
lifo: true
fairness: false
max-wait-millis: -1
min-evictable-idle-time-millis: 1800000
evictor-shutdown-timeout-millis: 10000
soft-min-evictable-idle-time-millis: 300000
soft-min-evictable-idle-time-millis: -1
num-tests-per-eviction-run: 3
time-between-eviction-runs-millis: 30000
time-between-eviction-runs-millis: -1
block-when-exhausted: true
remove-abandoned-on-borrow: false
remove-abandoned-on-maintenance: true
remove-abandoned-timeout-millis: 600000
remove-abandoned-logging: true
jmx-enabled: true
test-on-borrow: false
test-on-return: false
+2 -2
View File
@@ -4,13 +4,13 @@
<parent>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>2.2.6-SNAPSHOT</version>
<version>3.0.0-SNAPSHOT</version>
</parent>
<artifactId>timely-client</artifactId>
<properties>
<!-- PEP 440 compliant version gets substituted into src/main/python/template/setup.py -->
<!-- use .dev0 instead of -SNAPSHOT -->
<python.client.version>2.2.6.dev0</python.client.version>
<python.client.version>2.1.6.dev0</python.client.version>
</properties>
<dependencies>
<dependency>
@@ -18,27 +18,18 @@ public class TcpClient implements AutoCloseable {
private Socket sock = null;
private BufferedWriter out = null;
private long connectTime = 0L;
private long INIT_BACKOFF = 1000;
private long MAX_BACKOFF = 60000;
private long backoff = INIT_BACKOFF;
private int bufferSize;
private long latency;
private long backoff = 2000;
private int bufferSize = -1;
private int writesSinceFlush = 0;
private long lastFlushTime = System.currentTimeMillis();
public TcpClient(String hostname, int port) {
this(hostname, port, -1);
}
public TcpClient(String hostname, int port, int bufferSize) {
this(hostname, port, bufferSize, -1);
}
public TcpClient(String hostname, int port, int bufferSize, long latency) {
this.host = hostname;
this.port = port;
this.bufferSize = bufferSize;
this.latency = latency;
}
/**
@@ -63,54 +54,20 @@ public class TcpClient implements AutoCloseable {
if (connect() != 0) {
throw new IOException();
}
try {
out.write(metric);
writesSinceFlush++;
if (bufferSize > 0 && writesSinceFlush >= bufferSize) {
if (log.isTraceEnabled()) {
log.trace(String.format("Flushing buffer writesSinceFlush >= %d", writesSinceFlush));
}
flush();
}
if (latency > 0 && (System.currentTimeMillis() - lastFlushTime) >= latency) {
if (log.isTraceEnabled()) {
log.trace(String.format("Flushing buffer timeSinceFlush >= %d", latency));
}
flush();
}
} catch (IOException e) {
log.error(e.getMessage(), e);
reset();
throw e;
out.write(metric);
writesSinceFlush++;
if (bufferSize > 0 && writesSinceFlush >= bufferSize) {
flush();
}
}
public synchronized void flush() throws IOException {
if (null != out) {
try {
long start = System.currentTimeMillis();
out.flush();
lastFlushTime = System.currentTimeMillis();
if (log.isTraceEnabled()) {
log.trace(String.format("Flushed %d metrics to socket in %d ms", writesSinceFlush, (lastFlushTime - start)));
}
writesSinceFlush = 0;
} catch (IOException e) {
log.error(String.format("Failed to flush %d metrics to socket", writesSinceFlush), e);
reset();
throw e;
}
out.flush();
writesSinceFlush = 0;
}
}
private synchronized void reset() {
out = null;
sock = null;
backoff = INIT_BACKOFF;
writesSinceFlush = 0;
lastFlushTime = System.currentTimeMillis();
}
/**
* Closes the tcp connection to Timely
*
@@ -118,8 +75,8 @@ public class TcpClient implements AutoCloseable {
* if an error occurs
*/
@Override
public synchronized void close() throws IOException {
log.info(String.format("Shutting down connection to Timely at %s:%d", host, port));
public void close() throws IOException {
log.trace("Shutting down connection to Timely at {}:{}", host, port);
if (null != sock) {
try {
if (null != out) {
@@ -130,7 +87,7 @@ public class TcpClient implements AutoCloseable {
}
sock.close();
} catch (IOException e) {
log.error(String.format("Error closing connection to Timely at %s:%d", host, port), e);
log.error("Error closing connection to Timely at " + host + ":" + port + ". Error: " + e.getMessage());
} finally {
sock = null;
}
@@ -146,10 +103,10 @@ public class TcpClient implements AutoCloseable {
sock = new Socket(host, port);
osw = new OutputStreamWriter(sock.getOutputStream(), StandardCharsets.UTF_8);
out = new BufferedWriter(osw);
backoff = INIT_BACKOFF;
log.info(String.format("Connected to Timely at %s:%d", host, port));
backoff = 2000;
log.trace("Connected to Timely at {}:{}", host, port);
} catch (Exception e) {
log.error(String.format("Error connecting to Timely at %s:%d", host, port), e);
log.error("Error connecting to Timely at {}:{} - {}", host, port, e.getMessage());
if (sock != null) {
try {
sock.close();
@@ -175,13 +132,12 @@ public class TcpClient implements AutoCloseable {
out = null;
}
}
backoff = Math.min(MAX_BACKOFF, backoff * 2);
if (log.isTraceEnabled()) {
log.trace(String.format("Will retry connection in %d ms", backoff));
}
backoff = backoff * 2;
log.info("Will retry connection in {} ms.", backoff);
return -1;
}
} else {
log.warn("Not writing to Timely, waiting to reconnect");
return -1;
}
}
@@ -191,22 +147,4 @@ public class TcpClient implements AutoCloseable {
public long getConnectTime() {
return connectTime;
}
public int getWritesSinceFlush() {
return writesSinceFlush;
}
public long getTimeSinceFlush() {
return System.currentTimeMillis() - lastFlushTime;
}
public synchronized boolean isConnected() {
boolean isConnected = (null != sock && sock.isConnected());
if (isConnected) {
return true;
} else {
int status = connect();
return status == 0;
}
}
}
@@ -45,18 +45,21 @@ public class MetricTest {
public void testJson() throws IOException {
ObjectMapper mapper = new ObjectMapper();
String expectedJson = "{\"name\":\"m1\",\"timestamp\":1,\"measure\":1.0,\"tags\":[{\"k1\":\"v1\"}]}";
Long now = System.currentTimeMillis();
String ts = Long.toString(now);
Metric m1 = Metric.newBuilder().name("m1").tag("k1", "v1").value(1, 1.0).build();
String expectedJson = "{\"name\":\"m1\",\"timestamp\":" + ts + ",\"measure\":1.0,\"tags\":[{\"k1\":\"v1\"}]}";
Metric m1 = Metric.newBuilder().name("m1").tag("k1", "v1").value(now, 1.0).build();
Metric expectedMetric = mapper.readValue(expectedJson, Metric.class);
assertTrue(m1.equals(expectedMetric));
expectedJson = "{\"name\":\"m1\",\"tags\":[{\"k1\":\"v1\"}],\"timestamp\":5,\"measure\":5.0}";
expectedJson = "{\"name\":\"m1\",\"tags\":[{\"k1\":\"v1\"}],\"timestamp\":" + ts + ",\"measure\":5.0}";
expectedMetric = mapper.readValue(expectedJson, Metric.class);
assertEquals((long) expectedMetric.getValue().getTimestamp(), 5L);
assertEquals(expectedMetric.getValue().getTimestamp(), now);
assertEquals(expectedMetric.getValue().getMeasure(), 5.0D, 0.0);
}
@@ -14,11 +14,32 @@ public class MetricParserTest {
@Test
public void testParseWithEscapedCharacters() {
Long now = System.currentTimeMillis();
String ts = Long.toString(now);
MetricParser parser = new MetricParser();
Metric m = parser.parse("put mymetric 12341234 5.0 tag1=value1,value1 tag2=value2=value2");
Metric m = parser.parse("put mymetric " + ts + " 5.0 tag1=value1,value1 tag2=value2=value2");
Assert.assertEquals("mymetric", m.getName());
Assert.assertEquals(12341234, (long) m.getValue().getTimestamp());
Assert.assertEquals(now, m.getValue().getTimestamp());
Assert.assertEquals(5.0, (double) m.getValue().getMeasure(), 0);
List<Tag> expected = new ArrayList<>();
expected.add(new Tag("tag1", "value1,value1"));
expected.add(new Tag("tag2", "value2=value2"));
Assert.assertEquals(expected, m.getTags());
}
@Test
public void testParseWithSecondsTimestamp() {
Long now = System.currentTimeMillis() / 1000;
String ts = Long.toString(now);
MetricParser parser = new MetricParser();
Metric m = parser.parse("put mymetric " + ts + " 5.0 tag1=value1,value1 tag2=value2=value2");
Assert.assertEquals("mymetric", m.getName());
Assert.assertEquals((Long) (now * 1000L), m.getValue().getTimestamp());
Assert.assertEquals(5.0, (double) m.getValue().getMeasure(), 0);
List<Tag> expected = new ArrayList<>();
expected.add(new Tag("tag1", "value1,value1"));
+64
View File
@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>3.0.0-SNAPSHOT</version>
</parent>
<artifactId>collectd-nsq-plugin</artifactId>
<dependencies>
<dependency>
<groupId>collectd</groupId>
<artifactId>collectd-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</dependency>
<dependency>
<groupId>timely</groupId>
<artifactId>collectd-plugin-parent</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<configuration>
<artifactSet>
<includes />
</artifactSet>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>**/module-info.class</exclude>
</excludes>
</filter>
</filters>
<createDependencyReducedPom>false</createDependencyReducedPom>
</configuration>
<executions>
<execution>
<id>shade</id>
<goals>
<goal>shade</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -1,6 +1,5 @@
package timely.collectd.plugin;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.apache.commons.pool2.PooledObject;
@@ -9,6 +8,7 @@ import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.collectd.api.OConfigItem;
public class PooledCloseableHttpClientFactory implements PooledObjectFactory<CloseableHttpClient> {
@@ -17,25 +17,24 @@ public class PooledCloseableHttpClientFactory implements PooledObjectFactory<Clo
private int socketTimeout = 60000; // 60 seconds in milliseconds
private long connectionTimeToLive = 300000; // 300 seconds in milliseconds
public int config(Map<String,Object> configMap) {
for (Map.Entry<String,Object> entry : configMap.entrySet()) {
Object value = entry.getValue();
switch (entry.getKey()) {
public int config(OConfigItem config) {
for (OConfigItem child : config.getChildren()) {
switch (child.getKey()) {
case "connectionRequestTimeout":
case "ConnectionRequestTimeout":
connectionRequestTimeout = Integer.parseInt((String) value);
connectionRequestTimeout = Integer.parseInt(child.getValues().get(0).getString());
break;
case "connectTimeout":
case "ConnectTimeout":
connectTimeout = Integer.parseInt((String) value);
connectTimeout = Integer.parseInt(child.getValues().get(0).getString());
break;
case "socketTimeout":
case "SocketTimeout":
socketTimeout = Integer.parseInt((String) value);
socketTimeout = Integer.parseInt(child.getValues().get(0).getString());
break;
case "connectionTimeToLive":
case "ConnectionTimeToLive":
connectionTimeToLive = Long.parseLong((String) value);
connectionTimeToLive = Long.parseLong(child.getValues().get(0).getString());
break;
default:
}
@@ -4,8 +4,6 @@ import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import org.apache.commons.pool2.impl.GenericObjectPool;
@@ -21,9 +19,7 @@ import org.collectd.api.CollectdConfigInterface;
import org.collectd.api.CollectdShutdownInterface;
import org.collectd.api.CollectdWriteInterface;
import org.collectd.api.OConfigItem;
import org.collectd.api.OConfigValue;
import org.collectd.api.ValueList;
import org.slf4j.event.Level;
/**
*
@@ -50,28 +46,14 @@ public class WriteNSQPlugin extends CollectDPluginParent implements CollectdConf
Collectd.registerWrite(WriteNSQPlugin.class.getName(), this);
}
@Override
public void write(MetricData metricData) {
try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(baos)) {
super.process(metricData, out);
out.flush();
send(baos);
} catch (Exception e) {
Collectd.logError("Error sending metrics to NSQ: " + e.getMessage());
}
}
@Override
public int write(ValueList vl) {
MetricData metricData = new MetricData(vl);
write(metricData);
return 0;
}
private int send(ByteArrayOutputStream baos) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(baos);
super.process(vl, out);
CloseableHttpClient client = null;
try {
out.flush();
HttpPost post = new HttpPost(endpoint);
EntityBuilder request = EntityBuilder.create();
request.setBinary(baos.toByteArray());
@@ -104,6 +86,9 @@ public class WriteNSQPlugin extends CollectDPluginParent implements CollectdConf
if (client != null) {
clientPool.returnObject(client);
}
try {
out.close();
} catch (IOException e) {}
}
return 0;
}
@@ -129,39 +114,7 @@ public class WriteNSQPlugin extends CollectDPluginParent implements CollectdConf
@Override
public int config(OConfigItem config) {
Map<String,Object> configMap = new HashMap<>();
for (OConfigItem c : config.getChildren()) {
String key = c.getKey();
OConfigValue value = c.getValues().get(0);
int type = value.getType();
if (type == 0) {
configMap.put(key, value.getString());
} else if (type == 1) {
configMap.put(key, value.getNumber());
} else if (type == 2) {
configMap.put(key, value.getBoolean());
}
switch (key.toLowerCase()) {
case "host":
case "hostname":
String hostString = value.getString();
String[] hosts = hostString.split(",");
if (hosts.length == 1) {
host = hosts[0];
} else {
host = hosts[random.nextInt(hosts.length)];
}
break;
case "port":
port = Integer.parseInt(value.getString());
break;
case "topic":
topic = value.getString();
break;
default:
}
}
super.config(config);
GenericObjectPoolConfig<CloseableHttpClient> poolConfig = new GenericObjectPoolConfig<>();
// use max size for maxTotal and maxIdle
// no need to activate or passivate
@@ -169,32 +122,40 @@ public class WriteNSQPlugin extends CollectDPluginParent implements CollectdConf
poolConfig.setMaxIdle(POOL_MAX_SIZE);
poolConfig.setTestOnReturn(true);
PooledCloseableHttpClientFactory pooledCloseableHttpClientFactory = new PooledCloseableHttpClientFactory();
pooledCloseableHttpClientFactory.config(configMap);
pooledCloseableHttpClientFactory.config(config);
for (OConfigItem child : config.getChildren()) {
switch (child.getKey()) {
case "host":
case "hostname":
case "Host":
case "HostName":
String hostString = child.getValues().get(0).getString();
String[] hosts = hostString.split(",");
if (hosts.length == 1) {
host = hosts[0];
} else {
host = hosts[random.nextInt(hosts.length)];
}
break;
case "Port":
case "port":
port = Integer.parseInt(child.getValues().get(0).getString());
break;
case "topic":
case "Topic":
topic = child.getValues().get(0).getString();
break;
default:
}
}
if (host == null || port == 0 || topic == null) {
Collectd.logError("NSQ host, port, and topic must be configured");
return -1;
}
this.clientPool = new GenericObjectPool<>(pooledCloseableHttpClientFactory, poolConfig);
this.endpoint = "http://" + host + ":" + port + "/mpub?topic=" + topic;
super.config(configMap);
return 0;
}
public void log(Level level, String s) {
// collectd must be compiled with debug enabled in order for log level debug to work
if (isDebug()) {
Collectd.logInfo(s);
} else {
if (level.equals(Level.DEBUG)) {
Collectd.logDebug(s);
} else if (level.equals(Level.INFO)) {
Collectd.logInfo(s);
} else if (level.equals(Level.WARN)) {
Collectd.logWarning(s);
} else if (level.equals(Level.ERROR)) {
Collectd.logError(s);
}
}
}
}
+32
View File
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>3.0.0-SNAPSHOT</version>
</parent>
<artifactId>collectd-plugin-parent</artifactId>
<dependencies>
<dependency>
<groupId>collectd</groupId>
<artifactId>collectd-api</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.jcip</groupId>
<artifactId>jcip-annotations</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-easymock-release-full</artifactId>
<classifier>full</classifier>
<scope>test</scope>
</dependency>
</dependencies>
</project>
@@ -1,25 +1,20 @@
package timely.collectd.plugin;
import static org.slf4j.event.Level.DEBUG;
import static org.slf4j.event.Level.WARN;
import java.io.OutputStream;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.collectd.api.Collectd;
import org.collectd.api.OConfigItem;
import org.collectd.api.ValueList;
import timely.util.Exclusions;
public abstract class CollectDPluginParent implements WritesMetrics, PluginLogger {
public abstract class CollectDPluginParent {
private static final String PUT = "put {0} {1} {2} {3}";
private static final String INSTANCE = "instance";
@@ -27,44 +22,41 @@ public abstract class CollectDPluginParent implements WritesMetrics, PluginLogge
private static final String SAMPLE_TYPE = "sampleType";
private static final String CODE = "code";
private static final String PERIOD = ".";
private static final String COUNTER = "COUNTER";
private static final String GAUGE = "GAUGE";
private static final String DERIVE = "DERIVE";
private static final String ABSOLUTE = "ABSOLUTE";
private static final Collection<String> HADOOP_CONTEXTS = Arrays.asList("NameNode", "DataNode", "JobManager", "NodeManager", "JobHistoryServer",
"ResourceManager", "MRAppMaster", "JvmMetrics", "MapTask", "ReduceTask");
"ResourceManager", "MRAppMaster");
private static final Pattern HADOOP_PATTERN_4_GROUPS = Pattern.compile("([\\w-_]+)\\.([\\w-_]+)\\.([\\w-_]+)\\.([\\w-_]+)");
private static final Pattern STATSD_PATTERN_3_GROUPS = Pattern.compile("([\\w-_]+)\\.([\\w-_]+)\\.([\\w-_]+)");
private static final Pattern STATSD_PATTERN_4_GROUPS = Pattern.compile("([\\w-_]+)\\.([\\w-_]+)\\.([\\w-_]+)\\.([\\w-_]+)");
private static final Pattern STATSD_PATTERN_6_GROUPS = Pattern.compile("([\\w-_]+)\\.([\\w-_]+)\\.([\\w-_]+)\\.([\\w-_]+)\\.([\\w-_#]+)\\.([\\w-_]+)");
private static final Pattern ETHSTAT_QUEUE_PATTERN1 = Pattern.compile("([\\w-_]*)queue_([0-9]+)_([\\w-_]+)");
private static final Pattern ETHSTAT_QUEUE_PATTERN2 = Pattern.compile("([\\w-_\\.]*)(tx|rx)([-_]?)([0-9]+)([._])([\\w-_]+)");
private static final Pattern ETHSTAT_TRAFFIC_CLASS_PATTERN = Pattern.compile("([\\w-_\\.]*)(tc)([-_]?)([0-9]+)([._])([\\w-_]+)");
private static final Pattern ETHSTAT_CHANNEL_PATTERN = Pattern.compile("(ch)([0-9]+)([._])([\\w-_]+)");
private static final Pattern ETHSTAT_PATTERN = Pattern.compile("([\\w-_]+)_queue_([\\w-_]+)_([\\w-_]+)");
private static final String STATSD_PREFIX = "statsd";
private static final String NSQ_PREFIX = STATSD_PREFIX + ".nsq.";
private static final Pattern HAPROXY_PATTERN = Pattern.compile("\\[([\\w-_=]+),([\\w-_=]+)\\]");
private final SMARTCodeMapping smart = new SMARTCodeMapping();
protected Map<String,String> additionalTags = new TreeMap<>();
protected Map<String,String> addlTags = new TreeMap<>();
private boolean debug = false;
private Exclusions exclusions = new Exclusions();
public int config(Map<String,Object> configMap) {
for (Map.Entry<String,Object> entry : configMap.entrySet()) {
switch (entry.getKey().toLowerCase()) {
public int config(OConfigItem config) {
for (OConfigItem child : config.getChildren()) {
switch (child.getKey()) {
case "Tags":
case "tags":
String tagValue = (String) entry.getValue();
String tagValue = child.getValues().get(0).getString();
Arrays.stream(tagValue.split(",")).map(String::trim).forEach(p -> {
String[] pair = p.split("=");
if (pair.length == 2) {
addTag(additionalTags, pair[0], pair[1]);
addTag(addlTags, pair[0], pair[1]);
}
});
break;
case "filteredmetricsfile":
setFilteredMetricsFile((String) entry.getValue());
break;
case "filteredtagsfile":
setFilteredTagsFile((String) entry.getValue());
break;
case "Debug":
case "debug":
debug = (Boolean) entry.getValue();
debug = child.getValues().get(0).getBoolean();
break;
default:
}
@@ -72,22 +64,6 @@ public abstract class CollectDPluginParent implements WritesMetrics, PluginLogge
return 0;
}
public void setAdditionalTags(Map<String,String> additionalTags) {
this.additionalTags = additionalTags;
}
public void setFilteredMetricsFile(String filteredMetricsFile) {
if (StringUtils.isNotBlank(filteredMetricsFile)) {
exclusions.setFilteredMetricsFile(filteredMetricsFile);
}
}
public void setFilteredTagsFile(String filteredTagsFile) {
if (StringUtils.isNotBlank(filteredTagsFile)) {
exclusions.setFilteredTagsFile(filteredTagsFile);
}
}
public abstract void write(String metric, OutputStream out);
protected void addTag(Map<String,String> m, String k, String v) {
@@ -107,12 +83,22 @@ public abstract class CollectDPluginParent implements WritesMetrics, PluginLogge
}
}
public void process(MetricData metricData, OutputStream out) {
debugMetricData(metricData);
StringBuilder metric = new StringBuilder();
Map<String,String> tagMap = new TreeMap<>();
public void process(ValueList vl, OutputStream out) {
try {
processInternal(vl, out);
} catch (Exception e) {
Collectd.logError(e.getMessage());
e.printStackTrace(System.err);
}
}
String host = metricData.getHost();
private void processInternal(ValueList vl, OutputStream out) {
debugValueList(vl);
StringBuilder metric = new StringBuilder();
Long timestamp = vl.getTime();
Map<String,String> tagMap = new TreeMap<>();
String host = vl.getHost();
int idx = host.indexOf(PERIOD);
if (-1 != idx) {
addTag(tagMap, "host", host.substring(0, idx));
@@ -124,24 +110,23 @@ public abstract class CollectDPluginParent implements WritesMetrics, PluginLogge
if (-1 != nIdx) {
addTag(tagMap, "rack", hostSplit[0].substring(0, nIdx));
}
tagMap.putAll(additionalTags);
tagMap.putAll(addlTags);
String plugin = metricData.getPlugin();
String pluginInstance = metricData.getPluginInstance();
String type = metricData.getType();
String typeInstance = metricData.getTypeInstance();
List<Pair<Double,String>> values = metricData.getValuePairs();
String plugin = vl.getPlugin();
String pluginInstance = vl.getPluginInstance();
String type = vl.getType();
String typeInstance = vl.getTypeInstance();
switch (plugin) {
case STATSD_PREFIX:
Matcher hadoop_4_groups = HADOOP_PATTERN_4_GROUPS.matcher(typeInstance);
Matcher statsd_3_groups = STATSD_PATTERN_3_GROUPS.matcher(typeInstance);
Matcher statsd_4_groups = STATSD_PATTERN_4_GROUPS.matcher(typeInstance);
Matcher statsd_6_groups = STATSD_PATTERN_6_GROUPS.matcher(typeInstance);
String instance = null;
boolean defaultCase = false;
if (!typeInstance.startsWith("nsq")) {
String[] parts = typeInstance.split("\\.");
if (parts.length >= 4 && HADOOP_CONTEXTS.contains(parts[0])) {
if (parts.length >= 4 && (HADOOP_CONTEXTS.contains(parts[0]) || hadoop_4_groups.matches())) {
// Here we are processing the statsd metrics coming from the Hadoop Metrics2 StatsDSink without the host name.
// The format of metric is: serviceName.contextName.recordName.metricName
// The serviceName is used as the instance.
@@ -175,39 +160,28 @@ public abstract class CollectDPluginParent implements WritesMetrics, PluginLogge
int x = typeInstance.indexOf(".");
metric.append(STATSD_PREFIX).append(PERIOD).append(typeInstance.substring(x + 1));
addTag(tagMap, "queryId", parts[0]);
} else if (parts.length % 2 == 1) {
// EtsyStatsD format -- metric.(tagName.tagValue)*
metric.append(STATSD_PREFIX).append(PERIOD).append(parts[0]);
for (int x = 1; x < parts.length; x += 2) {
addTag(tagMap, parts[x], parts[x + 1]);
}
} else if (statsd_4_groups.matches()) {
// Here we are processing the statsd metrics coming from the Hadoop Metrics2 StatsDSink without the host name.
// The format of metric is: serviceName.contextName.recordName.metricName. The recordName is typically duplicative
// and is dropped here. The serviceName is used as the instance.
metric.append(STATSD_PREFIX).append(PERIOD).append(statsd_4_groups.group(2)).append(PERIOD).append(statsd_4_groups.group(4));
instance = statsd_4_groups.group(1);
} else if (parts.length >= 1) {
// EtsyStatsD format -- metric.(tagName.tagValue)*
// Will handle all key.value pairs and truncate at the end if line was malformed or truncated
metric.append(STATSD_PREFIX).append(PERIOD).append(parts[0]);
for (int x = 1; x + 1 < parts.length; x += 2) {
addTag(tagMap, parts[x], parts[x + 1]);
}
if (parts.length % 2 == 0) {
log(DEBUG, String.format("Using available tags from malformed/truncated line %s", typeInstance));
}
} else {
defaultCase = true;
}
} else if (statsd_3_groups.matches()) {
metric.append(NSQ_PREFIX).append(statsd_3_groups.group(2)).append(PERIOD).append(statsd_3_groups.group(3));
} else if (statsd_4_groups.matches()) {
metric.append(NSQ_PREFIX).append(statsd_4_groups.group(2)).append(PERIOD).append(statsd_4_groups.group(4));
instance = statsd_4_groups.group(3);
} else if (statsd_6_groups.matches()) {
metric.append(NSQ_PREFIX).append(statsd_6_groups.group(4)).append(PERIOD).append(statsd_6_groups.group(6));
instance = statsd_6_groups.group(5);
} else {
if (statsd_3_groups.matches()) {
metric.append(NSQ_PREFIX).append(statsd_3_groups.group(2)).append(PERIOD).append(statsd_3_groups.group(3));
} else if (statsd_4_groups.matches()) {
metric.append(NSQ_PREFIX).append(statsd_4_groups.group(2)).append(PERIOD).append(statsd_4_groups.group(4));
instance = statsd_4_groups.group(3);
} else if (statsd_6_groups.matches()) {
metric.append(NSQ_PREFIX).append(statsd_6_groups.group(4)).append(PERIOD).append(statsd_6_groups.group(6));
instance = statsd_6_groups.group(5);
} else {
defaultCase = true;
}
}
if (defaultCase) {
// Handle StatsD metrics of unknown formats. If there is a
// period in the metric name, use everything up to that as
// the instance.
@@ -225,39 +199,13 @@ public abstract class CollectDPluginParent implements WritesMetrics, PluginLogge
break;
case "ethstat":
metric.append("sys.ethstat.");
if (typeInstance.matches(ETHSTAT_QUEUE_PATTERN1.pattern())) {
Matcher ethstat_m = ETHSTAT_QUEUE_PATTERN1.matcher(typeInstance);
if (typeInstance.contains("queue")) {
Matcher ethstat_m = ETHSTAT_PATTERN.matcher(typeInstance);
if (ethstat_m.matches()) {
metric.append(ethstat_m.group(1));
metric.append("queue_");
metric.append(ethstat_m.group(3));
metric.append(ethstat_m.group(1)).append("_").append(ethstat_m.group(3));
addTag(tagMap, "queue", ethstat_m.group(2));
}
} else if (typeInstance.matches(ETHSTAT_QUEUE_PATTERN2.pattern())) {
Matcher ethstat_m = ETHSTAT_QUEUE_PATTERN2.matcher(typeInstance);
if (ethstat_m.matches()) {
metric.append(ethstat_m.group(1));
metric.append(ethstat_m.group(2));
metric.append("_queue_");
metric.append(ethstat_m.group(6));
addTag(tagMap, "queue", ethstat_m.group(4));
}
} else if (typeInstance.matches(ETHSTAT_TRAFFIC_CLASS_PATTERN.pattern())) {
Matcher ethstat_m = ETHSTAT_TRAFFIC_CLASS_PATTERN.matcher(typeInstance);
if (ethstat_m.matches()) {
metric.append(ethstat_m.group(1));
metric.append(ethstat_m.group(2));
metric.append(ethstat_m.group(5));
metric.append(ethstat_m.group(6));
addTag(tagMap, "trafficClass", ethstat_m.group(4));
}
} else if (typeInstance.matches(ETHSTAT_CHANNEL_PATTERN.pattern())) {
Matcher ethstat_m = ETHSTAT_CHANNEL_PATTERN.matcher(typeInstance);
if (ethstat_m.matches()) {
metric.append(ethstat_m.group(1));
metric.append("_");
metric.append(ethstat_m.group(4));
addTag(tagMap, "channel", ethstat_m.group(2));
} else {
metric.append(typeInstance);
}
} else {
metric.append(typeInstance);
@@ -296,10 +244,10 @@ public abstract class CollectDPluginParent implements WritesMetrics, PluginLogge
break;
case "haproxy":
metric.append("sys.haproxy.").append(typeInstance);
Matcher haproxy_m = HAPROXY_PATTERN.matcher(pluginInstance);
if (haproxy_m.matches()) {
addTag(tagMap, haproxy_m.group(1));
addTag(tagMap, haproxy_m.group(2));
Matcher hadoop_m2 = HAPROXY_PATTERN.matcher(pluginInstance);
if (hadoop_m2.matches()) {
addTag(tagMap, hadoop_m2.group(1));
addTag(tagMap, hadoop_m2.group(2));
}
break;
case "ipmi":
@@ -311,9 +259,7 @@ public abstract class CollectDPluginParent implements WritesMetrics, PluginLogge
metric.append("sys.load.").append(plugin).append(PERIOD).append(type);
// plugin returns 1 min, 5 min, 15 min averages, but Timely can
// downsample on its own so we only use the first (1 min) value
Pair<Double,String> first = values.get(0);
values.clear();
values.add(first);
vl.setValues(Arrays.asList(vl.getValues().get(0)));
break;
case "GenericJMX":
metric.append("sys.").append(plugin).append(PERIOD).append(type).append(PERIOD).append(typeInstance);
@@ -337,59 +283,77 @@ public abstract class CollectDPluginParent implements WritesMetrics, PluginLogge
} else if (notEmpty(type) && notEmpty(plugin)) {
metric.append("sys.").append(plugin).append(PERIOD).append(type);
} else {
log(WARN, "Unhandled metric: " + metricData);
Collectd.logWarning("Unhandled metric: " + vl);
return;
}
break;
}
final String metricName = metric.toString().replaceAll(" ", "_");
for (Pair<Double,String> pair : values) {
addTag(tagMap, SAMPLE_TYPE, pair.getRight());
Double value = pair.getLeft();
Long timestamp = metricData.getTimestamp();
for (int i = 0; i < vl.getValues().size(); i++) {
int dataSourceSampleType = vl.getDataSet().getDataSources().get(i).getType();
String sampleType = convertType(dataSourceSampleType);
if (null != sampleType) {
addTag(tagMap, SAMPLE_TYPE, sampleType);
}
Double value = vl.getValues().get(i).doubleValue();
String tagString = tagMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining(" "));
if (metricName.isBlank() || timestamp == null || value == null) {
log(DEBUG, String.format("Not writing unhandled metric: plugin:%s pluginInstance:%s type:%s typeInstance:%s", plugin, pluginInstance, type,
logDebug(String.format("Not writing unhandled metric: plugin:%s pluginInstance:%s type:%s typeInstance:%s", plugin, pluginInstance, type,
typeInstance));
} else {
String datapoint = MessageFormat.format(PUT, metricName, timestamp.toString(), value.toString(), tagString);
if (exclusions.filterMetric(datapoint)) {
log(DEBUG, String.format("Not writing excluded metric: %s", datapoint));
} else {
datapoint = exclusions.filterExcludedTags(datapoint);
log(DEBUG, String.format("Writing: %s", datapoint));
write(datapoint + "\n", out);
}
logDebug(String.format("Writing: %s", datapoint));
write(datapoint + "\n", out);
}
}
}
private void debugMetricData(MetricData metricData) {
private void logDebug(String s) {
// collectd must be compiled with debug enabled in order for log level debug to work
if (debug) {
Collectd.logInfo(s);
} else {
Collectd.logDebug(s);
}
}
private void debugValueList(ValueList vl) {
try {
StringBuilder sb = new StringBuilder();
sb.append(String.format("Plugin:%s", metricData.getPlugin()));
if (metricData.getPluginInstance() != null && !metricData.getPluginInstance().isBlank()) {
sb.append(String.format(" PluginInstance:%s", metricData.getPluginInstance()));
sb.append(String.format("Plugin:%s", vl.getPlugin()));
if (vl.getPluginInstance() != null && !vl.getPluginInstance().isBlank()) {
sb.append(String.format(" PluginInstance:%s", vl.getPluginInstance()));
}
sb.append(String.format(" Type:%s", metricData.getType()));
sb.append(String.format(" TypeInstance:%s", metricData.getTypeInstance()));
for (int i = 0; i < metricData.getValuePairs().size(); i++) {
Pair<Double,String> value = metricData.getValuePairs().get(i);
sb.append(String.format(" Value:%s", value.getLeft().toString()));
sb.append(String.format(" Type:%s", vl.getType()));
sb.append(String.format(" TypeInstance:%s", vl.getTypeInstance()));
for (int i = 0; i < vl.getValues().size(); i++) {
Number value = vl.getValues().get(i);
sb.append(String.format(" Value:%s", value.toString()));
}
log(DEBUG, sb.toString());
logDebug(sb.toString());
} catch (Exception e) {
e.printStackTrace();
log(DEBUG, e.getMessage());
logDebug(e.getMessage());
}
}
private String convertType(int type) {
switch (type) {
case 0:
return COUNTER;
case 1:
return GAUGE;
case 2:
return DERIVE;
case 3:
return ABSOLUTE;
default:
return GAUGE;
}
}
private boolean notEmpty(String arg) {
return (null != arg) && !(arg.equals(""));
}
public boolean isDebug() {
return debug;
}
}
@@ -3,7 +3,6 @@ package timely.collectd.plugin;
import static org.junit.Assert.*;
import java.io.OutputStream;
import java.net.URL;
import java.util.Collections;
import org.collectd.api.Collectd;
@@ -14,23 +13,20 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.easymock.PowerMock;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.event.Level;
import net.jcip.annotations.NotThreadSafe;
@RunWith(PowerMockRunner.class)
@PowerMockIgnore("jdk.internal.reflect.*")
@PrepareForTest({Collectd.class})
@NotThreadSafe
public class PluginParentTest {
public class TestPlugin extends CollectDPluginParent {
private Logger log = LoggerFactory.getLogger(this.getClass().getName());
public TestPlugin() {
super.additionalTags.put("addl1", "foo");
super.addlTags.put("addl1", "foo");
}
@Override
@@ -38,19 +34,6 @@ public class PluginParentTest {
result = metric;
}
@Override
public void log(Level level, String s) {
log.debug(s);
}
@Override
public void write(MetricData metricData) {
try {
process(metricData, null);
} catch (Exception e) {
log(Level.ERROR, e.getMessage());
}
}
}
private static final String HOST = "r01n01.test";
@@ -58,13 +41,12 @@ public class PluginParentTest {
public String result = null;
@Before
public void setup() {
result = null;
public void setup() throws Exception {
PowerMock.suppress(PowerMock.everythingDeclaredIn(Collectd.class));
}
@Test
public void testStatsDHadoopFormat() {
public void testStatsDHadoopFormat() throws Exception {
ValueList vl = new ValueList();
vl.setHost(HOST);
vl.setPlugin("statsd");
@@ -77,12 +59,12 @@ public class PluginParentTest {
vl.setDataSet(ds);
TestPlugin test = new TestPlugin();
test.write(new MetricData(vl));
assertEquals("put statsd.dfs.BlocksRead 1456156976840 1.0 addl1=foo host=r01n01 instance=DataNode rack=r01 sampleType=GAUGE\n", result);
test.process(vl, null);
assertEquals("put statsd.dfs.BlocksRead 1456156976840 1.0 host=r01n01 rack=r01 addl1=foo instance=DataNode sample=value sampleType=GAUGE\n", result);
}
@Test
public void testStatsDUnknownFormat() {
public void testStatsDUnknownFormat() throws Exception {
ValueList vl = new ValueList();
vl.setHost(HOST);
vl.setPlugin("statsd");
@@ -95,12 +77,12 @@ public class PluginParentTest {
vl.setDataSet(ds);
TestPlugin test = new TestPlugin();
test.write(new MetricData(vl));
assertEquals("put statsd.baz 1456156976840 1.0 addl1=foo host=r01n01 rack=r01 sampleType=GAUGE\n", result);
test.process(vl, null);
assertEquals("put statsd.baz 1456156976840 1.0 host=r01n01 rack=r01 addl1=foo sample=value sampleType=GAUGE\n", result);
}
@Test
public void testStatsDUnknownFormat2() {
public void testStatsDUnknownFormat2() throws Exception {
ValueList vl = new ValueList();
vl.setHost(HOST);
vl.setPlugin("statsd");
@@ -113,12 +95,12 @@ public class PluginParentTest {
vl.setDataSet(ds);
TestPlugin test = new TestPlugin();
test.write(new MetricData(vl));
assertEquals("put statsd.bar 1456156976840 1.0 addl1=foo host=r01n01 rack=r01 sampleType=GAUGE\n", result);
test.process(vl, null);
assertEquals("put statsd.baz 1456156976840 1.0 host=r01n01 rack=r01 addl1=foo instance=bar sample=value sampleType=GAUGE\n", result);
}
@Test
public void testHddTemp() {
public void testHddTemp() throws Exception {
ValueList vl = new ValueList();
vl.setHost(HOST);
vl.setPlugin("hddtemp");
@@ -131,12 +113,12 @@ public class PluginParentTest {
vl.setDataSet(ds);
TestPlugin test = new TestPlugin();
test.write(new MetricData(vl));
assertEquals("put sys.hddtemp.temperature 1456156976840 35.0 addl1=foo host=r01n01 instance=sda rack=r01 sampleType=GAUGE\n", result);
test.process(vl, null);
assertEquals("put sys.hddtemp.temperature 1456156976840 35.0 host=r01n01 rack=r01 addl1=foo instance=sda sample=value sampleType=GAUGE\n", result);
}
@Test
public void testSmart1() {
public void testSmart1() throws Exception {
ValueList vl = new ValueList();
vl.setHost(HOST);
vl.setPlugin("smart");
@@ -149,12 +131,12 @@ public class PluginParentTest {
vl.setDataSet(ds);
TestPlugin test = new TestPlugin();
test.write(new MetricData(vl));
assertEquals("put sys.smart.smart_badsectors 1456156976840 0.0 addl1=foo host=r01n01 instance=sda rack=r01 sampleType=GAUGE\n", result);
test.process(vl, null);
assertEquals("put sys.smart.smart_badsectors 1456156976840 0.0 host=r01n01 rack=r01 addl1=foo instance=sda sample=value sampleType=GAUGE\n", result);
}
@Test
public void testSmart2() {
public void testSmart2() throws Exception {
ValueList vl = new ValueList();
vl.setHost(HOST);
vl.setPlugin("smart");
@@ -168,12 +150,12 @@ public class PluginParentTest {
vl.setDataSet(ds);
TestPlugin test = new TestPlugin();
test.write(new MetricData(vl));
assertEquals("put sys.smart.raw-read-error-rate 1456156976840 0.0 addl1=foo host=r01n01 instance=sda rack=r01 sampleType=GAUGE\n", result);
test.process(vl, null);
assertEquals("put sys.smart.raw-read-error-rate 1456156976840 0.0 host=r01n01 rack=r01 addl1=foo instance=sda sample=value sampleType=GAUGE\n", result);
}
@Test
public void testSmartCode() {
public void testSmartCode() throws Exception {
ValueList vl = new ValueList();
vl.setHost(HOST);
vl.setPlugin("smart");
@@ -187,12 +169,13 @@ public class PluginParentTest {
vl.setDataSet(ds);
TestPlugin test = new TestPlugin();
test.write(new MetricData(vl));
assertEquals("put sys.smart.Total_LBAs_Read 1456156976840 0.0 addl1=foo code=242 host=r01n01 instance=sda rack=r01 sampleType=GAUGE\n", result);
test.process(vl, null);
assertEquals("put sys.smart.Total_LBAs_Read 1456156976840 0.0 host=r01n01 rack=r01 addl1=foo code=242 instance=sda sample=value sampleType=GAUGE\n",
result);
}
@Test
public void testSnmp() {
public void testSnmp() throws Exception {
ValueList vl = new ValueList();
vl.setHost(HOST);
vl.setPlugin("snmp");
@@ -206,8 +189,8 @@ public class PluginParentTest {
vl.setDataSet(ds);
TestPlugin test = new TestPlugin();
test.write(new MetricData(vl));
assertEquals("put sys.snmp.if_octets 1456156976840 0.0 addl1=foo host=r01n01 instance=Ethernet1 rack=r01 sampleType=GAUGE\n", result);
test.process(vl, null);
assertEquals("put sys.snmp.if_octets 1456156976840 0.0 host=r01n01 rack=r01 addl1=foo instance=Ethernet1 sample=value sampleType=GAUGE\n", result);
}
@@ -226,8 +209,9 @@ public class PluginParentTest {
vl.setDataSet(ds);
TestPlugin test = new TestPlugin();
test.write(new MetricData(vl));
assertEquals("put sys.sensors.temperature.coretemp-isa-0000 1456156976840 35.0 addl1=foo host=r01n01 instance=1 rack=r01 sampleType=GAUGE\n", result);
test.process(vl, null);
assertEquals("put sys.sensors.temperature.coretemp-isa-0000 1456156976840 35.0 host=r01n01 rack=r01 addl1=foo instance=1 sample=value sampleType=GAUGE\n",
result);
}
@Test
@@ -244,8 +228,8 @@ public class PluginParentTest {
vl.setDataSet(ds);
TestPlugin test = new TestPlugin();
test.write(new MetricData(vl));
assertEquals("put sys.haproxy.run_queue 1456156976840 0.0 addl1=foo host=r01n01 rack=r01 sampleType=GAUGE\n", result);
test.process(vl, null);
assertEquals("put sys.haproxy.run_queue 1456156976840 0.0 host=r01n01 rack=r01 addl1=foo sample=value sampleType=GAUGE\n", result);
}
@Test
@@ -263,8 +247,8 @@ public class PluginParentTest {
vl.setDataSet(ds);
TestPlugin test = new TestPlugin();
test.write(new MetricData(vl));
assertEquals("put sys.haproxy.queue_current 1456156976840 0.0 addl1=foo host=r01n01 proxy_name=proxy1 rack=r01 sampleType=GAUGE service_name=server1\n",
test.process(vl, null);
assertEquals("put sys.haproxy.queue_current 1456156976840 0.0 host=r01n01 rack=r01 addl1=foo proxy_name=proxy1 service_name=server1 sample=value sampleType=GAUGE\n",
result);
}
@@ -283,104 +267,30 @@ public class PluginParentTest {
vl.setDataSet(ds);
TestPlugin test = new TestPlugin();
test.write(new MetricData(vl));
assertEquals("put sys.ethstat.tx_comp_queue_full 1456156976840 6.0 addl1=foo host=r01n01 instance=eth0 rack=r01 sampleType=GAUGE\n", result);
}
@Test
public void testEthstatWithQueueStyle1() {
ValueList vl = new ValueList();
vl.setHost(HOST);
vl.setPlugin("ethstat");
vl.setPluginInstance("eth0");
vl.setTime(TIME);
vl.setType("derive");
vl.setTypeInstance("rx_queue_15_bytes");
vl.setValues(Collections.singletonList((Number) 6.0D));
DataSet ds = new DataSet("DERIVE");
ds.addDataSource(new DataSource("value", 1, 6.0D, 100.0D));
vl.setDataSet(ds);
TestPlugin test = new TestPlugin();
test.write(new MetricData(vl));
assertEquals("put sys.ethstat.rx_queue_bytes 1456156976840 6.0 addl1=foo host=r01n01 instance=eth0 queue=15 rack=r01 sampleType=GAUGE\n", result);
result = null;
vl.setTypeInstance("queue_7_tx_bytes");
test.write(new MetricData(vl));
assertEquals("put sys.ethstat.queue_tx_bytes 1456156976840 6.0 addl1=foo host=r01n01 instance=eth0 queue=7 rack=r01 sampleType=GAUGE\n", result);
result = null;
vl.setTypeInstance("queue_7_rx_xdp_drop");
test.write(new MetricData(vl));
assertEquals("put sys.ethstat.queue_rx_xdp_drop 1456156976840 6.0 addl1=foo host=r01n01 instance=eth0 queue=7 rack=r01 sampleType=GAUGE\n", result);
}
@Test
public void testEthstatWithQueueStyle2() {
ValueList vl = new ValueList();
vl.setHost(HOST);
vl.setPlugin("ethstat");
vl.setPluginInstance("eth0");
vl.setTime(TIME);
vl.setType("derive");
vl.setTypeInstance("rx-15.bytes");
vl.setValues(Collections.singletonList((Number) 6.0D));
DataSet ds = new DataSet("DERIVE");
ds.addDataSource(new DataSource("value", 1, 6.0D, 100.0D));
vl.setDataSet(ds);
TestPlugin test = new TestPlugin();
test.write(new MetricData(vl));
assertEquals("put sys.ethstat.rx_queue_bytes 1456156976840 6.0 addl1=foo host=r01n01 instance=eth0 queue=15 rack=r01 sampleType=GAUGE\n", result);
result = null;
vl.setTypeInstance("tx15_xdp_err");
test.write(new MetricData(vl));
assertEquals("put sys.ethstat.tx_queue_xdp_err 1456156976840 6.0 addl1=foo host=r01n01 instance=eth0 queue=15 rack=r01 sampleType=GAUGE\n", result);
}
@Test
public void testEthstatWithTrafficClass() {
ValueList vl = new ValueList();
vl.setHost(HOST);
vl.setPlugin("ethstat");
vl.setPluginInstance("eth0");
vl.setTime(TIME);
vl.setType("derive");
vl.setTypeInstance("veb.tc_15_tx_bytes");
vl.setValues(Collections.singletonList((Number) 6.0D));
DataSet ds = new DataSet("DERIVE");
ds.addDataSource(new DataSource("value", 1, 6.0D, 100.0D));
vl.setDataSet(ds);
TestPlugin test = new TestPlugin();
test.write(new MetricData(vl));
assertEquals("put sys.ethstat.veb.tc_tx_bytes 1456156976840 6.0 addl1=foo host=r01n01 instance=eth0 rack=r01 sampleType=GAUGE trafficClass=15\n",
test.process(vl, null);
assertEquals("put sys.ethstat.tx_comp_queue_full 1456156976840 6.0 host=r01n01 rack=r01 addl1=foo instance=eth0 sample=value sampleType=GAUGE\n",
result);
}
@Test
public void testEthstatWithChannel() {
public void testEthStatWithQueue() {
ValueList vl = new ValueList();
vl.setHost(HOST);
vl.setPlugin("ethstat");
vl.setPluginInstance("eth0");
vl.setTime(TIME);
vl.setType("derive");
vl.setTypeInstance("ch44_events");
vl.setTypeInstance("rx_queue_1_bytes");
vl.setValues(Collections.singletonList((Number) 6.0D));
DataSet ds = new DataSet("DERIVE");
ds.addDataSource(new DataSource("value", 1, 6.0D, 100.0D));
vl.setDataSet(ds);
TestPlugin test = new TestPlugin();
test.process(vl, null);
assertEquals("put sys.ethstat.rx_bytes 1456156976840 6.0 host=r01n01 rack=r01 addl1=foo queue=1 instance=eth0 sample=value sampleType=GAUGE\n", result);
test.write(new MetricData(vl));
assertEquals("put sys.ethstat.ch_events 1456156976840 6.0 addl1=foo channel=44 host=r01n01 instance=eth0 rack=r01 sampleType=GAUGE\n", result);
}
@Test
@@ -397,38 +307,10 @@ public class PluginParentTest {
vl.setDataSet(ds);
TestPlugin test = new TestPlugin();
test.write(new MetricData(vl));
assertEquals("put sys.ipmi.temperature 1456156976840 6.0 addl1=foo host=r01n01 instance=LAN_NIC_Temp_system_board_(3.2) rack=r01 sampleType=GAUGE\n",
test.process(vl, null);
assertEquals("put sys.ipmi.temperature 1456156976840 6.0 host=r01n01 rack=r01 addl1=foo instance=LAN_NIC_Temp_system_board_(3.2) sample=value sampleType=GAUGE\n",
result);
}
@Test
public void testExclusions() {
ValueList vl = new ValueList();
vl.setHost(HOST);
vl.setPlugin("ethstat");
vl.setPluginInstance("eth0");
vl.setTime(TIME);
vl.setType("derive");
vl.setTypeInstance("rx_queue_1_bytes");
vl.setValues(Collections.singletonList((Number) 6.0D));
DataSet ds = new DataSet("DERIVE");
ds.addDataSource(new DataSource("value", 1, 6.0D, 100.0D));
vl.setDataSet(ds);
TestPlugin test = new TestPlugin();
URL filteredMetricsFile = getClass().getClassLoader().getResource("filteredMetrics.txt");
test.setFilteredMetricsFile(filteredMetricsFile.getPath());
URL filteredTagsFile = getClass().getClassLoader().getResource("filteredTags.txt");
test.setFilteredTagsFile(filteredTagsFile.getPath());
test.write(new MetricData(vl));
assertEquals("put sys.ethstat.rx_queue_bytes 1456156976840 6.0 addl1=foo host=r01n01 queue=1 rack=r01\n", result);
result = null;
vl.setPlugin("interface");
vl.setTypeInstance("if_octets");
assertNull(result);
}
}
@@ -4,43 +4,37 @@
<parent>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>2.2.6-SNAPSHOT</version>
<version>3.0.0-SNAPSHOT</version>
</parent>
<artifactId>timely-collectd</artifactId>
<artifactId>collectd-timely-plugin</artifactId>
<dependencies>
<dependency>
<groupId>collectd</groupId>
<artifactId>collectd-api</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</dependency>
<dependency>
<groupId>timely</groupId>
<artifactId>timely-common</artifactId>
<artifactId>collectd-plugin-parent</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.jcip</groupId>
<artifactId>jcip-annotations</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-easymock-release-full</artifactId>
@@ -55,17 +49,7 @@
<artifactId>maven-shade-plugin</artifactId>
<configuration>
<artifactSet>
<includes>
<include>com.google.guava:guava</include>
<include>commons-codec:commons-codec</include>
<include>org.apache.commons:commons-lang3</include>
<include>org.apache.commons:commons-pool2</include>
<include>org.apache.httpcomponents:httpclient</include>
<include>org.apache.httpcomponents:httpcore</include>
<include>org.slf4j:jcl-over-slf4j</include>
<include>org.slf4j:slf4j-api</include>
<include>timely:timely-common</include>
</includes>
<includes />
</artifactSet>
<filters>
<filter>
@@ -1,12 +1,12 @@
package timely.collectd.plugin;
import java.net.Socket;
import java.util.Map;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.PooledObjectFactory;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.collectd.api.Collectd;
import org.collectd.api.OConfigItem;
public class PooledSocketFactory implements PooledObjectFactory<Socket> {
@@ -16,27 +16,26 @@ public class PooledSocketFactory implements PooledObjectFactory<Socket> {
private int socketTimeout = 60000; // 60 seconds in milliseconds
private long connectionTimeToLive = 300000; // 300 seconds in milliseconds
public int config(Map<String,Object> configMap) {
for (Map.Entry<String,Object> entry : configMap.entrySet()) {
Object value = entry.getValue();
switch (entry.getKey()) {
public int config(OConfigItem config) {
for (OConfigItem child : config.getChildren()) {
switch (child.getKey()) {
case "host":
case "hostname":
case "Host":
case "HostName":
host = (String) value;
host = child.getValues().get(0).getString();
break;
case "Port":
case "port":
port = Integer.parseInt((String) value);
port = Integer.parseInt(child.getValues().get(0).getString());
break;
case "socketTimeout":
case "SocketTimeout":
socketTimeout = Integer.parseInt((String) value);
socketTimeout = Integer.parseInt(child.getValues().get(0).getString());
break;
case "connectionTimeToLive":
case "ConnectionTimeToLive":
connectionTimeToLive = Long.parseLong((String) value);
connectionTimeToLive = Long.parseLong(child.getValues().get(0).getString());
break;
default:
@@ -81,15 +80,15 @@ public class PooledSocketFactory implements PooledObjectFactory<Socket> {
}
@Override
public PooledObject<Socket> makeObject() {
public PooledObject<Socket> makeObject() throws Exception {
return new DefaultPooledObject<>(connect());
}
@Override
public void destroyObject(PooledObject<Socket> pooledObject) {
public void destroyObject(PooledObject<Socket> pooledObject) throws Exception {
try {
Collectd.logDebug("Shutting down connection to Timely at " + host + ":" + port);
Socket socket = pooledObject.getObject();
Socket socket = (Socket) pooledObject.getObject();
if (socket != null) {
socket.close();
}
@@ -97,6 +96,7 @@ public class PooledSocketFactory implements PooledObjectFactory<Socket> {
Collectd.logError("Error closing connection to Timely at " + host + ":" + port + ". Error: " + e.getMessage());
} finally {
pooledObject.invalidate();
;
}
}
@@ -107,17 +107,17 @@ public class PooledSocketFactory implements PooledObjectFactory<Socket> {
return false;
}
}
Socket socket = pooledObject.getObject();
Socket socket = (Socket) pooledObject.getObject();
return socket != null && !socket.isClosed();
}
@Override
public void activateObject(PooledObject<Socket> pooledObject) {
public void activateObject(PooledObject<Socket> pooledObject) throws Exception {
// do nothing
}
@Override
public void passivateObject(PooledObject<Socket> pooledObject) {
public void passivateObject(PooledObject<Socket> pooledObject) throws Exception {
// do nothing
}
}
@@ -6,8 +6,6 @@ import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
@@ -16,9 +14,7 @@ import org.collectd.api.CollectdConfigInterface;
import org.collectd.api.CollectdShutdownInterface;
import org.collectd.api.CollectdWriteInterface;
import org.collectd.api.OConfigItem;
import org.collectd.api.OConfigValue;
import org.collectd.api.ValueList;
import org.slf4j.event.Level;
/**
*
@@ -39,15 +35,15 @@ public class WriteTimelyPlugin extends CollectDPluginParent implements CollectdC
Collectd.registerWrite(WriteTimelyPlugin.class.getName(), this);
}
@Override
public void write(MetricData metricData) {
public int write(ValueList vl) {
Socket socket = null;
try {
socket = socketPool.borrowObject();
if (socket == null) {
return;
return -1;
}
super.process(metricData, socket.getOutputStream());
super.process(vl, socket.getOutputStream());
} catch (Exception e) {
Collectd.logWarning(e.getMessage());
try {
@@ -59,16 +55,12 @@ public class WriteTimelyPlugin extends CollectDPluginParent implements CollectdC
} catch (IOException e1) {
Collectd.logError(e1.getMessage());
}
return -1;
} finally {
if (socket != null) {
socketPool.returnObject(socket);
}
}
}
public int write(ValueList vl) {
MetricData metricData = new MetricData(vl);
write(metricData);
return 0;
}
@@ -88,23 +80,10 @@ public class WriteTimelyPlugin extends CollectDPluginParent implements CollectdC
}
public int config(OConfigItem config) {
Map<String,Object> configMap = new HashMap<>();
for (OConfigItem c : config.getChildren()) {
String key = c.getKey();
OConfigValue value = c.getValues().get(0);
int type = value.getType();
if (type == 0) {
configMap.put(key, value.getString());
} else if (type == 1) {
configMap.put(key, value.getNumber());
} else if (type == 2) {
configMap.put(key, value.getBoolean());
}
}
int retval;
super.config(config);
int retval = 0;
PooledSocketFactory socketFactory = new PooledSocketFactory();
retval = socketFactory.config(configMap);
retval = socketFactory.config(config);
if (retval == 0) {
GenericObjectPoolConfig<Socket> poolConfig = new GenericObjectPoolConfig<>();
// use max size for maxTotal and maxIdle
@@ -114,24 +93,7 @@ public class WriteTimelyPlugin extends CollectDPluginParent implements CollectdC
poolConfig.setTestOnReturn(true);
socketPool = new GenericObjectPool<>(socketFactory, poolConfig);
}
super.config(configMap);
return retval;
}
public void log(Level level, String s) {
// collectd must be compiled with debug enabled in order for log level debug to work
if (isDebug()) {
Collectd.logInfo(s);
} else {
if (level.equals(Level.DEBUG)) {
Collectd.logDebug(s);
} else if (level.equals(Level.INFO)) {
Collectd.logInfo(s);
} else if (level.equals(Level.WARN)) {
Collectd.logWarning(s);
} else if (level.equals(Level.ERROR)) {
Collectd.logError(s);
}
}
}
}
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<FindBugsFilter>
<Match>
<Bug pattern="DM_DEFAULT_ENCODING"/>
<Method name="connect" />
<Class name="timely.collectd.plugin.WriteTimelyPlugin"/>
</Match>
</FindBugsFilter>
@@ -21,9 +21,12 @@ import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import net.jcip.annotations.NotThreadSafe;
@RunWith(PowerMockRunner.class)
@PrepareForTest({Collectd.class})
@PowerMockIgnore({"javax.management.*", "jdk.internal.reflect.*"})
@NotThreadSafe
@PowerMockIgnore("javax.management.*")
public class WriteTimelyPluginTest {
private WriteTimelyPlugin plugin = null;
@@ -0,0 +1,24 @@
Configuration:
status: warn
monitorInterval: 60
Properties:
Property:
- name: PID
value: "????"
- name: LOG_PATTERN
value: "%clr{%d{yyyy-MM-dd HH:mm:ss.SSS}}{faint} %clr{%5p} %clr{${sys:PID}}{magenta} %clr{---}{faint} %clr{[%15.15t]}{faint} %clr{%-40.40c{1.}}{cyan} %clr{:}{faint} %m%n%wEx"
Appenders:
Console:
name: Console
target: SYSTEM_OUT
follow: true
PatternLayout:
pattern: "${LOG_PATTERN}"
Loggers:
Root:
level: warn
AppenderRef:
- ref: Console
@@ -1,133 +0,0 @@
package timely.collectd.plugin;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.tuple.Pair;
import org.collectd.api.ValueList;
public class MetricData {
public static final String COUNTER = "COUNTER";
public static final String GAUGE = "GAUGE";
public static final String DERIVE = "DERIVE";
public static final String ABSOLUTE = "ABSOLUTE";
private Long timestamp;
private String host;
private String plugin;
private String pluginInstance;
private String type;
private String typeInstance;
private List<Pair<Double,String>> valuePairs = new ArrayList<>();
public MetricData(Long timestamp, String host, String plugin, String pluginInstance, String type, String typeInstance, List<Double> values,
List<String> valueTypes) {
this.timestamp = timestamp;
this.host = host;
this.plugin = plugin;
this.pluginInstance = pluginInstance;
this.type = type;
this.typeInstance = typeInstance;
for (int x = 0; x < values.size(); x++) {
Double value = values.get(x);
String valueType = valueTypes.size() > x ? valueTypes.get(x) : "GAUGE";
valuePairs.add(Pair.of(value, valueType));
}
}
public MetricData(ValueList vl) {
this.timestamp = vl.getTime();
this.host = vl.getHost();
this.plugin = vl.getPlugin();
this.pluginInstance = vl.getPluginInstance();
this.type = vl.getType();
this.typeInstance = vl.getTypeInstance();
List<Number> valueList = vl.getValues();
List<String> valueTypes = vl.getDataSet().getDataSources().stream().map(ds -> convertType(ds.getType())).collect(Collectors.toList());
for (int x = 0; x < vl.getValues().size(); x++) {
Number n = valueList.get(x);
String type = valueTypes.size() > x ? valueTypes.get(x) : "GAUGE";
valuePairs.add(Pair.of(n.doubleValue(), type));
}
}
public Long getTimestamp() {
return timestamp;
}
public void setTimestamp(Long timestamp) {
this.timestamp = timestamp;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public String getPlugin() {
return plugin;
}
public void setPlugin(String plugin) {
this.plugin = plugin;
}
public String getPluginInstance() {
return pluginInstance;
}
public void setPluginInstance(String pluginInstance) {
this.pluginInstance = pluginInstance;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getTypeInstance() {
return typeInstance;
}
public void setTypeInstance(String typeInstance) {
this.typeInstance = typeInstance;
}
public List<Pair<Double,String>> getValuePairs() {
return valuePairs;
}
public void setValuePairs(List<Pair<Double,String>> valuePairs) {
this.valuePairs = valuePairs;
}
public static String convertType(int type) {
switch (type) {
case 0:
return COUNTER;
case 1:
return GAUGE;
case 2:
return DERIVE;
case 3:
return ABSOLUTE;
default:
return GAUGE;
}
}
@Override
public String toString() {
return new ToStringBuilder(this).append("timestamp", timestamp).append("host", host).append("plugin", plugin).append("pluginInstance", pluginInstance)
.append("type", type).append("typeInstance", typeInstance).append("valuePairs", valuePairs).toString();
}
}
@@ -1,8 +0,0 @@
package timely.collectd.plugin;
import org.slf4j.event.Level;
public interface PluginLogger {
void log(Level level, String s);
}
@@ -1,6 +0,0 @@
package timely.collectd.plugin;
public interface WritesMetrics {
void write(MetricData metricData);
}
-15
View File
@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<FindBugsFilter>
<Match>
<Class name="timely.collectd.plugin.MetricData" />
<Bug pattern="EI_EXPOSE_REP" />
</Match>
<Match>
<Class name="timely.collectd.plugin.MetricData" />
<Bug pattern="EI_EXPOSE_REP2" />
</Match>
<Match>
<Class name="timely.collectd.plugin.CollectDPluginParent" />
<Bug pattern="EI_EXPOSE_REP2" />
</Match>
</FindBugsFilter>
@@ -1,3 +0,0 @@
sys.ethstat.*_to_.*
sys.interface.if_packets
sys.interface.if_octets
@@ -1,3 +0,0 @@
sys.interface.if_packets sampleType,instance
sys.interface.if_octets sampleType,instance
sys.ethstat.rx_queue_bytes sampleType,instance
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>2.2.6-SNAPSHOT</version>
<version>3.0.0-SNAPSHOT</version>
</parent>
<artifactId>timely-common</artifactId>
<dependencies>
@@ -1,63 +1,10 @@
package timely.api.request;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.util.ReferenceCounted;
public interface HttpRequest extends Request, ReferenceCounted {
public interface HttpRequest extends Request {
public void setHttpRequest(FullHttpRequest httpRequest);
public FullHttpRequest getHttpRequest();
default int refCnt() {
if (getHttpRequest() != null) {
return getHttpRequest().refCnt();
} else {
return 1;
}
}
default ReferenceCounted retain() {
if (getHttpRequest() != null) {
getHttpRequest().retain();
}
return this;
}
default ReferenceCounted retain(int increment) {
if (getHttpRequest() != null) {
getHttpRequest().retain(increment);
}
return this;
}
default ReferenceCounted touch() {
if (getHttpRequest() != null) {
getHttpRequest().touch();
}
return this;
}
default ReferenceCounted touch(Object hint) {
if (getHttpRequest() != null) {
getHttpRequest().touch(hint);
}
return this;
}
default boolean release() {
if (getHttpRequest() != null) {
return getHttpRequest().release();
} else {
return false;
}
}
default boolean release(int decrement) {
if (getHttpRequest() != null) {
return getHttpRequest().release(decrement);
} else {
return false;
}
}
}
@@ -0,0 +1,5 @@
package timely.api.response;
public class StrictTransportResponse {
}
@@ -1,7 +1,5 @@
package timely.common.configuration;
import static org.apache.accumulo.core.conf.ConfigurationTypeHelper.getTimeInMillis;
import org.apache.accumulo.core.client.Accumulo;
import org.apache.accumulo.core.client.AccumuloClient;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
@@ -18,13 +16,7 @@ public class AccumuloConfiguration {
@Bean
@ConditionalOnMissingBean
public AccumuloClient accumuloClient(AccumuloProperties accumuloProperties, ZookeeperProperties zookeeperProperties) {
// @formatter:off
return Accumulo.newClient()
.to(accumuloProperties.getInstanceName(), zookeeperProperties.getServers())
.as(accumuloProperties.getUsername(), accumuloProperties.getPassword())
.batchScannerQueryThreads(accumuloProperties.getScan().getThreads())
.zkTimeout(Long.valueOf(getTimeInMillis(zookeeperProperties.getTimeout())).intValue())
.build();
// @formatter:on
return Accumulo.newClient().to(accumuloProperties.getInstanceName(), zookeeperProperties.getServers())
.as(accumuloProperties.getUsername(), accumuloProperties.getPassword()).build();
}
}
@@ -20,6 +20,7 @@ public class HttpProperties {
private Integer basePort;
@NotNull
private String host;
private String redirectPath = "/secure-me";
private long strictTransportMaxAge = 604800;
@Valid
@NestedConfigurationProperty
@@ -57,6 +58,14 @@ public class HttpProperties {
this.host = host;
}
public String getRedirectPath() {
return redirectPath;
}
public void setRedirectPath(String redirectPath) {
this.redirectPath = redirectPath;
}
public long getStrictTransportMaxAge() {
return strictTransportMaxAge;
}
@@ -21,7 +21,8 @@ public class SslServerProperties implements SslProperties {
private static final Logger log = LoggerFactory.getLogger(SslServerProperties.class);
private String[] ciphers = new String[] {"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"};
private String[] ciphers = new String[] {"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "TLS_RSA_WITH_AES_128_GCM_SHA256",
"TLS_RSA_WITH_AES_128_CBC_SHA", "SSL_RSA_WITH_3DES_EDE_CBC_SHA"};
private String[] enabledProtocols;
private String keyStoreType;
private String keyStoreFile;
@@ -1,12 +1,8 @@
package timely.common.configuration;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import timely.util.Exclusions;
@Configuration
@EnableConfigurationProperties({AccumuloProperties.class, CacheProperties.class, CorsProperties.class, HttpProperties.class, MetaCacheProperties.class,
RestClientProperties.class, ScanProperties.class, SecurityProperties.class, ServerProperties.class, SslClientProperties.class,
@@ -14,15 +10,4 @@ import timely.util.Exclusions;
public class TimelyCommonConfiguration {
@Bean
public Exclusions exclusions(TimelyProperties timelyProperties) {
Exclusions exclusions = new Exclusions();
if (StringUtils.isNotBlank(timelyProperties.getFilteredMetricsFile())) {
exclusions.setFilteredMetricsFile(timelyProperties.getFilteredMetricsFile());
}
if (StringUtils.isNotBlank(timelyProperties.getFilteredTagsFile())) {
exclusions.setFilteredTagsFile(timelyProperties.getFilteredTagsFile());
}
return exclusions;
}
}
@@ -24,9 +24,6 @@ public class TimelyProperties {
private int instance = 1;
private int portIncrement = 100;
private String defaultVisibility = null;
private int tagFilterCacheSize = 10000;
private String filteredMetricsFile = null;
private String filteredTagsFile = null;
public boolean isTest() {
return test;
@@ -94,28 +91,4 @@ public class TimelyProperties {
this.metricsReportIgnoredTags = metricsReportIgnoredTags;
return this;
}
public void setTagFilterCacheSize(int tagFilterCacheSize) {
this.tagFilterCacheSize = tagFilterCacheSize;
}
public int getTagFilterCacheSize() {
return tagFilterCacheSize;
}
public void setFilteredMetricsFile(String filteredMetricsFile) {
this.filteredMetricsFile = filteredMetricsFile;
}
public String getFilteredMetricsFile() {
return filteredMetricsFile;
}
public void setFilteredTagsFile(String filteredTagsFile) {
this.filteredTagsFile = filteredTagsFile;
}
public String getFilteredTagsFile() {
return filteredTagsFile;
}
}
@@ -1,6 +1,7 @@
package timely.model;
import java.io.Serializable;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.TreeSet;
@@ -24,6 +25,12 @@ import timely.model.parse.TagParser;
@JsonPropertyOrder({"name"})
public class Metric implements Serializable {
// Capture the time that this class is loaded by the JVM for the purposes of
// comparing the timestamp. Some systems, like Telegraf's OpenTSDB output
// plugin, send the timestamp in seconds.
private static final long TIME_LOADED = System.currentTimeMillis();
private static final long TS_THRESHOLD = Duration.ofMillis(TIME_LOADED).minusDays(365).toMillis();
private static final long serialVersionUID = 1L;
private String name;
@@ -176,6 +183,9 @@ public class Metric implements Serializable {
}
public Builder value(long timestamp, double measure) {
if (timestamp <= TS_THRESHOLD) {
timestamp = timestamp * 1000;
}
metric.setValue(new Value(timestamp, measure));
return this;
}
+1 -2
View File
@@ -7,7 +7,6 @@ import java.util.stream.Stream;
import org.apache.commons.lang3.builder.CompareToBuilder;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
@@ -91,7 +90,7 @@ public class Tag implements Comparable<Tag>, Serializable {
@Override
public int hashCode() {
return new HashCodeBuilder().append(this.key).append(this.value).toHashCode();
return this.toString().hashCode();
}
@Override
@@ -4,7 +4,6 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import timely.model.Tag;
@@ -25,7 +24,9 @@ public class TagListParser implements Parser<List<Tag>>, Combiner<Collection<Tag
if (!p.isEmpty()) {
Tag tag = tagParser.parse(p);
// unescape commas in the key and value of the tag
tags.add(unescapeCommas(tag));
tag.setKey(tag.getKey().replaceAll("\\\\,", ","));
tag.setValue(tag.getValue().replaceAll("\\\\,", ","));
tags.add(tag);
}
}
return tags;
@@ -35,9 +36,7 @@ public class TagListParser implements Parser<List<Tag>>, Combiner<Collection<Tag
public String combine(Collection<Tag> tags) {
StringBuilder builder = new StringBuilder();
if (!tags.isEmpty()) {
tags.forEach(p -> {
builder.append(tagParser.combine(escapeCommas(p))).append(',');
});
tags.forEach(p -> builder.append(tagParser.combine(p)).append(','));
builder.setLength(builder.length() - 1);
}
return builder.toString();
@@ -49,26 +48,11 @@ public class TagListParser implements Parser<List<Tag>>, Combiner<Collection<Tag
* @return string representation
*/
public String combine(Map<String,String> tags) {
return combine(tags.entrySet().stream().map(e -> new Tag(e.getKey(), e.getValue())).collect(Collectors.toList()));
}
private Tag escapeCommas(Tag tag) {
return escapeCommas(tag.getKey(), tag.getValue());
}
private Tag escapeCommas(String k, String v) {
k = k.replaceAll(",", "\\\\,");
v = v.replaceAll(",", "\\\\,");
return new Tag(k, v);
}
private Tag unescapeCommas(Tag tag) {
return unescapeCommas(tag.getKey(), tag.getValue());
}
private Tag unescapeCommas(String k, String v) {
k = k.replaceAll("\\\\,", ",");
v = v.replaceAll("\\\\,", ",");
return new Tag(k, v);
StringBuilder builder = new StringBuilder();
if (!tags.isEmpty()) {
tags.forEach((k, v) -> builder.append(tagParser.combine(new Tag(k, v))).append(','));
builder.setLength(builder.length() - 1);
}
return builder.toString();
}
}
@@ -2,7 +2,7 @@ package timely.netty;
public class Constants {
public static final String ERR_WRITING_RESPONSE = "Error writing response to pipeline: %s";
public static final String ERR_WRITING_RESPONSE = "Error writing response to pipeline: {}";
public static final String TEXT_TYPE = "text/plain";
public static final String HTML_TYPE = "text/html";
public static final String JSON_TYPE = "application/json";
@@ -27,6 +27,7 @@ import timely.api.request.AuthenticatedRequest;
import timely.api.request.HttpGetRequest;
import timely.api.request.HttpPostRequest;
import timely.api.request.HttpRequest;
import timely.api.response.StrictTransportResponse;
import timely.api.response.TimelyException;
import timely.auth.SubjectIssuerDNPair;
import timely.auth.TimelyAuthenticationToken;
@@ -44,11 +45,13 @@ public class HttpRequestDecoder extends MessageToMessageDecoder<FullHttpRequest>
private static final String LOG_PARSED_REQUEST = "Parsed request {}";
protected SecurityProperties securityProperties;
private final String nonSecureRedirectAddress;
private AuthenticationService authenticationService;
public HttpRequestDecoder(AuthenticationService authenticationService, SecurityProperties securityProperties, HttpProperties httpProperties) {
this.securityProperties = securityProperties;
this.nonSecureRedirectAddress = httpProperties.getRedirectPath();
this.authenticationService = authenticationService;
}
@@ -79,6 +82,11 @@ public class HttpRequestDecoder extends MessageToMessageDecoder<FullHttpRequest>
final String uri = msg.uri();
final QueryStringDecoder decoder = new QueryStringDecoder(uri);
if (decoder.path().equals(nonSecureRedirectAddress)) {
out.add(new StrictTransportResponse());
return;
}
final String sessionId = getSessionId(msg);
log.trace("SessionID: " + sessionId);
@@ -0,0 +1,385 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package timely.netty.http;
import static io.netty.handler.codec.http.HttpHeaderNames.CACHE_CONTROL;
import static io.netty.handler.codec.http.HttpHeaderNames.CONNECTION;
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;
import static io.netty.handler.codec.http.HttpHeaderNames.DATE;
import static io.netty.handler.codec.http.HttpHeaderNames.EXPIRES;
import static io.netty.handler.codec.http.HttpHeaderNames.IF_MODIFIED_SINCE;
import static io.netty.handler.codec.http.HttpHeaderNames.LAST_MODIFIED;
import static io.netty.handler.codec.http.HttpHeaderNames.LOCATION;
import static io.netty.handler.codec.http.HttpMethod.GET;
import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST;
import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN;
import static io.netty.handler.codec.http.HttpResponseStatus.FOUND;
import static io.netty.handler.codec.http.HttpResponseStatus.INTERNAL_SERVER_ERROR;
import static io.netty.handler.codec.http.HttpResponseStatus.METHOD_NOT_ALLOWED;
import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND;
import static io.netty.handler.codec.http.HttpResponseStatus.NOT_MODIFIED;
import static io.netty.handler.codec.http.HttpResponseStatus.OK;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.TimeZone;
import java.util.regex.Pattern;
import javax.activation.MimetypesFileTypeMap;
import javax.net.ssl.SSLHandshakeException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.DefaultHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpChunkedInput;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpUtil;
import io.netty.handler.stream.ChunkedFile;
import io.netty.util.CharsetUtil;
import io.netty.util.internal.SystemPropertyUtil;
import timely.netty.Constants;
/**
* A simple handler that serves incoming HTTP requests to send their respective HTTP responses. It also implements {@code 'If-Modified-Since'} header to take
* advantage of browser cache, as described in <a href="http://tools.ietf.org/html/rfc2616#section-14.25">RFC 2616</a>.
*
* <h3>How Browser Caching Works</h3>
*
* Web browser caching works with HTTP headers as illustrated by the following sample:
* <ol>
* <li>Request #1 returns the content of {@code /file1.txt}.</li>
* <li>Contents of {@code /file1.txt} is cached by the browser.</li>
* <li>Request #2 for {@code /file1.txt} does return the contents of the file again. Rather, a 304 Not Modified is returned. This tells the browser to use the
* contents stored in its cache.</li>
* <li>The server knows the file has not been modified because the {@code If-Modified-Since} date is the same as the file's last modified date.</li>
* </ol>
*
* <pre>
* Request #1 Headers
* ===================
* GET /file1.txt HTTP/1.1
*
* Response #1 Headers
* ===================
* HTTP/1.1 200 OK
* Date: Tue, 01 Mar 2011 22:44:26 GMT
* Last-Modified: Wed, 30 Jun 2010 21:36:48 GMT
* Expires: Tue, 01 Mar 2012 22:44:26 GMT
* Cache-Control: private, max-age=31536000
*
* Request #2 Headers
* ===================
* GET /file1.txt HTTP/1.1
* If-Modified-Since: Wed, 30 Jun 2010 21:36:48 GMT
*
* Response #2 Headers
* ===================
* HTTP/1.1 304 Not Modified
* Date: Tue, 01 Mar 2011 22:44:28 GMT
*
* </pre>
*/
public class HttpStaticFileServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> implements TimelyHttpHandler {
private static final Logger log = LoggerFactory.getLogger(HttpStaticFileServerHandler.class);
public static final String HTTP_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz";
public static final String HTTP_DATE_GMT_TIMEZONE = "GMT";
public static final int HTTP_CACHE_SECONDS = 60;
private boolean ignoreSslHandshakeErrors = false;
@Override
public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
if (!request.decoderResult().isSuccess()) {
sendError(ctx, BAD_REQUEST);
return;
}
if (request.method() != GET) {
sendError(ctx, METHOD_NOT_ALLOWED);
return;
}
String uri = request.uri();
if (uri.startsWith("/favicon.ico")) {
uri = uri.replaceFirst("\\/", "/webapp/");
}
if (!uri.startsWith("/webapp")) {
sendError(ctx, FORBIDDEN);
return;
}
final String path = sanitizeUri(uri);
if (path == null) {
sendError(ctx, FORBIDDEN);
return;
}
log.trace("Looking for requested file at: " + path);
File file = new File(path);
if (file.isHidden() || !file.exists()) {
sendError(ctx, NOT_FOUND);
return;
}
if (file.isDirectory()) {
if (uri.endsWith("/")) {
sendListing(ctx, file);
} else {
sendRedirect(ctx, uri + '/');
}
return;
}
if (!file.isFile()) {
sendError(ctx, FORBIDDEN);
return;
}
// Cache Validation
String ifModifiedSince = request.headers().get(IF_MODIFIED_SINCE);
if (ifModifiedSince != null && !ifModifiedSince.isEmpty()) {
SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US);
Date ifModifiedSinceDate = dateFormatter.parse(ifModifiedSince);
// Only compare up to the second because the datetime format we send
// to the client
// does not have milliseconds
long ifModifiedSinceDateSeconds = ifModifiedSinceDate.getTime() / 1000;
long fileLastModifiedSeconds = file.lastModified() / 1000;
if (ifModifiedSinceDateSeconds == fileLastModifiedSeconds) {
sendNotModified(ctx);
return;
}
}
RandomAccessFile raf;
try {
raf = new RandomAccessFile(file, "r");
} catch (FileNotFoundException ignore) {
sendError(ctx, NOT_FOUND);
return;
}
long fileLength = raf.length();
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
HttpUtil.setContentLength(response, fileLength);
setContentTypeHeader(response, file);
setDateAndCacheHeaders(response, file);
HttpUtil.setTransferEncodingChunked(response, true);
if (HttpUtil.isKeepAlive(request)) {
response.headers().set(CONNECTION, HttpHeaderValues.KEEP_ALIVE);
}
// Write the initial line and the header.
sendResponse(ctx, response);
log.trace("Returning {}", response);
// Write the content.
sendResponse(ctx, new HttpChunkedInput(new ChunkedFile(raf, 0, fileLength, 8192)));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
// ignore SSLHandshakeException when using a self-signed server certificate
if (ignoreSslHandshakeErrors && cause.getCause() instanceof SSLHandshakeException) {
return;
}
log.error("Error serving file", cause);
if (ctx.channel().isActive()) {
sendError(ctx, INTERNAL_SERVER_ERROR);
}
}
private static final Pattern INSECURE_URI = Pattern.compile(".*[<>&\"].*");
private static String sanitizeUri(String uri) {
// Decode the path.
try {
uri = URLDecoder.decode(uri, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new Error(e);
}
if (uri.isEmpty() || uri.charAt(0) != '/') {
return null;
}
// Convert file separators.
uri = uri.replace('/', File.separatorChar);
// Simplistic dumb security check.
// You will have to do something serious in the production environment.
if (uri.contains(File.separator + '.') || uri.contains('.' + File.separator) || uri.charAt(0) == '.' || uri.charAt(uri.length() - 1) == '.'
|| INSECURE_URI.matcher(uri).matches()) {
return null;
}
// Convert to absolute path.
return SystemPropertyUtil.get("user.dir") + File.separator + uri;
}
private static final Pattern ALLOWED_FILE_NAME = Pattern.compile("[A-Za-z0-9][-_A-Za-z0-9\\.]*");
private static void sendListing(ChannelHandlerContext ctx, File dir) {
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK);
response.headers().set(CONTENT_TYPE, "text/html; charset=UTF-8");
String dirPath = dir.getPath();
StringBuilder buf = new StringBuilder().append("<!DOCTYPE html>\r\n").append("<html><head><title>").append("Listing of: ").append(dirPath)
.append("</title></head><body>\r\n")
.append("<h3>Listing of: ").append(dirPath).append("</h3>\r\n")
.append("<ul>").append("<li><a href=\"../\">..</a></li>\r\n");
File[] files = dir.listFiles();
if (null != files) {
for (File f : files) {
if (f.isHidden() || !f.canRead()) {
continue;
}
String name = f.getName();
if (!ALLOWED_FILE_NAME.matcher(name).matches()) {
continue;
}
buf.append("<li><a href=\"").append(name).append("\">").append(name).append("</a></li>\r\n");
}
}
buf.append("</ul></body></html>\r\n");
ByteBuf buffer = Unpooled.copiedBuffer(buf, CharsetUtil.UTF_8);
response.content().writeBytes(buffer);
buffer.release();
// Close the connection as soon as the error message is sent.
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
log.trace(Constants.LOG_RETURNING_RESPONSE, response);
}
private static void sendRedirect(ChannelHandlerContext ctx, String newUri) {
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, FOUND);
response.headers().set(LOCATION, newUri);
// Close the connection as soon as the error message is sent.
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
log.trace(Constants.LOG_RETURNING_RESPONSE, response);
}
private static void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) {
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, status, Unpooled.copiedBuffer("Failure: " + status + "\r\n", CharsetUtil.UTF_8));
response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8");
// Close the connection as soon as the error message is sent.
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
log.trace(Constants.LOG_RETURNING_RESPONSE, response);
}
/**
* When file timestamp is the same as what the browser is sending up, send a "304 Not Modified"
*
* @param ctx
* Context
*/
private static void sendNotModified(ChannelHandlerContext ctx) {
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, NOT_MODIFIED);
setDateHeader(response);
// Close the connection as soon as the error message is sent.
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
log.trace(Constants.LOG_RETURNING_RESPONSE, response);
}
/**
* Sets the Date header for the HTTP response
*
* @param response
* HTTP response
*/
private static void setDateHeader(FullHttpResponse response) {
SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US);
dateFormatter.setTimeZone(TimeZone.getTimeZone(HTTP_DATE_GMT_TIMEZONE));
Calendar time = new GregorianCalendar();
response.headers().set(DATE, dateFormatter.format(time.getTime()));
}
/**
* Sets the Date and Cache headers for the HTTP Response
*
* @param response
* HTTP response
* @param fileToCache
* file to extract content type
*/
private static void setDateAndCacheHeaders(HttpResponse response, File fileToCache) {
SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US);
dateFormatter.setTimeZone(TimeZone.getTimeZone(HTTP_DATE_GMT_TIMEZONE));
// Date header
Calendar time = new GregorianCalendar();
response.headers().set(DATE, dateFormatter.format(time.getTime()));
// Add cache headers
time.add(Calendar.SECOND, HTTP_CACHE_SECONDS);
response.headers().set(EXPIRES, dateFormatter.format(time.getTime()));
response.headers().set(CACHE_CONTROL, "private, max-age=" + HTTP_CACHE_SECONDS);
response.headers().set(LAST_MODIFIED, dateFormatter.format(new Date(fileToCache.lastModified())));
}
/**
* Sets the content type header for the HTTP Response
*
* @param response
* HTTP response
* @param file
* file to extract content type
*/
private static void setContentTypeHeader(HttpResponse response, File file) {
MimetypesFileTypeMap mimeTypesMap = new MimetypesFileTypeMap();
response.headers().set(CONTENT_TYPE, mimeTypesMap.getContentType(file.getPath()));
}
public HttpStaticFileServerHandler setIgnoreSslHandshakeErrors(boolean ignoreSslHandshakeErrors) {
this.ignoreSslHandshakeErrors = ignoreSslHandshakeErrors;
return this;
}
}
@@ -1,32 +0,0 @@
package timely.netty.http;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.HttpResponse;
import timely.common.configuration.HttpProperties;
public class HttpStrictTransportSecurityHandler extends ChannelOutboundHandlerAdapter {
public static final String HSTS_HEADER_NAME = "Strict-Transport-Security";
private final String HSTS_HEADER_VALUE;
public HttpStrictTransportSecurityHandler(HttpProperties httpProperties) {
this.HSTS_HEADER_VALUE = String.format("max-age=%d; includeSubDomains; preload", httpProperties.getStrictTransportMaxAge());
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
// Check if the connection is SSL/TLS encrypted
boolean isHttps = ctx.channel().pipeline().get(io.netty.handler.ssl.SslHandler.class) != null;
if (isHttps && msg instanceof HttpResponse) {
HttpResponse response = (HttpResponse) msg;
// Add the HSTS header only if it isn't already present
if (!response.headers().contains(HSTS_HEADER_NAME)) {
response.headers().set(HSTS_HEADER_NAME, HSTS_HEADER_VALUE);
}
}
// Forward the message to the next handler in the pipeline
super.write(ctx, msg, promise);
}
}
@@ -1,52 +0,0 @@
package timely.netty.http;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.ssl.OptionalSslHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;
import timely.netty.Constants;
public class NonSslForbiddenHandler extends OptionalSslHandler {
private static final Logger log = LoggerFactory.getLogger(NonSslForbiddenHandler.class);
public NonSslForbiddenHandler(SslContext sslContext) {
super(sslContext);
}
@Override
protected String newSslHandlerName() {
return "ssl";
}
@Override
protected String newNonSslHandlerName() {
return "nonSslForbidden";
}
@Override
protected ChannelHandler newNonSslHandler(ChannelHandlerContext context) {
return new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
try {
log.trace("Received non-SSL request, returning forbidden");
String response = "HTTP/1.1 403 Forbidden\r\nContent-Length: 0\r\n\r\n";
log.trace(Constants.LOG_RETURNING_RESPONSE, response);
ctx.writeAndFlush(Unpooled.copiedBuffer(response, CharsetUtil.UTF_8)).addListener(ChannelFutureListener.CLOSE);
} finally {
ReferenceCountUtil.release(msg);
}
}
};
}
}
@@ -0,0 +1,62 @@
package timely.netty.http;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.ssl.OptionalSslHandler;
import io.netty.handler.ssl.SslContext;
import timely.common.configuration.HttpProperties;
import timely.netty.Constants;
public class NonSslRedirectHandler extends OptionalSslHandler implements TimelyHttpHandler {
private static final Logger log = LoggerFactory.getLogger(NonSslRedirectHandler.class);
private final String redirectAddress;
public NonSslRedirectHandler(HttpProperties httpProperties, SslContext sslContext) {
super(sslContext);
String timelyHost = httpProperties.getHost();
int timelyPort = httpProperties.getPort();
String path = httpProperties.getRedirectPath();
redirectAddress = "https://" + timelyHost + ":" + timelyPort + path;
}
@Override
protected String newSslHandlerName() {
return "ssl";
}
@Override
protected String newNonSslHandlerName() {
return "ssl";
}
@Override
protected ChannelHandler newNonSslHandler(ChannelHandlerContext context) {
return new ChannelInboundHandlerAdapter() {
private HttpResponseEncoder encoder = new HttpResponseEncoder();
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
log.trace("Received non-SSL request, returning redirect");
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.MOVED_PERMANENTLY, Unpooled.EMPTY_BUFFER);
response.headers().set(HttpHeaderNames.LOCATION, redirectAddress);
log.trace(Constants.LOG_RETURNING_RESPONSE, response);
encoder.write(ctx, response, ctx.voidPromise());
ctx.flush();
}
};
}
}
@@ -1,46 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package timely.netty.http;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import timely.netty.Constants;
public class NotFoundHandler extends SimpleChannelInboundHandler<FullHttpRequest> implements TimelyHttpHandler {
private static final Logger log = LoggerFactory.getLogger(NotFoundHandler.class);
@Override
public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
// if the request reaches this handler, it means that no other handler has processed it, so we return a 404 Not Found response
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.NOT_FOUND, Unpooled.EMPTY_BUFFER);
response.headers().set(HttpHeaderNames.CONTENT_LENGTH, 0);
log.trace(Constants.LOG_RETURNING_RESPONSE, response);
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
}
@@ -23,27 +23,21 @@ public interface TimelyHttpHandler {
Logger log = LoggerFactory.getLogger(TimelyHttpHandler.class);
default void sendHttpError(ChannelHandlerContext ctx, TimelyException e) throws JsonProcessingException {
try {
log.error("Error in pipeline, response code: {}, message: {}", e.getCode(), e.getMessage());
byte[] buf = JsonUtil.getObjectMapper().writeValueAsBytes(new TimelyExceptionResponse(e));
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.valueOf(e.getCode()), Unpooled.copiedBuffer(buf));
response.headers().set(HttpHeaderNames.CONTENT_TYPE, Constants.JSON_TYPE);
response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
e.getResponseHeaders().entrySet().forEach(entry -> response.headers().set(entry.getKey(), entry.getValue()));
// Send the error response
sendResponse(ctx, response);
} catch (Exception ex) {
log.error(ex.getMessage(), ex);
}
log.error("Error in pipeline, response code: {}, message: {}", e.getCode(), e.getMessage());
byte[] buf = JsonUtil.getObjectMapper().writeValueAsBytes(new TimelyExceptionResponse(e));
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.valueOf(e.getCode()), Unpooled.copiedBuffer(buf));
response.headers().set(HttpHeaderNames.CONTENT_TYPE, Constants.JSON_TYPE);
response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
e.getResponseHeaders().entrySet().forEach(entry -> response.headers().set(entry.getKey(), entry.getValue()));
// Send the error response
sendResponse(ctx, response);
}
default void sendResponse(ChannelHandlerContext ctx, Object msg) {
ChannelFuture cf = ctx.writeAndFlush(msg);
ChannelFuture f = ctx.writeAndFlush(msg);
log.trace(Constants.LOG_RETURNING_RESPONSE, msg);
if (!cf.isSuccess()) {
Throwable t = cf.cause();
String message = (t == null) ? "" : t.getMessage();
log.error(String.format(Constants.ERR_WRITING_RESPONSE, message), cf.cause());
if (!f.isSuccess()) {
log.error(Constants.ERR_WRITING_RESPONSE, f.cause());
}
}
@@ -1,149 +0,0 @@
package timely.util;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import timely.model.Metric;
import timely.model.Tag;
import timely.model.parse.MetricParser;
public class Exclusions {
private static final Logger log = LoggerFactory.getLogger(Exclusions.class);
private static final MetricParser metricParser = new MetricParser();
private List<String> filteredMetrics = new ArrayList<>();
private Map<String,Set<String>> filteredTags = new LinkedHashMap<>();
public interface LineHandler {
void handleLine(String line);
}
public void readFile(String file, LineHandler lineHandler) {
File f = new File(file);
if (f.exists() && f.isFile() && f.canRead()) {
try (InputStream iStream = new FileInputStream(f);
InputStreamReader streamReader = new InputStreamReader(iStream, StandardCharsets.UTF_8);
BufferedReader reader = new BufferedReader(streamReader)) {
String line;
while ((line = reader.readLine()) != null) {
try {
lineHandler.handleLine(line);
} catch (Exception e) {
log.error(MessageFormat.format("Error reading line {0}", line), e);
}
}
} catch (IOException e) {
log.error(MessageFormat.format("Error reading file {0}", file), e);
}
} else {
log.error(MessageFormat.format("File ''{0}'' either does not exist or is not readable", file));
}
}
public List<String> getFilteredMetrics(String filteredMetricsFile) {
List<String> filteredMetricRegexes = new ArrayList<>();
readFile(filteredMetricsFile, (line) -> {
if (!line.endsWith(".*")) {
line = line + ".*";
}
filteredMetricRegexes.add(line);
});
return filteredMetricRegexes;
}
public Map<String,Set<String>> getFilteredTags(String filteredTagsFile) {
Map<String,Set<String>> filteredTagsForMetrics = new LinkedHashMap<>();
readFile(filteredTagsFile, (line) -> {
int x = line.indexOf(" ");
String metric = line.substring(0, x);
String tags = line.substring(x + 1).trim();
Set<String> tagSet = Arrays.stream(tags.split(",")).map(tag -> tag.trim()).collect(Collectors.toSet());
filteredTagsForMetrics.put(metric, tagSet);
});
return filteredTagsForMetrics;
}
public void setFilteredMetricsFile(String filteredMetricsFile) {
this.filteredMetrics = getFilteredMetrics(filteredMetricsFile);
}
public void setFilteredTagsFile(String filteredTagsFile) {
this.filteredTags = getFilteredTags(filteredTagsFile);
}
// format put metric timestamp value tag1=value1 tag2=value2 tag3=value3
// where the tags are sorted
public boolean filterMetric(String metricPutLine) {
String metricLine = buildMetricLine(metricPutLine);
for (String filteredRegex : filteredMetrics) {
if (metricLine.matches(filteredRegex)) {
return true;
}
}
return false;
}
public boolean filterMetric(Metric metric) {
String metricLine = buildMetricLine(metric);
for (String filteredRegex : filteredMetrics) {
if (metricLine.matches(filteredRegex)) {
return true;
}
}
return false;
}
protected String buildMetricLine(String metricPutLine) {
Metric metric = metricParser.parse(metricPutLine);
return buildMetricLine(metric);
}
protected String buildMetricLine(Metric metric) {
StringBuilder sb = new StringBuilder();
sb.append(metric.getName()).append(" ");
metric.getTags().stream().forEach(t -> sb.append(t.join()).append(" "));
return sb.toString().trim();
}
public String filterExcludedTags(String metricLineOrig) {
if (filteredTags.isEmpty()) {
return metricLineOrig;
}
Metric metric = metricParser.parse(metricLineOrig);
filterExcludedTags(metric);
StringBuilder sb = new StringBuilder();
sb.append("put ");
sb.append(metric.getName()).append(" ");
sb.append(metric.getValue().getTimestamp()).append(" ");
sb.append(metric.getValue().getMeasure()).append(" ");
metric.getTags().stream().forEach(t -> sb.append(t.join()).append(" "));
return sb.toString().trim();
}
public void filterExcludedTags(Metric metric) {
if (!filteredTags.isEmpty()) {
Set<String> tags = filteredTags.get(metric.getName());
if (tags != null && !tags.isEmpty()) {
List<Tag> filteredTags = metric.getTags().stream().filter(t -> !tags.contains(t.getKey())).collect(Collectors.toList());
metric.setTags(filteredTags);
}
}
}
}
@@ -84,13 +84,13 @@ public class MetricAdapterTest {
@Test
public void testParse() throws Exception {
Long ts = System.currentTimeMillis();
PairLexicoder<String,Long> rowCoder = new PairLexicoder<>(new StringLexicoder(), new LongLexicoder());
byte[] row = rowCoder.encode(new ComparablePair<>("sys.cpu.user", 1000L));
byte[] row = rowCoder.encode(new ComparablePair<>("sys.cpu.user", ts));
byte[] value = new byte[Double.BYTES];
ByteBuffer.wrap(value).putDouble(2.0D);
PairLexicoder<Long,String> colQualCoder = new PairLexicoder<>(new LongLexicoder(), new StringLexicoder());
Key k = new Key(row, "tag1=value1".getBytes(), colQualCoder.encode(new ComparablePair<>(new Long(1000), "tag2=value2,tag3=value3")),
"(a&b)|(c&d)".getBytes(), 1000);
Key k = new Key(row, "tag1=value1".getBytes(), colQualCoder.encode(new ComparablePair<>(ts, "tag2=value2,tag3=value3")), "(a&b)|(c&d)".getBytes(), ts);
Value v = new Value(value);
Metric m = MetricAdapter.parse(k, v);
Assert.assertEquals("sys.cpu.user", m.getName());
@@ -99,19 +99,20 @@ public class MetricAdapterTest {
tags.add(new Tag("tag2=value2"));
tags.add(new Tag("tag3=value3"));
Assert.assertEquals(tags, m.getTags());
Assert.assertEquals(new Long(1000), m.getValue().getTimestamp());
Assert.assertEquals(ts, m.getValue().getTimestamp());
Assert.assertEquals(2.0D, m.getValue().getMeasure(), 0.0D);
}
@Test
public void testParseWithEscapes() {
Long ts = System.currentTimeMillis();
PairLexicoder<String,Long> rowCoder = new PairLexicoder<>(new StringLexicoder(), new LongLexicoder());
byte[] row = rowCoder.encode(new ComparablePair<>("sys.cpu.user", 1000L));
byte[] row = rowCoder.encode(new ComparablePair<>("sys.cpu.user", ts));
byte[] value = new byte[Double.BYTES];
ByteBuffer.wrap(value).putDouble(2.0D);
PairLexicoder<Long,String> colQualCoder = new PairLexicoder<>(new LongLexicoder(), new StringLexicoder());
Key k = new Key(row, "tag1=value1".getBytes(), colQualCoder.encode(new ComparablePair<>(new Long(1000), "tag2=test\\,value2,tag3=val\\=ue3")),
"(a&b)|(c&d)".getBytes(), 1000);
Key k = new Key(row, "tag1=value1".getBytes(), colQualCoder.encode(new ComparablePair<>(ts, "tag2=test\\,value2,tag3=val\\=ue3")),
"(a&b)|(c&d)".getBytes(), ts);
Value v = new Value(value);
Metric m = MetricAdapter.parse(k, v);
Assert.assertEquals("sys.cpu.user", m.getName());
@@ -120,19 +121,19 @@ public class MetricAdapterTest {
tags.add(new Tag("tag2=test,value2"));
tags.add(new Tag("tag3=val\\=ue3"));
Assert.assertEquals(tags, m.getTags());
Assert.assertEquals(new Long(1000), m.getValue().getTimestamp());
Assert.assertEquals(ts, m.getValue().getTimestamp());
Assert.assertEquals(2.0D, m.getValue().getMeasure(), 0.0D);
}
@Test
public void testParseWithViz() throws Exception {
Long ts = System.currentTimeMillis();
PairLexicoder<String,Long> rowCoder = new PairLexicoder<>(new StringLexicoder(), new LongLexicoder());
byte[] row = rowCoder.encode(new ComparablePair<>("sys.cpu.user", 1000L));
byte[] row = rowCoder.encode(new ComparablePair<>("sys.cpu.user", ts));
byte[] value = new byte[Double.BYTES];
ByteBuffer.wrap(value).putDouble(2.0D);
PairLexicoder<Long,String> colQualCoder = new PairLexicoder<>(new LongLexicoder(), new StringLexicoder());
Key k = new Key(row, "tag1=value1".getBytes(), colQualCoder.encode(new ComparablePair<>(new Long(1000), "tag2=value2,tag3=value3")),
"(a&b)|(c&d)".getBytes(), 1000);
Key k = new Key(row, "tag1=value1".getBytes(), colQualCoder.encode(new ComparablePair<>(ts, "tag2=value2,tag3=value3")), "(a&b)|(c&d)".getBytes(), ts);
Value v = new Value(value);
Metric m = MetricAdapter.parse(k, v, true);
Assert.assertEquals("sys.cpu.user", m.getName());
@@ -142,7 +143,7 @@ public class MetricAdapterTest {
tags.add(new Tag("tag3=value3"));
tags.add(new Tag("viz=(a&b)|(c&d)"));
Assert.assertEquals(tags, m.getTags());
Assert.assertEquals(new Long(1000), m.getValue().getTimestamp());
Assert.assertEquals(ts, m.getValue().getTimestamp());
Assert.assertEquals(2.0D, m.getValue().getMeasure(), 0.0D);
}
@@ -1,45 +0,0 @@
package timely.util;
import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.junit.Assert;
import org.junit.Test;
public class ExclusionTest {
@Test
public void testLoadFilteredMetricsFile() {
Exclusions exclusions = new Exclusions();
URL url = getClass().getClassLoader().getResource("filteredMetrics.txt");
List<String> metricExclusions = exclusions.getFilteredMetrics(url.getFile());
Assert.assertEquals(4, metricExclusions.size());
System.out.println(metricExclusions);
}
@Test
public void testLoadFilteredTagsFile() {
Exclusions exclusions = new Exclusions();
URL url = getClass().getClassLoader().getResource("filteredTags.txt");
Map<String,Set<String>> filteredTags = exclusions.getFilteredTags(url.getFile());
Assert.assertEquals(2, filteredTags.size());
System.out.println(filteredTags);
}
@Test
public void testMetricExclusions() {
Exclusions exclusions = new Exclusions();
URL filteredMetrics = getClass().getClassLoader().getResource("filteredMetrics.txt");
URL filteredTags = getClass().getClassLoader().getResource("filteredTags.txt");
exclusions.setFilteredMetricsFile(filteredMetrics.getFile());
exclusions.setFilteredTagsFile(filteredTags.getFile());
String metric1 = "put sys.interface.if_octets 1740090590221 71838.0 host=localhost instance=veth88545ad sampleType=DERIVE";
Assert.assertTrue(exclusions.filterMetric(metric1));
String metric2 = "put sys.interface.if_errors 1740090590221 71838.0 host=localhost instance=veth88545ad sampleType=DERIVE";
Assert.assertFalse(exclusions.filterMetric(metric2));
Assert.assertEquals("put sys.interface.if_octets 1740090590221 71838.0 host=localhost", exclusions.filterExcludedTags(metric1));
}
}
@@ -1,4 +0,0 @@
sys.ethstat.(tx|rx)[-]?[0-9]+[_.]
sys.ethstat.*_to_.*
sys.interface.if_packets
sys.interface.if_octets
@@ -1,2 +0,0 @@
sys.interface.if_packets sampleType,instance
sys.interface.if_octets sampleType,instance
+3 -3
View File
@@ -7,9 +7,9 @@
</formats>
<fileSets>
<fileSet>
<directory>timely-datasource/dist</directory>
<outputDirectory>timely-datasource</outputDirectory>
<directory>timely-app/dist</directory>
<outputDirectory>timely-app</outputDirectory>
</fileSet>
</fileSets>
<includeBaseDirectory>false</includeBaseDirectory>
</assembly>
</assembly>
+2 -2
View File
@@ -4,9 +4,9 @@
<parent>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>2.2.6-SNAPSHOT</version>
<version>3.0.0-SNAPSHOT</version>
</parent>
<artifactId>timely-datasource</artifactId>
<artifactId>timely-app</artifactId>
<packaging>pom</packaging>
<build>
<plugins>
+2
View File
@@ -0,0 +1,2 @@
node_modules/
+3
View File
@@ -0,0 +1,3 @@
module.exports = {
...require('@grafana/toolkit/src/config/prettier.plugin.config.json'),
};
@@ -1,6 +1,6 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
httpProperties://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
@@ -192,10 +192,10 @@
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
httpProperties://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
limitations under the License.
+18
View File
@@ -0,0 +1,18 @@
//+build mage
package main
import (
"fmt"
// mage:import
_ "github.com/grafana/grafana-plugin-sdk-go/build"
)
// CheckCheck builds production back-end components.
func CheckCheck() {
fmt.Println("hello! TODO.... (the other commands are imported)")
}
// Default configures the default target.
var Default = CheckCheck
+6
View File
@@ -0,0 +1,6 @@
# Timely Data Source Plugin
Timely is a time series database application that provides secure access to time
series data. Timely is written in Java, uses
[Apache Accumulo](https://accumuloProperties.apache.org/) for backend storage, and has
a configurable length memory cache.
+26
View File
@@ -0,0 +1,26 @@
# Test against the latest version of this Node.js version
environment:
nodejs_version: '10'
# Local NPM Modules
cache:
- node_modules
# Install scripts. (runs after repo cloning)
install:
# Get the latest stable version of Node.js or io.js
- ps: Install-Product node $env:nodejs_version
# install modules
- npm install -g yarn --quiet
- yarn install --pure-lockfile
# Post-install test scripts.
test_script:
# Output useful info for debugging.
- node --version
- npm --version
# Run the build
build_script:
- yarn dev # This will also run prettier!
- yarn build # make sure both scripts work
+7
View File
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<testsuites name="jest tests" tests="1" failures="0" time="1.179">
<testsuite name="placeholder test" errors="0" failures="0" skipped="0" timestamp="2021-10-22T20:15:17" time="0.552" tests="1">
<testcase classname="placeholder test should return true" name="placeholder test should return true" time="0.003">
</testcase>
</testsuite>
</testsuites>
@@ -198,4 +198,4 @@
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
limitations under the License.
+6
View File
@@ -0,0 +1,6 @@
# Timely Data Source Plugin
Timely is a time series database application that provides secure access to time
series data. Timely is written in Java, uses
[Apache Accumulo](https://accumulo.apache.org/) for backend storage, and has
a configurable length memory cache.
+2
View File
@@ -0,0 +1,2 @@
define((function(){return function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=71)}({71:function(e,t,n){}})}));
//# sourceMappingURL=dark.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"sources":["webpack:///webpack/bootstrap"],"names":["installedModules","__webpack_require__","moduleId","exports","module","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","prototype","hasOwnProperty","p","s"],"mappings":"sCACE,IAAIA,EAAmB,GAGvB,SAASC,EAAoBC,GAG5B,GAAGF,EAAiBE,GACnB,OAAOF,EAAiBE,GAAUC,QAGnC,IAAIC,EAASJ,EAAiBE,GAAY,CACzCG,EAAGH,EACHI,GAAG,EACHH,QAAS,IAUV,OANAI,EAAQL,GAAUM,KAAKJ,EAAOD,QAASC,EAAQA,EAAOD,QAASF,GAG/DG,EAAOE,GAAI,EAGJF,EAAOD,QA0Df,OArDAF,EAAoBQ,EAAIF,EAGxBN,EAAoBS,EAAIV,EAGxBC,EAAoBU,EAAI,SAASR,EAASS,EAAMC,GAC3CZ,EAAoBa,EAAEX,EAASS,IAClCG,OAAOC,eAAeb,EAASS,EAAM,CAAEK,YAAY,EAAMC,IAAKL,KAKhEZ,EAAoBkB,EAAI,SAAShB,GACX,oBAAXiB,QAA0BA,OAAOC,aAC1CN,OAAOC,eAAeb,EAASiB,OAAOC,YAAa,CAAEC,MAAO,WAE7DP,OAAOC,eAAeb,EAAS,aAAc,CAAEmB,OAAO,KAQvDrB,EAAoBsB,EAAI,SAASD,EAAOE,GAEvC,GADU,EAAPA,IAAUF,EAAQrB,EAAoBqB,IAC/B,EAAPE,EAAU,OAAOF,EACpB,GAAW,EAAPE,GAA8B,iBAAVF,GAAsBA,GAASA,EAAMG,WAAY,OAAOH,EAChF,IAAII,EAAKX,OAAOY,OAAO,MAGvB,GAFA1B,EAAoBkB,EAAEO,GACtBX,OAAOC,eAAeU,EAAI,UAAW,CAAET,YAAY,EAAMK,MAAOA,IACtD,EAAPE,GAA4B,iBAATF,EAAmB,IAAI,IAAIM,KAAON,EAAOrB,EAAoBU,EAAEe,EAAIE,EAAK,SAASA,GAAO,OAAON,EAAMM,IAAQC,KAAK,KAAMD,IAC9I,OAAOF,GAIRzB,EAAoB6B,EAAI,SAAS1B,GAChC,IAAIS,EAAST,GAAUA,EAAOqB,WAC7B,WAAwB,OAAOrB,EAAgB,SAC/C,WAA8B,OAAOA,GAEtC,OADAH,EAAoBU,EAAEE,EAAQ,IAAKA,GAC5BA,GAIRZ,EAAoBa,EAAI,SAASiB,EAAQC,GAAY,OAAOjB,OAAOkB,UAAUC,eAAe1B,KAAKuB,EAAQC,IAGzG/B,EAAoBkC,EAAI,IAIjBlC,EAAoBA,EAAoBmC,EAAI,I","file":"dark.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 71);\n"],"sourceRoot":""}
+2
View File
@@ -0,0 +1,2 @@
define((function(){return function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=70)}({70:function(e,t,n){}})}));
//# sourceMappingURL=light.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"sources":["webpack:///webpack/bootstrap"],"names":["installedModules","__webpack_require__","moduleId","exports","module","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","prototype","hasOwnProperty","p","s"],"mappings":"sCACE,IAAIA,EAAmB,GAGvB,SAASC,EAAoBC,GAG5B,GAAGF,EAAiBE,GACnB,OAAOF,EAAiBE,GAAUC,QAGnC,IAAIC,EAASJ,EAAiBE,GAAY,CACzCG,EAAGH,EACHI,GAAG,EACHH,QAAS,IAUV,OANAI,EAAQL,GAAUM,KAAKJ,EAAOD,QAASC,EAAQA,EAAOD,QAASF,GAG/DG,EAAOE,GAAI,EAGJF,EAAOD,QA0Df,OArDAF,EAAoBQ,EAAIF,EAGxBN,EAAoBS,EAAIV,EAGxBC,EAAoBU,EAAI,SAASR,EAASS,EAAMC,GAC3CZ,EAAoBa,EAAEX,EAASS,IAClCG,OAAOC,eAAeb,EAASS,EAAM,CAAEK,YAAY,EAAMC,IAAKL,KAKhEZ,EAAoBkB,EAAI,SAAShB,GACX,oBAAXiB,QAA0BA,OAAOC,aAC1CN,OAAOC,eAAeb,EAASiB,OAAOC,YAAa,CAAEC,MAAO,WAE7DP,OAAOC,eAAeb,EAAS,aAAc,CAAEmB,OAAO,KAQvDrB,EAAoBsB,EAAI,SAASD,EAAOE,GAEvC,GADU,EAAPA,IAAUF,EAAQrB,EAAoBqB,IAC/B,EAAPE,EAAU,OAAOF,EACpB,GAAW,EAAPE,GAA8B,iBAAVF,GAAsBA,GAASA,EAAMG,WAAY,OAAOH,EAChF,IAAII,EAAKX,OAAOY,OAAO,MAGvB,GAFA1B,EAAoBkB,EAAEO,GACtBX,OAAOC,eAAeU,EAAI,UAAW,CAAET,YAAY,EAAMK,MAAOA,IACtD,EAAPE,GAA4B,iBAATF,EAAmB,IAAI,IAAIM,KAAON,EAAOrB,EAAoBU,EAAEe,EAAIE,EAAK,SAASA,GAAO,OAAON,EAAMM,IAAQC,KAAK,KAAMD,IAC9I,OAAOF,GAIRzB,EAAoB6B,EAAI,SAAS1B,GAChC,IAAIS,EAAST,GAAUA,EAAOqB,WAC7B,WAAwB,OAAOrB,EAAgB,SAC/C,WAA8B,OAAOA,GAEtC,OADAH,EAAoBU,EAAEE,EAAQ,IAAKA,GAC5BA,GAIRZ,EAAoBa,EAAI,SAASiB,EAAQC,GAAY,OAAOjB,OAAOkB,UAAUC,eAAe1B,KAAKuB,EAAQC,IAGzG/B,EAAoBkC,EAAI,IAIjBlC,EAAoBA,EAAoBmC,EAAI,I","file":"light.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 70);\n"],"sourceRoot":""}
+3
View File
@@ -0,0 +1,3 @@
/*! For license information please see module.js.LICENSE.txt */
define(["react","@grafana/ui","react-dom","lodash","@grafana/runtime","@grafana/data"],(function(e,t,n,r,o,i){return function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=72)}([function(t,n){t.exports=e},function(e,t,n){e.exports=n(48)()},function(e,t){e.exports=function(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e},e.exports.default=e.exports,e.exports.__esModule=!0},function(e,t){e.exports=function(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e},e.exports.default=e.exports,e.exports.__esModule=!0},function(e,t){function n(){return e.exports=n=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},e.exports.default=e.exports,e.exports.__esModule=!0,n.apply(this,arguments)}e.exports=n,e.exports.default=e.exports,e.exports.__esModule=!0},function(e,t){e.exports=function(e,t){if(null==e)return{};var n,r,o={},i=Object.keys(e);for(r=0;r<i.length;r++)n=i[r],t.indexOf(n)>=0||(o[n]=e[n]);return o},e.exports.default=e.exports,e.exports.__esModule=!0},function(e,t,n){var r;!function(){"use strict";var n={}.hasOwnProperty;function o(){for(var e=[],t=0;t<arguments.length;t++){var r=arguments[t];if(r){var i=typeof r;if("string"===i||"number"===i)e.push(r);else if(Array.isArray(r)){if(r.length){var a=o.apply(null,r);a&&e.push(a)}}else if("object"===i)if(r.toString===Object.prototype.toString)for(var s in r)n.call(r,s)&&r[s]&&e.push(s);else e.push(r.toString())}}return e.join(" ")}e.exports?(o.default=o,e.exports=o):void 0===(r=function(){return o}.apply(t,[]))||(e.exports=r)}()},function(e,n){e.exports=t},function(e,t,n){var r=n(50);e.exports=function(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,r(e,t)},e.exports.default=e.exports,e.exports.__esModule=!0},,function(e,t){e.exports=n},function(e,t){e.exports=r},function(e,t){e.exports=o},function(e,t,n){"use strict";e.exports=function e(t,n){if(t===n)return!0;if(t&&n&&"object"==typeof t&&"object"==typeof n){if(t.constructor!==n.constructor)return!1;var r,o,i;if(Array.isArray(t)){if((r=t.length)!=n.length)return!1;for(o=r;0!=o--;)if(!e(t[o],n[o]))return!1;return!0}if(t.constructor===RegExp)return t.source===n.source&&t.flags===n.flags;if(t.valueOf!==Object.prototype.valueOf)return t.valueOf()===n.valueOf();if(t.toString!==Object.prototype.toString)return t.toString()===n.toString();if((r=(i=Object.keys(t)).length)!==Object.keys(n).length)return!1;for(o=r;0!=o--;)if(!Object.prototype.hasOwnProperty.call(n,i[o]))return!1;for(o=r;0!=o--;){var a=i[o];if(!e(t[a],n[a]))return!1}return!0}return t!=t&&n!=n}},function(e,t,n){"use strict";e.exports=function(e,t,n,r,o,i,a,s){if(!e){var l;if(void 0===t)l=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var u=[n,r,o,i,a,s],c=0;(l=new Error(t.replace(/%s/g,(function(){return u[c++]})))).name="Invariant Violation"}throw l.framesToPop=1,l}}},function(e,t,n){"use strict";var r=n(26),o="function"==typeof Symbol&&"symbol"==typeof Symbol("foo"),i=Object.prototype.toString,a=Array.prototype.concat,s=Object.defineProperty,l=s&&function(){var e={};try{for(var t in s(e,"x",{enumerable:!1,value:e}),e)return!1;return e.x===e}catch(e){return!1}}(),u=function(e,t,n,r){var o;(!(t in e)||"function"==typeof(o=r)&&"[object Function]"===i.call(o)&&r())&&(l?s(e,t,{configurable:!0,enumerable:!1,value:n,writable:!0}):e[t]=n)},c=function(e,t){var n=arguments.length>2?arguments[2]:{},i=r(t);o&&(i=a.call(i,Object.getOwnPropertySymbols(t)));for(var s=0;s<i.length;s+=1)u(e,i[s],t[i[s]],n[i[s]])};c.supportsDescriptors=!!l,e.exports=c},function(e,t,n){"use strict";var r=function(){};e.exports=r},function(e,t,n){"use strict";function r(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n<t;n++)r[n]=e[n];return r}n.d(t,"a",(function(){return r}))},function(e,t){var n;n=function(){return this}();try{n=n||new Function("return this")()}catch(e){"object"==typeoLine truncated
//# sourceMappingURL=module.js.map
+87
View File
@@ -0,0 +1,87 @@
/*!
Copyright (c) 2018 Jed Watson.
Licensed under the MIT License (MIT), see
http://jedwatson.github.io/classnames
*/
/*! *****************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/**
* A better abstraction over CSS.
*
* @copyright Oleg Isonen (Slobodskoi) / Isonen 2014-present
* @website https://github.com/cssinjs/jss
* @license MIT
*/
/** @license React v16.13.1
* react-is.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**!
* @fileOverview Kickass library to create and place poppers near their reference elements.
* @version 1.16.1
* @license
* Copyright (c) 2016 Federico Zivolo and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**!
* @fileOverview Kickass library to create and place poppers near their reference elements.
* @version 1.16.1-lts
* @license
* Copyright (c) 2016 Federico Zivolo and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+1
View File
@@ -0,0 +1 @@
{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///external \"react\"","webpack:///../node_modules/prop-types/index.js","webpack:///../node_modules/@babel/runtime/helpers/assertThisInitialized.js","webpack:///../node_modules/@babel/runtime/helpers/defineProperty.js","webpack:///../node_modules/@babel/runtime/helpers/extends.js","webpack:///../node_modules/@babel/runtime/helpers/objectWithoutPropertiesLoose.js","webpack:///../node_modules/classnames/index.js","webpack:///external \"@grafana/ui\"","webpack:///../node_modules/@babel/runtime/helpers/inheritsLoose.js","webpack:///external \"react-dom\"","webpack:///external \"lodash\"","webpack:///external \"@grafana/runtime\"","webpack:///../node_modules/fast-deep-equal/index.js","webpack:///../node_modules/invariant/browser.js","webpack:///../node_modules/define-properties/index.js","webpack:///../node_modules/warning/warning.js","webpack:///../node_modules/@babel/runtime/helpers/esm/arrayLikeToArray.js","webpack:///../node_modules/webpack/buildin/global.js","webpack:///../node_modules/@babel/runtime/helpers/esm/unsupportedIterableToArray.js","webpack:///../node_modules/function-bind/index.js","webpack:///../node_modules/call-bind/index.js","webpack:///../node_modules/popper.js/dist/esm/popper.js","webpack:///../node_modules/@hypnosphi/create-react-context/lib/index.js","webpack:///../node_modules/@babel/runtime/helpers/esm/assertThisInitialized.js","webpack:///../node_modules/object-keys/index.js","webpack:///../node_modules/object-keys/isArguments.js","webpack:///../node_modules/call-bind/callBound.js","webpack:///../node_modules/get-intrinsic/index.js","webpack:///../node_modules/has-symbols/shams.js","webpack:///../node_modules/object-is/implementation.js","webpack:///../node_modules/object-is/polyfill.js","webpack:///../node_modules/regexp.prototype.flags/implementation.js","webpack:///../node_modules/regexp.prototype.flags/polyfill.js","webpack:///../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js","webpack:///../node_modules/css-loader/dist/runtime/api.js","webpack:///external \"@grafana/data\"","webpack:///../node_modules/dayjs/dayjs.min.js","webpack:///../node_modules/lodash.debounce/index.js","webpack:///../node_modules/deep-equal/index.js","webpack:///../node_modules/hoist-non-react-statics/dist/hoist-non-react-statics.cjs.js","webpack:///../node_modules/@babel/runtime/helpers/esm/setPrototypeOf.js","webpack:///../node_modules/@babel/runtime/helpers/esm/iterableToArray.js","webpack:///../node_modules/@babel/runtime/helpers/esm/arrayWithHoles.js","webpack:///../node_modules/@babel/runtime/helpers/esm/nonIterableRest.js","webpack:///../node_modules/@material-ui/core/node_modules/popper.js/dist/esm/popper.js","webpack:///../node_modules/prop-types/factoryWithThrowingShims.js","webpack:///../node_modules/prop-types/lib/ReactPropTypesSecret.js","webpack:///../node_modules/@babel/runtime/helpers/setPrototypeOf.js","webpack:///../node_modules/object-keys/implementation.js","webpack:///../node_modules/is-arguments/index.js","webpack:///../node_modules/has-symbols/index.js","webpack:///../node_modules/function-bind/implementation.js","webpack:///../node_modules/has/src/index.js","webpack:///../node_modules/object-is/index.js","webpack:///../node_modules/object-is/shim.js","webpack:///../node_modules/is-regex/index.js","webpack:///../node_modules/regexp.prototype.flags/index.js","webpack:///../node_modules/regexp.prototype.flags/shim.js","webpack:///../node_modules/is-date-object/index.js","webpack:///../node_modules/@hypnosphi/create-react-context/lib/implementation.js","webpack:///../node_modules/gud/index.js","webpack:///../node_modules/react-is/index.js","webpack:///../node_modules/react-is/cjs/react-is.production.min.js","webpack:///./static/css/timely-bootstrap.css?617f","webpack:///./static/css/timely-bootstrap.css","webpack:///./static/css/timely.css?f98e","webpack:///./static/css/timely.css","webpack:///../node_modules/tslib/tslib.es6.js","webpack:///../node_modules/rxjs/dist/esm5/internal/util/isFunction.js","webpack:///../node_modules/rxjs/dist/esm5/internal/util/lift.js","webpack:///../node_modules/rxjs/node_modules/tslib/tslib.es6.js","webpack:///../node_modules/rxjs/dist/esm5/internal/util/UnsubscriptionError.js","webpack:///../node_modules/rxjs/dist/esm5/internal/util/createErrorClass.js","webpack:///../node_modules/rxjs/dist/esm5/internal/util/arrRemove.js","webpack:///../node_modules/rxjs/dist/esm5/internal/Subscription.js","webpack:///../node_modules/rxjs/dist/esm5/internal/config.js","webpack:///../node_modules/rxjs/dist/esm5/internal/scheduler/timeoutProvider.js","webpack:///../node_modules/rxjs/dist/esm5/internal/util/reportUnhandledError.js","webpack:///../node_modules/rxjs/dist/esm5/internal/util/noop.js","webpack:///../node_modules/rxjs/dist/esm5/internal/NotificationFactories.js","webpack:///../node_modules/rxjs/dist/esm5/internal/util/errorContext.js","webpack:///../node_modules/rxjs/dist/esm5/inteLine truncated
+40
View File
@@ -0,0 +1,40 @@
{
"type": "datasource",
"name": "Timely",
"id": "nsa-timely-datasource",
"category": "tsdb",
"metrics": true,
"backend": true,
"alerting": true,
"executable": "timely_datasource_backend",
"info": {
"description": "Open source time series database backed by Accumulo",
"author": {
"name": "National Security Agency"
},
"keywords": ["Simple"],
"logos": {
"small": "static/img/timely_square_small.png",
"large": "static/img/timely_square_large.png"
},
"links": [
{
"name": "Website",
"url": "https://code.nsa.gov/timely"
},
{
"name": "License",
"url": "https://github.com/NationalSecurityAgency/timely/blob/master/LICENSE"
}
],
"screenshots": [],
"version": "2.1.6-SNAPSHOT",
"updated": "2024-04-10"
},
"dependencies": {
"classnames": "2.2.6",
"slate-plain-serializer": "0.7.10",
"grafanaVersion": "6.5.x",
"plugins": []
}
}
@@ -2,6 +2,10 @@ div.timely input:focus {
z-index: 1;
}
div.timely-edior-row div.gf-form-inline {
height: 40px !important;
}
div.timely input.form-control {
border-color: rgb(44, 50, 53);
border-top-color: rgb(44, 50, 53);
@@ -52,10 +56,6 @@ div.timely.tag.addtag svg {
color: #62a562;
}
div.timely.tag.deletetag svg {
color: #d9162d;
}
button[title='Move query down'] div svg {
color: #b3bfbd;
}
@@ -75,36 +75,3 @@ button[title='Disable/enable query'] div svg {
button[title='Remove query'] div svg {
color: #d9162d;
}
.queryErrorFieldRow {
margin-bottom: 4px;
}
.queryErrorContainer {
display: flex;
}
.queryErrorIconDiv {
background-color: rgb(209, 14, 92);
padding: 8px;
}
.queryErrorIcon {
height: 16px;
width: 16px;
display: inline-block;
flex-shrink: 0;
line-height: 0;
vertical-align: middle;
fill: rgb(255, 255, 255);
overflow-clip-margin: content-box;
overflow: hidden;
}
.queryErrorText {
text-align: left;
vertical-align: middle;
font-size: 0.857143rem;
font-family: "Roboto Mono", monospace;
padding: 8px;
}
File renamed without changes.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

@@ -0,0 +1,54 @@
$box-shadow-dimensions: 0 0 0 0.2rem;
$placeholder-color: #6c757d;
.rbt {
&-input-multi {
// Bootstrap 4 focus style
&.focus {
background-color: #fff;
border-color: #80bdff;
box-shadow: $box-shadow-dimensions rgba(0, 123, 255, 0.25);
color: #495057;
outline: 0;
}
&.is-invalid.focus {
border-color: #dc3545;
box-shadow: $box-shadow-dimensions rgba(220, 53, 69, 0.25);
}
&.is-valid.focus {
border-color: #28a745;
box-shadow: $box-shadow-dimensions rgba(40, 167, 69, 0.25);
}
input {
// Firefox
&::-moz-placeholder {
color: $placeholder-color;
opacity: 1;
}
// Internet Explorer 10+
&:-ms-input-placeholder {
color: $placeholder-color;
}
// Safari and Chrome
&::-webkit-input-placeholder {
color: $placeholder-color;
}
}
// Override height in main CSS file :/
& .rbt-input-main,
&.form-control-lg .rbt-input-main,
&.form-control-sm .rbt-input-main {
height: auto;
}
}
&-token {
margin: 1px 3px 2px 0;
}
}
+201
View File
@@ -0,0 +1,201 @@
.rbt .rbt-input-main::-ms-clear {
display: none !important;
}
/**
* Menu
*/
.rbt-menu {
margin-bottom: 2px !important;
}
.rbt-menu > .dropdown-item {
overflow: hidden !important;
text-overflow: ellipsis !important;
}
.rbt-menu > .dropdown-item:focus {
outline: none !important;
}
.rbt-menu-pagination-option {
text-align: center !important;
}
/**
* Multi-select Input
*/
.rbt-input-multi {
cursor: text !important;
overflow: hidden !important;
position: relative !important;
}
.rbt-input-multi.focus {
border-color: #80bdff !important;
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25) !important;
color: #495057 !important;
outline: 0 !important;
}
.rbt-input-multi.form-control {
height: auto !important;
}
.rbt-input-multi.form-control[disabled] {
background-color: #e9ecef !important;
opacity: 1 !important;
}
.rbt-input-multi.is-invalid.focus {
border-color: #dc3545 !important;
box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25) !important;
}
.rbt-input-multi.is-valid.focus {
border-color: #28a745 !important;
box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25) !important;
}
.rbt-input-multi input::-moz-placeholder {
color: #6c757d !important;
opacity: 1 !important;
}
.rbt-input-multi input:-ms-input-placeholder {
color: #6c757d !important;
}
.rbt-input-multi input::-webkit-input-placeholder {
color: #6c757d !important;
}
.rbt-input-multi .rbt-input-wrapper {
align-items: flex-start !important;
display: flex !important;
flex-wrap: wrap !important;
margin-bottom: -4px !important;
margin-top: -1px !important;
overflow: hidden !important;
}
.rbt-input-multi .rbt-input-main {
margin: 1px 0 4px !important;
}
/**
* Close Button
*/
.rbt-close {
z-index: 1 !important;
}
.rbt-close-lg {
font-size: 24px !important;
}
/**
* Token
*/
.rbt-token {
background-color: #e7f4ff !important;
border: 0 !important;
border-radius: 0.25rem !important;
color: #007bff !important;
display: inline-block !important;
line-height: 1em !important;
margin: 1px 3px 2px 0 !important;
padding: 4px 7px !important;
position: relative !important;
}
.rbt-token-disabled {
background-color: rgba(0, 0, 0, 0.1) !important;
color: #495057 !important;
pointer-events: none !important;
}
.rbt-token-removeable {
cursor: pointer !important;
padding-right: 21px !important;
}
.rbt-token-active {
background-color: #007bff !important;
color: #fff !important;
outline: none !important;
text-decoration: none !important;
}
.rbt-token .rbt-token-remove-button {
bottom: 0 !important;
color: inherit !important;
font-size: inherit !important;
font-weight: normal !important;
opacity: 1 !important;
outline: none !important;
padding: 3px 7px !important;
position: absolute !important;
right: 0 !important;
text-shadow: none !important;
top: -2px !important;
}
/**
* Loader + CloseButton container
*/
.rbt-aux {
align-items: center !important;
display: flex !important;
bottom: 0 !important;
justify-content: center !important;
pointer-events: none !important;
/* Don't block clicks on the input */
position: absolute !important;
right: 0 !important;
top: 0 !important;
width: 34px !important;
}
.rbt-aux-lg {
width: 46px !important;
}
.rbt-aux .rbt-close {
margin-top: -4px !important;
pointer-events: auto !important;
/* Override pointer-events: none; above */
}
.has-aux .rbt-input {
padding-right: 34px !important;
}
.rbt-highlight-text {
background-color: inherit !important;
color: inherit !important;
font-weight: bold !important;
padding: 0 !important;
}
/**
* Input Groups
*/
.input-group > .rbt {
flex: 1 !important;
}
.input-group > .rbt .rbt-input-hint,
.input-group > .rbt .rbt-aux {
z-index: 5 !important;
}
.input-group > .rbt:not(:first-child) .form-control {
border-top-left-radius: 0 !important;
border-bottom-left-radius: 0 !important;
}
.input-group > .rbt:not(:last-child) .form-control {
border-top-right-radius: 0 !important;
border-bottom-right-radius: 0 !important;
}
+240
View File
@@ -0,0 +1,240 @@
$animation: loader-animation 600ms infinite linear;
$inset: inset 0 1px 1px rgba(0, 0, 0, 0.075);
$placeholder-color: #999;
.rbt {
&-menu {
margin-bottom: 2px; // Spacing for dropup
& > li a {
overflow: hidden;
text-overflow: ellipsis;
&:focus {
outline: none;
}
}
&-pagination-option {
text-align: center;
}
}
// Hide IE's native "clear" button
& .rbt-input-main::-ms-clear {
display: none;
}
&-input-multi {
cursor: text;
overflow: hidden;
position: relative;
height: auto;
// Apply Bootstrap focus styles
&.focus {
box-shadow: $inset, 0 0 8px rgba(102, 175, 233, 0.6);
border-color: #66afe9;
outline: 0;
}
// BS4 uses the :disabled pseudo-class, which doesn't work with non-inputs.
&.form-control[disabled] {
background-color: #e9ecef;
opacity: 1;
}
// Apply Bootstrap placeholder styles
input {
// Firefox
&::-moz-placeholder {
color: $placeholder-color;
opacity: 1;
}
// Internet Explorer 10+
&:-ms-input-placeholder {
color: $placeholder-color;
}
// Safari and Chrome
&::-webkit-input-placeholder {
color: $placeholder-color;
}
}
.rbt-input-wrapper {
align-items: flex-start;
display: flex;
flex-wrap: wrap;
margin-bottom: -4px;
margin-top: -1px;
overflow: hidden;
}
.rbt-input-main {
// Set input height for cross-browser consistency
height: 20px;
margin: 1px 0 4px;
}
&.input,
&.form-control {
&-lg {
.rbt-input-main {
height: 24px;
}
}
&-sm {
.rbt-input-main {
height: 18px;
}
}
}
}
&-close {
z-index: 1;
&-lg {
font-size: 24px;
}
}
&-token {
background-color: #e7f4ff;
border: 0;
border-radius: 2px;
color: #1f8dd6;
display: inline-block;
line-height: 1em;
margin: 0 3px 3px 0;
padding: 4px 7px;
position: relative;
&-disabled {
background-color: #ddd;
color: #888;
pointer-events: none;
}
&-removeable {
cursor: pointer;
padding-right: 21px;
}
&-active {
background-color: #1f8dd6;
color: #fff;
outline: none;
text-decoration: none;
}
& &-remove-button {
bottom: 0;
color: inherit;
font-size: inherit;
font-weight: normal;
opacity: 1;
outline: none;
padding: 3px 7px;
position: absolute;
right: 0;
text-shadow: none;
top: -2px;
}
}
&-loader {
-moz-animation: $animation;
-webkit-animation: $animation;
animation: $animation;
border: 1px solid #d5d5d5;
border-radius: 50%;
border-top-color: #1f8dd6;
display: block;
height: 16px;
width: 16px;
&-lg {
height: 20px;
width: 20px;
}
}
&-aux {
align-items: center;
display: flex;
bottom: 0;
justify-content: center;
pointer-events: none; /* Don't block clicks on the input */
position: absolute;
right: 0;
top: 0;
width: 34px;
&-lg {
width: 46px;
}
& .rbt-close {
margin-top: -4px;
pointer-events: auto; /* Override pointer-events: none; above */
}
}
.has-aux &-input {
padding-right: 34px;
}
&-highlight-text {
background-color: inherit;
color: inherit;
font-weight: bold;
padding: 0;
}
}
/* Input Groups */
.input-group > .rbt {
flex: 1;
// Form-controls within input-groups have a higher z-index.
& .rbt-input-hint,
& .rbt-aux {
z-index: 5;
}
&:not(:first-child) .form-control {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
&:not(:last-child) .form-control {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
}
/* Validation States */
.has-error .rbt-input-multi.focus {
border-color: #843534;
box-shadow: $inset, 0 0 6px #ce8483;
}
.has-warning .rbt-input-multi.focus {
border-color: #66512c;
box-shadow: $inset, 0 0 6px #c0a16b;
}
.has-success .rbt-input-multi.focus {
border-color: #2b542c;
box-shadow: $inset, 0 0 6px #67b168;
}
@keyframes loader-animation {
to {
transform: rotate(1turn);
}
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+1
View File
@@ -0,0 +1 @@
div.timely .dropdown-item,div.timely .dropdown-item-text,div.timely input.form-control{color:#c7d0d9;background-color:#0b0c0e}.dropdown-item:active,.dropdown-item:focus,div.timely .dropdown-item.active,div.timely .dropdown-item:hover{background-color:#4f5255}.dropdown-item:disabled,div.timely .dropdown-header,div.timely .dropdown-item.disabled{color:#0b0c0e}div.timely .dropdown-menu{background-color:#0b0c0e;box-shadow:0 2px 2px 2px #31343b}div.timely input.form-control:focus{color:#c7d0d9;outline:0;box-shadow:0 0 0 2px #141619,0 0 0 4px #1f60c4;outline-offset:2px;transition:all .2s cubic-bezier(.19,1,.22,1) 0s}div.timely mark.rbt-highlight-text{background:linear-gradient(90deg,#56585c,#4c5057);color:#ff0}
+1
View File
@@ -0,0 +1 @@
div.timely input.form-control{color:#464c54;background-color:#fff}div.timely .dropdown-item,div.timely .dropdown-item-text{color:#464c54}.dropdown-item:active,.dropdown-item:focus,div.timely .dropdown-item.active,div.timely .dropdown-item:hover{color:#464c54;background-color:#e3e6e9}.dropdown-item:disabled,div.timely .dropdown-item.disabled{color:#abadaf}div.timely .dropdown-header{color:#464c54}div.timely .dropdown-menu{box-shadow:0 4px 2px 0 #cbcbcb}div.timely input.form-control:focus{color:#464c54;outline:0;box-shadow:0 0 0 2px #fff,0 0 0 4px #5794f2;outline-offset:2px;transition:all .2s cubic-bezier(.19,1,.22,1) 0s}div.timely mark.rbt-highlight-text{background:linear-gradient(90deg,#bfc0c1,#abadaf);color:#ff0}
Loaded 100 of 262 files, more files were not shown because too many files have changed in this diff. Show more