Compare commits

...
Author SHA1 Message Date
Bill Oley f582e8ca1c Refactor SSL integration tests to use the same SslContext when possible 2020-04-27 09:29:05 -04:00
Bill Oley 6972c46a1a Merge pull request #204 from matthpeterson/issue-203
Closes issue #203 - avoid calling sizeInBytes if it's not expected to exist
2020-04-17 10:42:14 -04:00
matthpeterson 4d7bee7566 Closes issue #203 - Stop calling sizeInBytes if it's not expected to exist 2020-04-16 15:17:08 +00:00
Bill Oley 4b11b3d50f closes #201 Create balancer assignment file if it doesn't exist 2020-04-10 10:27:18 -04:00
Bill Oley 84b9856ec7 closes #199 Fix issues when using a generated (self-signed) server certificate 2020-04-10 10:20:25 -04:00
Bill Oley c9548a9b9a closes #198 Cleaner shutdown of resources for Timely server, balancer, grafana-auth 2020-04-10 10:01:47 -04:00
Bill Oley aec7bd4c38 closes #197 Fix scan range in MetaCacheImpl 2020-04-09 11:58:30 -04:00
Bill Oley e2b8b643f2 closes #200 Initialize non-cached metrics stored in zookeeper 2020-04-09 11:32:03 -04:00
Bill Oley c19e1d8b2f Set version to 1.2.8-SNAPSHOT 2020-02-21 10:20:48 -05:00
Bill Oley cb73e8e75c Tag version 1.2.7 2020-02-21 10:02:31 -05:00
Bill Oley 4c508c1a7d Merge pull request #195 from dtspence/TIMELY-194
Fixed #194, corrected datasource.js to look at wildcard tag value
2020-02-21 08:55:37 -05:00
dtspence c6c2c0d41e Fixed #194, corrected datasource.js to look at wildcard tag value
The alignment process to match tag query results with the metric definition
looks at the tag k/v pairs and makes a special consideration for a wildcard tag value.
The change adjusts datasource.js so that the '.*' notation will match a query result
and metric definition.
2020-02-05 19:56:03 +00:00
Christopher Tubbs a24cef78b3 Update jackson (multiple CVEs) (#193) 2019-11-07 23:05:38 -05:00
Bill Oley 08f99f6540 Set version to 1.2.7-SNAPSHOT 2019-10-30 12:33:49 -04:00
Bill Oley be11da7d15 Tag version 1.2.6 2019-10-30 12:30:33 -04:00
Bill Oley 4bc5753328 fixes #192 Thread deadlock in BalancedMetricResolver 2019-10-29 12:24:10 -04:00
Bill Oley 2ccb89a06d re #191 Correct typos 2019-10-18 09:16:03 -04:00
Bill Oley dfc3840ad4 re #191 Delete cached metrics when they are no longer being updated 2019-10-17 17:49:24 -04:00
Bill Oley 6235034d8b BalancedMetricResolver in the non-leader balancer should read assignments at intervals 2019-10-17 17:49:24 -04:00
Bill Oley d8de6ac06f Make zookeeper timeout configurable 2019-10-17 17:49:24 -04:00
Christopher Tubbs f381946abe Merge pull request #190 from NationalSecurityAgency/dependabot/maven/io.netty-netty-all-4.1.42.Final
Bump netty-all from 4.1.35.Final to 4.1.42.Final
2019-10-17 09:35:13 -07:00
dependabot[bot] 48bbe6a561 Bump netty-all from 4.1.35.Final to 4.1.42.Final
Bumps [netty-all](https://github.com/netty/netty) from 4.1.35.Final to 4.1.42.Final.
- [Release notes](https://github.com/netty/netty/releases)
- [Commits](https://github.com/netty/netty/compare/netty-4.1.35.Final...netty-4.1.42.Final)

Signed-off-by: dependabot[bot] <[email protected]>
2019-10-11 18:58:57 +00:00
Christopher Tubbs 0f6110ea59 Update jackson to 2.9.10
* Fix CVE-2019-16335
* Fix CVE-2019-14540
2019-09-26 23:40:08 -04:00
Bill Oley 461b5e7e78 Pom version to 1.2.6-SNAPSHOT 2019-08-22 12:17:45 -04:00
Bill Oley 4ec7d94c28 Tag version 1.2.5 2019-08-22 12:12:49 -04:00
Bill Oley 77e910ae06 Use BufferedOutputWriter instead of PrintWriter, pass Exceptions instead of having to check error condition 2019-08-22 10:09:27 -04:00
Bill Oley a4998af887 Move jackson-databind version to the properties section 2019-08-08 13:50:08 -04:00
Bill Oley 2858549ede Merge pull request #188 from ctubbsii/jdk11-build-support
Bump versions / permit build with JDK11
2019-08-08 13:35:54 -04:00
Christopher Tubbs 57f0e1408f Bump versions / permit build with JDK11
Support builds on newer JDKs
* Update tycho-compiler-jdt in analytics/pom.xml
* Ignore spotbugs RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE for JDK11
* Bump several plugins to latest (maven-compiler-plugin, etc.)
* Add profile for using -release flag in Eclipse and with JDK9 and later

Fix recently added warnings
* Add try-with-resources for TreeCache objects to remove recent warnings
* Suppress generics warning in MetricCompactionStrategy
* Update TabletMetadataConsole to avoid deprecated Accumulo
  ClientConfiguration API

Other
* Bump jackson-databind for CVE-2019-14439, CVE-2019-14379
* Bump hadoop 2.7 to latest in series (2.7.3->2.7.7) for build
2019-08-06 16:14:13 -04:00
Bill Oley 151adc025a Merge pull request #185 from dtspence/TIMELY-184
Fixes #184 Metric compaction strategy and summary implementation
2019-08-06 15:15:45 -04:00
Bill Oley 5abd9d7200 closes #187 Prevent deadlock in balancer distributed read/write lock 2019-08-06 12:00:57 -04:00
Bill Oley fa2e211c72 closes #186 Make balancer websocket incomingBufferSize its own setting 2019-08-06 11:47:36 -04:00
dtspence a21438976e TIMELY-184: Metric compaction strategy and summary implementation. 2019-08-01 14:05:35 +00:00
Christopher Tubbs ba37047f3a Update jackson-databind (CVE-2019-12814) 2019-07-23 20:38:46 -04:00
Bill Oley 9305470c69 re #183 Check tagKey not null or blank in Meta.toKeys 2019-06-19 09:08:20 -04:00
Bill Oley 200233542e re #183 Add timestamps to meta tags to allow proper age off 2019-06-17 15:36:51 -04:00
Bill Oley 3cd78dc624 Merge branch 'drewfarris-config-fixup' 2019-05-30 12:55:35 -04:00
Bill Oley 9027cac6e3 Merge branch 'config-fixup' of https://github.com/drewfarris/timely into drewfarris-config-fixup
Conflicts:
	server/conf/timely-docker.yml
	server/conf/timely-standalone.yml
	server/conf/timely.yml
2019-05-30 12:50:31 -04:00
Bill Oley 3663b4e6d5 Pom version to 1.2.5-SNAPSHOT 2019-05-30 12:38:42 -04:00
Bill Oley 64973d0674 Merge pull request #182 from ctubbsii/fix-jackson
Fix most compiler warnings and many other issues
2019-05-30 12:25:00 -04:00
Christopher Tubbs 5512306f1e Fix most compiler warnings and other issues
Build:
* Fix netty-tcnative-boringssl-static version specs in pom.xml
* Bump the version of accumulo to latest release (1.9.3)
* Update jackson to 2.9.9 (CVE-2019-12086)

Warnings:
* Remove all use of deprecated netty stuffs and use their replacements
  in the netty APIs
* Add missing generics parameters everywhere
* Remove unused code (unused private fields, such as loggers)
* Remove standard javadoc tags that were missing descriptions (these do
  not add value to the javadocs, since default `@param`, `@throws`, and
  `@return` tags are provided by javadoc)
* Add missing serialVersionUid fields to Serializable classes
* Fix a resource leak warning about not closing ScannerOptionsHelper
  (NOTE: 3 other resource leak warnings exist, but did not attempt to
  fix those, since they were non-trivial)
* Remove deprecated annotations from hibernate and replace with their
  non-deprecated counterparts from javax.validation.constraints
* Remove use of deprecated Accumulo ClientConfiguration constructor

Other:
* Use lowercase header names, for HTTP/2 compatibility (and to stop
  incorrectly assuming case-sensitivity for HTTP/1)
* Narrow the scope of necessary `@SuppressWarnings("unchecked")`
  occurrences to a variable instead of suppressing for an entire method or
  an entire class
* IDE added some missing overrides annotations
* (style consistency) Replace tabs with spaces
* Set log4j2 root logger level for tests to WARN instead of TRACE
2019-05-24 12:12:00 -04:00
Bill Oley d35ae08188 Tag version 1.2.4 2019-05-22 17:14:42 -04:00
Bill Oley e448657afb Correction in websocket pipeline from Netty API change 2019-05-22 16:49:11 -04:00
Bill Oley 1dcd893711 Add age off iterator for meta table
Add age off iterator for meta table
2019-05-22 16:49:08 -04:00
Bill Oley 409f5375d1 Change pom version to 1.2.4-SNAPSHOT 2019-05-22 16:48:09 -04:00
Bill Oley c5df8139ca Correct typos 2019-05-22 16:46:55 -04:00
Bill Oley dcd23d4fc8 fixes #181 Use netty-tcnative-boringssl-static 2019-04-18 11:56:54 -04:00
Bill Oley cc7326fc19 Tag version 1.2.2 2019-04-12 11:24:53 -04:00
Bill Oley 0d2b6e61c8 Correct typos 2019-04-12 10:55:04 -04:00
Bill Oley b7cd8765c5 Correct typos and minor changed to MetaCache 2019-04-10 15:31:53 -04:00
Bill Oley a9d27d5b1b re #179 Allow AuthCache to periodically refresh credentials, cache meta table up to a configurable number of tags 2019-04-08 10:50:14 -04:00
Bill Oley 7d67eb218d Update netty-tcnative dependency, remove x509 principal extractor from AuthenticationService 2019-04-03 15:52:19 -04:00
Bill Oley 5fb7e3dd2b Bump pom versions to 1.2.2-SNAPSHOT 2019-04-02 12:57:22 -04:00
Bill Oley 5a89861cec Tag version 1.2.1 2019-04-02 12:53:04 -04:00
Bill Oley a3bc9a01ee Update dependency versions
Update dependencies

Update dependency versions
2019-04-02 12:43:36 -04:00
Bill Oley dba494b06d Bump pom versions to 1.2.1-SNAPSHOT 2019-02-25 15:41:31 -05:00
Bill Oley 5826884c3c Tag version 1.2.0 2019-02-25 15:11:57 -05:00
Bill Oley f94c731bbc Remove unused modeule directory introduced in error 2019-02-25 15:11:12 -05:00
Bill Oley 4e042bcb94 Allow all http methods in grafana auth proxy, disable cookies in HttpPools 2019-02-22 16:17:36 -05:00
Bill Oley ab7f499134 closes #180 Allow a configurable default visibility for metrics 2019-02-21 15:33:25 -05:00
Bill Oley c098f9f3e4 re #179 Create an authentication app for grafana that uses Timely's authentication framework and grafana's auth-proxy
re #179 Create authentication proxy for grafana
2019-02-21 13:28:12 -05:00
Bill Oley 94cfcbc0c4 Allow authentication provider to return roles, check users against requiredRoles and requiredAuths for access
index on master: 674e1b0 Improve synchronization in AuthenticationService

Check validated users against requiredRoles and requiredAuths

Conflicts:
	server/conf/security.xml
	server/src/main/java/timely/auth/AuthenticationService.java

More work
2019-02-15 11:24:07 -05:00
Bill Oley 674e1b0ab5 Improve synchronization in AuthenticationService 2019-02-08 16:53:35 -05:00
Bill Oley 4eedb84547 Add synchronization to AuthenticationService to prevent simultaneous authentications of the same user by multiple threads 2019-02-07 17:17:08 -05:00
Bill Oley bef86eac37 Fix websocket proxying with a client certificate, add ability to configure client cert in python websocket client 2019-02-07 17:16:00 -05:00
Bill Oley 27bb16e403 re #178 Allow proxying of http and ws requests
More work

Correct errors
2019-02-05 11:41:35 -05:00
Drew Farris e2bbe4e212 Minor fix-ups to default configuration files.
. Replace ssl with server-ssl in timely-standalone.yml and timely-docker.yml
  . Add a value for cache.enabled in timely.yml
2019-01-19 22:07:07 -05:00
Bill Oley a95d2d7ea1 Bump pom versions to 1.1.1-SNAPSHOT 2019-01-16 13:55:47 -05:00
Bill Oley f751175190 Update version of collectd 2019-01-16 13:53:43 -05:00
Bill Oley 648199f330 Revert "Bump pom versions to 1.1.1-SNAPSHOT"
This reverts commit fcfc3f7bc0.
2019-01-16 13:53:26 -05:00
Bill Oley fcfc3f7bc0 Bump pom versions to 1.1.1-SNAPSHOT 2019-01-15 15:48:43 -05:00
Bill Oley 200bc39902 Tag version 1.1.0
Grafana app changes
2019-01-15 15:47:14 -05:00
Bill Oley f854c9e394 Add build profile for python client package 2019-01-15 15:08:30 -05:00
Bill Oley 73ddae1f8d Correct typo 2019-01-15 14:15:20 -05:00
Bill Oley cb7184503e Allow Websocket requests to take advantage of cached metrics 2019-01-11 16:12:26 -05:00
Christopher Tubbs 3a357b50ef Update jackson-databind dependency
(Also, remove redundant test dependency on timely-common.)
2019-01-10 16:07:25 -05:00
Bill Oley 689aee0678 Fix deadlock in tracking nonCachedMetrics across processes 2019-01-08 12:06:41 -05:00
Bill Oley 32f6b9d4c9 Include the bin/testing directory in assembly 2019-01-04 11:14:11 -05:00
Bill Oley 512f318387 Add distributed tracking of nonCachedMetrics between the timely servers and the balancers
work in progress

Add distributed tracking of noncachedMetrics
2019-01-04 11:04:24 -05:00
226 changed files with 9825 additions and 2226 deletions

No files matched your search

+3 -2
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>timely</groupId> <groupId>timely</groupId>
<artifactId>timely</artifactId> <artifactId>timely</artifactId>
<version>1.0.1-SNAPSHOT</version> <version>1.2.8-SNAPSHOT</version>
</parent> </parent>
<artifactId>timely-analytics</artifactId> <artifactId>timely-analytics</artifactId>
<!-- <!--
@@ -68,6 +68,7 @@
<pluginManagement> <pluginManagement>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<configuration> <configuration>
<compilerId>jdt</compilerId> <compilerId>jdt</compilerId>
@@ -76,7 +77,7 @@
<dependency> <dependency>
<groupId>org.eclipse.tycho</groupId> <groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-compiler-jdt</artifactId> <artifactId>tycho-compiler-jdt</artifactId>
<version>0.21.0</version> <version>1.4.0</version>
</dependency> </dependency>
</dependencies> </dependencies>
</plugin> </plugin>
@@ -66,7 +66,7 @@ public class SubscriptionSource extends RichSourceFunction<MetricResponse> imple
LOG.info("Opening summarization job."); LOG.info("Opening summarization job.");
super.open(parameters); super.open(parameters);
client = new WebSocketSubscriptionClient(jp.getTimelyHostname(), jp.getTimelyHttpsPort(), jp.getTimelyWssPort(), client = new WebSocketSubscriptionClient(jp.getTimelyHostname(), jp.getTimelyHttpsPort(), jp.getTimelyWssPort(),
jp.isDoLogin(), jp.getTimelyUsername(), jp.getTimelyPassword(), jp.getKeyStoreFile(), jp.isClientAuth(), jp.isDoLogin(), jp.getTimelyUsername(), jp.getTimelyPassword(), jp.getKeyStoreFile(),
jp.getKeyStoreType(), jp.getKeyStorePass(), jp.getTrustStoreFile(), jp.getTrustStoreType(), jp.getKeyStoreType(), jp.getKeyStorePass(), jp.getTrustStoreFile(), jp.getTrustStoreType(),
jp.getTrustStorePass(), jp.isHostVerificationEnabled(), jp.getBufferSize()); jp.getTrustStorePass(), jp.isHostVerificationEnabled(), jp.getBufferSize());
} }
@@ -96,7 +96,7 @@ public class SubscriptionSource extends RichSourceFunction<MetricResponse> imple
@Override @Override
public void onOpen(Session session, EndpointConfig config) { public void onOpen(Session session, EndpointConfig config) {
session.addMessageHandler(new MessageHandler.Whole<String>() { session.addMessageHandler(String.class, new MessageHandler.Whole<String>() {
private final SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd-HH"); private final SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd-HH");
private long lastWatermarkTime = 0L; private long lastWatermarkTime = 0L;
@@ -67,7 +67,7 @@ public class SummarizationJob {
}) })
.name("Metric Windows") .name("Metric Windows")
.addSink( .addSink(
new SocketClientSink<MetricHistogram>(jp.getTimelyHostname(), jp.getTimelyTcpPort(), new SocketClientSink<MetricHistogram>(jp.getTimelyHostname(), jp.getTimelyTcpPort(),
new MetricHistogram(), 2, true) { new MetricHistogram(), 2, true) {
private final Logger LOG = LoggerFactory private final Logger LOG = LoggerFactory
@@ -18,6 +18,7 @@ public class SummarizationJobParameters extends GlobalJobParameters implements S
private final int timelyTcpPort; private final int timelyTcpPort;
private final int timelyHttpsPort; private final int timelyHttpsPort;
private final int timelyWssPort; private final int timelyWssPort;
private final boolean clientAuth;
private final boolean doLogin; private final boolean doLogin;
private final String timelyUsername; private final String timelyUsername;
private final String timelyPassword; private final String timelyPassword;
@@ -40,6 +41,7 @@ public class SummarizationJobParameters extends GlobalJobParameters implements S
timelyTcpPort = params.getInt("timelyTcpPort", 4241); timelyTcpPort = params.getInt("timelyTcpPort", 4241);
timelyHttpsPort = params.getInt("timelyHttpsPort", 4242); timelyHttpsPort = params.getInt("timelyHttpsPort", 4242);
timelyWssPort = params.getInt("timelyWssPort", 4243); timelyWssPort = params.getInt("timelyWssPort", 4243);
clientAuth = params.getBoolean("clientAuth", false);
doLogin = params.getBoolean("doLogin", false); doLogin = params.getBoolean("doLogin", false);
timelyUsername = params.get("timelyUsername", null); timelyUsername = params.get("timelyUsername", null);
timelyPassword = params.get("timelyPassword", null); timelyPassword = params.get("timelyPassword", null);
@@ -69,6 +71,7 @@ public class SummarizationJobParameters extends GlobalJobParameters implements S
timelyTcpPort = conf.getInteger("timelyTcpPort", 4241); timelyTcpPort = conf.getInteger("timelyTcpPort", 4241);
timelyHttpsPort = conf.getInteger("timelyHttpsPort", 4242); timelyHttpsPort = conf.getInteger("timelyHttpsPort", 4242);
timelyWssPort = conf.getInteger("timelyWssPort", 4243); timelyWssPort = conf.getInteger("timelyWssPort", 4243);
clientAuth = conf.getBoolean("clientAuth", false);
doLogin = conf.getBoolean("doLogin", false); doLogin = conf.getBoolean("doLogin", false);
timelyUsername = conf.getString("timelyUsername", null); timelyUsername = conf.getString("timelyUsername", null);
timelyPassword = conf.getString("timelyPassword", null); timelyPassword = conf.getString("timelyPassword", null);
@@ -112,6 +115,10 @@ public class SummarizationJobParameters extends GlobalJobParameters implements S
return timelyWssPort; return timelyWssPort;
} }
public boolean isClientAuth() {
return clientAuth;
}
public boolean isDoLogin() { public boolean isDoLogin() {
return doLogin; return doLogin;
} }
+6 -10
View File
@@ -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`
@@ -8,11 +8,6 @@ else
TCNATIVE_SUFFIX="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,14 +27,15 @@ 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.${NATIVE_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.${NATIVE_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="-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} -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"
echo "$JAVA_HOME/bin/java ${JVM_ARGS} timely.balancer.Balancer --spring.config.name=timely --spring.profiles.active=balancer" echo "$JAVA_HOME/bin/java ${JVM_ARGS} timely.balancer.Balancer --spring.config.name=timely --spring.profiles.active=balancer"
+3
View File
@@ -22,5 +22,8 @@
<AppenderRef ref="Console" level="warn" /> <AppenderRef ref="Console" level="warn" />
<AppenderRef ref="Rolling" level="trace" /> <AppenderRef ref="Rolling" level="trace" />
</Root> </Root>
<Logger name="timely" additivity="false">
<AppenderRef ref="Console" level="debug" />
</Logger>
</Loggers> </Loggers>
</Configuration> </Configuration>
+39 -17
View File
@@ -11,21 +11,33 @@
<beans:bean id="x509PrincipalExtractor" class="org.springframework.security.web.authentication.preauth.x509.SubjectDnX509PrincipalExtractor"> <beans:bean id="x509PrincipalExtractor" class="org.springframework.security.web.authentication.preauth.x509.SubjectDnX509PrincipalExtractor">
<beans:property name="subjectDnRegex" value="CN=(.*?)(?:,|$)"/> <beans:property name="subjectDnRegex" value="CN=(.*?)(?:,|$)"/>
</beans:bean> </beans:bean>
<beans:bean id="timelyUserDetailsService" class="timely.auth.UserDetailsService"> <beans:bean id="timelyUserDetailsService" class="timely.auth.UserDetailsService">
<beans:property name="users"> <beans:property name="users">
<util:map key-type="java.lang.String"> <util:list>
<beans:entry key="example.com"> <beans:bean class="timely.auth.TimelyUser">
<util:list> <beans:constructor-arg name="subjectDn">
<beans:value>D</beans:value> <beans:value>CN=example.com</beans:value>
<beans:value>E</beans:value> </beans:constructor-arg>
<beans:value>F</beans:value> <beans:constructor-arg name="auths">
</util:list> <util:list>
</beans:entry> <beans:value>D</beans:value>
</util:map> <beans:value>E</beans:value>
</beans:property> <beans:value>F</beans:value>
</beans:bean> </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:bean id="x509AuthorizationProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<beans:property name="preAuthenticatedUserDetailsService" ref="timelyUserDetailsService" /> <beans:property name="preAuthenticatedUserDetailsService" ref="timelyUserDetailsService" />
@@ -34,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">
@@ -45,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 -4
View File
@@ -8,12 +8,15 @@ timely-balancer:
assignmentFile: /timely/balancer/assignments.csv assignmentFile: /timely/balancer/assignments.csv
security: security:
session-max-age: 86400 session-max-age: 86400
allow-anonymous-access: true cache-expiration-minutes: 60
cache-refresh-minutes: 5
allow-anonymous-http-access: true
allow-anonymous-ws-access: true
serverSsl: serverSsl:
certificate-file: certificate-file:
key-file: key-file:
#key-password: #key-password:
use-generated-keypair: false use-generated-keypair: true
trust-store-file: trust-store-file:
use-openssl: true use-openssl: true
#use-ciphers: #use-ciphers:
@@ -31,7 +34,7 @@ timely-balancer:
nonCachedMetrics: nonCachedMetrics:
server: server:
ip: 127.0.0.1 ip: 127.0.0.1
tcp-port: 4242 tcpPort: 4242
numTcpPools: 1 numTcpPools: 1
tcpBufferSize: 100 tcpBufferSize: 100
tcpClientPool: tcpClientPool:
@@ -51,7 +54,7 @@ timely-balancer:
jmxEnabled: true jmxEnabled: true
testOnBorrow: false testOnBorrow: false
testOnReturn: false testOnReturn: false
udp-port: 4245 udpPort: 4245
udpClientPool: udpClientPool:
minIdlePerKey: 0 minIdlePerKey: 0
maxIdlePerKey: 40 maxIdlePerKey: 40
@@ -108,6 +111,7 @@ timely-balancer:
flush-interval-seconds: 30 flush-interval-seconds: 30
scanner-read-ahead: 1 scanner-read-ahead: 1
subscription-batch-size: 5000 subscription-batch-size: 5000
incomingBufferSize: 50000000
wsClientPool: wsClientPool:
minIdlePerKey: 0 minIdlePerKey: 0
maxIdlePerKey: 40 maxIdlePerKey: 40
+3 -44
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>timely</groupId> <groupId>timely</groupId>
<artifactId>timely</artifactId> <artifactId>timely</artifactId>
<version>1.0.1-SNAPSHOT</version> <version>1.2.8-SNAPSHOT</version>
</parent> </parent>
<artifactId>timely-balancer</artifactId> <artifactId>timely-balancer</artifactId>
<dependencies> <dependencies>
@@ -161,9 +161,7 @@
</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>
<classifier>${os.detected.classifier}</classifier>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<dependency> <dependency>
@@ -201,11 +199,6 @@
<artifactId>spring-boot-test-autoconfigure</artifactId> <artifactId>spring-boot-test-autoconfigure</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>timely</groupId>
<artifactId>timely-common</artifactId>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>
<plugins> <plugins>
@@ -252,9 +245,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>
@@ -296,37 +288,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>
+1 -1
View File
@@ -33,7 +33,7 @@
<directory>${project.basedir}/bin</directory> <directory>${project.basedir}/bin</directory>
<outputDirectory>bin</outputDirectory> <outputDirectory>bin</outputDirectory>
<includes> <includes>
<include>*</include> <include>**/*</include>
</includes> </includes>
</fileSet> </fileSet>
<fileSet> <fileSet>
@@ -1,7 +1,7 @@
package timely.balancer; package timely.balancer;
import java.util.Timer; import java.util.concurrent.ScheduledExecutorService;
import java.util.TimerTask; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import com.google.common.util.concurrent.AtomicDouble; import com.google.common.util.concurrent.AtomicDouble;
@@ -20,38 +20,26 @@ public class ArrivalRate {
private AtomicDouble longTermRate = new AtomicDouble(0); private AtomicDouble longTermRate = new AtomicDouble(0);
private AtomicLong longTermLastReset = new AtomicLong(0); private AtomicLong longTermLastReset = new AtomicLong(0);
public ArrivalRate(Timer timer) { public ArrivalRate(ScheduledExecutorService arrivalRateExecutor) {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
shortTermLastReset.set(now); shortTermLastReset.set(now);
mediumTermLastReset.set(now); mediumTermLastReset.set(now);
longTermLastReset.set(now); longTermLastReset.set(now);
// 2 minute // 2 minute
timer.schedule(new TimerTask() { arrivalRateExecutor.scheduleAtFixedRate(() -> {
resetShort();
@Override }, 2, 2, TimeUnit.MINUTES);
public void run() {
resetShort();
}
}, 120000, 120000);
// 5 minute // 5 minute
timer.schedule(new TimerTask() { arrivalRateExecutor.scheduleAtFixedRate(() -> {
resetMedium();
@Override }, 5, 5, TimeUnit.MINUTES);
public void run() {
resetMedium();
}
}, 300000, 300000);
// 15 minute // 15 minute
timer.schedule(new TimerTask() { arrivalRateExecutor.scheduleAtFixedRate(() -> {
resetLong();
@Override }, 15, 15, TimeUnit.MINUTES);
public void run() {
resetLong();
}
}, 900000, 900000);
} }
private void resetShort() { private void resetShort() {
@@ -1,5 +1,9 @@
package timely.balancer; package timely.balancer;
import static timely.Server.SERVICE_DISCOVERY_PATH;
import static timely.store.cache.DataStoreCache.NON_CACHED_METRICS;
import static timely.store.cache.DataStoreCache.NON_CACHED_METRICS_LOCK_PATH;
import java.io.File; import java.io.File;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@@ -21,6 +25,7 @@ import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption; import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup; import io.netty.channel.EventLoopGroup;
import io.netty.channel.WriteBufferWaterMark;
import io.netty.channel.epoll.EpollChannelOption; import io.netty.channel.epoll.EpollChannelOption;
import io.netty.channel.epoll.EpollDatagramChannel; import io.netty.channel.epoll.EpollDatagramChannel;
import io.netty.channel.epoll.EpollEventLoopGroup; import io.netty.channel.epoll.EpollEventLoopGroup;
@@ -52,10 +57,19 @@ import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler; import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.NettyRuntime; import io.netty.util.NettyRuntime;
import io.netty.util.internal.SystemPropertyUtil; import io.netty.util.internal.SystemPropertyUtil;
import org.apache.accumulo.core.conf.AccumuloConfiguration;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.RetryForever;
import org.apache.zookeeper.CreateMode;
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.balancer.configuration.BalancerConfiguration; import timely.balancer.configuration.BalancerConfiguration;
import timely.balancer.configuration.ClientSsl; import timely.balancer.configuration.ClientSsl;
import timely.balancer.configuration.SpringBootstrap; import timely.balancer.configuration.SpringBootstrap;
@@ -81,7 +95,7 @@ import timely.netty.tcp.TcpDecoder;
import timely.netty.tcp.TcpVersionHandler; import timely.netty.tcp.TcpVersionHandler;
import timely.netty.udp.UdpDecoder; import timely.netty.udp.UdpDecoder;
import timely.netty.udp.UdpPacketToByteBuf; import timely.netty.udp.UdpPacketToByteBuf;
import timely.netty.websocket.WebSocketHttpCookieHandler; import timely.netty.websocket.WebSocketFullRequestHandler;
import timely.netty.websocket.WebSocketRequestDecoder; import timely.netty.websocket.WebSocketRequestDecoder;
import timely.netty.websocket.subscription.WSTimelyExceptionHandler; import timely.netty.websocket.subscription.WSTimelyExceptionHandler;
@@ -115,9 +129,13 @@ public class Balancer {
protected Channel httpChannelHandle = null; protected Channel httpChannelHandle = null;
protected Channel wsChannelHandle = null; protected Channel wsChannelHandle = null;
protected List<Channel> udpChannelHandleList = new ArrayList<>(); protected List<Channel> udpChannelHandleList = new ArrayList<>();
private CuratorFramework curatorFramework;
protected volatile boolean shutdown = false; protected volatile boolean shutdown = false;
private static final int DEFAULT_EVENT_LOOP_THREADS; private static final int DEFAULT_EVENT_LOOP_THREADS;
private String[] zkPaths = new String[] { LEADER_LATCH_PATH, ASSIGNMENTS_LAST_UPDATED_PATH, ASSIGNMENTS_LOCK_PATH,
SERVICE_DISCOVERY_PATH, NON_CACHED_METRICS, NON_CACHED_METRICS_LOCK_PATH };
static { static {
DEFAULT_EVENT_LOOP_THREADS = Math.max(1, DEFAULT_EVENT_LOOP_THREADS = Math.max(1,
SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2)); SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));
@@ -127,8 +145,11 @@ public class Balancer {
this.balancerConfig = balancerConf; this.balancerConfig = balancerConf;
} }
protected static ConfigurableApplicationContext initializeConfiguration(String[] args) { protected static ConfigurableApplicationContext initializeContext(String[] args) {
return new SpringApplicationBuilder(SpringBootstrap.class).web(false).run(args); SpringApplicationBuilder builder = new SpringApplicationBuilder(SpringBootstrap.class);
builder.web(WebApplicationType.NONE);
builder.registerShutdownHook(false);
return builder.run(args);
} }
private void shutdownHook() { private void shutdownHook() {
@@ -160,8 +181,9 @@ public class Balancer {
channelFutures.add(wsChannelHandle.close()); channelFutures.add(wsChannelHandle.close());
} }
int udpChannel = 1;
for (Channel c : udpChannelHandleList) { for (Channel c : udpChannelHandleList) {
LOG.info("Closing udpChannelHandle"); LOG.info("Closing udpChannelHandle #" + udpChannel++);
channelFutures.add(c.close()); channelFutures.add(c.close());
} }
@@ -170,14 +192,13 @@ public class Balancer {
try { try {
f.get(); f.get();
} catch (final Exception e) { } catch (final Exception e) {
LOG.error("Error while shutting down channel: {}", f.channel().config()); LOG.error("Channel:" + f.channel().config() + " -> " + e.getMessage(), e);
LOG.error("{}", e.getMessage());
e.printStackTrace();
} }
}); });
Integer quietPeriod = balancerConfig.getServer().getShutdownQuietPeriod(); int quietPeriod = balancerConfig.getServer().getShutdownQuietPeriod();
List<Future<?>> groupFutures = new ArrayList<>(); List<Future<?>> groupFutures = new ArrayList<>();
if (tcpBossGroup != null) { if (tcpBossGroup != null) {
LOG.info("Shutting down tcpBossGroup"); LOG.info("Shutting down tcpBossGroup");
groupFutures.add(tcpBossGroup.shutdownGracefully(quietPeriod, 10, TimeUnit.SECONDS)); groupFutures.add(tcpBossGroup.shutdownGracefully(quietPeriod, 10, TimeUnit.SECONDS));
@@ -222,9 +243,7 @@ public class Balancer {
try { try {
f.get(); f.get();
} catch (final Exception e) { } catch (final Exception e) {
LOG.error("Error while shutting down group: {}", f.toString()); LOG.error("Group:" + f.toString() + " -> " + e.getMessage(), e);
LOG.error("{}", e.getMessage());
e.printStackTrace();
} }
}); });
@@ -235,10 +254,29 @@ public class Balancer {
LOG.error("Error closing WebSocketRequestDecoder during shutdown", e); LOG.error("Error closing WebSocketRequestDecoder during shutdown", e);
} }
if (this.metricResolver != null) {
try {
LOG.info("Closing metricResolver");
this.metricResolver.close();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
if (curatorFramework != null) {
try {
LOG.info("Closing curatorFramework");
curatorFramework.close();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
if (applicationContext != null) { if (applicationContext != null) {
LOG.info("Closing applicationContext"); LOG.info("Closing applicationContext");
applicationContext.close(); applicationContext.close();
} }
this.shutdown = true; this.shutdown = true;
LOG.info("Server shut down."); LOG.info("Server shut down.");
} }
@@ -320,8 +358,33 @@ public class Balancer {
ssl.getKeyFile(), ssl.getKeyType(), ssl.getKeyPassword()); ssl.getKeyFile(), ssl.getKeyType(), ssl.getKeyPassword());
} }
private void ensureZkPaths(CuratorFramework curatorFramework, String[] paths) {
for (String s : paths) {
try {
Stat stat = curatorFramework.checkExists().forPath(s);
if (stat == null) {
curatorFramework.create().creatingParentContainersIfNeeded().withMode(CreateMode.PERSISTENT)
.forPath(s);
}
} catch (Exception e) {
LOG.info(e.getMessage());
}
}
}
public void run() throws Exception { public void run() throws Exception {
// start curator framework
RetryPolicy retryPolicy = new RetryForever(1000);
int timeout = Long.valueOf(AccumuloConfiguration.getTimeInMillis(balancerConfig.getZooKeeper().getTimeout()))
.intValue();
curatorFramework = CuratorFrameworkFactory.newClient(balancerConfig.getZooKeeper().getServers(), timeout, 10000,
retryPolicy);
curatorFramework.start();
ensureZkPaths(curatorFramework, zkPaths);
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;
@@ -355,7 +418,7 @@ public class Balancer {
tcpClientPools.add(new TcpClientPool(this.balancerConfig)); tcpClientPools.add(new TcpClientPool(this.balancerConfig));
} }
HealthChecker healthChecker = new HealthChecker(this.balancerConfig, tcpClientPools.get(0)); HealthChecker healthChecker = new HealthChecker(this.balancerConfig, tcpClientPools.get(0));
this.metricResolver = new BalancedMetricResolver(this.balancerConfig, healthChecker); this.metricResolver = new BalancedMetricResolver(curatorFramework, this.balancerConfig, healthChecker);
final ServerBootstrap tcpServer = new ServerBootstrap(); final ServerBootstrap tcpServer = new ServerBootstrap();
tcpServer.group(tcpBossGroup, tcpWorkerGroup); tcpServer.group(tcpBossGroup, tcpWorkerGroup);
@@ -387,7 +450,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);
@@ -406,10 +470,9 @@ public class Balancer {
wsServer.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT); wsServer.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
wsServer.option(ChannelOption.SO_BACKLOG, 128); wsServer.option(ChannelOption.SO_BACKLOG, 128);
wsServer.option(ChannelOption.SO_KEEPALIVE, true); wsServer.option(ChannelOption.SO_KEEPALIVE, true);
/* Not sure if next three lines are necessary */ /* Not sure if next two lines are necessary */
wsServer.option(ChannelOption.SO_SNDBUF, 1048576); wsServer.option(ChannelOption.SO_SNDBUF, 1048576);
wsServer.option(ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK, 838860); wsServer.option(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(620145, 838860));
wsServer.option(ChannelOption.WRITE_BUFFER_LOW_WATER_MARK, 620145);
wsChannelHandle = wsServer.bind(wsIp, wsPort).sync().channel(); wsChannelHandle = wsServer.bind(wsIp, wsPort).sync().channel();
final String wsAddress = ((InetSocketAddress) wsChannelHandle.localAddress()).getAddress().getHostAddress(); final String wsAddress = ((InetSocketAddress) wsChannelHandle.localAddress()).getAddress().getHostAddress();
@@ -434,12 +497,12 @@ public class Balancer {
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
Balancer.applicationContext = Balancer.initializeConfiguration(args); Balancer.applicationContext = Balancer.initializeContext(args);
BalancerConfiguration balancerConf = applicationContext.getBean(BalancerConfiguration.class); BalancerConfiguration balancerConf = Balancer.applicationContext.getBean(BalancerConfiguration.class);
Balancer b = new Balancer(balancerConf); Balancer balancer = new Balancer(balancerConf);
try { try {
b.run(); balancer.run();
LATCH.await(); LATCH.await();
} catch (final InterruptedException e) { } catch (final InterruptedException e) {
LOG.info("Server shutting down."); LOG.info("Server shutting down.");
@@ -447,7 +510,7 @@ public class Balancer {
LOG.error("Error running server.", e); LOG.error("Error running server.", e);
} finally { } finally {
try { try {
b.shutdown(); balancer.shutdown();
} catch (Exception e) { } catch (Exception e) {
System.exit(1); System.exit(1);
} }
@@ -467,16 +530,17 @@ 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().setIgnoreSslHandshakeErrors(
balancerConfig.getSecurity().getServerSsl().isUseGeneratedKeypair()));
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().setIgnoreSslHandshakeErrors(
ch.pipeline().addLast("error", new TimelyExceptionHandler()); balancerConfig.getSecurity().getServerSsl().isUseGeneratedKeypair()));
} }
}; };
} }
@@ -489,7 +553,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));
} }
@@ -502,7 +566,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());
@@ -518,13 +582,13 @@ 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 WebSocketHttpCookieHandler(balancerConfig.getSecurity())); ch.pipeline().addLast("sessionExtractor", new WebSocketFullRequestHandler());
ch.pipeline().addLast("idle-handler", ch.pipeline().addLast("idle-handler",
new IdleStateHandler(balancerConfig.getWebsocket().getTimeout(), 0, 0)); new IdleStateHandler(balancerConfig.getWebsocket().getTimeout(), 0, 0));
ch.pipeline().addLast("ws-protocol", ch.pipeline().addLast("ws-protocol",
new WebSocketServerProtocolHandler(WS_PATH, null, true, 65536, true)); new WebSocketServerProtocolHandler(WS_PATH, null, true, 65536, false, true));
ch.pipeline().addLast("wsDecoder", new WebSocketRequestDecoder(balancerConfig.getSecurity())); ch.pipeline().addLast("wsDecoder", new WebSocketRequestDecoder(balancerConfig.getSecurity()));
ch.pipeline().addLast("httpRelay", new WsRelayHandler(balancerConfig, metricResolver, wsClientPool)); ch.pipeline().addLast("httpRelay", new WsRelayHandler(balancerConfig, metricResolver, wsClientPool));
ch.pipeline().addLast("error", new WSTimelyExceptionHandler()); ch.pipeline().addLast("error", new WSTimelyExceptionHandler());
@@ -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 {
@@ -3,6 +3,7 @@ package timely.balancer.configuration;
import javax.validation.Valid; import javax.validation.Valid;
import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig; import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.boot.context.properties.NestedConfigurationProperty; import org.springframework.boot.context.properties.NestedConfigurationProperty;
import timely.configuration.Http; import timely.configuration.Http;
@@ -10,9 +11,9 @@ public class BalancerHttp extends Http {
@Valid @Valid
@NestedConfigurationProperty @NestedConfigurationProperty
private GenericKeyedObjectPoolConfig httpClientPool = new GenericKeyedObjectPoolConfig(); private GenericKeyedObjectPoolConfig<CloseableHttpClient> httpClientPool = new GenericKeyedObjectPoolConfig<>();
public GenericKeyedObjectPoolConfig getHttpClientPool() { public GenericKeyedObjectPoolConfig<CloseableHttpClient> getHttpClientPool() {
return httpClientPool; return httpClientPool;
} }
} }
@@ -4,27 +4,29 @@ import javax.validation.Valid;
import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig; import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
import org.springframework.boot.context.properties.NestedConfigurationProperty; import org.springframework.boot.context.properties.NestedConfigurationProperty;
import timely.client.tcp.TcpClient;
import timely.client.udp.UdpClient;
import timely.configuration.Server; import timely.configuration.Server;
public class BalancerServer extends Server { public class BalancerServer extends Server {
@Valid @Valid
@NestedConfigurationProperty @NestedConfigurationProperty
private GenericKeyedObjectPoolConfig udpClientPool = new GenericKeyedObjectPoolConfig(); private GenericKeyedObjectPoolConfig<UdpClient> udpClientPool = new GenericKeyedObjectPoolConfig<>();
@Valid @Valid
@NestedConfigurationProperty @NestedConfigurationProperty
private GenericKeyedObjectPoolConfig tcpClientPool = new GenericKeyedObjectPoolConfig(); private GenericKeyedObjectPoolConfig<TcpClient> tcpClientPool = new GenericKeyedObjectPoolConfig<>();
private int numTcpPools = 1; private int numTcpPools = 1;
private int tcpBufferSize = -1; private int tcpBufferSize = -1;
public GenericKeyedObjectPoolConfig getUdpClientPool() { public GenericKeyedObjectPoolConfig<UdpClient> getUdpClientPool() {
return udpClientPool; return udpClientPool;
} }
public GenericKeyedObjectPoolConfig getTcpClientPool() { public GenericKeyedObjectPoolConfig<TcpClient> getTcpClientPool() {
return tcpClientPool; return tcpClientPool;
} }
@@ -4,15 +4,26 @@ import javax.validation.Valid;
import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig; import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
import org.springframework.boot.context.properties.NestedConfigurationProperty; import org.springframework.boot.context.properties.NestedConfigurationProperty;
import timely.client.websocket.subscription.WebSocketSubscriptionClient;
import timely.configuration.Websocket; import timely.configuration.Websocket;
public class BalancerWebsocket extends Websocket { public class BalancerWebsocket extends Websocket {
@Valid @Valid
@NestedConfigurationProperty @NestedConfigurationProperty
private GenericKeyedObjectPoolConfig wsClientPool = new GenericKeyedObjectPoolConfig(); private GenericKeyedObjectPoolConfig<WebSocketSubscriptionClient> wsClientPool = new GenericKeyedObjectPoolConfig<>();
public GenericKeyedObjectPoolConfig getWsClientPool() { public GenericKeyedObjectPoolConfig<WebSocketSubscriptionClient> getWsClientPool() {
return wsClientPool; return wsClientPool;
} }
public int incomingBufferSize = 50000000;
public int getIncomingBufferSize() {
return incomingBufferSize;
}
public void setIncomingBufferSize(int incomingBufferSize) {
this.incomingBufferSize = incomingBufferSize;
}
} }
@@ -8,6 +8,8 @@ public class ClientSsl {
private String trustStoreFile; private String trustStoreFile;
private String trustStoreType; private String trustStoreType;
private String trustStorePassword; private String trustStorePassword;
private boolean useClientCert = true;
private boolean hostVerificationEnabled = true;
public String getKeyFile() { public String getKeyFile() {
return keyFile; return keyFile;
@@ -55,6 +57,21 @@ public class ClientSsl {
public void setTrustStorePassword(String trustStorePassword) { public void setTrustStorePassword(String trustStorePassword) {
this.trustStorePassword = trustStorePassword; this.trustStorePassword = trustStorePassword;
}
public void setUseClientCert(boolean useClientCert) {
this.useClientCert = useClientCert;
}
public boolean isUseClientCert() {
return useClientCert;
}
public void setHostVerificationEnabled(boolean hostVerificationEnabled) {
this.hostVerificationEnabled = hostVerificationEnabled;
}
public boolean isHostVerificationEnabled() {
return hostVerificationEnabled;
} }
} }
@@ -1,9 +1,14 @@
package timely.balancer.configuration; package timely.balancer.configuration;
import javax.validation.constraints.NotBlank;
public class ZooKeeper { public class ZooKeeper {
private String servers; private String servers;
@NotBlank
private String timeout = "120s";
public String getServers() { public String getServers() {
return servers; return servers;
} }
@@ -11,4 +16,12 @@ public class ZooKeeper {
public void setServers(String servers) { public void setServers(String servers) {
this.servers = servers; this.servers = servers;
} }
public String getTimeout() {
return timeout;
}
public void setTimeout(String timeout) {
this.timeout = timeout;
}
} }
@@ -8,12 +8,9 @@ import org.slf4j.LoggerFactory;
import timely.balancer.ArrivalRate; import timely.balancer.ArrivalRate;
import timely.balancer.configuration.BalancerConfiguration; import timely.balancer.configuration.BalancerConfiguration;
import java.util.Timer;
public class TimelyBalancedHost { public class TimelyBalancedHost {
private static final Logger LOG = LoggerFactory.getLogger(TimelyBalancedHost.class); private static final Logger LOG = LoggerFactory.getLogger(TimelyBalancedHost.class);
static private Timer arrivalRateTimer = new Timer("ArrivalRateTimerTBH", true);
private String host; private String host;
private int tcpPort; private int tcpPort;
@@ -24,34 +21,25 @@ public class TimelyBalancedHost {
private BalancerConfiguration balancerConfig; private BalancerConfiguration balancerConfig;
private int failures = 0; private int failures = 0;
private int successes = 0; private int successes = 0;
private ArrivalRate arrivalRate = new ArrivalRate(arrivalRateTimer); private ArrivalRate arrivalRate = null;
public TimelyBalancedHost() { private TimelyBalancedHost() {
} }
public TimelyBalancedHost(String host, int tcpPort, int httpPort, int wsPort, int udpPort) { private TimelyBalancedHost(String host, int tcpPort, int httpPort, int wsPort, int udpPort,
ArrivalRate arrivalRate) {
this.host = host; this.host = host;
this.tcpPort = tcpPort; this.tcpPort = tcpPort;
this.httpPort = httpPort; this.httpPort = httpPort;
this.wsPort = wsPort; this.wsPort = wsPort;
this.udpPort = udpPort; this.udpPort = udpPort;
this.arrivalRate = arrivalRate;
} }
public TimelyBalancedHost(String host, int basePort) { static public TimelyBalancedHost of(String host, int tcpPort, int httpPort, int wsPort, int udpPort,
this.host = host; ArrivalRate arrivalRate) {
this.tcpPort = basePort; return new TimelyBalancedHost(host, tcpPort, httpPort, wsPort, udpPort, arrivalRate);
this.httpPort = basePort + 1;
this.wsPort = basePort + 2;
this.udpPort = basePort + 3;
}
static public TimelyBalancedHost of(String host, int tcpPort, int httpPort, int wsPort, int udpPort) {
return new TimelyBalancedHost(host, tcpPort, httpPort, wsPort, udpPort);
}
static public TimelyBalancedHost of(String host, int basePort) {
return new TimelyBalancedHost(host, basePort);
} }
public void setBalancerConfig(BalancerConfiguration balancerConfig) { public void setBalancerConfig(BalancerConfiguration balancerConfig) {
@@ -141,11 +129,17 @@ public class TimelyBalancedHost {
} }
public void arrived() { public void arrived() {
arrivalRate.arrived(); if (this.arrivalRate != null) {
this.arrivalRate.arrived();
}
} }
public double getArrivalRate() { public double getArrivalRate() {
return arrivalRate.getRate(); if (this.arrivalRate == null) {
return 0;
} else {
return this.arrivalRate.getRate();
}
} }
@Override @Override
@@ -5,22 +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.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 BalancerSecurity security;
public HttpClientFactory(SSLContext sslContext) { public HttpClientFactory(BalancerSecurity security, SSLContext sslContext) {
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, false)); return new DefaultPooledObject<>(timely.client.http.HttpClient.get(this.sslContext, null,
security.getClientSsl().isHostVerificationEnabled(), security.getClientSsl().isUseClientCert()));
} }
@Override @Override
@@ -4,16 +4,13 @@ import javax.net.ssl.SSLContext;
import org.apache.commons.pool2.impl.GenericKeyedObjectPool; 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 timely.balancer.configuration.BalancerHttp;
import org.slf4j.LoggerFactory; import timely.balancer.configuration.BalancerSecurity;
import timely.balancer.configuration.BalancerConfiguration;
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); public HttpClientPool(BalancerSecurity balancerSecurity, BalancerHttp http, SSLContext sslContext) {
super(new HttpClientFactory(balancerSecurity, sslContext), http.getHttpClientPool());
public HttpClientPool(BalancerConfiguration config, SSLContext sslContext) {
super(new HttpClientFactory(sslContext), config.getHttp().getHttpClientPool());
} }
} }
@@ -12,21 +12,21 @@ import timely.client.websocket.subscription.WebSocketSubscriptionClient;
public class WsClientFactory implements KeyedPooledObjectFactory<TimelyBalancedHost, WebSocketSubscriptionClient> { public class WsClientFactory implements KeyedPooledObjectFactory<TimelyBalancedHost, WebSocketSubscriptionClient> {
private final SSLContext sslContext; private final SSLContext sslContext;
private final boolean doLogin;
private BalancerConfiguration balancerConfig; private BalancerConfiguration balancerConfig;
public WsClientFactory(BalancerConfiguration balancerConfig, SSLContext sslContext) { public WsClientFactory(BalancerConfiguration balancerConfig, SSLContext sslContext) {
this.sslContext = sslContext;
this.doLogin = balancerConfig.isLoginRequired();
this.balancerConfig = balancerConfig; this.balancerConfig = balancerConfig;
this.sslContext = sslContext;
} }
@Override @Override
public PooledObject<WebSocketSubscriptionClient> makeObject(TimelyBalancedHost k) throws Exception { public PooledObject<WebSocketSubscriptionClient> makeObject(TimelyBalancedHost k) throws Exception {
int bufferSize = balancerConfig.getWebsocket().getSubscriptionBatchSize() * 500; int bufferSize = balancerConfig.getWebsocket().getIncomingBufferSize();
WebSocketSubscriptionClient client = new WebSocketSubscriptionClient(sslContext, k.getHost(), k.getHttpPort(), WebSocketSubscriptionClient client = new WebSocketSubscriptionClient(sslContext, k.getHost(), k.getHttpPort(),
k.getWsPort(), doLogin, "", "", false, bufferSize); k.getWsPort(), balancerConfig.getSecurity().getClientSsl().isUseClientCert(),
balancerConfig.isLoginRequired(), "", "",
balancerConfig.getSecurity().getClientSsl().isHostVerificationEnabled(), bufferSize);
return new DefaultPooledObject<>(client); return new DefaultPooledObject<>(client);
} }
@@ -2,8 +2,9 @@ package timely.balancer.healthcheck;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Timer; import java.util.concurrent.Executors;
import java.util.TimerTask; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -15,12 +16,12 @@ public class HealthChecker {
private static final Logger LOG = LoggerFactory.getLogger(HealthChecker.class); private static final Logger LOG = LoggerFactory.getLogger(HealthChecker.class);
private List<TimelyBalancedHost> timelyHosts = new ArrayList<>(); private List<TimelyBalancedHost> timelyHosts = new ArrayList<>();
private Timer timer; private ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
final private TCPServerHealthCheck tcpServerHealthCheck; final private TCPServerHealthCheck tcpServerHealthCheck;
private TimerTask check = new TimerTask() {
@Override public HealthChecker(BalancerConfiguration config, TcpClientPool tcpClientPool) {
public void run() { this.tcpServerHealthCheck = new TCPServerHealthCheck(tcpClientPool);
this.executorService.scheduleAtFixedRate(() -> {
synchronized (timelyHosts) { synchronized (timelyHosts) {
for (TimelyBalancedHost h : timelyHosts) { for (TimelyBalancedHost h : timelyHosts) {
try { try {
@@ -35,13 +36,7 @@ public class HealthChecker {
} }
} }
} }
} }, 0, config.getCheckServerHealthInterval(), TimeUnit.MILLISECONDS);
};
public HealthChecker(BalancerConfiguration config, TcpClientPool tcpClientPool) {
this.tcpServerHealthCheck = new TCPServerHealthCheck(tcpClientPool);
this.timer = new Timer("HealthCheckerThread", true);
this.timer.scheduleAtFixedRate(check, 0, config.getCheckServerHealthInterval());
} }
public void setTimelyHosts(List<TimelyBalancedHost> timelyHosts) { public void setTimelyHosts(List<TimelyBalancedHost> timelyHosts) {
@@ -50,4 +45,17 @@ public class HealthChecker {
this.timelyHosts.addAll(timelyHosts); this.timelyHosts.addAll(timelyHosts);
} }
} }
public void close() throws Exception {
this.executorService.shutdown();
try {
this.executorService.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
} finally {
if (!this.executorService.isTerminated()) {
this.executorService.shutdownNow();
}
}
}
} }
@@ -1,9 +1,8 @@
package timely.balancer.netty.http; package timely.balancer.netty.http;
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH; import static io.netty.handler.codec.http.HttpHeaderNames.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;
@@ -12,6 +11,7 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.google.common.collect.Multimap;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.SimpleChannelInboundHandler;
@@ -33,26 +33,26 @@ import org.apache.http.message.BasicHeader;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.util.StreamUtils; import org.springframework.util.StreamUtils;
import timely.api.request.AuthenticatedRequest;
import timely.api.request.MetricRequest; import timely.api.request.MetricRequest;
import timely.api.request.timeseries.HttpRequest; import timely.api.request.timeseries.HttpRequest;
import timely.api.request.timeseries.QueryRequest; import timely.api.request.timeseries.QueryRequest;
import timely.api.response.TimelyException; import timely.api.response.TimelyException;
import timely.balancer.configuration.BalancerConfiguration; import timely.auth.util.HttpHeaderUtils;
import timely.auth.util.ProxiedEntityUtils;
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;
import timely.netty.http.TimelyHttpHandler; import timely.netty.http.TimelyHttpHandler;
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;
} }
@@ -67,7 +67,7 @@ public class HttpRelayHandler extends SimpleChannelInboundHandler<HttpRequest> i
try { try {
try { try {
request = msg.getHttpRequest(); request = msg.getHttpRequest();
String originalURI = request.getUri(); String originalURI = request.uri();
originalURI = encodeURI(originalURI); originalURI = encodeURI(originalURI);
String metric = null; String metric = null;
@@ -86,14 +86,22 @@ 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);
List<Map.Entry<String, String>> headerList = request.headers().entries(); Multimap<String, String> headers = HttpHeaderUtils.toMultimap(request.headers());
List<Header> relayedHeaderList = new ArrayList<>(); if (msg instanceof AuthenticatedRequest) {
AuthenticatedRequest authenticatedRequest = (AuthenticatedRequest) msg;
TimelyAuthenticationToken token = authenticatedRequest.getToken();
if (token != null) {
if (token.getClientCert() != null) {
ProxiedEntityUtils.addProxyHeaders(headers, token.getClientCert());
}
}
}
for (Map.Entry<String, String> h : headerList) { List<Header> relayedHeaderList = new ArrayList<>();
if (!h.getKey().equals(CONTENT_LENGTH)) { for (Map.Entry<String, String> h : headers.entries()) {
if (!h.getKey().equals(CONTENT_LENGTH.toString())) {
relayedHeaderList.add(new BasicHeader(h.getKey(), h.getValue())); relayedHeaderList.add(new BasicHeader(h.getKey(), h.getValue()));
} }
} }
@@ -101,14 +109,14 @@ public class HttpRelayHandler extends SimpleChannelInboundHandler<HttpRequest> i
relayedHeaderList.toArray(headerArray); relayedHeaderList.toArray(headerArray);
String relayURI = "https://" + k.getHost() + ":" + k.getHttpPort() + originalURI; String relayURI = "https://" + k.getHost() + ":" + k.getHttpPort() + originalURI;
if (request.getMethod().equals(HttpMethod.GET)) { if (request.method().equals(HttpMethod.GET)) {
HttpUriRequest relayedRequest = new HttpGet(relayURI); HttpUriRequest relayedRequest = new HttpGet(relayURI);
if (headerArray.length > 0) { if (headerArray.length > 0) {
relayedRequest.setHeaders(headerArray); relayedRequest.setHeaders(headerArray);
} }
relayedResponse = client.execute(relayedRequest); relayedResponse = client.execute(relayedRequest);
} else if (request.getMethod().equals(HttpMethod.POST)) { } else if (request.method().equals(HttpMethod.POST)) {
HttpPost relayedRequest = new HttpPost(relayURI); HttpPost relayedRequest = new HttpPost(relayURI);
String content = request.content().toString(StandardCharsets.UTF_8); String content = request.content().toString(StandardCharsets.UTF_8);
@@ -143,7 +151,6 @@ public class HttpRelayHandler extends SimpleChannelInboundHandler<HttpRequest> i
this.sendHttpError(ctx, new TimelyException(HttpResponseStatus.METHOD_NOT_ALLOWED.code(), this.sendHttpError(ctx, new TimelyException(HttpResponseStatus.METHOD_NOT_ALLOWED.code(),
"Method not allowed", "Method not allowed")); "Method not allowed", "Method not allowed"));
} else { } else {
// buf = JsonUtil.getObjectMapper().writeValueAsBytes(msg);
ByteArrayOutputStream baos = new MyByteArrayOutputStream(); ByteArrayOutputStream baos = new MyByteArrayOutputStream();
StreamUtils.copy(relayedResponse.getEntity().getContent(), baos); StreamUtils.copy(relayedResponse.getEntity().getContent(), baos);
@@ -153,8 +160,6 @@ public class HttpRelayHandler extends SimpleChannelInboundHandler<HttpRequest> i
for (Header h : relayedResponse.getAllHeaders()) { for (Header h : relayedResponse.getAllHeaders()) {
response.headers().add(h.getName(), h.getValue()); response.headers().add(h.getName(), h.getValue());
} }
// response.headers().set(HttpHeaders.Names.CONTENT_TYPE,
// Constants.JSON_TYPE);
response.headers().set(CONTENT_LENGTH, response.content().readableBytes()); response.headers().set(CONTENT_LENGTH, response.content().readableBytes());
sendResponse(ctx, response); sendResponse(ctx, response);
} }
@@ -165,7 +170,7 @@ public class HttpRelayHandler extends SimpleChannelInboundHandler<HttpRequest> i
if (client != null && k != null) { if (client != null && k != null) {
httpClientPool.returnObject(k, client); httpClientPool.returnObject(k, client);
} else { } else {
LOG.error("NOT RETURNING CONNECTION! " + msg.getHttpRequest().getUri()); LOG.error("NOT RETURNING CONNECTION! " + msg.getHttpRequest().uri());
} }
} }
} }
@@ -197,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());
}
} }
@@ -1,12 +1,16 @@
package timely.balancer.netty.ws; package timely.balancer.netty.ws;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.websocket.CloseReason; import javax.websocket.CloseReason;
import javax.websocket.EndpointConfig; import javax.websocket.EndpointConfig;
import javax.websocket.MessageHandler;
import javax.websocket.Session; import javax.websocket.Session;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame; import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
@@ -15,25 +19,48 @@ import io.netty.util.concurrent.ScheduledFuture;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import timely.api.response.TimelyException; import timely.api.response.TimelyException;
import timely.auth.util.ProxiedEntityUtils;
import timely.client.websocket.ClientHandler; import timely.client.websocket.ClientHandler;
import timely.netty.http.auth.TimelyAuthenticationToken;
public class WsClientHandler extends ClientHandler { public class WsClientHandler extends ClientHandler {
private static final Logger LOG = LoggerFactory.getLogger(WsClientHandler.class); private static final Logger LOG = LoggerFactory.getLogger(WsClientHandler.class);
private final ChannelHandlerContext ctx; private final ChannelHandlerContext ctx;
private final TimelyAuthenticationToken token;
private ScheduledFuture<?> ping; private ScheduledFuture<?> ping;
public WsClientHandler(ChannelHandlerContext ctx, int pingRate) { public WsClientHandler(ChannelHandlerContext ctx, TimelyAuthenticationToken token, int pingRate) {
this.ctx = ctx; this.ctx = ctx;
this.token = token;
this.ping = this.ctx.executor().scheduleAtFixedRate(() -> { this.ping = this.ctx.executor().scheduleAtFixedRate(() -> {
LOG.trace("Sending ping on channel {}", ctx.channel()); LOG.trace("Sending ping on channel {}", ctx.channel());
ctx.writeAndFlush(new PingWebSocketFrame()); ctx.writeAndFlush(new PingWebSocketFrame());
}, pingRate, pingRate, TimeUnit.SECONDS); }, pingRate, pingRate, TimeUnit.SECONDS);
} }
@Override
public void beforeRequest(Map<String, List<String>> headers) {
if (token.getClientCert() != null) {
Multimap<String, String> proxyRequestHeaders = HashMultimap.create();
ProxiedEntityUtils.addProxyHeaders(proxyRequestHeaders, token.getClientCert());
for (String s : proxyRequestHeaders.keySet()) {
headers.put(s, new ArrayList<>(proxyRequestHeaders.get(s)));
}
}
Multimap<String, String> originalRequestHeaders = token.getHttpHeaders();
for (String s : originalRequestHeaders.keySet()) {
if (!headers.containsKey(s)) {
// add pre-existing values if key does not exist in proxyRequest
headers.put(s, new ArrayList<>(originalRequestHeaders.get(s)));
}
}
}
@Override @Override
public void onOpen(Session session, EndpointConfig config) { public void onOpen(Session session, EndpointConfig config) {
session.addMessageHandler((MessageHandler.Whole<String>) message -> { session.addMessageHandler(String.class, message -> {
ctx.writeAndFlush(new TextWebSocketFrame(message)); ctx.writeAndFlush(new TextWebSocketFrame(message));
LOG.debug("Message received on Websocket session {}: {}", session.getId(), message); LOG.debug("Message received on Websocket session {}: {}", session.getId(), message);
}); });
@@ -9,6 +9,7 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.google.common.collect.Multimap;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
@@ -24,6 +25,7 @@ import timely.api.request.subscription.CreateSubscription;
import timely.api.request.subscription.RemoveSubscription; import timely.api.request.subscription.RemoveSubscription;
import timely.api.request.subscription.SubscriptionRequest; import timely.api.request.subscription.SubscriptionRequest;
import timely.api.response.TimelyException; import timely.api.response.TimelyException;
import timely.auth.util.ProxiedEntityUtils;
import timely.balancer.configuration.BalancerConfiguration; import timely.balancer.configuration.BalancerConfiguration;
import timely.balancer.connection.TimelyBalancedHost; import timely.balancer.connection.TimelyBalancedHost;
import timely.balancer.connection.ws.WsClientPool; import timely.balancer.connection.ws.WsClientPool;
@@ -64,6 +66,10 @@ public class WsRelayHandler extends SimpleChannelInboundHandler<SubscriptionRequ
String subscriptionId; String subscriptionId;
try { try {
String metric = null; String metric = null;
Multimap<String, String> headers = msg.getRequestHeaders();
ProxiedEntityUtils.addProxyHeaders(headers, msg.getToken().getClientCert());
if (msg instanceof CreateSubscription) { if (msg instanceof CreateSubscription) {
CreateSubscription create = (CreateSubscription) msg; CreateSubscription create = (CreateSubscription) msg;
final String currentSubscriptionId = create.getSubscriptionId(); final String currentSubscriptionId = create.getSubscriptionId();
@@ -118,7 +124,8 @@ public class WsRelayHandler extends SimpleChannelInboundHandler<SubscriptionRequ
if (hostClientHolder == null) { if (hostClientHolder == null) {
k = metricResolver.getHostPortKey(metric); k = metricResolver.getHostPortKey(metric);
client = wsClientPool.borrowObject(k); client = wsClientPool.borrowObject(k);
client.open(new WsClientHandler(origContext, (balancerConfig.getWebsocket().getTimeout() / 2))); client.open(new WsClientHandler(origContext, add.getToken(),
(balancerConfig.getWebsocket().getTimeout() / 2)));
metricToClientMap.put(metric, new WsClientHolder(k, client)); metricToClientMap.put(metric, new WsClientHolder(k, client));
} else { } else {
client = hostClientHolder.getClient(); client = hostClientHolder.getClient();
@@ -4,7 +4,10 @@ import static timely.Server.SERVICE_DISCOVERY_PATH;
import static timely.balancer.Balancer.ASSIGNMENTS_LAST_UPDATED_PATH; import static timely.balancer.Balancer.ASSIGNMENTS_LAST_UPDATED_PATH;
import static timely.balancer.Balancer.ASSIGNMENTS_LOCK_PATH; import static timely.balancer.Balancer.ASSIGNMENTS_LOCK_PATH;
import static timely.balancer.Balancer.LEADER_LATCH_PATH; import static timely.balancer.Balancer.LEADER_LATCH_PATH;
import static timely.store.cache.DataStoreCache.NON_CACHED_METRICS;
import static timely.store.cache.DataStoreCache.NON_CACHED_METRICS_LOCK_PATH;
import java.io.FileNotFoundException;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
@@ -17,9 +20,11 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Random; import java.util.Random;
import java.util.Set; import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -27,10 +32,10 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
import com.csvreader.CsvReader; import com.csvreader.CsvReader;
import com.csvreader.CsvWriter; import com.csvreader.CsvWriter;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.apache.curator.RetryPolicy; import org.apache.commons.lang3.SerializationUtils;
import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.atomic.DistributedAtomicLong; import org.apache.curator.framework.recipes.atomic.DistributedAtomicLong;
import org.apache.curator.framework.recipes.atomic.DistributedAtomicValue;
import org.apache.curator.framework.recipes.cache.TreeCache; import org.apache.curator.framework.recipes.cache.TreeCache;
import org.apache.curator.framework.recipes.cache.TreeCacheEvent; import org.apache.curator.framework.recipes.cache.TreeCacheEvent;
import org.apache.curator.framework.recipes.cache.TreeCacheListener; import org.apache.curator.framework.recipes.cache.TreeCacheListener;
@@ -38,7 +43,7 @@ import org.apache.curator.framework.recipes.leader.LeaderLatch;
import org.apache.curator.framework.recipes.leader.LeaderLatchListener; import org.apache.curator.framework.recipes.leader.LeaderLatchListener;
import org.apache.curator.framework.recipes.locks.InterProcessReadWriteLock; import org.apache.curator.framework.recipes.locks.InterProcessReadWriteLock;
import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionState;
import org.apache.curator.retry.RetryUntilElapsed; import org.apache.curator.retry.RetryForever;
import org.apache.curator.x.discovery.ServiceCache; import org.apache.curator.x.discovery.ServiceCache;
import org.apache.curator.x.discovery.ServiceDiscovery; import org.apache.curator.x.discovery.ServiceDiscovery;
import org.apache.curator.x.discovery.ServiceDiscoveryBuilder; import org.apache.curator.x.discovery.ServiceDiscoveryBuilder;
@@ -49,7 +54,7 @@ import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FSDataOutputStream; import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path; import org.apache.hadoop.fs.Path;
import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.CreateMode;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import timely.ServerDetails; import timely.ServerDetails;
@@ -67,126 +72,159 @@ public class BalancedMetricResolver implements MetricResolver {
private List<TimelyBalancedHost> serverList = new ArrayList<>(); private List<TimelyBalancedHost> serverList = new ArrayList<>();
private Random r = new Random(); private Random r = new Random();
final private HealthChecker healthChecker; final private HealthChecker healthChecker;
private Timer timer = new Timer("RebalanceTimer", true); private ScheduledExecutorService assignmentExecutor = Executors.newScheduledThreadPool(2);
private Timer arrivalRateTimer = new Timer("AriivalRateTimerResolver", true); private ScheduledExecutorService arrivalRateExecutor = Executors.newScheduledThreadPool(2);
private int roundRobinCounter = 0; private int roundRobinCounter = 0;
private Set<String> nonCachedMetrics = new HashSet<>(); private Set<String> nonCachedMetrics = new HashSet<>();
private ReentrantReadWriteLock nonCachedMetricsLock = new ReentrantReadWriteLock(); private DistributedAtomicValue nonCachedMetricsIP;
private ReentrantReadWriteLock nonCachedMetricsLocalLock = new ReentrantReadWriteLock();
private InterProcessReadWriteLock nonCachedMetricsIPRWLock;
private BalancerConfiguration balancerConfig; private BalancerConfiguration balancerConfig;
private CuratorFramework curatorFramework;
private RetryPolicy retryPolicy = new RetryUntilElapsed(60000, 1000);
private LeaderLatch leaderLatch; private LeaderLatch leaderLatch;
private AtomicBoolean isLeader = new AtomicBoolean(false); private AtomicBoolean isLeader = new AtomicBoolean(false);
private InterProcessReadWriteLock assignmentsLock; private InterProcessReadWriteLock assignmentsIPRWLock;
private DistributedAtomicLong assignmentsLastUpdatedInHdfs; private DistributedAtomicLong assignmentsLastUpdatedInHdfs;
private AtomicLong assignmentsLastUpdatedLocal = new AtomicLong(0); private AtomicLong assignmentsLastUpdatedLocal = new AtomicLong(0);
private String[] zkPaths = new String[] { LEADER_LATCH_PATH, ASSIGNMENTS_LAST_UPDATED_PATH, ASSIGNMENTS_LOCK_PATH,
SERVICE_DISCOVERY_PATH };
private enum BalanceType { private enum BalanceType {
HIGH_LOW, HIGH_AVG, AVG_LOW; HIGH_LOW, HIGH_AVG, AVG_LOW
} }
public BalancedMetricResolver(BalancerConfiguration balancerConfig, HealthChecker healthChecker) { public BalancedMetricResolver(CuratorFramework curatorFramework, BalancerConfiguration balancerConfig,
HealthChecker healthChecker) {
this.balancerConfig = balancerConfig; this.balancerConfig = balancerConfig;
this.healthChecker = healthChecker; this.healthChecker = healthChecker;
// start curator framework assignmentsIPRWLock = new InterProcessReadWriteLock(curatorFramework, ASSIGNMENTS_LOCK_PATH);
curatorFramework = CuratorFrameworkFactory.newClient(balancerConfig.getZooKeeper().getServers(), 30000, 1000, testIPRWLock(curatorFramework, assignmentsIPRWLock, ASSIGNMENTS_LOCK_PATH);
retryPolicy);
curatorFramework.start();
ensureZkPaths(curatorFramework, zkPaths);
assignmentsLock = new InterProcessReadWriteLock(curatorFramework, ASSIGNMENTS_LOCK_PATH);
startLeaderLatch(curatorFramework); startLeaderLatch(curatorFramework);
startServiceListener(curatorFramework); startServiceListener(curatorFramework);
assignmentsLastUpdatedInHdfs = new DistributedAtomicLong(curatorFramework, ASSIGNMENTS_LAST_UPDATED_PATH, assignmentsLastUpdatedInHdfs = new DistributedAtomicLong(curatorFramework, ASSIGNMENTS_LAST_UPDATED_PATH,
retryPolicy); new RetryForever(1000));
TreeCacheListener listener = new TreeCacheListener() { TreeCacheListener assignmentListener = new TreeCacheListener() {
@Override @Override
public void childEvent(CuratorFramework curatorFramework, TreeCacheEvent event) throws Exception { public void childEvent(CuratorFramework curatorFramework, TreeCacheEvent event) throws Exception {
LOG.info("Handling event {}. assignmentsLastUpdatedInHdfs:{}", event.getType().toString(), LOG.debug("Handling assignments event {}. assignmentsLastUpdatedInHdfs:{}", event.getType().toString(),
new Date(assignmentsLastUpdatedInHdfs.get().postValue())); new Date(assignmentsLastUpdatedInHdfs.get().postValue()));
if (event.getType().equals(TreeCacheEvent.Type.NODE_UPDATED)) { if (event.getType().equals(TreeCacheEvent.Type.NODE_UPDATED)) {
long lastLocalUpdate = assignmentsLastUpdatedLocal.get(); readAssignmentsFromHdfs(true);
long lastHdfsUpdate = assignmentsLastUpdatedInHdfs.get().postValue();
if (lastHdfsUpdate > lastLocalUpdate) {
LOG.info("Reading assignments from hdfs lastHdfsUpdate ({}) > lastLocalUpdate ({})",
new Date(lastHdfsUpdate), new Date(lastLocalUpdate));
readAssignmentsFromHdfs();
} else {
LOG.info("Not reading assignments from hdfs lastHdfsUpdate ({}) <= lastLocalUpdate ({})",
new Date(lastHdfsUpdate), new Date(lastLocalUpdate));
}
} }
} }
}; };
try { try (TreeCache assignmentTreeCache = new TreeCache(curatorFramework, ASSIGNMENTS_LAST_UPDATED_PATH)) {
TreeCache treeCache = new TreeCache(curatorFramework, ASSIGNMENTS_LAST_UPDATED_PATH); assignmentTreeCache.getListenable().addListener(assignmentListener);
treeCache.getListenable().addListener(listener); assignmentTreeCache.start();
treeCache.start();
} catch (Exception e) { } catch (Exception e) {
LOG.error(e.getMessage(), e); LOG.error(e.getMessage(), e);
} }
nonCachedMetricsLock.writeLock().lock(); nonCachedMetricsIPRWLock = new InterProcessReadWriteLock(curatorFramework, NON_CACHED_METRICS_LOCK_PATH);
try { testIPRWLock(curatorFramework, nonCachedMetricsIPRWLock, NON_CACHED_METRICS_LOCK_PATH);
nonCachedMetrics.addAll(balancerConfig.getCache().getNonCachedMetrics()); nonCachedMetricsIP = new DistributedAtomicValue(curatorFramework, NON_CACHED_METRICS, new RetryForever(1000));
} finally { TreeCacheListener nonCachedMetricsListener = new TreeCacheListener() {
nonCachedMetricsLock.writeLock().unlock();
}
readAssignmentsFromHdfs();
timer.schedule(new TimerTask() {
@Override @Override
public void run() { public void childEvent(CuratorFramework curatorFramework, TreeCacheEvent event) throws Exception {
if (isLeader.get()) { if (event.getType().equals(TreeCacheEvent.Type.NODE_UPDATED)) {
try { LOG.debug("Handling nonCachedMetricsIP event {}", event.getType().toString());
balance(); readNonCachedMetricsIP();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
} }
} }
}, 900000, 900000); };
timer.schedule(new TimerTask() { try (TreeCache nonCachedMetricsTreeCache = new TreeCache(curatorFramework, NON_CACHED_METRICS)) {
nonCachedMetricsTreeCache.getListenable().addListener(nonCachedMetricsListener);
nonCachedMetricsTreeCache.start();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
@Override nonCachedMetricsLocalLock.writeLock().lock();
public void run() { try {
addNonCachedMetrics(balancerConfig.getCache().getNonCachedMetrics());
} finally {
nonCachedMetricsLocalLock.writeLock().unlock();
}
readNonCachedMetricsIP();
readAssignmentsFromHdfs(false);
assignmentExecutor.scheduleAtFixedRate(() -> {
if (isLeader.get()) {
try { try {
if (isLeader.get()) { balance();
long lastLocalUpdate = assignmentsLastUpdatedLocal.get();
long lastHdfsUpdate = assignmentsLastUpdatedInHdfs.get().postValue();
if (lastLocalUpdate > lastHdfsUpdate) {
LOG.info("Writing assignments to hdfs lastLocalUpdate ({}) > lastHdfsUpdate ({})",
new Date(lastLocalUpdate), new Date(lastHdfsUpdate));
writeAssigmentsToHdfs();
}
}
} catch (Exception e) { } catch (Exception e) {
LOG.error(e.getMessage(), e); LOG.error(e.getMessage(), e);
} }
} }
}, 10000, 60000); }, 15, 15, TimeUnit.MINUTES);
}
private void ensureZkPaths(CuratorFramework curatorFramework, String[] paths) { assignmentExecutor.scheduleAtFixedRate(() -> {
for (String s : paths) {
try { try {
Stat stat = curatorFramework.checkExists().forPath(s); if (isLeader.get()) {
if (stat == null) { long lastLocalUpdate = assignmentsLastUpdatedLocal.get();
curatorFramework.create().creatingParentContainersIfNeeded().forPath(s); long lastHdfsUpdate = assignmentsLastUpdatedInHdfs.get().postValue();
if (lastLocalUpdate > lastHdfsUpdate) {
LOG.debug("Leader writing assignments to hdfs lastLocalUpdate ({}) > lastHdfsUpdate ({})",
new Date(lastLocalUpdate), new Date(lastHdfsUpdate));
writeAssignmentsToHdfs();
} else {
LOG.trace("Leader not writing assignments to hdfs lastLocalUpdate ({}) <= lastHdfsUpdate ({})",
new Date(lastLocalUpdate), new Date(lastHdfsUpdate));
}
} else {
readAssignmentsFromHdfs(true);
} }
} catch (Exception e) { } catch (Exception e) {
LOG.info(e.getMessage()); LOG.error(e.getMessage(), e);
} }
}, 10, 60, TimeUnit.SECONDS);
}
private void readNonCachedMetricsIP() {
Set<String> currentNonCachedMetricsDistributed = new TreeSet<>();
try {
boolean acquired = false;
while (!acquired) {
acquired = nonCachedMetricsIPRWLock.readLock().acquire(60, TimeUnit.SECONDS);
}
byte[] currentNonCachedMetricsDistributedBytes = nonCachedMetricsIP.get().postValue();
if (currentNonCachedMetricsDistributedBytes != null) {
try {
currentNonCachedMetricsDistributed = SerializationUtils
.deserialize(currentNonCachedMetricsDistributedBytes);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
currentNonCachedMetricsDistributed = new TreeSet<>();
}
}
} catch (Exception e) {
LOG.error(e.getMessage(), e);
} finally {
try {
nonCachedMetricsIPRWLock.readLock().release();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
nonCachedMetricsLocalLock.writeLock().lock();
try {
if (nonCachedMetrics.containsAll(currentNonCachedMetricsDistributed)) {
LOG.debug("local nonCachedMetrics already contains {}", currentNonCachedMetricsDistributed);
} else {
currentNonCachedMetricsDistributed.removeAll(nonCachedMetrics);
LOG.debug("Adding {} to local nonCachedMetrics", currentNonCachedMetricsDistributed);
nonCachedMetrics.addAll(currentNonCachedMetricsDistributed);
}
} catch (Exception e) {
LOG.error(e.getMessage(), e);
} finally {
nonCachedMetricsLocalLock.writeLock().unlock();
} }
} }
@@ -201,7 +239,7 @@ public class BalancedMetricResolver implements MetricResolver {
public void isLeader() { public void isLeader() {
LOG.info("this balancer is the leader"); LOG.info("this balancer is the leader");
isLeader.set(true); isLeader.set(true);
writeAssigmentsToHdfs(); writeAssignmentsToHdfs();
} }
@Override @Override
@@ -217,17 +255,17 @@ public class BalancedMetricResolver implements MetricResolver {
private void startServiceListener(CuratorFramework curatorFramework) { private void startServiceListener(CuratorFramework curatorFramework) {
try { try {
ServiceDiscovery discovery = ServiceDiscoveryBuilder.builder(ServerDetails.class).client(curatorFramework) ServiceDiscovery<ServerDetails> discovery = ServiceDiscoveryBuilder.builder(ServerDetails.class)
.basePath(SERVICE_DISCOVERY_PATH).build(); .client(curatorFramework).basePath(SERVICE_DISCOVERY_PATH).build();
discovery.start(); discovery.start();
Collection<ServiceInstance> instances = discovery.queryForInstances("timely-server"); Collection<ServiceInstance<ServerDetails>> instances = discovery.queryForInstances("timely-server");
balancerLock.writeLock().lock(); balancerLock.writeLock().lock();
try { try {
for (ServiceInstance si : instances) { for (ServiceInstance<ServerDetails> si : instances) {
ServerDetails pl = (ServerDetails) si.getPayload(); ServerDetails pl = si.getPayload();
TimelyBalancedHost tbh = TimelyBalancedHost.of(pl.getHost(), pl.getTcpPort(), pl.getHttpPort(), TimelyBalancedHost tbh = TimelyBalancedHost.of(pl.getHost(), pl.getTcpPort(), pl.getHttpPort(),
pl.getWsPort(), pl.getUdpPort()); pl.getWsPort(), pl.getUdpPort(), new ArrivalRate(arrivalRateExecutor));
LOG.info("adding service {} host:{} tcpPort:{} httpPort:{} wsPort:{} udpPort:{}", si.getId(), LOG.info("adding service {} host:{} tcpPort:{} httpPort:{} wsPort:{} udpPort:{}", si.getId(),
pl.getHost(), pl.getTcpPort(), pl.getHttpPort(), pl.getWsPort(), pl.getUdpPort()); pl.getHost(), pl.getTcpPort(), pl.getHttpPort(), pl.getWsPort(), pl.getUdpPort());
tbh.setBalancerConfig(balancerConfig); tbh.setBalancerConfig(balancerConfig);
@@ -238,7 +276,8 @@ public class BalancedMetricResolver implements MetricResolver {
balancerLock.writeLock().unlock(); balancerLock.writeLock().unlock();
} }
final ServiceCache serviceCache = discovery.serviceCacheBuilder().name("timely-server").build(); final ServiceCache<ServerDetails> serviceCache = discovery.serviceCacheBuilder().name("timely-server")
.build();
ServiceCacheListener listener = new ServiceCacheListener() { ServiceCacheListener listener = new ServiceCacheListener() {
@Override @Override
@@ -246,12 +285,13 @@ public class BalancedMetricResolver implements MetricResolver {
boolean rebalanceNeeded = false; boolean rebalanceNeeded = false;
balancerLock.writeLock().lock(); balancerLock.writeLock().lock();
try { try {
List<ServiceInstance> instances = serviceCache.getInstances(); List<ServiceInstance<ServerDetails>> instances = serviceCache.getInstances();
Set<TimelyBalancedHost> availableHosts = new HashSet<>(); Set<TimelyBalancedHost> availableHosts = new HashSet<>();
for (ServiceInstance si : instances) { for (ServiceInstance<ServerDetails> si : instances) {
ServerDetails pl = (ServerDetails) si.getPayload(); ServerDetails pl = (ServerDetails) si.getPayload();
TimelyBalancedHost tbh = TimelyBalancedHost.of(pl.getHost(), pl.getTcpPort(), TimelyBalancedHost tbh = TimelyBalancedHost.of(pl.getHost(), pl.getTcpPort(),
pl.getHttpPort(), pl.getWsPort(), pl.getUdpPort()); pl.getHttpPort(), pl.getWsPort(), pl.getUdpPort(),
new ArrivalRate(arrivalRateExecutor));
tbh.setBalancerConfig(balancerConfig); tbh.setBalancerConfig(balancerConfig);
availableHosts.add(tbh); availableHosts.add(tbh);
} }
@@ -293,14 +333,16 @@ public class BalancedMetricResolver implements MetricResolver {
assignMetric(s, h); assignMetric(s, h);
LOG.debug("Assigned server removed. Assigning {} to server {}:{}", s, h.getTcpPort()); LOG.debug("Assigned server removed. Assigning {} to server {}:{}", s, h.getTcpPort());
} }
writeAssigmentsToHdfs();
} }
} finally { } finally {
balancerLock.writeLock().unlock(); balancerLock.writeLock().unlock();
} }
if (isLeader.get() && rebalanceNeeded) { if (isLeader.get()) {
balance(); if (rebalanceNeeded) {
balance();
}
writeAssignmentsToHdfs();
} }
} }
@@ -457,21 +499,21 @@ public class BalancedMetricResolver implements MetricResolver {
} }
if (highHosts.isEmpty() && lowHosts.isEmpty()) { if (highHosts.isEmpty() && lowHosts.isEmpty()) {
LOG.info("Host's arrival rates are within {} of the average", controlBandPercentage); LOG.debug("Host's arrival rates are within {} of the average", controlBandPercentage);
} else if (!highHosts.isEmpty() && !lowHosts.isEmpty()) { } else if (!highHosts.isEmpty() && !lowHosts.isEmpty()) {
LOG.info("begin rebalancing {}", BalanceType.HIGH_LOW); LOG.debug("begin rebalancing {}", BalanceType.HIGH_LOW);
numReassigned = rebalance(highHosts, lowHosts, calculatedRates, averageArrivalRate, numReassigned = rebalance(highHosts, lowHosts, calculatedRates, averageArrivalRate,
BalanceType.HIGH_LOW); BalanceType.HIGH_LOW);
LOG.info("end rebalancing {} - reassigned {}", BalanceType.HIGH_LOW, numReassigned); LOG.debug("end rebalancing {} - reassigned {}", BalanceType.HIGH_LOW, numReassigned);
} else if (lowHosts.isEmpty()) { } else if (lowHosts.isEmpty()) {
LOG.info("begin rebalancing {}", BalanceType.HIGH_AVG); LOG.debug("begin rebalancing {}", BalanceType.HIGH_AVG);
numReassigned = rebalance(highHosts, avgHosts, calculatedRates, averageArrivalRate, numReassigned = rebalance(highHosts, avgHosts, calculatedRates, averageArrivalRate,
BalanceType.HIGH_AVG); BalanceType.HIGH_AVG);
LOG.info("end rebalancing {} - reassigned {}", BalanceType.HIGH_AVG, numReassigned); LOG.debug("end rebalancing {} - reassigned {}", BalanceType.HIGH_AVG, numReassigned);
} else { } else {
LOG.info("begin rebalancing {}", BalanceType.AVG_LOW); LOG.debug("begin rebalancing {}", BalanceType.AVG_LOW);
numReassigned = rebalance(avgHosts, lowHosts, calculatedRates, averageArrivalRate, BalanceType.AVG_LOW); numReassigned = rebalance(avgHosts, lowHosts, calculatedRates, averageArrivalRate, BalanceType.AVG_LOW);
LOG.info("end rebalancing {} - reassigned {}", BalanceType.AVG_LOW, numReassigned); LOG.debug("end rebalancing {} - reassigned {}", BalanceType.AVG_LOW, numReassigned);
} }
} }
return numReassigned; return numReassigned;
@@ -582,6 +624,73 @@ public class BalancedMetricResolver implements MetricResolver {
return numReassigned; return numReassigned;
} }
private void addNonCachedMetrics(Collection<String> nonCachedMetricsUpdate) {
if (!nonCachedMetricsUpdate.isEmpty()) {
try {
LOG.debug("Adding {} to local nonCachedMetrics", nonCachedMetricsUpdate);
nonCachedMetricsLocalLock.writeLock().lock();
try {
nonCachedMetrics.addAll(nonCachedMetricsUpdate);
} finally {
nonCachedMetricsLocalLock.writeLock().unlock();
}
try {
boolean acquired = false;
while (!acquired) {
acquired = nonCachedMetricsIPRWLock.writeLock().acquire(60, TimeUnit.SECONDS);
}
byte[] currentNonCachedMetricsDistributedBytes = nonCachedMetricsIP.get().postValue();
Set<String> currentNonCachedMetricsIP;
if (currentNonCachedMetricsDistributedBytes == null) {
currentNonCachedMetricsIP = new TreeSet<>();
} else {
currentNonCachedMetricsIP = SerializationUtils
.deserialize(currentNonCachedMetricsDistributedBytes);
}
if (currentNonCachedMetricsIP.containsAll(nonCachedMetricsUpdate)) {
LOG.debug("nonCachedMetricsIP already contains {}", nonCachedMetricsUpdate);
} else {
nonCachedMetricsUpdate.removeAll(currentNonCachedMetricsIP);
LOG.debug("Adding {} to nonCachedMetricsIP", nonCachedMetricsUpdate);
TreeSet<String> updateSet = new TreeSet<>();
updateSet.addAll(currentNonCachedMetricsIP);
updateSet.addAll(nonCachedMetricsUpdate);
byte[] updateValue = SerializationUtils.serialize(updateSet);
nonCachedMetricsIP.trySet(updateValue);
if (!nonCachedMetricsIP.get().succeeded()) {
nonCachedMetricsIP.forceSet(updateValue);
}
}
} finally {
nonCachedMetricsIPRWLock.writeLock().release();
}
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
}
private void testIPRWLock(CuratorFramework curatorFramework, InterProcessReadWriteLock lock, String path) {
try {
lock.writeLock().acquire(10, TimeUnit.SECONDS);
} catch (Exception e1) {
try {
curatorFramework.delete().deletingChildrenIfNeeded().forPath(path);
curatorFramework.create().creatingParentContainersIfNeeded().withMode(CreateMode.PERSISTENT)
.forPath(path);
} catch (Exception e2) {
LOG.info(e2.getMessage(), e2);
}
} finally {
try {
lock.writeLock().release();
} catch (Exception e3) {
LOG.error(e3.getMessage(), e3);
}
}
}
private boolean shouldCache(String metricName) { private boolean shouldCache(String metricName) {
if (StringUtils.isBlank(metricName)) { if (StringUtils.isBlank(metricName)) {
@@ -589,6 +698,14 @@ public class BalancedMetricResolver implements MetricResolver {
} else { } else {
balancerLock.readLock().lock(); balancerLock.readLock().lock();
try { try {
nonCachedMetricsLocalLock.readLock().lock();
try {
if (nonCachedMetrics.contains(metricName)) {
return false;
}
} finally {
nonCachedMetricsLocalLock.readLock().unlock();
}
if (metricToHostMap.containsKey(metricName)) { if (metricToHostMap.containsKey(metricName)) {
return true; return true;
} }
@@ -596,27 +713,17 @@ public class BalancedMetricResolver implements MetricResolver {
balancerLock.readLock().unlock(); balancerLock.readLock().unlock();
} }
nonCachedMetricsLock.readLock().lock(); nonCachedMetricsLocalLock.writeLock().lock();
try { try {
if (nonCachedMetrics.contains(metricName)) {
return false;
}
for (String r : nonCachedMetrics) { for (String r : nonCachedMetrics) {
if (metricName.matches(r)) { if (metricName.matches(r)) {
LOG.info("Adding {} to list of non-cached metrics", metricName); LOG.debug("Adding {} to list of non-cached metrics", metricName);
nonCachedMetricsLock.readLock().unlock(); addNonCachedMetrics(Collections.singleton(metricName));
nonCachedMetricsLock.writeLock().lock();
nonCachedMetrics.add(metricName);
return false; return false;
} }
} }
} finally { } finally {
if (nonCachedMetricsLock.isWriteLockedByCurrentThread()) { nonCachedMetricsLocalLock.writeLock().unlock();
nonCachedMetricsLock.writeLock().unlock();
} else {
nonCachedMetricsLock.readLock().unlock();
}
} }
return true; return true;
} }
@@ -633,7 +740,7 @@ public class BalancedMetricResolver implements MetricResolver {
if (chooseMetricSpecificHost) { if (chooseMetricSpecificHost) {
ArrivalRate rate = metricMap.get(metric); ArrivalRate rate = metricMap.get(metric);
if (rate == null) { if (rate == null) {
rate = new ArrivalRate(arrivalRateTimer); rate = new ArrivalRate(arrivalRateExecutor);
if (!balancerLock.isWriteLockedByCurrentThread()) { if (!balancerLock.isWriteLockedByCurrentThread()) {
balancerLock.readLock().unlock(); balancerLock.readLock().unlock();
balancerLock.writeLock().lock(); balancerLock.writeLock().lock();
@@ -659,7 +766,7 @@ public class BalancedMetricResolver implements MetricResolver {
} else if (!tbh.isUp()) { } else if (!tbh.isUp()) {
TimelyBalancedHost oldTbh = tbh; TimelyBalancedHost oldTbh = tbh;
tbh = getLeastUsedHost(); tbh = getLeastUsedHost();
LOG.info("rebalancing from host that is down: reassigning metric {} from server {}:{} to {}:{}", LOG.debug("rebalancing from host that is down: reassigning metric {} from server {}:{} to {}:{}",
metric, oldTbh.getHost(), oldTbh.getTcpPort(), tbh.getHost(), tbh.getTcpPort()); metric, oldTbh.getHost(), oldTbh.getTcpPort(), tbh.getHost(), tbh.getTcpPort());
if (!balancerLock.isWriteLockedByCurrentThread()) { if (!balancerLock.isWriteLockedByCurrentThread()) {
balancerLock.readLock().unlock(); balancerLock.readLock().unlock();
@@ -764,11 +871,45 @@ public class BalancedMetricResolver implements MetricResolver {
return tbh; return tbh;
} }
private void readAssignmentsFromHdfs() { private void createAssignmentFile() {
try {
LOG.info("Creating assignment file: " + balancerConfig.getAssignmentFile());
Configuration configuration = new Configuration();
FileSystem fs = FileSystem.get(configuration);
Path assignmentFile = new Path(balancerConfig.getAssignmentFile());
fs.create(assignmentFile);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
private void readAssignmentsFromHdfs(boolean checkIfNecessary) {
try {
long lastLocalUpdate = assignmentsLastUpdatedLocal.get();
long lastHdfsUpdate = assignmentsLastUpdatedInHdfs.get().postValue();
if (checkIfNecessary) {
if (lastHdfsUpdate <= lastLocalUpdate) {
LOG.debug("Not reading assignments from hdfs lastHdfsUpdate ({}) <= lastLocalUpdate ({})",
new Date(lastHdfsUpdate), new Date(lastLocalUpdate));
return;
}
}
LOG.debug("Reading assignments from hdfs lastHdfsUpdate ({}) > lastLocalUpdate ({})",
new Date(lastHdfsUpdate), new Date(lastLocalUpdate));
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
// proceed with reading from HDFS
Map<String, TimelyBalancedHost> assignedMetricToHostMap = new TreeMap<>(); Map<String, TimelyBalancedHost> assignedMetricToHostMap = new TreeMap<>();
try { try {
assignmentsLock.readLock().acquire(); boolean acquired = false;
while (!acquired) {
acquired = assignmentsIPRWLock.readLock().acquire(60, TimeUnit.SECONDS);
}
balancerLock.writeLock().lock();
Configuration configuration = new Configuration(); Configuration configuration = new Configuration();
FileSystem fs = FileSystem.get(configuration); FileSystem fs = FileSystem.get(configuration);
Path assignmentFile = new Path(balancerConfig.getAssignmentFile()); Path assignmentFile = new Path(balancerConfig.getAssignmentFile());
@@ -798,37 +939,59 @@ public class BalancedMetricResolver implements MetricResolver {
} }
} }
balancerLock.writeLock().lock(); metricToHostMap.clear();
try { for (Map.Entry<String, TimelyBalancedHost> e : assignedMetricToHostMap.entrySet()) {
metricToHostMap.clear(); if (StringUtils.isNotBlank(e.getKey()) && e.getValue() != null) {
for (Map.Entry<String, TimelyBalancedHost> e : assignedMetricToHostMap.entrySet()) {
if (shouldCache(e.getKey())) { if (shouldCache(e.getKey())) {
metricToHostMap.put(e.getKey(), e.getValue()); metricToHostMap.put(e.getKey(), e.getValue());
} }
} else {
Exception e1 = new IllegalStateException(
"Bad assignment metric:" + e.getKey() + " host:" + e.getValue());
LOG.warn(e1.getMessage(), e1);
}
}
nonCachedMetricsLocalLock.readLock().lock();
try {
Iterator<Map.Entry<String, TimelyBalancedHost>> itr = metricToHostMap.entrySet().iterator();
while (itr.hasNext()) {
Map.Entry<String, TimelyBalancedHost> e = itr.next();
if (nonCachedMetrics.contains(e.getKey())) {
itr.remove();
}
} }
} finally { } finally {
balancerLock.writeLock().unlock(); nonCachedMetricsLocalLock.readLock().unlock();
} }
assignmentsLastUpdatedLocal.set(assignmentsLastUpdatedInHdfs.get().postValue()); assignmentsLastUpdatedLocal.set(assignmentsLastUpdatedInHdfs.get().postValue());
LOG.info("Read {} assignments from hdfs lastHdfsUpdate = lastLocalUpdate ({})", metricToHostMap.size(), LOG.info("Read {} assignments from hdfs lastHdfsUpdate = lastLocalUpdate ({})", metricToHostMap.size(),
new Date(assignmentsLastUpdatedLocal.get())); new Date(assignmentsLastUpdatedLocal.get()));
} catch (FileNotFoundException e) {
createAssignmentFile();
metricToHostMap.clear();
} catch (Exception e) { } catch (Exception e) {
LOG.error(e.getMessage(), e); LOG.error(e.getMessage(), e);
} finally { } finally {
balancerLock.writeLock().unlock();
try { try {
assignmentsLock.readLock().release(); assignmentsIPRWLock.readLock().release();
} catch (Exception e) { } catch (Exception e) {
LOG.error(e.getMessage(), e); LOG.error(e.getMessage(), e);
} }
} }
} }
private void writeAssigmentsToHdfs() { private void writeAssignmentsToHdfs() {
CsvWriter writer = null; CsvWriter writer = null;
try { try {
assignmentsLock.writeLock().acquire(); boolean acquired = false;
while (!acquired) {
acquired = assignmentsIPRWLock.writeLock().acquire(60, TimeUnit.SECONDS);
}
balancerLock.readLock().lock(); balancerLock.readLock().lock();
nonCachedMetricsLocalLock.readLock().lock();
try { try {
if (!metricToHostMap.isEmpty()) { if (!metricToHostMap.isEmpty()) {
Configuration configuration = new Configuration(); Configuration configuration = new Configuration();
@@ -845,12 +1008,14 @@ public class BalancedMetricResolver implements MetricResolver {
writer.write("tcpPort"); writer.write("tcpPort");
writer.endRecord(); writer.endRecord();
for (Map.Entry<String, TimelyBalancedHost> e : metricToHostMap.entrySet()) { for (Map.Entry<String, TimelyBalancedHost> e : metricToHostMap.entrySet()) {
writer.write(e.getKey()); if (!nonCachedMetrics.contains(e.getKey())) {
writer.write(e.getValue().getHost()); writer.write(e.getKey());
writer.write(Integer.toString(e.getValue().getTcpPort())); writer.write(e.getValue().getHost());
writer.endRecord(); writer.write(Integer.toString(e.getValue().getTcpPort()));
LOG.trace("Saving assigment: {} to {}:{}", e.getKey(), e.getValue().getHost(), writer.endRecord();
e.getValue().getTcpPort()); LOG.trace("Saving assigment: {} to {}:{}", e.getKey(), e.getValue().getHost(),
e.getValue().getTcpPort());
}
} }
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
@@ -859,10 +1024,11 @@ public class BalancedMetricResolver implements MetricResolver {
if (!assignmentsLastUpdatedInHdfs.get().succeeded()) { if (!assignmentsLastUpdatedInHdfs.get().succeeded()) {
assignmentsLastUpdatedInHdfs.forceSet(now); assignmentsLastUpdatedInHdfs.forceSet(now);
} }
LOG.info("Wrote {} assignments to hdfs lastHdfsUpdate = lastLocalUpdate ({})", LOG.debug("Wrote {} assignments to hdfs lastHdfsUpdate = lastLocalUpdate ({})",
metricToHostMap.size(), new Date(assignmentsLastUpdatedLocal.get())); metricToHostMap.size(), new Date(assignmentsLastUpdatedLocal.get()));
} }
} finally { } finally {
nonCachedMetricsLocalLock.readLock().unlock();
balancerLock.readLock().unlock(); balancerLock.readLock().unlock();
} }
} catch (Exception e) { } catch (Exception e) {
@@ -872,7 +1038,7 @@ public class BalancedMetricResolver implements MetricResolver {
writer.close(); writer.close();
} }
try { try {
assignmentsLock.writeLock().release(); assignmentsIPRWLock.writeLock().release();
} catch (Exception e) { } catch (Exception e) {
LOG.error(e.getMessage(), e); LOG.error(e.getMessage(), e);
} }
@@ -881,13 +1047,50 @@ public class BalancedMetricResolver implements MetricResolver {
private void assignMetric(String metric, TimelyBalancedHost tbh) { private void assignMetric(String metric, TimelyBalancedHost tbh) {
if (isLeader.get()) { if (isLeader.get()) {
balancerLock.writeLock().lock(); if (StringUtils.isNotBlank(metric) && tbh != null) {
try { balancerLock.writeLock().lock();
metricToHostMap.put(metric, tbh); try {
assignmentsLastUpdatedLocal.set(System.currentTimeMillis()); metricToHostMap.put(metric, tbh);
} finally { assignmentsLastUpdatedLocal.set(System.currentTimeMillis());
balancerLock.writeLock().unlock(); } finally {
balancerLock.writeLock().unlock();
}
} else {
Exception e = new IllegalStateException("Bad assignment metric:" + metric + " host:" + tbh);
LOG.warn(e.getMessage(), e);
} }
} }
} }
}
@Override
public void close() throws Exception {
this.assignmentExecutor.shutdown();
try {
this.assignmentExecutor.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
} finally {
if (!this.assignmentExecutor.isTerminated()) {
this.assignmentExecutor.shutdownNow();
}
}
this.arrivalRateExecutor.shutdown();
try {
this.arrivalRateExecutor.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
} finally {
if (!this.arrivalRateExecutor.isTerminated()) {
this.arrivalRateExecutor.shutdownNow();
}
}
try {
this.healthChecker.close();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
}
@@ -7,4 +7,6 @@ public interface MetricResolver {
public TimelyBalancedHost getHostPortKeyIngest(String metric); public TimelyBalancedHost getHostPortKeyIngest(String metric);
public TimelyBalancedHost getHostPortKey(String metric); public TimelyBalancedHost getHostPortKey(String metric);
public void close() throws Exception;
} }
+42 -2
View File
@@ -4,12 +4,13 @@
<parent> <parent>
<groupId>timely</groupId> <groupId>timely</groupId>
<artifactId>timely</artifactId> <artifactId>timely</artifactId>
<version>1.0.1-SNAPSHOT</version> <version>1.2.8-SNAPSHOT</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>0.0.7.dev0</python.client.version> <!-- use .dev0 instead of -SNAPSHOT -->
<python.client.version>1.2.8.dev0</python.client.version>
</properties> </properties>
<dependencies> <dependencies>
<dependency> <dependency>
@@ -107,4 +108,43 @@
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
<profiles>
<profile>
<id>python</id>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<executions>
<execution>
<id>python-dist</id>
<goals>
<goal>exec</goal>
</goals>
<phase>package</phase>
<configuration>
<executable>python</executable>
<workingDirectory>${project.basedir}/src/main/python</workingDirectory>
<commandlineArgs>setup.py sdist --dist-dir ${project.build.directory}</commandlineArgs>
</configuration>
</execution>
<execution>
<id>python-clean</id>
<goals>
<goal>exec</goal>
</goals>
<phase>package</phase>
<configuration>
<executable>rm</executable>
<workingDirectory>${project.basedir}/src/main/python</workingDirectory>
<commandlineArgs>-rf timely.egg-info setup.py</commandlineArgs>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project> </project>
@@ -1,12 +1,13 @@
package timely.client.http; package timely.client.http;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import org.apache.http.client.CookieStore; import org.apache.http.client.CookieStore;
import org.apache.http.client.config.CookieSpecs; import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig; import org.apache.http.client.config.RequestConfig;
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.client.HttpClients;
@@ -40,14 +41,31 @@ public class HttpClient {
} }
public static CloseableHttpClient get(SSLContext ssl, CookieStore cookieStore, boolean hostVerificationEnabled) { public static CloseableHttpClient get(SSLContext ssl, CookieStore cookieStore, boolean hostVerificationEnabled) {
RequestConfig defaultRequestConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build(); return get(ssl, cookieStore, hostVerificationEnabled, false);
}
HttpClientBuilder builder = HttpClients.custom().setSSLContext(ssl).setDefaultCookieStore(cookieStore) public static CloseableHttpClient get(SSLContext ssl, CookieStore cookieStore, boolean hostVerificationEnabled,
.setDefaultRequestConfig(defaultRequestConfig); boolean clientAuth) {
HostnameVerifier hostnameVerifier;
if (hostVerificationEnabled) { if (hostVerificationEnabled) {
builder.setSSLHostnameVerifier(new DefaultHostnameVerifier()); hostnameVerifier = SSLConnectionSocketFactory.getDefaultHostnameVerifier();
} else { } else {
builder.setSSLHostnameVerifier(new NoopHostnameVerifier()); hostnameVerifier = new NoopHostnameVerifier();
}
RequestConfig defaultRequestConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build();
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) {
SSLConnectionSocketFactory sslCSF = new SSLConnectionSocketFactory(ssl,
new String[] { "TLSv1.1", "TLSv1.2" }, null, hostnameVerifier);
builder.setSSLSocketFactory(sslCSF);
} }
return builder.build(); return builder.build();
} }
@@ -1,7 +1,8 @@
package timely.client.tcp; package timely.client.tcp;
import java.io.BufferedWriter;
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter; import java.io.OutputStreamWriter;
import java.net.Socket; import java.net.Socket;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -14,7 +15,7 @@ public class TcpClient implements AutoCloseable {
private final String host; private final String host;
private final int port; private final int port;
private Socket sock = null; private Socket sock = null;
private PrintWriter out = null; private BufferedWriter out = null;
private long connectTime = 0L; private long connectTime = 0L;
private long backoff = 2000; private long backoff = 2000;
private int bufferSize = -1; private int bufferSize = -1;
@@ -47,7 +48,6 @@ public class TcpClient implements AutoCloseable {
* *
* @param metric * @param metric
* newline terminated string representation of Timely metric * newline terminated string representation of Timely metric
* @throws IOException
*/ */
public synchronized void write(String metric) throws IOException { public synchronized void write(String metric) throws IOException {
if (connect() != 0) { if (connect() != 0) {
@@ -60,7 +60,7 @@ public class TcpClient implements AutoCloseable {
} }
} }
public synchronized void flush() { public synchronized void flush() throws IOException {
if (null != out) { if (null != out) {
out.flush(); out.flush();
writesSinceFlush = 0; writesSinceFlush = 0;
@@ -94,12 +94,12 @@ public class TcpClient implements AutoCloseable {
} }
private synchronized int connect() { private synchronized int connect() {
if (null == sock || !sock.isConnected() || out.checkError()) { if (null == sock || !sock.isConnected()) {
if (System.currentTimeMillis() > (connectTime + backoff)) { if (System.currentTimeMillis() > (connectTime + backoff)) {
try { try {
connectTime = System.currentTimeMillis(); connectTime = System.currentTimeMillis();
sock = new Socket(host, port); sock = new Socket(host, port);
out = new PrintWriter(sock.getOutputStream(), false); out = new BufferedWriter(new OutputStreamWriter(sock.getOutputStream()));
backoff = 2000; backoff = 2000;
LOG.trace("Connected to Timely at {}:{}", host, port); LOG.trace("Connected to Timely at {}:{}", host, port);
} catch (IOException e) { } catch (IOException e) {
@@ -1,5 +1,8 @@
package timely.client.websocket; package timely.client.websocket;
import java.util.List;
import java.util.Map;
import javax.websocket.CloseReason; import javax.websocket.CloseReason;
import javax.websocket.Endpoint; import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig; import javax.websocket.EndpointConfig;
@@ -35,4 +38,6 @@ public abstract class ClientHandler extends Endpoint {
LOG.error("Error occurred on Websocket session" + session.getId(), error); LOG.error("Error occurred on Websocket session" + session.getId(), error);
} }
public void beforeRequest(Map<String, List<String>> headers) {
}
} }
@@ -14,13 +14,15 @@ import org.apache.http.cookie.Cookie;
public class TimelyEndpointConfig implements ClientEndpointConfig { public class TimelyEndpointConfig implements ClientEndpointConfig {
private String sessionCookie = null; private String sessionCookie = null;
private final ClientHandler clientEndpoint;
private static final String FORMAT = "%s=%s;"; private static final String FORMAT = "%s=%s;";
public TimelyEndpointConfig(Cookie sessionCookie) { public TimelyEndpointConfig(ClientHandler clientEndpoint, Cookie sessionCookie) {
if (null != sessionCookie) { if (null != sessionCookie) {
this.sessionCookie = String.format(FORMAT, sessionCookie.getName(), sessionCookie.getValue()); this.sessionCookie = String.format(FORMAT, sessionCookie.getName(), sessionCookie.getValue());
} }
this.clientEndpoint = clientEndpoint;
} }
@Override @Override
@@ -58,6 +60,7 @@ public class TimelyEndpointConfig implements ClientEndpointConfig {
if (null != sessionCookie) { if (null != sessionCookie) {
headers.put("Cookie", Collections.singletonList(sessionCookie)); headers.put("Cookie", Collections.singletonList(sessionCookie));
} }
TimelyEndpointConfig.this.clientEndpoint.beforeRequest(headers);
} }
}; };
} }
@@ -40,6 +40,7 @@ public class WebSocketClient implements AutoCloseable {
private final int timelyHttpsPort; private final int timelyHttpsPort;
private final int timelyWssPort; private final int timelyWssPort;
private final boolean doLogin; private final boolean doLogin;
private final boolean clientAuth;
private final String timelyUsername; private final String timelyUsername;
private final String timelyPassword; private final String timelyPassword;
private final boolean hostVerificationEnabled; private final boolean hostVerificationEnabled;
@@ -51,13 +52,14 @@ public class WebSocketClient implements AutoCloseable {
protected volatile boolean closed = true; protected volatile boolean closed = true;
protected WebSocketClient(SSLContext ssl, String timelyHostname, int timelyHttpsPort, int timelyWssPort, protected WebSocketClient(SSLContext ssl, String timelyHostname, int timelyHttpsPort, int timelyWssPort,
boolean doLogin, String timelyUsername, String timelyPassword, boolean hostVerificationEnabled, boolean clientAuth, boolean doLogin, String timelyUsername, String timelyPassword,
int bufferSize) { boolean hostVerificationEnabled, int bufferSize) {
this.ssl = ssl; this.ssl = ssl;
this.timelyHostname = timelyHostname; this.timelyHostname = timelyHostname;
this.timelyHttpsPort = timelyHttpsPort; this.timelyHttpsPort = timelyHttpsPort;
this.timelyWssPort = timelyWssPort; this.timelyWssPort = timelyWssPort;
this.doLogin = doLogin; this.doLogin = doLogin;
this.clientAuth = clientAuth;
this.timelyUsername = timelyUsername; this.timelyUsername = timelyUsername;
this.timelyPassword = timelyPassword; this.timelyPassword = timelyPassword;
this.hostVerificationEnabled = hostVerificationEnabled; this.hostVerificationEnabled = hostVerificationEnabled;
@@ -67,28 +69,28 @@ public class WebSocketClient implements AutoCloseable {
Preconditions.checkNotNull(timelyHttpsPort, "%s must be supplied", "Timely HTTPS port"); Preconditions.checkNotNull(timelyHttpsPort, "%s must be supplied", "Timely HTTPS port");
Preconditions.checkNotNull(timelyWssPort, "%s must be supplied", "Timely WSS port"); Preconditions.checkNotNull(timelyWssPort, "%s must be supplied", "Timely WSS port");
if (doLogin && ((StringUtils.isEmpty(timelyUsername) && !StringUtils.isEmpty(timelyPassword) if (doLogin && !clientAuth) {
|| (!StringUtils.isEmpty(timelyUsername) && StringUtils.isEmpty(timelyPassword))))) { if ((StringUtils.isEmpty(timelyUsername) && !StringUtils.isEmpty(timelyPassword)
throw new IllegalArgumentException("Both Timely username and password must be empty or non-empty"); || (!StringUtils.isEmpty(timelyUsername) && StringUtils.isEmpty(timelyPassword)))) {
throw new IllegalArgumentException("Both Timely username and password must be empty or non-empty");
}
} }
} }
protected WebSocketClient(String timelyHostname, int timelyHttpsPort, int timelyWssPort, boolean doLogin, protected WebSocketClient(String timelyHostname, int timelyHttpsPort, int timelyWssPort, boolean clientAuth,
String timelyUsername, String timelyPassword, String keyStoreFile, String keyStoreType, String keyStorePass, boolean doLogin, String timelyUsername, String timelyPassword, String keyStoreFile, String keyStoreType,
String trustStoreFile, String trustStoreType, String trustStorePass, boolean hostVerificationEnabled, String keyStorePass, String trustStoreFile, String trustStoreType, String trustStorePass,
int bufferSize) { boolean hostVerificationEnabled, int bufferSize) {
this(HttpClient.getSSLContext(trustStoreFile, trustStoreType, trustStorePass, keyStoreFile, keyStoreType, this(HttpClient.getSSLContext(trustStoreFile, trustStoreType, trustStorePass, keyStoreFile, keyStoreType,
keyStorePass), timelyHostname, timelyHttpsPort, timelyWssPort, doLogin, timelyUsername, timelyPassword, keyStorePass), timelyHostname, timelyHttpsPort, timelyWssPort, clientAuth, doLogin, timelyUsername,
hostVerificationEnabled, bufferSize); timelyPassword, hostVerificationEnabled, bufferSize);
} }
public void open(ClientHandler clientEndpoint) throws IOException, DeploymentException, URISyntaxException { public void open(ClientHandler clientEndpoint) throws IOException, DeploymentException, URISyntaxException {
Cookie sessionCookie = null; Cookie sessionCookie = null;
if (doLogin) { if (doLogin) {
BasicCookieStore cookieJar = new BasicCookieStore(); BasicCookieStore cookieJar = new BasicCookieStore();
try (CloseableHttpClient client = HttpClient.get(ssl, cookieJar, hostVerificationEnabled)) { try (CloseableHttpClient client = HttpClient.get(ssl, cookieJar, hostVerificationEnabled, clientAuth)) {
String target = "https://" + timelyHostname + ":" + timelyHttpsPort + "/login"; String target = "https://" + timelyHostname + ":" + timelyHttpsPort + "/login";
@@ -140,8 +142,8 @@ public class WebSocketClient implements AutoCloseable {
webSocketClient.getProperties().put(ClientProperties.SSL_ENGINE_CONFIGURATOR, sslEngine); webSocketClient.getProperties().put(ClientProperties.SSL_ENGINE_CONFIGURATOR, sslEngine);
webSocketClient.getProperties().put(ClientProperties.INCOMING_BUFFER_SIZE, bufferSize); webSocketClient.getProperties().put(ClientProperties.INCOMING_BUFFER_SIZE, bufferSize);
String wssPath = "wss://" + timelyHostname + ":" + timelyWssPort + "/websocket"; String wssPath = "wss://" + timelyHostname + ":" + timelyWssPort + "/websocket";
session = webSocketClient.connectToServer(clientEndpoint, new TimelyEndpointConfig(sessionCookie), session = webSocketClient.connectToServer(clientEndpoint,
new URI(wssPath)); new TimelyEndpointConfig(clientEndpoint, sessionCookie), new URI(wssPath));
final ByteBuffer pingData = ByteBuffer.allocate(0); final ByteBuffer pingData = ByteBuffer.allocate(0);
webSocketClient.getScheduledExecutorService().scheduleAtFixedRate(() -> { webSocketClient.getScheduledExecutorService().scheduleAtFixedRate(() -> {
@@ -27,21 +27,21 @@ public class WebSocketSubscriptionClient extends WebSocketClient {
private final String subscriptionId; private final String subscriptionId;
public WebSocketSubscriptionClient(SSLContext ssl, String timelyHostname, int timelyHttpsPort, int timelyWssPort, public WebSocketSubscriptionClient(SSLContext ssl, String timelyHostname, int timelyHttpsPort, int timelyWssPort,
boolean doLogin, String timelyUsername, String timelyPassword, boolean hostVerificationEnabled, boolean clientAuth, boolean doLogin, String timelyUsername, String timelyPassword,
int bufferSize) { boolean hostVerificationEnabled, int bufferSize) {
super(ssl, timelyHostname, timelyHttpsPort, timelyWssPort, doLogin, timelyUsername, timelyPassword, super(ssl, timelyHostname, timelyHttpsPort, timelyWssPort, clientAuth, doLogin, timelyUsername, timelyPassword,
hostVerificationEnabled, bufferSize); hostVerificationEnabled, bufferSize);
subscriptionId = UUID.randomUUID().toString(); subscriptionId = UUID.randomUUID().toString();
LOG.trace("Created WebSocketClient with subscriptionId {}", this.subscriptionId); LOG.trace("Created WebSocketClient with subscriptionId {}", this.subscriptionId);
} }
public WebSocketSubscriptionClient(String timelyHostname, int timelyHttpsPort, int timelyWssPort, boolean doLogin, public WebSocketSubscriptionClient(String timelyHostname, int timelyHttpsPort, int timelyWssPort,
String timelyUsername, String timelyPassword, String keyStoreFile, String keyStoreType, String keyStorePass, boolean clientAuth, boolean doLogin, String timelyUsername, String timelyPassword, String keyStoreFile,
String trustStoreFile, String trustStoreType, String trustStorePass, boolean hostVerificationEnabled, String keyStoreType, String keyStorePass, String trustStoreFile, String trustStoreType,
int bufferSize) { String trustStorePass, boolean hostVerificationEnabled, int bufferSize) {
super(timelyHostname, timelyHttpsPort, timelyWssPort, doLogin, timelyUsername, timelyPassword, keyStoreFile, super(timelyHostname, timelyHttpsPort, timelyWssPort, clientAuth, doLogin, timelyUsername, timelyPassword,
keyStoreType, keyStorePass, trustStoreFile, trustStoreType, trustStorePass, hostVerificationEnabled, keyStoreFile, keyStoreType, keyStorePass, trustStoreFile, trustStoreType, trustStorePass,
bufferSize); hostVerificationEnabled, bufferSize);
subscriptionId = UUID.randomUUID().toString(); subscriptionId = UUID.randomUUID().toString();
LOG.trace("Created WebSocketClient with subscriptionId {}", this.subscriptionId); LOG.trace("Created WebSocketClient with subscriptionId {}", this.subscriptionId);
} }
@@ -22,7 +22,7 @@ import timely.model.parse.TagParser;
@JsonRootName("metric") @JsonRootName("metric")
@JsonInclude(JsonInclude.Include.NON_NULL) @JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({ "name" }) @JsonPropertyOrder({ "name" })
public class Metric implements Serializable { public class Metric implements Serializable, ObjectSizeOf {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@@ -186,4 +186,9 @@ public class Metric implements Serializable {
return this.metric; return this.metric;
} }
} }
@Override
public long sizeInBytes() {
return 0;
}
} }
@@ -2,13 +2,16 @@ package timely.model;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.Stack;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.apache.commons.lang3.StringUtils;
/** /**
* A simple interface that objects can implement to return the object size. * A simple interface that objects can implement to return the object size.
*/ */
@@ -22,15 +25,29 @@ public interface ObjectSizeOf {
public static class ObjectInstance { public static class ObjectInstance {
private Object o; private Object o;
private String name = "";
public ObjectInstance(Object _o) { public ObjectInstance(Object _o) {
this.o = _o; this.o = _o;
} }
public ObjectInstance(Object _o, String name) {
this.o = _o;
this.name = name;
}
public Object getObject() { public Object getObject() {
return o; return o;
} }
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override @Override
public int hashCode() { public int hashCode() {
return System.identityHashCode(o); return System.identityHashCode(o);
@@ -62,113 +79,136 @@ public interface ObjectSizeOf {
* ObjectSizeOf interface and if implemented on the object will use that. * ObjectSizeOf interface and if implemented on the object will use that.
* Otherwise it will do a simple navigation of the fields using reflection. * Otherwise it will do a simple navigation of the fields using reflection.
* *
* @param o
* @return an approximation of the object size * @return an approximation of the object size
*/ */
public static long getObjectSize(Object o, boolean useSizeInBytesMethod, boolean debug) {
long estSize = getObjectSize(o, new HashSet<ObjectInstance>(), new Stack<ObjectInstance>(),
useSizeInBytesMethod, debug);
return estSize;
}
public static long getObjectSize(Object o) { public static long getObjectSize(Object o) {
long estSize = getObjectSize(o, new HashSet<ObjectInstance>(), new Stack<ObjectInstance>(), true, false); return getObjectSize(o, true, false);
return estSize;
} }
public static long getObjectSize(Object o, Set<ObjectInstance> visited, Stack<ObjectInstance> stack, public static long getObjectSize(Object o, boolean useSizeInBytesMethod, boolean debug) {
boolean useSizeInBytesMethod, boolean debug) { List<String> output = null;
long totalSize = 0; if (debug) {
stack.add(new ObjectInstance(o)); output = new ArrayList<>();
while (!stack.isEmpty()) {
long size = 0;
ObjectInstance oi = stack.pop();
o = oi.getObject();
if (o != null && !visited.contains(oi)) {
visited.add(oi);
try {
if (useSizeInBytesMethod) {
try {
if (o instanceof ObjectSizeOf) {
size = ((ObjectSizeOf) o).sizeInBytes();
} else {
Method sizeInBytes = o.getClass().getMethod("sizeInBytes", (Class<?>[]) null);
size = (Long) sizeInBytes.invoke(o);
}
} catch (Throwable t) {
// ok, lets do this the hard way...
}
}
if (size == 0) {
// the hard way...
// do not include Class related objects or
// reflection objects
if ((o instanceof Class) || (o instanceof ClassLoader) || (o.getClass().getPackage() != null
&& o.getClass().getPackage().getName().startsWith("java.lang.reflect"))) {
size = 0;
} else if (o instanceof Number || o instanceof Boolean || o instanceof Character) {
size = NUMBER_SIZE;
} else {
// lets do a simple sizing
Class<?> c = o.getClass();
if (c.isArray()) {
size = ARRAY_OVERHEAD;
int length = Array.getLength(o);
if (c.getComponentType().isPrimitive()) {
size += length * getPrimitiveObjectSize(c.getComponentType());
} else {
size += length * REFERENCE;
for (int i = 0; i < length; i++) {
Object element = Array.get(o, i);
if (element != null) {
stack.add(new ObjectInstance(element));
}
}
}
} else {
size += OBJECT_OVERHEAD;
while (c != null) {
for (Field field : c.getDeclaredFields()) {
if (Modifier.isStatic(field.getModifiers())) {
continue;
}
if (field.getType().isPrimitive()) {
size += getPrimitiveObjectSize(field.getType());
} else {
size += REFERENCE;
boolean accessible = field.isAccessible();
field.setAccessible(true);
try {
Object fieldObject = field.get(o);
if (fieldObject != null) {
stack.push(new ObjectInstance(fieldObject));
}
} catch (Exception e) {
// cannot get to field, so
// ignore it in this size
// calculation
e.printStackTrace();
}
field.setAccessible(accessible);
}
}
c = c.getSuperclass();
}
}
size = roundUp(size);
}
}
} catch (Throwable t) {
log.warning("Unable to determine object size for " + o);
}
if (debug) {
System.out.println(o.getClass().getCanonicalName() + "=" + size);
}
}
totalSize += size;
} }
long size = getObjectSize(new ObjectInstance(o, o.getClass().getSimpleName()), new HashSet<>(), 0,
useSizeInBytesMethod, output);
if (debug) {
Collections.reverse(output);
for (String s : output)
System.out.println(s);
}
return size;
}
return totalSize; public static long getObjectSize(ObjectInstance oi, Set<ObjectInstance> visited, int indent,
boolean useSizeInBytesMethod, List<String> output) {
long size = 0;
Object o = oi.getObject();
int arrayOverhead = 0;
int objectOverhead = 0;
int reference = 0;
boolean roundUp = false;
if (o != null && !visited.contains(oi)) {
visited.add(oi);
try {
if (useSizeInBytesMethod) {
if (o instanceof ObjectSizeOf) {
size = ((ObjectSizeOf) o).sizeInBytes();
if (output != null && size > 0) {
output.add(StringUtils.repeat(' ', indent) + oi.getName() + " [" + size + "] ("
+ o.getClass().getCanonicalName() + ") [ObjectSizeOf]");
}
}
}
if (size == 0) {
// the hard way...
// do not include Class related objects or
// reflection objects
if ((o instanceof Class) || (o instanceof ClassLoader) || (o.getClass().getPackage() != null
&& o.getClass().getPackage().getName().startsWith("java.lang.reflect"))) {
size = 0;
} else if (o instanceof Number || o instanceof Boolean || o instanceof Character) {
size = NUMBER_SIZE;
} else {
// lets do a simple sizing
Class<?> c = o.getClass();
if (c.isArray()) {
arrayOverhead++;
int length = Array.getLength(o);
if (c.getComponentType().isPrimitive()) {
size += length * getPrimitiveObjectSize(c.getComponentType());
} else {
reference += length;
for (int i = 0, index = length - 1; i < length; i++, index--) {
Object element = Array.get(o, index);
if (element != null) {
size += getObjectSize(
new ObjectInstance(element, oi.getName() + "[" + index + "]"),
visited, indent + 2, useSizeInBytesMethod, output);
}
}
}
} else {
objectOverhead++;
while (c != null) {
for (Field field : c.getDeclaredFields()) {
if (Modifier.isStatic(field.getModifiers())) {
continue;
}
if (field.getType().isPrimitive()) {
long primSize = getPrimitiveObjectSize(field.getType());
if (output != null) {
output.add(StringUtils.repeat(' ', indent + 2) + field.getName() + " ["
+ primSize + "] (" + field.getType().getCanonicalName() + ")");
}
size += primSize;
} else {
reference++;
boolean accessible = field.isAccessible();
field.setAccessible(true);
try {
Object fieldObject = field.get(o);
if (fieldObject != null) {
size += getObjectSize(
new ObjectInstance(fieldObject, field.getName()), visited,
indent + 2, useSizeInBytesMethod, output);
}
} catch (Exception e) {
// cannot get to field, so
// ignore it in this size
// calculation
e.printStackTrace();
}
field.setAccessible(accessible);
}
}
c = c.getSuperclass();
}
}
roundUp = true;
}
}
long underlyingObjects = size;
long overheads = REFERENCE * reference + OBJECT_OVERHEAD * objectOverhead
+ ARRAY_OVERHEAD * arrayOverhead;
long addedOverheads = size + overheads;
if (roundUp) {
size = roundUp(addedOverheads);
} else {
size = addedOverheads;
}
if (output != null) {
output.add(StringUtils.repeat(' ', indent) + oi.getName() + " [" + size + "] ("
+ o.getClass().getCanonicalName() + ") = " + underlyingObjects + "+" + overheads + "+"
+ (size - addedOverheads) + " REF=" + reference + " OBJ=" + objectOverhead + " ARRAY="
+ arrayOverhead + " ROUND=" + (size - addedOverheads));
}
} catch (Throwable t) {
log.warning("Unable to determine object size for " + o);
}
}
return size;
} }
public static long roundUp(long size) { public static long roundUp(long size) {
@@ -192,6 +232,42 @@ public interface ObjectSizeOf {
return 0; return 0;
} }
} }
public static long getStringSize(String s) {
long size = 0;
size += ObjectSizeOf.Sizer.OBJECT_OVERHEAD;
size += ObjectSizeOf.Sizer.REFERENCE;
size += ObjectSizeOf.Sizer.getPrimitiveObjectSize(int.class);
try {
// change with Java 9
if (String.class.getDeclaredField("coder") != null) {
size += ObjectSizeOf.Sizer.getPrimitiveObjectSize(byte.class);
}
} catch (Exception e) {
}
size += roundUp(ARRAY_OVERHEAD + (s.length() * ObjectSizeOf.Sizer.getPrimitiveObjectSize(byte.class)));
return roundUp(size);
}
}
public static void main(String[] args) {
Metric m = new Metric("mymetric", 100000l, 32);
m.addTag(new Tag("key1", "value1"));
m.addTag(new Tag("key2", "value2"));
m.addTag(new Tag("key3", "value3"));
m.addTag(new Tag("key4", "value4"));
m.addTag(new Tag("key5", "value5"));
long newSize1 = ObjectSizeOf.Sizer.getObjectSize(m, false, true);
System.out.println("new size #1: " + newSize1);
System.out.println();
System.out.println();
System.out.println();
long newSize2 = ObjectSizeOf.Sizer.getObjectSize(m, true, true);
System.out.println("new size #2: " + newSize2);
} }
} }
+32 -3
View File
@@ -18,7 +18,7 @@ import timely.model.parse.TagParser;
* Tag consists of key value pair * Tag consists of key value pair
*/ */
@JsonRootName("tag") @JsonRootName("tag")
public class Tag implements Comparable<Tag>, Serializable { public class Tag implements Comparable<Tag>, ObjectSizeOf, Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@@ -27,6 +27,12 @@ public class Tag implements Comparable<Tag>, Serializable {
private String key; private String key;
private String value; private String value;
@JsonIgnore
private boolean mustRecalculateLength = true;
@JsonIgnore
private long length;
public Tag() { public Tag() {
} }
@@ -55,6 +61,7 @@ public class Tag implements Comparable<Tag>, Serializable {
public void set(String key, String value) { public void set(String key, String value) {
this.key = key; this.key = key;
this.value = value; this.value = value;
this.mustRecalculateLength = true;
} }
@JsonIgnore @JsonIgnore
@@ -64,6 +71,7 @@ public class Tag implements Comparable<Tag>, Serializable {
public void setKey(String key) { public void setKey(String key) {
this.key = key; this.key = key;
this.mustRecalculateLength = true;
} }
@JsonIgnore @JsonIgnore
@@ -73,12 +81,11 @@ public class Tag implements Comparable<Tag>, Serializable {
public void setValue(String value) { public void setValue(String value) {
this.value = value; this.value = value;
this.mustRecalculateLength = true;
} }
/** /**
* Joins key and value into key=value * Joins key and value into key=value
*
* @return
*/ */
public String join() { public String join() {
return tagParser.combine(this); return tagParser.combine(this);
@@ -108,4 +115,26 @@ public class Tag implements Comparable<Tag>, Serializable {
public int compareTo(Tag other) { public int compareTo(Tag other) {
return new CompareToBuilder().append(this.key, other.key).append(this.value, other.value).toComparison(); return new CompareToBuilder().append(this.key, other.key).append(this.value, other.value).toComparison();
} }
@JsonIgnore
@Override
public long sizeInBytes() {
if (this.mustRecalculateLength) {
calculateLength();
this.mustRecalculateLength = false;
}
return this.length;
}
private void calculateLength() {
long l = 0;
l += 2 * Sizer.REFERENCE;
l += Sizer.OBJECT_OVERHEAD;
l += Sizer.getPrimitiveObjectSize(long.class);
l += Sizer.getPrimitiveObjectSize(boolean.class);
l += Sizer.getStringSize(key);
l += Sizer.getStringSize(value);
this.length = Sizer.roundUp(l);
}
} }
+13 -1
View File
@@ -5,13 +5,14 @@ import java.util.Comparator;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Objects; import com.google.common.base.Objects;
/** /**
* Value consists of a long timestamp and a double measurement * Value consists of a long timestamp and a double measurement
*/ */
public class Value implements Comparator<Value>, Serializable { public class Value implements Comparator<Value>, ObjectSizeOf, Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@@ -90,4 +91,15 @@ public class Value implements Comparator<Value>, Serializable {
public String toString() { public String toString() {
return "Value{" + "timestamp=" + timestamp + ", measure=" + measure + "}"; return "Value{" + "timestamp=" + timestamp + ", measure=" + measure + "}";
} }
@JsonIgnore
@Override
public long sizeInBytes() {
long l = 0;
l += Sizer.OBJECT_OVERHEAD;
l += 2 * Sizer.REFERENCE;
l += Sizer.NUMBER_SIZE;
l += Sizer.NUMBER_SIZE;
return Sizer.roundUp(l);
}
} }
@@ -47,7 +47,6 @@ public class TagListParser implements Parser<List<Tag>>, Combiner<Collection<Tag
* An alternative combiner for a map of string to string instead of a list of * An alternative combiner for a map of string to string instead of a list of
* tags * tags
* *
* @param tags
* @return string representation * @return string representation
*/ */
public String combine(Map<String, String> tags) { public String combine(Map<String, String> tags) {
@@ -23,7 +23,7 @@ def pivot(df, metric, groupByColumn=None):
return dataFrame return dataFrame
def resample(df, sample, how='mean'): def resample(df, sample, how='mean', interpolate=True, fill=None):
dataFrame = pandas.DataFrame(df, copy=True) dataFrame = pandas.DataFrame(df, copy=True)
if dataFrame is not None: if dataFrame is not None:
@@ -2,7 +2,6 @@ import time
from datetime import datetime from datetime import datetime
from datetime import timedelta from datetime import timedelta
from datetime import tzinfo from datetime import tzinfo
from pandas.tslib import Timedelta
import pandas import pandas
class TimeDateError(ValueError): class TimeDateError(ValueError):
@@ -16,14 +16,15 @@ utc = UTC()
class TimelyWebSocketClient(WebSocketClient): class TimelyWebSocketClient(WebSocketClient):
def __init__(self, hostport, metric, tags, startTime, endTime, connect_timeout=WebSocketClient.DEFAULT_CONNECT_TIMEOUT, def __init__(self, hostport, metric, tags, startTime, endTime, connect_timeout=WebSocketClient.DEFAULT_CONNECT_TIMEOUT,
request_timeout=WebSocketClient.DEFAULT_REQUEST_TIMEOUT): request_timeout=WebSocketClient.DEFAULT_REQUEST_TIMEOUT, client_cert=None, client_key=None, ca_certs=None, validate_cert=False):
self.metric = metric self.metric = metric
self.tags = tags self.tags = tags
self.startTime = startTime self.startTime = startTime
self.endTime = endTime self.endTime = endTime
self.subscriptionId = random.randint(1000000000, 9999999999) self.subscriptionId = random.randint(1000000000, 9999999999)
WebSocketClient.__init__(self, hostport, '/websocket', {'metric' : metric}, connect_timeout, request_timeout) WebSocketClient.__init__(self, hostport, '/websocket', {'metric' : metric}, connect_timeout, request_timeout,
client_cert=client_cert, client_key=client_key, ca_certs=ca_certs, validate_cert=validate_cert)
def _on_message(self, msg): def _on_message(self, msg):
# implement in subclass # implement in subclass
@@ -72,7 +73,8 @@ class TimelyMetric(TimelyWebSocketClient):
client = None client = None
endtime = None endtime = None
def __init__(self, hostport, metric, tags, beginStr, endStr, period, sample): def __init__(self, hostport, metric, tags, beginStr, endStr, period, sample,
client_cert=None, client_key=None, ca_certs=None, validate_cert=False):
self.series = pandas.Series() self.series = pandas.Series()
self.metric = metric self.metric = metric
self.tags = tags self.tags = tags
@@ -82,7 +84,8 @@ class TimelyMetric(TimelyWebSocketClient):
self.data = [] self.data = []
self.timeDateRange = TimeDateRange(beginStr, endStr, period) self.timeDateRange = TimeDateRange(beginStr, endStr, period)
TimelyWebSocketClient.__init__(self, hostport, metric, tags, self.timeDateRange.getBeginMs(), self.timeDateRange.getEndMs()) TimelyWebSocketClient.__init__(self, hostport, metric, tags, self.timeDateRange.getBeginMs(), self.timeDateRange.getEndMs(),
client_cert=client_cert, client_key=client_key, ca_certs=ca_certs, validate_cert=validate_cert)
def debugOn(self): def debugOn(self):
self.debug = True self.debug = True
@@ -15,10 +15,14 @@ class WebSocketClient():
APPLICATION_JSON = 'application/json' APPLICATION_JSON = 'application/json'
def __init__(self, hostport, path, params, connect_timeout=DEFAULT_CONNECT_TIMEOUT, def __init__(self, hostport, path, params, connect_timeout=DEFAULT_CONNECT_TIMEOUT,
request_timeout=DEFAULT_REQUEST_TIMEOUT): request_timeout=DEFAULT_REQUEST_TIMEOUT, client_cert=None, client_key=None, ca_certs=None, validate_cert=False):
self.connect_timeout = connect_timeout self.connect_timeout = connect_timeout
self.request_timeout = request_timeout self.request_timeout = request_timeout
self.client_cert=client_cert
self.client_key=client_key
self.ca_certs=ca_certs
self.validate_cert=validate_cert
self.url = 'wss://' + hostport self.url = 'wss://' + hostport
if (path is not None and len(path) > 0): if (path is not None and len(path) > 0):
if (path.startswith('/')): if (path.startswith('/')):
@@ -39,7 +43,8 @@ class WebSocketClient():
"""Connect to the server. """Connect to the server.
:param str url: server URL. :param str url: server URL.
""" """
request = websocket.httpclient.HTTPRequest(self.url, connect_timeout=self.connect_timeout, request_timeout=self.request_timeout, validate_cert=False) request = websocket.httpclient.HTTPRequest(self.url, connect_timeout=self.connect_timeout, request_timeout=self.request_timeout, validate_cert=self.validate_cert,
client_cert=self.client_cert, client_key=self.client_key, ca_certs=self.ca_certs)
websocket.websocket_connect(request, io_loop=ioloop, callback=self._connect_callback) websocket.websocket_connect(request, io_loop=ioloop, callback=self._connect_callback)
def send(self, data): def send(self, data):
@@ -5,7 +5,9 @@ import os
from timely.TimelyAnalyticConfiguration import TimelyAnalyticConfiguration from timely.TimelyAnalyticConfiguration import TimelyAnalyticConfiguration
hostport = "127.0.0.1:5244" hostport = "127.0.0.1:5244"
timelyMetric = timely.TimelyMetric.TimelyMetric(hostport, 'timely.keys.metric.inserted', None, None, None, '24 hours', None).fetch() client_cert = None
client_key = None
timelyMetric = timely.TimelyMetric.TimelyMetric(hostport, 'timely.keys.metric.inserted', None, None, None, '24 hours', None, client_cert=client_cert, client_key=client_key).fetch()
print(timelyMetric.getDataFrame()) print(timelyMetric.getDataFrame())
exit() exit()
+5
View File
@@ -5,4 +5,9 @@
<Method name="connect" /> <Method name="connect" />
<Class name="timely.client.tcp.TcpClient"/> <Class name="timely.client.tcp.TcpClient"/>
</Match> </Match>
<Match>
<!-- Must ignore these everywhere, because of a javac byte code generation bug -->
<!-- https://github.com/spotbugs/spotbugs/issues/756 -->
<Bug pattern="RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE" />
</Match>
</FindBugsFilter> </FindBugsFilter>
@@ -47,9 +47,9 @@ public class SerializationTest {
testSerialization(remove); testSerialization(remove);
} }
@SuppressWarnings("unchecked")
private <T> void testSerialization(T o) throws Exception { private <T> void testSerialization(T o) throws Exception {
String ser = mapper.writeValueAsString(o); String ser = mapper.writeValueAsString(o);
@SuppressWarnings("unchecked")
T des = mapper.readValue(ser, (Class<T>) o.getClass()); T des = mapper.readValue(ser, (Class<T>) o.getClass());
Assert.assertEquals(o, des); Assert.assertEquals(o, des);
} }
+1 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>timely</groupId> <groupId>timely</groupId>
<artifactId>timely</artifactId> <artifactId>timely</artifactId>
<version>1.0.1-SNAPSHOT</version> <version>1.2.8-SNAPSHOT</version>
</parent> </parent>
<artifactId>collectd-nsq-plugin</artifactId> <artifactId>collectd-nsq-plugin</artifactId>
<dependencies> <dependencies>
@@ -10,7 +10,7 @@ import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClientBuilder;
import org.collectd.api.OConfigItem; import org.collectd.api.OConfigItem;
public class PooledCloseableHttpClientFactory implements PooledObjectFactory { public class PooledCloseableHttpClientFactory implements PooledObjectFactory<CloseableHttpClient> {
private int connectionRequestTimeout = 10000; // 10 seconds in milliseconds private int connectionRequestTimeout = 10000; // 10 seconds in milliseconds
private int connectTimeout = 30000; // 30 seconds in milliseconds private int connectTimeout = 30000; // 30 seconds in milliseconds
@@ -43,19 +43,19 @@ public class PooledCloseableHttpClientFactory implements PooledObjectFactory {
} }
@Override @Override
public PooledObject makeObject() throws Exception { public PooledObject<CloseableHttpClient> makeObject() throws Exception {
RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(connectionRequestTimeout) RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(connectionRequestTimeout)
.setConnectTimeout(connectTimeout).setSocketTimeout(socketTimeout).build(); .setConnectTimeout(connectTimeout).setSocketTimeout(socketTimeout).build();
HttpClientBuilder builder = HttpClientBuilder.create(); HttpClientBuilder builder = HttpClientBuilder.create();
builder.setDefaultRequestConfig(requestConfig); builder.setDefaultRequestConfig(requestConfig);
builder.setConnectionTimeToLive(connectionTimeToLive, TimeUnit.MILLISECONDS); builder.setConnectionTimeToLive(connectionTimeToLive, TimeUnit.MILLISECONDS);
return new DefaultPooledObject(builder.build()); return new DefaultPooledObject<>(builder.build());
} }
@Override @Override
public void destroyObject(PooledObject pooledObject) throws Exception { public void destroyObject(PooledObject<CloseableHttpClient> pooledObject) throws Exception {
try { try {
((CloseableHttpClient) pooledObject.getObject()).close(); pooledObject.getObject().close();
} catch (Exception e) { } catch (Exception e) {
// do nothing // do nothing
} finally { } finally {
@@ -65,17 +65,17 @@ public class PooledCloseableHttpClientFactory implements PooledObjectFactory {
} }
@Override @Override
public boolean validateObject(PooledObject pooledObject) { public boolean validateObject(PooledObject<CloseableHttpClient> pooledObject) {
return pooledObject.getObject() != null; return pooledObject.getObject() != null;
} }
@Override @Override
public void activateObject(PooledObject pooledObject) throws Exception { public void activateObject(PooledObject<CloseableHttpClient> pooledObject) throws Exception {
// do nothing // do nothing
} }
@Override @Override
public void passivateObject(PooledObject pooledObject) throws Exception { public void passivateObject(PooledObject<CloseableHttpClient> pooledObject) throws Exception {
// do nothing // do nothing
} }
@@ -47,6 +47,7 @@ public class WriteNSQPlugin extends CollectDPluginParent
Collectd.registerWrite(WriteNSQPlugin.class.getName(), this); Collectd.registerWrite(WriteNSQPlugin.class.getName(), this);
} }
@Override
public int write(ValueList vl) { public int write(ValueList vl) {
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(baos); DataOutputStream out = new DataOutputStream(baos);
@@ -110,15 +111,17 @@ public class WriteNSQPlugin extends CollectDPluginParent
} }
} }
@Override
public int shutdown() { public int shutdown() {
Collectd.logInfo("Shutting down connection to NSQ at " + host + ":" + port); Collectd.logInfo("Shutting down connection to NSQ at " + host + ":" + port);
clientPool.close(); clientPool.close();
return 0; return 0;
} }
@Override
public int config(OConfigItem config) { public int config(OConfigItem config) {
super.config(config); super.config(config);
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); GenericObjectPoolConfig<CloseableHttpClient> poolConfig = new GenericObjectPoolConfig<>();
// use max size for maxTotal and maxIdle // use max size for maxTotal and maxIdle
// no need to activate or passivate // no need to activate or passivate
poolConfig.setMaxTotal(POOL_MAX_SIZE); poolConfig.setMaxTotal(POOL_MAX_SIZE);
@@ -156,7 +159,7 @@ public class WriteNSQPlugin extends CollectDPluginParent
Collectd.logError("NSQ host, port, and topic must be configured"); Collectd.logError("NSQ host, port, and topic must be configured");
return -1; return -1;
} }
this.clientPool = new GenericObjectPool(pooledCloseableHttpClientFactory, poolConfig); this.clientPool = new GenericObjectPool<>(pooledCloseableHttpClientFactory, poolConfig);
this.endpoint = "http://" + host + ":" + port + "/mpub?topic=" + topic; this.endpoint = "http://" + host + ":" + port + "/mpub?topic=" + topic;
return 0; return 0;
} }
+1 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>timely</groupId> <groupId>timely</groupId>
<artifactId>timely</artifactId> <artifactId>timely</artifactId>
<version>1.0.1-SNAPSHOT</version> <version>1.2.8-SNAPSHOT</version>
</parent> </parent>
<artifactId>collectd-plugin-parent</artifactId> <artifactId>collectd-plugin-parent</artifactId>
<dependencies> <dependencies>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>timely</groupId> <groupId>timely</groupId>
<artifactId>timely</artifactId> <artifactId>timely</artifactId>
<version>1.0.1-SNAPSHOT</version> <version>1.2.8-SNAPSHOT</version>
</parent> </parent>
<artifactId>collectd-timely-plugin</artifactId> <artifactId>collectd-timely-plugin</artifactId>
<dependencies> <dependencies>
@@ -8,7 +8,7 @@ import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.collectd.api.Collectd; import org.collectd.api.Collectd;
import org.collectd.api.OConfigItem; import org.collectd.api.OConfigItem;
public class PooledSocketFactory implements PooledObjectFactory { public class PooledSocketFactory implements PooledObjectFactory<Socket> {
private String host = null; private String host = null;
private int port = 0; private int port = 0;
@@ -81,12 +81,12 @@ public class PooledSocketFactory implements PooledObjectFactory {
} }
@Override @Override
public PooledObject makeObject() throws Exception { public PooledObject<Socket> makeObject() throws Exception {
return new DefaultPooledObject(connect()); return new DefaultPooledObject<>(connect());
} }
@Override @Override
public void destroyObject(PooledObject pooledObject) throws Exception { public void destroyObject(PooledObject<Socket> pooledObject) throws Exception {
try { try {
Collectd.logDebug("Shutting down connection to Timely at " + host + ":" + port); Collectd.logDebug("Shutting down connection to Timely at " + host + ":" + port);
Socket socket = (Socket) pooledObject.getObject(); Socket socket = (Socket) pooledObject.getObject();
@@ -103,7 +103,7 @@ public class PooledSocketFactory implements PooledObjectFactory {
} }
@Override @Override
public boolean validateObject(PooledObject pooledObject) { public boolean validateObject(PooledObject<Socket> pooledObject) {
if (connectionTimeToLive > -1) { if (connectionTimeToLive > -1) {
if (System.currentTimeMillis() > pooledObject.getCreateTime() + connectionTimeToLive) { if (System.currentTimeMillis() > pooledObject.getCreateTime() + connectionTimeToLive) {
return false; return false;
@@ -114,12 +114,12 @@ public class PooledSocketFactory implements PooledObjectFactory {
} }
@Override @Override
public void activateObject(PooledObject pooledObject) throws Exception { public void activateObject(PooledObject<Socket> pooledObject) throws Exception {
// do nothing // do nothing
} }
@Override @Override
public void passivateObject(PooledObject pooledObject) throws Exception { public void passivateObject(PooledObject<Socket> pooledObject) throws Exception {
// do nothing // do nothing
} }
} }
@@ -86,13 +86,13 @@ public class WriteTimelyPlugin extends CollectDPluginParent
PooledSocketFactory socketFactory = new PooledSocketFactory(); PooledSocketFactory socketFactory = new PooledSocketFactory();
retval = socketFactory.config(config); retval = socketFactory.config(config);
if (retval == 0) { if (retval == 0) {
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); GenericObjectPoolConfig<Socket> poolConfig = new GenericObjectPoolConfig<>();
// use max size for maxTotal and maxIdle // use max size for maxTotal and maxIdle
// no need to activate and passivate // no need to activate and passivate
poolConfig.setMaxTotal(POOL_MAX_SIZE); poolConfig.setMaxTotal(POOL_MAX_SIZE);
poolConfig.setMaxIdle(POOL_MAX_SIZE); poolConfig.setMaxIdle(POOL_MAX_SIZE);
poolConfig.setTestOnReturn(true); poolConfig.setTestOnReturn(true);
socketPool = new GenericObjectPool(socketFactory, poolConfig); socketPool = new GenericObjectPool<>(socketFactory, poolConfig);
} }
return retval; return retval;
} }
+1 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>timely</groupId> <groupId>timely</groupId>
<artifactId>timely</artifactId> <artifactId>timely</artifactId>
<version>1.0.1-SNAPSHOT</version> <version>1.2.8-SNAPSHOT</version>
</parent> </parent>
<artifactId>timely-common</artifactId> <artifactId>timely-common</artifactId>
<dependencies> <dependencies>
+42
View File
@@ -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
+55
View File
@@ -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>
+29
View File
@@ -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>
+71
View File
@@ -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>
+295
View File
@@ -0,0 +1,295 @@
<?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.8-SNAPSHOT</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>
<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
View File
@@ -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,322 @@
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 initializeContext(String[] args) {
SpringApplicationBuilder builder = new SpringApplicationBuilder(SpringBootstrap.class);
builder.web(WebApplicationType.NONE);
builder.registerShutdownHook(false);
return builder.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("Channel:" + f.channel().config() + " -> " + e.getMessage(), e);
}
});
int 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("Group:" + f.toString() + " -> " + e.getMessage(), e);
}
});
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.initializeContext(args);
GrafanaAuthConfiguration grafanaAuthConf = GrafanaAuth.applicationContext
.getBean(GrafanaAuthConfiguration.class);
GrafanaAuth grafanaAuth = new GrafanaAuth(grafanaAuthConf);
try {
grafanaAuth.run();
LATCH.await();
} catch (final InterruptedException e) {
LOG.info("Server shutting down.");
} catch (Exception e) {
LOG.error("Error running server.", e);
} finally {
try {
grafanaAuth.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()
.setIgnoreSslHandshakeErrors(config.getSecurity().getServerSsl().isUseGeneratedKeypair()));
}
};
}
}
@@ -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;
}
}
@@ -0,0 +1,54 @@
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.
*/
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.HttpHeaderNames.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.uri();
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, null);
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.toString())) {
relayedHeaderList.add(new BasicHeader(h.getKey(), h.getValue()));
}
}
Header[] headerArray = new Header[relayedHeaderList.size()];
relayedHeaderList.toArray(headerArray);
relayURI = protocol + "://" + host + ":" + port + originalURI;
if (request.method().equals(HttpMethod.HEAD)) {
HttpHead relayedRequest = new HttpHead(relayURI);
if (headerArray.length > 0) {
relayedRequest.setHeaders(headerArray);
}
relayedResponse = client.execute(relayedRequest);
} else if (request.method().equals(HttpMethod.GET)) {
HttpGet relayedRequest = new HttpGet(relayURI);
if (headerArray.length > 0) {
relayedRequest.setHeaders(headerArray);
}
relayedResponse = client.execute(relayedRequest);
} else if (request.method().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.method().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.method().equals(HttpMethod.DELETE)) {
HttpDelete relayedRequest = new HttpDelete(relayURI);
if (headerArray.length > 0) {
relayedRequest.setHeaders(headerArray);
}
relayedResponse = client.execute(relayedRequest);
} else if (request.method().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().uri());
}
}
}
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.HttpHeaderNames;
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(HttpHeaderNames.COOKIE.toString());
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.uri();
final QueryStringDecoder decoder = new QueryStringDecoder(uri);
if (decoder.path().equals(nonSecureRedirectAddress)) {
out.add(new StrictTransportResponse());
return;
}
final String sessionId = getSessionId(msg);
LOG.trace("SessionID: " + sessionId);
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
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>timely</groupId> <groupId>timely</groupId>
<artifactId>timely</artifactId> <artifactId>timely</artifactId>
<version>1.0.1-SNAPSHOT</version> <version>1.2.8-SNAPSHOT</version>
</parent> </parent>
<artifactId>timely-app</artifactId> <artifactId>timely-app</artifactId>
<packaging>pom</packaging> <packaging>pom</packaging>
+1 -1
View File
@@ -509,7 +509,7 @@ System.register(['lodash', 'angular', '../../../app/core/utils/datemath'], funct
} else { } else {
return target.metric === metricData.metric && _.every(target.tags, function (tagV, tagK) { return target.metric === metricData.metric && _.every(target.tags, function (tagV, tagK) {
interpolatedTagValue = this.templateSrv.replace(tagV, options.scopedVars, 'pipe'); interpolatedTagValue = this.templateSrv.replace(tagV, options.scopedVars, 'pipe');
return metricData.tags[tagK] === interpolatedTagValue || interpolatedTagValue === "*"; return metricData.tags[tagK] === interpolatedTagValue || interpolatedTagValue === ".*";
}.bind(this)); }.bind(this));
} }
}.bind(this)); }.bind(this));
+1 -1
View File
@@ -1 +1 @@
{"version":3,"sources":["../../src/datasource/datasource.js"],"names":["_","angular","dateMath","TimelyDatasource","instanceSettings","$q","backendSrv","templateSrv","contextSrv","type","name","jsonData","httpsPort","timelyHost","wsPort","basicAuths","url","_makeHttpsUrl","withCredentials","basicAuth","supportMetrics","tagKeys","aggregatorsPromise","filterTypesPromise","options","start","convertToTSDBTime","rangeRaw","from","end","to","qs","each","targets","target","metric","push","convertTargetToQuery","bind","queries","compact","isEmpty","d","defer","resolve","data","promise","groupByTags","query","filters","length","val","tagk","tags","key","performTimeSeriesQuery","then","response","metricToTargetMapping","mapMetricsToTargets","result","map","metricData","index","_saveTagKeys","transformMetricData","datasourceRequest","method","status","message","title","eventList","aggregator","annotation","results","annotationObject","annotations","isGlobal","globalAnnotations","event","description","time","Math","floor","startTime","text","notes","when","interpolated","replace","err","reject","responseTransform","value","metrics_regex","tag_names_regex","tag_values_regex","tag_names_suggest_regex","tag_values_suggest_regex","metrics_query","match","_performSuggestQuery","tag_names_query","_performMetricKeyLookup","tag_values_query","_performMetricKeyValueLookup","tag_names_suggest_query","tag_values_suggest_query","msResolution","responsePromises","reqBody","showQuery","_addCredentialOptions","headers","Promise","all","queryResponses","combinedResponse","x","concat","Object","keys","aggregateTags","tag","_get","q","max","keysArray","split","trim","keysQuery","splice","join","m","limit","tagvs","r","indexOf","tagks","tagv","relativeUrl","params","isArray","sort","md","metricLabel","createMetricLabel","dps","v","k","datapoints","alias","scopedVars","clone","label","tagData","toPairs","has","hide","shouldComputeRate","rate","rateOptions","counter","isCounter","counterMax","parseInt","counterResetValue","resetValue","disableDownsampling","interval","downsampleInterval","parseFloat","downsample","downsampleAggregator","downsampleFillPolicy","copy","filter_key","filter","tag_key","metrics","interpolatedTagValue","findIndex","every","tagV","tagK","date","roundUp","parse","valueOf"],"mappings":";;;;;;;;;;;;;;;AAAOA,O;;AACAC,a;;AACKC,c;;;;;;;;;;;;;;;;;;;;;kCAECC,gB;;AAEX;AACA,kCAAYC,gBAAZ,EAA8BC,EAA9B,EAAkCC,UAAlC,EAA8CC,WAA9C,EAA2DC,UAA3D,EAAuE;AAAA;;AACrE,eAAKC,IAAL,GAAYL,iBAAiBK,IAA7B;AACA,eAAKC,IAAL,GAAYN,iBAAiBM,IAA7B;;AAEA,eAAKC,QAAL,GAAgBP,iBAAiBO,QAAjB,IAA6B,EAA7C;AACA,eAAKC,SAAL,GAAiBR,iBAAiBO,QAAjB,CAA0BC,SAA3C;AACA,eAAKC,UAAL,GAAkBT,iBAAiBO,QAAjB,CAA0BE,UAA5C;AACA,eAAKC,MAAL,GAAcV,iBAAiBO,QAAjB,CAA0BG,MAAxC;AACA,cAAI,CAACV,iBAAiBO,QAAjB,CAA0BI,UAA/B,EAA0C;AACxCX,6BAAiBO,QAAjB,CAA0BI,UAA1B,GAAuC,KAAvC;AACD;;AAED,eAAKC,GAAL,GAAW,KAAKC,aAAL,EAAX;AACA,eAAKC,eAAL,GAAuB,IAAvB;AACA,eAAKC,SAAL,GAAiB,KAAjB;;AAEA,eAAKC,cAAL,GAAsB,IAAtB;AACA,eAAKC,OAAL,GAAe,EAAf;;AAEA,eAAKhB,EAAL,GAAUA,EAAV;AACA,eAAKC,UAAL,GAAkBA,UAAlB;AACA,eAAKC,WAAL,GAAmBA,WAAnB;AACA,eAAKC,UAAL,GAAkBA,UAAlB;;AAEA,eAAKc,kBAAL,GAA0B,IAA1B;AACA,eAAKC,kBAAL,GAA0B,IAA1B;AAED;;AAED;;;;;gCACMC,O,EAAQ;AACZ,gBAAIC,QAAQ,KAAKC,iBAAL,CAAuBF,QAAQG,QAAR,CAAiBC,IAAxC,EAA8C,KAA9C,CAAZ;AACA,gBAAIC,MAAM,KAAKH,iBAAL,CAAuBF,QAAQG,QAAR,CAAiBG,EAAxC,EAA4C,IAA5C,CAAV;AACA,gBAAIC,KAAK,EAAT;;AAEA/B,cAAEgC,IAAF,CAAOR,QAAQS,OAAf,EAAwB,UAASC,MAAT,EAAiB;AACvC,kBAAI,CAACA,OAAOC,MAAZ,EAAoB;AAAE;AAAS;AAC/BJ,iBAAGK,IAAH,CAAQ,KAAKC,oBAAL,CAA0BH,MAA1B,EAAkCV,OAAlC,CAAR;AACD,aAHuB,CAGtBc,IAHsB,CAGjB,IAHiB,CAAxB;;AAKA,gBAAIC,UAAUvC,EAAEwC,OAAF,CAAUT,EAAV,CAAd;;AAEA;AACA,gBAAI/B,EAAEyC,OAAF,CAAUF,OAAV,CAAJ,EAAwB;AACtB,kBAAIG,IAAI,KAAKrC,EAAL,CAAQsC,KAAR,EAAR;AACAD,gBAAEE,OAAF,CAAU,EAAEC,MAAM,EAAR,EAAV;AACA,qBAAOH,EAAEI,OAAT;AACD;;AAED,gBAAIC,cAAc,EAAlB;AACA/C,cAAEgC,IAAF,CAAOO,OAAP,EAAgB,UAASS,KAAT,EAAgB;AAC9B,kBAAIA,MAAMC,OAAN,IAAiBD,MAAMC,OAAN,CAAcC,MAAd,GAAuB,CAA5C,EAA+C;AAC7ClD,kBAAEgC,IAAF,CAAOgB,MAAMC,OAAb,EAAsB,UAASE,GAAT,EAAc;AAClCJ,8BAAYI,IAAIC,IAAhB,IAAwB,IAAxB;AACD,iBAFD;AAGD,eAJD,MAIO;AACLpD,kBAAEgC,IAAF,CAAOgB,MAAMK,IAAb,EAAmB,UAASF,GAAT,EAAcG,GAAd,EAAmB;AACpCP,8BAAYO,GAAZ,IAAmB,IAAnB;AACD,iBAFD;AAGD;AACF,aAVD;;AAYA,mBAAO,KAAKC,sBAAL,CAA4BhB,OAA5B,EAAqCd,KAArC,EAA4CI,GAA5C,EAAiD2B,IAAjD,CAAsD,UAASC,QAAT,EAAmB;AAC9E,kBAAIC,wBAAwB,KAAKC,mBAAL,CAAyBF,SAASZ,IAAlC,EAAwCrB,OAAxC,CAA5B;AACA,kBAAIoC,SAAS5D,EAAE6D,GAAF,CAAMJ,SAASZ,IAAf,EAAqB,UAASiB,UAAT,EAAqBC,KAArB,EAA4B;AAC5DA,wBAAQL,sBAAsBK,KAAtB,CAAR;AACA,oBAAIA,UAAU,CAAC,CAAf,EAAkB;AAChBA,0BAAQ,CAAR;AACD;AACD,qBAAKC,YAAL,CAAkBF,UAAlB;;AAEA,uBAAO,KAAKG,mBAAL,CAAyBH,UAAzB,EAAqCf,WAArC,EAAkDvB,QAAQS,OAAR,CAAgB8B,KAAhB,CAAlD,EAA0EvC,OAA1E,CAAP;AACD,eARiC,CAQhCc,IARgC,CAQ3B,IAR2B,CAArB,CAAb;AASA,qBAAO,EAAEO,MAAMe,MAAR,EAAP;AACD,aAZ4D,CAY3DtB,IAZ2D,CAYtD,IAZsD,CAAtD,CAAP;AAaD;;;2CAGe;AACd,mBAAO,KAAKhC,UAAL,CAAgB4D,iBAAhB,CAAkC;AACvClD,mBAAK,KAAKA,GAAL,GAAW,UADuB;AAEvCmD,sBAAQ;AAF+B,aAAlC,EAGJX,IAHI,CAGC,oBAAY;AAClB,kBAAIC,SAASW,MAAT,KAAoB,GLine truncated {"version":3,"sources":["../../src/datasource/datasource.js"],"names":["_","angular","dateMath","TimelyDatasource","instanceSettings","$q","backendSrv","templateSrv","contextSrv","type","name","jsonData","httpsPort","timelyHost","wsPort","basicAuths","url","_makeHttpsUrl","withCredentials","basicAuth","supportMetrics","tagKeys","aggregatorsPromise","filterTypesPromise","options","start","convertToTSDBTime","rangeRaw","from","end","to","qs","each","targets","target","metric","push","convertTargetToQuery","bind","queries","compact","isEmpty","d","defer","resolve","data","promise","groupByTags","query","filters","length","val","tagk","tags","key","performTimeSeriesQuery","then","response","metricToTargetMapping","mapMetricsToTargets","result","map","metricData","index","_saveTagKeys","transformMetricData","datasourceRequest","method","status","message","title","eventList","aggregator","annotation","results","annotationObject","annotations","isGlobal","globalAnnotations","event","description","time","Math","floor","startTime","text","notes","when","interpolated","replace","err","reject","responseTransform","value","metrics_regex","tag_names_regex","tag_values_regex","tag_names_suggest_regex","tag_values_suggest_regex","metrics_query","match","_performSuggestQuery","tag_names_query","_performMetricKeyLookup","tag_values_query","_performMetricKeyValueLookup","tag_names_suggest_query","tag_values_suggest_query","msResolution","responsePromises","reqBody","showQuery","_addCredentialOptions","headers","Promise","all","queryResponses","combinedResponse","x","concat","Object","keys","aggregateTags","tag","_get","q","max","keysArray","split","trim","keysQuery","splice","join","m","limit","tagvs","r","indexOf","tagks","tagv","relativeUrl","params","isArray","sort","md","metricLabel","createMetricLabel","dps","v","k","datapoints","alias","scopedVars","clone","label","tagData","toPairs","has","hide","shouldComputeRate","rate","rateOptions","counter","isCounter","counterMax","parseInt","counterResetValue","resetValue","disableDownsampling","interval","downsampleInterval","parseFloat","downsample","downsampleAggregator","downsampleFillPolicy","copy","filter_key","filter","tag_key","metrics","interpolatedTagValue","findIndex","every","tagV","tagK","date","roundUp","parse","valueOf"],"mappings":";;;;;;;;;;;;;;;AAAOA,O;;AACAC,a;;AACKC,c;;;;;;;;;;;;;;;;;;;;;kCAECC,gB;;AAEX;AACA,kCAAYC,gBAAZ,EAA8BC,EAA9B,EAAkCC,UAAlC,EAA8CC,WAA9C,EAA2DC,UAA3D,EAAuE;AAAA;;AACrE,eAAKC,IAAL,GAAYL,iBAAiBK,IAA7B;AACA,eAAKC,IAAL,GAAYN,iBAAiBM,IAA7B;;AAEA,eAAKC,QAAL,GAAgBP,iBAAiBO,QAAjB,IAA6B,EAA7C;AACA,eAAKC,SAAL,GAAiBR,iBAAiBO,QAAjB,CAA0BC,SAA3C;AACA,eAAKC,UAAL,GAAkBT,iBAAiBO,QAAjB,CAA0BE,UAA5C;AACA,eAAKC,MAAL,GAAcV,iBAAiBO,QAAjB,CAA0BG,MAAxC;AACA,cAAI,CAACV,iBAAiBO,QAAjB,CAA0BI,UAA/B,EAA0C;AACxCX,6BAAiBO,QAAjB,CAA0BI,UAA1B,GAAuC,KAAvC;AACD;;AAED,eAAKC,GAAL,GAAW,KAAKC,aAAL,EAAX;AACA,eAAKC,eAAL,GAAuB,IAAvB;AACA,eAAKC,SAAL,GAAiB,KAAjB;;AAEA,eAAKC,cAAL,GAAsB,IAAtB;AACA,eAAKC,OAAL,GAAe,EAAf;;AAEA,eAAKhB,EAAL,GAAUA,EAAV;AACA,eAAKC,UAAL,GAAkBA,UAAlB;AACA,eAAKC,WAAL,GAAmBA,WAAnB;AACA,eAAKC,UAAL,GAAkBA,UAAlB;;AAEA,eAAKc,kBAAL,GAA0B,IAA1B;AACA,eAAKC,kBAAL,GAA0B,IAA1B;AAED;;AAED;;;;;gCACMC,O,EAAQ;AACZ,gBAAIC,QAAQ,KAAKC,iBAAL,CAAuBF,QAAQG,QAAR,CAAiBC,IAAxC,EAA8C,KAA9C,CAAZ;AACA,gBAAIC,MAAM,KAAKH,iBAAL,CAAuBF,QAAQG,QAAR,CAAiBG,EAAxC,EAA4C,IAA5C,CAAV;AACA,gBAAIC,KAAK,EAAT;;AAEA/B,cAAEgC,IAAF,CAAOR,QAAQS,OAAf,EAAwB,UAASC,MAAT,EAAiB;AACvC,kBAAI,CAACA,OAAOC,MAAZ,EAAoB;AAAE;AAAS;AAC/BJ,iBAAGK,IAAH,CAAQ,KAAKC,oBAAL,CAA0BH,MAA1B,EAAkCV,OAAlC,CAAR;AACD,aAHuB,CAGtBc,IAHsB,CAGjB,IAHiB,CAAxB;;AAKA,gBAAIC,UAAUvC,EAAEwC,OAAF,CAAUT,EAAV,CAAd;;AAEA;AACA,gBAAI/B,EAAEyC,OAAF,CAAUF,OAAV,CAAJ,EAAwB;AACtB,kBAAIG,IAAI,KAAKrC,EAAL,CAAQsC,KAAR,EAAR;AACAD,gBAAEE,OAAF,CAAU,EAAEC,MAAM,EAAR,EAAV;AACA,qBAAOH,EAAEI,OAAT;AACD;;AAED,gBAAIC,cAAc,EAAlB;AACA/C,cAAEgC,IAAF,CAAOO,OAAP,EAAgB,UAASS,KAAT,EAAgB;AAC9B,kBAAIA,MAAMC,OAAN,IAAiBD,MAAMC,OAAN,CAAcC,MAAd,GAAuB,CAA5C,EAA+C;AAC7ClD,kBAAEgC,IAAF,CAAOgB,MAAMC,OAAb,EAAsB,UAASE,GAAT,EAAc;AAClCJ,8BAAYI,IAAIC,IAAhB,IAAwB,IAAxB;AACD,iBAFD;AAGD,eAJD,MAIO;AACLpD,kBAAEgC,IAAF,CAAOgB,MAAMK,IAAb,EAAmB,UAASF,GAAT,EAAcG,GAAd,EAAmB;AACpCP,8BAAYO,GAAZ,IAAmB,IAAnB;AACD,iBAFD;AAGD;AACF,aAVD;;AAYA,mBAAO,KAAKC,sBAAL,CAA4BhB,OAA5B,EAAqCd,KAArC,EAA4CI,GAA5C,EAAiD2B,IAAjD,CAAsD,UAASC,QAAT,EAAmB;AAC9E,kBAAIC,wBAAwB,KAAKC,mBAAL,CAAyBF,SAASZ,IAAlC,EAAwCrB,OAAxC,CAA5B;AACA,kBAAIoC,SAAS5D,EAAE6D,GAAF,CAAMJ,SAASZ,IAAf,EAAqB,UAASiB,UAAT,EAAqBC,KAArB,EAA4B;AAC5DA,wBAAQL,sBAAsBK,KAAtB,CAAR;AACA,oBAAIA,UAAU,CAAC,CAAf,EAAkB;AAChBA,0BAAQ,CAAR;AACD;AACD,qBAAKC,YAAL,CAAkBF,UAAlB;;AAEA,uBAAO,KAAKG,mBAAL,CAAyBH,UAAzB,EAAqCf,WAArC,EAAkDvB,QAAQS,OAAR,CAAgB8B,KAAhB,CAAlD,EAA0EvC,OAA1E,CAAP;AACD,eARiC,CAQhCc,IARgC,CAQ3B,IAR2B,CAArB,CAAb;AASA,qBAAO,EAAEO,MAAMe,MAAR,EAAP;AACD,aAZ4D,CAY3DtB,IAZ2D,CAYtD,IAZsD,CAAtD,CAAP;AAaD;;;2CAGe;AACd,mBAAO,KAAKhC,UAAL,CAAgB4D,iBAAhB,CAAkC;AACvClD,mBAAK,KAAKA,GAAL,GAAW,UADuB;AAEvCmD,sBAAQ;AAF+B,aAAlC,EAGJX,IAHI,CAGC,oBAAY;AAClB,kBAAIC,SAASW,MAAT,KAAoB,GLine truncated
+2 -2
View File
@@ -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": "0.0.6-SNAPSHOT", "version": "1.2.8-SNAPSHOT",
"updated": "2017-08-11" "updated": "2020-02-21"
}, },
"includes": [ "includes": [
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "timely-app", "name": "timely-app",
"private": true, "private": true,
"version": "0.0.2", "version": "1.2.8-SNAPSHOT",
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
+2 -2
View File
@@ -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": "0.0.6-SNAPSHOT", "version": "1.2.8-SNAPSHOT",
"updated": "2017-08-11" "updated": "2020-02-21"
}, },
"includes": [ "includes": [
@@ -446,7 +446,7 @@ export class TimelyDatasource {
} else { } else {
return target.metric === metricData.metric && _.every(target.tags, function(tagV, tagK) { return target.metric === metricData.metric && _.every(target.tags, function(tagV, tagK) {
interpolatedTagValue = this.templateSrv.replace(tagV, options.scopedVars, 'pipe'); interpolatedTagValue = this.templateSrv.replace(tagV, options.scopedVars, 'pipe');
return metricData.tags[tagK] === interpolatedTagValue || interpolatedTagValue === "*"; return metricData.tags[tagK] === interpolatedTagValue || interpolatedTagValue === ".*";
}.bind(this)); }.bind(this));
} }
}.bind(this)); }.bind(this));
+82 -42
View File
@@ -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.0.1-SNAPSHOT</version> <version>1.2.8-SNAPSHOT</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>
@@ -40,50 +41,50 @@
<os.detection.classifierWithLikes>fedora</os.detection.classifierWithLikes> <os.detection.classifierWithLikes>fedora</os.detection.classifierWithLikes>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<version.accumulo>1.7.4</version.accumulo> <version.accumulo>1.9.3</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.5.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.easymock>4.0.2</version.easymock>
<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.7</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.3.0</version.impsort>
<version.jackson>2.9.7</version.jackson> <version.jackson>2.10.0</version.jackson>
<version.jacoco>0.7.6.201602180812</version.jacoco> <version.jackson.databind>2.10.0</version.jackson.databind>
<version.jacoco>0.8.4</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>3.1.1</version.maven-assembly>
<version.maven-build-helper>1.12</version.maven-build-helper> <version.maven-build-helper>3.0.0</version.maven-build-helper>
<version.maven-compiler>3.3</version.maven-compiler> <version.maven-compiler>3.8.1</version.maven-compiler>
<version.maven-dependency>2.10</version.maven-dependency> <version.maven-dependency>3.1.1</version.maven-dependency>
<version.maven-jar>3.0.1</version.maven-jar> <version.maven-jar>3.1.2</version.maven-jar>
<version.maven-pmd>3.6</version.maven-pmd> <version.maven-pmd>3.12.0</version.maven-pmd>
<version.maven-rpm>2.1.4</version.maven-rpm> <version.maven-rpm>2.2.0</version.maven-rpm>
<version.maven-shade>2.4.2</version.maven-shade> <version.maven-shade>3.2.1</version.maven-shade>
<version.maven-surefire>2.19.1</version.maven-surefire> <version.maven-surefire>3.0.0-M3</version.maven-surefire>
<version.netty>4.0.56.Final</version.netty> <version.netty>4.1.42.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.12.1</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>
@@ -100,6 +101,11 @@
<dependency> <dependency>
<groupId>com.fasterxml.jackson.core</groupId> <groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId> <artifactId>jackson-databind</artifactId>
<version>${version.jackson.databind}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-guava</artifactId>
<version>${version.jackson}</version> <version>${version.jackson}</version>
</dependency> </dependency>
<dependency> <dependency>
@@ -135,13 +141,18 @@
<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>
<artifactId>netty-all</artifactId> <artifactId>netty-all</artifactId>
<version>${version.netty}</version> <version>${version.netty}</version>
</dependency> </dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-tcnative-boringssl-static</artifactId>
<version>${version.netty-tcnative}</version>
</dependency>
<dependency> <dependency>
<groupId>net.sourceforge.javacsv</groupId> <groupId>net.sourceforge.javacsv</groupId>
<artifactId>javacsv</artifactId> <artifactId>javacsv</artifactId>
@@ -151,6 +162,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>
@@ -304,6 +321,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>
@@ -376,6 +398,12 @@
<version>${version.jcip-annotations}</version> <version>${version.jcip-annotations}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>${version.easymock}</version>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.powermock</groupId> <groupId>org.powermock</groupId>
<artifactId>powermock-easymock-release-full</artifactId> <artifactId>powermock-easymock-release-full</artifactId>
@@ -391,7 +419,7 @@
<plugin> <plugin>
<groupId>net.revelc.code.formatter</groupId> <groupId>net.revelc.code.formatter</groupId>
<artifactId>formatter-maven-plugin</artifactId> <artifactId>formatter-maven-plugin</artifactId>
<version>2.8.0</version> <version>2.10.0</version>
<configuration> <configuration>
<compilerCompliance>${maven.compiler.source}</compilerCompliance> <compilerCompliance>${maven.compiler.source}</compilerCompliance>
<compilerSource>${maven.compiler.source}</compilerSource> <compilerSource>${maven.compiler.source}</compilerSource>
@@ -409,7 +437,7 @@
<plugin> <plugin>
<groupId>com.github.ekryd.sortpom</groupId> <groupId>com.github.ekryd.sortpom</groupId>
<artifactId>sortpom-maven-plugin</artifactId> <artifactId>sortpom-maven-plugin</artifactId>
<version>2.8.0</version> <version>2.10.0</version>
<configuration> <configuration>
<predefinedSortOrder>recommended_2008_06</predefinedSortOrder> <predefinedSortOrder>recommended_2008_06</predefinedSortOrder>
<createBackupFile>false</createBackupFile> <createBackupFile>false</createBackupFile>
@@ -441,7 +469,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>
@@ -484,7 +512,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>
@@ -553,7 +581,7 @@
<configuration> <configuration>
<rules> <rules>
<requireMavenVersion> <requireMavenVersion>
<version>3.0.5</version> <version>3.5.0</version>
</requireMavenVersion> </requireMavenVersion>
<requireJavaVersion> <requireJavaVersion>
<version>[${maven.compiler.target},)</version> <version>[${maven.compiler.target},)</version>
@@ -724,6 +752,9 @@
<name>m2e.version</name> <name>m2e.version</name>
</property> </property>
</activation> </activation>
<properties>
<maven.compiler.release>8</maven.compiler.release>
</properties>
<build> <build>
<pluginManagement> <pluginManagement>
<plugins> <plugins>
@@ -784,5 +815,14 @@
</pluginManagement> </pluginManagement>
</build> </build>
</profile> </profile>
<profile>
<id>jdk-release-flag</id>
<activation>
<jdk>[9,)</jdk>
</activation>
<properties>
<maven.compiler.release>8</maven.compiler.release>
</properties>
</profile>
</profiles> </profiles>
</project> </project>
+21
View File
@@ -0,0 +1,21 @@
#!/bin/bash
if [[ `uname` == "Darwin" ]]; then
THIS_SCRIPT=`python -c 'import os,sys; print os.path.realpath(sys.argv[1])' $0`
else
THIS_SCRIPT=`readlink -f $0`
fi
THIS_DIR="${THIS_SCRIPT%/*}"
BASE_DIR=${THIS_DIR}/..
CONF_DIR="${BASE_DIR}/conf"
LIB_DIR="${BASE_DIR}/lib"
export CLASSPATH="${CONF_DIR}:${LIB_DIR}/*"
JVM_ARGS="-Xmx1024m -Xms128m"
JVM_ARGS="${JVM_ARGS} -Dlogging.config=${CONF_DIR}/log4j2-error-console.xml"
JVM_ARGS="${JVM_ARGS} -Dlog4j.configurationFile=${CONF_DIR}/log4j2-error-console.xml"
JVM_ARGS="${JVM_ARGS} -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector"
echo "$JAVA_HOME/bin/java ${JVM_ARGS} timely.store.compaction.util.TabletMetadataConsole --spring.config.name=timely"
$JAVA_HOME/bin/java ${JVM_ARGS} timely.store.compaction.util.TabletMetadataConsole --spring.config.name=timely
+4 -9
View File
@@ -8,11 +8,6 @@ else
TCNATIVE_SUFFIX="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,12 +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.${NATIVE_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.${NATIVE_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}"
+6 -10
View File
@@ -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
+5 -5
View File
@@ -26,12 +26,12 @@
</Console> </Console>
</Appenders> </Appenders>
<Loggers> <Loggers>
<Logger name="timely.subscription" level="trace" additivity="false"> <Root level="debug" includeLocation="false">
<AppenderRef ref="Subscription" />
</Logger>
<Root level="trace" includeLocation="false">
<AppenderRef ref="Console" level="warn" /> <AppenderRef ref="Console" level="warn" />
<AppenderRef ref="Rolling" level="trace" /> <AppenderRef ref="Rolling" level="debug" />
</Root> </Root>
<Logger name="timely" additivity="false">
<AppenderRef ref="Console" level="debug" />
</Logger>
</Loggers> </Loggers>
</Configuration> </Configuration>
+34 -12
View File
@@ -15,15 +15,27 @@
<beans:bean id="timelyUserDetailsService" class="timely.auth.UserDetailsService"> <beans:bean id="timelyUserDetailsService" class="timely.auth.UserDetailsService">
<beans:property name="users"> <beans:property name="users">
<util:map key-type="java.lang.String"> <util:list>
<beans:entry key="example.com"> <beans:bean class="timely.auth.TimelyUser">
<util:list> <beans:constructor-arg name="subjectDn">
<beans:value>D</beans:value> <beans:value>CN=example.com</beans:value>
<beans:value>E</beans:value> </beans:constructor-arg>
<beans:value>F</beans:value> <beans:constructor-arg name="auths">
</util:list> <util:list>
</beans:entry> <beans:value>D</beans:value>
</util:map> <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:property>
</beans:bean> </beans:bean>
@@ -34,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">
@@ -45,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 -5
View File
@@ -18,8 +18,11 @@ timely:
max-downsample-memory: 10000000 max-downsample-memory: 10000000
security: security:
session-max-age: 86400 session-max-age: 86400
allow-anonymous-access: true cache-expiration-minutes: 60
ssl: cache-refresh-minutes: 5
allow-anonymous-http-access: true
allow-anonymous-ws-access: true
server-ssl:
certificate-file: /timely-server-src/server/pki/timely.csr certificate-file: /timely-server-src/server/pki/timely.csr
key-file: /timely-server-src/server/pki/timely-pkcs8.key key-file: /timely-server-src/server/pki/timely-pkcs8.key
key-password: password key-password: password
@@ -49,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
+10 -5
View File
@@ -3,6 +3,8 @@ timely:
meta-table: timely.meta meta-table: timely.meta
metric-age-off-days: metric-age-off-days:
default: 7 default: 7
cache:
enabled: false
metrics-report-ignored-tags: metrics-report-ignored-tags:
accumulo: accumulo:
instance-name: TimelyStandalone instance-name: TimelyStandalone
@@ -18,8 +20,11 @@ timely:
max-downsample-memory: 10000000 max-downsample-memory: 10000000
security: security:
session-max-age: 86400 session-max-age: 86400
allow-anonymous-access: true cache-expiration-minutes: 60
ssl: cache-refresh-minutes: 5
allow-anonymous-http-access: true
allow-anonymous-ws-access: true
server-ssl:
certificate-file: certificate-file:
key-file: key-file:
key-password: timely key-password: timely
@@ -53,9 +58,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
+24 -21
View File
@@ -4,14 +4,15 @@ timely:
metric-age-off-days: metric-age-off-days:
default: 7 default: 7
cache: cache:
enabled: enabled: true
age-off-hours: age-off-hours:
default: 30 default: 26
metrics-report-ignored-tags: metrics-report-ignored-tags:
defaultVisibility:
accumulo: accumulo:
instance-name: instance-name: accumulo
zookeepers: zookeepers: localhost:2181
username: username: root
password: password:
write: write:
latency: 5s latency: 5s
@@ -22,23 +23,26 @@ timely:
max-downsample-memory: 10000000 max-downsample-memory: 10000000
security: security:
session-max-age: 86400 session-max-age: 86400
allow-anonymous-access: false cache-expiration-minutes: 60
ssl: cache-refresh-minutes: 5
allow-anonymous-http-access: true
allow-anonymous-ws-access: true
server-ssl:
certificate-file: certificate-file:
key-file: key-file:
key-password: key-password:
use-generated-keypair: false use-generated-keypair: true
trust-store-file: trust-store-file:
use-openssl: true use-openssl: true
#use-ciphers: #use-ciphers:
server: server:
ip: ip: 0.0.0.0
tcp-port: tcp-port: 4242
udp-port: udp-port: 4542
http: http:
host: host: localhost
ip: ip: 0.0.0.0
port: port: 4243
redirect-path: /secure-me redirect-path: /secure-me
strict-transport-max-age: 608400 strict-transport-max-age: 608400
cors: cors:
@@ -49,8 +53,8 @@ timely:
allowed-methods: DELETE,GET,HEAD,OPTIONS,PUT,POST allowed-methods: DELETE,GET,HEAD,OPTIONS,PUT,POST
allowed-headers: content-type allowed-headers: content-type
websocket: websocket:
ip: ip: 0.0.0.0
port: port: 4244
timeout: 60 timeout: 60
subscription-lag: 120 subscription-lag: 120
scanner-batch-size: 10000 scanner-batch-size: 10000
@@ -58,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
+18 -39
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>timely</groupId> <groupId>timely</groupId>
<artifactId>timely</artifactId> <artifactId>timely</artifactId>
<version>1.0.1-SNAPSHOT</version> <version>1.2.8-SNAPSHOT</version>
</parent> </parent>
<artifactId>timely-server</artifactId> <artifactId>timely-server</artifactId>
<dependencies> <dependencies>
@@ -20,6 +20,10 @@
<groupId>com.fasterxml.jackson.core</groupId> <groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId> <artifactId>jackson-databind</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-guava</artifactId>
</dependency>
<dependency> <dependency>
<groupId>com.fasterxml.jackson.datatype</groupId> <groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId> <artifactId>jackson-datatype-jdk8</artifactId>
@@ -145,9 +149,7 @@
</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>
<classifier>${os.detected.classifier}</classifier>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<dependency> <dependency>
@@ -175,11 +177,22 @@
<artifactId>junit</artifactId> <artifactId>junit</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.jsoup</groupId> <groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId> <artifactId>jsoup</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-easymock-release-full</artifactId>
<classifier>full</classifier>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test-autoconfigure</artifactId> <artifactId>spring-boot-test-autoconfigure</artifactId>
@@ -236,9 +249,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>
@@ -280,37 +292,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>
+1 -1
View File
@@ -33,7 +33,7 @@
<directory>${project.basedir}/bin</directory> <directory>${project.basedir}/bin</directory>
<outputDirectory>bin</outputDirectory> <outputDirectory>bin</outputDirectory>
<includes> <includes>
<include>*</include> <include>**/*</include>
</includes> </includes>
</fileSet> </fileSet>
<fileSet> <fileSet>
+116 -60
View File
@@ -1,5 +1,8 @@
package timely; package timely;
import static timely.store.cache.DataStoreCache.NON_CACHED_METRICS;
import static timely.store.cache.DataStoreCache.NON_CACHED_METRICS_LOCK_PATH;
import java.io.File; import java.io.File;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
@@ -21,6 +24,7 @@ import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption; import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup; import io.netty.channel.EventLoopGroup;
import io.netty.channel.WriteBufferWaterMark;
import io.netty.channel.epoll.EpollChannelOption; import io.netty.channel.epoll.EpollChannelOption;
import io.netty.channel.epoll.EpollDatagramChannel; import io.netty.channel.epoll.EpollDatagramChannel;
import io.netty.channel.epoll.EpollEventLoopGroup; import io.netty.channel.epoll.EpollEventLoopGroup;
@@ -41,6 +45,7 @@ import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder; import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.codec.http.HttpServerCodec; import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.cors.CorsConfig; import io.netty.handler.codec.http.cors.CorsConfig;
import io.netty.handler.codec.http.cors.CorsConfigBuilder;
import io.netty.handler.codec.http.cors.CorsHandler; import io.netty.handler.codec.http.cors.CorsHandler;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.logging.LoggingHandler; import io.netty.handler.logging.LoggingHandler;
@@ -55,6 +60,7 @@ import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler; import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.NettyRuntime; import io.netty.util.NettyRuntime;
import io.netty.util.internal.SystemPropertyUtil; import io.netty.util.internal.SystemPropertyUtil;
import org.apache.accumulo.core.conf.AccumuloConfiguration;
import org.apache.curator.RetryPolicy; import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.CuratorFrameworkFactory;
@@ -63,12 +69,13 @@ import org.apache.curator.x.discovery.ServiceDiscovery;
import org.apache.curator.x.discovery.ServiceDiscoveryBuilder; import org.apache.curator.x.discovery.ServiceDiscoveryBuilder;
import org.apache.curator.x.discovery.ServiceInstance; import org.apache.curator.x.discovery.ServiceInstance;
import org.apache.curator.x.discovery.ServiceInstanceBuilder; import org.apache.curator.x.discovery.ServiceInstanceBuilder;
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.auth.AuthCache; import timely.auth.AuthCache;
import timely.auth.VisibilityCache; import timely.auth.VisibilityCache;
import timely.configuration.Configuration; import timely.configuration.Configuration;
@@ -97,7 +104,7 @@ import timely.netty.udp.UdpDecoder;
import timely.netty.udp.UdpPacketToByteBuf; import timely.netty.udp.UdpPacketToByteBuf;
import timely.netty.websocket.WSMetricPutHandler; import timely.netty.websocket.WSMetricPutHandler;
import timely.netty.websocket.WSVersionRequestHandler; import timely.netty.websocket.WSVersionRequestHandler;
import timely.netty.websocket.WebSocketHttpCookieHandler; import timely.netty.websocket.WebSocketFullRequestHandler;
import timely.netty.websocket.WebSocketRequestDecoder; import timely.netty.websocket.WebSocketRequestDecoder;
import timely.netty.websocket.subscription.WSAddSubscriptionRequestHandler; import timely.netty.websocket.subscription.WSAddSubscriptionRequestHandler;
import timely.netty.websocket.subscription.WSCloseSubscriptionRequestHandler; import timely.netty.websocket.subscription.WSCloseSubscriptionRequestHandler;
@@ -125,6 +132,8 @@ public class Server {
private static final String OS_VERSION = "os.version"; private static final String OS_VERSION = "os.version";
private static final String WS_PATH = "/websocket"; private static final String WS_PATH = "/websocket";
private SslContext sslCtx = null;
private CuratorFramework curatorFramework;
protected static final CountDownLatch LATCH = new CountDownLatch(1); protected static final CountDownLatch LATCH = new CountDownLatch(1);
static ConfigurableApplicationContext applicationContext; static ConfigurableApplicationContext applicationContext;
@@ -146,6 +155,9 @@ public class Server {
protected volatile boolean shutdown = false; protected volatile boolean shutdown = false;
private final int DEFAULT_EVENT_LOOP_THREADS; private final int DEFAULT_EVENT_LOOP_THREADS;
private String[] zkPaths = new String[] { SERVICE_DISCOVERY_PATH, NON_CACHED_METRICS,
NON_CACHED_METRICS_LOCK_PATH };
private static boolean useEpoll() { private static boolean useEpoll() {
// Should we just return true if this is Linux and if we get an error // Should we just return true if this is Linux and if we get an error
@@ -175,13 +187,8 @@ public class Server {
} }
} }
public void registerService() { public void registerService(CuratorFramework curatorFramework) {
try { try {
RetryPolicy retryPolicy = new RetryForever(1000);
CuratorFramework curatorFramework = CuratorFrameworkFactory.newClient(config.getAccumulo().getZookeepers(),
30000, 10000, retryPolicy);
curatorFramework.start();
try { try {
Stat stat = curatorFramework.checkExists().forPath(SERVICE_DISCOVERY_PATH); Stat stat = curatorFramework.checkExists().forPath(SERVICE_DISCOVERY_PATH);
if (stat == null) { if (stat == null) {
@@ -205,13 +212,13 @@ public class Server {
payload.setWsPort(config.getWebsocket().getPort()); payload.setWsPort(config.getWebsocket().getPort());
payload.setUdpPort(config.getServer().getUdpPort()); payload.setUdpPort(config.getServer().getUdpPort());
ServiceInstanceBuilder builder = ServiceInstance.builder(); ServiceInstanceBuilder<ServerDetails> builder = ServiceInstance.builder();
String serviceName = host + ":" + config.getServer().getTcpPort(); String serviceName = host + ":" + config.getServer().getTcpPort();
ServiceInstance serviceInstance = builder.id(serviceName).name("timely-server") ServiceInstance<ServerDetails> serviceInstance = builder.id(serviceName).name("timely-server")
.address(config.getServer().getIp()).port(config.getServer().getTcpPort()).payload(payload).build(); .address(config.getServer().getIp()).port(config.getServer().getTcpPort()).payload(payload).build();
ServiceDiscovery discovery = ServiceDiscoveryBuilder.builder(ServerDetails.class).client(curatorFramework) ServiceDiscovery<ServerDetails> discovery = ServiceDiscoveryBuilder.builder(ServerDetails.class)
.basePath(SERVICE_DISCOVERY_PATH).build(); .client(curatorFramework).basePath(SERVICE_DISCOVERY_PATH).build();
discovery.start(); discovery.start();
discovery.registerService(serviceInstance); discovery.registerService(serviceInstance);
@@ -227,11 +234,12 @@ public class Server {
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
Configuration conf = initializeConfiguration(args); Server.applicationContext = initializeContext(args);
Configuration conf = Server.applicationContext.getBean(Configuration.class);
Server s = new Server(conf); Server server = new Server(conf);
try { try {
s.run(); server.run(createSSLContext(conf));
LATCH.await(); LATCH.await();
} catch (final InterruptedException e) { } catch (final InterruptedException e) {
LOG.info("Server shutting down."); LOG.info("Server shutting down.");
@@ -239,16 +247,18 @@ public class Server {
LOG.error("Error running server.", e); LOG.error("Error running server.", e);
} finally { } finally {
try { try {
s.shutdown(); server.shutdown();
} catch (Exception e) { } catch (Exception e) {
System.exit(1); System.exit(1);
} }
} }
} }
protected static Configuration initializeConfiguration(String[] args) { protected static ConfigurableApplicationContext initializeContext(String[] args) {
applicationContext = new SpringApplicationBuilder(SpringBootstrap.class).web(false).run(args); SpringApplicationBuilder builder = new SpringApplicationBuilder(SpringBootstrap.class);
return applicationContext.getBean(Configuration.class); builder.web(WebApplicationType.NONE);
builder.registerShutdownHook(false);
return builder.run(args);
} }
private void shutdownHook() { private void shutdownHook() {
@@ -280,8 +290,9 @@ public class Server {
channelFutures.add(wsChannelHandle.close()); channelFutures.add(wsChannelHandle.close());
} }
int udpChannel = 1;
for (Channel c : udpChannelHandleList) { for (Channel c : udpChannelHandleList) {
LOG.info("Closing udpChannelHandle"); LOG.info("Closing udpChannelHandle #" + udpChannel++);
channelFutures.add(c.close()); channelFutures.add(c.close());
} }
@@ -290,14 +301,13 @@ public class Server {
try { try {
f.get(); f.get();
} catch (final Exception e) { } catch (final Exception e) {
LOG.error("Error while shutting down channel: {}", f.channel().config()); LOG.error("Channel:" + f.channel().config() + " -> " + e.getMessage(), e);
LOG.error("{}", e.getMessage());
e.printStackTrace();
} }
}); });
Integer quietPeriod = config.getServer().getShutdownQuietPeriod(); int quietPeriod = config.getServer().getShutdownQuietPeriod();
List<Future<?>> groupFutures = new ArrayList<>(); List<Future<?>> groupFutures = new ArrayList<>();
if (tcpBossGroup != null) { if (tcpBossGroup != null) {
LOG.info("Shutting down tcpBossGroup"); LOG.info("Shutting down tcpBossGroup");
groupFutures.add(tcpBossGroup.shutdownGracefully(quietPeriod, 10, TimeUnit.SECONDS)); groupFutures.add(tcpBossGroup.shutdownGracefully(quietPeriod, 10, TimeUnit.SECONDS));
@@ -342,36 +352,58 @@ public class Server {
try { try {
f.get(); f.get();
} catch (final Exception e) { } catch (final Exception e) {
LOG.error("Error while shutting down group: {}", f.toString()); LOG.error("Group:" + f.toString() + " -> " + e.getMessage(), e);
LOG.error("{}", e.getMessage());
e.printStackTrace();
} }
}); });
try { if (dataStore != null) {
LOG.info("Flushing datastore."); try {
dataStore.flush(); LOG.info("Closing dataStore");
} catch (TimelyException e) { dataStore.close();
LOG.error("Error flushing to server during shutdown", e); } catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
if (dataStoreCache != null) {
try {
LOG.info("Closing dataStoreCache");
dataStoreCache.close();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
} }
try { try {
LOG.info("Closing MetaCacheFactory"); LOG.info("Closing metaCacheFactory");
MetaCacheFactory.close(); MetaCacheFactory.close();
} catch (Exception e) { } catch (Exception e) {
LOG.error("Error closing MetaCacheFactory during shutdown", e); LOG.error(e.getMessage(), e);
} }
try { try {
LOG.info("Closing WebSocketRequestDecoder"); LOG.info("Closing webSocketRequestDecoder subscriptions");
WebSocketRequestDecoder.close(); WebSocketRequestDecoder.close();
} catch (Exception e) { } catch (Exception e) {
LOG.error("Error closing WebSocketRequestDecoder during shutdown", e); LOG.error(e.getMessage(), e);
}
if (curatorFramework != null) {
try {
LOG.info("Closing curatorFramework");
curatorFramework.close();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
} }
if (applicationContext != null) { if (applicationContext != null) {
LOG.info("Closing applicationContext"); try {
applicationContext.close(); LOG.info("Closing applicationContext");
applicationContext.close();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
} }
this.shutdown = true; this.shutdown = true;
LOG.info("Server shut down."); LOG.info("Server shut down.");
@@ -390,20 +422,42 @@ public class Server {
this.config = conf; this.config = conf;
} }
public void run() throws Exception { private void ensureZkPaths(CuratorFramework curatorFramework, String[] paths) {
for (String s : paths) {
try {
Stat stat = curatorFramework.checkExists().forPath(s);
if (stat == null) {
curatorFramework.create().creatingParentContainersIfNeeded().withMode(CreateMode.PERSISTENT)
.forPath(s);
}
} catch (Exception e) {
LOG.info(e.getMessage());
}
}
}
public void run(SslContext sslCtx) throws Exception {
RetryPolicy retryPolicy = new RetryForever(1000);
int timeout = Long.valueOf(AccumuloConfiguration.getTimeInMillis(config.getAccumulo().getZookeeperTimeout()))
.intValue();
curatorFramework = CuratorFrameworkFactory.newClient(config.getAccumulo().getZookeepers(), timeout, 10000,
retryPolicy);
curatorFramework.start();
ensureZkPaths(curatorFramework, zkPaths);
int nettyThreads = Math.max(1, int nettyThreads = Math.max(1,
SystemPropertyUtil.getInt("io.netty.eventLoopThreads", Runtime.getRuntime().availableProcessors() * 2)); SystemPropertyUtil.getInt("io.netty.eventLoopThreads", Runtime.getRuntime().availableProcessors() * 2));
dataStore = DataStoreFactory.create(config, nettyThreads); dataStore = DataStoreFactory.create(config, nettyThreads);
if (config.getCache().isEnabled()) { if (config.getCache().isEnabled()) {
dataStoreCache = new DataStoreCache(config); dataStoreCache = new DataStoreCache(curatorFramework, config);
dataStoreCache.setInternalMetrics(dataStore.getInternalMetrics()); dataStoreCache.setInternalMetrics(dataStore.getInternalMetrics());
dataStore.setCache(dataStoreCache); dataStore.setCache(dataStoreCache);
} }
// Initialize the MetaCache // Initialize the MetaCache
MetaCacheFactory.getCache(config); MetaCacheFactory.getCache(config);
// initialize the auth cache // initialize the auth cache
AuthCache.setSessionMaxAge(config); AuthCache.configure(config.getSecurity());
// Initialize the VisibilityCache // Initialize the VisibilityCache
VisibilityCache.init(config); VisibilityCache.init(config);
final boolean useEpoll = useEpoll(); final boolean useEpoll = useEpoll();
@@ -449,7 +503,6 @@ public class Server {
final int httpPort = config.getHttp().getPort(); final int httpPort = config.getHttp().getPort();
final String httpIp = config.getHttp().getIp(); final String httpIp = config.getHttp().getIp();
SslContext sslCtx = createSSLContext(config);
if (sslCtx instanceof OpenSslServerContext) { if (sslCtx instanceof OpenSslServerContext) {
OpenSslServerContext openssl = (OpenSslServerContext) sslCtx; OpenSslServerContext openssl = (OpenSslServerContext) sslCtx;
String application = "Timely_" + httpPort; String application = "Timely_" + httpPort;
@@ -480,10 +533,9 @@ public class Server {
wsServer.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT); wsServer.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
wsServer.option(ChannelOption.SO_BACKLOG, 128); wsServer.option(ChannelOption.SO_BACKLOG, 128);
wsServer.option(ChannelOption.SO_KEEPALIVE, true); wsServer.option(ChannelOption.SO_KEEPALIVE, true);
/* Not sure if next three lines are necessary */ /* Not sure if next two lines are necessary */
wsServer.option(ChannelOption.SO_SNDBUF, 1048576); wsServer.option(ChannelOption.SO_SNDBUF, 1048576);
wsServer.option(ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK, 838860); wsServer.option(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(620145, 838860));
wsServer.option(ChannelOption.WRITE_BUFFER_LOW_WATER_MARK, 620145);
wsChannelHandle = wsServer.bind(wsIp, wsPort).sync().channel(); wsChannelHandle = wsServer.bind(wsIp, wsPort).sync().channel();
final String wsAddress = ((InetSocketAddress) wsChannelHandle.localAddress()).getAddress().getHostAddress(); final String wsAddress = ((InetSocketAddress) wsChannelHandle.localAddress()).getAddress().getHostAddress();
@@ -500,14 +552,14 @@ public class Server {
udpServer.option(EpollChannelOption.SO_REUSEPORT, true); udpServer.option(EpollChannelOption.SO_REUSEPORT, true);
udpChannelHandleList.add(udpServer.bind(udpIp, udpPort).sync().channel()); udpChannelHandleList.add(udpServer.bind(udpIp, udpPort).sync().channel());
} }
registerService(); registerService(curatorFramework);
shutdownHook(); shutdownHook();
LOG.info( LOG.info(
"Server started. Listening on {}:{} for TCP traffic, {}:{} for HTTP traffic, {}:{} for WebSocket traffic, and {}:{} for UDP traffic", "Server started. Listening on {}:{} for TCP traffic, {}:{} for HTTP traffic, {}:{} for WebSocket traffic, and {}:{} for UDP traffic",
tcpAddress, tcpPort, httpAddress, httpPort, wsAddress, wsPort, wsAddress, udpPort); tcpAddress, tcpPort, httpAddress, httpPort, wsAddress, wsPort, wsAddress, udpPort);
} }
protected SslContext createSSLContext(Configuration config) throws Exception { public static SslContext createSSLContext(Configuration config) throws Exception {
ServerSsl sslCfg = config.getSecurity().getServerSsl(); ServerSsl sslCfg = config.getSecurity().getServerSsl();
Boolean generate = sslCfg.isUseGeneratedKeypair(); Boolean generate = sslCfg.isUseGeneratedKeypair();
@@ -515,7 +567,7 @@ public class Server {
if (generate) { if (generate) {
LOG.warn("Using generated self signed server certificate"); LOG.warn("Using generated self signed server certificate");
Date begin = new Date(); Date begin = new Date();
Date end = new Date(begin.getTime() + 86400000); Date end = new Date(begin.getTime() + TimeUnit.DAYS.toMillis(7));
SelfSignedCertificate ssc = new SelfSignedCertificate("localhost", begin, end); SelfSignedCertificate ssc = new SelfSignedCertificate("localhost", begin, end);
ssl = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()); ssl = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey());
} else { } else {
@@ -560,14 +612,14 @@ 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 CorsConfigBuilder ccb;
if (corsCfg.isAllowAnyOrigin()) { if (corsCfg.isAllowAnyOrigin()) {
ccb = new CorsConfig.Builder(); ccb = CorsConfigBuilder.forAnyOrigin();
} else { } else {
ccb = new CorsConfig.Builder(corsCfg.getAllowedOrigins().stream().toArray(String[]::new)); ccb = CorsConfigBuilder.forOrigins(corsCfg.getAllowedOrigins().stream().toArray(String[]::new));
} }
if (corsCfg.isAllowNullOrigin()) { if (corsCfg.isAllowNullOrigin()) {
ccb.allowNullOrigin(); ccb.allowNullOrigin();
@@ -582,7 +634,8 @@ public class Server {
ch.pipeline().addLast("cors", new CorsHandler(cors)); ch.pipeline().addLast("cors", new CorsHandler(cors));
ch.pipeline().addLast("queryDecoder", ch.pipeline().addLast("queryDecoder",
new timely.netty.http.HttpRequestDecoder(config.getSecurity(), config.getHttp())); new timely.netty.http.HttpRequestDecoder(config.getSecurity(), config.getHttp()));
ch.pipeline().addLast("fileServer", new HttpStaticFileServerHandler()); ch.pipeline().addLast("fileServer", new HttpStaticFileServerHandler()
.setIgnoreSslHandshakeErrors(config.getSecurity().getServerSsl().isUseGeneratedKeypair()));
ch.pipeline().addLast("strict", new StrictTransportHandler(config)); ch.pipeline().addLast("strict", new StrictTransportHandler(config));
ch.pipeline().addLast("login", new X509LoginRequestHandler(config.getSecurity(), config.getHttp())); ch.pipeline().addLast("login", new X509LoginRequestHandler(config.getSecurity(), config.getHttp()));
ch.pipeline().addLast("doLogin", ch.pipeline().addLast("doLogin",
@@ -595,7 +648,9 @@ public class Server {
ch.pipeline().addLast("version", new HttpVersionRequestHandler()); ch.pipeline().addLast("version", new HttpVersionRequestHandler());
ch.pipeline().addLast("cache", new HttpCacheRequestHandler(dataStoreCache)); ch.pipeline().addLast("cache", new HttpCacheRequestHandler(dataStoreCache));
ch.pipeline().addLast("put", new HttpMetricPutHandler(dataStore)); ch.pipeline().addLast("put", new HttpMetricPutHandler(dataStore));
ch.pipeline().addLast("error", new TimelyExceptionHandler()); ch.pipeline().addLast("error", new TimelyExceptionHandler()
.setIgnoreSslHandshakeErrors(config.getSecurity().getServerSsl().isUseGeneratedKeypair()));
} }
}; };
} }
@@ -608,7 +663,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));
} }
@@ -621,7 +676,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());
@@ -636,11 +691,11 @@ 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 WebSocketHttpCookieHandler(config.getSecurity())); 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",
new WebSocketServerProtocolHandler(WS_PATH, null, true, 65536, true)); new WebSocketServerProtocolHandler(WS_PATH, null, true, 65536, false, true));
ch.pipeline().addLast("wsDecoder", new WebSocketRequestDecoder(config.getSecurity())); ch.pipeline().addLast("wsDecoder", new WebSocketRequestDecoder(config.getSecurity()));
ch.pipeline().addLast("aggregators", new WSAggregatorsRequestHandler()); ch.pipeline().addLast("aggregators", new WSAggregatorsRequestHandler());
ch.pipeline().addLast("metrics", new WSMetricsRequestHandler(config)); ch.pipeline().addLast("metrics", new WSMetricsRequestHandler(config));
@@ -649,7 +704,8 @@ public class Server {
ch.pipeline().addLast("suggest", new WSSuggestRequestHandler(dataStore)); ch.pipeline().addLast("suggest", new WSSuggestRequestHandler(dataStore));
ch.pipeline().addLast("version", new WSVersionRequestHandler()); ch.pipeline().addLast("version", new WSVersionRequestHandler());
ch.pipeline().addLast("put", new WSMetricPutHandler(dataStore)); ch.pipeline().addLast("put", new WSMetricPutHandler(dataStore));
ch.pipeline().addLast("create", new WSCreateSubscriptionRequestHandler(dataStore, config)); ch.pipeline().addLast("create",
new WSCreateSubscriptionRequestHandler(dataStore, dataStoreCache, config));
ch.pipeline().addLast("add", new WSAddSubscriptionRequestHandler()); ch.pipeline().addLast("add", new WSAddSubscriptionRequestHandler());
ch.pipeline().addLast("remove", new WSRemoveSubscriptionRequestHandler()); ch.pipeline().addLast("remove", new WSRemoveSubscriptionRequestHandler());
ch.pipeline().addLast("close", new WSCloseSubscriptionRequestHandler()); ch.pipeline().addLast("close", new WSCloseSubscriptionRequestHandler());
@@ -52,7 +52,8 @@ public class StandaloneServer extends Server {
System.exit(1); System.exit(1);
} }
Configuration conf = initializeConfiguration(args); StandaloneServer.applicationContext = initializeContext(args);
Configuration conf = StandaloneServer.applicationContext.getBean(Configuration.class);
File accumuloDir = new File(tmp, "accumulo"); File accumuloDir = new File(tmp, "accumulo");
MiniAccumuloConfig macConfig = new MiniAccumuloConfig(accumuloDir, "secret"); MiniAccumuloConfig macConfig = new MiniAccumuloConfig(accumuloDir, "secret");
@@ -84,7 +85,7 @@ public class StandaloneServer extends Server {
try { try {
LOG.info("Starting StandaloneServer"); LOG.info("Starting StandaloneServer");
StandaloneServer s = new StandaloneServer(conf); StandaloneServer s = new StandaloneServer(conf);
s.run(); s.run(createSSLContext(conf));
} catch (Exception e) { } catch (Exception e) {
System.err.println("Error starting server"); System.err.println("Error starting server");
e.printStackTrace(); e.printStackTrace();
@@ -0,0 +1,39 @@
package timely.adapter.accumulo;
import org.apache.accumulo.core.data.Key;
import timely.api.model.Meta;
import timely.model.Metric;
/**
* Creates Accumulo data structures from {@link Metric}
*/
public class MetaAdapter {
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();
}
}
@@ -8,6 +8,7 @@ import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
@@ -39,6 +40,8 @@ public class MetricAdapter {
private static final TagParser tagParser = new TagParser(); private static final TagParser tagParser = new TagParser();
private static final TagListParser tagListParser = new TagListParser(); private static final TagListParser tagListParser = new TagListParser();
private static Pattern equalsPattern = Pattern.compile("=");
private static Pattern commaPattern = Pattern.compile(",");
public static final ColumnVisibility EMPTY_VISIBILITY = new ColumnVisibility(); public static final ColumnVisibility EMPTY_VISIBILITY = new ColumnVisibility();
public static final String VISIBILITY_TAG = "viz"; public static final String VISIBILITY_TAG = "viz";
@@ -59,10 +62,10 @@ public class MetricAdapter {
// are used as a delimiter // are used as a delimiter
String k = t.getKey(); String k = t.getKey();
String v = t.getValue(); String v = t.getValue();
k = k.replaceAll("=", "\\="); k = equalsPattern.matcher(k).replaceAll("\\=");
k = k.replaceAll(",", "\\,"); k = commaPattern.matcher(k).replaceAll("\\,");
v = v.replaceAll("=", "\\="); v = equalsPattern.matcher(v).replaceAll("\\=");
v = v.replaceAll(",", "\\,"); v = commaPattern.matcher(v).replaceAll("\\,");
newTags.add(new Tag(k, v)); newTags.add(new Tag(k, v));
} }
return newTags; return newTags;
@@ -75,10 +78,10 @@ public class MetricAdapter {
// are used as a delimiter // are used as a delimiter
String k = t.getKey(); String k = t.getKey();
String v = t.getValue(); String v = t.getValue();
k = k.replaceAll("=", "\\="); k = equalsPattern.matcher(k).replaceAll("\\=");
k = k.replaceAll(",", "\\,"); k = commaPattern.matcher(k).replaceAll("\\,");
v = v.replaceAll("=", "\\="); v = equalsPattern.matcher(v).replaceAll("\\=");
v = v.replaceAll(",", "\\,"); v = commaPattern.matcher(v).replaceAll("\\,");
newTags.put(k, v); newTags.put(k, v);
} }
return newTags; return newTags;
+65 -47
View File
@@ -5,6 +5,10 @@ import java.util.List;
import org.apache.accumulo.core.data.Key; import org.apache.accumulo.core.data.Key;
import org.apache.accumulo.core.data.Value; import org.apache.accumulo.core.data.Value;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.CompareToBuilder;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
public class Meta implements Comparable<Meta> { public class Meta implements Comparable<Meta> {
@@ -49,60 +53,79 @@ public class Meta implements Comparable<Meta> {
public List<Key> toKeys() { public List<Key> toKeys() {
List<Key> keys = new ArrayList<>(); List<Key> keys = new ArrayList<>();
keys.add(new Key(METRIC_PREFIX + metric)); if (StringUtils.isNotBlank(metric)) {
keys.add(new Key(TAG_PREFIX + metric, tagKey)); keys.add(new Key(METRIC_PREFIX + metric));
keys.add(new Key(VALUE_PREFIX + metric, tagKey, tagValue)); }
if (StringUtils.isNotBlank(tagKey)) {
keys.add(new Key(TAG_PREFIX + metric, tagKey));
}
if (StringUtils.isNotBlank(tagKey) && StringUtils.isNotBlank(tagValue)) {
keys.add(new Key(VALUE_PREFIX + metric, tagKey, tagValue));
}
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);
} }
} }
@Override @Override
public int hashCode() { public int hashCode() {
final int prime = 31; HashCodeBuilder builder = new HashCodeBuilder();
int result = 1; builder.append(metric);
result = prime * result + ((metric == null) ? 0 : metric.hashCode()); builder.append(tagKey);
result = prime * result + ((tagKey == null) ? 0 : tagKey.hashCode()); builder.append(tagValue);
result = prime * result + ((tagValue == null) ? 0 : tagValue.hashCode()); return builder.toHashCode();
return result;
} }
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object o) {
if (this == obj) if (o instanceof Meta) {
return true; Meta obj = (Meta) o;
if (obj == null) EqualsBuilder builder = new EqualsBuilder();
builder.append(this.metric, obj.metric);
builder.append(this.tagKey, obj.tagKey);
builder.append(this.tagValue, obj.tagValue);
return builder.isEquals();
} else {
return false; return false;
if (getClass() != obj.getClass()) }
return false;
Meta other = (Meta) obj;
if (metric == null) {
if (other.metric != null)
return false;
} else if (!metric.equals(other.metric))
return false;
if (tagKey == null) {
if (other.tagKey != null)
return false;
} else if (!tagKey.equals(other.tagKey))
return false;
if (tagValue == null) {
if (other.tagValue != null)
return false;
} else if (!tagValue.equals(other.tagValue))
return false;
return true;
} }
@Override @Override
@@ -112,15 +135,10 @@ public class Meta implements Comparable<Meta> {
@Override @Override
public int compareTo(Meta o) { public int compareTo(Meta o) {
int result = metric.compareTo(o.metric); CompareToBuilder builder = new CompareToBuilder();
if (result != 0) { builder.append(this.metric, o.metric);
return result; builder.append(this.tagKey, o.tagKey);
} builder.append(this.tagValue, o.tagValue);
result = tagKey.compareTo(o.tagKey); return builder.toComparison();
if (result != 0) {
return result;
}
return tagValue.compareTo(o.tagValue);
} }
} }
@@ -1,18 +1,22 @@
package timely.api.request; package timely.api.request;
import java.util.Collections; import com.fasterxml.jackson.annotation.JsonIgnore;
import java.util.HashMap; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import java.util.List; import com.google.common.collect.HashMultimap;
import java.util.Map; import com.google.common.collect.Multimap;
import java.util.Map.Entry; import timely.auth.TimelyUser;
import timely.auth.util.HttpHeaderUtils;
import timely.netty.http.auth.TimelyAuthenticationToken;
/** /**
* Base class for requests that require authentication * Base class for requests that require auth
*/ */
@JsonIgnoreProperties({ "userName", "requestHeader" })
public class AuthenticatedRequest implements Request { public class AuthenticatedRequest implements Request {
private String sessionId = null; private String sessionId = null;
private Map<String, String> requestHeaders = new HashMap<>(); private TimelyAuthenticationToken token = null;
private Multimap<String, String> requestHeaders = HashMultimap.create();
public String getSessionId() { public String getSessionId() {
return sessionId; return sessionId;
@@ -22,12 +26,17 @@ public class AuthenticatedRequest implements Request {
this.sessionId = sessionId; this.sessionId = sessionId;
} }
public void addHeaders(List<Entry<String, String>> headers) { public void addHeaders(Multimap<String, String> headers) {
headers.forEach(l -> requestHeaders.put(l.getKey(), l.getValue())); requestHeaders.putAll(headers);
} }
public Map<String, String> getRequestHeaders() { public Multimap<String, String> getRequestHeaders() {
return Collections.unmodifiableMap(requestHeaders); return requestHeaders;
}
@JsonIgnore
public String getRequestHeader(String name) {
return HttpHeaderUtils.getSingleHeader(requestHeaders, name, false);
} }
@Override @Override
@@ -35,17 +44,32 @@ public class AuthenticatedRequest implements Request {
StringBuilder buf = new StringBuilder(); StringBuilder buf = new StringBuilder();
buf.append("{"); buf.append("{");
buf.append("Session ID: ").append(sessionId); buf.append("Session ID: ").append(sessionId);
buf.append("Token: ").append(token == null ? null : token.getTimelyPrincipal().getName());
buf.append(", Request Headers: ").append(requestHeaders.toString()); buf.append(", Request Headers: ").append(requestHeaders.toString());
buf.append("}"); buf.append("}");
return buf.toString(); return buf.toString();
} }
@Override public void setToken(TimelyAuthenticationToken token) {
public void validate() { this.token = token;
Request.super.validate();
if (null == sessionId) {
throw new IllegalArgumentException("SessionID is null, must log in first");
}
} }
public TimelyAuthenticationToken getToken() {
return token;
}
@JsonIgnore
public String getUserName() {
String name;
try {
TimelyUser user = token.getTimelyPrincipal().getPrimaryUser();
name = user.getCommonName();
if (name == null) {
name = user.getName();
}
} catch (Exception e) {
name = "UNKNOWN";
}
return name;
}
} }
@@ -4,8 +4,6 @@ public interface TcpRequest extends Request {
/** /**
* Parse a line of text received and populate this object * Parse a line of text received and populate this object
*
* @param line
*/ */
public void parse(String line); public void parse(String line);
@@ -4,8 +4,6 @@ public interface UdpRequest {
/** /**
* Parse a line of text received and populate this object * Parse a line of text received and populate this object
*
* @param line
*/ */
public void parse(String line); public void parse(String line);
@@ -18,7 +18,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
@@ -129,8 +129,8 @@ public class MetricsResponse {
} }
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK,
Unpooled.copiedBuffer(buf)); Unpooled.copiedBuffer(buf));
response.headers().set(Names.CONTENT_TYPE, responseType); response.headers().set(HttpHeaderNames.CONTENT_TYPE, responseType);
response.headers().set(Names.CONTENT_LENGTH, response.content().readableBytes()); response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
return response; return response;
} }
@@ -156,8 +156,10 @@ public class MetricsResponse {
b.append(TR_END); b.append(TR_END);
String prevMetric = null; String prevMetric = null;
StringBuilder tags = new StringBuilder(); StringBuilder tags = new StringBuilder();
Map<String, Long> tagMap = new HashMap<>();
for (Meta m : tree) { for (Meta m : tree) {
if (prevMetric != null && !m.getMetric().equals(prevMetric)) { if (prevMetric != null && !m.getMetric().equals(prevMetric)) {
tagMap.clear();
b.append(TR_START); b.append(TR_START);
b.append(TD_START).append(prevMetric).append(TD_END); b.append(TD_START).append(prevMetric).append(TD_END);
b.append(TD_START).append(tags.toString()).append(TD_END); b.append(TD_START).append(tags.toString()).append(TD_END);
@@ -168,8 +170,12 @@ public class MetricsResponse {
if (prevMetric == null) { if (prevMetric == null) {
prevMetric = m.getMetric(); prevMetric = m.getMetric();
} }
long numValues = tagMap.getOrDefault(m.getTagKey(), 0l);
if (!(this.ignoredTags.contains(m.getTagKey()))) { if (!(this.ignoredTags.contains(m.getTagKey()))) {
tags.append(m.getTagKey()).append("=").append(m.getTagValue()).append(" "); if (numValues <= conf.getMetaCache().getMaxTagValues()) {
tags.append(m.getTagKey()).append("=").append(m.getTagValue()).append(" ");
tagMap.put(m.getTagKey(), ++numValues);
}
} }
} }
b.append(TR_START); b.append(TR_START);
+110 -41
View File
@@ -1,81 +1,150 @@
package timely.auth; package timely.auth;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
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 io.netty.handler.codec.http.HttpResponseStatus;
import org.apache.accumulo.core.security.Authorizations; import org.apache.accumulo.core.security.Authorizations;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.security.core.Authentication; import org.slf4j.Logger;
import org.springframework.security.core.GrantedAuthority; import org.slf4j.LoggerFactory;
import timely.api.request.AuthenticatedRequest; import timely.api.request.AuthenticatedRequest;
import timely.api.request.Request; import timely.api.request.AuthenticatedWebSocketRequest;
import timely.api.response.TimelyException; import timely.auth.util.AuthorizationsUtil;
import timely.configuration.Configuration;
import timely.configuration.Security; import timely.configuration.Security;
import timely.netty.http.auth.TimelyAuthenticationToken;
public class AuthCache { public class AuthCache {
private static Cache<String, Authentication> CACHE = null; private static final Logger LOG = LoggerFactory.getLogger(AuthCache.class);
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(Configuration config) { 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 = config.getSecurity().getSessionMaxAge(); cacheExpirationMinutes = security.getCacheExpirationMinutes();
cacheRefreshMinutes = security.getCacheRefreshMinutes();
} }
public static Cache<String, Authentication> 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;
} }
public static Authorizations getAuthorizations(String sessionId) { private static TimelyPrincipal getTimelyPrincipal(String key) {
if (!StringUtils.isEmpty(sessionId)) { LOG.debug("Refreshing TimelyPrincipal {}", key);
Authentication auth = CACHE.asMap().get(sessionId); TimelyPrincipal timelyPrincipal = null;
if (null != auth) { String[] dnArray = key.split(" -> ");
Collection<? extends GrantedAuthority> authorities = CACHE.asMap().get(sessionId).getAuthorities(); if (dnArray.length == 1) {
String[] auths = new String[authorities.size()]; SubjectIssuerDNPair pair = SubjectIssuerDNPair.parse(dnArray[0]);
final AtomicInteger i = new AtomicInteger(0); TimelyAuthenticationToken token = AuthenticationService.getAuthenticationToken(new Object(),
authorities.forEach(a -> auths[i.getAndIncrement()] = a.getAuthority()); pair.subjectDN(), pair.issuerDN());
return new Authorizations(auths); 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) {
return getCache().getIfPresent(key) != null;
}
public static TimelyPrincipal get(String key) {
return getCache().getIfPresent(key);
}
public static void put(String key, TimelyPrincipal principal) {
getCache().put(key, principal);
}
public static void remove(String key) {
getCache().invalidate(key);
}
public static void clear() {
if (CACHE != null) {
CACHE.invalidateAll();
}
}
protected static Collection<Authorizations> getAuthorizations(String entityName) {
if (!StringUtils.isEmpty(entityName)) {
List<Authorizations> authorizationsList = new ArrayList<>();
TimelyPrincipal principal = AuthCache.get(entityName);
if (principal != null) {
for (Collection<String> authCollection : principal.getAuthorizations()) {
authorizationsList.add(AuthorizationsUtil.toAuthorizations(authCollection));
}
LOG.debug("Authorizations for user {} {}", entityName, principal.getAuthorizationsString());
return authorizationsList;
} else { } else {
return null; return Collections.singletonList(Authorizations.EMPTY);
} }
} else { } else {
throw new IllegalArgumentException("session id cannot be null"); throw new IllegalArgumentException("entityName can not be null");
} }
} }
public static void enforceAccess(Security security, Request r) throws Exception { public static Collection<Authorizations> getAuthorizations(AuthenticatedRequest request, Security security) {
if (!security.isAllowAnonymousAccess() && (r instanceof AuthenticatedRequest)) { Collection<Authorizations> auths;
AuthenticatedRequest ar = (AuthenticatedRequest) r; boolean anonAccessAllowed;
if (StringUtils.isEmpty(ar.getSessionId())) { if (request instanceof AuthenticatedWebSocketRequest) {
throw new TimelyException(HttpResponseStatus.UNAUTHORIZED.code(), "User must log in", anonAccessAllowed = security.isAllowAnonymousWsAccess();
"Anonymous access is disabled, log in first"); } else {
} anonAccessAllowed = security.isAllowAnonymousHttpAccess();
if (!AuthCache.getCache().asMap().containsKey(ar.getSessionId())) {
throw new TimelyException(HttpResponseStatus.UNAUTHORIZED.code(), "User must log in",
"Unknown session id was submitted, log in again");
}
} }
TimelyAuthenticationToken token = request.getToken();
String sessionId = request.getSessionId();
if (StringUtils.isBlank(sessionId) && token.getClientCert() == null) {
if (anonAccessAllowed) {
auths = Collections.singletonList(Authorizations.EMPTY);
} else {
throw new IllegalArgumentException("User must provide either a sessionId or a client certificate");
}
} else if (StringUtils.isNotBlank(sessionId)) {
auths = getAuthorizations(sessionId);
} else {
auths = getAuthorizations(token.getTimelyPrincipal().getName());
}
return auths;
} }
} }
@@ -1,37 +1,275 @@
package timely.auth; package timely.auth;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.ServiceConfigurationError; import java.util.ServiceConfigurationError;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import com.google.common.collect.Multimap;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.ssl.SslHandler;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext; 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.web.authentication.preauth.x509.SubjectDnX509PrincipalExtractor; import org.springframework.security.core.Authentication;
import timely.api.request.AuthenticatedRequest;
import timely.api.response.TimelyException;
import timely.auth.util.DnUtils;
import timely.netty.http.auth.TimelyAuthenticationToken;
import timely.netty.http.auth.TimelyUserDetails;
public class AuthenticationService { public class AuthenticationService {
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> fetchingPrincipal = new ConcurrentHashMap<>();
private static Collection<String> requiredRoles;
private static Collection<String> requiredAuths;
static { static {
try { try {
springContext = new ClassPathXmlApplicationContext("security.xml"); springContext = new ClassPathXmlApplicationContext("security.xml");
authManager = (AuthenticationManager) springContext.getBean("authenticationManager"); authManager = springContext.getBean("authenticationManager", AuthenticationManager.class);
x509 = (SubjectDnX509PrincipalExtractor) springContext.getBean("x509PrincipalExtractor"); @SuppressWarnings("unchecked")
List<String> uncheckedRoles = springContext.getBean("requiredRoles", List.class);
requiredRoles = uncheckedRoles;
requiredRoles.removeIf(String::isEmpty);
@SuppressWarnings("unchecked")
List<String> uncheckedAuths = springContext.getBean("requiredAuths", List.class);
requiredAuths = uncheckedAuths;
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());
} }
} }
public static AuthenticationManager getAuthenticationManager() { private static AuthenticationManager getAuthenticationManager() {
return authManager; return authManager;
} }
public static String extractDN(X509Certificate clientCert) throws Exception { public static TimelyPrincipal authenticate(Authentication authentication, String cacheName) {
return (String) x509.extractPrincipal(clientCert); return authenticate(authentication, SubjectIssuerDNPair.of(cacheName), cacheName);
} }
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
TimelyPrincipal principal = null;
if (useCache) {
principal = AuthCache.get(entity);
}
// once an entity is in the AuthCache, we can bypass all of this
if (principal == null) {
Object newObj = new Object();
Object o = fetchingEntity.getOrDefault(entity, newObj);
if (o == null) {
o = newObj;
}
try {
synchronized (o) {
// if multiple threads are waiting on this synchronized, then the 2nd and all
// others will get the principal here
if (useCache) {
principal = AuthCache.get(entity);
}
if (principal == null) {
Authentication token = getAuthenticationManager().authenticate(authentication);
principal = getTimelyPrincipal(token, pair);
LOG.trace("Got entity principal for {} from AuthenticationManager", entity);
if (useCache) {
AuthCache.put(entity, principal);
}
} else {
LOG.trace("Got entity principal for {} from AuthCache on 2nd attempt", entity);
}
}
} finally {
fetchingEntity.remove(entity);
}
} else {
LOG.trace("Got entity principal for {} from AuthCache on 1st attempt", entity);
}
return principal;
}
public static void enforceAccess(AuthenticatedRequest request) throws Exception {
String sessionId = request.getSessionId();
X509Certificate clientCert = request.getToken().getClientCert();
TimelyPrincipal verifiedPrincipal = null;
if (StringUtils.isBlank(sessionId) && clientCert == null) {
throw new TimelyException(HttpResponseStatus.UNAUTHORIZED.code(), "User must log in",
"Anonymous access is disabled. User must either send a client certificate or log in");
} else if (StringUtils.isNotBlank(sessionId)) {
verifiedPrincipal = AuthCache.get(sessionId);
if (verifiedPrincipal == null) {
throw new TimelyException(HttpResponseStatus.UNAUTHORIZED.code(), "User must log in",
"Unknown session id was submitted, log in again");
}
} else {
try {
TimelyAuthenticationToken token = request.getToken();
TimelyPrincipal requestPrincipal = token.getTimelyPrincipal();
String entity = requestPrincipal.getName();
verifiedPrincipal = AuthCache.get(entity);
if (verifiedPrincipal == null) {
Object newObj = new Object();
Object o = fetchingPrincipal.getOrDefault(entity, newObj);
if (o == null) {
o = newObj;
}
try {
synchronized (o) {
// check again if in the AuthCache
verifiedPrincipal = AuthCache.get(entity);
if (verifiedPrincipal == null) {
// first thread here, so authenticate all entities in requestPrincipal
List<TimelyUser> authenticatedTimelyUsers = new ArrayList<>();
for (TimelyUser user : requestPrincipal.getProxiedUsers()) {
SubjectIssuerDNPair p = user.getDn();
TimelyAuthenticationToken entityToken = getAuthenticationToken(clientCert,
p.subjectDN(), p.issuerDN());
TimelyPrincipal entityPrincipal = authenticate(entityToken, p,
entityToken.getTimelyPrincipal().getName());
authenticatedTimelyUsers.addAll(entityPrincipal.getProxiedUsers());
}
verifiedPrincipal = new TimelyPrincipal(authenticatedTimelyUsers);
AuthCache.put(verifiedPrincipal.getName(), verifiedPrincipal);
LOG.debug("Authenticated user {} for request {} with authorizations {}",
verifiedPrincipal.getName(), request.toString(),
verifiedPrincipal.getAuthorizationsString());
} else {
LOG.trace("Verified principal {} in AuthCache on 2nd attempt", entity);
}
}
} finally {
fetchingPrincipal.remove(entity);
}
} else {
LOG.trace("Verified principal {} in AuthCache on 1st attempt", entity);
}
} catch (RuntimeException e) {
LOG.error(e.getMessage(), e);
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) {
X509Certificate clientCert = null;
if (ctx != null) {
SslHandler sslHandler = (SslHandler) ctx.channel().pipeline().get("ssl");
if (null != sslHandler) {
try {
clientCert = (X509Certificate) sslHandler.engine().getSession().getPeerCertificates()[0];
} catch (Exception e) {
}
} else {
throw new IllegalStateException("The expected SSL handler is not in the pipeline.");
}
}
return clientCert;
}
public static TimelyAuthenticationToken getAuthenticationToken(X509Certificate clientCert,
Multimap<String, String> headers) {
TimelyAuthenticationToken authenticationToken = null;
try {
authenticationToken = new TimelyAuthenticationToken(clientCert.getSubjectDN().getName(), clientCert,
headers);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
return authenticationToken;
}
public static TimelyAuthenticationToken getAuthenticationToken(Object clientCert, String subjectDn,
String issuerDn) {
TimelyAuthenticationToken authenticationToken = null;
try {
authenticationToken = new TimelyAuthenticationToken(subjectDn, issuerDn, clientCert);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
return authenticationToken;
}
private static TimelyPrincipal getTimelyPrincipal(Authentication authentication,
SubjectIssuerDNPair subjectIssuerDNPair) {
String subjectDn = subjectIssuerDNPair.subjectDN();
TimelyUser.UserType userType = DnUtils.isServerDN(subjectDn) ? TimelyUser.UserType.SERVER
: TimelyUser.UserType.USER;
Collection<String> auths = authentication.getAuthorities().stream().map(a -> a.getAuthority())
.collect(Collectors.toList());
Object o = authentication.getPrincipal();
Collection<String> roles = null;
if (o instanceof TimelyUserDetails) {
roles = ((TimelyUserDetails) o).getRoles();
}
TimelyUser timelyUser = new TimelyUser(subjectIssuerDNPair, userType, auths, roles, null,
System.currentTimeMillis());
return new TimelyPrincipal(Arrays.asList(timelyUser));
}
} }
@@ -0,0 +1,93 @@
package timely.auth;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.accumulo.core.security.Authorizations;
/**
* @see AuthorizationsMinimizer#minimize(Collection)
*/
public class AuthorizationsMinimizer {
/**
* Given a collection of Accumulo {@link Authorizations} objects, this method
* attempts to compute a "minimum" set that can be used for a multi-entity scan
* of data from Accumulo. The idea is that there is a call chain with multiple
* entities (e.g., a middleware broker acting on behalf of a GUI that is in turn
* acting on behalf of a user). In order to ensure that only data visible to all
* of the entities is returned, we must add a visibility filter for each entity
* in the chain to ensure that each key/value pair returned by Accumulo can be
* seen by all of the entities in the chain. Since we may be scanning a very
* large number of keys, it would be ideal if we could reduce the number of
* visibility filters to a small set if that can be done safely.
* <p>
* The crux of the problem, and the reason we need multiple filters in the first
* place (as opposed to just intersecting the authorizations for all all
* entities and using that set), is the fact that Accumulo visibility
* expressions may include disjunctions. Consider the following visibility
* expression.
* <p>
* {@code (A & B & ORG1) | (A & C & ORG2)}
* <p>
* If the incoming user has the Accumulo authorizations A, B, and ORG1 and the
* user is coming through a server with the Accumulo authorizations A, C, and
* ORG2, then the server is allowed to see data protected with this expression
* and the user is also allowed, so the data should be returned with those two
* calling entities. If we simply computed an intersection of the authorizations
* that set would be A. That set of authorizations does not match the visibility
* expression and data that should be returned would not be.
* <p>
* Given that we must apply multiple visibility filters in order to properly
* return data for a calling entity chain, it is desirable to minimize the
* number of visibility filters used, if possible. If a set of authorizations is
* a superset of any other set of authorizations, then it does not need to be
* tested. As long as we are not changing any of the authorizations sets, we
* know we will never allow any data to be returned that would otherwise not be.
* By removing authorizations sets, we might only prevent the return of data
* that should otherwise be returned. Consider a given set of authorizations
* that is a subset of another. If the subset passes a visibility expression,
* then the superset will too since it contains at least the same
* authorizations, so there is no need to test against it. If the subset does
* not pass, then the data is not visible to the chain anyway, so there is no
* point in testing the superset.
*
* @param authorizations
* the list of Authorizations for each entity in the call chain
* @return a minimized set of Authorizations that allows visibility of exactly
* the same data as {@code authorizations}
*/
public static Collection<Authorizations> minimize(Collection<Authorizations> authorizations) {
if (authorizations.size() > 1) {
// Convert collection of Authorizations into a collection of String sets (the
// individual authorizations).
// Since we are adding to a LinkedHashSet, this will de-dupe any duplicate
// authorization sets.
final LinkedHashSet<Set<String>> allAuths = authorizations.stream().map(
a -> a.getAuthorizations().stream().map(String::new).collect(Collectors.toCollection(HashSet::new)))
.collect(Collectors.toCollection(LinkedHashSet::new));
// Go through the authorizations sets and remove any that are supersets of any
// other.
for (Iterator<Set<String>> it = allAuths.iterator(); it.hasNext(); /* empty */) {
Set<String> currentSet = it.next();
if (allAuths.stream().filter(a -> a != currentSet && a.size() <= currentSet.size())
.anyMatch(currentSet::containsAll))
it.remove();
}
// If we removed any sets of authorizations, then we need to convert the reduced
// set from
// TreeSet<String> objects back into Authorizations objects.
if (allAuths.size() < authorizations.size()) {
authorizations = allAuths.stream().map(a -> new Authorizations(a.toArray(new String[0])))
.collect(Collectors.toCollection(LinkedHashSet::new));
}
}
return authorizations;
}
}
Loaded 100 of 226 files, more files were not shown because too many files have changed in this diff. Show more