mirror of
https://github.com/NationalSecurityAgency/timely.git
synced 2026-11-10 11:51:29 -10:00
Compare commits
18
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b93f4c743 | ||
|
|
3d7d6d0efd | ||
|
|
dcd23d4fc8 | ||
|
|
cc7326fc19 | ||
|
|
0d2b6e61c8 | ||
|
|
b7cd8765c5 | ||
|
|
a9d27d5b1b | ||
|
|
7d67eb218d | ||
|
|
5fb7e3dd2b | ||
|
|
5a89861cec | ||
|
|
a3bc9a01ee | ||
|
|
dba494b06d | ||
|
|
5826884c3c | ||
|
|
f94c731bbc | ||
|
|
4e042bcb94 | ||
|
|
ab7f499134 | ||
|
|
c098f9f3e4 | ||
|
|
94cfcbc0c4 |
No files matched your search
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>timely</groupId>
|
<groupId>timely</groupId>
|
||||||
<artifactId>timely</artifactId>
|
<artifactId>timely</artifactId>
|
||||||
<version>1.1.1-SNAPSHOT</version>
|
<version>1.2.3</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>timely-analytics</artifactId>
|
<artifactId>timely-analytics</artifactId>
|
||||||
<!--
|
<!--
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash -vx
|
#!/bin/bash
|
||||||
|
|
||||||
if [[ `uname` == "Darwin" ]]; then
|
if [[ `uname` == "Darwin" ]]; then
|
||||||
THIS_SCRIPT=`python -c 'import os,sys; print os.path.realpath(sys.argv[1])' $0`
|
THIS_SCRIPT=`python -c 'import os,sys; print os.path.realpath(sys.argv[1])' $0`
|
||||||
@@ -27,15 +27,14 @@ fi
|
|||||||
mkdir -p ${NATIVE_DIR}
|
mkdir -p ${NATIVE_DIR}
|
||||||
|
|
||||||
pushd ${BASE_DIR}/bin
|
pushd ${BASE_DIR}/bin
|
||||||
$JAVA_HOME/bin/jar xf ${LIB_DIR}/netty-tcnative*.jar META-INF/native/libnetty_tcnative.${TCNATIVE_SUFFIX}
|
$JAVA_HOME/bin/jar xf ${LIB_DIR}/netty-tcnative-boringssl-static*.jar META-INF/native/libnetty_tcnative_linux_x86_64.${TCNATIVE_SUFFIX}
|
||||||
$JAVA_HOME/bin/jar xf ${LIB_DIR}/netty-transport-native-epoll*.jar META-INF/native/libnetty_transport_native_epoll_x86_64.${TCNATIVE_SUFFIX}
|
$JAVA_HOME/bin/jar xf ${LIB_DIR}/netty-all*.jar META-INF/native/libnetty_transport_native_epoll_x86_64.${TCNATIVE_SUFFIX}
|
||||||
popd
|
popd
|
||||||
|
|
||||||
export CLASSPATH="${CONF_DIR}:${LIB_DIR}/*:${HADOOP_CONF_DIR}"
|
export CLASSPATH="${CONF_DIR}:${LIB_DIR}/*:${HADOOP_CONF_DIR}"
|
||||||
JVM_ARGS="-Xmx1G -Xms1G -Dio.netty.eventLoopThreads=${NUM_SERVER_THREADS}"
|
JVM_ARGS="-Xmx1G -Xms1G -Dio.netty.eventLoopThreads=${NUM_SERVER_THREADS} -Dlog4j.configurationFile=${CONF_DIR}/log4j2-spring.xml"
|
||||||
JVM_ARGS="${JVM_ARGS} -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector"
|
JVM_ARGS="${JVM_ARGS} -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector"
|
||||||
JVM_ARGS="${JVM_ARGS} -Djava.library.path=${NATIVE_DIR}"
|
JVM_ARGS="${JVM_ARGS} -Djava.library.path=${NATIVE_DIR}"
|
||||||
JVM_ARGS="${JVM_ARGS} -Djava.library.path=${NATIVE_DIR}/libnetty-${TCNATIVE_SUFFIX}"
|
|
||||||
|
|
||||||
#JVM_ARGS="${JVM_ARGS} -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005"
|
#JVM_ARGS="${JVM_ARGS} -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005"
|
||||||
|
|
||||||
|
|||||||
@@ -46,9 +46,12 @@
|
|||||||
<user-service id="usernamePasswordService">
|
<user-service id="usernamePasswordService">
|
||||||
<user name="test" password="test1" authorities="A,B,C" />
|
<user name="test" password="test1" authorities="A,B,C" />
|
||||||
</user-service>
|
</user-service>
|
||||||
|
|
||||||
<beans:bean id="UserNamePasswordAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
|
<beans:bean id="noOpPasswordEncoder" class="org.springframework.security.crypto.password.NoOpPasswordEncoder"/>
|
||||||
|
|
||||||
|
<beans:bean id="UserNamePasswordAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
|
||||||
<beans:property name="userDetailsService" ref="usernamePasswordService"/>
|
<beans:property name="userDetailsService" ref="usernamePasswordService"/>
|
||||||
|
<beans:property name="passwordEncoder" ref="noOpPasswordEncoder"/>
|
||||||
</beans:bean>
|
</beans:bean>
|
||||||
|
|
||||||
<authentication-manager alias="authenticationManager">
|
<authentication-manager alias="authenticationManager">
|
||||||
@@ -57,5 +60,12 @@
|
|||||||
<!-- Basic authorization provider, validates provided username and password -->
|
<!-- Basic authorization provider, validates provided username and password -->
|
||||||
<authentication-provider ref="UserNamePasswordAuthenticationProvider" />
|
<authentication-provider ref="UserNamePasswordAuthenticationProvider" />
|
||||||
</authentication-manager>
|
</authentication-manager>
|
||||||
|
|
||||||
|
<util:list id="requiredRoles" value-type="java.lang.String">
|
||||||
|
<beans:value></beans:value>
|
||||||
|
</util:list>
|
||||||
|
|
||||||
|
<util:list id="requiredAuths" value-type="java.lang.String">
|
||||||
|
<beans:value></beans:value>
|
||||||
|
</util:list>
|
||||||
</beans:beans>
|
</beans:beans>
|
||||||
@@ -8,6 +8,8 @@ timely-balancer:
|
|||||||
assignmentFile: /timely/balancer/assignments.csv
|
assignmentFile: /timely/balancer/assignments.csv
|
||||||
security:
|
security:
|
||||||
session-max-age: 86400
|
session-max-age: 86400
|
||||||
|
cache-expiration-minutes: 60
|
||||||
|
cache-refresh-minutes: 5
|
||||||
allow-anonymous-http-access: true
|
allow-anonymous-http-access: true
|
||||||
allow-anonymous-ws-access: true
|
allow-anonymous-ws-access: true
|
||||||
serverSsl:
|
serverSsl:
|
||||||
|
|||||||
+3
-38
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>timely</groupId>
|
<groupId>timely</groupId>
|
||||||
<artifactId>timely</artifactId>
|
<artifactId>timely</artifactId>
|
||||||
<version>1.1.1-SNAPSHOT</version>
|
<version>1.2.3</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>timely-balancer</artifactId>
|
<artifactId>timely-balancer</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@@ -161,9 +161,8 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.netty</groupId>
|
<groupId>io.netty</groupId>
|
||||||
<artifactId>netty-tcnative</artifactId>
|
<artifactId>netty-tcnative-boringssl-static</artifactId>
|
||||||
<version>${version.netty-tcnative}</version>
|
<version>${version.netty-tcnative}</version>
|
||||||
<classifier>${os.detected.classifier}</classifier>
|
|
||||||
<scope>runtime</scope>
|
<scope>runtime</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -247,9 +246,8 @@
|
|||||||
<artifactItems>
|
<artifactItems>
|
||||||
<artifactItem>
|
<artifactItem>
|
||||||
<groupId>io.netty</groupId>
|
<groupId>io.netty</groupId>
|
||||||
<artifactId>netty-tcnative</artifactId>
|
<artifactId>netty-tcnative-boringssl-static</artifactId>
|
||||||
<version>${version.netty-tcnative}</version>
|
<version>${version.netty-tcnative}</version>
|
||||||
<classifier>${os.detected.classifier}</classifier>
|
|
||||||
<type>jar</type>
|
<type>jar</type>
|
||||||
<overWrite>false</overWrite>
|
<overWrite>false</overWrite>
|
||||||
<outputDirectory>${project.build.directory}/native</outputDirectory>
|
<outputDirectory>${project.build.directory}/native</outputDirectory>
|
||||||
@@ -291,37 +289,4 @@
|
|||||||
</extension>
|
</extension>
|
||||||
</extensions>
|
</extensions>
|
||||||
</build>
|
</build>
|
||||||
<profiles>
|
|
||||||
<profile>
|
|
||||||
<id>native-epoll-linux</id>
|
|
||||||
<activation>
|
|
||||||
<os>
|
|
||||||
<family>!mac</family>
|
|
||||||
</os>
|
|
||||||
</activation>
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.netty</groupId>
|
|
||||||
<artifactId>netty-transport-native-epoll</artifactId>
|
|
||||||
<version>${version.netty}</version>
|
|
||||||
<classifier>linux-x86_64</classifier>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
</profile>
|
|
||||||
<profile>
|
|
||||||
<id>native-epoll</id>
|
|
||||||
<activation>
|
|
||||||
<os>
|
|
||||||
<family>mac</family>
|
|
||||||
</os>
|
|
||||||
</activation>
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.netty</groupId>
|
|
||||||
<artifactId>netty-transport-native-epoll</artifactId>
|
|
||||||
<version>${version.netty}</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
</profile>
|
|
||||||
</profiles>
|
|
||||||
</project>
|
</project>
|
||||||
@@ -64,6 +64,7 @@ import org.apache.zookeeper.CreateMode;
|
|||||||
import org.apache.zookeeper.data.Stat;
|
import org.apache.zookeeper.data.Stat;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.boot.WebApplicationType;
|
||||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||||
import org.springframework.context.ConfigurableApplicationContext;
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
import timely.auth.AuthCache;
|
import timely.auth.AuthCache;
|
||||||
@@ -143,7 +144,7 @@ public class Balancer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected static ConfigurableApplicationContext initializeConfiguration(String[] args) {
|
protected static ConfigurableApplicationContext initializeConfiguration(String[] args) {
|
||||||
return new SpringApplicationBuilder(SpringBootstrap.class).web(false).run(args);
|
return new SpringApplicationBuilder(SpringBootstrap.class).web(WebApplicationType.NONE).run(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void shutdownHook() {
|
private void shutdownHook() {
|
||||||
@@ -363,7 +364,7 @@ public class Balancer {
|
|||||||
curatorFramework.start();
|
curatorFramework.start();
|
||||||
ensureZkPaths(curatorFramework, zkPaths);
|
ensureZkPaths(curatorFramework, zkPaths);
|
||||||
|
|
||||||
AuthCache.setSessionMaxAge(balancerConfig.getSecurity());
|
AuthCache.configure(balancerConfig.getSecurity());
|
||||||
final boolean useEpoll = useEpoll();
|
final boolean useEpoll = useEpoll();
|
||||||
Class<? extends ServerSocketChannel> channelClass;
|
Class<? extends ServerSocketChannel> channelClass;
|
||||||
Class<? extends Channel> datagramChannelClass;
|
Class<? extends Channel> datagramChannelClass;
|
||||||
@@ -429,7 +430,8 @@ public class Balancer {
|
|||||||
httpServer.group(httpBossGroup, httpWorkerGroup);
|
httpServer.group(httpBossGroup, httpWorkerGroup);
|
||||||
httpServer.channel(channelClass);
|
httpServer.channel(channelClass);
|
||||||
httpServer.handler(new LoggingHandler());
|
httpServer.handler(new LoggingHandler());
|
||||||
HttpClientPool httpClientPool = new HttpClientPool(balancerConfig, clientSSLContext);
|
HttpClientPool httpClientPool = new HttpClientPool(balancerConfig.getSecurity(), balancerConfig.getHttp(),
|
||||||
|
clientSSLContext);
|
||||||
httpServer.childHandler(setupHttpChannel(balancerConfig, sslCtx, metricResolver, httpClientPool));
|
httpServer.childHandler(setupHttpChannel(balancerConfig, sslCtx, metricResolver, httpClientPool));
|
||||||
httpServer.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
|
httpServer.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
|
||||||
httpServer.option(ChannelOption.SO_BACKLOG, 128);
|
httpServer.option(ChannelOption.SO_BACKLOG, 128);
|
||||||
@@ -509,15 +511,14 @@ public class Balancer {
|
|||||||
ch.pipeline().addLast("decoder", new HttpRequestDecoder());
|
ch.pipeline().addLast("decoder", new HttpRequestDecoder());
|
||||||
ch.pipeline().addLast("compressor", new HttpContentCompressor());
|
ch.pipeline().addLast("compressor", new HttpContentCompressor());
|
||||||
ch.pipeline().addLast("decompressor", new HttpContentDecompressor());
|
ch.pipeline().addLast("decompressor", new HttpContentDecompressor());
|
||||||
ch.pipeline().addLast("aggregator", new HttpObjectAggregator(8192));
|
ch.pipeline().addLast("aggregator", new HttpObjectAggregator(65536));
|
||||||
ch.pipeline().addLast("chunker", new ChunkedWriteHandler());
|
ch.pipeline().addLast("chunker", new ChunkedWriteHandler());
|
||||||
ch.pipeline().addLast("queryDecoder", new timely.netty.http.HttpRequestDecoder(
|
ch.pipeline().addLast("queryDecoder", new timely.netty.http.HttpRequestDecoder(
|
||||||
balancerConfig.getSecurity(), balancerConfig.getHttp()));
|
balancerConfig.getSecurity(), balancerConfig.getHttp()));
|
||||||
ch.pipeline().addLast("fileServer", new HttpStaticFileServerHandler());
|
ch.pipeline().addLast("fileServer", new HttpStaticFileServerHandler());
|
||||||
ch.pipeline().addLast("login",
|
ch.pipeline().addLast("login",
|
||||||
new X509LoginRequestHandler(balancerConfig.getSecurity(), balancerConfig.getHttp()));
|
new X509LoginRequestHandler(balancerConfig.getSecurity(), balancerConfig.getHttp()));
|
||||||
ch.pipeline().addLast("httpRelay",
|
ch.pipeline().addLast("httpRelay", new HttpRelayHandler(metricResolver, httpClientPool));
|
||||||
new HttpRelayHandler(balancerConfig, metricResolver, httpClientPool));
|
|
||||||
ch.pipeline().addLast("error", new TimelyExceptionHandler());
|
ch.pipeline().addLast("error", new TimelyExceptionHandler());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -531,7 +532,7 @@ public class Balancer {
|
|||||||
ch.pipeline().addLast("logger", new LoggingHandler());
|
ch.pipeline().addLast("logger", new LoggingHandler());
|
||||||
ch.pipeline().addLast("packetDecoder", new UdpPacketToByteBuf());
|
ch.pipeline().addLast("packetDecoder", new UdpPacketToByteBuf());
|
||||||
ch.pipeline().addLast("buffer", new MetricsBufferDecoder());
|
ch.pipeline().addLast("buffer", new MetricsBufferDecoder());
|
||||||
ch.pipeline().addLast("frame", new DelimiterBasedFrameDecoder(8192, true, Delimiters.lineDelimiter()));
|
ch.pipeline().addLast("frame", new DelimiterBasedFrameDecoder(65536, true, Delimiters.lineDelimiter()));
|
||||||
ch.pipeline().addLast("putDecoder", new UdpDecoder());
|
ch.pipeline().addLast("putDecoder", new UdpDecoder());
|
||||||
ch.pipeline().addLast("udpRelayHandler", new UdpRelayHandler(metricResolver, udpClientPool));
|
ch.pipeline().addLast("udpRelayHandler", new UdpRelayHandler(metricResolver, udpClientPool));
|
||||||
}
|
}
|
||||||
@@ -544,7 +545,7 @@ public class Balancer {
|
|||||||
@Override
|
@Override
|
||||||
protected void initChannel(SocketChannel ch) throws Exception {
|
protected void initChannel(SocketChannel ch) throws Exception {
|
||||||
ch.pipeline().addLast("buffer", new MetricsBufferDecoder());
|
ch.pipeline().addLast("buffer", new MetricsBufferDecoder());
|
||||||
ch.pipeline().addLast("frame", new DelimiterBasedFrameDecoder(8192, true, Delimiters.lineDelimiter()));
|
ch.pipeline().addLast("frame", new DelimiterBasedFrameDecoder(65536, true, Delimiters.lineDelimiter()));
|
||||||
ch.pipeline().addLast("putDecoder", new TcpDecoder());
|
ch.pipeline().addLast("putDecoder", new TcpDecoder());
|
||||||
ch.pipeline().addLast("tcpRelayHandler", new TcpRelayHandler(metricResolver, tcpClientPools));
|
ch.pipeline().addLast("tcpRelayHandler", new TcpRelayHandler(metricResolver, tcpClientPools));
|
||||||
ch.pipeline().addLast("versionHandler", new TcpVersionHandler());
|
ch.pipeline().addLast("versionHandler", new TcpVersionHandler());
|
||||||
@@ -560,7 +561,7 @@ public class Balancer {
|
|||||||
protected void initChannel(SocketChannel ch) throws Exception {
|
protected void initChannel(SocketChannel ch) throws Exception {
|
||||||
ch.pipeline().addLast("ssl", sslCtx.newHandler(ch.alloc()));
|
ch.pipeline().addLast("ssl", sslCtx.newHandler(ch.alloc()));
|
||||||
ch.pipeline().addLast("httpServer", new HttpServerCodec());
|
ch.pipeline().addLast("httpServer", new HttpServerCodec());
|
||||||
ch.pipeline().addLast("aggregator", new HttpObjectAggregator(8192));
|
ch.pipeline().addLast("aggregator", new HttpObjectAggregator(65536));
|
||||||
ch.pipeline().addLast("sessionExtractor", new WebSocketFullRequestHandler());
|
ch.pipeline().addLast("sessionExtractor", new WebSocketFullRequestHandler());
|
||||||
|
|
||||||
ch.pipeline().addLast("idle-handler",
|
ch.pipeline().addLast("idle-handler",
|
||||||
|
|||||||
@@ -5,8 +5,10 @@ import javax.validation.Valid;
|
|||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
import org.springframework.boot.context.properties.NestedConfigurationProperty;
|
import org.springframework.boot.context.properties.NestedConfigurationProperty;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
import timely.configuration.Cache;
|
import timely.configuration.Cache;
|
||||||
|
|
||||||
|
@Validated
|
||||||
@Component
|
@Component
|
||||||
@ConfigurationProperties(prefix = "timely-balancer")
|
@ConfigurationProperties(prefix = "timely-balancer")
|
||||||
public class BalancerConfiguration {
|
public class BalancerConfiguration {
|
||||||
|
|||||||
@@ -5,27 +5,25 @@ import javax.net.ssl.SSLContext;
|
|||||||
import org.apache.commons.pool2.KeyedPooledObjectFactory;
|
import org.apache.commons.pool2.KeyedPooledObjectFactory;
|
||||||
import org.apache.commons.pool2.PooledObject;
|
import org.apache.commons.pool2.PooledObject;
|
||||||
import org.apache.commons.pool2.impl.DefaultPooledObject;
|
import org.apache.commons.pool2.impl.DefaultPooledObject;
|
||||||
import org.apache.http.impl.client.BasicCookieStore;
|
|
||||||
import org.apache.http.impl.client.CloseableHttpClient;
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
import timely.balancer.configuration.BalancerConfiguration;
|
import timely.balancer.configuration.BalancerSecurity;
|
||||||
import timely.balancer.connection.TimelyBalancedHost;
|
import timely.balancer.connection.TimelyBalancedHost;
|
||||||
|
|
||||||
public class HttpClientFactory implements KeyedPooledObjectFactory<TimelyBalancedHost, CloseableHttpClient> {
|
public class HttpClientFactory implements KeyedPooledObjectFactory<TimelyBalancedHost, CloseableHttpClient> {
|
||||||
|
|
||||||
private final SSLContext sslContext;
|
private final SSLContext sslContext;
|
||||||
private final BalancerConfiguration balancerConfig;
|
private final BalancerSecurity security;
|
||||||
|
|
||||||
public HttpClientFactory(BalancerConfiguration balancerConfig, SSLContext sslContext) {
|
public HttpClientFactory(BalancerSecurity security, SSLContext sslContext) {
|
||||||
this.balancerConfig = balancerConfig;
|
this.security = security;
|
||||||
this.sslContext = sslContext;
|
this.sslContext = sslContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PooledObject<CloseableHttpClient> makeObject(TimelyBalancedHost k) throws Exception {
|
public PooledObject<CloseableHttpClient> makeObject(TimelyBalancedHost k) throws Exception {
|
||||||
BasicCookieStore cookieJar = new BasicCookieStore();
|
// disable cookie management because we are sharing connections
|
||||||
return new DefaultPooledObject<>(timely.client.http.HttpClient.get(this.sslContext, cookieJar,
|
return new DefaultPooledObject<>(timely.client.http.HttpClient.get(this.sslContext, null,
|
||||||
balancerConfig.getSecurity().getClientSsl().isHostVerificationEnabled(),
|
security.getClientSsl().isHostVerificationEnabled(), security.getClientSsl().isUseClientCert()));
|
||||||
balancerConfig.getSecurity().getClientSsl().isUseClientCert()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -6,14 +6,15 @@ import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
|
|||||||
import org.apache.http.impl.client.CloseableHttpClient;
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import timely.balancer.configuration.BalancerConfiguration;
|
import timely.balancer.configuration.BalancerHttp;
|
||||||
|
import timely.balancer.configuration.BalancerSecurity;
|
||||||
import timely.balancer.connection.TimelyBalancedHost;
|
import timely.balancer.connection.TimelyBalancedHost;
|
||||||
|
|
||||||
public class HttpClientPool extends GenericKeyedObjectPool<TimelyBalancedHost, CloseableHttpClient> {
|
public class HttpClientPool extends GenericKeyedObjectPool<TimelyBalancedHost, CloseableHttpClient> {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(HttpClientPool.class);
|
private static final Logger LOG = LoggerFactory.getLogger(HttpClientPool.class);
|
||||||
|
|
||||||
public HttpClientPool(BalancerConfiguration balancerConfig, SSLContext sslContext) {
|
public HttpClientPool(BalancerSecurity balancerSecurity, BalancerHttp http, SSLContext sslContext) {
|
||||||
super(new HttpClientFactory(balancerConfig, sslContext), balancerConfig.getHttp().getHttpClientPool());
|
super(new HttpClientFactory(balancerSecurity, sslContext), http.getHttpClientPool());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,6 @@ package timely.balancer.netty.http;
|
|||||||
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH;
|
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.net.URI;
|
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -41,7 +40,6 @@ import timely.api.request.timeseries.QueryRequest;
|
|||||||
import timely.api.response.TimelyException;
|
import timely.api.response.TimelyException;
|
||||||
import timely.auth.util.HttpHeaderUtils;
|
import timely.auth.util.HttpHeaderUtils;
|
||||||
import timely.auth.util.ProxiedEntityUtils;
|
import timely.auth.util.ProxiedEntityUtils;
|
||||||
import timely.balancer.configuration.BalancerConfiguration;
|
|
||||||
import timely.balancer.connection.TimelyBalancedHost;
|
import timely.balancer.connection.TimelyBalancedHost;
|
||||||
import timely.balancer.connection.http.HttpClientPool;
|
import timely.balancer.connection.http.HttpClientPool;
|
||||||
import timely.balancer.resolver.MetricResolver;
|
import timely.balancer.resolver.MetricResolver;
|
||||||
@@ -51,13 +49,10 @@ import timely.netty.http.auth.TimelyAuthenticationToken;
|
|||||||
public class HttpRelayHandler extends SimpleChannelInboundHandler<HttpRequest> implements TimelyHttpHandler {
|
public class HttpRelayHandler extends SimpleChannelInboundHandler<HttpRequest> implements TimelyHttpHandler {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(HttpRelayHandler.class);
|
private static final Logger LOG = LoggerFactory.getLogger(HttpRelayHandler.class);
|
||||||
private final BalancerConfiguration balancerConf;
|
|
||||||
private final HttpClientPool httpClientPool;
|
private final HttpClientPool httpClientPool;
|
||||||
private MetricResolver metricResolver;
|
private MetricResolver metricResolver;
|
||||||
|
|
||||||
public HttpRelayHandler(BalancerConfiguration balancerConf, MetricResolver metricResolver,
|
public HttpRelayHandler(MetricResolver metricResolver, HttpClientPool httpClientPool) {
|
||||||
HttpClientPool httpClientPool) {
|
|
||||||
this.balancerConf = balancerConf;
|
|
||||||
this.metricResolver = metricResolver;
|
this.metricResolver = metricResolver;
|
||||||
this.httpClientPool = httpClientPool;
|
this.httpClientPool = httpClientPool;
|
||||||
}
|
}
|
||||||
@@ -91,7 +86,6 @@ public class HttpRelayHandler extends SimpleChannelInboundHandler<HttpRequest> i
|
|||||||
} else {
|
} else {
|
||||||
k = metricResolver.getHostPortKey(null);
|
k = metricResolver.getHostPortKey(null);
|
||||||
}
|
}
|
||||||
k = metricResolver.getHostPortKey(metric);
|
|
||||||
client = httpClientPool.borrowObject(k);
|
client = httpClientPool.borrowObject(k);
|
||||||
|
|
||||||
Multimap<String, String> headers = HttpHeaderUtils.toMultimap(request.headers());
|
Multimap<String, String> headers = HttpHeaderUtils.toMultimap(request.headers());
|
||||||
@@ -208,13 +202,4 @@ public class HttpRelayHandler extends SimpleChannelInboundHandler<HttpRequest> i
|
|||||||
}
|
}
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
|
|
||||||
String s = "https://127.0.0.1:4243/api/query?start=1356998400000&end=1528398304000&m=sum:rate{false,100,0}:sys.thermal.gauge{class=aws}";
|
|
||||||
URI uri = URI.create(encodeURI(s));
|
|
||||||
|
|
||||||
System.out.println(uri.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
+2
-2
@@ -4,12 +4,12 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>timely</groupId>
|
<groupId>timely</groupId>
|
||||||
<artifactId>timely</artifactId>
|
<artifactId>timely</artifactId>
|
||||||
<version>1.1.1-SNAPSHOT</version>
|
<version>1.2.3</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>timely-client</artifactId>
|
<artifactId>timely-client</artifactId>
|
||||||
<properties>
|
<properties>
|
||||||
<!-- PEP 440 compliant version gets substituted into src/main/python/template/setup.py -->
|
<!-- PEP 440 compliant version gets substituted into src/main/python/template/setup.py -->
|
||||||
<python.client.version>1.1.1.dev0</python.client.version>
|
<python.client.version>1.2.3</python.client.version>
|
||||||
</properties>
|
</properties>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|||||||
@@ -46,15 +46,21 @@ public class HttpClient {
|
|||||||
|
|
||||||
public static CloseableHttpClient get(SSLContext ssl, CookieStore cookieStore, boolean hostVerificationEnabled,
|
public static CloseableHttpClient get(SSLContext ssl, CookieStore cookieStore, boolean hostVerificationEnabled,
|
||||||
boolean clientAuth) {
|
boolean clientAuth) {
|
||||||
RequestConfig defaultRequestConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build();
|
|
||||||
HostnameVerifier hostnameVerifier;
|
HostnameVerifier hostnameVerifier;
|
||||||
if (hostVerificationEnabled) {
|
if (hostVerificationEnabled) {
|
||||||
hostnameVerifier = SSLConnectionSocketFactory.getDefaultHostnameVerifier();
|
hostnameVerifier = SSLConnectionSocketFactory.getDefaultHostnameVerifier();
|
||||||
} else {
|
} else {
|
||||||
hostnameVerifier = new NoopHostnameVerifier();
|
hostnameVerifier = new NoopHostnameVerifier();
|
||||||
}
|
}
|
||||||
HttpClientBuilder builder = HttpClients.custom().setSSLContext(ssl).setDefaultCookieStore(cookieStore)
|
RequestConfig defaultRequestConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build();
|
||||||
.setDefaultRequestConfig(defaultRequestConfig).setSSLHostnameVerifier(hostnameVerifier);
|
HttpClientBuilder builder;
|
||||||
|
if (cookieStore == null) {
|
||||||
|
builder = HttpClients.custom().setSSLContext(ssl).disableCookieManagement()
|
||||||
|
.setDefaultRequestConfig(defaultRequestConfig).setSSLHostnameVerifier(hostnameVerifier);
|
||||||
|
} else {
|
||||||
|
builder = HttpClients.custom().setSSLContext(ssl).setDefaultCookieStore(cookieStore)
|
||||||
|
.setDefaultRequestConfig(defaultRequestConfig).setSSLHostnameVerifier(hostnameVerifier);
|
||||||
|
}
|
||||||
|
|
||||||
if (clientAuth) {
|
if (clientAuth) {
|
||||||
SSLConnectionSocketFactory sslCSF = new SSLConnectionSocketFactory(ssl,
|
SSLConnectionSocketFactory sslCSF = new SSLConnectionSocketFactory(ssl,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>timely</groupId>
|
<groupId>timely</groupId>
|
||||||
<artifactId>timely</artifactId>
|
<artifactId>timely</artifactId>
|
||||||
<version>1.1.1-SNAPSHOT</version>
|
<version>1.2.3</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>collectd-nsq-plugin</artifactId>
|
<artifactId>collectd-nsq-plugin</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>timely</groupId>
|
<groupId>timely</groupId>
|
||||||
<artifactId>timely</artifactId>
|
<artifactId>timely</artifactId>
|
||||||
<version>1.1.1-SNAPSHOT</version>
|
<version>1.2.3</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>collectd-plugin-parent</artifactId>
|
<artifactId>collectd-plugin-parent</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>timely</groupId>
|
<groupId>timely</groupId>
|
||||||
<artifactId>timely</artifactId>
|
<artifactId>timely</artifactId>
|
||||||
<version>1.1.1-SNAPSHOT</version>
|
<version>1.2.3</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>collectd-timely-plugin</artifactId>
|
<artifactId>collectd-timely-plugin</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>timely</groupId>
|
<groupId>timely</groupId>
|
||||||
<artifactId>timely</artifactId>
|
<artifactId>timely</artifactId>
|
||||||
<version>1.1.1-SNAPSHOT</version>
|
<version>1.2.3</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>timely-common</artifactId>
|
<artifactId>timely-common</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|||||||
Executable
+42
@@ -0,0 +1,42 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if [[ `uname` == "Darwin" ]]; then
|
||||||
|
THIS_SCRIPT=`python -c 'import os,sys; print os.path.realpath(sys.argv[1])' $0`
|
||||||
|
TCNATIVE_SUFFIX="jnilib"
|
||||||
|
else
|
||||||
|
THIS_SCRIPT=`readlink -f $0`
|
||||||
|
TCNATIVE_SUFFIX="so"
|
||||||
|
fi
|
||||||
|
|
||||||
|
THIS_DIR="${THIS_SCRIPT%/*}"
|
||||||
|
NATIVE_DIR="${THIS_DIR}/META-INF/native"
|
||||||
|
BASE_DIR=${THIS_DIR}/..
|
||||||
|
TMP_DIR="${BASE_DIR}/tmp"
|
||||||
|
CONF_DIR="${BASE_DIR}/conf"
|
||||||
|
LIB_DIR="${BASE_DIR}/lib"
|
||||||
|
NUM_SERVER_THREADS=4
|
||||||
|
|
||||||
|
if [[ -e ${TMP_DIR} ]]; then
|
||||||
|
rm -rf ${TMP_DIR}
|
||||||
|
fi
|
||||||
|
mkdir ${TMP_DIR}
|
||||||
|
|
||||||
|
if [[ -e ${NATIVE_DIR} ]]; then
|
||||||
|
rm -rf ${NATIVE_DIR}
|
||||||
|
fi
|
||||||
|
mkdir -p ${NATIVE_DIR}
|
||||||
|
|
||||||
|
pushd ${BASE_DIR}/bin
|
||||||
|
$JAVA_HOME/bin/jar xf ${LIB_DIR}/netty-tcnative-boringssl-static*.jar META-INF/native/libnetty_tcnative_linux_x86_64.${TCNATIVE_SUFFIX}
|
||||||
|
$JAVA_HOME/bin/jar xf ${LIB_DIR}/netty-all*.jar META-INF/native/libnetty_transport_native_epoll_x86_64.${TCNATIVE_SUFFIX}
|
||||||
|
popd
|
||||||
|
|
||||||
|
export CLASSPATH="${CONF_DIR}:${LIB_DIR}/*:${HADOOP_CONF_DIR}"
|
||||||
|
JVM_ARGS="-Xmx1G -Xms1G -Dio.netty.eventLoopThreads=${NUM_SERVER_THREADS} Dlog4j.configurationFile=${CONF_DIR}/log4j2-spring.xml"
|
||||||
|
JVM_ARGS="${JVM_ARGS} -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector"
|
||||||
|
JVM_ARGS="${JVM_ARGS} -Djava.library.path=${NATIVE_DIR}"
|
||||||
|
|
||||||
|
#JVM_ARGS="${JVM_ARGS} -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005"
|
||||||
|
|
||||||
|
echo "$JAVA_HOME/bin/java ${JVM_ARGS} timely.grafana.auth.GrafanaAuth --spring.config.name=grafana-auth"
|
||||||
|
$JAVA_HOME/bin/java ${JVM_ARGS} timely.grafana.auth.GrafanaAuth --spring.config.name=grafana-auth
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
grafana-auth:
|
||||||
|
grafana:
|
||||||
|
host: localhost
|
||||||
|
port: 3001
|
||||||
|
security:
|
||||||
|
session-max-age: 86400
|
||||||
|
cache-expiration-minutes: 60
|
||||||
|
cache-refresh-minutes: 5
|
||||||
|
allow-anonymous-http-access: true
|
||||||
|
allow-anonymous-ws-access: true
|
||||||
|
serverSsl:
|
||||||
|
certificate-file:
|
||||||
|
key-file:
|
||||||
|
#key-password:
|
||||||
|
use-generated-keypair: true
|
||||||
|
trust-store-file:
|
||||||
|
use-openssl: true
|
||||||
|
#use-ciphers:
|
||||||
|
clientSsl:
|
||||||
|
keyFile:
|
||||||
|
keyType: pkcs12
|
||||||
|
#keyPassword:
|
||||||
|
trustStoreFile:
|
||||||
|
trustStoreType: jks
|
||||||
|
#trustStorePassword:
|
||||||
|
http:
|
||||||
|
host: localhost
|
||||||
|
ip: 127.0.0.1
|
||||||
|
port: 3000
|
||||||
|
redirect-path: /secure-me
|
||||||
|
strict-transport-max-age: 608400
|
||||||
|
cors:
|
||||||
|
allow-any-origin: false
|
||||||
|
allow-null-origin: false
|
||||||
|
allow-credentials: true
|
||||||
|
allowed-origins:
|
||||||
|
allowed-methods: DELETE,GET,HEAD,OPTIONS,PUT,POST
|
||||||
|
allowed-headers: content-type
|
||||||
|
httpClientPool:
|
||||||
|
minIdlePerKey: 0
|
||||||
|
maxIdlePerKey: 40
|
||||||
|
maxTotalPerKey: 40
|
||||||
|
maxTotal: -1
|
||||||
|
lifo: true
|
||||||
|
fairness: false
|
||||||
|
maxWaitMillis: -1
|
||||||
|
minEvictableIdleTimeMillis: 1800000
|
||||||
|
evictorShutdownTimeoutMillis: 10000
|
||||||
|
softMinEvictableIdleTimeMillis: -1
|
||||||
|
numTestsPerEvictionRun: 3
|
||||||
|
timeBetweenEvictionRunsMillis: -1
|
||||||
|
blockWhenExhausted: true
|
||||||
|
jmxEnabled: true
|
||||||
|
testOnBorrow: false
|
||||||
|
testOnReturn: false
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Configuration status="warn" monitorInterval="60">
|
||||||
|
<Appenders>
|
||||||
|
<Console name="Console" target="SYSTEM_OUT">
|
||||||
|
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
|
||||||
|
</Console>
|
||||||
|
</Appenders>
|
||||||
|
<Loggers>
|
||||||
|
<Root level="error" includeLocation="false">
|
||||||
|
<AppenderRef ref="Console" level="error" />
|
||||||
|
</Root>
|
||||||
|
</Loggers>
|
||||||
|
</Configuration>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Configuration status="warn" monitorInterval="60">
|
||||||
|
<Properties>
|
||||||
|
<Property name="PID">????</Property>
|
||||||
|
<Property name="LOG_PATTERN">%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</Property>
|
||||||
|
</Properties>
|
||||||
|
<Appenders>
|
||||||
|
<RollingRandomAccessFile name="Rolling" fileName="/srv/logs/timely/grafana-auth.log"
|
||||||
|
filePattern="/srv/logs/timely/$${date:yyyy-MM}/grafana-auth-%d{MM-dd-yyyy}-%i.log.gz">
|
||||||
|
<PatternLayout pattern="${LOG_PATTERN}"/>
|
||||||
|
<Policies>
|
||||||
|
<SizeBasedTriggeringPolicy size="1024 MB" />
|
||||||
|
</Policies>
|
||||||
|
<DefaultRolloverStrategy max="10" />
|
||||||
|
</RollingRandomAccessFile>
|
||||||
|
<Console name="Console" target="SYSTEM_OUT">
|
||||||
|
<PatternLayout pattern="${LOG_PATTERN}"/>
|
||||||
|
</Console>
|
||||||
|
</Appenders>
|
||||||
|
<Loggers>
|
||||||
|
<Root level="trace" includeLocation="false">
|
||||||
|
<AppenderRef ref="Console" level="warn" />
|
||||||
|
<AppenderRef ref="Rolling" level="trace" />
|
||||||
|
</Root>
|
||||||
|
<Logger name="timely" additivity="false">
|
||||||
|
<AppenderRef ref="Console" level="debug" />
|
||||||
|
</Logger>
|
||||||
|
</Loggers>
|
||||||
|
</Configuration>
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
<beans:beans xmlns="http://www.springframework.org/schema/security"
|
||||||
|
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||||
|
xmlns:util="http://www.springframework.org/schema/util"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||||
|
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
|
||||||
|
http://www.springframework.org/schema/security
|
||||||
|
http://www.springframework.org/schema/security/spring-security.xsd
|
||||||
|
http://www.springframework.org/schema/util
|
||||||
|
http://www.springframework.org/schema/util/spring-util.xsd">
|
||||||
|
|
||||||
|
<beans:bean id="x509PrincipalExtractor" class="org.springframework.security.web.authentication.preauth.x509.SubjectDnX509PrincipalExtractor">
|
||||||
|
<beans:property name="subjectDnRegex" value="CN=(.*?)(?:,|$)"/>
|
||||||
|
</beans:bean>
|
||||||
|
|
||||||
|
<beans:bean id="timelyUserDetailsService" class="timely.auth.UserDetailsService">
|
||||||
|
<beans:property name="users">
|
||||||
|
<util:list>
|
||||||
|
<beans:bean class="timely.auth.TimelyUser">
|
||||||
|
<beans:constructor-arg name="subjectDn">
|
||||||
|
<beans:value>CN=example.com</beans:value>
|
||||||
|
</beans:constructor-arg>
|
||||||
|
<beans:constructor-arg name="auths">
|
||||||
|
<util:list>
|
||||||
|
<beans:value>D</beans:value>
|
||||||
|
<beans:value>E</beans:value>
|
||||||
|
<beans:value>F</beans:value>
|
||||||
|
</util:list>
|
||||||
|
</beans:constructor-arg>
|
||||||
|
<beans:constructor-arg name="roles">
|
||||||
|
<util:list>
|
||||||
|
<beans:value>G</beans:value>
|
||||||
|
<beans:value>H</beans:value>
|
||||||
|
<beans:value>I</beans:value>
|
||||||
|
</util:list>
|
||||||
|
</beans:constructor-arg>
|
||||||
|
</beans:bean>
|
||||||
|
</util:list>
|
||||||
|
</beans:property>
|
||||||
|
</beans:bean>
|
||||||
|
|
||||||
|
<beans:bean id="x509AuthorizationProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
|
||||||
|
<beans:property name="preAuthenticatedUserDetailsService" ref="timelyUserDetailsService" />
|
||||||
|
</beans:bean>
|
||||||
|
|
||||||
|
<user-service id="usernamePasswordService">
|
||||||
|
<user name="test" password="test1" authorities="A,B,C" />
|
||||||
|
</user-service>
|
||||||
|
|
||||||
|
<beans:bean id="noOpPasswordEncoder" class="org.springframework.security.crypto.password.NoOpPasswordEncoder"/>
|
||||||
|
|
||||||
|
<beans:bean id="UserNamePasswordAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
|
||||||
|
<beans:property name="userDetailsService" ref="usernamePasswordService"/>
|
||||||
|
<beans:property name="passwordEncoder" ref="noOpPasswordEncoder"/>
|
||||||
|
</beans:bean>
|
||||||
|
|
||||||
|
<authentication-manager alias="authenticationManager">
|
||||||
|
<!-- X509 PreAuthenticated authorization provider -->
|
||||||
|
<authentication-provider ref="x509AuthorizationProvider"/>
|
||||||
|
<!-- Basic authorization provider, validates provided username and password -->
|
||||||
|
<authentication-provider ref="UserNamePasswordAuthenticationProvider" />
|
||||||
|
</authentication-manager>
|
||||||
|
|
||||||
|
<util:list id="requiredRoles" value-type="java.lang.String">
|
||||||
|
<beans:value></beans:value>
|
||||||
|
</util:list>
|
||||||
|
|
||||||
|
<util:list id="requiredAuths" value-type="java.lang.String">
|
||||||
|
<beans:value></beans:value>
|
||||||
|
</util:list>
|
||||||
|
</beans:beans>
|
||||||
@@ -0,0 +1,296 @@
|
|||||||
|
<?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>1.2.3</version>
|
||||||
|
</parent>
|
||||||
|
<artifactId>grafana-auth</artifactId>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-annotations</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||||
|
<artifactId>jackson-datatype-jdk8</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||||
|
<artifactId>caffeine</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.flatbuffers</groupId>
|
||||||
|
<artifactId>flatbuffers-java</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-beanutils</groupId>
|
||||||
|
<artifactId>commons-beanutils</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.github.lukehutch</groupId>
|
||||||
|
<artifactId>fast-classpath-scanner</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.netty</groupId>
|
||||||
|
<artifactId>netty-all</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.sourceforge.javacsv</groupId>
|
||||||
|
<artifactId>javacsv</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.accumulo</groupId>
|
||||||
|
<artifactId>accumulo-core</artifactId>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-log4j12</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.accumulo</groupId>
|
||||||
|
<artifactId>accumulo-minicluster</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-collections4</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.curator</groupId>
|
||||||
|
<artifactId>curator-client</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.curator</groupId>
|
||||||
|
<artifactId>curator-framework</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.curator</groupId>
|
||||||
|
<artifactId>curator-recipes</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.curator</groupId>
|
||||||
|
<artifactId>curator-x-discovery</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.hadoop</groupId>
|
||||||
|
<artifactId>hadoop-client</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.hadoop</groupId>
|
||||||
|
<artifactId>hadoop-common</artifactId>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>jdk.tools</groupId>
|
||||||
|
<artifactId>jdk.tools</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.hadoop</groupId>
|
||||||
|
<artifactId>hadoop-minicluster</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-websocket</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter</artifactId>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-logging</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-log4j2</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-validation</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.security</groupId>
|
||||||
|
<artifactId>spring-security-config</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.security</groupId>
|
||||||
|
<artifactId>spring-security-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>timely</groupId>
|
||||||
|
<artifactId>timely-balancer</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>timely</groupId>
|
||||||
|
<artifactId>timely-client</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>timely</groupId>
|
||||||
|
<artifactId>timely-common</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>timely</groupId>
|
||||||
|
<artifactId>timely-server</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.lmax</groupId>
|
||||||
|
<artifactId>disruptor</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.netty</groupId>
|
||||||
|
<artifactId>netty-tcnative-boringssl-static</artifactId>
|
||||||
|
<version>${version.netty-tcnative}</version>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
|
<artifactId>log4j-api</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
|
<artifactId>log4j-core</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
|
<artifactId>log4j-slf4j-impl</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.javassist</groupId>
|
||||||
|
<artifactId>javassist</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jsoup</groupId>
|
||||||
|
<artifactId>jsoup</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-test-autoconfigure</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>assemble</id>
|
||||||
|
<goals>
|
||||||
|
<goal>single</goal>
|
||||||
|
</goals>
|
||||||
|
<phase>package</phase>
|
||||||
|
<configuration>
|
||||||
|
<descriptors>
|
||||||
|
<descriptor>src/main/dist/assembly.xml</descriptor>
|
||||||
|
</descriptors>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-dependency-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>copy-dependencies</id>
|
||||||
|
<goals>
|
||||||
|
<goal>copy-dependencies</goal>
|
||||||
|
</goals>
|
||||||
|
<phase>prepare-package</phase>
|
||||||
|
<configuration>
|
||||||
|
<excludeArtifactIds>junit</excludeArtifactIds>
|
||||||
|
<outputDirectory>${project.build.directory}/lib</outputDirectory>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
<execution>
|
||||||
|
<id>unpack-native</id>
|
||||||
|
<goals>
|
||||||
|
<goal>unpack</goal>
|
||||||
|
</goals>
|
||||||
|
<phase>process-test-classes</phase>
|
||||||
|
<configuration>
|
||||||
|
<artifactItems>
|
||||||
|
<artifactItem>
|
||||||
|
<groupId>io.netty</groupId>
|
||||||
|
<artifactId>netty-tcnative-boringssl-static</artifactId>
|
||||||
|
<version>${version.netty-tcnative}</version>
|
||||||
|
<type>jar</type>
|
||||||
|
<overWrite>false</overWrite>
|
||||||
|
<outputDirectory>${project.build.directory}/native</outputDirectory>
|
||||||
|
<includes>**/*.so</includes>
|
||||||
|
</artifactItem>
|
||||||
|
</artifactItems>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-failsafe-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
|
<artifactId>build-helper-maven-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>add-source</id>
|
||||||
|
<goals>
|
||||||
|
<goal>add-source</goal>
|
||||||
|
</goals>
|
||||||
|
<phase>generate-sources</phase>
|
||||||
|
<configuration>
|
||||||
|
<sources>
|
||||||
|
<source>src/main/generated-java</source>
|
||||||
|
</sources>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
<extensions>
|
||||||
|
<!-- Use os-maven-plugin to initialize the "os.detected" properties -->
|
||||||
|
<extension>
|
||||||
|
<groupId>kr.motd.maven</groupId>
|
||||||
|
<artifactId>os-maven-plugin</artifactId>
|
||||||
|
</extension>
|
||||||
|
</extensions>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
+55
@@ -0,0 +1,55 @@
|
|||||||
|
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
|
||||||
|
<id>dist</id>
|
||||||
|
<formats>
|
||||||
|
<format>tar.gz</format>
|
||||||
|
</formats>
|
||||||
|
<fileSets>
|
||||||
|
<fileSet>
|
||||||
|
<directory>${project.basedir}</directory>
|
||||||
|
<outputDirectory></outputDirectory>
|
||||||
|
<includes>
|
||||||
|
<include>README*</include>
|
||||||
|
<include>LICENSE*</include>
|
||||||
|
<include>NOTICE*</include>
|
||||||
|
</includes>
|
||||||
|
</fileSet>
|
||||||
|
<fileSet>
|
||||||
|
<directory>${project.build.directory}</directory>
|
||||||
|
<outputDirectory>lib</outputDirectory>
|
||||||
|
<includes>
|
||||||
|
<include>timely*.jar</include>
|
||||||
|
<include>grafana*.jar</include>
|
||||||
|
</includes>
|
||||||
|
</fileSet>
|
||||||
|
<fileSet>
|
||||||
|
<directory>${project.build.directory}/lib</directory>
|
||||||
|
<outputDirectory>lib</outputDirectory>
|
||||||
|
<includes>
|
||||||
|
<include>*.jar</include>
|
||||||
|
</includes>
|
||||||
|
</fileSet>
|
||||||
|
<fileSet>
|
||||||
|
<directory>${project.basedir}/bin</directory>
|
||||||
|
<outputDirectory>bin</outputDirectory>
|
||||||
|
<includes>
|
||||||
|
<include>**/*</include>
|
||||||
|
</includes>
|
||||||
|
</fileSet>
|
||||||
|
<fileSet>
|
||||||
|
<directory>${project.basedir}/conf</directory>
|
||||||
|
<outputDirectory>conf</outputDirectory>
|
||||||
|
<includes>
|
||||||
|
<include>*</include>
|
||||||
|
</includes>
|
||||||
|
</fileSet>
|
||||||
|
<fileSet>
|
||||||
|
<directory>${project.basedir}/src/main/resources/webapp</directory>
|
||||||
|
<outputDirectory>bin/webapp</outputDirectory>
|
||||||
|
<includes>
|
||||||
|
<include>*</include>
|
||||||
|
</includes>
|
||||||
|
</fileSet>
|
||||||
|
</fileSets>
|
||||||
|
</assembly>
|
||||||
@@ -0,0 +1,321 @@
|
|||||||
|
package timely.grafana.auth;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
|
|
||||||
|
import io.netty.bootstrap.ServerBootstrap;
|
||||||
|
import io.netty.buffer.PooledByteBufAllocator;
|
||||||
|
import io.netty.channel.Channel;
|
||||||
|
import io.netty.channel.ChannelFuture;
|
||||||
|
import io.netty.channel.ChannelHandler;
|
||||||
|
import io.netty.channel.ChannelInitializer;
|
||||||
|
import io.netty.channel.ChannelOption;
|
||||||
|
import io.netty.channel.EventLoopGroup;
|
||||||
|
import io.netty.channel.epoll.EpollEventLoopGroup;
|
||||||
|
import io.netty.channel.epoll.EpollServerSocketChannel;
|
||||||
|
import io.netty.channel.nio.NioEventLoopGroup;
|
||||||
|
import io.netty.channel.socket.ServerSocketChannel;
|
||||||
|
import io.netty.channel.socket.SocketChannel;
|
||||||
|
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||||
|
import io.netty.handler.codec.http.HttpContentCompressor;
|
||||||
|
import io.netty.handler.codec.http.HttpContentDecompressor;
|
||||||
|
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||||
|
import io.netty.handler.codec.http.HttpRequestDecoder;
|
||||||
|
import io.netty.handler.codec.http.HttpResponseEncoder;
|
||||||
|
import io.netty.handler.logging.LoggingHandler;
|
||||||
|
import io.netty.handler.ssl.ClientAuth;
|
||||||
|
import io.netty.handler.ssl.OpenSslServerContext;
|
||||||
|
import io.netty.handler.ssl.OpenSslServerSessionContext;
|
||||||
|
import io.netty.handler.ssl.SslContext;
|
||||||
|
import io.netty.handler.ssl.SslContextBuilder;
|
||||||
|
import io.netty.handler.ssl.SslProvider;
|
||||||
|
import io.netty.handler.ssl.util.SelfSignedCertificate;
|
||||||
|
import io.netty.handler.stream.ChunkedWriteHandler;
|
||||||
|
import io.netty.util.internal.SystemPropertyUtil;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.boot.WebApplicationType;
|
||||||
|
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||||
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
|
import timely.auth.AuthCache;
|
||||||
|
import timely.balancer.configuration.ClientSsl;
|
||||||
|
import timely.balancer.connection.http.HttpClientPool;
|
||||||
|
import timely.client.http.HttpClient;
|
||||||
|
import timely.configuration.ServerSsl;
|
||||||
|
import timely.grafana.auth.configuration.GrafanaAuthConfiguration;
|
||||||
|
import timely.grafana.auth.configuration.SpringBootstrap;
|
||||||
|
import timely.grafana.auth.netty.http.GrafanaRelayHandler;
|
||||||
|
import timely.grafana.auth.netty.http.GrafanaRequestDecoder;
|
||||||
|
import timely.netty.http.HttpStaticFileServerHandler;
|
||||||
|
import timely.netty.http.NonSslRedirectHandler;
|
||||||
|
import timely.netty.http.TimelyExceptionHandler;
|
||||||
|
import timely.netty.http.auth.X509LoginRequestHandler;
|
||||||
|
|
||||||
|
public class GrafanaAuth {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(GrafanaAuth.class);
|
||||||
|
private static final int EPOLL_MIN_MAJOR_VERSION = 2;
|
||||||
|
private static final int EPOLL_MIN_MINOR_VERSION = 6;
|
||||||
|
private static final int EPOLL_MIN_PATCH_VERSION = 32;
|
||||||
|
private static final String OS_NAME = "os.name";
|
||||||
|
private static final String OS_VERSION = "os.version";
|
||||||
|
protected static final CountDownLatch LATCH = new CountDownLatch(1);
|
||||||
|
static ConfigurableApplicationContext applicationContext;
|
||||||
|
|
||||||
|
private final GrafanaAuthConfiguration grafanaAuthConfig;
|
||||||
|
private EventLoopGroup httpWorkerGroup = null;
|
||||||
|
private EventLoopGroup httpBossGroup = null;
|
||||||
|
protected Channel httpChannelHandle = null;
|
||||||
|
protected volatile boolean shutdown = false;
|
||||||
|
|
||||||
|
public GrafanaAuth(GrafanaAuthConfiguration grafanaAuthConfig) throws Exception {
|
||||||
|
this.grafanaAuthConfig = grafanaAuthConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static ConfigurableApplicationContext initializeConfiguration(String[] args) {
|
||||||
|
return new SpringApplicationBuilder(SpringBootstrap.class).web(WebApplicationType.NONE).run(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shutdownHook() {
|
||||||
|
|
||||||
|
final Runnable shutdownRunner = () -> {
|
||||||
|
if (!shutdown) {
|
||||||
|
shutdown();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
final Thread hook = new Thread(shutdownRunner, "shutdown-hook-thread");
|
||||||
|
Runtime.getRuntime().addShutdownHook(hook);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void shutdown() {
|
||||||
|
List<ChannelFuture> channelFutures = new ArrayList<>();
|
||||||
|
|
||||||
|
if (httpChannelHandle != null) {
|
||||||
|
LOG.info("Closing httpChannelHandle");
|
||||||
|
channelFutures.add(httpChannelHandle.close());
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for the channels to shutdown
|
||||||
|
channelFutures.forEach(f -> {
|
||||||
|
try {
|
||||||
|
f.get();
|
||||||
|
} catch (final Exception e) {
|
||||||
|
LOG.error("Error while shutting down channel: {}", f.channel().config());
|
||||||
|
LOG.error("{}", e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Integer quietPeriod = grafanaAuthConfig.getShutdownQuietPeriod();
|
||||||
|
List<Future<?>> groupFutures = new ArrayList<>();
|
||||||
|
|
||||||
|
if (httpBossGroup != null) {
|
||||||
|
LOG.info("Shutting down httpBossGroup");
|
||||||
|
groupFutures.add(httpBossGroup.shutdownGracefully(quietPeriod, 10, TimeUnit.SECONDS));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (httpWorkerGroup != null) {
|
||||||
|
LOG.info("Shutting down httpWorkerGroup");
|
||||||
|
groupFutures.add(httpWorkerGroup.shutdownGracefully(quietPeriod, 10, TimeUnit.SECONDS));
|
||||||
|
}
|
||||||
|
|
||||||
|
groupFutures.parallelStream().forEach(f -> {
|
||||||
|
try {
|
||||||
|
f.get();
|
||||||
|
} catch (final Exception e) {
|
||||||
|
LOG.error("Error while shutting down group: {}", f.toString());
|
||||||
|
LOG.error("{}", e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (applicationContext != null) {
|
||||||
|
LOG.info("Closing applicationContext");
|
||||||
|
applicationContext.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.shutdown = true;
|
||||||
|
LOG.info("Server shut down.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean useEpoll() {
|
||||||
|
|
||||||
|
// Should we just return true if this is Linux and if we get an error
|
||||||
|
// during Epoll
|
||||||
|
// setup handle it there?
|
||||||
|
final String os = SystemPropertyUtil.get(OS_NAME).toLowerCase().trim();
|
||||||
|
final String[] version = SystemPropertyUtil.get(OS_VERSION).toLowerCase().trim().split("\\.");
|
||||||
|
if (os.startsWith("linux") && version.length >= 3) {
|
||||||
|
final int major = Integer.parseInt(version[0]);
|
||||||
|
if (major > EPOLL_MIN_MAJOR_VERSION) {
|
||||||
|
return true;
|
||||||
|
} else if (major == EPOLL_MIN_MAJOR_VERSION) {
|
||||||
|
final int minor = Integer.parseInt(version[1]);
|
||||||
|
if (minor > EPOLL_MIN_MINOR_VERSION) {
|
||||||
|
return true;
|
||||||
|
} else if (minor == EPOLL_MIN_MINOR_VERSION) {
|
||||||
|
final int patch = Integer.parseInt(version[2].substring(0, 2));
|
||||||
|
return patch >= EPOLL_MIN_PATCH_VERSION;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected SslContext createSSLContext(GrafanaAuthConfiguration config) throws Exception {
|
||||||
|
|
||||||
|
ServerSsl sslCfg = config.getSecurity().getServerSsl();
|
||||||
|
Boolean generate = sslCfg.isUseGeneratedKeypair();
|
||||||
|
SslContextBuilder ssl;
|
||||||
|
if (generate) {
|
||||||
|
LOG.warn("Using generated self signed server certificate");
|
||||||
|
Date begin = new Date();
|
||||||
|
Date end = new Date(begin.getTime() + 86400000);
|
||||||
|
SelfSignedCertificate ssc = new SelfSignedCertificate("localhost", begin, end);
|
||||||
|
ssl = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey());
|
||||||
|
} else {
|
||||||
|
String cert = sslCfg.getCertificateFile();
|
||||||
|
String key = sslCfg.getKeyFile();
|
||||||
|
String keyPass = sslCfg.getKeyPassword();
|
||||||
|
if (null == cert || null == key) {
|
||||||
|
throw new IllegalArgumentException("Check your SSL properties, something is wrong.");
|
||||||
|
}
|
||||||
|
ssl = SslContextBuilder.forServer(new File(cert), new File(key), keyPass);
|
||||||
|
}
|
||||||
|
|
||||||
|
ssl.ciphers(sslCfg.getUseCiphers());
|
||||||
|
|
||||||
|
// Can't set to REQUIRE because the CORS pre-flight requests will fail.
|
||||||
|
ssl.clientAuth(ClientAuth.OPTIONAL);
|
||||||
|
|
||||||
|
Boolean useOpenSSL = sslCfg.isUseOpenssl();
|
||||||
|
if (useOpenSSL) {
|
||||||
|
ssl.sslProvider(SslProvider.OPENSSL);
|
||||||
|
} else {
|
||||||
|
ssl.sslProvider(SslProvider.JDK);
|
||||||
|
}
|
||||||
|
String trustStore = sslCfg.getTrustStoreFile();
|
||||||
|
if (null != trustStore) {
|
||||||
|
if (!trustStore.isEmpty()) {
|
||||||
|
ssl.trustManager(new File(trustStore));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ssl.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected SSLContext createSSLClientContext(GrafanaAuthConfiguration config) throws Exception {
|
||||||
|
|
||||||
|
ClientSsl ssl = config.getSecurity().getClientSsl();
|
||||||
|
return HttpClient.getSSLContext(ssl.getTrustStoreFile(), ssl.getTrustStoreType(), ssl.getTrustStorePassword(),
|
||||||
|
ssl.getKeyFile(), ssl.getKeyType(), ssl.getKeyPassword());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() throws Exception {
|
||||||
|
|
||||||
|
AuthCache.configure(grafanaAuthConfig.getSecurity());
|
||||||
|
final boolean useEpoll = useEpoll();
|
||||||
|
Class<? extends ServerSocketChannel> channelClass;
|
||||||
|
if (useEpoll) {
|
||||||
|
httpWorkerGroup = new EpollEventLoopGroup();
|
||||||
|
httpBossGroup = new EpollEventLoopGroup();
|
||||||
|
channelClass = EpollServerSocketChannel.class;
|
||||||
|
} else {
|
||||||
|
httpWorkerGroup = new NioEventLoopGroup();
|
||||||
|
httpBossGroup = new NioEventLoopGroup();
|
||||||
|
channelClass = NioServerSocketChannel.class;
|
||||||
|
}
|
||||||
|
LOG.info("Using channel class {}", channelClass.getSimpleName());
|
||||||
|
|
||||||
|
final int httpPort = grafanaAuthConfig.getHttp().getPort();
|
||||||
|
final String httpIp = grafanaAuthConfig.getHttp().getIp();
|
||||||
|
SslContext sslCtx = createSSLContext(grafanaAuthConfig);
|
||||||
|
if (sslCtx instanceof OpenSslServerContext) {
|
||||||
|
OpenSslServerContext openssl = (OpenSslServerContext) sslCtx;
|
||||||
|
String application = "GrafanaAuth_" + httpPort;
|
||||||
|
OpenSslServerSessionContext opensslCtx = openssl.sessionContext();
|
||||||
|
opensslCtx.setSessionCacheEnabled(true);
|
||||||
|
opensslCtx.setSessionCacheSize(128);
|
||||||
|
opensslCtx.setSessionIdContext(application.getBytes(StandardCharsets.UTF_8));
|
||||||
|
opensslCtx.setSessionTimeout(grafanaAuthConfig.getSecurity().getSessionMaxAge());
|
||||||
|
}
|
||||||
|
SSLContext clientSSLContext = createSSLClientContext(grafanaAuthConfig);
|
||||||
|
final ServerBootstrap httpServer = new ServerBootstrap();
|
||||||
|
httpServer.group(httpBossGroup, httpWorkerGroup);
|
||||||
|
httpServer.channel(channelClass);
|
||||||
|
httpServer.handler(new LoggingHandler());
|
||||||
|
HttpClientPool httpClientPool = new HttpClientPool(grafanaAuthConfig.getSecurity(), grafanaAuthConfig.getHttp(),
|
||||||
|
clientSSLContext);
|
||||||
|
httpServer.childHandler(setupHttpChannel(grafanaAuthConfig, sslCtx, httpClientPool));
|
||||||
|
httpServer.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
|
||||||
|
httpServer.option(ChannelOption.SO_BACKLOG, 128);
|
||||||
|
httpServer.option(ChannelOption.SO_KEEPALIVE, true);
|
||||||
|
httpChannelHandle = httpServer.bind(httpIp, httpPort).sync().channel();
|
||||||
|
final String httpAddress = ((InetSocketAddress) httpChannelHandle.localAddress()).getAddress().getHostAddress();
|
||||||
|
|
||||||
|
shutdownHook();
|
||||||
|
LOG.info("Server started. Listening on {}:{} for HTTP traffic", httpAddress, httpPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
|
||||||
|
GrafanaAuth.applicationContext = GrafanaAuth.initializeConfiguration(args);
|
||||||
|
GrafanaAuthConfiguration balancerConf = applicationContext.getBean(GrafanaAuthConfiguration.class);
|
||||||
|
|
||||||
|
GrafanaAuth b = new GrafanaAuth(balancerConf);
|
||||||
|
try {
|
||||||
|
b.run();
|
||||||
|
LATCH.await();
|
||||||
|
} catch (final InterruptedException e) {
|
||||||
|
LOG.info("Server shutting down.");
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error("Error running server.", e);
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
b.shutdown();
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ChannelHandler setupHttpChannel(GrafanaAuthConfiguration config, SslContext sslCtx,
|
||||||
|
HttpClientPool httpClientPool) {
|
||||||
|
|
||||||
|
return new ChannelInitializer<SocketChannel>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void initChannel(SocketChannel ch) throws Exception {
|
||||||
|
|
||||||
|
ch.pipeline().addLast("ssl", new NonSslRedirectHandler(config.getHttp(), sslCtx));
|
||||||
|
ch.pipeline().addLast("encoder", new HttpResponseEncoder());
|
||||||
|
ch.pipeline().addLast("decoder", new HttpRequestDecoder());
|
||||||
|
ch.pipeline().addLast("compressor", new HttpContentCompressor());
|
||||||
|
ch.pipeline().addLast("decompressor", new HttpContentDecompressor());
|
||||||
|
// high maximum contentLength so that grafana snapshots can be delivered
|
||||||
|
// might not be necessary if inbound chunking (while proxying) is handled
|
||||||
|
ch.pipeline().addLast("aggregator", new HttpObjectAggregator(2097152));
|
||||||
|
ch.pipeline().addLast("chunker", new ChunkedWriteHandler());
|
||||||
|
ch.pipeline().addLast("grafanaDecoder",
|
||||||
|
new GrafanaRequestDecoder(config.getSecurity(), config.getHttp()));
|
||||||
|
ch.pipeline().addLast("fileServer", new HttpStaticFileServerHandler());
|
||||||
|
ch.pipeline().addLast("login", new X509LoginRequestHandler(config.getSecurity(), config.getHttp()));
|
||||||
|
ch.pipeline().addLast("httpRelay", new GrafanaRelayHandler(config, httpClientPool));
|
||||||
|
ch.pipeline().addLast("error", new TimelyExceptionHandler());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package timely.grafana.auth.configuration;
|
||||||
|
|
||||||
|
public class Grafana {
|
||||||
|
|
||||||
|
private String host = "localhost";
|
||||||
|
private Integer port = 3000;
|
||||||
|
private String protocol = "http";
|
||||||
|
|
||||||
|
public void setHost(String host) {
|
||||||
|
this.host = host;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHost() {
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPort(Integer port) {
|
||||||
|
this.port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getPort() {
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProtocol(String protocol) {
|
||||||
|
this.protocol = protocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProtocol() {
|
||||||
|
return protocol;
|
||||||
|
}
|
||||||
|
}
|
||||||
+56
@@ -0,0 +1,56 @@
|
|||||||
|
package timely.grafana.auth.configuration;
|
||||||
|
|
||||||
|
import javax.validation.Valid;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.boot.context.properties.NestedConfigurationProperty;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import timely.balancer.configuration.BalancerHttp;
|
||||||
|
import timely.balancer.configuration.BalancerSecurity;
|
||||||
|
|
||||||
|
@Validated
|
||||||
|
@Component
|
||||||
|
@ConfigurationProperties(prefix = "grafana-auth")
|
||||||
|
public class GrafanaAuthConfiguration {
|
||||||
|
|
||||||
|
@Valid
|
||||||
|
@NestedConfigurationProperty
|
||||||
|
private BalancerHttp http = new BalancerHttp();
|
||||||
|
|
||||||
|
@Valid
|
||||||
|
@NestedConfigurationProperty
|
||||||
|
private BalancerSecurity security = new BalancerSecurity();
|
||||||
|
|
||||||
|
@Valid
|
||||||
|
@NestedConfigurationProperty
|
||||||
|
private Grafana grafana = new Grafana();
|
||||||
|
|
||||||
|
private Integer shutdownQuietPeriod = 5;
|
||||||
|
|
||||||
|
public BalancerSecurity getSecurity() {
|
||||||
|
return security;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BalancerHttp getHttp() {
|
||||||
|
return http;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Grafana getGrafana() {
|
||||||
|
return grafana;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Time to wait (in seconds) for connections to finish and to make sure no new
|
||||||
|
* connections happen before shutting down Netty event loop groups.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public int getShutdownQuietPeriod() {
|
||||||
|
return this.shutdownQuietPeriod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShutdownQuietPeriod(Integer quietPeriod) {
|
||||||
|
this.shutdownQuietPeriod = quietPeriod;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package timely.grafana.auth.configuration;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
@SpringBootApplication(scanBasePackageClasses = { SpringBootstrap.class })
|
||||||
|
public class SpringBootstrap {
|
||||||
|
}
|
||||||
@@ -0,0 +1,234 @@
|
|||||||
|
package timely.grafana.auth.netty.http;
|
||||||
|
|
||||||
|
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.google.common.collect.Multimap;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
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.HttpMethod;
|
||||||
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
|
import io.netty.handler.codec.http.HttpVersion;
|
||||||
|
import io.netty.util.ReferenceCountUtil;
|
||||||
|
import org.apache.http.Header;
|
||||||
|
import org.apache.http.HttpEntity;
|
||||||
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpDelete;
|
||||||
|
import org.apache.http.client.methods.HttpGet;
|
||||||
|
import org.apache.http.client.methods.HttpHead;
|
||||||
|
import org.apache.http.client.methods.HttpOptions;
|
||||||
|
import org.apache.http.client.methods.HttpPost;
|
||||||
|
import org.apache.http.client.methods.HttpPut;
|
||||||
|
import org.apache.http.entity.StringEntity;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.apache.http.message.BasicHeader;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.util.StreamUtils;
|
||||||
|
import timely.api.request.timeseries.HttpRequest;
|
||||||
|
import timely.api.response.TimelyException;
|
||||||
|
import timely.auth.TimelyPrincipal;
|
||||||
|
import timely.auth.TimelyUser;
|
||||||
|
import timely.auth.util.DnUtils;
|
||||||
|
import timely.auth.util.HttpHeaderUtils;
|
||||||
|
import timely.balancer.connection.TimelyBalancedHost;
|
||||||
|
import timely.balancer.connection.http.HttpClientPool;
|
||||||
|
import timely.grafana.auth.configuration.GrafanaAuthConfiguration;
|
||||||
|
import timely.grafana.request.GrafanaRequest;
|
||||||
|
import timely.netty.http.TimelyHttpHandler;
|
||||||
|
import timely.netty.http.auth.TimelyAuthenticationToken;
|
||||||
|
|
||||||
|
public class GrafanaRelayHandler extends SimpleChannelInboundHandler<HttpRequest> implements TimelyHttpHandler {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(GrafanaRelayHandler.class);
|
||||||
|
private final HttpClientPool httpClientPool;
|
||||||
|
private GrafanaAuthConfiguration config;
|
||||||
|
|
||||||
|
public GrafanaRelayHandler(GrafanaAuthConfiguration config, HttpClientPool httpClientPool) {
|
||||||
|
this.config = config;
|
||||||
|
this.httpClientPool = httpClientPool;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void channelRead0(ChannelHandlerContext ctx, HttpRequest msg) throws Exception {
|
||||||
|
|
||||||
|
CloseableHttpClient client = null;
|
||||||
|
CloseableHttpResponse relayedResponse = null;
|
||||||
|
FullHttpRequest request = null;
|
||||||
|
GrafanaRequest grafanaRequest;
|
||||||
|
TimelyBalancedHost k = null;
|
||||||
|
String relayURI = null;
|
||||||
|
try {
|
||||||
|
try {
|
||||||
|
request = msg.getHttpRequest();
|
||||||
|
String originalURI = request.getUri();
|
||||||
|
originalURI = encodeURI(originalURI);
|
||||||
|
|
||||||
|
if (msg instanceof GrafanaRequest) {
|
||||||
|
grafanaRequest = (GrafanaRequest) msg;
|
||||||
|
} else {
|
||||||
|
throw new UnsupportedOperationException("Request not supported");
|
||||||
|
}
|
||||||
|
TimelyAuthenticationToken token = grafanaRequest.getToken();
|
||||||
|
if (token == null) {
|
||||||
|
throw new IllegalStateException("No token on grafana request");
|
||||||
|
}
|
||||||
|
|
||||||
|
String protocol = config.getGrafana().getProtocol();
|
||||||
|
String host = config.getGrafana().getHost();
|
||||||
|
Integer port = config.getGrafana().getPort();
|
||||||
|
k = TimelyBalancedHost.of(host, -1, port, -1, -1);
|
||||||
|
client = httpClientPool.borrowObject(k);
|
||||||
|
Multimap<String, String> headers = HttpHeaderUtils.toMultimap(request.headers());
|
||||||
|
|
||||||
|
// If incoming user sets these headers, do not relay them
|
||||||
|
headers.removeAll("X-WEBAUTH-USER");
|
||||||
|
headers.removeAll("X-WEBAUTH-NAME");
|
||||||
|
TimelyPrincipal principal = token.getTimelyPrincipal();
|
||||||
|
TimelyUser user = principal.getPrimaryUser();
|
||||||
|
String subjectDn = user.getDn().subjectDN();
|
||||||
|
String userName = DnUtils.getCommonName(subjectDn);
|
||||||
|
String loginName = DnUtils.getShortName(subjectDn);
|
||||||
|
headers.put("X-WEBAUTH-USER", loginName);
|
||||||
|
headers.put("X-WEBAUTH-NAME", userName);
|
||||||
|
List<Header> relayedHeaderList = new ArrayList<>();
|
||||||
|
for (Map.Entry<String, String> h : headers.entries()) {
|
||||||
|
if (!h.getKey().equals(CONTENT_LENGTH)) {
|
||||||
|
relayedHeaderList.add(new BasicHeader(h.getKey(), h.getValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Header[] headerArray = new Header[relayedHeaderList.size()];
|
||||||
|
relayedHeaderList.toArray(headerArray);
|
||||||
|
relayURI = protocol + "://" + host + ":" + port + originalURI;
|
||||||
|
if (request.getMethod().equals(HttpMethod.HEAD)) {
|
||||||
|
HttpHead relayedRequest = new HttpHead(relayURI);
|
||||||
|
if (headerArray.length > 0) {
|
||||||
|
relayedRequest.setHeaders(headerArray);
|
||||||
|
}
|
||||||
|
relayedResponse = client.execute(relayedRequest);
|
||||||
|
} else if (request.getMethod().equals(HttpMethod.GET)) {
|
||||||
|
HttpGet relayedRequest = new HttpGet(relayURI);
|
||||||
|
if (headerArray.length > 0) {
|
||||||
|
relayedRequest.setHeaders(headerArray);
|
||||||
|
}
|
||||||
|
relayedResponse = client.execute(relayedRequest);
|
||||||
|
} else if (request.getMethod().equals(HttpMethod.POST)) {
|
||||||
|
HttpPost relayedRequest = new HttpPost(relayURI);
|
||||||
|
String content = request.content().toString(StandardCharsets.UTF_8);
|
||||||
|
StringEntity entity = new StringEntity(content);
|
||||||
|
relayedRequest.setEntity(entity);
|
||||||
|
if (headerArray.length > 0) {
|
||||||
|
relayedRequest.setHeaders(headerArray);
|
||||||
|
}
|
||||||
|
relayedResponse = client.execute(relayedRequest);
|
||||||
|
} else if (request.getMethod().equals(HttpMethod.PUT)) {
|
||||||
|
HttpPut relayedRequest = new HttpPut(relayURI);
|
||||||
|
String content = request.content().toString(StandardCharsets.UTF_8);
|
||||||
|
StringEntity entity = new StringEntity(content);
|
||||||
|
relayedRequest.setEntity(entity);
|
||||||
|
if (headerArray.length > 0) {
|
||||||
|
relayedRequest.setHeaders(headerArray);
|
||||||
|
}
|
||||||
|
relayedResponse = client.execute(relayedRequest);
|
||||||
|
} else if (request.getMethod().equals(HttpMethod.DELETE)) {
|
||||||
|
HttpDelete relayedRequest = new HttpDelete(relayURI);
|
||||||
|
if (headerArray.length > 0) {
|
||||||
|
relayedRequest.setHeaders(headerArray);
|
||||||
|
}
|
||||||
|
relayedResponse = client.execute(relayedRequest);
|
||||||
|
} else if (request.getMethod().equals(HttpMethod.OPTIONS)) {
|
||||||
|
HttpOptions relayedRequest = new HttpOptions(relayURI);
|
||||||
|
if (headerArray.length > 0) {
|
||||||
|
relayedRequest.setHeaders(headerArray);
|
||||||
|
}
|
||||||
|
relayedResponse = client.execute(relayedRequest);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
String message = e.getMessage();
|
||||||
|
if (message == null) {
|
||||||
|
LOG.error("", e);
|
||||||
|
} 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 && request instanceof AutoCloseable) {
|
||||||
|
ReferenceCountUtil.release(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FullHttpResponse response = null;
|
||||||
|
if (relayedResponse == null) {
|
||||||
|
this.sendHttpError(ctx, new TimelyException(HttpResponseStatus.METHOD_NOT_ALLOWED.code(),
|
||||||
|
"Method not allowed", "Method not allowed"));
|
||||||
|
} else {
|
||||||
|
LOG.debug("RequestURL:{} ResponseCode:{}", relayURI, relayedResponse.getStatusLine().getStatusCode());
|
||||||
|
HttpEntity httpEntity = relayedResponse.getEntity();
|
||||||
|
if (httpEntity == null) {
|
||||||
|
response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
|
||||||
|
HttpResponseStatus.valueOf(relayedResponse.getStatusLine().getStatusCode()));
|
||||||
|
} else {
|
||||||
|
ByteArrayOutputStream baos = new MyByteArrayOutputStream();
|
||||||
|
StreamUtils.copy(httpEntity.getContent(), baos);
|
||||||
|
response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
|
||||||
|
HttpResponseStatus.valueOf(relayedResponse.getStatusLine().getStatusCode()),
|
||||||
|
Unpooled.copiedBuffer(baos.toByteArray()));
|
||||||
|
response.headers().set(CONTENT_LENGTH, response.content().readableBytes());
|
||||||
|
}
|
||||||
|
for (Header h : relayedResponse.getAllHeaders()) {
|
||||||
|
response.headers().add(h.getName(), h.getValue());
|
||||||
|
}
|
||||||
|
sendResponse(ctx, response);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (relayedResponse != null) {
|
||||||
|
relayedResponse.close();
|
||||||
|
}
|
||||||
|
if (client != null && k != null) {
|
||||||
|
httpClientPool.returnObject(k, client);
|
||||||
|
} else {
|
||||||
|
LOG.error("NOT RETURNING CONNECTION! " + msg.getHttpRequest().getUri());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static public class MyByteArrayOutputStream extends ByteArrayOutputStream {
|
||||||
|
|
||||||
|
public MyByteArrayOutputStream() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public MyByteArrayOutputStream(int size) {
|
||||||
|
super(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCount() {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getBuf() {
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static public String encodeURI(String s) {
|
||||||
|
try {
|
||||||
|
s = s.replaceAll("\\{", URLEncoder.encode("{", "UTF-8"));
|
||||||
|
s = s.replaceAll("\\}", URLEncoder.encode("}", "UTF-8"));
|
||||||
|
} catch (Exception e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
package timely.grafana.auth.netty.http;
|
||||||
|
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.google.common.collect.Multimap;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.handler.codec.MessageToMessageDecoder;
|
||||||
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
|
import io.netty.handler.codec.http.HttpHeaders.Names;
|
||||||
|
import io.netty.handler.codec.http.QueryStringDecoder;
|
||||||
|
import io.netty.handler.codec.http.cookie.ServerCookieDecoder;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import timely.api.request.AuthenticatedRequest;
|
||||||
|
import timely.api.request.timeseries.HttpRequest;
|
||||||
|
import timely.api.response.StrictTransportResponse;
|
||||||
|
import timely.api.response.TimelyException;
|
||||||
|
import timely.auth.AuthCache;
|
||||||
|
import timely.auth.AuthenticationService;
|
||||||
|
import timely.auth.SubjectIssuerDNPair;
|
||||||
|
import timely.auth.TimelyPrincipal;
|
||||||
|
import timely.auth.util.HttpHeaderUtils;
|
||||||
|
import timely.configuration.Http;
|
||||||
|
import timely.configuration.Security;
|
||||||
|
import timely.grafana.request.GrafanaRequest;
|
||||||
|
import timely.netty.Constants;
|
||||||
|
import timely.netty.http.TimelyHttpHandler;
|
||||||
|
import timely.netty.http.auth.TimelyAuthenticationToken;
|
||||||
|
|
||||||
|
public class GrafanaRequestDecoder extends MessageToMessageDecoder<FullHttpRequest> implements TimelyHttpHandler {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(GrafanaRequestDecoder.class);
|
||||||
|
private static final String LOG_RECEIVED_REQUEST = "Received HTTP request {}";
|
||||||
|
private static final String LOG_PARSED_REQUEST = "Parsed request {}";
|
||||||
|
|
||||||
|
private final Security security;
|
||||||
|
private final String nonSecureRedirectAddress;
|
||||||
|
|
||||||
|
public GrafanaRequestDecoder(Security security, Http http) {
|
||||||
|
|
||||||
|
this.security = security;
|
||||||
|
this.nonSecureRedirectAddress = http.getRedirectPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getSessionId(FullHttpRequest msg) {
|
||||||
|
Multimap<String, String> headers = HttpHeaderUtils.toMultimap(msg.headers());
|
||||||
|
Collection<String> cookies = headers.get(Names.COOKIE);
|
||||||
|
final StringBuilder buf = new StringBuilder();
|
||||||
|
cookies.forEach(h -> {
|
||||||
|
ServerCookieDecoder.STRICT.decode(h).forEach(c -> {
|
||||||
|
if (c.name().equals(Constants.COOKIE_NAME)) {
|
||||||
|
if (buf.length() == 0) {
|
||||||
|
buf.append(c.value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if (buf.length() == 0) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void decode(ChannelHandlerContext ctx, FullHttpRequest msg, List<Object> out) throws Exception {
|
||||||
|
|
||||||
|
LOG.trace(LOG_RECEIVED_REQUEST, msg);
|
||||||
|
|
||||||
|
final String uri = msg.getUri();
|
||||||
|
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);
|
||||||
|
|
||||||
|
HttpRequest request;
|
||||||
|
try {
|
||||||
|
request = new GrafanaRequest();
|
||||||
|
Multimap<String, String> headers = HttpHeaderUtils.toMultimap(msg.headers());
|
||||||
|
((AuthenticatedRequest) request).addHeaders(headers);
|
||||||
|
X509Certificate clientCert = AuthenticationService.getClientCertificate(ctx);
|
||||||
|
TimelyAuthenticationToken token;
|
||||||
|
if (StringUtils.isNotBlank(sessionId)) {
|
||||||
|
TimelyPrincipal principal;
|
||||||
|
((AuthenticatedRequest) request).setSessionId(sessionId);
|
||||||
|
if (AuthCache.containsKey(sessionId)) {
|
||||||
|
principal = AuthCache.get(sessionId);
|
||||||
|
} else {
|
||||||
|
principal = TimelyPrincipal.anonymousPrincipal();
|
||||||
|
}
|
||||||
|
SubjectIssuerDNPair dn = principal.getPrimaryUser().getDn();
|
||||||
|
token = AuthenticationService.getAuthenticationToken(null, dn.subjectDN(), dn.issuerDN());
|
||||||
|
} else if (clientCert != null) {
|
||||||
|
token = AuthenticationService.getAuthenticationToken(clientCert, headers);
|
||||||
|
} else {
|
||||||
|
SubjectIssuerDNPair dn = TimelyPrincipal.anonymousPrincipal().getPrimaryUser().getDn();
|
||||||
|
token = AuthenticationService.getAuthenticationToken(null, dn.subjectDN(), dn.issuerDN());
|
||||||
|
}
|
||||||
|
((AuthenticatedRequest) request).setToken(token);
|
||||||
|
request.setHttpRequest(msg.copy());
|
||||||
|
LOG.trace(LOG_PARSED_REQUEST, request);
|
||||||
|
request.validate();
|
||||||
|
out.add(request);
|
||||||
|
} catch (UnsupportedOperationException | NullPointerException e) {
|
||||||
|
// Return the original http request to route to the static file
|
||||||
|
// server
|
||||||
|
msg.retain();
|
||||||
|
out.add(msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
try {
|
||||||
|
AuthenticationService.enforceAccess((AuthenticatedRequest) request);
|
||||||
|
} catch (TimelyException e) {
|
||||||
|
if (!security.isAllowAnonymousHttpAccess()) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
out.clear();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package timely.grafana.request;
|
||||||
|
|
||||||
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
|
import io.netty.handler.codec.http.QueryStringDecoder;
|
||||||
|
import timely.api.annotation.Http;
|
||||||
|
import timely.api.request.AuthenticatedRequest;
|
||||||
|
import timely.api.request.HttpGetRequest;
|
||||||
|
import timely.api.request.HttpPostRequest;
|
||||||
|
|
||||||
|
@Http(path = "/")
|
||||||
|
public class GrafanaRequest extends AuthenticatedRequest implements HttpGetRequest, HttpPostRequest {
|
||||||
|
|
||||||
|
private FullHttpRequest httpRequest = null;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpGetRequest parseQueryParameters(QueryStringDecoder decoder) throws Exception {
|
||||||
|
return new GrafanaRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpPostRequest parseBody(String content) throws Exception {
|
||||||
|
return new GrafanaRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHttpRequest(FullHttpRequest httpRequest) {
|
||||||
|
this.httpRequest = httpRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FullHttpRequest getHttpRequest() {
|
||||||
|
return httpRequest;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<FindBugsFilter>
|
||||||
|
<Match>
|
||||||
|
<Class name="timely.grafana.auth.netty.http.GrafanaRelayHandler$MyByteArrayOutputStream" />
|
||||||
|
<Method name="getBuf" />
|
||||||
|
<Bug pattern="EI_EXPOSE_REP" />
|
||||||
|
</Match>
|
||||||
|
</FindBugsFilter>
|
||||||
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>timely</groupId>
|
<groupId>timely</groupId>
|
||||||
<artifactId>timely</artifactId>
|
<artifactId>timely</artifactId>
|
||||||
<version>1.1.1-SNAPSHOT</version>
|
<version>1.2.3</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>timely-app</artifactId>
|
<artifactId>timely-app</artifactId>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
|
|||||||
Vendored
+2
-2
@@ -23,8 +23,8 @@
|
|||||||
{"name": "Project site", "url": "https://github.com/NationalSecurityAgency/timely"},
|
{"name": "Project site", "url": "https://github.com/NationalSecurityAgency/timely"},
|
||||||
{"name": "License & Terms", "url": "https://raw.githubusercontent.com/NationalSecurityAgency/timely/master/LICENSE"}
|
{"name": "License & Terms", "url": "https://raw.githubusercontent.com/NationalSecurityAgency/timely/master/LICENSE"}
|
||||||
],
|
],
|
||||||
"version": "1.1.0",
|
"version": "1.2.3",
|
||||||
"updated": "2019-01-15"
|
"updated": "2019-04-18"
|
||||||
},
|
},
|
||||||
|
|
||||||
"includes": [
|
"includes": [
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "timely-app",
|
"name": "timely-app",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.1.0",
|
"version": "1.2.3",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -23,8 +23,8 @@
|
|||||||
{"name": "Project site", "url": "https://github.com/NationalSecurityAgency/timely"},
|
{"name": "Project site", "url": "https://github.com/NationalSecurityAgency/timely"},
|
||||||
{"name": "License & Terms", "url": "https://raw.githubusercontent.com/NationalSecurityAgency/timely/master/LICENSE"}
|
{"name": "License & Terms", "url": "https://raw.githubusercontent.com/NationalSecurityAgency/timely/master/LICENSE"}
|
||||||
],
|
],
|
||||||
"version": "1.1.0",
|
"version": "1.2.3",
|
||||||
"updated": "2019-01-15"
|
"updated": "2019-04-18"
|
||||||
},
|
},
|
||||||
|
|
||||||
"includes": [
|
"includes": [
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>timely</groupId>
|
<groupId>timely</groupId>
|
||||||
<artifactId>timely</artifactId>
|
<artifactId>timely</artifactId>
|
||||||
<version>1.1.1-SNAPSHOT</version>
|
<version>1.2.3</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
<name>Timely</name>
|
<name>Timely</name>
|
||||||
<description>A secure and scalable time series database based on Apache Accumulo</description>
|
<description>A secure and scalable time series database based on Apache Accumulo</description>
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
<module>grafana</module>
|
<module>grafana</module>
|
||||||
<module>analytics</module>
|
<module>analytics</module>
|
||||||
<module>balancer</module>
|
<module>balancer</module>
|
||||||
|
<module>grafana-auth</module>
|
||||||
</modules>
|
</modules>
|
||||||
<scm>
|
<scm>
|
||||||
<connection>scm:git:https://github.com/NationalSecurityAgency/timely.git</connection>
|
<connection>scm:git:https://github.com/NationalSecurityAgency/timely.git</connection>
|
||||||
@@ -42,31 +43,29 @@
|
|||||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
<version.accumulo>1.9.2</version.accumulo>
|
<version.accumulo>1.9.2</version.accumulo>
|
||||||
<version.buildnumber-maven>1.4</version.buildnumber-maven>
|
<version.buildnumber-maven>1.4</version.buildnumber-maven>
|
||||||
<version.caffeine>2.3.3</version.caffeine>
|
<version.caffeine>2.6.2</version.caffeine>
|
||||||
<version.collectd>5.7.1</version.collectd>
|
<version.collectd>5.7.1</version.collectd>
|
||||||
<version.commons-collections4>4.1</version.commons-collections4>
|
<version.commons-collections4>4.3</version.commons-collections4>
|
||||||
<version.commons-configuration>1.10</version.commons-configuration>
|
<version.commons-lang3>3.8.1</version.commons-lang3>
|
||||||
<version.commons-io>2.6</version.commons-io>
|
<version.commons-pool2>2.6.1</version.commons-pool2>
|
||||||
<version.commons-lang3>3.5</version.commons-lang3>
|
<version.curator>4.1.0</version.curator>
|
||||||
<version.commons-pool2>2.6.0</version.commons-pool2>
|
<version.disruptor>3.4.2</version.disruptor>
|
||||||
<version.curator>2.11.1</version.curator>
|
|
||||||
<version.disruptor>3.3.6</version.disruptor>
|
|
||||||
<version.eclipse-lifecycle>1.0.0</version.eclipse-lifecycle>
|
<version.eclipse-lifecycle>1.0.0</version.eclipse-lifecycle>
|
||||||
<version.fast-classpath-scanner>2.0.5</version.fast-classpath-scanner>
|
<version.fast-classpath-scanner>3.1.13</version.fast-classpath-scanner>
|
||||||
<version.flink>1.1.4</version.flink>
|
<version.flink>1.1.4</version.flink>
|
||||||
<version.google-flatbuffers>1.10.0</version.google-flatbuffers>
|
<version.google-flatbuffers>1.10.0</version.google-flatbuffers>
|
||||||
<version.guava>19.0</version.guava>
|
<version.guava>27.1-jre</version.guava>
|
||||||
<version.hadoop>2.7.3</version.hadoop>
|
<version.hadoop>2.7.3</version.hadoop>
|
||||||
<version.httpcomponents>4.5.2</version.httpcomponents>
|
<version.httpcomponents>4.5.6</version.httpcomponents>
|
||||||
<version.impsort>1.2.0</version.impsort>
|
<version.impsort>1.2.0</version.impsort>
|
||||||
<version.jackson>2.9.8</version.jackson>
|
<version.jackson>2.9.8</version.jackson>
|
||||||
<version.jacoco>0.7.6.201602180812</version.jacoco>
|
<version.jacoco>0.7.6.201602180812</version.jacoco>
|
||||||
<version.javacsv>2.0</version.javacsv>
|
<version.javacsv>2.0</version.javacsv>
|
||||||
<version.javassist>3.21.0-GA</version.javassist>
|
<version.javassist>3.24.1-GA</version.javassist>
|
||||||
<version.jcip-annotations>1.0</version.jcip-annotations>
|
<version.jcip-annotations>1.0</version.jcip-annotations>
|
||||||
<version.jsoup>1.9.2</version.jsoup>
|
<version.jsoup>1.11.3</version.jsoup>
|
||||||
<version.junit>4.12</version.junit>
|
<version.junit>4.12</version.junit>
|
||||||
<version.log4j>2.6.2</version.log4j>
|
<version.log4j>2.11.2</version.log4j>
|
||||||
<version.maven-assembly>2.6</version.maven-assembly>
|
<version.maven-assembly>2.6</version.maven-assembly>
|
||||||
<version.maven-build-helper>1.12</version.maven-build-helper>
|
<version.maven-build-helper>1.12</version.maven-build-helper>
|
||||||
<version.maven-compiler>3.3</version.maven-compiler>
|
<version.maven-compiler>3.3</version.maven-compiler>
|
||||||
@@ -76,14 +75,14 @@
|
|||||||
<version.maven-rpm>2.1.4</version.maven-rpm>
|
<version.maven-rpm>2.1.4</version.maven-rpm>
|
||||||
<version.maven-shade>2.4.2</version.maven-shade>
|
<version.maven-shade>2.4.2</version.maven-shade>
|
||||||
<version.maven-surefire>2.19.1</version.maven-surefire>
|
<version.maven-surefire>2.19.1</version.maven-surefire>
|
||||||
<version.netty>4.0.56.Final</version.netty>
|
<version.netty>4.1.35.Final</version.netty>
|
||||||
<version.netty-tcnative>2.0.7.Final</version.netty-tcnative>
|
<version.netty-tcnative>2.0.25.Final</version.netty-tcnative>
|
||||||
<version.os-maven>1.5.0.Final</version.os-maven>
|
<version.os-maven>1.5.0.Final</version.os-maven>
|
||||||
<version.powermock>1.6.4</version.powermock>
|
<version.powermock>1.6.4</version.powermock>
|
||||||
<version.slf4j>1.7.21</version.slf4j>
|
<version.slf4j>1.7.26</version.slf4j>
|
||||||
<version.spotbugs-maven>3.1.7</version.spotbugs-maven>
|
<version.spotbugs-maven>3.1.7</version.spotbugs-maven>
|
||||||
<version.spring-boot>1.4.1.RELEASE</version.spring-boot>
|
<version.spring-boot>2.1.3.RELEASE</version.spring-boot>
|
||||||
<version.tyrus>1.13</version.tyrus>
|
<version.tyrus>1.15</version.tyrus>
|
||||||
</properties>
|
</properties>
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@@ -140,7 +139,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.github.lukehutch</groupId>
|
<groupId>io.github.lukehutch</groupId>
|
||||||
<artifactId>fast-classpath-scanner</artifactId>
|
<artifactId>fast-classpath-scanner</artifactId>
|
||||||
<version>2.0.5</version>
|
<version>${version.fast-classpath-scanner}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.netty</groupId>
|
<groupId>io.netty</groupId>
|
||||||
@@ -156,6 +155,12 @@
|
|||||||
<groupId>org.apache.accumulo</groupId>
|
<groupId>org.apache.accumulo</groupId>
|
||||||
<artifactId>accumulo-core</artifactId>
|
<artifactId>accumulo-core</artifactId>
|
||||||
<version>${version.accumulo}</version>
|
<version>${version.accumulo}</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>io.netty</groupId>
|
||||||
|
<artifactId>netty</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.accumulo</groupId>
|
<groupId>org.apache.accumulo</groupId>
|
||||||
@@ -309,6 +314,11 @@
|
|||||||
<artifactId>collectd-plugin-parent</artifactId>
|
<artifactId>collectd-plugin-parent</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>timely</groupId>
|
||||||
|
<artifactId>timely-balancer</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>timely</groupId>
|
<groupId>timely</groupId>
|
||||||
<artifactId>timely-client</artifactId>
|
<artifactId>timely-client</artifactId>
|
||||||
@@ -446,7 +456,7 @@
|
|||||||
<artifactId>maven-failsafe-plugin</artifactId>
|
<artifactId>maven-failsafe-plugin</artifactId>
|
||||||
<version>${version.maven-surefire}</version>
|
<version>${version.maven-surefire}</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<argLine>${failsafeArgLine} -Djava.library.path=${project.build.directory}/native/META-INF/native/libnetty-tcnative.so -Dzookeeper.sasl.client=false</argLine>
|
<argLine>${failsafeArgLine} -Djava.library.path=${project.build.directory}/native/META-INF/native/libnetty_tcnative_linux_x86_64.so -Dzookeeper.sasl.client=false</argLine>
|
||||||
<includes>
|
<includes>
|
||||||
<include>**/*.java</include>
|
<include>**/*.java</include>
|
||||||
</includes>
|
</includes>
|
||||||
@@ -489,7 +499,7 @@
|
|||||||
<artifactId>maven-surefire-plugin</artifactId>
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
<version>${version.maven-surefire}</version>
|
<version>${version.maven-surefire}</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<argLine>${sureFireArgLine} -Djava.library.path=${project.build.directory}/native/META-INF/native/libnetty-tcnative.so -Dzookeeper.sasl.client=false</argLine>
|
<argLine>${sureFireArgLine} -Djava.library.path=${project.build.directory}/native/META-INF/native/libnetty-tcnative_linux_x86_64.so -Dzookeeper.sasl.client=false</argLine>
|
||||||
<excludedGroups>timely.test.IntegrationTest</excludedGroups>
|
<excludedGroups>timely.test.IntegrationTest</excludedGroups>
|
||||||
<redirectTestOutputToFile>true</redirectTestOutputToFile>
|
<redirectTestOutputToFile>true</redirectTestOutputToFile>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|||||||
@@ -27,15 +27,14 @@ fi
|
|||||||
mkdir -p ${NATIVE_DIR}
|
mkdir -p ${NATIVE_DIR}
|
||||||
|
|
||||||
pushd ${BASE_DIR}/bin
|
pushd ${BASE_DIR}/bin
|
||||||
$JAVA_HOME/bin/jar xf ${LIB_DIR}/netty-tcnative*.jar META-INF/native/libnetty_tcnative.${TCNATIVE_SUFFIX}
|
$JAVA_HOME/bin/jar xf ${LIB_DIR}/netty-tcnative-boringssl-static*.jar META-INF/native/libnetty_tcnative_linux_x86_64.${TCNATIVE_SUFFIX}
|
||||||
$JAVA_HOME/bin/jar xf ${LIB_DIR}/netty-transport-native-epoll*.jar META-INF/native/libnetty_transport_native_epoll_x86_64.${TCNATIVE_SUFFIX}
|
$JAVA_HOME/bin/jar xf ${LIB_DIR}/netty-all*.jar META-INF/native/libnetty_transport_native_epoll_x86_64.${TCNATIVE_SUFFIX}
|
||||||
popd
|
popd
|
||||||
|
|
||||||
export CLASSPATH="${CONF_DIR}:${LIB_DIR}/*"
|
export CLASSPATH="${CONF_DIR}:${LIB_DIR}/*:${HADOOP_CONF_DIR}"
|
||||||
JVM_ARGS="-Xmx256m -Xms256m -Dio.netty.eventLoopThreads=${NUM_SERVER_THREADS}"
|
JVM_ARGS="-Xmx256m -Xms256m -Dio.netty.eventLoopThreads=${NUM_SERVER_THREADS} -Dlog4j.configurationFile=${CONF_DIR}/log4j2-spring.xml"
|
||||||
JVM_ARGS="${JVM_ARGS} -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector"
|
JVM_ARGS="${JVM_ARGS} -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector"
|
||||||
JVM_ARGS="${JVM_ARGS} -Djava.library.path=${NATIVE_DIR}"
|
JVM_ARGS="${JVM_ARGS} -Djava.library.path=${NATIVE_DIR}"
|
||||||
JVM_ARGS="${JVM_ARGS} -Djava.library.path=${NATIVE_DIR}/libnetty-${TCNATIVE_SUFFIX}"
|
|
||||||
|
|
||||||
echo "$JAVA_HOME/bin/java ${JVM_ARGS} timely.Server --spring.config.name=timely"
|
echo "$JAVA_HOME/bin/java ${JVM_ARGS} timely.Server --spring.config.name=timely"
|
||||||
$JAVA_HOME/bin/java ${JVM_ARGS} timely.Server --spring.config.name=timely
|
$JAVA_HOME/bin/java ${JVM_ARGS} timely.Server --spring.config.name=timely
|
||||||
|
|||||||
@@ -2,17 +2,12 @@
|
|||||||
|
|
||||||
if [[ `uname` == "Darwin" ]]; then
|
if [[ `uname` == "Darwin" ]]; then
|
||||||
THIS_SCRIPT=`python -c 'import os,sys; print os.path.realpath(sys.argv[1])' $0`
|
THIS_SCRIPT=`python -c 'import os,sys; print os.path.realpath(sys.argv[1])' $0`
|
||||||
TCNATIVE_SUFFIX="tcnative.jnilib"
|
TCNATIVE_SUFFIX="jnilib"
|
||||||
else
|
else
|
||||||
THIS_SCRIPT=`readlink -f $0`
|
THIS_SCRIPT=`readlink -f $0`
|
||||||
TCNATIVE_SUFFIX="tcnative.so"
|
TCNATIVE_SUFFIX="so"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# netty tcnative file reference
|
|
||||||
#netty-tcnative-1.1.33.Fork18-linux-x86_64-fedora.jar -> libnetty-tcnative.so
|
|
||||||
#netty-tcnative-1.1.33.Fork18-linux-x86_64.jar -> libnetty-tcnative.so
|
|
||||||
#netty-tcnative-1.1.33.Fork18-osx-x86_64.jar -> libnetty-tcnative.jnilib
|
|
||||||
|
|
||||||
THIS_DIR="${THIS_SCRIPT%/*}"
|
THIS_DIR="${THIS_SCRIPT%/*}"
|
||||||
NATIVE_DIR="${THIS_DIR}/META-INF/native"
|
NATIVE_DIR="${THIS_DIR}/META-INF/native"
|
||||||
BASE_DIR=${THIS_DIR}/..
|
BASE_DIR=${THIS_DIR}/..
|
||||||
@@ -32,11 +27,12 @@ fi
|
|||||||
mkdir -p ${NATIVE_DIR}
|
mkdir -p ${NATIVE_DIR}
|
||||||
|
|
||||||
pushd ${BASE_DIR}/bin
|
pushd ${BASE_DIR}/bin
|
||||||
$JAVA_HOME/bin/jar xf ${LIB_DIR}/netty-tcnative*.jar META-INF/native/libnetty-${TCNATIVE_SUFFIX}
|
$JAVA_HOME/bin/jar xf ${LIB_DIR}/netty-tcnative-boringssl-static*.jar META-INF/native/libnetty_tcnative_linux_x86_64.${TCNATIVE_SUFFIX}
|
||||||
|
$JAVA_HOME/bin/jar xf ${LIB_DIR}/netty-all*.jar META-INF/native/libnetty_transport_native_epoll_x86_64.${TCNATIVE_SUFFIX}
|
||||||
popd
|
popd
|
||||||
|
|
||||||
export CLASSPATH="${CONF_DIR}:${LIB_DIR}/*"
|
export CLASSPATH="${CONF_DIR}:${LIB_DIR}/*:${HADOOP_CONF_DIR}"
|
||||||
JVM_ARGS="-Xmx256m -Xms256m -Dio.netty.eventLoopThreads=${NUM_SERVER_THREADS}"
|
JVM_ARGS="-Xmx256m -Xms256m -Dio.netty.eventLoopThreads=${NUM_SERVER_THREADS} -Dlog4j.configurationFile=${CONF_DIR}/log4j2-spring.xml"
|
||||||
JVM_ARGS="${JVM_ARGS} -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector"
|
JVM_ARGS="${JVM_ARGS} -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector"
|
||||||
JVM_ARGS="${JVM_ARGS} -Djava.library.path=${NATIVE_DIR}"
|
JVM_ARGS="${JVM_ARGS} -Djava.library.path=${NATIVE_DIR}"
|
||||||
# for debugging
|
# for debugging
|
||||||
|
|||||||
@@ -46,9 +46,12 @@
|
|||||||
<user-service id="usernamePasswordService">
|
<user-service id="usernamePasswordService">
|
||||||
<user name="test" password="test1" authorities="A,B,C" />
|
<user name="test" password="test1" authorities="A,B,C" />
|
||||||
</user-service>
|
</user-service>
|
||||||
|
|
||||||
<beans:bean id="UserNamePasswordAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
|
<beans:bean id="noOpPasswordEncoder" class="org.springframework.security.crypto.password.NoOpPasswordEncoder"/>
|
||||||
|
|
||||||
|
<beans:bean id="UserNamePasswordAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
|
||||||
<beans:property name="userDetailsService" ref="usernamePasswordService"/>
|
<beans:property name="userDetailsService" ref="usernamePasswordService"/>
|
||||||
|
<beans:property name="passwordEncoder" ref="noOpPasswordEncoder"/>
|
||||||
</beans:bean>
|
</beans:bean>
|
||||||
|
|
||||||
<authentication-manager alias="authenticationManager">
|
<authentication-manager alias="authenticationManager">
|
||||||
@@ -58,8 +61,11 @@
|
|||||||
<authentication-provider ref="UserNamePasswordAuthenticationProvider" />
|
<authentication-provider ref="UserNamePasswordAuthenticationProvider" />
|
||||||
</authentication-manager>
|
</authentication-manager>
|
||||||
|
|
||||||
<beans:bean id="requiredRoles" class="Array">
|
<util:list id="requiredRoles" value-type="java.lang.String">
|
||||||
|
<beans:value></beans:value>
|
||||||
</beans:bean>
|
</util:list>
|
||||||
|
|
||||||
|
<util:list id="requiredAuths" value-type="java.lang.String">
|
||||||
|
<beans:value></beans:value>
|
||||||
|
</util:list>
|
||||||
</beans:beans>
|
</beans:beans>
|
||||||
@@ -18,6 +18,8 @@ timely:
|
|||||||
max-downsample-memory: 10000000
|
max-downsample-memory: 10000000
|
||||||
security:
|
security:
|
||||||
session-max-age: 86400
|
session-max-age: 86400
|
||||||
|
cache-expiration-minutes: 60
|
||||||
|
cache-refresh-minutes: 5
|
||||||
allow-anonymous-http-access: true
|
allow-anonymous-http-access: true
|
||||||
allow-anonymous-ws-access: true
|
allow-anonymous-ws-access: true
|
||||||
ssl:
|
ssl:
|
||||||
@@ -50,9 +52,9 @@ timely:
|
|||||||
timeout: 60
|
timeout: 60
|
||||||
subscription-lag: 120
|
subscription-lag: 120
|
||||||
meta-cache:
|
meta-cache:
|
||||||
expiration-minutes: 60
|
expiration-minutes: 1440
|
||||||
initial-capacity: 2000
|
cache-refresh-minutes: 60
|
||||||
max-capacity: 10000
|
max-tag-values: 100
|
||||||
visibility-cache:
|
visibility-cache:
|
||||||
expiration-minutes: 60
|
expiration-minutes: 60
|
||||||
initial-capacity: 2000
|
initial-capacity: 2000
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ timely:
|
|||||||
max-downsample-memory: 10000000
|
max-downsample-memory: 10000000
|
||||||
security:
|
security:
|
||||||
session-max-age: 86400
|
session-max-age: 86400
|
||||||
|
cache-expiration-minutes: 60
|
||||||
|
cache-refresh-minutes: 5
|
||||||
allow-anonymous-http-access: true
|
allow-anonymous-http-access: true
|
||||||
allow-anonymous-ws-access: true
|
allow-anonymous-ws-access: true
|
||||||
ssl:
|
ssl:
|
||||||
@@ -54,9 +56,9 @@ timely:
|
|||||||
scanner-read-ahead: 1
|
scanner-read-ahead: 1
|
||||||
subscription-batch-size: 5000
|
subscription-batch-size: 5000
|
||||||
meta-cache:
|
meta-cache:
|
||||||
expiration-minutes: 60
|
expiration-minutes: 1440
|
||||||
initial-capacity: 2000
|
cache-refresh-minutes: 60
|
||||||
max-capacity: 10000
|
max-tag-values: 100
|
||||||
visibility-cache:
|
visibility-cache:
|
||||||
expiration-minutes: 60
|
expiration-minutes: 60
|
||||||
initial-capacity: 2000
|
initial-capacity: 2000
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ timely:
|
|||||||
age-off-hours:
|
age-off-hours:
|
||||||
default: 26
|
default: 26
|
||||||
metrics-report-ignored-tags:
|
metrics-report-ignored-tags:
|
||||||
|
defaultVisibility:
|
||||||
accumulo:
|
accumulo:
|
||||||
instance-name: accumulo
|
instance-name: accumulo
|
||||||
zookeepers: localhost:2181
|
zookeepers: localhost:2181
|
||||||
@@ -22,6 +23,8 @@ timely:
|
|||||||
max-downsample-memory: 10000000
|
max-downsample-memory: 10000000
|
||||||
security:
|
security:
|
||||||
session-max-age: 86400
|
session-max-age: 86400
|
||||||
|
cache-expiration-minutes: 60
|
||||||
|
cache-refresh-minutes: 5
|
||||||
allow-anonymous-http-access: true
|
allow-anonymous-http-access: true
|
||||||
allow-anonymous-ws-access: true
|
allow-anonymous-ws-access: true
|
||||||
serverSsl:
|
serverSsl:
|
||||||
@@ -59,10 +62,9 @@ timely:
|
|||||||
scanner-read-ahead: 1
|
scanner-read-ahead: 1
|
||||||
subscription-batch-size: 5000
|
subscription-batch-size: 5000
|
||||||
meta-cache:
|
meta-cache:
|
||||||
expiration-minutes: 60
|
expiration-minutes: 1440
|
||||||
initial-capacity: 2000
|
cache-refresh-minutes: 60
|
||||||
max-capacity: 10000
|
max-tag-values: 100
|
||||||
visibility-cache:
|
visibility-cache:
|
||||||
expiration-minutes: 60
|
expiration-minutes: 60
|
||||||
initial-capacity: 2000
|
|
||||||
max-capacity: 10000
|
|
||||||
+3
-38
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>timely</groupId>
|
<groupId>timely</groupId>
|
||||||
<artifactId>timely</artifactId>
|
<artifactId>timely</artifactId>
|
||||||
<version>1.1.1-SNAPSHOT</version>
|
<version>1.2.3</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>timely-server</artifactId>
|
<artifactId>timely-server</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@@ -149,9 +149,8 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.netty</groupId>
|
<groupId>io.netty</groupId>
|
||||||
<artifactId>netty-tcnative</artifactId>
|
<artifactId>netty-tcnative-boringssl-static</artifactId>
|
||||||
<version>${version.netty-tcnative}</version>
|
<version>${version.netty-tcnative}</version>
|
||||||
<classifier>${os.detected.classifier}</classifier>
|
|
||||||
<scope>runtime</scope>
|
<scope>runtime</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -240,9 +239,8 @@
|
|||||||
<artifactItems>
|
<artifactItems>
|
||||||
<artifactItem>
|
<artifactItem>
|
||||||
<groupId>io.netty</groupId>
|
<groupId>io.netty</groupId>
|
||||||
<artifactId>netty-tcnative</artifactId>
|
<artifactId>netty-tcnative-boringssl-static</artifactId>
|
||||||
<version>${version.netty-tcnative}</version>
|
<version>${version.netty-tcnative}</version>
|
||||||
<classifier>${os.detected.classifier}</classifier>
|
|
||||||
<type>jar</type>
|
<type>jar</type>
|
||||||
<overWrite>false</overWrite>
|
<overWrite>false</overWrite>
|
||||||
<outputDirectory>${project.build.directory}/native</outputDirectory>
|
<outputDirectory>${project.build.directory}/native</outputDirectory>
|
||||||
@@ -284,37 +282,4 @@
|
|||||||
</extension>
|
</extension>
|
||||||
</extensions>
|
</extensions>
|
||||||
</build>
|
</build>
|
||||||
<profiles>
|
|
||||||
<profile>
|
|
||||||
<id>native-epoll-linux</id>
|
|
||||||
<activation>
|
|
||||||
<os>
|
|
||||||
<family>!mac</family>
|
|
||||||
</os>
|
|
||||||
</activation>
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.netty</groupId>
|
|
||||||
<artifactId>netty-transport-native-epoll</artifactId>
|
|
||||||
<version>${version.netty}</version>
|
|
||||||
<classifier>linux-x86_64</classifier>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
</profile>
|
|
||||||
<profile>
|
|
||||||
<id>native-epoll</id>
|
|
||||||
<activation>
|
|
||||||
<os>
|
|
||||||
<family>mac</family>
|
|
||||||
</os>
|
|
||||||
</activation>
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.netty</groupId>
|
|
||||||
<artifactId>netty-transport-native-epoll</artifactId>
|
|
||||||
<version>${version.netty}</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
</profile>
|
|
||||||
</profiles>
|
|
||||||
</project>
|
</project>
|
||||||
@@ -70,6 +70,7 @@ import org.apache.zookeeper.CreateMode;
|
|||||||
import org.apache.zookeeper.data.Stat;
|
import org.apache.zookeeper.data.Stat;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.boot.WebApplicationType;
|
||||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||||
import org.springframework.context.ConfigurableApplicationContext;
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
import timely.api.response.TimelyException;
|
import timely.api.response.TimelyException;
|
||||||
@@ -250,7 +251,7 @@ public class Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected static Configuration initializeConfiguration(String[] args) {
|
protected static Configuration initializeConfiguration(String[] args) {
|
||||||
applicationContext = new SpringApplicationBuilder(SpringBootstrap.class).web(false).run(args);
|
applicationContext = new SpringApplicationBuilder(SpringBootstrap.class).web(WebApplicationType.NONE).run(args);
|
||||||
return applicationContext.getBean(Configuration.class);
|
return applicationContext.getBean(Configuration.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -430,7 +431,7 @@ public class Server {
|
|||||||
// Initialize the MetaCache
|
// Initialize the MetaCache
|
||||||
MetaCacheFactory.getCache(config);
|
MetaCacheFactory.getCache(config);
|
||||||
// initialize the auth cache
|
// initialize the auth cache
|
||||||
AuthCache.setSessionMaxAge(config.getSecurity());
|
AuthCache.configure(config.getSecurity());
|
||||||
// Initialize the VisibilityCache
|
// Initialize the VisibilityCache
|
||||||
VisibilityCache.init(config);
|
VisibilityCache.init(config);
|
||||||
final boolean useEpoll = useEpoll();
|
final boolean useEpoll = useEpoll();
|
||||||
@@ -587,7 +588,7 @@ public class Server {
|
|||||||
ch.pipeline().addLast("decoder", new HttpRequestDecoder());
|
ch.pipeline().addLast("decoder", new HttpRequestDecoder());
|
||||||
ch.pipeline().addLast("compressor", new HttpContentCompressor());
|
ch.pipeline().addLast("compressor", new HttpContentCompressor());
|
||||||
ch.pipeline().addLast("decompressor", new HttpContentDecompressor());
|
ch.pipeline().addLast("decompressor", new HttpContentDecompressor());
|
||||||
ch.pipeline().addLast("aggregator", new HttpObjectAggregator(8192));
|
ch.pipeline().addLast("aggregator", new HttpObjectAggregator(65536));
|
||||||
ch.pipeline().addLast("chunker", new ChunkedWriteHandler());
|
ch.pipeline().addLast("chunker", new ChunkedWriteHandler());
|
||||||
final Cors corsCfg = config.getHttp().getCors();
|
final Cors corsCfg = config.getHttp().getCors();
|
||||||
final CorsConfig.Builder ccb;
|
final CorsConfig.Builder ccb;
|
||||||
@@ -635,7 +636,7 @@ public class Server {
|
|||||||
ch.pipeline().addLast("logger", new LoggingHandler());
|
ch.pipeline().addLast("logger", new LoggingHandler());
|
||||||
ch.pipeline().addLast("packetDecoder", new UdpPacketToByteBuf());
|
ch.pipeline().addLast("packetDecoder", new UdpPacketToByteBuf());
|
||||||
ch.pipeline().addLast("buffer", new MetricsBufferDecoder());
|
ch.pipeline().addLast("buffer", new MetricsBufferDecoder());
|
||||||
ch.pipeline().addLast("frame", new DelimiterBasedFrameDecoder(8192, true, Delimiters.lineDelimiter()));
|
ch.pipeline().addLast("frame", new DelimiterBasedFrameDecoder(65536, true, Delimiters.lineDelimiter()));
|
||||||
ch.pipeline().addLast("putDecoder", new UdpDecoder());
|
ch.pipeline().addLast("putDecoder", new UdpDecoder());
|
||||||
ch.pipeline().addLast("putHandler", new TcpPutHandler(dataStore));
|
ch.pipeline().addLast("putHandler", new TcpPutHandler(dataStore));
|
||||||
}
|
}
|
||||||
@@ -648,7 +649,7 @@ public class Server {
|
|||||||
@Override
|
@Override
|
||||||
protected void initChannel(SocketChannel ch) throws Exception {
|
protected void initChannel(SocketChannel ch) throws Exception {
|
||||||
ch.pipeline().addLast("buffer", new MetricsBufferDecoder());
|
ch.pipeline().addLast("buffer", new MetricsBufferDecoder());
|
||||||
ch.pipeline().addLast("frame", new DelimiterBasedFrameDecoder(8192, true, Delimiters.lineDelimiter()));
|
ch.pipeline().addLast("frame", new DelimiterBasedFrameDecoder(65536, true, Delimiters.lineDelimiter()));
|
||||||
ch.pipeline().addLast("putDecoder", new TcpDecoder());
|
ch.pipeline().addLast("putDecoder", new TcpDecoder());
|
||||||
ch.pipeline().addLast("putHandler", new TcpPutHandler(dataStore));
|
ch.pipeline().addLast("putHandler", new TcpPutHandler(dataStore));
|
||||||
ch.pipeline().addLast("versionHandler", new TcpVersionHandler());
|
ch.pipeline().addLast("versionHandler", new TcpVersionHandler());
|
||||||
@@ -663,7 +664,7 @@ public class Server {
|
|||||||
protected void initChannel(SocketChannel ch) throws Exception {
|
protected void initChannel(SocketChannel ch) throws Exception {
|
||||||
ch.pipeline().addLast("ssl", sslCtx.newHandler(ch.alloc()));
|
ch.pipeline().addLast("ssl", sslCtx.newHandler(ch.alloc()));
|
||||||
ch.pipeline().addLast("httpServer", new HttpServerCodec());
|
ch.pipeline().addLast("httpServer", new HttpServerCodec());
|
||||||
ch.pipeline().addLast("aggregator", new HttpObjectAggregator(8192));
|
ch.pipeline().addLast("aggregator", new HttpObjectAggregator(65536));
|
||||||
ch.pipeline().addLast("sessionExtractor", new WebSocketFullRequestHandler());
|
ch.pipeline().addLast("sessionExtractor", new WebSocketFullRequestHandler());
|
||||||
ch.pipeline().addLast("idle-handler", new IdleStateHandler(conf.getWebsocket().getTimeout(), 0, 0));
|
ch.pipeline().addLast("idle-handler", new IdleStateHandler(conf.getWebsocket().getTimeout(), 0, 0));
|
||||||
ch.pipeline().addLast("ws-protocol",
|
ch.pipeline().addLast("ws-protocol",
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package timely.adapter.accumulo;
|
||||||
|
|
||||||
|
import org.apache.accumulo.core.client.lexicoder.LongLexicoder;
|
||||||
|
import org.apache.accumulo.core.client.lexicoder.PairLexicoder;
|
||||||
|
import org.apache.accumulo.core.client.lexicoder.StringLexicoder;
|
||||||
|
import org.apache.accumulo.core.data.Key;
|
||||||
|
import timely.api.model.Meta;
|
||||||
|
import timely.model.Metric;
|
||||||
|
import timely.model.parse.TagListParser;
|
||||||
|
import timely.model.parse.TagParser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates Accumulo data structures from {@link Metric}
|
||||||
|
*/
|
||||||
|
public class MetaAdapter {
|
||||||
|
|
||||||
|
private static final PairLexicoder<String, Long> rowCoder = new PairLexicoder<>(new StringLexicoder(),
|
||||||
|
new LongLexicoder());
|
||||||
|
|
||||||
|
private static final PairLexicoder<Long, String> colQualCoder = new PairLexicoder<>(new LongLexicoder(),
|
||||||
|
new StringLexicoder());
|
||||||
|
|
||||||
|
private static final TagParser tagParser = new TagParser();
|
||||||
|
private static final TagListParser tagListParser = new TagListParser();
|
||||||
|
|
||||||
|
public static Key createMetricKey(String metricName, Long timestamp) {
|
||||||
|
return new Key(Meta.METRIC_PREFIX + metricName, "", "", timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Key createTagKey(String metricName, String tagName, Long timestamp) {
|
||||||
|
return new Key(Meta.TAG_PREFIX + metricName, tagName, "", timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Key createValueKey(String metricName, String tagName, String tagValue, Long timestamp) {
|
||||||
|
return new Key(Meta.VALUE_PREFIX + metricName, tagName, tagValue, timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Meta parse(Key key) {
|
||||||
|
String row = key.getRow().toString();
|
||||||
|
if (row.startsWith(Meta.METRIC_PREFIX)) {
|
||||||
|
return Meta.parse(key, null, Meta.METRIC_PREFIX);
|
||||||
|
} else if (row.startsWith(Meta.TAG_PREFIX)) {
|
||||||
|
return Meta.parse(key, null, Meta.TAG_PREFIX);
|
||||||
|
} else if (row.startsWith(Meta.VALUE_PREFIX)) {
|
||||||
|
return Meta.parse(key, null, Meta.VALUE_PREFIX);
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("Invalid key in meta " + key.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String decodeRowKey(Key key) {
|
||||||
|
return parse(key).getMetric();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -55,16 +55,43 @@ public class Meta implements Comparable<Meta> {
|
|||||||
return keys;
|
return keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Meta parse(Key k, Value v) {
|
public static String prefix(Key key) {
|
||||||
|
String row = key.getRow().toString();
|
||||||
|
if (row.startsWith(Meta.METRIC_PREFIX)) {
|
||||||
|
return Meta.METRIC_PREFIX;
|
||||||
|
} else if (row.startsWith(Meta.TAG_PREFIX)) {
|
||||||
|
return Meta.TAG_PREFIX;
|
||||||
|
} else if (row.startsWith(Meta.VALUE_PREFIX)) {
|
||||||
|
return Meta.VALUE_PREFIX;
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("Invalid key in meta " + key.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Meta parse(Key key) {
|
||||||
|
return parse(key, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Meta parse(Key key, Value value) {
|
||||||
|
String row = key.getRow().toString();
|
||||||
|
if (row.startsWith(Meta.METRIC_PREFIX)) {
|
||||||
|
return Meta.parse(key, value, Meta.METRIC_PREFIX);
|
||||||
|
} else if (row.startsWith(Meta.TAG_PREFIX)) {
|
||||||
|
return Meta.parse(key, value, Meta.TAG_PREFIX);
|
||||||
|
} else if (row.startsWith(Meta.VALUE_PREFIX)) {
|
||||||
|
return Meta.parse(key, value, Meta.VALUE_PREFIX);
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("Invalid key in meta " + key.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Meta parse(Key k, Value v, String prefix) {
|
||||||
if (k.getColumnQualifier().getLength() > 0) {
|
if (k.getColumnQualifier().getLength() > 0) {
|
||||||
return new Meta(k.getRow().toString().substring(METRIC_PREFIX.length()), k.getColumnFamily().toString(),
|
return new Meta(k.getRow().toString().substring(prefix.length()), k.getColumnFamily().toString(),
|
||||||
k.getColumnQualifier().toString());
|
k.getColumnQualifier().toString());
|
||||||
} else if (k.getColumnFamily().getLength() > 0) {
|
} else if (k.getColumnFamily().getLength() > 0) {
|
||||||
return new Meta(k.getRow().toString().substring(METRIC_PREFIX.length()), k.getColumnFamily().toString(),
|
return new Meta(k.getRow().toString().substring(prefix.length()), k.getColumnFamily().toString(), null);
|
||||||
null);
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return new Meta(k.getRow().toString().substring(METRIC_PREFIX.length()), null, null);
|
return new Meta(k.getRow().toString().substring(prefix.length()), null, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import timely.auth.util.HttpHeaderUtils;
|
|||||||
import timely.netty.http.auth.TimelyAuthenticationToken;
|
import timely.netty.http.auth.TimelyAuthenticationToken;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for requests that require authentication
|
* Base class for requests that require auth
|
||||||
*/
|
*/
|
||||||
@JsonIgnoreProperties({ "userName", "requestHeader" })
|
@JsonIgnoreProperties({ "userName", "requestHeader" })
|
||||||
public class AuthenticatedRequest implements Request {
|
public class AuthenticatedRequest implements Request {
|
||||||
|
|||||||
@@ -23,32 +23,67 @@ public class AuthCache {
|
|||||||
private static final Logger LOG = LoggerFactory.getLogger(AuthCache.class);
|
private static final Logger LOG = LoggerFactory.getLogger(AuthCache.class);
|
||||||
private static Cache<String, TimelyPrincipal> CACHE = null;
|
private static Cache<String, TimelyPrincipal> CACHE = null;
|
||||||
|
|
||||||
private static int sessionMaxAge = -1;
|
private static int cacheExpirationMinutes = -1;
|
||||||
|
private static int cacheRefreshMinutes = -1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For tests only
|
* For tests only
|
||||||
*/
|
*/
|
||||||
public static void resetSessionMaxAge() {
|
public static void resetConfiguration() {
|
||||||
sessionMaxAge = -1;
|
cacheExpirationMinutes = -1;
|
||||||
|
cacheRefreshMinutes = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setSessionMaxAge(Security security) {
|
public static void configure(Security security) {
|
||||||
if (-1 != sessionMaxAge) {
|
if (-1 != cacheExpirationMinutes || -1 != cacheRefreshMinutes) {
|
||||||
throw new IllegalStateException("Cache session max age already configured.");
|
throw new IllegalStateException("Cache already configured.");
|
||||||
}
|
}
|
||||||
sessionMaxAge = security.getSessionMaxAge();
|
cacheExpirationMinutes = security.getCacheExpirationMinutes();
|
||||||
|
cacheRefreshMinutes = security.getCacheRefreshMinutes();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Cache<String, TimelyPrincipal> getCache() {
|
private static Cache<String, TimelyPrincipal> getCache() {
|
||||||
if (-1 == sessionMaxAge) {
|
if (-1 == cacheExpirationMinutes) {
|
||||||
throw new IllegalStateException("Cache session max age not configured.");
|
throw new IllegalStateException("Cache session max age not configured.");
|
||||||
}
|
}
|
||||||
if (null == CACHE) {
|
if (null == CACHE) {
|
||||||
CACHE = Caffeine.newBuilder().expireAfterAccess(sessionMaxAge, TimeUnit.SECONDS).build();
|
Caffeine<Object, Object> caffeine = Caffeine.newBuilder();
|
||||||
|
caffeine.expireAfterWrite(cacheExpirationMinutes, TimeUnit.MINUTES);
|
||||||
|
if (cacheRefreshMinutes > 0) {
|
||||||
|
caffeine.refreshAfterWrite(cacheRefreshMinutes, TimeUnit.MINUTES);
|
||||||
|
CACHE = caffeine.build(key -> getTimelyPrincipal(key));
|
||||||
|
} else {
|
||||||
|
CACHE = caffeine.build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return CACHE;
|
return CACHE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static TimelyPrincipal getTimelyPrincipal(String key) {
|
||||||
|
LOG.debug("Refreshing TimelyPrincipal {}", key);
|
||||||
|
TimelyPrincipal timelyPrincipal = null;
|
||||||
|
String[] dnArray = key.split(" -> ");
|
||||||
|
if (dnArray.length == 1) {
|
||||||
|
SubjectIssuerDNPair pair = SubjectIssuerDNPair.parse(dnArray[0]);
|
||||||
|
TimelyAuthenticationToken token = AuthenticationService.getAuthenticationToken(new Object(),
|
||||||
|
pair.subjectDN(), pair.issuerDN());
|
||||||
|
timelyPrincipal = AuthenticationService.authenticate(token, pair, token.getTimelyPrincipal().getName(),
|
||||||
|
false);
|
||||||
|
} else if (dnArray.length > 1) {
|
||||||
|
Collection<TimelyUser> timelyUsers = new ArrayList<>();
|
||||||
|
for (String s : dnArray) {
|
||||||
|
SubjectIssuerDNPair pair = SubjectIssuerDNPair.parse(s);
|
||||||
|
TimelyAuthenticationToken token = AuthenticationService.getAuthenticationToken(new Object(),
|
||||||
|
pair.subjectDN(), pair.issuerDN());
|
||||||
|
TimelyPrincipal p = AuthenticationService.authenticate(token, pair,
|
||||||
|
token.getTimelyPrincipal().getName(), false);
|
||||||
|
timelyUsers.addAll(p.getProxiedUsers());
|
||||||
|
}
|
||||||
|
timelyPrincipal = new TimelyPrincipal(timelyUsers);
|
||||||
|
}
|
||||||
|
return timelyPrincipal;
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean containsKey(String key) {
|
public static boolean containsKey(String key) {
|
||||||
return getCache().getIfPresent(key) != null;
|
return getCache().getIfPresent(key) != null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import org.springframework.context.ApplicationContext;
|
|||||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.web.authentication.preauth.x509.SubjectDnX509PrincipalExtractor;
|
|
||||||
import timely.api.request.AuthenticatedRequest;
|
import timely.api.request.AuthenticatedRequest;
|
||||||
import timely.api.response.TimelyException;
|
import timely.api.response.TimelyException;
|
||||||
import timely.auth.util.DnUtils;
|
import timely.auth.util.DnUtils;
|
||||||
@@ -35,7 +34,6 @@ public class AuthenticationService {
|
|||||||
private static final Logger LOG = LoggerFactory.getLogger(AuthenticationService.class);
|
private static final Logger LOG = LoggerFactory.getLogger(AuthenticationService.class);
|
||||||
private static ApplicationContext springContext = null;
|
private static ApplicationContext springContext = null;
|
||||||
private static AuthenticationManager authManager = null;
|
private static AuthenticationManager authManager = null;
|
||||||
private static SubjectDnX509PrincipalExtractor x509 = null;
|
|
||||||
private static ConcurrentHashMap<String, Object> fetchingEntity = new ConcurrentHashMap<>();
|
private static ConcurrentHashMap<String, Object> fetchingEntity = new ConcurrentHashMap<>();
|
||||||
private static ConcurrentHashMap<String, Object> fetchingPrincipal = new ConcurrentHashMap<>();
|
private static ConcurrentHashMap<String, Object> fetchingPrincipal = new ConcurrentHashMap<>();
|
||||||
private static Collection<String> requiredRoles;
|
private static Collection<String> requiredRoles;
|
||||||
@@ -45,9 +43,10 @@ public class AuthenticationService {
|
|||||||
try {
|
try {
|
||||||
springContext = new ClassPathXmlApplicationContext("security.xml");
|
springContext = new ClassPathXmlApplicationContext("security.xml");
|
||||||
authManager = springContext.getBean("authenticationManager", AuthenticationManager.class);
|
authManager = springContext.getBean("authenticationManager", AuthenticationManager.class);
|
||||||
x509 = springContext.getBean("x509PrincipalExtractor", SubjectDnX509PrincipalExtractor.class);
|
|
||||||
requiredRoles = springContext.getBean("requiredRoles", List.class);
|
requiredRoles = springContext.getBean("requiredRoles", List.class);
|
||||||
|
requiredRoles.removeIf(String::isEmpty);
|
||||||
requiredAuths = springContext.getBean("requiredAuths", List.class);
|
requiredAuths = springContext.getBean("requiredAuths", List.class);
|
||||||
|
requiredAuths.removeIf(String::isEmpty);
|
||||||
} catch (BeansException e) {
|
} catch (BeansException e) {
|
||||||
throw new ServiceConfigurationError("Error setting up Authentication objects: " + e.getMessage(),
|
throw new ServiceConfigurationError("Error setting up Authentication objects: " + e.getMessage(),
|
||||||
e.getRootCause());
|
e.getRootCause());
|
||||||
@@ -63,9 +62,17 @@ public class AuthenticationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static TimelyPrincipal authenticate(Authentication authentication, SubjectIssuerDNPair pair, String entity) {
|
public static TimelyPrincipal authenticate(Authentication authentication, SubjectIssuerDNPair pair, String entity) {
|
||||||
|
return authenticate(authentication, pair, entity, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TimelyPrincipal authenticate(Authentication authentication, SubjectIssuerDNPair pair, String entity,
|
||||||
|
boolean useCache) {
|
||||||
|
|
||||||
// only one thread should call the authenticationManager for a given entity
|
// only one thread should call the authenticationManager for a given entity
|
||||||
TimelyPrincipal principal = AuthCache.get(entity);
|
TimelyPrincipal principal = null;
|
||||||
|
if (useCache) {
|
||||||
|
principal = AuthCache.get(entity);
|
||||||
|
}
|
||||||
// once an entity is in the AuthCache, we can bypass all of this
|
// once an entity is in the AuthCache, we can bypass all of this
|
||||||
if (principal == null) {
|
if (principal == null) {
|
||||||
Object newObj = new Object();
|
Object newObj = new Object();
|
||||||
@@ -77,12 +84,16 @@ public class AuthenticationService {
|
|||||||
synchronized (o) {
|
synchronized (o) {
|
||||||
// if multiple threads are waiting on this synchronized, then the 2nd and all
|
// if multiple threads are waiting on this synchronized, then the 2nd and all
|
||||||
// others will get the principal here
|
// others will get the principal here
|
||||||
principal = AuthCache.get(entity);
|
if (useCache) {
|
||||||
|
principal = AuthCache.get(entity);
|
||||||
|
}
|
||||||
if (principal == null) {
|
if (principal == null) {
|
||||||
Authentication token = getAuthenticationManager().authenticate(authentication);
|
Authentication token = getAuthenticationManager().authenticate(authentication);
|
||||||
principal = getTimelyPrincipal(token, pair);
|
principal = getTimelyPrincipal(token, pair);
|
||||||
LOG.trace("Got entity principal for {} from AuthenticationManager", entity);
|
LOG.trace("Got entity principal for {} from AuthenticationManager", entity);
|
||||||
AuthCache.put(entity, principal);
|
if (useCache) {
|
||||||
|
AuthCache.put(entity, principal);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
LOG.trace("Got entity principal for {} from AuthCache on 2nd attempt", entity);
|
LOG.trace("Got entity principal for {} from AuthCache on 2nd attempt", entity);
|
||||||
}
|
}
|
||||||
@@ -114,7 +125,8 @@ public class AuthenticationService {
|
|||||||
TimelyAuthenticationToken token = request.getToken();
|
TimelyAuthenticationToken token = request.getToken();
|
||||||
TimelyPrincipal requestPrincipal = token.getTimelyPrincipal();
|
TimelyPrincipal requestPrincipal = token.getTimelyPrincipal();
|
||||||
String entity = requestPrincipal.getName();
|
String entity = requestPrincipal.getName();
|
||||||
if (!AuthCache.containsKey(requestPrincipal.getName())) {
|
verifiedPrincipal = AuthCache.get(entity);
|
||||||
|
if (verifiedPrincipal == null) {
|
||||||
Object newObj = new Object();
|
Object newObj = new Object();
|
||||||
Object o = fetchingPrincipal.getOrDefault(entity, newObj);
|
Object o = fetchingPrincipal.getOrDefault(entity, newObj);
|
||||||
if (o == null) {
|
if (o == null) {
|
||||||
@@ -123,7 +135,8 @@ public class AuthenticationService {
|
|||||||
try {
|
try {
|
||||||
synchronized (o) {
|
synchronized (o) {
|
||||||
// check again if in the AuthCache
|
// check again if in the AuthCache
|
||||||
if (!AuthCache.containsKey(entity)) {
|
verifiedPrincipal = AuthCache.get(entity);
|
||||||
|
if (verifiedPrincipal == null) {
|
||||||
// first thread here, so authenticate all entities in requestPrincipal
|
// first thread here, so authenticate all entities in requestPrincipal
|
||||||
List<TimelyUser> authenticatedTimelyUsers = new ArrayList<>();
|
List<TimelyUser> authenticatedTimelyUsers = new ArrayList<>();
|
||||||
for (TimelyUser user : requestPrincipal.getProxiedUsers()) {
|
for (TimelyUser user : requestPrincipal.getProxiedUsers()) {
|
||||||
@@ -134,10 +147,11 @@ public class AuthenticationService {
|
|||||||
entityToken.getTimelyPrincipal().getName());
|
entityToken.getTimelyPrincipal().getName());
|
||||||
authenticatedTimelyUsers.addAll(entityPrincipal.getProxiedUsers());
|
authenticatedTimelyUsers.addAll(entityPrincipal.getProxiedUsers());
|
||||||
}
|
}
|
||||||
TimelyPrincipal principal = new TimelyPrincipal(authenticatedTimelyUsers);
|
verifiedPrincipal = new TimelyPrincipal(authenticatedTimelyUsers);
|
||||||
AuthCache.put(principal.getName(), principal);
|
AuthCache.put(verifiedPrincipal.getName(), verifiedPrincipal);
|
||||||
LOG.debug("Authenticated user {} for request {} with authorizations {}",
|
LOG.debug("Authenticated user {} for request {} with authorizations {}",
|
||||||
principal.getName(), request.toString(), principal.getAuthorizationsString());
|
verifiedPrincipal.getName(), request.toString(),
|
||||||
|
verifiedPrincipal.getAuthorizationsString());
|
||||||
} else {
|
} else {
|
||||||
LOG.trace("Verified principal {} in AuthCache on 2nd attempt", entity);
|
LOG.trace("Verified principal {} in AuthCache on 2nd attempt", entity);
|
||||||
}
|
}
|
||||||
@@ -148,32 +162,54 @@ public class AuthenticationService {
|
|||||||
} else {
|
} else {
|
||||||
LOG.trace("Verified principal {} in AuthCache on 1st attempt", entity);
|
LOG.trace("Verified principal {} in AuthCache on 1st attempt", entity);
|
||||||
}
|
}
|
||||||
|
} catch (RuntimeException e) {
|
||||||
TimelyUser primaryUser = verifiedPrincipal.getPrimaryUser();
|
|
||||||
if (requiredRoles != null) {
|
|
||||||
if (!primaryUser.getRoles().containsAll(requiredRoles)) {
|
|
||||||
Set<String> missingRoles = new TreeSet<>();
|
|
||||||
missingRoles.addAll(requiredRoles);
|
|
||||||
missingRoles.removeAll(primaryUser.getRoles());
|
|
||||||
throw new TimelyException(HttpResponseStatus.UNAUTHORIZED.code(), "Access denied",
|
|
||||||
"User:" + primaryUser.getCommonName() + " is missing auth(s):" + missingRoles);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (requiredAuths != null) {
|
|
||||||
if (!primaryUser.getAuths().containsAll(requiredAuths)) {
|
|
||||||
Set<String> missingAuths = new TreeSet<>();
|
|
||||||
missingAuths.addAll(requiredRoles);
|
|
||||||
missingAuths.removeAll(primaryUser.getAuths());
|
|
||||||
throw new TimelyException(HttpResponseStatus.UNAUTHORIZED.code(), "Access denied",
|
|
||||||
"User:" + primaryUser.getCommonName() + " is missing role(s):" + missingAuths);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
LOG.error(e.getMessage(), e);
|
LOG.error(e.getMessage(), e);
|
||||||
throw new TimelyException(HttpResponseStatus.UNAUTHORIZED.code(), "Access denied", e.getMessage());
|
throw new TimelyException(HttpResponseStatus.UNAUTHORIZED.code(), "Access denied", e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
checkAllowedAccess(verifiedPrincipal.getPrimaryUser());
|
||||||
|
} catch (TimelyException e) {
|
||||||
|
// validation based on roles and/or auths failed
|
||||||
|
// remove combined principal and primary user from cache
|
||||||
|
// i.e. don't cache a failure
|
||||||
|
AuthCache.remove(verifiedPrincipal.getName());
|
||||||
|
AuthCache.remove(verifiedPrincipal.getPrimaryUser().getName());
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void checkAllowedAccess(TimelyUser primaryUser) throws TimelyException {
|
||||||
|
Set<String> missingRoles = new TreeSet<>();
|
||||||
|
Set<String> missingAuths = new TreeSet<>();
|
||||||
|
if (requiredRoles != null) {
|
||||||
|
if (!primaryUser.getRoles().containsAll(requiredRoles)) {
|
||||||
|
missingRoles.addAll(requiredRoles);
|
||||||
|
missingRoles.removeAll(primaryUser.getRoles());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (requiredAuths != null) {
|
||||||
|
if (!primaryUser.getAuths().containsAll(requiredAuths)) {
|
||||||
|
missingAuths.addAll(requiredRoles);
|
||||||
|
missingAuths.removeAll(primaryUser.getAuths());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!missingRoles.isEmpty() || !missingAuths.isEmpty()) {
|
||||||
|
String message = "";
|
||||||
|
if (!missingRoles.isEmpty() && !missingAuths.isEmpty()) {
|
||||||
|
message = "User:" + primaryUser.getName() + " is missing role(s):" + missingRoles + " and auth(s):"
|
||||||
|
+ missingAuths;
|
||||||
|
|
||||||
|
} else if (!missingRoles.isEmpty()) {
|
||||||
|
message = "User:" + primaryUser.getName() + " is missing role(s):" + missingRoles;
|
||||||
|
} else if (missingAuths.isEmpty()) {
|
||||||
|
message = "User:" + primaryUser.getName() + " is missing auths(s):" + missingAuths;
|
||||||
|
}
|
||||||
|
LOG.debug(message);
|
||||||
|
throw new TimelyException(HttpResponseStatus.UNAUTHORIZED.code(), "Access denied", message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static X509Certificate getClientCertificate(ChannelHandlerContext ctx) {
|
public static X509Certificate getClientCertificate(ChannelHandlerContext ctx) {
|
||||||
@@ -205,7 +241,7 @@ public class AuthenticationService {
|
|||||||
return authenticationToken;
|
return authenticationToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TimelyAuthenticationToken getAuthenticationToken(X509Certificate clientCert, String subjectDn,
|
public static TimelyAuthenticationToken getAuthenticationToken(Object clientCert, String subjectDn,
|
||||||
String issuerDn) {
|
String issuerDn) {
|
||||||
TimelyAuthenticationToken authenticationToken = null;
|
TimelyAuthenticationToken authenticationToken = null;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import java.util.List;
|
|||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import io.netty.handler.codec.http.QueryStringDecoder;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -200,4 +201,12 @@ public class DnUtils {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
|
||||||
|
QueryStringDecoder decoder = new QueryStringDecoder("https://my.server:3000");
|
||||||
|
|
||||||
|
System.out.println(decoder.path());
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,9 @@ import javax.validation.Valid;
|
|||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
import org.springframework.boot.context.properties.NestedConfigurationProperty;
|
import org.springframework.boot.context.properties.NestedConfigurationProperty;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
|
@Validated
|
||||||
@Component
|
@Component
|
||||||
@ConfigurationProperties(prefix = "timely")
|
@ConfigurationProperties(prefix = "timely")
|
||||||
public class Configuration {
|
public class Configuration {
|
||||||
@@ -19,6 +21,7 @@ public class Configuration {
|
|||||||
private HashMap<String, Integer> metricAgeOffDays = new HashMap<>();
|
private HashMap<String, Integer> metricAgeOffDays = new HashMap<>();
|
||||||
private List<String> metricsReportIgnoredTags = new ArrayList<>();
|
private List<String> metricsReportIgnoredTags = new ArrayList<>();
|
||||||
private String instance = null;
|
private String instance = null;
|
||||||
|
private String defaultVisibility = null;
|
||||||
|
|
||||||
@Valid
|
@Valid
|
||||||
@NestedConfigurationProperty
|
@NestedConfigurationProperty
|
||||||
@@ -71,6 +74,14 @@ public class Configuration {
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getDefaultVisibility() {
|
||||||
|
return defaultVisibility;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDefaultVisibility(String defaultVisibility) {
|
||||||
|
this.defaultVisibility = defaultVisibility;
|
||||||
|
}
|
||||||
|
|
||||||
public HashMap<String, Integer> getMetricAgeOffDays() {
|
public HashMap<String, Integer> getMetricAgeOffDays() {
|
||||||
return metricAgeOffDays;
|
return metricAgeOffDays;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ package timely.configuration;
|
|||||||
|
|
||||||
public class MetaCache {
|
public class MetaCache {
|
||||||
|
|
||||||
private long expirationMinutes = 60;
|
private long expirationMinutes = 1440;
|
||||||
private int initialCapacity = 2000;
|
private int cacheRefreshMinutes = -1;
|
||||||
private long maxCapacity = 10000;
|
private long maxTagValues = 100;
|
||||||
|
|
||||||
public long getExpirationMinutes() {
|
public long getExpirationMinutes() {
|
||||||
return expirationMinutes;
|
return expirationMinutes;
|
||||||
@@ -14,19 +14,19 @@ public class MetaCache {
|
|||||||
this.expirationMinutes = expirationMinutes;
|
this.expirationMinutes = expirationMinutes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getInitialCapacity() {
|
public void setCacheRefreshMinutes(int cacheRefreshMinutes) {
|
||||||
return initialCapacity;
|
this.cacheRefreshMinutes = cacheRefreshMinutes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setInitialCapacity(int initialCapacity) {
|
public int getCacheRefreshMinutes() {
|
||||||
this.initialCapacity = initialCapacity;
|
return cacheRefreshMinutes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getMaxCapacity() {
|
public long getMaxTagValues() {
|
||||||
return maxCapacity;
|
return maxTagValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMaxCapacity(long maxCapacity) {
|
public void setMaxTagValues(long maxTagValues) {
|
||||||
this.maxCapacity = maxCapacity;
|
this.maxTagValues = maxTagValues;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,6 +9,8 @@ public class Security {
|
|||||||
private boolean allowAnonymousHttpAccess = false;
|
private boolean allowAnonymousHttpAccess = false;
|
||||||
private boolean allowAnonymousWsAccess = false;
|
private boolean allowAnonymousWsAccess = false;
|
||||||
private int sessionMaxAge = 86400;
|
private int sessionMaxAge = 86400;
|
||||||
|
private int cacheExpirationMinutes = 60;
|
||||||
|
private int cacheRefreshMinutes = 5;
|
||||||
@Valid
|
@Valid
|
||||||
@NestedConfigurationProperty
|
@NestedConfigurationProperty
|
||||||
private ServerSsl serverSsl = new ServerSsl();
|
private ServerSsl serverSsl = new ServerSsl();
|
||||||
@@ -37,6 +39,22 @@ public class Security {
|
|||||||
this.sessionMaxAge = sessionMaxAge;
|
this.sessionMaxAge = sessionMaxAge;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setCacheExpirationMinutes(int cacheExpirationMinutes) {
|
||||||
|
this.cacheExpirationMinutes = cacheExpirationMinutes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCacheExpirationMinutes() {
|
||||||
|
return cacheExpirationMinutes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCacheRefreshMinutes(int cacheRefreshMinutes) {
|
||||||
|
this.cacheRefreshMinutes = cacheRefreshMinutes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCacheRefreshMinutes() {
|
||||||
|
return cacheRefreshMinutes;
|
||||||
|
}
|
||||||
|
|
||||||
public ServerSsl getServerSsl() {
|
public ServerSsl getServerSsl() {
|
||||||
return serverSsl;
|
return serverSsl;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,19 +22,20 @@ public class TimelyAuthenticationToken extends PreAuthenticatedAuthenticationTok
|
|||||||
public static final String PROXIED_ISSUERS_HEADER = "X-ProxiedIssuersChain";
|
public static final String PROXIED_ISSUERS_HEADER = "X-ProxiedIssuersChain";
|
||||||
|
|
||||||
private Multimap<String, String> httpHeaders = null;
|
private Multimap<String, String> httpHeaders = null;
|
||||||
private X509Certificate clientCert;
|
private X509Certificate clientCert = null;
|
||||||
private TimelyPrincipal timelyPrincipal;
|
private TimelyPrincipal timelyPrincipal;
|
||||||
|
|
||||||
public TimelyAuthenticationToken(String subjectDn, X509Certificate clientCert,
|
public TimelyAuthenticationToken(String subjectDn, Object clientCert, Multimap<String, String> httpHeaders) {
|
||||||
Multimap<String, String> httpHeaders) {
|
|
||||||
super(subjectDn, clientCert);
|
super(subjectDn, clientCert);
|
||||||
if (httpHeaders == null) {
|
if (httpHeaders == null) {
|
||||||
this.httpHeaders = HashMultimap.create();
|
this.httpHeaders = HashMultimap.create();
|
||||||
} else {
|
} else {
|
||||||
this.httpHeaders = httpHeaders;
|
this.httpHeaders = httpHeaders;
|
||||||
}
|
}
|
||||||
this.clientCert = clientCert;
|
if (clientCert instanceof X509Certificate) {
|
||||||
String issuerDn = clientCert == null ? null : clientCert.getIssuerDN().getName();
|
this.clientCert = (X509Certificate) clientCert;
|
||||||
|
}
|
||||||
|
String issuerDn = clientCert == null ? null : this.clientCert.getIssuerDN().getName();
|
||||||
String proxiedEntities;
|
String proxiedEntities;
|
||||||
String proxiedIssuers;
|
String proxiedIssuers;
|
||||||
try {
|
try {
|
||||||
@@ -61,9 +62,11 @@ public class TimelyAuthenticationToken extends PreAuthenticatedAuthenticationTok
|
|||||||
LOG.trace("Created TimelyAuthenticationToken: {}", timelyPrincipal.getName());
|
LOG.trace("Created TimelyAuthenticationToken: {}", timelyPrincipal.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
public TimelyAuthenticationToken(String subjectDn, String issuerDn, X509Certificate clientCert) {
|
public TimelyAuthenticationToken(String subjectDn, String issuerDn, Object clientCert) {
|
||||||
super(subjectDn, clientCert);
|
super(subjectDn, clientCert);
|
||||||
this.clientCert = clientCert;
|
if (clientCert instanceof X509Certificate) {
|
||||||
|
this.clientCert = (X509Certificate) clientCert;
|
||||||
|
}
|
||||||
TimelyUser.UserType userType = DnUtils.isServerDN(subjectDn) ? TimelyUser.UserType.SERVER
|
TimelyUser.UserType userType = DnUtils.isServerDN(subjectDn) ? TimelyUser.UserType.SERVER
|
||||||
: TimelyUser.UserType.USER;
|
: TimelyUser.UserType.USER;
|
||||||
TimelyUser timelyUser = new TimelyUser(SubjectIssuerDNPair.of(subjectDn, issuerDn), userType, null, null, null,
|
TimelyUser timelyUser = new TimelyUser(SubjectIssuerDNPair.of(subjectDn, issuerDn), userType, null, null, null,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package timely.store;
|
|||||||
|
|
||||||
import static org.apache.accumulo.core.conf.AccumuloConfiguration.getMemoryInBytes;
|
import static org.apache.accumulo.core.conf.AccumuloConfiguration.getMemoryInBytes;
|
||||||
import static org.apache.accumulo.core.conf.AccumuloConfiguration.getTimeInMillis;
|
import static org.apache.accumulo.core.conf.AccumuloConfiguration.getTimeInMillis;
|
||||||
|
import static timely.adapter.accumulo.MetricAdapter.VISIBILITY_TAG;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
@@ -18,6 +19,7 @@ import java.util.LinkedHashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.SortedMap;
|
import java.util.SortedMap;
|
||||||
import java.util.Timer;
|
import java.util.Timer;
|
||||||
@@ -50,8 +52,11 @@ import org.apache.accumulo.core.data.*;
|
|||||||
import org.apache.accumulo.core.iterators.IteratorUtil.IteratorScope;
|
import org.apache.accumulo.core.iterators.IteratorUtil.IteratorScope;
|
||||||
import org.apache.accumulo.core.iterators.user.RegExFilter;
|
import org.apache.accumulo.core.iterators.user.RegExFilter;
|
||||||
import org.apache.accumulo.core.security.Authorizations;
|
import org.apache.accumulo.core.security.Authorizations;
|
||||||
|
import org.apache.accumulo.core.security.ColumnVisibility;
|
||||||
|
import org.apache.accumulo.core.security.VisibilityEvaluator;
|
||||||
import org.apache.accumulo.core.util.Pair;
|
import org.apache.accumulo.core.util.Pair;
|
||||||
import org.apache.commons.configuration.BaseConfiguration;
|
import org.apache.commons.configuration.BaseConfiguration;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.hadoop.io.Text;
|
import org.apache.hadoop.io.Text;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -130,6 +135,7 @@ public class DataStoreImpl implements DataStore {
|
|||||||
private final Map<String, String> ageOff;
|
private final Map<String, String> ageOff;
|
||||||
private final long defaultAgeOffMilliSec;
|
private final long defaultAgeOffMilliSec;
|
||||||
private DataStoreCache cache = null;
|
private DataStoreCache cache = null;
|
||||||
|
private final String defaultVisibility;
|
||||||
|
|
||||||
public DataStoreImpl(Configuration conf, int numWriteThreads) throws TimelyException {
|
public DataStoreImpl(Configuration conf, int numWriteThreads) throws TimelyException {
|
||||||
|
|
||||||
@@ -149,6 +155,31 @@ public class DataStoreImpl implements DataStore {
|
|||||||
scannerThreads = accumuloConf.getScan().getThreads();
|
scannerThreads = accumuloConf.getScan().getThreads();
|
||||||
maxDownsampleMemory = accumuloConf.getScan().getMaxDownsampleMemory();
|
maxDownsampleMemory = accumuloConf.getScan().getMaxDownsampleMemory();
|
||||||
security = conf.getSecurity();
|
security = conf.getSecurity();
|
||||||
|
defaultVisibility = conf.getDefaultVisibility();
|
||||||
|
if (StringUtils.isNotBlank(defaultVisibility)) {
|
||||||
|
ColumnVisibility vis;
|
||||||
|
try {
|
||||||
|
// validate visibility
|
||||||
|
vis = new ColumnVisibility(defaultVisibility);
|
||||||
|
LOG.info("Using defaultVisibility: {}", vis.toString());
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
LOG.error("Error validating defaultVisibility " + defaultVisibility + " " + e.getMessage(), e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
String whoami = connector.whoami();
|
||||||
|
Authorizations auths = connector.securityOperations().getUserAuthorizations(whoami);
|
||||||
|
VisibilityEvaluator eval = new VisibilityEvaluator(auths);
|
||||||
|
if (!eval.evaluate(vis)) {
|
||||||
|
throw new IllegalArgumentException("Accumulo user " + whoami + " with authorization "
|
||||||
|
+ auths.toString() + " can not see data with defaultVisibility " + vis.toString());
|
||||||
|
}
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
LOG.error(e.getMessage(), e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
metricsTable = conf.getMetricsTable();
|
metricsTable = conf.getMetricsTable();
|
||||||
if (metricsTable.contains(".")) {
|
if (metricsTable.contains(".")) {
|
||||||
@@ -279,6 +310,17 @@ public class DataStoreImpl implements DataStore {
|
|||||||
public void store(Metric metric, boolean cacheEnabled) {
|
public void store(Metric metric, boolean cacheEnabled) {
|
||||||
LOG.trace("Received Store Request for: {}", metric);
|
LOG.trace("Received Store Request for: {}", metric);
|
||||||
|
|
||||||
|
// if default visibility is configured and current metric does not contain a
|
||||||
|
// visibility
|
||||||
|
// add it here, so that the cache and accumulo stores both have the correct
|
||||||
|
// visibility
|
||||||
|
if (StringUtils.isNotBlank(defaultVisibility)) {
|
||||||
|
Optional<Tag> visTag = metric.getTags().stream().filter(t -> t.getKey().equals(VISIBILITY_TAG)).findFirst();
|
||||||
|
if (!visTag.isPresent()) {
|
||||||
|
metric.addTag(new Tag(VISIBILITY_TAG, defaultVisibility));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (cache != null && cacheEnabled) {
|
if (cache != null && cacheEnabled) {
|
||||||
cache.store(metric);
|
cache.store(metric);
|
||||||
}
|
}
|
||||||
@@ -396,6 +438,7 @@ public class DataStoreImpl implements DataStore {
|
|||||||
@Override
|
@Override
|
||||||
public SuggestResponse suggest(SuggestRequest request) throws TimelyException {
|
public SuggestResponse suggest(SuggestRequest request) throws TimelyException {
|
||||||
SuggestResponse result = new SuggestResponse();
|
SuggestResponse result = new SuggestResponse();
|
||||||
|
Scanner scanner = null;
|
||||||
try {
|
try {
|
||||||
if (request.getType().equals("metrics")) {
|
if (request.getType().equals("metrics")) {
|
||||||
Range range;
|
Range range;
|
||||||
@@ -415,7 +458,7 @@ public class DataStoreImpl implements DataStore {
|
|||||||
end.append(lastBytes, 0, lastBytes.length);
|
end.append(lastBytes, 0, lastBytes.length);
|
||||||
range = new Range(start, end);
|
range = new Range(start, end);
|
||||||
}
|
}
|
||||||
Scanner scanner = connector.createScanner(metaTable, Authorizations.EMPTY);
|
scanner = connector.createScanner(metaTable, Authorizations.EMPTY);
|
||||||
scanner.setRange(range);
|
scanner.setRange(range);
|
||||||
List<String> metrics = new ArrayList<>();
|
List<String> metrics = new ArrayList<>();
|
||||||
for (Entry<Key, Value> metric : scanner) {
|
for (Entry<Key, Value> metric : scanner) {
|
||||||
@@ -430,6 +473,10 @@ public class DataStoreImpl implements DataStore {
|
|||||||
LOG.error("Error during suggest: " + ex.getMessage(), ex);
|
LOG.error("Error during suggest: " + ex.getMessage(), ex);
|
||||||
throw new TimelyException(HttpResponseStatus.INTERNAL_SERVER_ERROR.code(),
|
throw new TimelyException(HttpResponseStatus.INTERNAL_SERVER_ERROR.code(),
|
||||||
"Error during suggest: " + ex.getMessage(), ex.getMessage(), ex);
|
"Error during suggest: " + ex.getMessage(), ex.getMessage(), ex);
|
||||||
|
} finally {
|
||||||
|
if (scanner != null) {
|
||||||
|
scanner.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -464,28 +511,34 @@ public class DataStoreImpl implements DataStore {
|
|||||||
tagPatterns.put(k, Pattern.compile(v));
|
tagPatterns.put(k, Pattern.compile(v));
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
List<Result> resultField = new ArrayList<>();
|
final Scanner scanner = connector.createScanner(metaTable, Authorizations.EMPTY);
|
||||||
Scanner scanner = connector.createScanner(metaTable, Authorizations.EMPTY);
|
try {
|
||||||
Key start = new Key(Meta.VALUE_PREFIX + msg.getQuery());
|
List<Result> resultField = new ArrayList<>();
|
||||||
Key end = start.followingKey(PartialKey.ROW);
|
Key start = new Key(Meta.VALUE_PREFIX + msg.getQuery());
|
||||||
Range range = new Range(start, end);
|
Key end = start.followingKey(PartialKey.ROW);
|
||||||
scanner.setRange(range);
|
Range range = new Range(start, end);
|
||||||
tags.keySet().forEach(k -> scanner.fetchColumnFamily(new Text(k)));
|
scanner.setRange(range);
|
||||||
int total = 0;
|
tags.keySet().forEach(k -> scanner.fetchColumnFamily(new Text(k)));
|
||||||
for (Entry<Key, Value> entry : scanner) {
|
int total = 0;
|
||||||
Meta metaEntry = Meta.parse(entry.getKey(), entry.getValue());
|
for (Entry<Key, Value> entry : scanner) {
|
||||||
if (matches(metaEntry.getTagKey(), metaEntry.getTagValue(), tagPatterns)) {
|
Meta metaEntry = Meta.parse(entry.getKey(), entry.getValue());
|
||||||
if (resultField.size() < msg.getLimit()) {
|
if (matches(metaEntry.getTagKey(), metaEntry.getTagValue(), tagPatterns)) {
|
||||||
Result r = new Result();
|
if (resultField.size() < msg.getLimit()) {
|
||||||
r.putTag(metaEntry.getTagKey(), metaEntry.getTagValue());
|
Result r = new Result();
|
||||||
resultField.add(r);
|
r.putTag(metaEntry.getTagKey(), metaEntry.getTagValue());
|
||||||
|
resultField.add(r);
|
||||||
|
}
|
||||||
|
total++;
|
||||||
}
|
}
|
||||||
total++;
|
}
|
||||||
|
result.setResults(resultField);
|
||||||
|
result.setTotalResults(total);
|
||||||
|
result.setTime((int) (System.currentTimeMillis() - startMillis));
|
||||||
|
} finally {
|
||||||
|
if (scanner != null) {
|
||||||
|
scanner.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result.setResults(resultField);
|
|
||||||
result.setTotalResults(total);
|
|
||||||
result.setTime((int) (System.currentTimeMillis() - startMillis));
|
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
LOG.error("Error during lookup: " + ex.getMessage(), ex);
|
LOG.error("Error during lookup: " + ex.getMessage(), ex);
|
||||||
throw new TimelyException(HttpResponseStatus.INTERNAL_SERVER_ERROR.code(),
|
throw new TimelyException(HttpResponseStatus.INTERNAL_SERVER_ERROR.code(),
|
||||||
@@ -756,70 +809,77 @@ public class DataStoreImpl implements DataStore {
|
|||||||
|
|
||||||
public Set<Tag> getColumnFamilies(String metric, Map<String, String> requestedTags) throws TableNotFoundException {
|
public Set<Tag> getColumnFamilies(String metric, Map<String, String> requestedTags) throws TableNotFoundException {
|
||||||
|
|
||||||
Map<String, String> tags = (requestedTags == null) ? new LinkedHashMap<>() : requestedTags;
|
Scanner meta = null;
|
||||||
LOG.trace("Looking for requested tags: {}", tags);
|
try {
|
||||||
Scanner meta = connector.createScanner(metaTable, Authorizations.EMPTY);
|
Map<String, String> tags = (requestedTags == null) ? new LinkedHashMap<>() : requestedTags;
|
||||||
Text start = new Text(Meta.VALUE_PREFIX + metric);
|
LOG.trace("Looking for requested tags: {}", tags);
|
||||||
Text end = new Text(Meta.VALUE_PREFIX + metric + "\\x0000");
|
meta = connector.createScanner(metaTable, Authorizations.EMPTY);
|
||||||
end.append(new byte[] { (byte) 0xff }, 0, 1);
|
Text start = new Text(Meta.VALUE_PREFIX + metric);
|
||||||
meta.setRange(new Range(start, end));
|
Text end = new Text(Meta.VALUE_PREFIX + metric + "\\x0000");
|
||||||
// Only look for the meta entries that match our tags, if any
|
end.append(new byte[] { (byte) 0xff }, 0, 1);
|
||||||
boolean onlyFirstRow = false;
|
meta.setRange(new Range(start, end));
|
||||||
Entry<String, String> first = null;
|
// Only look for the meta entries that match our tags, if any
|
||||||
// Set the columns on the meta scanner based on the first tag
|
boolean onlyFirstRow = false;
|
||||||
// in the set of tags passed in the query. If no tags are present
|
Entry<String, String> first = null;
|
||||||
// then we are only going to return the first tag name present in the
|
// Set the columns on the meta scanner based on the first tag
|
||||||
// meta table.
|
// in the set of tags passed in the query. If no tags are present
|
||||||
Iterator<Entry<String, String>> tagIter = tags.entrySet().iterator();
|
// then we are only going to return the first tag name present in the
|
||||||
if (tagIter.hasNext()) {
|
// meta table.
|
||||||
first = tagIter.next();
|
Iterator<Entry<String, String>> tagIter = tags.entrySet().iterator();
|
||||||
if (isTagValueRegex(first.getValue())) {
|
if (tagIter.hasNext()) {
|
||||||
meta.fetchColumnFamily(new Text(first.getKey()));
|
first = tagIter.next();
|
||||||
} else {
|
if (isTagValueRegex(first.getValue())) {
|
||||||
meta.fetchColumn(new Text(first.getKey()), new Text(first.getValue()));
|
meta.fetchColumnFamily(new Text(first.getKey()));
|
||||||
}
|
} else {
|
||||||
} else {
|
meta.fetchColumn(new Text(first.getKey()), new Text(first.getValue()));
|
||||||
// grab all of the values found for the first tag for the metric
|
|
||||||
onlyFirstRow = true;
|
|
||||||
}
|
|
||||||
final boolean ONLY_RETURN_FIRST_TAG = onlyFirstRow;
|
|
||||||
Iterator<Entry<Key, Value>> iter = meta.iterator();
|
|
||||||
Iterator<Pair<String, String>> knownKeyValues = new Iterator<Pair<String, String>>() {
|
|
||||||
|
|
||||||
Text firstTag = null;
|
|
||||||
Text tagName = null;
|
|
||||||
Text tagValue = null;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasNext() {
|
|
||||||
if (iter.hasNext()) {
|
|
||||||
Entry<Key, Value> metaEntry = iter.next();
|
|
||||||
if (null == firstTag) {
|
|
||||||
firstTag = metaEntry.getKey().getColumnFamily();
|
|
||||||
}
|
|
||||||
tagName = metaEntry.getKey().getColumnFamily();
|
|
||||||
tagValue = metaEntry.getKey().getColumnQualifier();
|
|
||||||
LOG.trace("Found tag entry {}={}", tagName, tagValue);
|
|
||||||
|
|
||||||
if (ONLY_RETURN_FIRST_TAG && !tagName.equals(firstTag)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
} else {
|
||||||
|
// grab all of the values found for the first tag for the metric
|
||||||
|
onlyFirstRow = true;
|
||||||
}
|
}
|
||||||
|
final boolean ONLY_RETURN_FIRST_TAG = onlyFirstRow;
|
||||||
|
Iterator<Entry<Key, Value>> iter = meta.iterator();
|
||||||
|
Iterator<Pair<String, String>> knownKeyValues = new Iterator<Pair<String, String>>() {
|
||||||
|
|
||||||
@Override
|
Text firstTag = null;
|
||||||
public Pair<String, String> next() {
|
Text tagName = null;
|
||||||
LOG.trace("Returning tag {}={}", tagName, tagValue);
|
Text tagValue = null;
|
||||||
return new Pair<>(tagName.toString(), tagValue.toString());
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
if (iter.hasNext()) {
|
||||||
|
Entry<Key, Value> metaEntry = iter.next();
|
||||||
|
if (null == firstTag) {
|
||||||
|
firstTag = metaEntry.getKey().getColumnFamily();
|
||||||
|
}
|
||||||
|
tagName = metaEntry.getKey().getColumnFamily();
|
||||||
|
tagValue = metaEntry.getKey().getColumnQualifier();
|
||||||
|
LOG.trace("Found tag entry {}={}", tagName, tagValue);
|
||||||
|
|
||||||
|
if (ONLY_RETURN_FIRST_TAG && !tagName.equals(firstTag)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Pair<String, String> next() {
|
||||||
|
LOG.trace("Returning tag {}={}", tagName, tagValue);
|
||||||
|
return new Pair<>(tagName.toString(), tagValue.toString());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Expand the list of tags in the meta table for this metric that
|
||||||
|
// matches
|
||||||
|
// the pattern of the first tag in the query. The resulting set of tags
|
||||||
|
// will be used to fetch specific columns from the metric table.
|
||||||
|
return expandTagValues(first, knownKeyValues);
|
||||||
|
} finally {
|
||||||
|
if (meta != null) {
|
||||||
|
meta.close();
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
// Expand the list of tags in the meta table for this metric that
|
|
||||||
// matches
|
|
||||||
// the pattern of the first tag in the query. The resulting set of tags
|
|
||||||
// will be used to fetch specific columns from the metric table.
|
|
||||||
return expandTagValues(first, knownKeyValues);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setQueryColumns(ScannerBase scanner, String metric, Map<String, String> tags, Set<Tag> colFamValues)
|
private void setQueryColumns(ScannerBase scanner, String metric, Map<String, String> tags, Set<Tag> colFamValues)
|
||||||
|
|||||||
@@ -0,0 +1,116 @@
|
|||||||
|
package timely.store;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.accumulo.core.data.ByteSequence;
|
||||||
|
import org.apache.accumulo.core.data.Key;
|
||||||
|
import org.apache.accumulo.core.data.Range;
|
||||||
|
import org.apache.accumulo.core.data.Value;
|
||||||
|
import org.apache.accumulo.core.iterators.IteratorEnvironment;
|
||||||
|
import org.apache.accumulo.core.iterators.OptionDescriber;
|
||||||
|
import org.apache.accumulo.core.iterators.SortedKeyValueIterator;
|
||||||
|
import org.apache.accumulo.core.iterators.WrappingIterator;
|
||||||
|
import org.apache.commons.collections4.trie.PatriciaTrie;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import timely.api.model.Meta;
|
||||||
|
|
||||||
|
public class MetaAgeOffIterator extends WrappingIterator implements OptionDescriber {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(MetaAgeOffIterator.class);
|
||||||
|
public static final String AGE_OFF_PREFIX = "ageoff.";
|
||||||
|
public static final String DEFAULT_AGEOFF_KEY = "default";
|
||||||
|
|
||||||
|
/* set in init */
|
||||||
|
private PatriciaTrie<Long> ageoffs = null;
|
||||||
|
private Long currentTime = 0L;
|
||||||
|
private Long defaultAgeOff = 0L;
|
||||||
|
private Long minAgeOff = Long.MAX_VALUE;
|
||||||
|
private Long maxAgeOff = Long.MIN_VALUE;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SortedKeyValueIterator<Key, Value> deepCopy(IteratorEnvironment env) {
|
||||||
|
MetaAgeOffIterator iter = new MetaAgeOffIterator();
|
||||||
|
iter.ageoffs = this.ageoffs;
|
||||||
|
iter.defaultAgeOff = this.defaultAgeOff;
|
||||||
|
iter.currentTime = this.currentTime;
|
||||||
|
iter.minAgeOff = this.minAgeOff;
|
||||||
|
iter.maxAgeOff = this.maxAgeOff;
|
||||||
|
return iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(SortedKeyValueIterator<Key, Value> source, Map<String, String> options, IteratorEnvironment env)
|
||||||
|
throws IOException {
|
||||||
|
super.init(source, options, env);
|
||||||
|
validateOptions(options);
|
||||||
|
ageoffs = new PatriciaTrie<>();
|
||||||
|
options.forEach((k, v) -> {
|
||||||
|
if (k.startsWith(AGE_OFF_PREFIX)) {
|
||||||
|
String name = k.substring(AGE_OFF_PREFIX.length());
|
||||||
|
LOG.trace("Adding {} to Trie with value {}", name, Long.parseLong(v));
|
||||||
|
long ageoff = Long.parseLong(v);
|
||||||
|
this.minAgeOff = Math.min(this.minAgeOff, ageoff);
|
||||||
|
this.maxAgeOff = Math.max(this.maxAgeOff, ageoff);
|
||||||
|
ageoffs.put(name, ageoff);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
defaultAgeOff = ageoffs.get(DEFAULT_AGEOFF_KEY);
|
||||||
|
currentTime = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void next() throws IOException {
|
||||||
|
super.next();
|
||||||
|
skipExpiredData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void seek(Range range, Collection<ByteSequence> columnFamilies, boolean inclusive) throws IOException {
|
||||||
|
super.seek(range, columnFamilies, inclusive);
|
||||||
|
skipExpiredData();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void skipExpiredData() throws IOException {
|
||||||
|
|
||||||
|
boolean done = false;
|
||||||
|
while (!done && super.hasTop()) {
|
||||||
|
Key top = super.getTopKey();
|
||||||
|
long dataAge = this.currentTime - top.getTimestamp();
|
||||||
|
// If less than any configured ageoff, then we want to return this K,V
|
||||||
|
if (dataAge < this.minAgeOff) {
|
||||||
|
done = true;
|
||||||
|
} else {
|
||||||
|
String metricName = Meta.parse(top).getMetric();
|
||||||
|
long metricAgeOff = ageoffs.getOrDefault(metricName, defaultAgeOff);
|
||||||
|
if (dataAge > metricAgeOff) {
|
||||||
|
// data beyond ageOff, so skip
|
||||||
|
super.next();
|
||||||
|
} else {
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IteratorOptions describeOptions() {
|
||||||
|
return new IteratorOptions("metric-age-off", "Iterator that ages off meta entries for Timely metrics",
|
||||||
|
Collections.singletonMap(AGE_OFF_PREFIX + DEFAULT_AGEOFF_KEY, "default age off days"),
|
||||||
|
Collections.singletonList("Additional metric age off properties where the metric is specified as "
|
||||||
|
+ AGE_OFF_PREFIX
|
||||||
|
+ " the metric name and the value is an integer representing the number of days to keep"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean validateOptions(Map<String, String> options) {
|
||||||
|
if (null == options.get(AGE_OFF_PREFIX + DEFAULT_AGEOFF_KEY)) {
|
||||||
|
throw new IllegalArgumentException(DEFAULT_AGEOFF_KEY + " must be configured for MetaAgeOffFilter");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,27 +1,123 @@
|
|||||||
package timely.store;
|
package timely.store;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.Timer;
|
||||||
|
import java.util.TimerTask;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
import com.github.benmanes.caffeine.cache.Cache;
|
import com.github.benmanes.caffeine.cache.Cache;
|
||||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||||
|
import org.apache.accumulo.core.client.ClientConfiguration;
|
||||||
|
import org.apache.accumulo.core.client.Connector;
|
||||||
|
import org.apache.accumulo.core.client.Instance;
|
||||||
|
import org.apache.accumulo.core.client.Scanner;
|
||||||
|
import org.apache.accumulo.core.client.ZooKeeperInstance;
|
||||||
|
import org.apache.accumulo.core.client.security.tokens.PasswordToken;
|
||||||
|
import org.apache.accumulo.core.data.Key;
|
||||||
|
import org.apache.accumulo.core.data.Range;
|
||||||
|
import org.apache.accumulo.core.data.Value;
|
||||||
|
import org.apache.accumulo.core.security.Authorizations;
|
||||||
|
import org.apache.commons.configuration.BaseConfiguration;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import timely.api.model.Meta;
|
import timely.api.model.Meta;
|
||||||
|
import timely.configuration.Accumulo;
|
||||||
import timely.configuration.Configuration;
|
import timely.configuration.Configuration;
|
||||||
|
|
||||||
public class MetaCacheImpl implements MetaCache {
|
public class MetaCacheImpl implements MetaCache {
|
||||||
|
|
||||||
|
private static Logger LOG = LoggerFactory.getLogger(MetaCacheImpl.class);
|
||||||
private static final Object DUMMY = new Object();
|
private static final Object DUMMY = new Object();
|
||||||
private volatile boolean closed = false;
|
private volatile boolean closed = false;
|
||||||
private Cache<Meta, Object> cache = null;
|
private Cache<Meta, Object> cache = null;
|
||||||
|
private Configuration configuration;
|
||||||
|
private Timer timer = new Timer("MetaCacheImpl", true);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(Configuration config) {
|
public void init(Configuration config) {
|
||||||
long expirationMinutes = config.getMetaCache().getExpirationMinutes();
|
configuration = config;
|
||||||
int initialCapacity = config.getMetaCache().getInitialCapacity();
|
cache = Caffeine.newBuilder().initialCapacity(1000).build();
|
||||||
long maxCapacity = config.getMetaCache().getMaxCapacity();
|
long cacheRefreshMinutes = configuration.getMetaCache().getCacheRefreshMinutes();
|
||||||
cache = Caffeine.newBuilder().expireAfterAccess(expirationMinutes, TimeUnit.MINUTES)
|
if (cacheRefreshMinutes > 0) {
|
||||||
.initialCapacity(initialCapacity).maximumSize(maxCapacity).build();
|
timer.schedule(new TimerTask() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
refreshCache(config.getMetaCache().getExpirationMinutes());
|
||||||
|
}
|
||||||
|
}, 60000, cacheRefreshMinutes * 60 * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshCache(long expirationMinutes) {
|
||||||
|
long oldestTimestamp = System.currentTimeMillis() - (expirationMinutes * 60 * 1000);
|
||||||
|
final BaseConfiguration apacheConf = new BaseConfiguration();
|
||||||
|
Accumulo accumuloConf = configuration.getAccumulo();
|
||||||
|
apacheConf.setProperty("instance.name", accumuloConf.getInstanceName());
|
||||||
|
apacheConf.setProperty("instance.zookeeper.host", accumuloConf.getZookeepers());
|
||||||
|
final ClientConfiguration aConf = new ClientConfiguration(Collections.singletonList(apacheConf));
|
||||||
|
String metaTable = configuration.getMetaTable();
|
||||||
|
Map<String, Map<String, Long>> metricMap = new HashMap<>();
|
||||||
|
Scanner scanner = null;
|
||||||
|
try {
|
||||||
|
final Instance instance = new ZooKeeperInstance(aConf);
|
||||||
|
Connector connector = instance.getConnector(accumuloConf.getUsername(),
|
||||||
|
new PasswordToken(accumuloConf.getPassword()));
|
||||||
|
scanner = connector.createScanner(metaTable, Authorizations.EMPTY);
|
||||||
|
LOG.debug("Begin scanning " + metaTable);
|
||||||
|
Range metricRange = new Range(new Key(Meta.METRIC_PREFIX), true, new Key(Meta.METRIC_PREFIX + '\0'), false);
|
||||||
|
scanner.setRange(metricRange);
|
||||||
|
Set<String> allMetrics = new TreeSet<>();
|
||||||
|
for (Map.Entry<Key, Value> entry : scanner) {
|
||||||
|
Meta meta = Meta.parse(entry.getKey(), entry.getValue(), Meta.METRIC_PREFIX);
|
||||||
|
allMetrics.add(meta.getMetric());
|
||||||
|
}
|
||||||
|
for (String currMetric : allMetrics) {
|
||||||
|
Key begin = new Key(Meta.VALUE_PREFIX + currMetric);
|
||||||
|
Key end = new Key(Meta.VALUE_PREFIX + currMetric + '\0');
|
||||||
|
Range range = new Range(begin, true, end, false);
|
||||||
|
scanner.setRange(range);
|
||||||
|
Iterator<Map.Entry<Key, Value>> itr = scanner.iterator();
|
||||||
|
boolean maxedOutValues = false;
|
||||||
|
while (itr.hasNext() && !maxedOutValues) {
|
||||||
|
Map.Entry<Key, Value> entry = itr.next();
|
||||||
|
Meta meta = Meta.parse(entry.getKey(), entry.getValue(), Meta.VALUE_PREFIX);
|
||||||
|
String metric = meta.getMetric();
|
||||||
|
String tagKey = meta.getTagKey();
|
||||||
|
Map<String, Long> tagMap = metricMap.get(metric);
|
||||||
|
if (tagMap == null) {
|
||||||
|
tagMap = new HashMap<>();
|
||||||
|
metricMap.put(metric, tagMap);
|
||||||
|
}
|
||||||
|
long numTagValues = tagMap.getOrDefault(tagKey, 0l);
|
||||||
|
if (entry.getKey().getTimestamp() > oldestTimestamp) {
|
||||||
|
tagMap.put(tagKey, ++numTagValues);
|
||||||
|
if (numTagValues <= configuration.getMetaCache().getMaxTagValues()) {
|
||||||
|
cache.put(meta, DUMMY);
|
||||||
|
} else {
|
||||||
|
// found maxTagValues on this refresh, so remove this key
|
||||||
|
cache.invalidate(meta);
|
||||||
|
maxedOutValues = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// this entry is more than expirationMinutes old, so remove it
|
||||||
|
cache.invalidate(meta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG.debug("Finished scanning " + metaTable);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error(e.getMessage(), e);
|
||||||
|
} finally {
|
||||||
|
if (scanner != null) {
|
||||||
|
scanner.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -53,5 +149,4 @@ public class MetaCacheImpl implements MetaCache {
|
|||||||
public boolean isClosed() {
|
public boolean isClosed() {
|
||||||
return closed;
|
return closed;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -14,6 +14,7 @@ import org.apache.accumulo.core.data.Range;
|
|||||||
import org.apache.accumulo.core.data.Value;
|
import org.apache.accumulo.core.data.Value;
|
||||||
import org.apache.commons.configuration.BaseConfiguration;
|
import org.apache.commons.configuration.BaseConfiguration;
|
||||||
import org.springframework.boot.Banner.Mode;
|
import org.springframework.boot.Banner.Mode;
|
||||||
|
import org.springframework.boot.WebApplicationType;
|
||||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||||
import org.springframework.context.ConfigurableApplicationContext;
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
import timely.api.model.Meta;
|
import timely.api.model.Meta;
|
||||||
@@ -26,7 +27,7 @@ public class GetMetricTableSplitPoints {
|
|||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
|
|
||||||
try (ConfigurableApplicationContext ctx = new SpringApplicationBuilder(SpringBootstrap.class)
|
try (ConfigurableApplicationContext ctx = new SpringApplicationBuilder(SpringBootstrap.class)
|
||||||
.bannerMode(Mode.OFF).web(false).run(args)) {
|
.bannerMode(Mode.OFF).web(WebApplicationType.NONE).run(args)) {
|
||||||
Configuration conf = ctx.getBean(Configuration.class);
|
Configuration conf = ctx.getBean(Configuration.class);
|
||||||
|
|
||||||
final BaseConfiguration apacheConf = new BaseConfiguration();
|
final BaseConfiguration apacheConf = new BaseConfiguration();
|
||||||
|
|||||||
@@ -4,16 +4,22 @@ import static org.junit.Assert.assertEquals;
|
|||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.springframework.beans.factory.BeanCreationException;
|
import org.springframework.beans.factory.BeanCreationException;
|
||||||
import org.springframework.boot.test.util.EnvironmentTestUtils;
|
import org.springframework.boot.test.util.TestPropertyValues;
|
||||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||||
import timely.configuration.Configuration;
|
import timely.configuration.Configuration;
|
||||||
import timely.configuration.SpringBootstrap;
|
import timely.configuration.SpringBootstrap;
|
||||||
|
|
||||||
public class ConfigurationTest {
|
public class ConfigurationTest {
|
||||||
|
|
||||||
private final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
private AnnotationConfigApplicationContext context;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void createContext() {
|
||||||
|
context = new AnnotationConfigApplicationContext();
|
||||||
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void closeContext() {
|
public void closeContext() {
|
||||||
@@ -24,7 +30,7 @@ public class ConfigurationTest {
|
|||||||
public void testMinimalConfiguration() throws Exception {
|
public void testMinimalConfiguration() throws Exception {
|
||||||
context.register(SpringBootstrap.class);
|
context.register(SpringBootstrap.class);
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
EnvironmentTestUtils.addEnvironment(this.context,
|
TestPropertyValues.of(
|
||||||
"timely.server.ip:127.0.0.1",
|
"timely.server.ip:127.0.0.1",
|
||||||
"timely.server.tcp-port:54321",
|
"timely.server.tcp-port:54321",
|
||||||
"timely.server.udp-port:54325",
|
"timely.server.udp-port:54325",
|
||||||
@@ -38,7 +44,7 @@ public class ConfigurationTest {
|
|||||||
"timely.accumulo.password:secret",
|
"timely.accumulo.password:secret",
|
||||||
"timely.http.host:localhost",
|
"timely.http.host:localhost",
|
||||||
"timely.security.serverSsl.use-generated-keypair:true",
|
"timely.security.serverSsl.use-generated-keypair:true",
|
||||||
"timely.metric-age-off-days[default]:7");
|
"timely.metric-age-off-days[default]:7").applyTo(this.context);
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
context.refresh();
|
context.refresh();
|
||||||
Configuration config = this.context.getBean(Configuration.class);
|
Configuration config = this.context.getBean(Configuration.class);
|
||||||
@@ -62,7 +68,7 @@ public class ConfigurationTest {
|
|||||||
public void testMissingSSLProperty() throws Exception {
|
public void testMissingSSLProperty() throws Exception {
|
||||||
context.register(SpringBootstrap.class);
|
context.register(SpringBootstrap.class);
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
EnvironmentTestUtils.addEnvironment(this.context,
|
TestPropertyValues.of(
|
||||||
"timely.server.ip:127.0.0.1",
|
"timely.server.ip:127.0.0.1",
|
||||||
"timely.server.tcp-port:54321",
|
"timely.server.tcp-port:54321",
|
||||||
"timely.server.udp-port:54325",
|
"timely.server.udp-port:54325",
|
||||||
@@ -74,7 +80,7 @@ public class ConfigurationTest {
|
|||||||
"timely.accumulo.instance-name:test",
|
"timely.accumulo.instance-name:test",
|
||||||
"timely.accumulo.username:root",
|
"timely.accumulo.username:root",
|
||||||
"timely.accumulo.password:secret",
|
"timely.accumulo.password:secret",
|
||||||
"timely.http.host:localhost");
|
"timely.http.host:localhost").applyTo(this.context);
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
context.refresh();
|
context.refresh();
|
||||||
}
|
}
|
||||||
@@ -83,7 +89,7 @@ public class ConfigurationTest {
|
|||||||
public void testSSLProperty() throws Exception {
|
public void testSSLProperty() throws Exception {
|
||||||
context.register(SpringBootstrap.class);
|
context.register(SpringBootstrap.class);
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
EnvironmentTestUtils.addEnvironment(this.context,
|
TestPropertyValues.of(
|
||||||
"timely.server.ip:127.0.0.1",
|
"timely.server.ip:127.0.0.1",
|
||||||
"timely.server.tcp-port:54321",
|
"timely.server.tcp-port:54321",
|
||||||
"timely.server.udp-port:54325",
|
"timely.server.udp-port:54325",
|
||||||
@@ -97,7 +103,7 @@ public class ConfigurationTest {
|
|||||||
"timely.accumulo.password:secret",
|
"timely.accumulo.password:secret",
|
||||||
"timely.http.host:localhost",
|
"timely.http.host:localhost",
|
||||||
"timely.security.serverSsl.certificate-file:/tmp/foo",
|
"timely.security.serverSsl.certificate-file:/tmp/foo",
|
||||||
"timely.security.serverSsl.key-file:/tmp/bar");
|
"timely.security.serverSsl.key-file:/tmp/bar").applyTo(this.context);
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
context.refresh();
|
context.refresh();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,19 +30,19 @@ public class AuthCacheTest {
|
|||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
public static void after() {
|
public static void after() {
|
||||||
AuthCache.resetSessionMaxAge();
|
AuthCache.resetConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup() throws Exception {
|
public void setup() throws Exception {
|
||||||
AuthCache.setSessionMaxAge(config.getSecurity());
|
AuthCache.configure(config.getSecurity());
|
||||||
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("test", "test1");
|
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("test", "test1");
|
||||||
AuthenticationService.authenticate(token, cookie);
|
AuthenticationService.authenticate(token, cookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() throws Exception {
|
public void tearDown() throws Exception {
|
||||||
AuthCache.resetSessionMaxAge();
|
AuthCache.resetConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ public class AuthenticationServiceTest {
|
|||||||
public static void before() {
|
public static void before() {
|
||||||
config = TestConfiguration.createMinimalConfigurationForTest();
|
config = TestConfiguration.createMinimalConfigurationForTest();
|
||||||
config.getSecurity().setSessionMaxAge(Integer.MAX_VALUE);
|
config.getSecurity().setSessionMaxAge(Integer.MAX_VALUE);
|
||||||
AuthCache.resetSessionMaxAge();
|
AuthCache.resetConfiguration();
|
||||||
AuthCache.setSessionMaxAge(config.getSecurity());
|
AuthCache.configure(config.getSecurity());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@@ -34,7 +34,7 @@ public class AuthenticationServiceTest {
|
|||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
public static void after() {
|
public static void after() {
|
||||||
AuthCache.resetSessionMaxAge();
|
AuthCache.resetConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = BadCredentialsException.class)
|
@Test(expected = BadCredentialsException.class)
|
||||||
|
|||||||
@@ -70,14 +70,14 @@ public class HttpRequestDecoderTest {
|
|||||||
anonConfig = TestConfiguration.createMinimalConfigurationForTest();
|
anonConfig = TestConfiguration.createMinimalConfigurationForTest();
|
||||||
anonConfig.getSecurity().setAllowAnonymousHttpAccess(true);
|
anonConfig.getSecurity().setAllowAnonymousHttpAccess(true);
|
||||||
cookie = URLEncoder.encode(UUID.randomUUID().toString(), StandardCharsets.UTF_8.name());
|
cookie = URLEncoder.encode(UUID.randomUUID().toString(), StandardCharsets.UTF_8.name());
|
||||||
AuthCache.setSessionMaxAge(config.getSecurity());
|
AuthCache.configure(config.getSecurity());
|
||||||
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("test", "test1");
|
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("test", "test1");
|
||||||
AuthenticationService.authenticate(token, cookie);
|
AuthenticationService.authenticate(token, cookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
public static void after() {
|
public static void after() {
|
||||||
AuthCache.resetSessionMaxAge();
|
AuthCache.resetConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
|
|||||||
@@ -47,12 +47,12 @@ public class BasicAuthLoginRequestHandlerTest {
|
|||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void before() throws Exception {
|
public static void before() throws Exception {
|
||||||
AuthCache.setSessionMaxAge(TestConfiguration.createMinimalConfigurationForTest().getSecurity());
|
AuthCache.configure(TestConfiguration.createMinimalConfigurationForTest().getSecurity());
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
public static void after() throws Exception {
|
public static void after() throws Exception {
|
||||||
AuthCache.resetSessionMaxAge();
|
AuthCache.resetConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
|
|||||||
@@ -64,14 +64,14 @@ public class WebSocketRequestDecoderTest {
|
|||||||
anonConfig = TestConfiguration.createMinimalConfigurationForTest();
|
anonConfig = TestConfiguration.createMinimalConfigurationForTest();
|
||||||
anonConfig.getSecurity().setAllowAnonymousWsAccess(true);
|
anonConfig.getSecurity().setAllowAnonymousWsAccess(true);
|
||||||
cookie = URLEncoder.encode(UUID.randomUUID().toString(), StandardCharsets.UTF_8.name());
|
cookie = URLEncoder.encode(UUID.randomUUID().toString(), StandardCharsets.UTF_8.name());
|
||||||
AuthCache.setSessionMaxAge(config.getSecurity());
|
AuthCache.configure(config.getSecurity());
|
||||||
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("test", "test1");
|
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("test", "test1");
|
||||||
AuthCache.put(cookie, new TimelyPrincipal(TimelyUser.ANONYMOUS_USER));
|
AuthCache.put(cookie, new TimelyPrincipal(TimelyUser.ANONYMOUS_USER));
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
public static void after() {
|
public static void after() {
|
||||||
AuthCache.resetSessionMaxAge();
|
AuthCache.resetConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
|
|||||||
@@ -0,0 +1,241 @@
|
|||||||
|
package timely.store;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.SortedMap;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
import org.apache.accumulo.core.data.ByteSequence;
|
||||||
|
import org.apache.accumulo.core.data.Key;
|
||||||
|
import org.apache.accumulo.core.data.Range;
|
||||||
|
import org.apache.accumulo.core.data.Value;
|
||||||
|
import org.apache.accumulo.core.iterators.SortedKeyValueIterator;
|
||||||
|
import org.apache.accumulo.core.iterators.SortedMapIterator;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import timely.adapter.accumulo.MetaAdapter;
|
||||||
|
import timely.adapter.accumulo.MetricAdapter;
|
||||||
|
|
||||||
|
public class MetaAgeOffIteratorTest {
|
||||||
|
|
||||||
|
private static final Long TEST_TIME = System.currentTimeMillis() - 1000;
|
||||||
|
private static final Integer ONE_DAY = 86400000;
|
||||||
|
private static final Value EMPTY_VALUE = new Value(new byte[0]);
|
||||||
|
private static final Collection<ByteSequence> columnFamilies = new ArrayList<>();
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testDefaultMissing() throws Exception {
|
||||||
|
SortedMap<Key, Value> table = new TreeMap<>();
|
||||||
|
SortedKeyValueIterator<Key, Value> source = new SortedMapIterator(table);
|
||||||
|
MetaAgeOffIterator iter = new MetaAgeOffIterator();
|
||||||
|
HashMap<String, String> options = new HashMap<>();
|
||||||
|
iter.init(source, options, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefault() throws Exception {
|
||||||
|
SortedMap<Key, Value> table = new TreeMap<>();
|
||||||
|
table.put(MetaAdapter.createMetricKey("sys.cpu.user", TEST_TIME), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createMetricKey("sys.cpu.user", TEST_TIME + 1), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createMetricKey("sys.cpu.user", TEST_TIME + 2), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createMetricKey("sys.cpu.user", TEST_TIME + 3), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createMetricKey("sys.cpu.user", TEST_TIME + 4), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createMetricKey("sys.cpu.user", TEST_TIME + 5), new Value(new byte[0]));
|
||||||
|
|
||||||
|
SortedKeyValueIterator<Key, Value> source = new SortedMapIterator(table);
|
||||||
|
MetaAgeOffIterator iter = new MetaAgeOffIterator();
|
||||||
|
HashMap<String, String> options = new HashMap<>();
|
||||||
|
options.put(MetaAgeOffIterator.AGE_OFF_PREFIX + "default", Integer.toString(1 * ONE_DAY));
|
||||||
|
iter.init(source, options, null);
|
||||||
|
iter.seek(new Range(), columnFamilies, true);
|
||||||
|
int seen = 0;
|
||||||
|
while (iter.hasTop()) {
|
||||||
|
Key k = iter.getTopKey();
|
||||||
|
Assert.assertTrue(k.getTimestamp() >= TEST_TIME && k.getTimestamp() <= TEST_TIME + 5);
|
||||||
|
seen++;
|
||||||
|
iter.next();
|
||||||
|
}
|
||||||
|
Assert.assertEquals(6, seen);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMixed() throws Exception {
|
||||||
|
SortedMap<Key, Value> table = new TreeMap<>();
|
||||||
|
table.put(MetaAdapter.createMetricKey("sys.cpu.idle", TEST_TIME), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createMetricKey("sys.cpu.idle", TEST_TIME + 1), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createMetricKey("sys.cpu.idle", TEST_TIME + 2), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createMetricKey("sys.cpu.idle", TEST_TIME + 3), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createMetricKey("sys.cpu.idle", TEST_TIME + 4), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createMetricKey("sys.cpu.idle", TEST_TIME + 5), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createMetricKey("sys.cpu.user", TEST_TIME), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createMetricKey("sys.cpu.user", TEST_TIME + 1), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createMetricKey("sys.cpu.user", TEST_TIME + 2), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createMetricKey("sys.cpu.user", TEST_TIME + 3), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createMetricKey("sys.cpu.user", TEST_TIME + 4), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createMetricKey("sys.cpu.user", TEST_TIME + 5), new Value(new byte[0]));
|
||||||
|
|
||||||
|
SortedKeyValueIterator<Key, Value> source = new SortedMapIterator(table);
|
||||||
|
MetaAgeOffIterator iter = new MetaAgeOffIterator();
|
||||||
|
HashMap<String, String> options = new HashMap<>();
|
||||||
|
options.put(MetaAgeOffIterator.AGE_OFF_PREFIX + "default", Integer.toString(1 * ONE_DAY));
|
||||||
|
iter.init(source, options, null);
|
||||||
|
iter.seek(new Range(), columnFamilies, true);
|
||||||
|
int seen = 0;
|
||||||
|
while (iter.hasTop()) {
|
||||||
|
Key k = iter.getTopKey();
|
||||||
|
Assert.assertTrue(k.getTimestamp() >= TEST_TIME && k.getTimestamp() <= TEST_TIME + 5);
|
||||||
|
seen++;
|
||||||
|
iter.next();
|
||||||
|
}
|
||||||
|
Assert.assertEquals(12, seen);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAgeoffMixedMetricKey() throws Exception {
|
||||||
|
SortedMap<Key, Value> table = new TreeMap<>();
|
||||||
|
table.put(MetaAdapter.createMetricKey("sys.cpu.idle", TEST_TIME - (3 * ONE_DAY)), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createMetricKey("sys.cpu.idle", TEST_TIME - (2 * ONE_DAY)), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createMetricKey("sys.cpu.idle", TEST_TIME - (1 * ONE_DAY)), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createMetricKey("sys.cpu.idle", TEST_TIME), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createMetricKey("sys.cpu.idle", TEST_TIME + (1 * ONE_DAY)), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createMetricKey("sys.cpu.idle", TEST_TIME + (2 * ONE_DAY)), new Value(new byte[0]));
|
||||||
|
|
||||||
|
table.put(MetaAdapter.createMetricKey("sys.cpu.user", TEST_TIME - (3 * ONE_DAY)), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createMetricKey("sys.cpu.user", TEST_TIME - (2 * ONE_DAY)), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createMetricKey("sys.cpu.user", TEST_TIME - (1 * ONE_DAY)), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createMetricKey("sys.cpu.user", TEST_TIME), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createMetricKey("sys.cpu.user", TEST_TIME + (1 * ONE_DAY)), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createMetricKey("sys.cpu.user", TEST_TIME + (2 * ONE_DAY)), new Value(new byte[0]));
|
||||||
|
|
||||||
|
SortedKeyValueIterator<Key, Value> source = new SortedMapIterator(table);
|
||||||
|
MetaAgeOffIterator iter = new MetaAgeOffIterator();
|
||||||
|
HashMap<String, String> options = new HashMap<>();
|
||||||
|
options.put(MetaAgeOffIterator.AGE_OFF_PREFIX + "default", Integer.toString(1 * ONE_DAY));
|
||||||
|
options.put(MetaAgeOffIterator.AGE_OFF_PREFIX + "sys.cpu.user", Integer.toString(2 * ONE_DAY));
|
||||||
|
iter.init(source, options, null);
|
||||||
|
iter.seek(new Range(), columnFamilies, true);
|
||||||
|
int seen = 0;
|
||||||
|
while (iter.hasTop()) {
|
||||||
|
Key k = iter.getTopKey();
|
||||||
|
Assert.assertTrue(
|
||||||
|
k.getTimestamp() >= (TEST_TIME - (2 * ONE_DAY)) && k.getTimestamp() <= TEST_TIME + (2 * ONE_DAY));
|
||||||
|
seen++;
|
||||||
|
iter.next();
|
||||||
|
}
|
||||||
|
Assert.assertEquals(7, seen);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAgeoffMixedTagKey() throws Exception {
|
||||||
|
SortedMap<Key, Value> table = new TreeMap<>();
|
||||||
|
table.put(MetaAdapter.createTagKey("sys.cpu.idle", "tag1", TEST_TIME - (3 * ONE_DAY)), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createTagKey("sys.cpu.idle", "tag2", TEST_TIME - (2 * ONE_DAY)), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createTagKey("sys.cpu.idle", "tag3", TEST_TIME - (1 * ONE_DAY)), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createTagKey("sys.cpu.idle", "tag4", TEST_TIME), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createTagKey("sys.cpu.idle", "tag5", TEST_TIME + (1 * ONE_DAY)), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createTagKey("sys.cpu.idle", "tag6", TEST_TIME + (2 * ONE_DAY)), new Value(new byte[0]));
|
||||||
|
|
||||||
|
table.put(MetaAdapter.createTagKey("sys.cpu.user", "tag1", TEST_TIME - (3 * ONE_DAY)), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createTagKey("sys.cpu.user", "tag2", TEST_TIME - (2 * ONE_DAY)), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createTagKey("sys.cpu.user", "tag3", TEST_TIME - (1 * ONE_DAY)), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createTagKey("sys.cpu.user", "tag4", TEST_TIME), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createTagKey("sys.cpu.user", "tag5", TEST_TIME + (1 * ONE_DAY)), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createTagKey("sys.cpu.user", "tag6", TEST_TIME + (2 * ONE_DAY)), new Value(new byte[0]));
|
||||||
|
|
||||||
|
SortedKeyValueIterator<Key, Value> source = new SortedMapIterator(table);
|
||||||
|
MetaAgeOffIterator iter = new MetaAgeOffIterator();
|
||||||
|
HashMap<String, String> options = new HashMap<>();
|
||||||
|
options.put(MetaAgeOffIterator.AGE_OFF_PREFIX + "default", Integer.toString(1 * ONE_DAY));
|
||||||
|
options.put(MetaAgeOffIterator.AGE_OFF_PREFIX + "sys.cpu.user", Integer.toString(2 * ONE_DAY));
|
||||||
|
iter.init(source, options, null);
|
||||||
|
iter.seek(new Range(), columnFamilies, true);
|
||||||
|
int seen = 0;
|
||||||
|
while (iter.hasTop()) {
|
||||||
|
Key k = iter.getTopKey();
|
||||||
|
Assert.assertTrue(
|
||||||
|
k.getTimestamp() >= (TEST_TIME - (2 * ONE_DAY)) && k.getTimestamp() <= TEST_TIME + (2 * ONE_DAY));
|
||||||
|
seen++;
|
||||||
|
iter.next();
|
||||||
|
}
|
||||||
|
Assert.assertEquals(7, seen);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAgeoffMixedValueKey() throws Exception {
|
||||||
|
SortedMap<Key, Value> table = new TreeMap<>();
|
||||||
|
table.put(MetaAdapter.createValueKey("sys.cpu.idle", "tag1", "value1", TEST_TIME - (3 * ONE_DAY)),
|
||||||
|
new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createValueKey("sys.cpu.idle", "tag2", "value2", TEST_TIME - (2 * ONE_DAY)),
|
||||||
|
new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createValueKey("sys.cpu.idle", "tag3", "value3", TEST_TIME - (1 * ONE_DAY)),
|
||||||
|
new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createValueKey("sys.cpu.idle", "tag4", "value4", TEST_TIME), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createValueKey("sys.cpu.idle", "tag5", "value5", TEST_TIME + (1 * ONE_DAY)),
|
||||||
|
new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createValueKey("sys.cpu.idle", "tag6", "value6", TEST_TIME + (2 * ONE_DAY)),
|
||||||
|
new Value(new byte[0]));
|
||||||
|
|
||||||
|
table.put(MetaAdapter.createValueKey("sys.cpu.user", "tag1", "value1", TEST_TIME - (3 * ONE_DAY)),
|
||||||
|
new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createValueKey("sys.cpu.user", "tag2", "value2", TEST_TIME - (2 * ONE_DAY)),
|
||||||
|
new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createValueKey("sys.cpu.user", "tag3", "value3", TEST_TIME - (1 * ONE_DAY)),
|
||||||
|
new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createValueKey("sys.cpu.user", "tag4", "value4", TEST_TIME), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createValueKey("sys.cpu.user", "tag5", "value5", TEST_TIME + (1 * ONE_DAY)),
|
||||||
|
new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createValueKey("sys.cpu.user", "tag6", "value6", TEST_TIME + (2 * ONE_DAY)),
|
||||||
|
new Value(new byte[0]));
|
||||||
|
|
||||||
|
SortedKeyValueIterator<Key, Value> source = new SortedMapIterator(table);
|
||||||
|
MetaAgeOffIterator iter = new MetaAgeOffIterator();
|
||||||
|
HashMap<String, String> options = new HashMap<>();
|
||||||
|
options.put(MetaAgeOffIterator.AGE_OFF_PREFIX + "default", Integer.toString(1 * ONE_DAY));
|
||||||
|
options.put(MetaAgeOffIterator.AGE_OFF_PREFIX + "sys.cpu.user", Integer.toString(2 * ONE_DAY));
|
||||||
|
iter.init(source, options, null);
|
||||||
|
iter.seek(new Range(), columnFamilies, true);
|
||||||
|
int seen = 0;
|
||||||
|
while (iter.hasTop()) {
|
||||||
|
Key k = iter.getTopKey();
|
||||||
|
Assert.assertTrue(
|
||||||
|
k.getTimestamp() >= (TEST_TIME - (2 * ONE_DAY)) && k.getTimestamp() <= TEST_TIME + (2 * ONE_DAY));
|
||||||
|
seen++;
|
||||||
|
iter.next();
|
||||||
|
}
|
||||||
|
Assert.assertEquals(7, seen);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSeekPastEndKey() throws Exception {
|
||||||
|
SortedMap<Key, Value> table = new TreeMap<>();
|
||||||
|
table.put(MetaAdapter.createMetricKey("sys.cpu.user", TEST_TIME), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createMetricKey("sys.cpu.user", TEST_TIME + 1), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createMetricKey("sys.cpu.user", TEST_TIME + 2), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createMetricKey("sys.cpu.user", TEST_TIME + 3), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createMetricKey("sys.cpu.user", TEST_TIME + 4), new Value(new byte[0]));
|
||||||
|
table.put(MetaAdapter.createMetricKey("sys.cpu.user", TEST_TIME + 5), new Value(new byte[0]));
|
||||||
|
|
||||||
|
SortedKeyValueIterator<Key, Value> source = new SortedMapIterator(table);
|
||||||
|
MetaAgeOffIterator iter = new MetaAgeOffIterator();
|
||||||
|
HashMap<String, String> options = new HashMap<>();
|
||||||
|
options.put(MetaAgeOffIterator.AGE_OFF_PREFIX + "default", Integer.toString(1));
|
||||||
|
iter.init(source, options, null);
|
||||||
|
iter.seek(new Range(new Key("sys.cpu.user"), true,
|
||||||
|
new Key(MetricAdapter.encodeRowKey("sys.cpu.user", TEST_TIME + 3), new byte[0], new byte[0],
|
||||||
|
new byte[0], TEST_TIME + 3),
|
||||||
|
true), columnFamilies, true);
|
||||||
|
int seen = 0;
|
||||||
|
while (iter.hasTop()) {
|
||||||
|
Key k = iter.getTopKey();
|
||||||
|
Assert.assertTrue(k.getTimestamp() >= TEST_TIME && k.getTimestamp() <= TEST_TIME + 5);
|
||||||
|
seen++;
|
||||||
|
iter.next();
|
||||||
|
}
|
||||||
|
Assert.assertEquals(0, seen);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -41,6 +41,11 @@ public class CaptureChannelHandlerContext implements ChannelHandlerContext {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> boolean hasAttr(AttributeKey<T> attributeKey) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Channel channel() {
|
public Channel channel() {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ public class OneWaySSLBase extends QueryBase {
|
|||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() throws Exception {
|
public void tearDown() throws Exception {
|
||||||
AuthCache.resetSessionMaxAge();
|
AuthCache.resetConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -87,7 +87,7 @@ public class OneWaySSLBasicAuthAccessIT extends OneWaySSLBase {
|
|||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() throws Exception {
|
public void tearDown() throws Exception {
|
||||||
AuthCache.resetSessionMaxAge();
|
AuthCache.resetConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ public class TwoWaySSLFailureIT extends QueryBase {
|
|||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() throws Exception {
|
public void tearDown() throws Exception {
|
||||||
AuthCache.resetSessionMaxAge();
|
AuthCache.resetConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = UnauthorizedUserException.class)
|
@Test(expected = UnauthorizedUserException.class)
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ public class TwoWaySSLIT extends QueryBase {
|
|||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() throws Exception {
|
public void tearDown() throws Exception {
|
||||||
AuthCache.resetSessionMaxAge();
|
AuthCache.resetConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ public class TwoWaySSLOpenSSLIT extends QueryBase {
|
|||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() throws Exception {
|
public void tearDown() throws Exception {
|
||||||
AuthCache.resetSessionMaxAge();
|
AuthCache.resetConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ public class TcpClientIT extends OneWaySSLBase {
|
|||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() throws Exception {
|
public void tearDown() throws Exception {
|
||||||
AuthCache.resetSessionMaxAge();
|
AuthCache.resetConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ public class UdpClientIT extends MacITBase {
|
|||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() throws Exception {
|
public void tearDown() throws Exception {
|
||||||
AuthCache.resetSessionMaxAge();
|
AuthCache.resetConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ public class WebSocketClientIT extends OneWaySSLBase {
|
|||||||
@After
|
@After
|
||||||
public void tearDown() throws Exception {
|
public void tearDown() throws Exception {
|
||||||
s.shutdown();
|
s.shutdown();
|
||||||
AuthCache.resetSessionMaxAge();
|
AuthCache.resetConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testWorkflow(WebSocketSubscriptionClient client) throws Exception {
|
public void testWorkflow(WebSocketSubscriptionClient client) throws Exception {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import static org.junit.Assert.assertTrue;
|
|||||||
|
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
@@ -20,6 +21,13 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||||||
|
|
||||||
import javax.net.ssl.HttpsURLConnection;
|
import javax.net.ssl.HttpsURLConnection;
|
||||||
|
|
||||||
|
import com.google.common.collect.HashMultimap;
|
||||||
|
import com.google.common.collect.Multimap;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
import org.jsoup.Jsoup;
|
import org.jsoup.Jsoup;
|
||||||
import org.jsoup.nodes.Document;
|
import org.jsoup.nodes.Document;
|
||||||
import org.jsoup.select.Elements;
|
import org.jsoup.select.Elements;
|
||||||
@@ -130,12 +138,44 @@ public class HttpApiIT extends OneWaySSLBase {
|
|||||||
String metrics = "https://localhost:54322/api/metrics";
|
String metrics = "https://localhost:54322/api/metrics";
|
||||||
// Test prefix matching
|
// Test prefix matching
|
||||||
String result = query(metrics, "application/json");
|
String result = query(metrics, "application/json");
|
||||||
assertEquals(expected, result);
|
|
||||||
|
Gson gson = new Gson();
|
||||||
|
JsonObject response = gson.fromJson(result, JsonObject.class);
|
||||||
|
JsonArray metricsArray = response.getAsJsonArray("metrics");
|
||||||
|
assertEquals(3, metricsArray.size());
|
||||||
|
|
||||||
|
for (JsonElement e : metricsArray) {
|
||||||
|
JsonObject metricObject = e.getAsJsonObject();
|
||||||
|
String metricName = metricObject.get("metric").getAsString();
|
||||||
|
Iterator<JsonElement> tagItr = metricObject.get("tags").getAsJsonArray().iterator();
|
||||||
|
Multimap<String, String> tagMap = HashMultimap.create();
|
||||||
|
while (tagItr.hasNext()) {
|
||||||
|
JsonObject tagObject = tagItr.next().getAsJsonObject();
|
||||||
|
tagMap.put(tagObject.get("key").getAsString(), tagObject.get("value").getAsString());
|
||||||
|
}
|
||||||
|
switch (metricName) {
|
||||||
|
case "sys.cpu.user":
|
||||||
|
assertEquals(2, tagMap.size());
|
||||||
|
assertTrue(CollectionUtils.isEqualCollection(Arrays.asList("value1"), tagMap.get("tag1")));
|
||||||
|
assertTrue(CollectionUtils.isEqualCollection(Arrays.asList("value2"), tagMap.get("tag2")));
|
||||||
|
break;
|
||||||
|
case "sys.cpu.idle":
|
||||||
|
assertEquals(2, tagMap.size());
|
||||||
|
assertTrue(CollectionUtils.isEqualCollection(Arrays.asList("value3"), tagMap.get("tag3")));
|
||||||
|
assertTrue(CollectionUtils.isEqualCollection(Arrays.asList("value4"), tagMap.get("tag4")));
|
||||||
|
break;
|
||||||
|
case "zzzz":
|
||||||
|
assertEquals(1, tagMap.size());
|
||||||
|
assertTrue(CollectionUtils.isEqualCollection(Arrays.asList("localhost"), tagMap.get("host")));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
} finally {
|
} finally {
|
||||||
s.shutdown();
|
s.shutdown();
|
||||||
}
|
}
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ public class TimelyTcpIT extends MacITBase {
|
|||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() throws Exception {
|
public void tearDown() throws Exception {
|
||||||
AuthCache.resetSessionMaxAge();
|
AuthCache.resetConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ public class TimelyUdpIT extends MacITBase {
|
|||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() throws Exception {
|
public void tearDown() throws Exception {
|
||||||
AuthCache.resetSessionMaxAge();
|
AuthCache.resetConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ public class WebSocketIT extends OneWaySSLBase {
|
|||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
public static void after() {
|
public static void after() {
|
||||||
AuthCache.resetSessionMaxAge();
|
AuthCache.resetConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
private EventLoopGroup group = null;
|
private EventLoopGroup group = null;
|
||||||
|
|||||||
@@ -46,9 +46,12 @@
|
|||||||
<user-service id="usernamePasswordService">
|
<user-service id="usernamePasswordService">
|
||||||
<user name="test" password="test1" authorities="A,B,C" />
|
<user name="test" password="test1" authorities="A,B,C" />
|
||||||
</user-service>
|
</user-service>
|
||||||
|
|
||||||
|
<beans:bean id="noOpPasswordEncoder" class="org.springframework.security.crypto.password.NoOpPasswordEncoder"/>
|
||||||
|
|
||||||
<beans:bean id="UserNamePasswordAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
|
<beans:bean id="UserNamePasswordAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
|
||||||
<beans:property name="userDetailsService" ref="usernamePasswordService"/>
|
<beans:property name="userDetailsService" ref="usernamePasswordService"/>
|
||||||
|
<beans:property name="passwordEncoder" ref="noOpPasswordEncoder"/>
|
||||||
</beans:bean>
|
</beans:bean>
|
||||||
|
|
||||||
<authentication-manager alias="authenticationManager">
|
<authentication-manager alias="authenticationManager">
|
||||||
@@ -57,5 +60,12 @@
|
|||||||
<!-- Basic authorization provider, validates provided username and password -->
|
<!-- Basic authorization provider, validates provided username and password -->
|
||||||
<authentication-provider ref="UserNamePasswordAuthenticationProvider" />
|
<authentication-provider ref="UserNamePasswordAuthenticationProvider" />
|
||||||
</authentication-manager>
|
</authentication-manager>
|
||||||
|
|
||||||
|
<util:list id="requiredRoles" value-type="java.lang.String">
|
||||||
|
<beans:value></beans:value>
|
||||||
|
</util:list>
|
||||||
|
|
||||||
|
<util:list id="requiredAuths" value-type="java.lang.String">
|
||||||
|
<beans:value></beans:value>
|
||||||
|
</util:list>
|
||||||
</beans:beans>
|
</beans:beans>
|
||||||
Reference in new issue
Block a user