Compare commits

..
18 Commits
Author SHA1 Message Date
Jim Klucar d18b5bc46d commit to switch to my laptop 2016-08-25 16:02:23 -04:00
Jim Klucar ba60f9ef00 Merge branch 'master' into wamp 2016-08-25 14:19:58 -04:00
Jim Klucar 49a8d970fe minor pom consistency tweaks 2016-08-25 14:18:55 -04:00
Jim Klucar 345f4b7006 merge of spring config changes 2016-08-25 14:07:51 -04:00
Jim Klucar b81783f7ea initial WAMP setup 2016-08-25 13:48:41 -04:00
brianloss 1be6f24676 Use Spring Configuration (#68)
* Initial pass at converting timely configuration to Spring Boot for type-safe config that is a lot more flexible than standard properties file configuration.

* Change banner color to red.

* Update Configuration class to support fluent API.

* Re-work configuration to group properties in a more sensible manner.

* Use NotBlank everywhere instead of NotEmpty for validations.
2016-08-25 13:19:59 -04:00
Dave Marion 82b94f1a71 fixes #40 - add regex support for lookup requests 2016-08-23 09:26:56 -04:00
Jim Klucar 019c5789ce fixed bug where timely.ssl.trust.store.file property is defined but is empty string 2016-08-05 11:27:43 -04:00
klucar 4336defc14 docker update version 2016-08-05 07:45:56 -04:00
klucar 712a2100bb added instances to metrics 2016-08-05 07:44:57 -04:00
klucar bc46f54604 updated to 0.0.3-SNAPSHOT 2016-08-03 13:54:42 -04:00
Dave Marion 1b713e7874 re #56: fixed some scanner handling, log messages 2016-08-02 10:38:25 -04:00
Dave Marion 3f5dfd2219 fixes #56: Added a Netty handler in the web socket pipeline to get the TSESSIONID cookie value before the web socket upgrade and store it in the channel attribute map. Replaced sessionId property on subscription objects with subscriptionId, which will now allow multiple subscriptions per channel. Modified MetricResponse to contain the subscriptionId in the returned data. Updated tests and example. 2016-08-02 09:29:49 -04:00
Dave Marion ea707fda42 fixes #57, moved plugin/dependency versions to parent pom 2016-08-01 13:02:29 -04:00
klucar b245be476a fixed display name of dashboard 2016-07-28 09:25:24 -04:00
klucar 9619530814 making dashboards available for #59 2016-07-28 09:23:21 -04:00
klucar adf617e59f First cut at #59 2016-07-28 09:20:05 -04:00
Dave Marion 3f49855840 Set version to 0.0.3-SNAPSHOT 2016-07-27 08:23:50 -04:00
89 changed files with 18121 additions and 1182 deletions

No files matched your search

+1
View File
@@ -4,3 +4,4 @@
target/
.idea/
*.iml
logs/
+1
View File
@@ -3,3 +3,4 @@ Eric Newton ([email protected])
Jim Klucar ([email protected])
Charlie Williams ([email protected])
Drew Farris ([email protected])
Brian Loss ([email protected])
+1 -36
View File
@@ -4,21 +4,17 @@
<parent>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>0.0.2</version>
<version>0.0.3-SNAPSHOT</version>
</parent>
<artifactId>collectd-nsq-plugin</artifactId>
<dependencies>
<dependency>
<groupId>collectd</groupId>
<artifactId>collectd-api</artifactId>
<version>5.5.1</version>
<scope>system</scope>
<systemPath>${collectd-api-jar}</systemPath>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.1</version>
</dependency>
<dependency>
<groupId>timely</groupId>
@@ -56,7 +52,6 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.2</version>
<configuration>
<artifactSet>
<includes>
@@ -77,7 +72,6 @@
<plugin>
<groupId>net.revelc.code</groupId>
<artifactId>formatter-maven-plugin</artifactId>
<version>0.5.2</version>
<configuration>
<configFile>${project.parent.basedir}/eclipse/Eclipse-Formatter.xml</configFile>
<lineEnding>LF</lineEnding>
@@ -92,34 +86,5 @@
</executions>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>net.revelc.code</groupId>
<artifactId>formatter-maven-plugin</artifactId>
<versionRange>[0.5.2,)</versionRange>
<goals>
<goal>format</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore/>
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
+1 -37
View File
@@ -4,21 +4,17 @@
<parent>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>0.0.2</version>
<version>0.0.3-SNAPSHOT</version>
</parent>
<artifactId>collectd-plugin-parent</artifactId>
<dependencies>
<dependency>
<groupId>collectd</groupId>
<artifactId>collectd-api</artifactId>
<version>5.5.1</version>
<scope>system</scope>
<systemPath>${collectd-api-jar}</systemPath>
</dependency>
<dependency>
<groupId>net.jcip</groupId>
<artifactId>jcip-annotations</artifactId>
<version>1.0</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -29,7 +25,6 @@
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-easymock-release-full</artifactId>
<version>1.6.4</version>
<classifier>full</classifier>
<scope>test</scope>
</dependency>
@@ -64,7 +59,6 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19</version>
<configuration>
<excludedGroups>timely.test.IntegrationTest</excludedGroups>
<redirectTestOutputToFile>true</redirectTestOutputToFile>
@@ -73,7 +67,6 @@
<plugin>
<groupId>net.revelc.code</groupId>
<artifactId>formatter-maven-plugin</artifactId>
<version>0.5.2</version>
<configuration>
<configFile>${project.parent.basedir}/eclipse/Eclipse-Formatter.xml</configFile>
<lineEnding>LF</lineEnding>
@@ -88,34 +81,5 @@
</executions>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>net.revelc.code</groupId>
<artifactId>formatter-maven-plugin</artifactId>
<versionRange>[0.5.2,)</versionRange>
<goals>
<goal>format</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore/>
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
+1 -38
View File
@@ -4,21 +4,17 @@
<parent>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>0.0.2</version>
<version>0.0.3-SNAPSHOT</version>
</parent>
<artifactId>collectd-timely-plugin</artifactId>
<dependencies>
<dependency>
<groupId>collectd</groupId>
<artifactId>collectd-api</artifactId>
<version>5.5.1</version>
<scope>system</scope>
<systemPath>${collectd-api-jar}</systemPath>
</dependency>
<dependency>
<groupId>net.jcip</groupId>
<artifactId>jcip-annotations</artifactId>
<version>1.0</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -34,7 +30,6 @@
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-easymock-release-full</artifactId>
<version>1.6.4</version>
<classifier>full</classifier>
<scope>test</scope>
</dependency>
@@ -75,7 +70,6 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.2</version>
<configuration>
<artifactSet>
<includes>
@@ -96,7 +90,6 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19</version>
<configuration>
<excludedGroups>timely.test.IntegrationTest</excludedGroups>
<redirectTestOutputToFile>true</redirectTestOutputToFile>
@@ -105,7 +98,6 @@
<plugin>
<groupId>net.revelc.code</groupId>
<artifactId>formatter-maven-plugin</artifactId>
<version>0.5.2</version>
<configuration>
<configFile>${project.parent.basedir}/eclipse/Eclipse-Formatter.xml</configFile>
<lineEnding>LF</lineEnding>
@@ -120,34 +112,5 @@
</executions>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>net.revelc.code</groupId>
<artifactId>formatter-maven-plugin</artifactId>
<versionRange>[0.5.2,)</versionRange>
<goals>
<goal>format</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore/>
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
+1 -2
View File
@@ -4,14 +4,13 @@
<parent>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>0.0.2</version>
<version>0.0.3-SNAPSHOT</version>
</parent>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.6</version>
<executions>
<execution>
<id>assemble</id>
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,786 @@
{
"__inputs": [
{
"name": "DS_TIMELY",
"label": "Timely",
"description": "",
"type": "datasource",
"pluginId": "timely-datasource",
"pluginName": "Timely"
}
],
"__requires": [
{
"type": "panel",
"id": "singlestat",
"name": "Singlestat",
"version": ""
},
{
"type": "panel",
"id": "graph",
"name": "Graph",
"version": ""
},
{
"type": "grafana",
"id": "grafana",
"name": "Grafana",
"version": "3.1.0"
},
{
"type": "datasource",
"id": "timely-datasource",
"name": "Timely",
"version": "1.0.0"
}
],
"id": null,
"title": "Timely Status",
"tags": [],
"style": "dark",
"timezone": "browser",
"editable": true,
"hideControls": false,
"sharedCrosshair": false,
"rows": [
{
"collapse": false,
"editable": true,
"height": "150",
"panels": [
{
"cacheTimeout": null,
"colorBackground": false,
"colorValue": false,
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)"
],
"datasource": "${DS_TIMELY}",
"editable": true,
"error": false,
"format": "none",
"gauge": {
"maxValue": 100000,
"minValue": 0,
"show": true,
"thresholdLabels": false,
"thresholdMarkers": false
},
"height": "150",
"hideTimeOverride": true,
"id": 8,
"interval": null,
"isNew": true,
"links": [],
"mappingType": 1,
"mappingTypes": [
{
"name": "value to text",
"value": 1
},
{
"name": "range to text",
"value": 2
}
],
"maxDataPoints": 100,
"nullPointMode": "connected",
"nullText": null,
"postfix": "",
"postfixFontSize": "50%",
"prefix": "",
"prefixFontSize": "50%",
"rangeMaps": [
{
"from": "null",
"text": "N/A",
"to": "null"
}
],
"span": 4,
"sparkline": {
"fillColor": "rgba(31, 118, 189, 0.18)",
"full": false,
"lineColor": "rgb(31, 120, 193)",
"show": false
},
"targets": [
{
"aggregator": "count",
"downsampleAggregator": "sum",
"downsampleFillPolicy": "none",
"downsampleInterval": "1m",
"metric": "timely.metrics.received",
"refId": "A"
}
],
"thresholds": "",
"timeFrom": null,
"timeShift": "1m",
"title": "Metrics Received Last Minute",
"transparent": true,
"type": "singlestat",
"valueFontSize": "80%",
"valueMaps": [
{
"op": "=",
"text": "N/A",
"value": "null"
}
],
"valueName": "avg"
},
{
"cacheTimeout": null,
"colorBackground": false,
"colorValue": false,
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)"
],
"datasource": "${DS_TIMELY}",
"editable": true,
"error": false,
"format": "none",
"gauge": {
"maxValue": 100,
"minValue": 0,
"show": false,
"thresholdLabels": false,
"thresholdMarkers": true
},
"height": "150",
"hideTimeOverride": true,
"id": 9,
"interval": null,
"isNew": true,
"links": [],
"mappingType": 1,
"mappingTypes": [
{
"name": "value to text",
"value": 1
},
{
"name": "range to text",
"value": 2
}
],
"maxDataPoints": 100,
"nullPointMode": "connected",
"nullText": null,
"postfix": "",
"postfixFontSize": "50%",
"prefix": "",
"prefixFontSize": "50%",
"rangeMaps": [
{
"from": "null",
"text": "N/A",
"to": "null"
}
],
"span": 4,
"sparkline": {
"fillColor": "rgba(31, 118, 189, 0.18)",
"full": false,
"lineColor": "rgb(31, 120, 193)",
"show": false
},
"targets": [
{
"aggregator": "count",
"currentTagKey": "",
"currentTagValue": "",
"downsampleAggregator": "count",
"downsampleFillPolicy": "none",
"downsampleInterval": "1m",
"metric": "sys.collectd.queue_length",
"refId": "A",
"tags": {}
}
],
"thresholds": "",
"timeShift": "1m",
"title": "CollectD Instances Reporting",
"transparent": true,
"type": "singlestat",
"valueFontSize": "80%",
"valueMaps": [
{
"op": "=",
"text": "N/A",
"value": "null"
}
],
"valueName": "avg"
},
{
"cacheTimeout": null,
"colorBackground": false,
"colorValue": false,
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)"
],
"datasource": "${DS_TIMELY}",
"editable": true,
"error": false,
"format": "none",
"gauge": {
"maxValue": 100,
"minValue": 0,
"show": false,
"thresholdLabels": false,
"thresholdMarkers": true
},
"height": "150",
"hideTimeOverride": true,
"id": 10,
"interval": null,
"isNew": true,
"links": [],
"mappingType": 1,
"mappingTypes": [
{
"name": "value to text",
"value": 1
},
{
"name": "range to text",
"value": 2
}
],
"maxDataPoints": 100,
"nullPointMode": "connected",
"nullText": null,
"postfix": "",
"postfixFontSize": "50%",
"prefix": "",
"prefixFontSize": "50%",
"rangeMaps": [
{
"from": "null",
"text": "N/A",
"to": "null"
}
],
"span": 4,
"sparkline": {
"fillColor": "rgba(31, 118, 189, 0.18)",
"full": false,
"lineColor": "rgb(31, 120, 193)",
"show": false
},
"targets": [
{
"aggregator": "sum",
"currentTagKey": "",
"currentTagValue": "",
"downsampleAggregator": "sum",
"downsampleFillPolicy": "none",
"downsampleInterval": "1m",
"metric": "sys.collectd.derive.dropped",
"refId": "A",
"tags": {}
}
],
"thresholds": "",
"timeShift": "1m",
"title": "Metrics Dropped By CollectD",
"transparent": true,
"type": "singlestat",
"valueFontSize": "80%",
"valueMaps": [
{
"op": "=",
"text": "N/A",
"value": "null"
}
],
"valueName": "avg"
}
],
"title": "New row"
},
{
"collapse": false,
"editable": true,
"height": "250px",
"panels": [
{
"aliasColors": {},
"bars": false,
"datasource": "${DS_TIMELY}",
"editable": true,
"error": false,
"fill": 0,
"grid": {
"threshold1": null,
"threshold1Color": "rgba(216, 200, 27, 0.27)",
"threshold2": null,
"threshold2Color": "rgba(234, 112, 112, 0.22)"
},
"id": 3,
"isNew": true,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 2,
"links": [],
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"span": 4,
"stack": false,
"steppedLine": false,
"targets": [
{
"aggregator": "sum",
"currentTagKey": "",
"currentTagValue": "",
"downsampleAggregator": "sum",
"downsampleFillPolicy": "none",
"downsampleInterval": "1m",
"metric": "timely.keys.metric.inserted",
"refId": "A",
"tags": {
"host": ".*"
}
}
],
"timeFrom": null,
"timeShift": null,
"title": "Timely Metrics Inserted",
"tooltip": {
"msResolution": true,
"shared": true,
"sort": 0,
"value_type": "cumulative"
},
"type": "graph",
"xaxis": {
"show": true
},
"yaxes": [
{
"format": "none",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
]
},
{
"aliasColors": {},
"bars": false,
"datasource": "${DS_TIMELY}",
"editable": true,
"error": false,
"fill": 0,
"grid": {
"threshold1": null,
"threshold1Color": "rgba(216, 200, 27, 0.27)",
"threshold2": null,
"threshold2Color": "rgba(234, 112, 112, 0.22)"
},
"id": 4,
"isNew": true,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 2,
"links": [],
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"span": 4,
"stack": false,
"steppedLine": false,
"targets": [
{
"aggregator": "sum",
"currentTagKey": "",
"currentTagValue": "",
"downsampleAggregator": "avg",
"downsampleFillPolicy": "none",
"downsampleInterval": "1m",
"metric": "timely.keys.meta.inserted",
"refId": "A",
"tags": {
"host": ".*"
}
}
],
"timeFrom": null,
"timeShift": null,
"title": "Timely Meta Inserted",
"tooltip": {
"msResolution": true,
"shared": true,
"sort": 0,
"value_type": "cumulative"
},
"type": "graph",
"xaxis": {
"show": true
},
"yaxes": [
{
"format": "none",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
]
},
{
"aliasColors": {},
"bars": false,
"datasource": "${DS_TIMELY}",
"editable": true,
"error": false,
"fill": 0,
"grid": {
"threshold1": null,
"threshold1Color": "rgba(216, 200, 27, 0.27)",
"threshold2": null,
"threshold2Color": "rgba(234, 112, 112, 0.22)"
},
"id": 5,
"isNew": true,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 2,
"links": [],
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"span": 4,
"stack": false,
"steppedLine": false,
"targets": [
{
"aggregator": "sum",
"currentTagKey": "",
"currentTagValue": "",
"downsampleAggregator": "sum",
"downsampleFillPolicy": "none",
"downsampleInterval": "1m",
"metric": "timely.metrics.received",
"refId": "A",
"tags": {
"host": ".*"
}
}
],
"timeFrom": null,
"timeShift": null,
"title": "Timely Metrics Received",
"tooltip": {
"msResolution": true,
"shared": true,
"sort": 0,
"value_type": "cumulative"
},
"type": "graph",
"xaxis": {
"show": true
},
"yaxes": [
{
"format": "none",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
]
}
],
"title": "New row"
},
{
"collapse": false,
"editable": true,
"height": "250px",
"panels": [
{
"aliasColors": {},
"bars": false,
"datasource": "${DS_TIMELY}",
"editable": true,
"error": false,
"fill": 0,
"grid": {
"threshold1": null,
"threshold1Color": "rgba(216, 200, 27, 0.27)",
"threshold2": null,
"threshold2Color": "rgba(234, 112, 112, 0.22)"
},
"id": 7,
"isNew": true,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 2,
"links": [],
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"span": 6,
"stack": false,
"steppedLine": false,
"targets": [
{
"aggregator": "sum",
"alias": "$tag_host",
"currentTagKey": "",
"currentTagValue": "",
"downsampleAggregator": "sum",
"downsampleFillPolicy": "none",
"downsampleInterval": "1m",
"metric": "sys.collectd.cache_size",
"refId": "A",
"tags": {
"host": ".*"
}
}
],
"timeFrom": null,
"timeShift": null,
"title": "CollectD Cache Sizes",
"tooltip": {
"msResolution": true,
"shared": true,
"sort": 0,
"value_type": "cumulative"
},
"type": "graph",
"xaxis": {
"show": true
},
"yaxes": [
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
]
},
{
"aliasColors": {},
"bars": false,
"datasource": "${DS_TIMELY}",
"editable": true,
"error": false,
"fill": 0,
"grid": {
"threshold1": null,
"threshold1Color": "rgba(216, 200, 27, 0.27)",
"threshold2": null,
"threshold2Color": "rgba(234, 112, 112, 0.22)"
},
"id": 11,
"isNew": true,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 2,
"links": [],
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"span": 6,
"stack": false,
"steppedLine": false,
"targets": [
{
"aggregator": "sum",
"alias": "$tag_host",
"currentTagKey": "",
"currentTagValue": "",
"downsampleAggregator": "sum",
"downsampleFillPolicy": "none",
"downsampleInterval": "1m",
"metric": "sys.collectd.queue_length",
"refId": "A",
"tags": {
"host": ".*"
}
}
],
"timeFrom": null,
"timeShift": null,
"title": "CollectD Queue Length",
"tooltip": {
"msResolution": true,
"shared": true,
"sort": 0,
"value_type": "cumulative"
},
"type": "graph",
"xaxis": {
"show": true
},
"yaxes": [
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
]
}
],
"title": "New row"
}
],
"time": {
"from": "now-1h",
"to": "now"
},
"timepicker": {
"now": true,
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"templating": {
"list": []
},
"annotations": {
"list": []
},
"refresh": "1m",
"schemaVersion": 12,
"version": 8,
"links": [],
"gnetId": null
}
@@ -7,8 +7,8 @@
}
],
"id": null,
"title": "${DS_NAME} - Status",
"originalTitle": "${DS_NAME} - Status",
"title": "Timely Standalone Demo",
"originalTitle": "Timely Standalone Demo",
"tags": ["imported"],
"style": "dark",
"timezone": "browser",
+5 -1
View File
@@ -8,7 +8,11 @@
"annotations": true,
"includes": [
{"type": "dashboard", "name": "Timely Metrics", "path": "dashboards/standalone_test.json"}
{"type": "dashboard", "name": "Timely Standalone Test", "path": "dashboards/standalone_test.json"},
{"type": "dashboard", "name": "Accumulo Overview", "path": "dashboards/AccumuloOverview.json"},
{"type": "dashboard", "name": "Hadoop Overview", "path": "dashboards/HadoopOverview.json"},
{"type": "dashboard", "name": "System Overview", "path": "dashboards/SystemOverview.json"},
{"type": "dashboard", "name": "Timely Status", "path": "dashboards/TimelyStatus.json"}
],
"info": {
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,786 @@
{
"__inputs": [
{
"name": "DS_TIMELY",
"label": "Timely",
"description": "",
"type": "datasource",
"pluginId": "timely-datasource",
"pluginName": "Timely"
}
],
"__requires": [
{
"type": "panel",
"id": "singlestat",
"name": "Singlestat",
"version": ""
},
{
"type": "panel",
"id": "graph",
"name": "Graph",
"version": ""
},
{
"type": "grafana",
"id": "grafana",
"name": "Grafana",
"version": "3.1.0"
},
{
"type": "datasource",
"id": "timely-datasource",
"name": "Timely",
"version": "1.0.0"
}
],
"id": null,
"title": "Timely Status",
"tags": [],
"style": "dark",
"timezone": "browser",
"editable": true,
"hideControls": false,
"sharedCrosshair": false,
"rows": [
{
"collapse": false,
"editable": true,
"height": "150",
"panels": [
{
"cacheTimeout": null,
"colorBackground": false,
"colorValue": false,
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)"
],
"datasource": "${DS_TIMELY}",
"editable": true,
"error": false,
"format": "none",
"gauge": {
"maxValue": 100000,
"minValue": 0,
"show": true,
"thresholdLabels": false,
"thresholdMarkers": false
},
"height": "150",
"hideTimeOverride": true,
"id": 8,
"interval": null,
"isNew": true,
"links": [],
"mappingType": 1,
"mappingTypes": [
{
"name": "value to text",
"value": 1
},
{
"name": "range to text",
"value": 2
}
],
"maxDataPoints": 100,
"nullPointMode": "connected",
"nullText": null,
"postfix": "",
"postfixFontSize": "50%",
"prefix": "",
"prefixFontSize": "50%",
"rangeMaps": [
{
"from": "null",
"text": "N/A",
"to": "null"
}
],
"span": 4,
"sparkline": {
"fillColor": "rgba(31, 118, 189, 0.18)",
"full": false,
"lineColor": "rgb(31, 120, 193)",
"show": false
},
"targets": [
{
"aggregator": "count",
"downsampleAggregator": "sum",
"downsampleFillPolicy": "none",
"downsampleInterval": "1m",
"metric": "timely.metrics.received",
"refId": "A"
}
],
"thresholds": "",
"timeFrom": null,
"timeShift": "1m",
"title": "Metrics Received Last Minute",
"transparent": true,
"type": "singlestat",
"valueFontSize": "80%",
"valueMaps": [
{
"op": "=",
"text": "N/A",
"value": "null"
}
],
"valueName": "avg"
},
{
"cacheTimeout": null,
"colorBackground": false,
"colorValue": false,
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)"
],
"datasource": "${DS_TIMELY}",
"editable": true,
"error": false,
"format": "none",
"gauge": {
"maxValue": 100,
"minValue": 0,
"show": false,
"thresholdLabels": false,
"thresholdMarkers": true
},
"height": "150",
"hideTimeOverride": true,
"id": 9,
"interval": null,
"isNew": true,
"links": [],
"mappingType": 1,
"mappingTypes": [
{
"name": "value to text",
"value": 1
},
{
"name": "range to text",
"value": 2
}
],
"maxDataPoints": 100,
"nullPointMode": "connected",
"nullText": null,
"postfix": "",
"postfixFontSize": "50%",
"prefix": "",
"prefixFontSize": "50%",
"rangeMaps": [
{
"from": "null",
"text": "N/A",
"to": "null"
}
],
"span": 4,
"sparkline": {
"fillColor": "rgba(31, 118, 189, 0.18)",
"full": false,
"lineColor": "rgb(31, 120, 193)",
"show": false
},
"targets": [
{
"aggregator": "count",
"currentTagKey": "",
"currentTagValue": "",
"downsampleAggregator": "count",
"downsampleFillPolicy": "none",
"downsampleInterval": "1m",
"metric": "sys.collectd.queue_length",
"refId": "A",
"tags": {}
}
],
"thresholds": "",
"timeShift": "1m",
"title": "CollectD Instances Reporting",
"transparent": true,
"type": "singlestat",
"valueFontSize": "80%",
"valueMaps": [
{
"op": "=",
"text": "N/A",
"value": "null"
}
],
"valueName": "avg"
},
{
"cacheTimeout": null,
"colorBackground": false,
"colorValue": false,
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)"
],
"datasource": "${DS_TIMELY}",
"editable": true,
"error": false,
"format": "none",
"gauge": {
"maxValue": 100,
"minValue": 0,
"show": false,
"thresholdLabels": false,
"thresholdMarkers": true
},
"height": "150",
"hideTimeOverride": true,
"id": 10,
"interval": null,
"isNew": true,
"links": [],
"mappingType": 1,
"mappingTypes": [
{
"name": "value to text",
"value": 1
},
{
"name": "range to text",
"value": 2
}
],
"maxDataPoints": 100,
"nullPointMode": "connected",
"nullText": null,
"postfix": "",
"postfixFontSize": "50%",
"prefix": "",
"prefixFontSize": "50%",
"rangeMaps": [
{
"from": "null",
"text": "N/A",
"to": "null"
}
],
"span": 4,
"sparkline": {
"fillColor": "rgba(31, 118, 189, 0.18)",
"full": false,
"lineColor": "rgb(31, 120, 193)",
"show": false
},
"targets": [
{
"aggregator": "sum",
"currentTagKey": "",
"currentTagValue": "",
"downsampleAggregator": "sum",
"downsampleFillPolicy": "none",
"downsampleInterval": "1m",
"metric": "sys.collectd.derive.dropped",
"refId": "A",
"tags": {}
}
],
"thresholds": "",
"timeShift": "1m",
"title": "Metrics Dropped By CollectD",
"transparent": true,
"type": "singlestat",
"valueFontSize": "80%",
"valueMaps": [
{
"op": "=",
"text": "N/A",
"value": "null"
}
],
"valueName": "avg"
}
],
"title": "New row"
},
{
"collapse": false,
"editable": true,
"height": "250px",
"panels": [
{
"aliasColors": {},
"bars": false,
"datasource": "${DS_TIMELY}",
"editable": true,
"error": false,
"fill": 0,
"grid": {
"threshold1": null,
"threshold1Color": "rgba(216, 200, 27, 0.27)",
"threshold2": null,
"threshold2Color": "rgba(234, 112, 112, 0.22)"
},
"id": 3,
"isNew": true,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 2,
"links": [],
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"span": 4,
"stack": false,
"steppedLine": false,
"targets": [
{
"aggregator": "sum",
"currentTagKey": "",
"currentTagValue": "",
"downsampleAggregator": "sum",
"downsampleFillPolicy": "none",
"downsampleInterval": "1m",
"metric": "timely.keys.metric.inserted",
"refId": "A",
"tags": {
"host": ".*"
}
}
],
"timeFrom": null,
"timeShift": null,
"title": "Timely Metrics Inserted",
"tooltip": {
"msResolution": true,
"shared": true,
"sort": 0,
"value_type": "cumulative"
},
"type": "graph",
"xaxis": {
"show": true
},
"yaxes": [
{
"format": "none",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
]
},
{
"aliasColors": {},
"bars": false,
"datasource": "${DS_TIMELY}",
"editable": true,
"error": false,
"fill": 0,
"grid": {
"threshold1": null,
"threshold1Color": "rgba(216, 200, 27, 0.27)",
"threshold2": null,
"threshold2Color": "rgba(234, 112, 112, 0.22)"
},
"id": 4,
"isNew": true,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 2,
"links": [],
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"span": 4,
"stack": false,
"steppedLine": false,
"targets": [
{
"aggregator": "sum",
"currentTagKey": "",
"currentTagValue": "",
"downsampleAggregator": "avg",
"downsampleFillPolicy": "none",
"downsampleInterval": "1m",
"metric": "timely.keys.meta.inserted",
"refId": "A",
"tags": {
"host": ".*"
}
}
],
"timeFrom": null,
"timeShift": null,
"title": "Timely Meta Inserted",
"tooltip": {
"msResolution": true,
"shared": true,
"sort": 0,
"value_type": "cumulative"
},
"type": "graph",
"xaxis": {
"show": true
},
"yaxes": [
{
"format": "none",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
]
},
{
"aliasColors": {},
"bars": false,
"datasource": "${DS_TIMELY}",
"editable": true,
"error": false,
"fill": 0,
"grid": {
"threshold1": null,
"threshold1Color": "rgba(216, 200, 27, 0.27)",
"threshold2": null,
"threshold2Color": "rgba(234, 112, 112, 0.22)"
},
"id": 5,
"isNew": true,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 2,
"links": [],
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"span": 4,
"stack": false,
"steppedLine": false,
"targets": [
{
"aggregator": "sum",
"currentTagKey": "",
"currentTagValue": "",
"downsampleAggregator": "sum",
"downsampleFillPolicy": "none",
"downsampleInterval": "1m",
"metric": "timely.metrics.received",
"refId": "A",
"tags": {
"host": ".*"
}
}
],
"timeFrom": null,
"timeShift": null,
"title": "Timely Metrics Received",
"tooltip": {
"msResolution": true,
"shared": true,
"sort": 0,
"value_type": "cumulative"
},
"type": "graph",
"xaxis": {
"show": true
},
"yaxes": [
{
"format": "none",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
]
}
],
"title": "New row"
},
{
"collapse": false,
"editable": true,
"height": "250px",
"panels": [
{
"aliasColors": {},
"bars": false,
"datasource": "${DS_TIMELY}",
"editable": true,
"error": false,
"fill": 0,
"grid": {
"threshold1": null,
"threshold1Color": "rgba(216, 200, 27, 0.27)",
"threshold2": null,
"threshold2Color": "rgba(234, 112, 112, 0.22)"
},
"id": 7,
"isNew": true,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 2,
"links": [],
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"span": 6,
"stack": false,
"steppedLine": false,
"targets": [
{
"aggregator": "sum",
"alias": "$tag_host",
"currentTagKey": "",
"currentTagValue": "",
"downsampleAggregator": "sum",
"downsampleFillPolicy": "none",
"downsampleInterval": "1m",
"metric": "sys.collectd.cache_size",
"refId": "A",
"tags": {
"host": ".*"
}
}
],
"timeFrom": null,
"timeShift": null,
"title": "CollectD Cache Sizes",
"tooltip": {
"msResolution": true,
"shared": true,
"sort": 0,
"value_type": "cumulative"
},
"type": "graph",
"xaxis": {
"show": true
},
"yaxes": [
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
]
},
{
"aliasColors": {},
"bars": false,
"datasource": "${DS_TIMELY}",
"editable": true,
"error": false,
"fill": 0,
"grid": {
"threshold1": null,
"threshold1Color": "rgba(216, 200, 27, 0.27)",
"threshold2": null,
"threshold2Color": "rgba(234, 112, 112, 0.22)"
},
"id": 11,
"isNew": true,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 2,
"links": [],
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"span": 6,
"stack": false,
"steppedLine": false,
"targets": [
{
"aggregator": "sum",
"alias": "$tag_host",
"currentTagKey": "",
"currentTagValue": "",
"downsampleAggregator": "sum",
"downsampleFillPolicy": "none",
"downsampleInterval": "1m",
"metric": "sys.collectd.queue_length",
"refId": "A",
"tags": {
"host": ".*"
}
}
],
"timeFrom": null,
"timeShift": null,
"title": "CollectD Queue Length",
"tooltip": {
"msResolution": true,
"shared": true,
"sort": 0,
"value_type": "cumulative"
},
"type": "graph",
"xaxis": {
"show": true
},
"yaxes": [
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
]
}
],
"title": "New row"
}
],
"time": {
"from": "now-1h",
"to": "now"
},
"timepicker": {
"now": true,
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"templating": {
"list": []
},
"annotations": {
"list": []
},
"refresh": "1m",
"schemaVersion": 12,
"version": 8,
"links": [],
"gnetId": null
}
@@ -7,8 +7,8 @@
}
],
"id": null,
"title": "${DS_NAME} - Status",
"originalTitle": "${DS_NAME} - Status",
"title": "Timely Standalone Demo",
"originalTitle": "Timely Standalone Demo",
"tags": ["imported"],
"style": "dark",
"timezone": "browser",
@@ -8,7 +8,11 @@
"annotations": true,
"includes": [
{"type": "dashboard", "name": "Timely Metrics", "path": "dashboards/standalone_test.json"}
{"type": "dashboard", "name": "Timely Standalone Test", "path": "dashboards/standalone_test.json"},
{"type": "dashboard", "name": "Accumulo Overview", "path": "dashboards/AccumuloOverview.json"},
{"type": "dashboard", "name": "Hadoop Overview", "path": "dashboards/HadoopOverview.json"},
{"type": "dashboard", "name": "System Overview", "path": "dashboards/SystemOverview.json"},
{"type": "dashboard", "name": "Timely Status", "path": "dashboards/TimelyStatus.json"}
],
"info": {
+181 -16
View File
@@ -3,8 +3,25 @@
<modelVersion>4.0.0</modelVersion>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>0.0.2</version>
<version>0.0.3-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Timely</name>
<description>A secure time series database based on Apache Accumulo</description>
<licenses>
<license>
<name>Apache License, Version 2.1</name>
<url>https://www.apache.org/licenses/LICENSE-2.0</url>
</license>
</licenses>
<scm>
<connection>scm:git:https://github.com/NationalSecurityAgency/timely.git</connection>
<developerConnection>scm:git:[email protected]:NationalSecurityAgency/timely.git</developerConnection>
<url>https://github.com/NationalSecurityAgency/timely</url>
</scm>
<issueManagement>
<system>GitHub</system>
<url>https://github.com/NationalSecurityAgency/timely/issues</url>
</issueManagement>
<modules>
<module>server</module>
<module>collectd-timely-plugin</module>
@@ -18,25 +35,54 @@
<os.detection.classifierWithLikes>fedora</os.detection.classifierWithLikes>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- dependency versions -->
<collectd-api-jar>/usr/share/collectd/java/collectd-api.jar</collectd-api-jar>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<version.accumulo>1.7.0</version.accumulo>
<version.caffeine>2.2.0</version.caffeine>
<version.collectd>5.5.1</version.collectd>
<version.commons-configuration>1.10</version.commons-configuration>
<version.commons-lang3>3.1</version.commons-lang3>
<version.disruptor>3.3.2</version.disruptor>
<version.fast-classpath-scanner>1.10.4</version.fast-classpath-scanner>
<version.hadoop>2.6.2</version.hadoop>
<version.httpcomponents>4.5.1</version.httpcomponents>
<version.jackson>2.6.1</version.jackson>
<version.javassist>3.20.0-GA</version.javassist>
<version.junit>4.11</version.junit>
<version.jcip-annotations>1.0</version.jcip-annotations>
<version.junit>4.12</version.junit>
<version.log4j>2.5</version.log4j>
<version.netty>4.0.38.Final</version.netty>
<version.netty-tcnative>1.1.33.Fork18</version.netty-tcnative>
<version.security>4.0.4.RELEASE</version.security>
<version.powermock>1.6.4</version.powermock>
<version.slf4j>1.7.12</version.slf4j>
<!-- plugin versions -->
<version.eclipse-lifecycle>1.0.0</version.eclipse-lifecycle>
<version.findbugs-maven>3.0.3</version.findbugs-maven>
<version.formatter>0.5.2</version.formatter>
<version.jacoco>0.7.6.201602180812</version.jacoco>
<version.maven-assembly>2.6</version.maven-assembly>
<version.maven-compiler>3.3</version.maven-compiler>
<version.maven-dependency>2.10</version.maven-dependency>
<version.maven-jar>3.0.1</version.maven-jar>
<version.os-maven>1.5.0.Final</version.os-maven>
<version.maven-pmd>3.6</version.maven-pmd>
<version.maven-rpm>2.1.4</version.maven-rpm>
<version.maven-shade>2.4.2</version.maven-shade>
<version.maven-surefire>2.19.1</version.maven-surefire>
<version.jawampa>0.4.2</version.jawampa>
<version.spring.boot>1.4.0.RELEASE</version.spring.boot>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>collectd</groupId>
<artifactId>collectd-api</artifactId>
<version>${version.collectd}</version>
<scope>system</scope>
<systemPath>${collectd-api-jar}</systemPath>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
@@ -61,6 +107,17 @@
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>${version.caffeine}</version>
</dependency>
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>${version.disruptor}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>LATEST</version>
</dependency>
<dependency>
<groupId>commons-configuration</groupId>
@@ -73,6 +130,11 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.github.lukehutch</groupId>
<artifactId>fast-classpath-scanner</artifactId>
<version>${version.fast-classpath-scanner}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
@@ -84,6 +146,12 @@
<version>${version.junit}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.jcip</groupId>
<artifactId>jcip-annotations</artifactId>
<version>${version.jcip-annotations}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
@@ -184,50 +252,99 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${version.httpcomponents}</version>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>${version.javassist}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${version.security}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${version.security}</version>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-easymock-release-full</artifactId>
<version>${version.powermock}</version>
<classifier>full</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${version.slf4j}</version>
</dependency>
<dependency>
<groupId>ws.wamp.jawampa</groupId>
<artifactId>jawampa-core</artifactId>
<version>${version.jawampa}</version>
</dependency>
<dependency>
<groupId>ws.wamp.jawampa</groupId>
<artifactId>jawampa-netty</artifactId>
<version>${version.jawampa}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${version.spring.boot}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>net.revelc.code</groupId>
<artifactId>formatter-maven-plugin</artifactId>
<version>${version.formatter}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>${version.maven-assembly}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<version>${version.maven-compiler}</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>${version.maven-dependency}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>${version.maven-surefire}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
<version>${version.maven-jar}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>${version.maven-shade}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${version.maven-surefire}</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
<version>3.0.3</version>
<version>${version.findbugs-maven}</version>
<configuration>
<effort>Max</effort>
<threshold>Medium</threshold>
@@ -242,6 +359,54 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>${version.jacoco}</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>rpm-maven-plugin</artifactId>
<version>${version.maven-rpm}</version>
</plugin>
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>${version.eclipse-lifecycle}</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>net.revelc.code</groupId>
<artifactId>formatter-maven-plugin</artifactId>
<versionRange>[0.5.2,)</versionRange>
<goals>
<goal>format</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore/>
</action>
</pluginExecution>
<pluginExecution>
<pluginExecutionFilter>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<versionRange>[2.10,)</versionRange>
<goals>
<goal>copy-dependencies</goal>
<goal>unpack</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore/>
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
+2 -1
View File
@@ -3,6 +3,7 @@ FROM centos:centos7
MAINTAINER Jim Klucar "[email protected]"
VOLUME /timely-server-src
VOLUME /timely
RUN yum -y update && \
yum -y install java-1.8.0-openjdk-devel maven openssl apr && \
@@ -12,6 +13,6 @@ ENV JAVA_HOME /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.91-1.b14.el7_2.x86_64
EXPOSE 9804 54321 54322
ENV TIMELY_VERSION 0.0.2-SNAPSHOT
ENV TIMELY_VERSION 0.0.3-SNAPSHOT
#CMD /timely/bin/timely-standalone.sh
+6 -6
View File
@@ -6,14 +6,14 @@
/timely-server-src/bin/docker/wait-for-it.sh timely:54322 -t 45
# create datasource in grafana
curl -X POST -u admin:admin -H "Content-Type: application/json" -d \
'{"name":"Timely","type":"opentsdb","url":"https://timely:54322","access":"proxy","jsonData":{"tsdbVersion":1,"tsdbResolution":1}}' \
http://grafana:3000/api/datasources
#curl -X POST -u admin:admin -H "Content-Type: application/json" -d \
#'{"name":"Timely","type":"opentsdb","url":"https://timely:54322","access":"proxy","jsonData":{"tsdbVersion":1,"tsdbResolution":1}}' \
#http://grafana:3000/api/datasources
# insert the dashboard
curl -X POST -u admin:admin -H "Content-Type: application/json" \
-d @/timely-server-src/bin/docker/standalone_test.json \
http://grafana:3000/api/dashboards/db
#curl -X POST -u admin:admin -H "Content-Type: application/json" \
#-d @/timely-server-src/bin/docker/standalone_test.json \
#http://grafana:3000/api/dashboards/db
/timely/bin/insert-test-data.sh
+2 -3
View File
@@ -9,12 +9,11 @@ fi
THIS_DIR="${THIS_SCRIPT%/*}"
BASE_DIR=${THIS_DIR}/..
CONF_DIR="${BASE_DIR}/conf"
CONF_FILE="${CONF_DIR}/timely.properties"
LIB_DIR="${BASE_DIR}/lib"
export CLASSPATH="${CONF_DIR}:${LIB_DIR}/*"
JVM_ARGS="-Xmx256m -Xms128m -Dlog4j.configurationFile=${THIS_DIR}/log4j2.xml"
JVM_ARGS="${JVM_ARGS} -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector"
echo "$JAVA_HOME/bin/java ${JVM_ARGS} timely.util.GetMetricTableSplitPoints "${CONF_FILE}""
$JAVA_HOME/bin/java ${JVM_ARGS} timely.util.GetMetricTableSplitPoints "${CONF_FILE}"
echo "$JAVA_HOME/bin/java ${JVM_ARGS} timely.util.GetMetricTableSplitPoints --spring.config.name=timely"
$JAVA_HOME/bin/java ${JVM_ARGS} timely.util.GetMetricTableSplitPoints --spring.config.name=timely
-2
View File
@@ -8,8 +8,6 @@ fi
THIS_DIR="${THIS_SCRIPT%/*}"
BASE_DIR=${THIS_DIR}/..
CONF_DIR="${BASE_DIR}/conf"
CONF_FILE="${CONF_DIR}/timely.properties"
LIB_DIR="${BASE_DIR}/lib"
NUM_SERVER_THREADS=4
-2
View File
@@ -8,8 +8,6 @@ fi
THIS_DIR="${THIS_SCRIPT%/*}"
BASE_DIR=${THIS_DIR}/..
CONF_DIR="${BASE_DIR}/conf"
CONF_FILE="${CONF_DIR}/timely.properties"
LIB_DIR="${BASE_DIR}/lib"
NUM_SERVER_THREADS=4
+2 -3
View File
@@ -18,7 +18,6 @@ NATIVE_DIR="${THIS_DIR}/META-INF/native"
BASE_DIR=${THIS_DIR}/..
TMP_DIR="${BASE_DIR}/tmp"
CONF_DIR="${BASE_DIR}/conf"
CONF_FILE="${CONF_DIR}/timely.properties"
LIB_DIR="${BASE_DIR}/lib"
NUM_SERVER_THREADS=4
@@ -41,6 +40,6 @@ JVM_ARGS="-Xmx256m -Xms256m -Dio.netty.eventLoopThreads=${NUM_SERVER_THREADS} -D
JVM_ARGS="${JVM_ARGS} -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector"
JVM_ARGS="${JVM_ARGS} -Djava.library.path=${NATIVE_DIR}/libnetty-${TCNATIVE_SUFFIX}"
echo "$JAVA_HOME/bin/java ${JVM_ARGS} timely.Server "${CONF_FILE}""
$JAVA_HOME/bin/java ${JVM_ARGS} timely.Server "${CONF_FILE}"
echo "$JAVA_HOME/bin/java ${JVM_ARGS} timely.Server --spring.config.name=timely"
$JAVA_HOME/bin/java ${JVM_ARGS} timely.Server --spring.config.name=timely
+2 -3
View File
@@ -18,7 +18,6 @@ NATIVE_DIR="${THIS_DIR}/META-INF/native"
BASE_DIR=${THIS_DIR}/..
TMP_DIR="${BASE_DIR}/tmp"
CONF_DIR="${BASE_DIR}/conf"
CONF_FILE="${CONF_DIR}/timely-standalone.properties"
LIB_DIR="${BASE_DIR}/lib"
NUM_SERVER_THREADS=4
@@ -43,6 +42,6 @@ JVM_ARGS="${JVM_ARGS} -Djava.library.path=${NATIVE_DIR}"
# for debugging
#JVM_ARGS="${JVM_ARGS} -agentlib:jdwp=transport=dt_socket,address=54323,server=y,suspend=y"
echo "$JAVA_HOME/bin/java ${JVM_ARGS} timely.StandaloneServer "${CONF_FILE}" "${TMP_DIR}""
$JAVA_HOME/bin/java ${JVM_ARGS} timely.StandaloneServer "${CONF_FILE}" "${TMP_DIR}"
echo "$JAVA_HOME/bin/java ${JVM_ARGS} timely.StandaloneServer "${TMP_DIR}" --spring.config.name=timely --spring.profiles.active=standalone"
$JAVA_HOME/bin/java ${JVM_ARGS} timely.StandaloneServer "${TMP_DIR}" --spring.config.name=timely --spring.profiles.active=standalone
-39
View File
@@ -1,39 +0,0 @@
timely.ip=0.0.0.0
timely.port.put=54321
timely.port.query=54322
timely.port.websocket=54323
timely.instance_name=TimelyStandalone
timely.zookeepers=127.0.0.1:9804
timely.username=root
timely.password=secret
timely.table=timely.metrics
timely.meta=timely.meta
timely.write.latency=5s
timely.write.threads=3
timely.write.buffer.size=52428800
timely.metric.age.off.days=7
timely.cors.allow.any.origin=true
timely.cors.allow.null.origin=false
timely.cors.allowed.origins=
timely.cors.allowed.methods=DELETE,GET,HEAD,OPTIONS,PUT,POST
timely.cors.allowed.headers=content-type
timely.cors.allow.credentials=true
timely.scanner.threads=4
timely.metrics.report.tags.ignored=
timely.meta.cache.expiration.minutes=60
timely.meta.cache.initial.capacity=2000
timely.meta.cache.max.capacity=10000
timely.ssl.certificate.file=/timely-server-src/bin/docker/grafana/pki/timely-cert.pem
timely.ssl.key.file=/timely-server-src/bin/docker/grafana/pki/timely-pkcs8-key.pem
timely.ssl.key.pass=timely
timely.ssl.use.generated.keypair=false
timely.ssl.trust.store.file=/timely-server-src/bin/docker/grafana/pki/timely-ca.pem
timely.ssl.use.openssl=true
#timely.ssl.use.ciphers=
timely.session.max.age=86400
timely.http.host=localhost
timely.allow.anonymous.access=true
timely.web.socket.timeout=60
timely.ws.subscription.lag=120
timely.http.redirect.path=/secure-me
timely.hsts.max.age=608400
+55
View File
@@ -0,0 +1,55 @@
timely:
metrics-table: timely.metrics
meta-table: timely.meta
metric-age-off-days: 7
metrics-report-ignored-tags:
accumulo:
instance-name: TimelyStandalone
zookeepers: 127.0.0.1:9804
username: root
password: secret
write:
latency: 5s
threads: 3
bufferSize: 52428800
scan:
threads: 4
security:
session-max-age: 86400
allow-anonymous-access: true
ssl:
certificate-file:
key-file:
key-password: timely
use-generated-keypair: true
trust-store-file:
use-openssl: false
server:
ip: 0.0.0.0
tcp-port: 54321
http:
ip: 0.0.0.0
port: 54322
host: localhost
redirect-path: /secure-me
strict-transport-max-age: 608400
cors:
allow-any-origin: true
allow-null-origin: false
allow-credentials: true
allowed-origins:
allowed-methods: DELETE,GET,HEAD,OPTIONS,PUT,POST
allowed-header: content-type
websocket:
ip: 0.0.0.0
port: 54323
timeout: 60
subscription-lag: 120
meta-cache:
expiration-minutes: 60
initial-capacity: 2000
max-capacity: 10000
visibility-cache:
expiration-minutes: 60
initial-capacity: 2000
max-capacity: 10000
-39
View File
@@ -1,39 +0,0 @@
timely.ip=
timely.port.put=
timely.port.query=
timely.port.websocket=
timely.instance_name=
timely.zookeepers=
timely.username=
timely.password=
timely.table=timely.metrics
timely.meta=timely.meta
timely.write.latency=5s
timely.write.threads=3
timely.write.buffer.size=52428800
timely.metric.age.off.days=7
timely.cors.allow.any.origin=false
timely.cors.allow.null.origin=false
timely.cors.allowed.origins=
timely.cors.allowed.methods=DELETE,GET,HEAD,OPTIONS,PUT,POST
timely.cors.allowed.headers=content-type
timely.cors.allow.credentials=true
timely.scanner.threads=4
timely.metrics.report.tags.ignored=
timely.meta.cache.expiration.minutes=60
timely.meta.cache.initial.capacity=2000
timely.meta.cache.max.capacity=10000
timely.ssl.certificate.file=
timely.ssl.key.file=
timely.ssl.key.pass=
timely.ssl.use.generated.keypair=false
timely.ssl.trust.store.file=
timely.ssl.use.openssl=true
timely.ssl.use.ciphers=
timely.session.max.age=86400
timely.http.host=
timely.allow.anonymous.access=false
timely.web.socket.timeout=60
timely.ws.subscription.lag=120
timely.http.redirect.path=/secure-me
timely.hsts.max.age=608400
+56
View File
@@ -0,0 +1,56 @@
timely:
metrics-table: timely.metrics
meta-table: timely.meta
metric-age-off-days: 7
metrics-report-ignored-tags:
accumulo:
instance-name:
zookeepers:
username:
password:
write:
latency: 5s
threads: 3
bufferSize: 52428800
scan:
threads: 4
security:
session-max-age: 86400
allow-anonymous-access: false
ssl:
certificate-file:
key-file:
key-password:
use-generated-keypair: false
trust-store-file:
use-openssl: true
#use-ciphers:
server:
ip:
tcp-port:
http:
host:
ip:
port:
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
websocket:
ip:
port:
timeout: 60
subscription-lag: 120
meta-cache:
expiration-minutes: 60
initial-capacity: 2000
max-capacity: 10000
visibility-cache:
expiration-minutes: 60
initial-capacity: 2000
max-capacity: 10000
+2 -1
View File
@@ -11,7 +11,7 @@ services:
- ${PWD}/:/timely-server-src
command: /timely-server-src/bin/docker/deploy-startup-standalone.sh
environment:
- TIMELY_VERSION=0.0.2-SNAPSHOT
- TIMELY_VERSION=0.0.3-SNAPSHOT
ingest:
image: timely
entrypoint: /timely-server-src/bin/docker/deploy-wait-and-insert.sh
@@ -22,6 +22,7 @@ services:
environment:
- TIMELY_HOST=timely
- TIMELY_PORT=54321
- TIMELY_VERSION=0.0.3-SNAPSHOT
grafana:
image: grafana/grafana
ports:
+47 -60
View File
@@ -5,7 +5,7 @@
<parent>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>0.0.2</version>
<version>0.0.3-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
@@ -32,16 +32,18 @@
<groupId>commons-configuration</groupId>
<artifactId>commons-configuration</artifactId>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
</dependency>
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.3.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.github.lukehutch</groupId>
<artifactId>fast-classpath-scanner</artifactId>
<version>1.10.4</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
@@ -54,6 +56,12 @@
<classifier>${os.detected.classifier}</classifier>
<!-- Eclipse doesn't like this, but it works on the cmd line -->
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-tcnative-boringssl-static</artifactId>
<version>${version.netty-tcnative}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
@@ -115,6 +123,29 @@
<artifactId>javassist</artifactId>
<scope>runtime</scope>
</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.boot</groupId>
<artifactId>spring-boot-test-autoconfigure</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
@@ -127,6 +158,14 @@
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>ws.wamp.jawampa</groupId>
<artifactId>jawampa-core</artifactId>
</dependency>
<dependency>
<groupId>ws.wamp.jawampa</groupId>
<artifactId>jawampa-netty</artifactId>
</dependency>
</dependencies>
<build>
<extensions>
@@ -134,14 +173,13 @@
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.5.0.Final</version>
<version>${version.os-maven}</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.6</version>
<executions>
<execution>
<id>assemble</id>
@@ -161,7 +199,6 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.10</version>
<executions>
<execution>
<id>copy-dependencies</id>
@@ -200,9 +237,8 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.19</version>
<configuration>
<argLine>${failsafeArgLine} -Djava.library.path=${project.build.dir}/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.so -Dzookeeper.sasl.client=false</argLine>
<includes>
<include>**/*.java</include>
</includes>
@@ -249,9 +285,8 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19</version>
<configuration>
<argLine>${sureFireArgLine} -Djava.library.path=${project.build.dir}/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.so -Dzookeeper.sasl.client=false</argLine>
<excludedGroups>timely.test.IntegrationTest</excludedGroups>
<redirectTestOutputToFile>true</redirectTestOutputToFile>
</configuration>
@@ -259,7 +294,6 @@
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.7.6.201602180812</version>
<executions>
<execution>
<id>jacoco-prepare-ut</id>
@@ -288,7 +322,6 @@
<plugin>
<groupId>net.revelc.code</groupId>
<artifactId>formatter-maven-plugin</artifactId>
<version>0.5.2</version>
<configuration>
<configFile>${project.parent.basedir}/eclipse/Eclipse-Formatter.xml</configFile>
<lineEnding>LF</lineEnding>
@@ -303,50 +336,6 @@
</executions>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<!--This plugin's configuration is used to store Eclipse m2e settings
only. It has no influence on the Maven build itself. -->
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>net.revelc.code</groupId>
<artifactId>formatter-maven-plugin</artifactId>
<versionRange>[0.5.2,)</versionRange>
<goals>
<goal>format</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore/>
</action>
</pluginExecution>
<pluginExecution>
<pluginExecutionFilter>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<versionRange>[2.10,)</versionRange>
<goals>
<goal>copy-dependencies</goal>
<goal>unpack</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore/>
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
<profiles>
<profile>
@@ -356,7 +345,6 @@
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>rpm-maven-plugin</artifactId>
<version>2.1.4</version>
<executions>
<execution>
<id>generate-rpm</id>
@@ -434,7 +422,7 @@
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
<version>3.0.3</version>
<version>${version.findbugs-maven}</version>
<configuration>
<effort>Max</effort>
<threshold>Medium</threshold>
@@ -443,7 +431,6 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
<version>3.6</version>
<configuration>
<targetJdk>1.8</targetJdk>
</configuration>
@@ -451,7 +438,7 @@
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.7.6.201602180812</version>
<version>${version.jacoco}</version>
<reportSets>
<reportSet>
<id>JaCoCo Unit Test Coverage</id>
+580 -169
View File
@@ -1,196 +1,607 @@
package timely;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import com.google.common.collect.Lists;
import org.apache.accumulo.core.client.BatchWriterConfig;
import org.apache.commons.lang3.StringUtils;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.hibernate.validator.constraints.NotBlank;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.stereotype.Component;
import timely.validator.NotEmptyIfFieldSet;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@Component
@ConfigurationProperties(prefix = "timely")
public class Configuration {
public static final String IP = "timely.ip";
public static final String PUT_PORT = "timely.port.put";
public static final String QUERY_PORT = "timely.port.query";
public static final String WEBSOCKET_PORT = "timely.port.websocket";
public static final String ZOOKEEPERS = "timely.zookeepers";
public static final String INSTANCE_NAME = "timely.instance_name";
public static final String USERNAME = "timely.username";
public static final String PASSWORD = "timely.password";
public static final String METRICS_TABLE = "timely.table";
private static final String METRICS_TABLE_DEFAULT = "timely.metrics";
public static final String META_TABLE = "timely.meta";
private static final String META_TABLE_DEFAULT = "timely.meta";
public static final String METRICS_AGEOFF_DAYS = "timely.metric.age.off.days";
private static final String METRICS_AGEOFF_DAYS_DEFAULT = "7";
private String metricsTable = "timely.metrics";
private String metaTable = "timely.meta";
private int metricAgeOffDays = 7;
private List<String> metricsReportIgnoredTags = new ArrayList<>();
public static final String MAX_LATENCY = "timely.write.latency";
private static final String MAX_LATENCY_DEFAULT = "5s";
public static final String WRITE_THREADS = "timely.write.threads";
public static final String WRITE_BUFFER_SIZE = "timely.write.buffer.size";
public static final String SCANNER_THREADS = "timely.scanner.threads";
public static final String CORS_ALLOW_ANY_ORIGIN = "timely.cors.allow.any.origin";
private static final String CORS_ALLOW_ANY_ORIGIN_DEFAULT = "false";
public static final String CORS_ALLOW_NULL_ORIGIN = "timely.cors.allow.null.origin";
private static final String CORS_ALLOW_NULL_ORIGIN_DEFAULT = "false";
public static final String CORS_ALLOWED_ORIGINS = "timely.cors.allowed.origins";
private static final String CORS_ALLOWED_ORIGINS_DEFAULT = "";
public static final String CORS_ALLOWED_METHODS = "timely.cors.allowed.methods";
private static final String CORS_ALLOWED_METHODS_DEFAULT = "DELETE,GET,HEAD,OPTIONS,PUT,POST";
public static final String CORS_ALLOWED_HEADERS = "timely.cors.allowed.headers";
private static final String CORS_ALLOWED_HEADERS_DEFAULT = "content-type";
public static final String CORS_ALLOW_CREDENTIALS = "timely.cors.allow.credentials";
private static final String CORS_ALLOW_CREDENTIALS_DEFAULT = "true";
public static final String METRICS_IGNORED_TAGS = "timely.metrics.report.tags.ignored";
public static final String META_CACHE_EXPIRATION = "timely.meta.cache.expiration.minutes";
public static final Long META_CACHE_EXPIRATION_DEFAULT = 60L;
public static final String META_CACHE_INITIAL_CAPACITY = "timely.meta.cache.initial.capacity";
public static final Integer META_CACHE_INITIAL_CAPACITY_DEFAULT = 2000;
public static final String META_CACHE_MAX_CAPACITY = "timely.meta.cache.max.capacity";
public static final Integer META_CACHE_MAX_CAPACITY_DEFAULT = 10000;
public static final String VISIBILITY_CACHE_EXPIRATION = "timely.visibility.cache.expiration.minutes";
public static final Long VISIBILITY_EXPIRATION_DEFAULT = 60L;
public static final String VISIBILITY_CACHE_INITIAL_CAPACITY = "timely.visibility.cache.initial.capacity";
public static final Integer VISIBILITY_CACHE_INITIAL_CAPACITY_DEFAULT = 2000;
public static final String VISIBILITY_CACHE_MAX_CAPACITY = "timely.visibility.cache.max.capacity";
public static final Integer VISIBILITY_CACHE_MAX_CAPACITY_DEFAULT = 10000;
/** Security properties */
public static final String SSL_CERTIFICATE_FILE = "timely.ssl.certificate.file";
public static final String SSL_PRIVATE_KEY_FILE = "timely.ssl.key.file";
public static final String SSL_PRIVATE_KEY_PASS = "timely.ssl.key.pass";
public static final String SSL_USE_GENERATED_KEYPAIR = "timely.ssl.use.generated.keypair";
private static final String SSL_USE_GENERATED_KEYPAIR_DEFAULT = "false";
public static final String SSL_TRUST_STORE_FILE = "timely.ssl.trust.store.file";
public static final String SSL_USE_OPENSSL = "timely.ssl.use.openssl";
private static final String SSL_USE_OPENSSL_DEFAULT = "true";
public static final String SSL_USE_CIPHERS = "timely.ssl.use.ciphers";
private static final String SSL_USE_CIPHERS_DEFAULT = "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:"
+ "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:TLS_RSA_WITH_AES_128_GCM_SHA256:"
+ "TLS_RSA_WITH_AES_128_CBC_SHA:SSL_RSA_WITH_3DES_EDE_CBC_SHA";
public static final String SESSION_MAX_AGE = "timely.session.max.age";
public static final long SESSION_MAX_AGE_DEFAULT = 86400;
public static final String TIMELY_HTTP_HOST = "timely.http.host";
public static final String ALLOW_ANONYMOUS_ACCESS = "timely.allow.anonymous.access";
private static final String ALLOW_ANONYMOUS_ACCESS_DEFAULT = "false";
public static final String WS_TIMEOUT_SECONDS = "timely.web.socket.timeout";
private static final String WS_TIMEOUT_SECONDS_DEFAULT = "60";
public static final String WS_SUBSCRIPTION_LAG = "timely.ws.subscription.lag";
private static final String WS_SUBSCRIPTION_LAG_DEFAULT = "120";
public static final String NON_SECURE_REDIRECT_PATH = "timely.http.redirect.path";
private static final String NON_SECURE_REDIRECT_PATH_DEFAULT = "/secure-me";
public static final String STRICT_TRANSPORT_MAX_AGE = "timely.hsts.max.age";
private static final String STRICT_TRANSPORT_MAX_AGE_DEFAULT = "604800";
private static final List<String> REQUIRED_PROPERTIES = new ArrayList<>();
static {
REQUIRED_PROPERTIES.add(IP);
REQUIRED_PROPERTIES.add(PUT_PORT);
REQUIRED_PROPERTIES.add(QUERY_PORT);
REQUIRED_PROPERTIES.add(WEBSOCKET_PORT);
REQUIRED_PROPERTIES.add(ZOOKEEPERS);
REQUIRED_PROPERTIES.add(INSTANCE_NAME);
REQUIRED_PROPERTIES.add(USERNAME);
REQUIRED_PROPERTIES.add(PASSWORD);
REQUIRED_PROPERTIES.add(TIMELY_HTTP_HOST);
};
private final Properties props = new Properties();
public Configuration(Path path) throws IOException {
org.apache.hadoop.conf.Configuration hadoopConf = new org.apache.hadoop.conf.Configuration();
try (FileSystem fs = FileSystem.get(hadoopConf)) {
try (InputStream inputStream = fs.open(path)) {
init(inputStream);
}
}
@Valid
@NestedConfigurationProperty
private Accumulo accumulo = new Accumulo();
@Valid
@NestedConfigurationProperty
private Security security = new Security();
@Valid
@NestedConfigurationProperty
private Server server = new Server();
@Valid
@NestedConfigurationProperty
private Http http = new Http();
@Valid
@NestedConfigurationProperty
private MetaCache metaCache = new MetaCache();
@Valid
@NestedConfigurationProperty
private VisibilityCache visibilityCache = new VisibilityCache();
@Valid
@NestedConfigurationProperty
private Websocket websocket = new Websocket();
public String getMetricsTable() {
return metricsTable;
}
public Configuration(File conf) throws IOException {
try (InputStream inputStream = new FileInputStream(conf)) {
init(inputStream);
public Configuration setMetricsTable(String metricsTable) {
this.metricsTable = metricsTable;
return this;
}
public String getMetaTable() {
return metaTable;
}
public Configuration setMetaTable(String metaTable) {
this.metaTable = metaTable;
return this;
}
public int getMetricAgeOffDays() {
return metricAgeOffDays;
}
public Configuration setMetricAgeOffDays(int metricAgeOffDays) {
this.metricAgeOffDays = metricAgeOffDays;
return this;
}
public List<String> getMetricsReportIgnoredTags() {
return metricsReportIgnoredTags;
}
public Configuration setMetricsReportIgnoredTags(List<String> metricsReportIgnoredTags) {
this.metricsReportIgnoredTags = metricsReportIgnoredTags;
return this;
}
public Accumulo getAccumulo() {
return accumulo;
}
public Security getSecurity() {
return security;
}
public Server getServer() {
return server;
}
public Http getHttp() {
return http;
}
public Websocket getWebsocket() {
return websocket;
}
public MetaCache getMetaCache() {
return metaCache;
}
public VisibilityCache getVisibilityCache() {
return visibilityCache;
}
public class Accumulo {
@NotBlank
private String zookeepers;
@NotBlank
private String instanceName;
@NotBlank
private String username;
@NotBlank
private String password;
@Valid
@NestedConfigurationProperty
private Write write = new Write();
@Valid
@NestedConfigurationProperty
private Scan scan = new Scan();
public String getInstanceName() {
return instanceName;
}
public String getZookeepers() {
return zookeepers;
}
public Configuration setZookeepers(String zookeepers) {
this.zookeepers = zookeepers;
return Configuration.this;
}
public Write getWrite() {
return write;
}
public Scan getScan() {
return scan;
}
public Configuration setInstanceName(String instanceName) {
this.instanceName = instanceName;
return Configuration.this;
}
public String getUsername() {
return username;
}
public Configuration setUsername(String username) {
this.username = username;
return Configuration.this;
}
public String getPassword() {
return password;
}
public Configuration setPassword(String password) {
this.password = password;
return Configuration.this;
}
}
private void init(InputStream configStream) throws IOException {
// Add defaults
props.setProperty(METRICS_TABLE, METRICS_TABLE_DEFAULT);
props.setProperty(META_TABLE, META_TABLE_DEFAULT);
props.setProperty(MAX_LATENCY, MAX_LATENCY_DEFAULT);
props.setProperty(METRICS_AGEOFF_DAYS, METRICS_AGEOFF_DAYS_DEFAULT);
final BatchWriterConfig defaults = new BatchWriterConfig();
props.setProperty(WRITE_BUFFER_SIZE, "" + defaults.getMaxMemory());
props.setProperty(WRITE_THREADS, "" + defaults.getMaxWriteThreads());
props.setProperty(SCANNER_THREADS, "" + 4);
props.setProperty(CORS_ALLOW_ANY_ORIGIN, CORS_ALLOW_ANY_ORIGIN_DEFAULT);
props.setProperty(CORS_ALLOW_NULL_ORIGIN, CORS_ALLOW_NULL_ORIGIN_DEFAULT);
props.setProperty(CORS_ALLOWED_ORIGINS, CORS_ALLOWED_ORIGINS_DEFAULT);
props.setProperty(CORS_ALLOWED_METHODS, CORS_ALLOWED_METHODS_DEFAULT);
props.setProperty(CORS_ALLOWED_HEADERS, CORS_ALLOWED_HEADERS_DEFAULT);
props.setProperty(CORS_ALLOW_CREDENTIALS, CORS_ALLOW_CREDENTIALS_DEFAULT);
props.setProperty(META_CACHE_EXPIRATION, "" + META_CACHE_EXPIRATION_DEFAULT);
props.setProperty(META_CACHE_INITIAL_CAPACITY, "" + META_CACHE_INITIAL_CAPACITY_DEFAULT);
props.setProperty(META_CACHE_MAX_CAPACITY, "" + META_CACHE_MAX_CAPACITY_DEFAULT);
props.setProperty(SSL_USE_GENERATED_KEYPAIR, SSL_USE_GENERATED_KEYPAIR_DEFAULT);
props.setProperty(SSL_USE_OPENSSL, SSL_USE_OPENSSL_DEFAULT);
props.setProperty(SSL_USE_CIPHERS, SSL_USE_CIPHERS_DEFAULT);
props.setProperty(ALLOW_ANONYMOUS_ACCESS, ALLOW_ANONYMOUS_ACCESS_DEFAULT);
props.setProperty(SESSION_MAX_AGE, SESSION_MAX_AGE_DEFAULT + "");
public class Write {
props.setProperty(VISIBILITY_CACHE_EXPIRATION, VISIBILITY_EXPIRATION_DEFAULT + "");
props.setProperty(VISIBILITY_CACHE_INITIAL_CAPACITY, VISIBILITY_CACHE_INITIAL_CAPACITY_DEFAULT + "");
props.setProperty(VISIBILITY_CACHE_MAX_CAPACITY, VISIBILITY_CACHE_MAX_CAPACITY_DEFAULT + "");
private String latency = "5s";
private int threads;
private String bufferSize;
props.setProperty(WS_TIMEOUT_SECONDS, WS_TIMEOUT_SECONDS_DEFAULT);
props.setProperty(WS_SUBSCRIPTION_LAG, WS_SUBSCRIPTION_LAG_DEFAULT);
public Write() {
BatchWriterConfig config = new BatchWriterConfig();
threads = config.getMaxWriteThreads();
bufferSize = Long.toString(config.getMaxMemory());
}
props.setProperty(NON_SECURE_REDIRECT_PATH, NON_SECURE_REDIRECT_PATH_DEFAULT);
props.setProperty(STRICT_TRANSPORT_MAX_AGE, STRICT_TRANSPORT_MAX_AGE_DEFAULT);
public String getLatency() {
return latency;
}
try (BufferedReader reader = new BufferedReader(new InputStreamReader(configStream, StandardCharsets.UTF_8))) {
props.load(reader);
validate();
public Configuration setLatency(String latency) {
this.latency = latency;
return Configuration.this;
}
public int getThreads() {
return threads;
}
public Configuration setThreads(int threads) {
this.threads = threads;
return Configuration.this;
}
public String getBufferSize() {
return bufferSize;
}
public Configuration setBufferSize(String bufferSize) {
this.bufferSize = bufferSize;
return Configuration.this;
}
}
protected void validate() {
REQUIRED_PROPERTIES.forEach(p -> {
if (!props.containsKey(p) || StringUtils.isEmpty(props.getProperty(p))) {
throw new IllegalArgumentException("Required property " + p + " must be specified.");
}
});
if (!getBoolean(Configuration.SSL_USE_GENERATED_KEYPAIR)) {
if (StringUtils.isEmpty(get(Configuration.SSL_CERTIFICATE_FILE))) {
throw new IllegalArgumentException(Configuration.SSL_CERTIFICATE_FILE + "must be specified.");
}
if (StringUtils.isEmpty(get(Configuration.SSL_PRIVATE_KEY_FILE))) {
throw new IllegalArgumentException(Configuration.SSL_PRIVATE_KEY_FILE + "must be specified.");
}
public class Scan {
private int threads = 4;
public int getThreads() {
return threads;
}
public Configuration setThreads(int threads) {
this.threads = threads;
return Configuration.this;
}
}
public String get(String property) {
return props.getProperty(property);
public class Security {
private boolean allowAnonymousAccess = false;
private int sessionMaxAge = 86400;
@Valid
@NestedConfigurationProperty
private Ssl ssl = new Ssl();
public boolean isAllowAnonymousAccess() {
return allowAnonymousAccess;
}
public Configuration setAllowAnonymousAccess(boolean allowAnonymousAccess) {
this.allowAnonymousAccess = allowAnonymousAccess;
return Configuration.this;
}
public int getSessionMaxAge() {
return sessionMaxAge;
}
public Configuration setSessionMaxAge(int sessionMaxAge) {
this.sessionMaxAge = sessionMaxAge;
return Configuration.this;
}
public Ssl getSsl() {
return ssl;
}
}
public boolean getBoolean(String property) {
return Boolean.parseBoolean(get(property));
@NotEmptyIfFieldSet.List({
@NotEmptyIfFieldSet(fieldName = "useGeneratedKeypair", fieldValue = "false", notNullFieldName = "certificateFile", message = "must be set if timely.security.ssl.use-generated-keypair is false"),
@NotEmptyIfFieldSet(fieldName = "useGeneratedKeypair", fieldValue = "false", notNullFieldName = "keyFile", message = "must be set if timely.security.ssl.use-generated-keypair is false") })
public class Ssl {
private String certificateFile;
private String keyFile;
private String keyPassword;
private String trustStoreFile;
private boolean useGeneratedKeypair = false;
private boolean useOpenssl = true;
private List<String> useCiphers = Lists.newArrayList("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "TLS_RSA_WITH_AES_128_GCM_SHA256",
"TLS_RSA_WITH_AES_128_CBC_SHA", "SSL_RSA_WITH_3DES_EDE_CBC_SHA");
public String getCertificateFile() {
return certificateFile;
}
public Configuration setCertificateFile(String certificateFile) {
this.certificateFile = certificateFile;
return Configuration.this;
}
public String getKeyFile() {
return keyFile;
}
public Configuration setKeyFile(String keyFile) {
this.keyFile = keyFile;
return Configuration.this;
}
public String getKeyPassword() {
return keyPassword;
}
public Configuration setKeyPassword(String keyPassword) {
this.keyPassword = keyPassword;
return Configuration.this;
}
public String getTrustStoreFile() {
return trustStoreFile;
}
public Configuration setTrustStoreFile(String trustStoreFile) {
this.trustStoreFile = trustStoreFile;
return Configuration.this;
}
public boolean isUseGeneratedKeypair() {
return useGeneratedKeypair;
}
public Configuration setUseGeneratedKeypair(boolean useGeneratedKeypair) {
this.useGeneratedKeypair = useGeneratedKeypair;
return Configuration.this;
}
public boolean isUseOpenssl() {
return useOpenssl;
}
public Configuration setUseOpenssl(boolean useOpenssl) {
this.useOpenssl = useOpenssl;
return Configuration.this;
}
public List<String> getUseCiphers() {
return useCiphers;
}
public Configuration setUseCiphers(List<String> useCiphers) {
this.useCiphers = useCiphers;
return Configuration.this;
}
}
public class Server {
@NotBlank
private String ip;
@NotNull
private Integer tcpPort;
@NotNull
public String getIp() {
return ip;
}
public Configuration setIp(String ip) {
this.ip = ip;
return Configuration.this;
}
public int getTcpPort() {
return tcpPort;
}
public Configuration setTcpPort(int tcpPort) {
this.tcpPort = tcpPort;
return Configuration.this;
}
}
public class Http {
@NotBlank
private String ip;
@NotNull
private Integer port;
@NotNull
private String host;
private String redirectPath = "/secure-me";
private long strictTransportMaxAge = 604800;
@Valid
@NestedConfigurationProperty
private Cors cors = new Cors();
public String getIp() {
return ip;
}
public Configuration setIp(String ip) {
this.ip = ip;
return Configuration.this;
}
public int getPort() {
return port;
}
public Configuration setPort(int port) {
this.port = port;
return Configuration.this;
}
public String getHost() {
return host;
}
public Configuration setHost(String host) {
this.host = host;
return Configuration.this;
}
public String getRedirectPath() {
return redirectPath;
}
public Configuration setRedirectPath(String redirectPath) {
this.redirectPath = redirectPath;
return Configuration.this;
}
public long getStrictTransportMaxAge() {
return strictTransportMaxAge;
}
public Configuration setStrictTransportMaxAge(long strictTransportMaxAge) {
this.strictTransportMaxAge = strictTransportMaxAge;
return Configuration.this;
}
public Cors getCors() {
return cors;
}
}
public class Cors {
private boolean allowAnyOrigin = false;
private boolean allowNullOrigin = false;
private Set<String> allowedOrigins = new HashSet<>();
private List<String> allowedMethods = Lists.newArrayList("DELETE", "GET", "HEAD", "OPTIONS", "PUT", "POST");
private List<String> allowedHeaders = Lists.newArrayList("content-type");
private boolean allowCredentials = true;
public boolean isAllowAnyOrigin() {
return allowAnyOrigin;
}
public Configuration setAllowAnyOrigin(boolean allowAnyOrigin) {
this.allowAnyOrigin = allowAnyOrigin;
return Configuration.this;
}
public boolean isAllowNullOrigin() {
return allowNullOrigin;
}
public Configuration setAllowNullOrigin(boolean allowNullOrigin) {
this.allowNullOrigin = allowNullOrigin;
return Configuration.this;
}
public Set<String> getAllowedOrigins() {
return allowedOrigins;
}
public Configuration setAllowedOrigins(Set<String> allowedOrigins) {
this.allowedOrigins = allowedOrigins;
return Configuration.this;
}
public List<String> getAllowedMethods() {
return allowedMethods;
}
public Configuration setAllowedMethods(List<String> allowedMethods) {
this.allowedMethods = allowedMethods;
return Configuration.this;
}
public List<String> getAllowedHeaders() {
return allowedHeaders;
}
public Configuration setAllowedHeaders(List<String> allowedHeaders) {
this.allowedHeaders = allowedHeaders;
return Configuration.this;
}
public boolean isAllowCredentials() {
return allowCredentials;
}
public Configuration setAllowCredentials(boolean allowCredentials) {
this.allowCredentials = allowCredentials;
return Configuration.this;
}
}
public class Websocket {
@NotBlank
private String ip;
@NotNull
private Integer port;
public int timeout = 60;
public int subscriptionLag = 120;
public String getIp() {
return ip;
}
public Configuration setIp(String ip) {
this.ip = ip;
return Configuration.this;
}
public int getPort() {
return port;
}
public Configuration setPort(int port) {
this.port = port;
return Configuration.this;
}
public int getTimeout() {
return timeout;
}
public Configuration setTimeout(int timeout) {
this.timeout = timeout;
return Configuration.this;
}
public int getSubscriptionLag() {
return subscriptionLag;
}
public Configuration setSubscriptionLag(int subscriptionLag) {
this.subscriptionLag = subscriptionLag;
return Configuration.this;
}
}
public class MetaCache {
private long expirationMinutes = 60;
private int initialCapacity = 2000;
private long maxCapacity = 10000;
public long getExpirationMinutes() {
return expirationMinutes;
}
public Configuration setExpirationMinutes(long expirationMinutes) {
this.expirationMinutes = expirationMinutes;
return Configuration.this;
}
public int getInitialCapacity() {
return initialCapacity;
}
public Configuration setInitialCapacity(int initialCapacity) {
this.initialCapacity = initialCapacity;
return Configuration.this;
}
public long getMaxCapacity() {
return maxCapacity;
}
public Configuration setMaxCapacity(long maxCapacity) {
this.maxCapacity = maxCapacity;
return Configuration.this;
}
}
public class VisibilityCache {
private long expirationMinutes = 60;
private int initialCapacity = 2000;
private long maxCapacity = 10000;
public long getExpirationMinutes() {
return expirationMinutes;
}
public Configuration setExpirationMinutes(long expirationMinutes) {
this.expirationMinutes = expirationMinutes;
return Configuration.this;
}
public int getInitialCapacity() {
return initialCapacity;
}
public Configuration setInitialCapacity(int initialCapacity) {
this.initialCapacity = initialCapacity;
return Configuration.this;
}
public long getMaxCapacity() {
return maxCapacity;
}
public Configuration setMaxCapacity(long maxCapacity) {
this.maxCapacity = maxCapacity;
return Configuration.this;
}
}
}
+101 -62
View File
@@ -40,14 +40,16 @@ import io.netty.util.internal.SystemPropertyUtil;
import java.io.File;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Date;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import rx.Observable;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import timely.api.response.TimelyException;
import timely.auth.AuthCache;
import timely.auth.VisibilityCache;
@@ -69,6 +71,7 @@ import timely.netty.tcp.TcpPutHandler;
import timely.netty.tcp.TcpVersionHandler;
import timely.netty.websocket.WSMetricPutHandler;
import timely.netty.websocket.WSVersionRequestHandler;
import timely.netty.websocket.WebSocketHttpCookieHandler;
import timely.netty.websocket.WebSocketRequestDecoder;
import timely.netty.websocket.subscription.WSAddSubscriptionRequestHandler;
import timely.netty.websocket.subscription.WSCloseSubscriptionRequestHandler;
@@ -82,6 +85,13 @@ import timely.netty.websocket.timeseries.WSSuggestRequestHandler;
import timely.store.DataStore;
import timely.store.DataStoreFactory;
import timely.store.MetaCacheFactory;
import timely.wamp.StateObserver;
import timely.wamp.VersionObserver;
import ws.wamp.jawampa.*;
import ws.wamp.jawampa.connection.IWampConnectorProvider;
import ws.wamp.jawampa.transport.netty.NettyWampClientConnectorProvider;
import ws.wamp.jawampa.transport.netty.NettyWampConnectionConfig;
import ws.wamp.jawampa.transport.netty.WampServerWebsocketHandler;
public class Server {
@@ -92,10 +102,12 @@ public class Server {
private static final String OS_NAME = "os.name";
private static final String OS_VERSION = "os.version";
private static final String WS_PATH = "/websocket";
private static final String WAMP_PATH = "/wamp";
protected static final CountDownLatch LATCH = new CountDownLatch(1);
static ConfigurableApplicationContext applicationContext;
private Configuration config = null;
private final Configuration config;
private EventLoopGroup tcpWorkerGroup = null;
private EventLoopGroup tcpBossGroup = null;
private EventLoopGroup httpWorkerGroup = null;
@@ -107,6 +119,8 @@ public class Server {
protected Channel wsChannelHandle = null;
protected DataStore dataStore = null;
protected volatile boolean shutdown = false;
private WampRouter wampRouter = null;
private WampClient wampClient;
private static boolean useEpoll() {
@@ -125,11 +139,7 @@ public class Server {
return true;
} else if (minor == EPOLL_MIN_MINOR_VERSION) {
final int patch = Integer.parseInt(version[2].substring(0, 2));
if (patch >= EPOLL_MIN_PATCH_VERSION) {
return true;
} else {
return false;
}
return patch >= EPOLL_MIN_PATCH_VERSION;
} else {
return false;
}
@@ -141,11 +151,6 @@ public class Server {
}
}
private static String usage() {
return "Server <configFile>";
}
public static void fatal(String msg, Throwable t) {
LOG.error(msg, t);
LATCH.countDown();
@@ -153,13 +158,8 @@ public class Server {
public static void main(String[] args) throws Exception {
if (args.length != 1) {
System.err.println(usage());
}
final File conf = new File(args[0]);
if (!conf.canRead()) {
throw new RuntimeException("Configuration file does not exist or cannot be read");
}
Configuration conf = initializeConfiguration(args);
Server s = new Server(conf);
try {
LATCH.await();
@@ -170,15 +170,16 @@ public class Server {
}
}
protected static Configuration initializeConfiguration(String[] args) {
applicationContext = new SpringApplicationBuilder(SpringBootstrap.class).web(false).run(args);
return applicationContext.getBean(Configuration.class);
}
private void shutdownHook() {
final Runnable shutdownRunner = new Runnable() {
@Override
public void run() {
if (!shutdown) {
shutdown();
}
final Runnable shutdownRunner = () -> {
if (!shutdown) {
shutdown();
}
};
final Thread hook = new Thread(shutdownRunner, "shutdown-hook-thread");
@@ -236,15 +237,23 @@ public class Server {
} catch (TimelyException e) {
LOG.error("Error flushing to server during shutdown", e);
}
LOG.info("Shutting down WAMP Router");
wampClient.close().toBlocking().last();
wampRouter.close().toBlocking().last(); // Blocks until router is fully
// shut down
MetaCacheFactory.close();
WebSocketRequestDecoder.close();
if (applicationContext != null) {
applicationContext.close();
}
this.shutdown = true;
LOG.info("Server shut down.");
}
public Server(File conf) throws Exception {
public Server(Configuration conf) throws Exception {
this.config = conf;
config = new Configuration(conf);
int nettyThreads = Math.max(1,
SystemPropertyUtil.getInt("io.netty.eventLoopThreads", Runtime.getRuntime().availableProcessors() * 2));
dataStore = DataStoreFactory.create(config, nettyThreads);
@@ -255,7 +264,7 @@ public class Server {
// Initialize the VisibilityCache
VisibilityCache.init(config);
final boolean useEpoll = useEpoll();
Class<? extends ServerSocketChannel> channelClass = null;
Class<? extends ServerSocketChannel> channelClass;
if (useEpoll) {
tcpWorkerGroup = new EpollEventLoopGroup();
tcpBossGroup = new EpollEventLoopGroup();
@@ -283,11 +292,11 @@ public class Server {
putServer.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
putServer.option(ChannelOption.SO_BACKLOG, 128);
putServer.option(ChannelOption.SO_KEEPALIVE, true);
final int putPort = Integer.parseInt(config.get(Configuration.PUT_PORT));
final int putPort = config.getServer().getTcpPort();
putChannelHandle = putServer.bind(putPort).sync().channel();
final String putAddress = ((InetSocketAddress) putChannelHandle.localAddress()).getAddress().getHostAddress();
final int queryPort = Integer.parseInt(config.get(Configuration.QUERY_PORT));
final int queryPort = config.getHttp().getPort();
SslContext sslCtx = createSSLContext(config);
if (sslCtx instanceof OpenSslServerContext) {
OpenSslServerContext openssl = (OpenSslServerContext) sslCtx;
@@ -296,7 +305,7 @@ public class Server {
opensslCtx.setSessionCacheEnabled(true);
opensslCtx.setSessionCacheSize(128);
opensslCtx.setSessionIdContext(application.getBytes(StandardCharsets.UTF_8));
opensslCtx.setSessionTimeout(Integer.parseInt(config.get(Configuration.SESSION_MAX_AGE)));
opensslCtx.setSessionTimeout(config.getSecurity().getSessionMaxAge());
}
final ServerBootstrap queryServer = new ServerBootstrap();
queryServer.group(httpBossGroup, httpWorkerGroup);
@@ -310,8 +319,9 @@ public class Server {
final String queryAddress = ((InetSocketAddress) queryChannelHandle.localAddress()).getAddress()
.getHostAddress();
final int wsPort = Integer.parseInt(config.get(Configuration.WEBSOCKET_PORT));
final int wsPort = config.getWebsocket().getPort();
final ServerBootstrap wsServer = new ServerBootstrap();
setupWampRouter();
wsServer.group(wsBossGroup, wsWorkerGroup);
wsServer.channel(channelClass);
wsServer.handler(new LoggingHandler());
@@ -323,6 +333,9 @@ public class Server {
final String wsAddress = ((InetSocketAddress) wsChannelHandle.localAddress()).getAddress().getHostAddress();
shutdownHook();
setupWampClient(wsAddress, wsPort);
LOG.info(
"Server started. Listening on {}:{} for TCP traffic, {}:{} for HTTP traffic, and {}:{} for WebSocket traffic",
putAddress, putPort, queryAddress, queryPort, wsAddress, wsPort);
@@ -330,8 +343,9 @@ public class Server {
protected SslContext createSSLContext(Configuration config) throws Exception {
Boolean generate = config.getBoolean(Configuration.SSL_USE_GENERATED_KEYPAIR);
SslContextBuilder ssl = null;
Configuration.Ssl sslCfg = config.getSecurity().getSsl();
Boolean generate = sslCfg.isUseGeneratedKeypair();
SslContextBuilder ssl;
if (generate) {
LOG.warn("Using generated self signed server certificate");
Date begin = new Date();
@@ -339,30 +353,31 @@ public class Server {
SelfSignedCertificate ssc = new SelfSignedCertificate("localhost", begin, end);
ssl = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey());
} else {
String cert = config.get(Configuration.SSL_CERTIFICATE_FILE);
String key = config.get(Configuration.SSL_PRIVATE_KEY_FILE);
String keyPass = config.get(Configuration.SSL_PRIVATE_KEY_PASS);
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);
}
String ciphers = config.get(Configuration.SSL_USE_CIPHERS);
ssl.ciphers(Arrays.asList(ciphers.split(":")));
ssl.ciphers(sslCfg.getUseCiphers());
// Can't set to REQUIRE because the CORS pre-flight requests will fail.
ssl.clientAuth(ClientAuth.OPTIONAL);
Boolean useOpenSSL = config.getBoolean(Configuration.SSL_USE_OPENSSL);
Boolean useOpenSSL = sslCfg.isUseOpenssl();
if (useOpenSSL) {
ssl.sslProvider(SslProvider.OPENSSL);
} else {
ssl.sslProvider(SslProvider.JDK);
}
String trustStore = config.get(Configuration.SSL_TRUST_STORE_FILE);
String trustStore = sslCfg.getTrustStoreFile();
if (null != trustStore) {
ssl.trustManager(new File(trustStore));
if (!trustStore.isEmpty()) {
ssl.trustManager(new File(trustStore));
}
}
return ssl.build();
}
@@ -382,30 +397,21 @@ public class Server {
ch.pipeline().addLast("decompressor", new HttpContentDecompressor());
ch.pipeline().addLast("aggregator", new HttpObjectAggregator(8192));
ch.pipeline().addLast("chunker", new ChunkedWriteHandler());
final Configuration.Cors corsCfg = config.getHttp().getCors();
final CorsConfig.Builder ccb;
if (config.getBoolean(Configuration.CORS_ALLOW_ANY_ORIGIN)) {
if (corsCfg.isAllowAnyOrigin()) {
ccb = new CorsConfig.Builder();
} else {
String origins = config.get(Configuration.CORS_ALLOWED_ORIGINS);
ccb = new CorsConfig.Builder(origins.split(","));
ccb = new CorsConfig.Builder(corsCfg.getAllowedOrigins().stream().toArray(String[]::new));
}
if (config.getBoolean(Configuration.CORS_ALLOW_NULL_ORIGIN)) {
if (corsCfg.isAllowNullOrigin()) {
ccb.allowNullOrigin();
}
if (config.getBoolean(Configuration.CORS_ALLOW_CREDENTIALS)) {
if (corsCfg.isAllowCredentials()) {
ccb.allowCredentials();
}
if (!StringUtils.isEmpty(config.get(Configuration.CORS_ALLOWED_METHODS))) {
String[] methods = config.get(Configuration.CORS_ALLOWED_METHODS).split(",");
HttpMethod[] m = new HttpMethod[methods.length];
for (int i = 0; i < methods.length; i++) {
m[i] = HttpMethod.valueOf(methods[i]);
}
ccb.allowedRequestMethods(m);
}
if (!StringUtils.isEmpty(config.get(Configuration.CORS_ALLOWED_HEADERS))) {
ccb.allowedRequestHeaders(config.get(Configuration.CORS_ALLOWED_HEADERS).split(","));
}
corsCfg.getAllowedMethods().stream().map(HttpMethod::valueOf).forEach(ccb::allowedRequestMethods);
corsCfg.getAllowedHeaders().forEach(ccb::allowedRequestHeaders);
CorsConfig cors = ccb.build();
LOG.trace("Cors configuration: {}", cors);
ch.pipeline().addLast("cors", new CorsHandler(cors));
@@ -445,10 +451,11 @@ public class Server {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast("ssl", sslCtx.newHandler(ch.alloc()));
ch.pipeline().addLast("wamp", new WampServerWebsocketHandler(WAMP_PATH, wampRouter));
ch.pipeline().addLast("httpServer", new HttpServerCodec());
ch.pipeline().addLast("aggregator", new HttpObjectAggregator(8192));
ch.pipeline().addLast("idle-handler",
new IdleStateHandler(Integer.parseInt(conf.get(Configuration.WS_TIMEOUT_SECONDS)), 0, 0));
ch.pipeline().addLast("sessionExtractor", new WebSocketHttpCookieHandler(config));
ch.pipeline().addLast("idle-handler", new IdleStateHandler(conf.getWebsocket().getTimeout(), 0, 0));
ch.pipeline().addLast("ws-protocol", new WebSocketServerProtocolHandler(WS_PATH, null, true));
ch.pipeline().addLast("wsDecoder", new WebSocketRequestDecoder(config));
ch.pipeline().addLast("aggregators", new WSAggregatorsRequestHandler());
@@ -468,4 +475,36 @@ public class Server {
}
protected void setupWampRouter() throws ApplicationError {
wampRouter = new WampRouterBuilder().addRealm("default").build();
}
protected void setupWampClient(String wsAddress, int wsPort) throws Exception {
IWampConnectorProvider connectorProvider = new NettyWampClientConnectorProvider();
NettyWampConnectionConfig connectionConfiguration = new NettyWampConnectionConfig.Builder()
.withSslContext(SslContextBuilder.forClient().build())
.build();
// Create a builder and configure the client
WampClientBuilder builder = new WampClientBuilder();
String wampUri = String.format("wss://0.0.0.0:%d%s", wsPort, WAMP_PATH);
System.out.println(wampUri);
builder.withConnectorProvider(connectorProvider)
.withConnectionConfiguration(connectionConfiguration)
.withUri(wampUri)
.withRealm("timely")
.withInfiniteReconnects()
.withReconnectInterval(5, TimeUnit.SECONDS);
// Create a client through the builder. This will not immediately start
// a connection attempt
wampClient = builder.build();
Observable<WampClient.State> stateObservable = wampClient.statusChanged();
stateObservable.subscribe(new StateObserver());
wampClient.open();
Observable<Request> request = wampClient.registerProcedure("timely.version");
request.subscribe(new VersionObserver());
}
}
@@ -0,0 +1,7 @@
package timely;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication(scanBasePackageClasses = { Configuration.class })
public class SpringBootstrap {
}
@@ -21,7 +21,7 @@ public class StandaloneServer extends Server {
private static MiniAccumuloCluster mac = null;
public StandaloneServer(File conf) throws Exception {
public StandaloneServer(Configuration conf) throws Exception {
super(conf);
}
@@ -38,22 +38,21 @@ public class StandaloneServer extends Server {
}
private static String usage() {
return "StandaloneServer <configFile> <directory>";
return "StandaloneServer <directory>";
}
public static void main(String[] args) {
if (args.length != 2) {
if (args.length < 1) {
System.err.println(usage());
}
final File conf = new File(args[0]);
if (!conf.canRead()) {
throw new RuntimeException("Configuration file does not exist or cannot be read");
}
File tmp = new File(args[1]);
File tmp = new File(args[0]);
if (!tmp.canWrite()) {
System.err.println("Unable to write to directory: " + tmp);
System.exit(1);
}
Configuration conf = initializeConfiguration(args);
File accumuloDir = new File(tmp, "accumulo");
MiniAccumuloConfig macConfig = new MiniAccumuloConfig(accumuloDir, "secret");
macConfig.setInstanceName("TimelyStandalone");
@@ -95,5 +94,4 @@ public class StandaloneServer extends Server {
LOG.info("Server shutting down.");
}
}
}
@@ -219,12 +219,13 @@ public class Metric implements TcpRequest, HttpPostRequest, WebSocketRequest {
return JsonUtil.getObjectMapper().readValue(content.getBytes(StandardCharsets.UTF_8), Metric.class);
}
public MetricResponse toMetricResponse() {
public MetricResponse toMetricResponse(String subscriptionId) {
MetricResponse response = new MetricResponse();
response.setMetric(this.metric);
response.setTimestamp(this.timestamp);
response.setTags(this.tags);
response.setValue(this.value);
response.setSubscriptionId(subscriptionId);
return response;
}
@@ -4,10 +4,9 @@ import java.util.Map;
import java.util.Optional;
import timely.api.annotation.WebSocket;
import timely.api.request.AuthenticatedWebSocketRequest;
@WebSocket(operation = "add")
public class AddSubscription extends AuthenticatedWebSocketRequest {
public class AddSubscription extends SubscriptionRequest {
private String metric = null;
private Optional<Map<String, String>> tags = Optional.empty();
@@ -1,8 +1,7 @@
package timely.api.request.subscription;
import timely.api.annotation.WebSocket;
import timely.api.request.AuthenticatedWebSocketRequest;
@WebSocket(operation = "close")
public class CloseSubscription extends AuthenticatedWebSocketRequest {
public class CloseSubscription extends SubscriptionRequest {
}
@@ -1,8 +1,7 @@
package timely.api.request.subscription;
import timely.api.annotation.WebSocket;
import timely.api.request.AuthenticatedWebSocketRequest;
@WebSocket(operation = "create")
public class CreateSubscription extends AuthenticatedWebSocketRequest {
public class CreateSubscription extends SubscriptionRequest {
}
@@ -1,10 +1,9 @@
package timely.api.request.subscription;
import timely.api.annotation.WebSocket;
import timely.api.request.AuthenticatedWebSocketRequest;
@WebSocket(operation = "remove")
public class RemoveSubscription extends AuthenticatedWebSocketRequest {
public class RemoveSubscription extends SubscriptionRequest {
private String metric = null;
@@ -0,0 +1,24 @@
package timely.api.request.subscription;
import timely.api.request.AuthenticatedWebSocketRequest;
public class SubscriptionRequest extends AuthenticatedWebSocketRequest {
private String subscriptionId = null;
public String getSubscriptionId() {
return subscriptionId;
}
public void setSubscriptionId(String subscriptionId) {
this.subscriptionId = subscriptionId;
}
@Override
public void validate() {
if (null == subscriptionId) {
throw new IllegalArgumentException("Subscription ID is required.");
}
}
}
@@ -14,6 +14,7 @@ public class MetricResponse {
private long timestamp;
private double value;
private List<Tag> tags;
private String subscriptionId;
public String getMetric() {
return metric;
@@ -47,6 +48,14 @@ public class MetricResponse {
this.tags = tags;
}
public String getSubscriptionId() {
return subscriptionId;
}
public void setSubscriptionId(String subscriptionId) {
this.subscriptionId = subscriptionId;
}
@Override
public int hashCode() {
HashCodeBuilder hash = new HashCodeBuilder();
@@ -54,6 +63,7 @@ public class MetricResponse {
hash.append(this.timestamp);
hash.append(this.value);
hash.append(this.tags);
hash.append(this.subscriptionId);
return hash.hashCode();
}
@@ -74,6 +84,7 @@ public class MetricResponse {
equals.append(this.timestamp, other.timestamp);
equals.append(this.value, other.value);
equals.append(this.tags, other.tags);
equals.append(this.subscriptionId, other.subscriptionId);
return equals.isEquals();
}
@@ -84,6 +95,7 @@ public class MetricResponse {
tsb.append("timestamp", this.timestamp);
tsb.append("value", this.value);
tsb.append("tags", this.tags);
tsb.append("subscriptionId", this.subscriptionId);
return tsb.toString();
}
@@ -18,7 +18,6 @@ import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
@@ -62,20 +61,15 @@ public class MetricsResponse {
private static final String TD_END = "</td>\n";
private Set<String> ignoredTags = Collections.emptySet();
private Configuration conf = null;
private final Configuration conf;
public MetricsResponse() {
conf = null;
}
public MetricsResponse(Configuration conf) {
String tagsToIgnore = conf.get(Configuration.METRICS_IGNORED_TAGS);
if (!StringUtils.isEmpty(tagsToIgnore)) {
this.ignoredTags = new HashSet<>();
for (String tag : tagsToIgnore.split(",")) {
ignoredTags.add(tag);
}
}
this.conf = conf;
ignoredTags = new HashSet<>(conf.getMetricsReportIgnoredTags());
}
public TextWebSocketFrame toWebSocketResponse(String acceptHeader) throws Exception {
@@ -23,20 +23,20 @@ public class AuthCache {
private static Cache<String, Authentication> CACHE = null;
private static Long sessionMaxAge = -1L;
private static int sessionMaxAge = -1;
/**
* For tests only
*/
public static void resetSessionMaxAge() {
sessionMaxAge = -1L;
sessionMaxAge = -1;
}
public static void setSessionMaxAge(Configuration config) {
if (-1 != sessionMaxAge) {
throw new IllegalStateException("Cache session max age already configured.");
}
sessionMaxAge = Long.parseLong(config.get(Configuration.SESSION_MAX_AGE));
sessionMaxAge = config.getSecurity().getSessionMaxAge();
}
public static Cache<String, Authentication> getCache() {
@@ -56,9 +56,7 @@ public class AuthCache {
Collection<? extends GrantedAuthority> authorities = CACHE.asMap().get(sessionId).getAuthorities();
String[] auths = new String[authorities.size()];
final AtomicInteger i = new AtomicInteger(0);
authorities.forEach(a -> {
auths[i.getAndIncrement()] = a.getAuthority();
});
authorities.forEach(a -> auths[i.getAndIncrement()] = a.getAuthority());
return new Authorizations(auths);
} else {
return null;
@@ -69,7 +67,7 @@ public class AuthCache {
}
public static void enforceAccess(Configuration conf, Request r) throws Exception {
if (!conf.getBoolean(Configuration.ALLOW_ANONYMOUS_ACCESS) && (r instanceof AuthenticatedRequest)) {
if (!conf.getSecurity().isAllowAnonymousAccess() && (r instanceof AuthenticatedRequest)) {
AuthenticatedRequest ar = (AuthenticatedRequest) r;
if (StringUtils.isEmpty(ar.getSessionId())) {
throw new TimelyException(HttpResponseStatus.UNAUTHORIZED.code(), "User must log in",
@@ -11,22 +11,11 @@ public class VisibilityCache {
private static volatile Cache<String, ColumnVisibility> CACHE = null;
/** Used for testing */
public static synchronized void init() {
if (CACHE == null) {
long defaultExpiration = Configuration.VISIBILITY_EXPIRATION_DEFAULT;
int initialCapacity = Configuration.VISIBILITY_CACHE_INITIAL_CAPACITY_DEFAULT;
long maxCapacity = Configuration.VISIBILITY_CACHE_MAX_CAPACITY_DEFAULT;
CACHE = Caffeine.newBuilder().expireAfterAccess(defaultExpiration, TimeUnit.MINUTES)
.initialCapacity(initialCapacity).maximumSize(maxCapacity).build();
}
}
public static synchronized void init(Configuration config) {
if (CACHE == null) {
long expireMinutes = Long.parseLong(config.get(Configuration.VISIBILITY_CACHE_EXPIRATION));
int initialCapacity = Integer.parseInt(config.get(Configuration.VISIBILITY_CACHE_INITIAL_CAPACITY));
long maxCapacity = Long.parseLong(config.get(Configuration.VISIBILITY_CACHE_MAX_CAPACITY));
long expireMinutes = config.getVisibilityCache().getExpirationMinutes();
int initialCapacity = config.getVisibilityCache().getInitialCapacity();
long maxCapacity = config.getVisibilityCache().getMaxCapacity();
CACHE = Caffeine.newBuilder().expireAfterAccess(expireMinutes, TimeUnit.MINUTES)
.initialCapacity(initialCapacity).maximumSize(maxCapacity).build();
}
@@ -40,8 +40,28 @@ public class HttpRequestDecoder extends MessageToMessageDecoder<FullHttpRequest>
public HttpRequestDecoder(Configuration config) {
this.conf = config;
this.anonymousAccessAllowed = conf.getBoolean(Configuration.ALLOW_ANONYMOUS_ACCESS);
this.nonSecureRedirectAddress = conf.get(Configuration.NON_SECURE_REDIRECT_PATH);
this.anonymousAccessAllowed = conf.getSecurity().isAllowAnonymousAccess();
this.nonSecureRedirectAddress = conf.getHttp().getRedirectPath();
}
public static String getSessionId(FullHttpRequest msg, boolean anonymousAccessAllowed) {
final StringBuilder buf = new StringBuilder();
msg.headers().getAll(Names.COOKIE).forEach(h -> {
ServerCookieDecoder.STRICT.decode(h).forEach(c -> {
if (c.name().equals(Constants.COOKIE_NAME)) {
if (buf.length() == 0) {
buf.append(c.value());
}
}
});
});
String sessionId = buf.toString();
if (sessionId.length() == 0 && anonymousAccessAllowed) {
sessionId = NO_AUTHORIZATIONS;
} else if (sessionId.length() == 0) {
sessionId = null;
}
return sessionId;
}
@Override
@@ -56,27 +76,10 @@ public class HttpRequestDecoder extends MessageToMessageDecoder<FullHttpRequest>
return;
}
final StringBuilder buf = new StringBuilder();
msg.headers().getAll(Names.COOKIE).forEach(h -> {
ServerCookieDecoder.STRICT.decode(h).forEach(c -> {
if (c.name().equals(Constants.COOKIE_NAME)) {
if (buf.length() == 0) {
buf.append(c.value());
}
}
});
});
String sId = buf.toString();
if (sId.length() == 0 && this.anonymousAccessAllowed) {
sId = NO_AUTHORIZATIONS;
} else if (sId.length() == 0) {
sId = null;
}
final String sessionId = sId;
final String sessionId = getSessionId(msg, this.anonymousAccessAllowed);
LOG.trace("SessionID: " + sessionId);
Request request = null;
Request request;
try {
if (msg.getMethod().equals(HttpMethod.GET)) {
HttpGetRequest get = AnnotationResolver.getClassForHttpGet(decoder.path());
@@ -22,9 +22,9 @@ public class NonSecureHttpHandler extends ChannelInboundHandlerAdapter implement
private final String redirectAddress;
public NonSecureHttpHandler(Configuration conf) {
String timelyHost = conf.get(Configuration.TIMELY_HTTP_HOST);
String timelyPort = conf.get(Configuration.QUERY_PORT);
String path = conf.get(Configuration.NON_SECURE_REDIRECT_PATH);
String timelyHost = conf.getHttp().getHost();
int timelyPort = conf.getHttp().getPort();
String path = conf.getHttp().getRedirectPath();
redirectAddress = "https://" + timelyHost + ":" + timelyPort + path;
}
@@ -13,7 +13,7 @@ public class StrictTransportHandler extends SimpleChannelInboundHandler<StrictTr
private String hstsMaxAge = "max-age=";
public StrictTransportHandler(Configuration conf) {
String maxAge = conf.get(Configuration.STRICT_TRANSPORT_MAX_AGE);
long maxAge = conf.getHttp().getStrictTransportMaxAge();
hstsMaxAge = "max-age=" + maxAge;
}
@@ -27,15 +27,12 @@ public abstract class TimelyLoginRequestHandler<T> extends SimpleChannelInboundH
private static final Logger LOG = LoggerFactory.getLogger(TimelyLoginRequestHandler.class);
private long maxAge = Configuration.SESSION_MAX_AGE_DEFAULT;
private String domain = null;
private final long maxAge;
private final String domain;
public TimelyLoginRequestHandler(Configuration conf) {
String ma = conf.get(Configuration.SESSION_MAX_AGE);
if (null != ma) {
maxAge = Long.parseLong(ma);
}
domain = conf.get(Configuration.TIMELY_HTTP_HOST);
maxAge = conf.getSecurity().getSessionMaxAge();
domain = conf.getHttp().getHost();
}
@Override
@@ -0,0 +1,42 @@
package timely.netty.websocket;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec;
import io.netty.handler.codec.http.FullHttpRequest;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import timely.Configuration;
import timely.netty.http.HttpRequestDecoder;
import timely.subscription.SubscriptionRegistry;
public class WebSocketHttpCookieHandler extends MessageToMessageCodec<FullHttpRequest, FullHttpRequest> {
private static final Logger LOG = LoggerFactory.getLogger(WebSocketHttpCookieHandler.class);
private boolean anonymousAccessAllowed;
public WebSocketHttpCookieHandler(Configuration config) {
super();
this.anonymousAccessAllowed = config.getSecurity().isAllowAnonymousAccess();
}
@Override
protected void decode(ChannelHandlerContext ctx, FullHttpRequest msg, List<Object> out) throws Exception {
msg.retain();
out.add(msg);
// If the session cookie exists, set its value on the ctx.
final String sessionId = HttpRequestDecoder.getSessionId(msg, this.anonymousAccessAllowed);
ctx.channel().attr(SubscriptionRegistry.SESSION_ID_ATTR).set(sessionId);
LOG.info("Found session id in WebSocket channel, setting sessionId {} on context", sessionId);
}
@Override
protected void encode(ChannelHandlerContext ctx, FullHttpRequest msg, List<Object> out) throws Exception {
msg.retain();
out.add(msg);
}
}
@@ -15,6 +15,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import timely.Configuration;
import timely.api.request.AuthenticatedRequest;
import timely.api.request.WebSocketRequest;
import timely.api.response.TimelyException;
import timely.auth.AuthCache;
@@ -40,6 +41,13 @@ public class WebSocketRequestDecoder extends MessageToMessageDecoder<WebSocketFr
WebSocketRequest request = JsonUtil.getObjectMapper().readValue(content, WebSocketRequest.class);
LOG.trace("Received WS request {}", content);
String sessionId = ctx.channel().attr(SubscriptionRegistry.SESSION_ID_ATTR).get();
if (request instanceof AuthenticatedRequest && !StringUtils.isEmpty(sessionId)) {
LOG.info("Found session id in WebSocket channel, setting sessionId {} on request", sessionId);
AuthenticatedRequest ar = (AuthenticatedRequest) request;
ar.setSessionId(sessionId);
}
try {
request.validate();
} catch (IllegalArgumentException e) {
@@ -81,14 +89,14 @@ public class WebSocketRequestDecoder extends MessageToMessageDecoder<WebSocketFr
if (idle.state() == IdleState.READER_IDLE) {
// We have not read any data from client in a while, let's close
// the subscriptions for this context.
String sessionId = ctx.attr(SubscriptionRegistry.SESSION_ID_ATTR).get();
if (!StringUtils.isEmpty(sessionId)) {
if (SubscriptionRegistry.get().containsKey(sessionId)) {
LOG.info("Closing subscription with session id {} due to idle event", sessionId);
SubscriptionRegistry.get().get(sessionId).close();
String subscriptionId = ctx.channel().attr(SubscriptionRegistry.SUBSCRIPTION_ID_ATTR).get();
if (!StringUtils.isEmpty(subscriptionId)) {
if (SubscriptionRegistry.get().containsKey(subscriptionId)) {
LOG.info("Closing subscription with subscription id {} due to idle event", subscriptionId);
SubscriptionRegistry.get().get(subscriptionId).close();
}
} else {
LOG.warn("Channel idle, but no session id found on context. Unable to close subscriptions");
LOG.warn("Channel idle, but no subscription id found on context. Unable to close subscriptions");
}
}
} else {
@@ -19,7 +19,7 @@ public class WSAddSubscriptionRequestHandler extends SimpleChannelInboundHandler
@Override
protected void channelRead0(ChannelHandlerContext ctx, AddSubscription add) throws Exception {
Subscription s = SubscriptionRegistry.get().get(add.getSessionId());
Subscription s = SubscriptionRegistry.get().get(add.getSubscriptionId());
if (null != s) {
String metric = add.getMetric();
if (null == metric) {
@@ -40,8 +40,8 @@ public class WSAddSubscriptionRequestHandler extends SimpleChannelInboundHandler
}
s.addMetric(metric, tags, startTime, delayTime);
} else {
LOG.error("Unknown session id, create subscription first");
ctx.writeAndFlush(new CloseWebSocketFrame(1003, "Unknown session id, create subscription first"));
LOG.error("Unknown subscription id, create subscription first");
ctx.writeAndFlush(new CloseWebSocketFrame(1003, "Unknown subscription id, create subscription first"));
}
}
@@ -10,7 +10,7 @@ public class WSCloseSubscriptionRequestHandler extends SimpleChannelInboundHandl
@Override
protected void channelRead0(ChannelHandlerContext ctx, CloseSubscription close) throws Exception {
Subscription s = SubscriptionRegistry.get().remove(close.getSessionId());
Subscription s = SubscriptionRegistry.get().remove(close.getSubscriptionId());
if (null != s) {
s.close();
}
@@ -27,19 +27,21 @@ public class WSCreateSubscriptionRequestHandler extends SimpleChannelInboundHand
@Override
protected void channelRead0(ChannelHandlerContext ctx, CreateSubscription create) throws Exception {
SubscriptionRegistry.get().put(create.getSessionId(),
new Subscription(create.getSessionId(), store, ctx, this.conf));
final String subscriptionId = create.getSubscriptionId();
SubscriptionRegistry.get().put(subscriptionId,
new Subscription(subscriptionId, create.getSessionId(), store, ctx, this.conf));
// Store the session id as an attribute on the context.
ctx.attr(SubscriptionRegistry.SESSION_ID_ATTR).set(create.getSessionId());
ctx.channel().attr(SubscriptionRegistry.SUBSCRIPTION_ID_ATTR).set(subscriptionId);
LOG.info("Created subscription {} on channel {}", subscriptionId, ctx);
ctx.channel().closeFuture().addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
Subscription s = SubscriptionRegistry.get().remove(create.getSessionId());
Subscription s = SubscriptionRegistry.get().remove(subscriptionId);
if (null != s) {
LOG.info("Channel closed, closing subscriptions for sessionId: " + create.getSessionId());
LOG.info("Channel closed, closing subscriptions for subscriptionId: " + subscriptionId);
s.close();
}
}
@@ -10,7 +10,7 @@ public class WSRemoveSubscriptionRequestHandler extends SimpleChannelInboundHand
@Override
protected void channelRead0(ChannelHandlerContext ctx, RemoveSubscription remove) throws Exception {
Subscription s = SubscriptionRegistry.get().get(remove.getSessionId());
Subscription s = SubscriptionRegistry.get().get(remove.getSubscriptionId());
if (null != s) {
s.removeMetric(remove.getMetric());
}
@@ -1,6 +1,5 @@
package timely.store;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.apache.accumulo.core.conf.AccumuloConfiguration.getMemoryInBytes;
import static org.apache.accumulo.core.conf.AccumuloConfiguration.getTimeInMillis;
import io.netty.handler.codec.http.HttpResponseStatus;
@@ -62,8 +61,8 @@ import org.apache.hadoop.io.Text;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import timely.Configuration;
import timely.Server;
import timely.Configuration;
import timely.api.model.Meta;
import timely.api.model.Metric;
import timely.api.model.Tag;
@@ -113,7 +112,6 @@ public class DataStoreImpl implements DataStore {
}
Configuration conf;
private final Connector connector;
private MetaCache metaCache = null;
private final AtomicLong lastCountTime = new AtomicLong(System.currentTimeMillis());
@@ -133,27 +131,28 @@ public class DataStoreImpl implements DataStore {
try {
final BaseConfiguration apacheConf = new BaseConfiguration();
apacheConf.setProperty("instance.name", conf.get(Configuration.INSTANCE_NAME));
apacheConf.setProperty("instance.zookeeper.host", conf.get(Configuration.ZOOKEEPERS));
Configuration.Accumulo accumuloConf = conf.getAccumulo();
apacheConf.setProperty("instance.name", accumuloConf.getInstanceName());
apacheConf.setProperty("instance.zookeeper.host", accumuloConf.getZookeepers());
final ClientConfiguration aconf = new ClientConfiguration(Collections.singletonList(apacheConf));
final Instance instance = new ZooKeeperInstance(aconf);
final byte[] passwd = conf.get(Configuration.PASSWORD).getBytes(UTF_8);
connector = instance.getConnector(conf.get(Configuration.USERNAME), new PasswordToken(passwd));
connector = instance
.getConnector(accumuloConf.getUsername(), new PasswordToken(accumuloConf.getPassword()));
bwConfig = new BatchWriterConfig();
bwConfig.setMaxLatency(getTimeInMillis(conf.get(Configuration.MAX_LATENCY)), TimeUnit.MILLISECONDS);
bwConfig.setMaxMemory(getMemoryInBytes(conf.get(Configuration.WRITE_BUFFER_SIZE)) / numWriteThreads);
bwConfig.setMaxWriteThreads(Integer.parseInt(conf.get(Configuration.WRITE_THREADS)));
scannerThreads = Integer.parseInt(conf.get(Configuration.SCANNER_THREADS));
anonAccessAllowed = conf.getBoolean(Configuration.ALLOW_ANONYMOUS_ACCESS);
bwConfig.setMaxLatency(getTimeInMillis(accumuloConf.getWrite().getLatency()), TimeUnit.MILLISECONDS);
bwConfig.setMaxMemory(getMemoryInBytes(accumuloConf.getWrite().getBufferSize()) / numWriteThreads);
bwConfig.setMaxWriteThreads(accumuloConf.getWrite().getThreads());
scannerThreads = accumuloConf.getScan().getThreads();
anonAccessAllowed = conf.getSecurity().isAllowAnonymousAccess();
String ageoff = Long.toString(Integer.parseInt(conf.get(Configuration.METRICS_AGEOFF_DAYS)) * 86400000L);
String ageoff = Long.toString(conf.getMetricAgeOffDays() * 86400000L);
Map<String, String> ageOffOptions = new HashMap<>();
ageOffOptions.put("ttl", ageoff);
IteratorSetting ageOffIteratorSettings = new IteratorSetting(100, "ageoff", AgeOffFilter.class,
ageOffOptions);
EnumSet<IteratorScope> ageOffIteratorScope = EnumSet.allOf(IteratorScope.class);
metricsTable = conf.get(Configuration.METRICS_TABLE);
metricsTable = conf.getMetricsTable();
if (metricsTable.contains(".")) {
final String[] parts = metricsTable.split("\\.", 2);
final String namespace = parts[0];
@@ -184,7 +183,7 @@ public class DataStoreImpl implements DataStore {
}
}
}
metaTable = conf.get(Configuration.META_TABLE);
metaTable = conf.getMetaTable();
if (!tableIdMap.containsKey(metaTable)) {
try {
LOG.info("Creating table " + metaTable);
@@ -392,6 +391,10 @@ public class DataStoreImpl implements DataStore {
}
result.setTags(tags);
result.setLimit(msg.getLimit());
Map<String, Pattern> tagPatterns = new HashMap<>();
tags.forEach((k, v) -> {
tagPatterns.put(k, Pattern.compile(v));
});
try {
List<Result> resultField = new ArrayList<>();
Scanner scanner = connector.createScanner(metaTable, Authorizations.EMPTY);
@@ -399,11 +402,11 @@ public class DataStoreImpl implements DataStore {
Key end = start.followingKey(PartialKey.ROW);
Range range = new Range(start, end);
scanner.setRange(range);
// TODO compute columns to fetch
tags.keySet().forEach(k -> scanner.fetchColumnFamily(new Text(k)));
int total = 0;
for (Entry<Key, Value> entry : scanner) {
Meta metaEntry = Meta.parse(entry.getKey(), entry.getValue());
if (matches(metaEntry.getTagKey(), metaEntry.getTagValue(), tags)) {
if (matches(metaEntry.getTagKey(), metaEntry.getTagValue(), tagPatterns)) {
if (resultField.size() < msg.getLimit()) {
Result r = new Result();
r.putTag(metaEntry.getTagKey(), metaEntry.getTagValue());
@@ -423,12 +426,9 @@ public class DataStoreImpl implements DataStore {
return result;
}
private boolean matches(String tagk, String tagv, Map<String, String> tags) {
// first, match keys...
for (Entry<String, String> entry : tags.entrySet()) {
String k = entry.getKey();
String v = entry.getValue();
if (("*".equals(k) || tagk.equals(k)) && ("*".equals(v) || tagv.equals(v))) {
private boolean matches(String tagk, String tagv, Map<String, Pattern> tags) {
for (Entry<String, Pattern> entry : tags.entrySet()) {
if (tagk.equals(entry.getKey()) && entry.getValue().matcher(tagv).matches()) {
return true;
}
}
@@ -18,10 +18,10 @@ public class MetaCacheImpl implements MetaCache {
@Override
public void init(Configuration config) {
long defaultExpiration = Long.parseLong(config.get(Configuration.META_CACHE_EXPIRATION));
int initialCapacity = Integer.parseInt(config.get(Configuration.META_CACHE_INITIAL_CAPACITY));
long maxCapacity = Long.parseLong(config.get(Configuration.META_CACHE_MAX_CAPACITY));
cache = Caffeine.newBuilder().expireAfterAccess(defaultExpiration, TimeUnit.MINUTES)
long expirationMinutes = config.getMetaCache().getExpirationMinutes();
int initialCapacity = config.getMetaCache().getInitialCapacity();
long maxCapacity = config.getMetaCache().getMaxCapacity();
cache = Caffeine.newBuilder().expireAfterAccess(expirationMinutes, TimeUnit.MINUTES)
.initialCapacity(initialCapacity).maximumSize(maxCapacity).build();
}
@@ -36,16 +36,21 @@ public class MetricScanner extends Thread implements UncaughtExceptionHandler {
private final long delay;
private final String name;
private final int lag;
private final String subscriptionId;
private final String metric;
public MetricScanner(String sessionId, DataStore store, String metric, Map<String, String> tags, long startTime,
long delay, int lag, ChannelHandlerContext ctx) throws TimelyException {
public MetricScanner(String subscriptionId, String sessionId, DataStore store, String metric,
Map<String, String> tags, long startTime, long delay, int lag, ChannelHandlerContext ctx)
throws TimelyException {
this.setDaemon(true);
this.setUncaughtExceptionHandler(this);
this.ctx = ctx;
this.lag = lag;
this.metric = metric;
this.scanner = store.createScannerForMetric(sessionId, metric, tags, startTime, lag);
this.iter = scanner.iterator();
this.delay = delay;
this.subscriptionId = subscriptionId;
ToStringBuilder buf = new ToStringBuilder(this);
buf.append("sessionId", sessionId);
buf.append("metric", metric);
@@ -70,27 +75,32 @@ public class MetricScanner extends Thread implements UncaughtExceptionHandler {
Entry<Key, Value> e = this.iter.next();
m = Metric.parse(e.getKey(), e.getValue());
try {
String json = om.writeValueAsString(m.toMetricResponse());
String json = om.writeValueAsString(m.toMetricResponse(this.subscriptionId));
LOG.trace("Returning {} for subscription", json);
this.ctx.writeAndFlush(new TextWebSocketFrame(json));
} catch (JsonProcessingException e1) {
LOG.error("Error serializing metric: " + m, e1);
}
} else {
// Reset the starting range to the last key returned
LOG.debug("Exhausted scanner, waiting {}ms to retry.", delay);
UtilWaitThread.sleep(delay);
long endTime = (System.currentTimeMillis() - (lag * 1000));
byte[] end = Metric.encodeRowKey(this.metric, endTime);
Text endRow = new Text(end);
this.scanner.close();
Range prevRange = this.scanner.getRange();
if (null != m) {
long endTime = (System.currentTimeMillis() - (lag * 1000));
byte[] end = Metric.encodeRowKey(m.getMetric(), endTime);
Text endRow = new Text(end);
if (null == m) {
LOG.debug("No results found, waiting {}ms to retry.", delay);
UtilWaitThread.sleep(delay);
this.scanner.setRange(new Range(prevRange.getStartKey().getRow(), prevRange
.isStartKeyInclusive(), endRow, false));
this.iter = this.scanner.iterator();
} else {
// Reset the starting range to the last key returned
LOG.debug("Exhausted scanner, waiting {}ms to retry.", delay);
UtilWaitThread.sleep(delay);
this.scanner.setRange(new Range(new Text(Metric.encodeRowKey(m.getMetric(), m.getTimestamp())),
false, endRow, prevRange.isEndKeyInclusive()));
m = null;
this.iter = this.scanner.iterator();
}
this.iter = this.scanner.iterator();
}
}
} finally {
@@ -25,27 +25,27 @@ public class Subscription {
private final ChannelHandlerContext ctx;
private final ScheduledFuture<?> ping;
private final Integer lag;
private final String subscriptionId;
public Subscription(String sessionId, DataStore store, ChannelHandlerContext ctx, Configuration conf) {
public Subscription(String subscriptionId, String sessionId, DataStore store, ChannelHandlerContext ctx,
Configuration conf) {
this.subscriptionId = subscriptionId;
this.sessionId = sessionId;
this.store = store;
this.ctx = ctx;
this.lag = Integer.parseInt(conf.get(Configuration.WS_SUBSCRIPTION_LAG));
this.lag = conf.getWebsocket().getSubscriptionLag();
// send a websocket ping at half the timeout interval.
int rate = (Integer.parseInt(conf.get(Configuration.WS_TIMEOUT_SECONDS)) / 2);
this.ping = this.ctx.executor().scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
LOG.trace("Sending ping on channel {}", ctx.channel());
ctx.writeAndFlush(new PingWebSocketFrame());
}
int rate = conf.getWebsocket().getTimeout() / 2;
this.ping = this.ctx.executor().scheduleAtFixedRate(() -> {
LOG.trace("Sending ping on channel {}", ctx.channel());
ctx.writeAndFlush(new PingWebSocketFrame());
}, rate, rate, TimeUnit.SECONDS);
}
public void addMetric(String metric, Map<String, String> tags, long startTime, long delay) throws TimelyException {
LOG.debug("Adding metric scanner for subscription {}", this.sessionId);
MetricScanner m = new MetricScanner(this.sessionId, store, metric, tags, startTime, delay, lag, ctx);
LOG.debug("Adding metric scanner for subscription {}", this.subscriptionId);
MetricScanner m = new MetricScanner(this.subscriptionId, this.sessionId, store, metric, tags, startTime, delay,
lag, ctx);
METRICS.put(metric, m);
m.start();
}
@@ -63,7 +63,7 @@ public class Subscription {
}
public void close() {
LOG.info("Closing subscriptions for session: " + this.sessionId);
LOG.info("Closing subscriptions for {}", this.subscriptionId);
this.ping.cancel(false);
METRICS.forEach((k, v) -> {
v.close();
@@ -7,6 +7,7 @@ import java.util.concurrent.ConcurrentHashMap;
public class SubscriptionRegistry extends ConcurrentHashMap<String, Subscription> {
public static final AttributeKey<String> SESSION_ID_ATTR = AttributeKey.newInstance("sessionId");
public static final AttributeKey<String> SUBSCRIPTION_ID_ATTR = AttributeKey.newInstance("subscriptionId");
private static final long serialVersionUID = 1L;
private static final SubscriptionRegistry REGISTRY = new SubscriptionRegistry();
@@ -1,8 +1,5 @@
package timely.util;
import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.File;
import java.util.Collections;
import java.util.Map.Entry;
@@ -17,6 +14,9 @@ import org.apache.accumulo.core.data.Range;
import org.apache.accumulo.core.data.Value;
import org.apache.commons.configuration.BaseConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import timely.SpringBootstrap;
import timely.Configuration;
import timely.api.model.Meta;
@@ -24,33 +24,28 @@ public class GetMetricTableSplitPoints {
public static void main(String[] args) throws Exception {
if (args.length != 1) {
System.err.println("CreateMetricTableSplitPoints <configFile>");
System.exit(1);
}
try (ConfigurableApplicationContext ctx = new SpringApplicationBuilder(SpringBootstrap.class).web(false).run(
args)) {
Configuration conf = ctx.getBean(Configuration.class);
final File f = new File(args[0]);
if (!f.canRead()) {
throw new RuntimeException("Configuration file does not exist or cannot be read");
}
Configuration conf = new Configuration(f);
final BaseConfiguration apacheConf = new BaseConfiguration();
apacheConf.setProperty("instance.name", conf.get(Configuration.INSTANCE_NAME));
apacheConf.setProperty("instance.zookeeper.host", conf.get(Configuration.ZOOKEEPERS));
final ClientConfiguration aconf = new ClientConfiguration(Collections.singletonList(apacheConf));
final Instance instance = new ZooKeeperInstance(aconf);
final byte[] passwd = conf.get(Configuration.PASSWORD).getBytes(UTF_8);
Connector con = instance.getConnector(conf.get(Configuration.USERNAME), new PasswordToken(passwd));
Scanner s = con.createScanner(conf.get(Configuration.META_TABLE), con.securityOperations()
.getUserAuthorizations(con.whoami()));
try {
s.setRange(new Range(Meta.METRIC_PREFIX, true, Meta.TAG_PREFIX, false));
for (Entry<Key, Value> e : s) {
System.out.println(e.getKey().getRow().toString().substring(Meta.METRIC_PREFIX.length()));
final BaseConfiguration apacheConf = new BaseConfiguration();
Configuration.Accumulo accumuloConf = conf.getAccumulo();
apacheConf.setProperty("instance.name", accumuloConf.getInstanceName());
apacheConf.setProperty("instance.zookeeper.host", accumuloConf.getZookeepers());
final ClientConfiguration aconf = new ClientConfiguration(Collections.singletonList(apacheConf));
final Instance instance = new ZooKeeperInstance(aconf);
Connector con = instance.getConnector(accumuloConf.getUsername(),
new PasswordToken(accumuloConf.getPassword()));
Scanner s = con.createScanner(conf.getMetaTable(),
con.securityOperations().getUserAuthorizations(con.whoami()));
try {
s.setRange(new Range(Meta.METRIC_PREFIX, true, Meta.TAG_PREFIX, false));
for (Entry<Key, Value> e : s) {
System.out.println(e.getKey().getRow().toString().substring(Meta.METRIC_PREFIX.length()));
}
} finally {
s.close();
}
} finally {
s.close();
}
}
}
@@ -19,18 +19,18 @@ public class InsertTestData {
private static final List<String> METRICS = new ArrayList<>();
static {
METRICS.add("sys.cpu0.user");
METRICS.add("sys.cpu0.idle");
METRICS.add("sys.cpu0.system");
METRICS.add("sys.cpu0.wait");
METRICS.add("sys.cpu.user");
METRICS.add("sys.cpu.idle");
METRICS.add("sys.cpu.system");
METRICS.add("sys.cpu.wait");
METRICS.add("sys.memory.free");
METRICS.add("sys.memory.used");
METRICS.add("sys.eth0.rx-errors");
METRICS.add("sys.eth0.rx-dropped");
METRICS.add("sys.eth0.rx-packets");
METRICS.add("sys.eth0.tx-errors");
METRICS.add("sys.eth0.tx-dropped");
METRICS.add("sys.eth0.tx-packets");
METRICS.add("sys.eth.rx-errors");
METRICS.add("sys.eth.rx-dropped");
METRICS.add("sys.eth.rx-packets");
METRICS.add("sys.eth.tx-errors");
METRICS.add("sys.eth.tx-dropped");
METRICS.add("sys.eth.tx-packets");
METRICS.add("sys.swap.free");
METRICS.add("sys.swap.used");
}
@@ -49,17 +49,25 @@ public class InsertTestData {
RACKS.add("r02");
}
private static final Map<String, String> VISIBILITIES = new HashMap<>();
private static final List<String> INSTANCES = new ArrayList<>();
static {
VISIBILITIES.put("sys.eth0.rx-errors", "A");
VISIBILITIES.put("sys.eth0.rx-dropped", "B");
VISIBILITIES.put("sys.eth0.rx-packets", "C");
VISIBILITIES.put("sys.eth0.tx-errors", "D");
VISIBILITIES.put("sys.eth0.tx-dropped", "E");
VISIBILITIES.put("sys.eth0.tx-packets", "F");
INSTANCES.add("0");
INSTANCES.add("1");
INSTANCES.add("2");
INSTANCES.add("3");
}
private static final String FMT = "put {0} {1,number,#} {2,number} host={4}{3} rack={4}";
private static final Map<String, String> VISIBILITIES = new HashMap<>();
static {
VISIBILITIES.put("sys.eth.rx-errors", "A");
VISIBILITIES.put("sys.eth.rx-dropped", "B");
VISIBILITIES.put("sys.eth.rx-packets", "C");
VISIBILITIES.put("sys.eth.tx-errors", "D");
VISIBILITIES.put("sys.eth.tx-dropped", "E");
VISIBILITIES.put("sys.eth.tx-packets", "F");
}
private static final String FMT = "put {0} {1,number,#} {2,number} host={4}{3} rack={4} instance={5}";
private static String usage() {
return "InsertTestData <host> <port> [--fast]";
@@ -87,12 +95,17 @@ public class InsertTestData {
final String viz = VISIBILITIES.get(m);
RACKS.forEach(rack -> {
HOSTS.forEach(host -> {
String put = MessageFormat.format(FMT, m, time,
ThreadLocalRandom.current().nextDouble(0.0D, 100.0D), host, rack);
if (viz != null)
put += " viz=" + viz;
writer.println(put);
LOG.info(put);
INSTANCES.forEach(instance -> {
if (!m.startsWith("sys.cpu.") && !instance.equals("0")) {
return;
}
String put = MessageFormat.format(FMT, m, time,
ThreadLocalRandom.current().nextDouble(0.0D, 100.0D), host, rack, instance);
if (viz != null)
put += " viz=" + viz;
writer.println(put);
LOG.info(put);
});
});
});
});
@@ -0,0 +1,45 @@
package timely.validator;
import javax.validation.Constraint;
import javax.validation.Payload;
import javax.validation.ReportAsSingleViolation;
import javax.validation.constraints.NotNull;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Validates that field {@code notNullFieldName} is not null if
* {@code fieldName} is set to {@code fieldValue}
*/
@Documented
@Constraint(validatedBy = { NotEmptyIfFieldSetValidator.class })
@Target({ TYPE, ANNOTATION_TYPE })
@Retention(RUNTIME)
@ReportAsSingleViolation
@NotNull
public @interface NotEmptyIfFieldSet {
String fieldName();
String fieldValue();
String notNullFieldName();
String message() default "{NotEmptyIfFieldSet.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
@Target({ TYPE, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Documented
@interface List {
NotEmptyIfFieldSet[] value();
}
}
@@ -0,0 +1,47 @@
package timely.validator;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.lang.reflect.InvocationTargetException;
/**
* Implementation of {@link NotEmptyIfFieldSet} validator.
*/
public class NotEmptyIfFieldSetValidator implements ConstraintValidator<NotEmptyIfFieldSet, Object> {
private String fieldName;
private String fieldSetValue;
private String notNullFieldName;
@Override
public void initialize(NotEmptyIfFieldSet annotation) {
fieldName = annotation.fieldName();
fieldSetValue = annotation.fieldValue();
notNullFieldName = annotation.notNullFieldName();
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}
try {
final String fieldValue = BeanUtils.getProperty(value, fieldName);
final String notNullFieldValue = BeanUtils.getProperty(value, notNullFieldName);
if (StringUtils.equals(fieldValue, fieldSetValue) && StringUtils.isEmpty(notNullFieldValue)) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate())
.addPropertyNode(notNullFieldName).addConstraintViolation();
return false;
}
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
throw new RuntimeException(e);
}
return true;
}
}
@@ -0,0 +1,29 @@
package timely.wamp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import rx.Observer;
import ws.wamp.jawampa.WampClient;
/**
* Timely Client state observer
*/
public class StateObserver implements Observer<WampClient.State> {
private static final Logger LOG = LoggerFactory.getLogger(StateObserver.class);
@Override
public void onCompleted() {
LOG.info("WAMP state completed");
}
@Override
public void onError(Throwable e) {
LOG.error("Wamp State Error: {}", e);
}
@Override
public void onNext(WampClient.State state) {
LOG.info("WAMP state change: {}", state);
}
}
@@ -0,0 +1,26 @@
package timely.wamp;
import rx.Observer;
import timely.api.request.VersionRequest;
import ws.wamp.jawampa.Request;
/**
* Handles timely.version requests
*/
public class VersionObserver implements Observer<Request> {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(Request request) {
request.reply(VersionRequest.VERSION);
}
}
+8
View File
@@ -0,0 +1,8 @@
${AnsiColor.RED} ._____________ _
${AnsiColor.RED} .. __(___ ___) | |
${AnsiColor.RED} ..____ | |(_)_ __ ___ ____ | |_ _
${AnsiColor.RED} .._| || | '_ ' _ \/ /_\ \ | \ / /
${AnsiColor.RED} . ._ | || | | | | | | ____/ |\ \/ /
${AnsiColor.RED} .. ._ ___|_||_|_| |_| |_|_\___,_| \ /${AnsiColor.DEFAULT}
=================================${AnsiColor.RED}__/ /${AnsiColor.DEFAULT}===================
${AnsiColor.YELLOW}:: Timely ::${AnsiColor.DEFAULT} ${AnsiColor.RED}|___/${AnsiColor.DEFAULT} ${AnsiStyle.FAINT}${AnsiColor.WHITE}${application.formatted-version}${AnsiColor.DEFAULT}${AnsiStyle.NORMAL}
+6 -6
View File
@@ -24,23 +24,23 @@
console.log("Web socket connection established.");
var create = {
"operation" : "create",
"sessionId" : "12345"
"subscriptionId" : "12345"
}
sock.send(JSON.stringify(create));
var m1 = {
"operation" : "add",
"sessionId" : "12345",
"subscriptionId" : "12345",
"metric" : "timely.metrics.received"
}
sock.send(JSON.stringify(m1));
var m1 = {
var m2 = {
"operation" : "add",
"sessionId" : "12345",
"subscriptionId" : "12345",
"metric" : "timely.keys.metric.inserted"
}
sock.send(JSON.stringify(m1));
sock.send(JSON.stringify(m2));
}
sock.onclose = function()
{
@@ -50,7 +50,7 @@
{
var close = {
"operation" : "close",
"sessionId" : "12345"
"subscriptionId" : "12345"
}
sock.send(JSON.stringify(close));
sock.close();
@@ -1,78 +1,67 @@
package timely;
import java.io.File;
import java.io.FileWriter;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.After;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.boot.test.util.EnvironmentTestUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class ConfigurationTest {
@ClassRule
public static final TemporaryFolder temp = new TemporaryFolder();
private final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
@BeforeClass
public static void before() throws Exception {
temp.create();
@After
public void closeContext() {
context.close();
}
@Test
public void testMinimalConfiguration() throws Exception {
File conf = temp.newFile("config.properties");
conf.deleteOnExit();
try (FileWriter writer = new FileWriter(conf)) {
writer.write(Configuration.IP + "=127.0.0.1\n");
writer.write(Configuration.PUT_PORT + "=54321\n");
writer.write(Configuration.QUERY_PORT + "=54322\n");
writer.write(Configuration.WEBSOCKET_PORT + "=54323\n");
writer.write(Configuration.ZOOKEEPERS + "=localhost:2181\n");
writer.write(Configuration.INSTANCE_NAME + "=test\n");
writer.write(Configuration.USERNAME + "=root\n");
writer.write(Configuration.PASSWORD + "=secret\n");
writer.write(Configuration.TIMELY_HTTP_HOST + "=localhost\n");
writer.write(Configuration.SSL_USE_GENERATED_KEYPAIR + "=true\n");
}
new Configuration(conf);
context.register(SpringBootstrap.class);
EnvironmentTestUtils.addEnvironment(this.context, "timely.server.ip:127.0.0.1", "timely.server.tcp-port:54321",
"timely.http.ip:127.0.0.1", "timely.http.port:54322", "timely.websocket.ip:127.0.0.1",
"timely.websocket.port:54323", "timely.accumulo.zookeepers:localhost:2181",
"timely.accumulo.instance-name:test", "timely.accumulo.username:root",
"timely.accumulo.password:secret", "timely.http.host:localhost",
"timely.security.ssl.use-generated-keypair:true");
context.refresh();
Configuration config = this.context.getBean(Configuration.class);
assertEquals("127.0.0.1", config.getServer().getIp());
assertEquals(54321, config.getServer().getTcpPort());
assertEquals(54322, config.getHttp().getPort());
assertEquals(54323, config.getWebsocket().getPort());
assertEquals("localhost:2181", config.getAccumulo().getZookeepers());
assertEquals("test", config.getAccumulo().getInstanceName());
assertEquals("root", config.getAccumulo().getUsername());
assertEquals("secret", config.getAccumulo().getPassword());
assertEquals("localhost", config.getHttp().getHost());
assertTrue(config.getSecurity().getSsl().isUseGeneratedKeypair());
}
@Test(expected = IllegalArgumentException.class)
@Test(expected = BeanCreationException.class)
public void testMissingSSLProperty() throws Exception {
File conf = temp.newFile("config2.properties");
conf.deleteOnExit();
try (FileWriter writer = new FileWriter(conf)) {
writer.write(Configuration.IP + "=127.0.0.1\n");
writer.write(Configuration.PUT_PORT + "=54321\n");
writer.write(Configuration.QUERY_PORT + "=54322\n");
writer.write(Configuration.WEBSOCKET_PORT + "=54323\n");
writer.write(Configuration.ZOOKEEPERS + "=localhost:2181\n");
writer.write(Configuration.INSTANCE_NAME + "=test\n");
writer.write(Configuration.USERNAME + "=root\n");
writer.write(Configuration.PASSWORD + "=secret\n");
writer.write(Configuration.TIMELY_HTTP_HOST + "=localhost\n");
}
new Configuration(conf);
context.register(SpringBootstrap.class);
EnvironmentTestUtils.addEnvironment(this.context, "timely.server.ip:127.0.0.1", "timely.server.tcp-port:54321",
"timely.http.ip:127.0.0.1", "timely.http.port:54322", "timely.websocket.ip:127.0.0.1",
"timely.websocket.port:54323", "timely.accumulo.zookeepers:localhost:2181",
"timely.accumulo.instance-name:test", "timely.accumulo.username:root",
"timely.accumulo.password:secret", "timely.http.host:localhost");
context.refresh();
}
@Test
public void testSSLProperty() throws Exception {
File conf = temp.newFile("config3.properties");
conf.deleteOnExit();
try (FileWriter writer = new FileWriter(conf)) {
writer.write(Configuration.IP + "=127.0.0.1\n");
writer.write(Configuration.PUT_PORT + "=54321\n");
writer.write(Configuration.QUERY_PORT + "=54322\n");
writer.write(Configuration.WEBSOCKET_PORT + "=54323\n");
writer.write(Configuration.ZOOKEEPERS + "=localhost:2181\n");
writer.write(Configuration.INSTANCE_NAME + "=test\n");
writer.write(Configuration.USERNAME + "=root\n");
writer.write(Configuration.PASSWORD + "=secret\n");
writer.write(Configuration.TIMELY_HTTP_HOST + "=localhost\n");
writer.write(Configuration.SSL_CERTIFICATE_FILE + "=/tmp/foo\n");
writer.write(Configuration.SSL_PRIVATE_KEY_FILE + "=/tmp/bar\n");
}
new Configuration(conf);
context.register(SpringBootstrap.class);
EnvironmentTestUtils.addEnvironment(this.context, "timely.server.ip:127.0.0.1", "timely.server.tcp-port:54321",
"timely.http.ip:127.0.0.1", "timely.http.port:54322", "timely.websocket.ip:127.0.0.1",
"timely.websocket.port:54323", "timely.accumulo.zookeepers:localhost:2181",
"timely.accumulo.instance-name:test", "timely.accumulo.username:root",
"timely.accumulo.password:secret", "timely.http.host:localhost",
"timely.security.ssl.certificate-file:/tmp/foo", "timely.security.ssl.key-file:/tmp/bar");
context.refresh();
}
}
+1 -3
View File
@@ -10,8 +10,6 @@ import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.ssl.SslContext;
import java.io.File;
import timely.netty.tcp.TcpDecoder;
import timely.test.TestCaptureRequestHandler;
@@ -20,7 +18,7 @@ public class TestServer extends Server {
private TestCaptureRequestHandler tcpRequests = new TestCaptureRequestHandler();
private TestCaptureRequestHandler httpRequests = new TestCaptureRequestHandler();
public TestServer(File conf) throws Exception {
public TestServer(Configuration conf) throws Exception {
super(conf);
}
@@ -15,6 +15,7 @@ import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import timely.Configuration;
import timely.auth.VisibilityCache;
import timely.util.JsonUtil;
@@ -22,7 +23,7 @@ public class MetricTest {
@Before
public void before() {
VisibilityCache.init();
VisibilityCache.init(new Configuration());
}
@Test
@@ -41,7 +42,7 @@ public class MetricTest {
PairLexicoder<String, Long> rowCoder = new PairLexicoder<>(new StringLexicoder(), new LongLexicoder());
DoubleLexicoder valueCoder = new DoubleLexicoder();
byte[] row = rowCoder.encode(new ComparablePair<String, Long>("sys.cpu.user", ts));
byte[] row = rowCoder.encode(new ComparablePair<>("sys.cpu.user", ts));
byte[] value = valueCoder.encode(2.0D);
Assert.assertEquals(rowCoder.decode(row), rowCoder.decode(mut.getRow()));
Assert.assertEquals(3, mut.getUpdates().size());
@@ -94,6 +95,7 @@ public class MetricTest {
@Test
public void testToMetricResponse() throws Exception {
String subscriptionId = "12345";
long ts = 1000L;
List<Tag> tags = new ArrayList<>();
tags.add(new Tag("tag1", "value1"));
@@ -103,8 +105,8 @@ public class MetricTest {
m.setValue(2.0D);
m.setTags(tags);
m.setVisibility(new ColumnVisibility("(a&b)|(c&d)"));
String json = JsonUtil.getObjectMapper().writeValueAsString(m.toMetricResponse());
String expected = "{\"metric\":\"sys.cpu.user\",\"timestamp\":1000,\"value\":2.0,\"tags\":[{\"key\":\"tag1\",\"value\":\"value1\"}]}";
String json = JsonUtil.getObjectMapper().writeValueAsString(m.toMetricResponse(subscriptionId));
String expected = "{\"metric\":\"sys.cpu.user\",\"timestamp\":1000,\"value\":2.0,\"tags\":[{\"key\":\"tag1\",\"value\":\"value1\"}],\"subscriptionId\":\"12345\"}";
Assert.assertEquals(expected, json);
}
}
@@ -1,19 +1,13 @@
package timely.api.response.timeseries;
import java.io.File;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import timely.Configuration;
import timely.api.model.Meta;
import timely.api.response.timeseries.MetricsResponse;
import timely.store.MetaCache;
import timely.store.MetaCacheFactory;
import timely.test.TestConfiguration;
@@ -38,61 +32,37 @@ public class MetricsResponseTest {
}
@ClassRule
public static final TemporaryFolder temp = new TemporaryFolder();
private static File conf = null;
@BeforeClass
public static void beforeClass() throws Exception {
temp.create();
}
@Test
public void testGenerateHtml() throws Exception {
conf = temp.newFile("config.properties");
try {
TestConfiguration cfg = TestConfiguration.createMinimalConfigurationForTest();
cfg.toConfiguration(conf);
Configuration config = new Configuration(conf);
MetaCache cache = MetaCacheFactory.getCache(config);
cache.add(new Meta("sys.cpu.user", "host", "localhost"));
cache.add(new Meta("sys.cpu.user", "instance", "0"));
cache.add(new Meta("sys.cpu.idle", "host", "localhost"));
cache.add(new Meta("sys.cpu.idle", "instance", "0"));
TestMetricsResponse r = new TestMetricsResponse(config);
String html = r.generateHtml().toString();
Assert.assertTrue(html.contains("<td>sys.cpu.idle</td>"));
Assert.assertTrue(html.contains("<td>host=localhost instance=0 </td>"));
Assert.assertTrue(html.contains("<td>sys.cpu.user</td>"));
Assert.assertTrue(html.contains("<td>host=localhost instance=0 </td>"));
} finally {
conf.delete();
}
Configuration cfg = TestConfiguration.createMinimalConfigurationForTest();
MetaCache cache = MetaCacheFactory.getCache(cfg);
cache.add(new Meta("sys.cpu.user", "host", "localhost"));
cache.add(new Meta("sys.cpu.user", "instance", "0"));
cache.add(new Meta("sys.cpu.idle", "host", "localhost"));
cache.add(new Meta("sys.cpu.idle", "instance", "0"));
TestMetricsResponse r = new TestMetricsResponse(cfg);
String html = r.generateHtml().toString();
Assert.assertTrue(html.contains("<td>sys.cpu.idle</td>"));
Assert.assertTrue(html.contains("<td>host=localhost instance=0 </td>"));
Assert.assertTrue(html.contains("<td>sys.cpu.user</td>"));
Assert.assertTrue(html.contains("<td>host=localhost instance=0 </td>"));
}
@Test
public void testGenerateHtmlWithIgnoredTags() throws Exception {
conf = temp.newFile("config.properties");
try {
TestConfiguration cfg = TestConfiguration.createMinimalConfigurationForTest();
cfg.put(Configuration.METRICS_IGNORED_TAGS, "instance");
cfg.toConfiguration(conf);
Configuration config = new Configuration(conf);
MetaCache cache = MetaCacheFactory.getCache(config);
cache.add(new Meta("sys.cpu.user", "host", "localhost"));
cache.add(new Meta("sys.cpu.user", "instance", "0"));
cache.add(new Meta("sys.cpu.idle", "host", "localhost"));
cache.add(new Meta("sys.cpu.idle", "instance", "0"));
TestMetricsResponse r = new TestMetricsResponse(config);
String html = r.generateHtml().toString();
Assert.assertTrue(html.contains("<td>sys.cpu.idle</td>"));
Assert.assertTrue(html.contains("<td>host=localhost </td>"));
Assert.assertTrue(html.contains("<td>sys.cpu.user</td>"));
Assert.assertTrue(html.contains("<td>host=localhost </td>"));
} finally {
conf.delete();
}
Configuration cfg = TestConfiguration.createMinimalConfigurationForTest();
cfg.getMetricsReportIgnoredTags().add("instance");
MetaCache cache = MetaCacheFactory.getCache(cfg);
cache.add(new Meta("sys.cpu.user", "host", "localhost"));
cache.add(new Meta("sys.cpu.user", "instance", "0"));
cache.add(new Meta("sys.cpu.idle", "host", "localhost"));
cache.add(new Meta("sys.cpu.idle", "instance", "0"));
TestMetricsResponse r = new TestMetricsResponse(cfg);
String html = r.generateHtml().toString();
Assert.assertTrue(html.contains("<td>sys.cpu.idle</td>"));
Assert.assertTrue(html.contains("<td>host=localhost </td>"));
Assert.assertTrue(html.contains("<td>sys.cpu.user</td>"));
Assert.assertTrue(html.contains("<td>host=localhost </td>"));
}
}
@@ -1,6 +1,5 @@
package timely.auth;
import java.io.File;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
@@ -10,9 +9,7 @@ import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
@@ -21,19 +18,13 @@ import timely.test.TestConfiguration;
public class AuthCacheTest {
@ClassRule
public static final TemporaryFolder temp = new TemporaryFolder();
private static Configuration config = null;
private static Configuration config;
private static String cookie = null;
@BeforeClass
public static void before() throws Exception {
File conf = temp.newFile("config.properties");
conf.deleteOnExit();
TestConfiguration cfg = TestConfiguration.createMinimalConfigurationForTest();
cfg.put(Configuration.SESSION_MAX_AGE, "5000");
config = cfg.toConfiguration(conf);
config = TestConfiguration.createMinimalConfigurationForTest();
config.getSecurity().setSessionMaxAge(5000);
cookie = URLEncoder.encode(UUID.randomUUID().toString(), StandardCharsets.UTF_8.name());
}
@@ -8,7 +8,6 @@ import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.cookie.ClientCookieEncoder;
import java.io.File;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
@@ -21,9 +20,7 @@ import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import timely.Configuration;
@@ -61,9 +58,6 @@ public class HttpRequestDecoderTest {
private static final Long TEST_TIME = System.currentTimeMillis();
@ClassRule
public static final TemporaryFolder temp = new TemporaryFolder();
private static Configuration config = null;
private static Configuration anonConfig = null;
private TestHttpQueryDecoder decoder = null;
@@ -72,17 +66,9 @@ public class HttpRequestDecoderTest {
@BeforeClass
public static void before() throws Exception {
File conf = temp.newFile("config.properties");
conf.deleteOnExit();
TestConfiguration cfg = TestConfiguration.createMinimalConfigurationForTest();
cfg.toConfiguration(conf);
config = new Configuration(conf);
File conf2 = temp.newFile("anon-config.properties");
conf2.deleteOnExit();
TestConfiguration cfg2 = TestConfiguration.createMinimalConfigurationForTest();
cfg2.put(Configuration.ALLOW_ANONYMOUS_ACCESS, "true");
cfg2.toConfiguration(conf2);
anonConfig = new Configuration(conf2);
config = TestConfiguration.createMinimalConfigurationForTest();
anonConfig = TestConfiguration.createMinimalConfigurationForTest();
anonConfig.getSecurity().setAllowAnonymousAccess(true);
cookie = URLEncoder.encode(UUID.randomUUID().toString(), StandardCharsets.UTF_8.name());
AuthCache.setSessionMaxAge(config);
AuthCache.getCache().put(cookie, new UsernamePasswordAuthenticationToken("test", "test1"));
@@ -11,7 +11,6 @@ import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.cookie.ClientCookieDecoder;
import io.netty.handler.codec.http.cookie.Cookie;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@@ -20,9 +19,7 @@ import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import timely.Configuration;
import timely.api.request.auth.BasicAuthLoginRequest;
@@ -48,19 +45,11 @@ public class BasicAuthLoginRequestHandlerTest {
}
@ClassRule
public static final TemporaryFolder temp = new TemporaryFolder();
private List<Object> results = new ArrayList<>();
@BeforeClass
public static void before() throws Exception {
File conf = temp.newFile("pre-config.properties");
conf.deleteOnExit();
TestConfiguration cfg = TestConfiguration.createMinimalConfigurationForTest();
cfg.toConfiguration(conf);
Configuration config = new Configuration(conf);
AuthCache.setSessionMaxAge(config);
AuthCache.setSessionMaxAge(TestConfiguration.createMinimalConfigurationForTest());
}
@AfterClass
@@ -75,11 +64,7 @@ public class BasicAuthLoginRequestHandlerTest {
@Test
public void testBasicAuthentication() throws Exception {
File conf = temp.newFile("config.properties");
conf.deleteOnExit();
TestConfiguration cfg = TestConfiguration.createMinimalConfigurationForTest();
cfg.toConfiguration(conf);
Configuration config = new Configuration(conf);
Configuration config = TestConfiguration.createMinimalConfigurationForTest();
// @formatter:off
String form =
@@ -99,7 +84,7 @@ public class BasicAuthLoginRequestHandlerTest {
BasicAuthLoginRequestHandler handler = new BasicAuthLoginRequestHandler(config);
CaptureChannelHandlerContext ctx = new CaptureChannelHandlerContext();
handler.channelRead(ctx, (BasicAuthLoginRequest) result);
handler.channelRead(ctx, result);
Assert.assertNotNull(ctx.msg);
Assert.assertTrue(ctx.msg instanceof DefaultFullHttpResponse);
DefaultFullHttpResponse response = (DefaultFullHttpResponse) ctx.msg;
@@ -118,11 +103,7 @@ public class BasicAuthLoginRequestHandlerTest {
@Test
public void testBasicAuthenticationFailure() throws Exception {
File conf = temp.newFile("config2.properties");
conf.deleteOnExit();
TestConfiguration cfg = TestConfiguration.createMinimalConfigurationForTest();
cfg.toConfiguration(conf);
Configuration config = new Configuration(conf);
Configuration config = TestConfiguration.createMinimalConfigurationForTest();
// @formatter:off
String form =
@@ -142,7 +123,7 @@ public class BasicAuthLoginRequestHandlerTest {
BasicAuthLoginRequestHandler handler = new BasicAuthLoginRequestHandler(config);
CaptureChannelHandlerContext ctx = new CaptureChannelHandlerContext();
handler.channelRead(ctx, (BasicAuthLoginRequest) result);
handler.channelRead(ctx, result);
Assert.assertNotNull(ctx.msg);
Assert.assertTrue(ctx.msg instanceof DefaultFullHttpResponse);
DefaultFullHttpResponse response = (DefaultFullHttpResponse) ctx.msg;
@@ -11,6 +11,7 @@ import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import timely.Configuration;
import timely.api.model.Metric;
import timely.api.model.Tag;
import timely.api.request.VersionRequest;
@@ -22,7 +23,7 @@ public class TcpDecoderTest {
@Before
public void setup() {
VisibilityCache.init();
VisibilityCache.init(new Configuration());
}
@Test
@@ -1,9 +1,10 @@
package timely.netty.websocket;
import io.netty.channel.Channel;
import io.netty.channel.local.LocalChannel;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import java.io.File;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
@@ -14,9 +15,7 @@ import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import timely.Configuration;
@@ -32,35 +31,37 @@ import timely.api.request.timeseries.QueryRequest;
import timely.api.request.timeseries.SearchLookupRequest;
import timely.api.request.timeseries.SuggestRequest;
import timely.auth.AuthCache;
import timely.subscription.SubscriptionRegistry;
import timely.test.CaptureChannelHandlerContext;
import timely.test.TestConfiguration;
public class WebSocketRequestDecoderTest {
private static final Long TEST_TIME = System.currentTimeMillis();
public static class WebSocketCaptureChannelHandlerContext extends CaptureChannelHandlerContext {
@ClassRule
public static final TemporaryFolder temp = new TemporaryFolder();
private Channel channel = new LocalChannel();
@Override
public Channel channel() {
return channel;
}
}
private static final Long TEST_TIME = System.currentTimeMillis();
private static Configuration config = null;
private static Configuration anonConfig = null;
private WebSocketRequestDecoder decoder = null;
private List<Object> results = new ArrayList<>();
private static String cookie = null;
private CaptureChannelHandlerContext ctx = new WebSocketCaptureChannelHandlerContext();
@BeforeClass
public static void before() throws Exception {
File conf = temp.newFile("config.properties");
conf.deleteOnExit();
TestConfiguration cfg = TestConfiguration.createMinimalConfigurationForTest();
cfg.toConfiguration(conf);
config = new Configuration(conf);
File conf2 = temp.newFile("anon-config.properties");
conf2.deleteOnExit();
TestConfiguration cfg2 = TestConfiguration.createMinimalConfigurationForTest();
cfg2.put(Configuration.ALLOW_ANONYMOUS_ACCESS, "true");
cfg2.toConfiguration(conf2);
anonConfig = new Configuration(conf2);
config = TestConfiguration.createMinimalConfigurationForTest();
anonConfig = TestConfiguration.createMinimalConfigurationForTest();
anonConfig.getSecurity().setAllowAnonymousAccess(true);
cookie = URLEncoder.encode(UUID.randomUUID().toString(), StandardCharsets.UTF_8.name());
AuthCache.setSessionMaxAge(config);
AuthCache.getCache().put(cookie, new UsernamePasswordAuthenticationToken("test", "test1"));
@@ -74,15 +75,20 @@ public class WebSocketRequestDecoderTest {
@Before
public void setup() throws Exception {
results.clear();
ctx = new WebSocketCaptureChannelHandlerContext();
}
@Test
public void testVersion() throws Exception {
decoder = new WebSocketRequestDecoder(anonConfig);
String request = "{ \"operation\" : \"version\" }";
// @formatter:off
String request = "{ "+
"\"operation\" : \"version\"" +
" }";
// @formatter:on
TextWebSocketFrame frame = new TextWebSocketFrame();
frame.content().writeBytes(request.getBytes(StandardCharsets.UTF_8));
decoder.decode(null, frame, results);
decoder.decode(ctx, frame, results);
Assert.assertEquals(1, results.size());
Assert.assertEquals(VersionRequest.class, results.get(0).getClass());
}
@@ -90,13 +96,24 @@ public class WebSocketRequestDecoderTest {
@Test
public void testPutMetric() throws Exception {
decoder = new WebSocketRequestDecoder(anonConfig);
String request = "{" + "\"operation\" : \"put\",\n" + "\"metric\" : \"sys.cpu.user\",\n" + "\"timestamp\":"
+ TEST_TIME + ",\n" + "\"value\":1.0,\n" + "\"tags\":[ {\n" + "\"key\":\"tag1\",\n"
+ "\"value\":\"value1\"\n" + "},{\n" + "\"key\":\"tag2\",\n" + "\"value\":\"value2\"\n" + "}]\n" + "}";
// @formatter:off
String request = "{" +
"\"operation\" : \"put\",\n" +
"\"metric\" : \"sys.cpu.user\",\n" +
"\"timestamp\":" + TEST_TIME + ",\n" +
"\"value\":1.0,\n" +
"\"tags\":[ {\n" +
"\"key\":\"tag1\",\n" +
"\"value\":\"value1\"\n" +
"},{\n" +
"\"key\":\"tag2\",\n" +
"\"value\":\"value2\"\n" +
"}]\n" +
"}";
// @formatter:on
TextWebSocketFrame frame = new TextWebSocketFrame();
frame.content().writeBytes(request.getBytes(StandardCharsets.UTF_8));
decoder.decode(null, frame, results);
decoder.decode(ctx, frame, results);
Assert.assertEquals(1, results.size());
Assert.assertEquals(Metric.class, results.get(0).getClass());
Metric metric = (Metric) results.iterator().next();
@@ -107,10 +124,34 @@ public class WebSocketRequestDecoderTest {
}
@Test
public void testCreateSubscriptionWithSessionIdAndNonAnonymousAccess() throws Exception {
CaptureChannelHandlerContext ctx = new CaptureChannelHandlerContext();
public void testCreateSubscriptionWithMissingSessionId() throws Exception {
decoder = new WebSocketRequestDecoder(config);
String request = "{ \"operation\" : \"create\", \"sessionId\" : \"1234\" }";
// @formatter:off
String request = "{ "+
"\"operation\" : \"create\", " +
"\"subscriptionId\" : \"1234\"" +
" }";
// @formatter:on
TextWebSocketFrame frame = new TextWebSocketFrame();
frame.content().writeBytes(request.getBytes(StandardCharsets.UTF_8));
decoder.decode(ctx, frame, results);
Assert.assertNotNull(ctx.msg);
Assert.assertEquals(CloseWebSocketFrame.class, ctx.msg.getClass());
Assert.assertEquals(1008, ((CloseWebSocketFrame) ctx.msg).statusCode());
Assert.assertEquals("User must log in", ((CloseWebSocketFrame) ctx.msg).reasonText());
}
@Test
public void testCreateSubscriptionWithInvalidSessionIdAndNonAnonymousAccess() throws Exception {
ctx.channel().attr(SubscriptionRegistry.SESSION_ID_ATTR)
.set(URLEncoder.encode(UUID.randomUUID().toString(), StandardCharsets.UTF_8.name()));
decoder = new WebSocketRequestDecoder(config);
// @formatter:off
String request = "{ "+
"\"operation\" : \"create\", " +
"\"subscriptionId\" : \"1234\"" +
" }";
// @formatter:on
TextWebSocketFrame frame = new TextWebSocketFrame();
frame.content().writeBytes(request.getBytes(StandardCharsets.UTF_8));
decoder.decode(ctx, frame, results);
@@ -122,9 +163,14 @@ public class WebSocketRequestDecoderTest {
@Test
public void testCreateSubscriptionWithValidSessionIdAndNonAnonymousAccess() throws Exception {
CaptureChannelHandlerContext ctx = new CaptureChannelHandlerContext();
ctx.channel().attr(SubscriptionRegistry.SESSION_ID_ATTR).set(cookie);
decoder = new WebSocketRequestDecoder(config);
String request = "{ \"operation\" : \"create\", \"sessionId\" : \"" + cookie + "\" }";
// @formatter:off
String request = "{ " +
"\"operation\" : \"create\"," +
"\"subscriptionId\" : \"" + cookie + "\"" +
"}";
// @formatter:on
TextWebSocketFrame frame = new TextWebSocketFrame();
frame.content().writeBytes(request.getBytes(StandardCharsets.UTF_8));
decoder.decode(ctx, frame, results);
@@ -133,8 +179,7 @@ public class WebSocketRequestDecoderTest {
}
@Test
public void testCreateSubscriptionWithoutSessionId() throws Exception {
CaptureChannelHandlerContext ctx = new CaptureChannelHandlerContext();
public void testCreateSubscriptionWithoutSubscriptionId() throws Exception {
decoder = new WebSocketRequestDecoder(anonConfig);
String request = "{ \"operation\" : \"create\" }";
TextWebSocketFrame frame = new TextWebSocketFrame();
@@ -143,14 +188,13 @@ public class WebSocketRequestDecoderTest {
Assert.assertNotNull(ctx.msg);
Assert.assertEquals(CloseWebSocketFrame.class, ctx.msg.getClass());
Assert.assertEquals(1008, ((CloseWebSocketFrame) ctx.msg).statusCode());
Assert.assertEquals("SessionID is null, must log in first", ((CloseWebSocketFrame) ctx.msg).reasonText());
Assert.assertEquals("Subscription ID is required.", ((CloseWebSocketFrame) ctx.msg).reasonText());
}
@Test
public void testCreateSubscription() throws Exception {
CaptureChannelHandlerContext ctx = new CaptureChannelHandlerContext();
decoder = new WebSocketRequestDecoder(anonConfig);
String request = "{ \"operation\" : \"create\", \"sessionId\" : \"1234\" }";
String request = "{ \"operation\" : \"create\", \"subscriptionId\" : \"1234\" }";
TextWebSocketFrame frame = new TextWebSocketFrame();
frame.content().writeBytes(request.getBytes(StandardCharsets.UTF_8));
decoder.decode(ctx, frame, results);
@@ -162,9 +206,8 @@ public class WebSocketRequestDecoderTest {
@Test
public void testAddSubscription() throws Exception {
CaptureChannelHandlerContext ctx = new CaptureChannelHandlerContext();
decoder = new WebSocketRequestDecoder(anonConfig);
String request = "{ \"operation\" : \"add\", \"sessionId\" : \"1234\" }";
String request = "{ \"operation\" : \"add\", \"subscriptionId\" : \"1234\" }";
TextWebSocketFrame frame = new TextWebSocketFrame();
frame.content().writeBytes(request.getBytes(StandardCharsets.UTF_8));
decoder.decode(ctx, frame, results);
@@ -176,9 +219,8 @@ public class WebSocketRequestDecoderTest {
@Test
public void testRemoveSubscription() throws Exception {
CaptureChannelHandlerContext ctx = new CaptureChannelHandlerContext();
decoder = new WebSocketRequestDecoder(anonConfig);
String request = "{ \"operation\" : \"remove\", \"sessionId\" : \"1234\" }";
String request = "{ \"operation\" : \"remove\", \"subscriptionId\" : \"1234\" }";
TextWebSocketFrame frame = new TextWebSocketFrame();
frame.content().writeBytes(request.getBytes(StandardCharsets.UTF_8));
decoder.decode(ctx, frame, results);
@@ -190,9 +232,8 @@ public class WebSocketRequestDecoderTest {
@Test
public void testCloseSubscription() throws Exception {
CaptureChannelHandlerContext ctx = new CaptureChannelHandlerContext();
decoder = new WebSocketRequestDecoder(anonConfig);
String request = "{ \"operation\" : \"close\", \"sessionId\" : \"1234\" }";
String request = "{ \"operation\" : \"close\", \"subscriptionId\" : \"1234\" }";
TextWebSocketFrame frame = new TextWebSocketFrame();
frame.content().writeBytes(request.getBytes(StandardCharsets.UTF_8));
decoder.decode(ctx, frame, results);
@@ -204,7 +245,6 @@ public class WebSocketRequestDecoderTest {
@Test
public void testAggregrators() throws Exception {
CaptureChannelHandlerContext ctx = new CaptureChannelHandlerContext();
decoder = new WebSocketRequestDecoder(anonConfig);
String request = "{ \"operation\" : \"aggregators\", \"sessionId\" : \"1234\" }";
TextWebSocketFrame frame = new TextWebSocketFrame();
@@ -218,7 +258,6 @@ public class WebSocketRequestDecoderTest {
@Test
public void testMetrics() throws Exception {
CaptureChannelHandlerContext ctx = new CaptureChannelHandlerContext();
decoder = new WebSocketRequestDecoder(anonConfig);
String request = "{ \"operation\" : \"metrics\", \"sessionId\" : \"1234\" }";
TextWebSocketFrame frame = new TextWebSocketFrame();
@@ -275,7 +314,6 @@ public class WebSocketRequestDecoderTest {
" ]\n"+
"}";
// @formatter:on
CaptureChannelHandlerContext ctx = new CaptureChannelHandlerContext();
decoder = new WebSocketRequestDecoder(anonConfig);
TextWebSocketFrame frame = new TextWebSocketFrame();
frame.content().writeBytes(request.getBytes(StandardCharsets.UTF_8));
@@ -289,7 +327,6 @@ public class WebSocketRequestDecoderTest {
@Test
public void testLookup() throws Exception {
String request = "{ \"operation\" : \"lookup\", \"sessionId\" : \"1234\", \"metric\" : \"sys.cpu.user\" }";
CaptureChannelHandlerContext ctx = new CaptureChannelHandlerContext();
decoder = new WebSocketRequestDecoder(anonConfig);
TextWebSocketFrame frame = new TextWebSocketFrame();
frame.content().writeBytes(request.getBytes(StandardCharsets.UTF_8));
@@ -313,7 +350,6 @@ public class WebSocketRequestDecoderTest {
" \"max\": 30\n" +
"}";
// @formatter:on
CaptureChannelHandlerContext ctx = new CaptureChannelHandlerContext();
decoder = new WebSocketRequestDecoder(anonConfig);
TextWebSocketFrame frame = new TextWebSocketFrame();
frame.content().writeBytes(request.getBytes(StandardCharsets.UTF_8));
@@ -23,6 +23,7 @@ import org.apache.accumulo.core.iterators.SortedMapIterator;
import org.junit.Before;
import org.junit.Test;
import timely.Configuration;
import timely.api.model.Metric;
import timely.api.model.Tag;
import timely.auth.VisibilityCache;
@@ -37,7 +38,7 @@ public class DownsampleIteratorTest {
@Before
public void before() {
VisibilityCache.init();
VisibilityCache.init(new Configuration());
}
@Before
@@ -1,37 +1,18 @@
package timely.test;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.Properties;
import timely.Configuration;
import com.google.common.io.Files;
public class TestConfiguration {
public class TestConfiguration extends Properties {
private static final long serialVersionUID = 1L;
public static final String TIMELY_HTTP_ADDRESS_DEFAULT = "localhost";
public Configuration toConfiguration(File propertiesFile) throws Exception {
this.store(Files.newWriter(propertiesFile, StandardCharsets.UTF_8), null);
return new Configuration(propertiesFile);
}
public static TestConfiguration createMinimalConfigurationForTest() {
TestConfiguration cfg = new TestConfiguration();
cfg.put(Configuration.IP, "127.0.0.1");
cfg.put(Configuration.PUT_PORT, "54321");
cfg.put(Configuration.QUERY_PORT, "54322");
cfg.put(Configuration.WEBSOCKET_PORT, "54323");
cfg.put(Configuration.ZOOKEEPERS, "localhost:2181");
cfg.put(Configuration.INSTANCE_NAME, "test");
cfg.put(Configuration.USERNAME, "root");
cfg.put(Configuration.PASSWORD, "secret");
cfg.put(Configuration.TIMELY_HTTP_HOST, "localhost");
cfg.put(Configuration.SSL_USE_GENERATED_KEYPAIR, "true");
cfg.put(Configuration.MAX_LATENCY, "2s");
cfg.put(Configuration.WS_TIMEOUT_SECONDS, "20");
public static Configuration createMinimalConfigurationForTest() {
Configuration cfg = new Configuration().getServer().setIp("127.0.0.1").getServer().setTcpPort(54321).getHttp()
.setIp("127.0.0.1").getHttp().setPort(54322).getWebsocket().setIp("127.0.0.1").getWebsocket()
.setPort(54323).getAccumulo().setZookeepers("localhost:2181").getAccumulo().setInstanceName("test")
.getAccumulo().setUsername("root").getAccumulo().setPassword("secret").getHttp().setHost("localhost")
.getSecurity().getSsl().setUseGeneratedKeypair(true).getAccumulo().getWrite().setLatency("2s")
.getWebsocket().setTimeout(20);
return cfg;
}
@@ -11,10 +11,8 @@ import io.netty.handler.ssl.util.SelfSignedCertificate;
import java.io.File;
import java.net.URL;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import org.apache.accumulo.core.client.Connector;
@@ -41,9 +39,9 @@ public class OneWaySSLBase extends QueryBase {
@ClassRule
public static final TemporaryFolder temp = new TemporaryFolder();
private static MiniAccumuloCluster mac = null;
protected static File conf = null;
private static File clientTrustStoreFile = null;
protected static MiniAccumuloCluster mac = null;
protected static Configuration conf = null;
protected static File clientTrustStoreFile = null;
protected SSLSocketFactory getSSLSocketFactory() throws Exception {
SslContextBuilder builder = SslContextBuilder.forClient();
@@ -57,14 +55,14 @@ public class OneWaySSLBase extends QueryBase {
return jdkSslContext.getSocketFactory();
}
protected static void setupSSL(TestConfiguration config) throws Exception {
protected static void setupSSL(Configuration config) throws Exception {
SelfSignedCertificate serverCert = new SelfSignedCertificate();
config.put(Configuration.SSL_CERTIFICATE_FILE, serverCert.certificate().getAbsolutePath());
config.getSecurity().getSsl().setCertificateFile(serverCert.certificate().getAbsolutePath());
clientTrustStoreFile = serverCert.certificate().getAbsoluteFile();
config.put(Configuration.SSL_PRIVATE_KEY_FILE, serverCert.privateKey().getAbsolutePath());
config.put(Configuration.SSL_USE_OPENSSL, "false");
config.put(Configuration.SSL_USE_GENERATED_KEYPAIR, "false");
config.put(Configuration.ALLOW_ANONYMOUS_ACCESS, "true");
config.getSecurity().getSsl().setKeyFile(serverCert.privateKey().getAbsolutePath());
config.getSecurity().getSsl().setUseOpenssl(false);
config.getSecurity().getSsl().setUseGeneratedKeypair(false);
config.getSecurity().setAllowAnonymousAccess(true);
}
@Override
@@ -76,28 +74,19 @@ public class OneWaySSLBase extends QueryBase {
protected HttpsURLConnection getUrlConnection(URL url) throws Exception {
HttpsURLConnection.setDefaultSSLSocketFactory(getSSLSocketFactory());
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
con.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
con.setHostnameVerifier((host, session) -> true);
return con;
}
@BeforeClass
public static void beforeClass() throws Exception {
temp.create();
final MiniAccumuloConfig macConfig = new MiniAccumuloConfig(temp.newFolder("mac"), "secret");
mac = new MiniAccumuloCluster(macConfig);
mac.start();
conf = temp.newFile("config.properties");
TestConfiguration config = TestConfiguration.createMinimalConfigurationForTest();
config.put(Configuration.INSTANCE_NAME, mac.getInstanceName());
config.put(Configuration.ZOOKEEPERS, mac.getZooKeepers());
setupSSL(config);
config.toConfiguration(conf);
conf = TestConfiguration.createMinimalConfigurationForTest();
conf.getAccumulo().setInstanceName(mac.getInstanceName());
conf.getAccumulo().setZookeepers(mac.getZooKeepers());
setupSSL(conf);
}
@AfterClass
@@ -6,15 +6,7 @@ import static org.junit.Assert.assertEquals;
import io.netty.handler.codec.http.HttpHeaders.Names;
import io.netty.handler.codec.http.cookie.ClientCookieDecoder;
import io.netty.handler.codec.http.cookie.Cookie;
import io.netty.handler.ssl.ApplicationProtocolConfig;
import io.netty.handler.ssl.JdkSslClientContext;
import io.netty.handler.ssl.JdkSslContext;
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 java.io.File;
import java.io.OutputStream;
import java.net.URL;
import java.util.List;
@@ -22,27 +14,18 @@ import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import org.apache.accumulo.core.client.Connector;
import org.apache.accumulo.core.security.Authorizations;
import org.apache.accumulo.minicluster.MiniAccumuloCluster;
import org.apache.accumulo.minicluster.MiniAccumuloConfig;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.rules.TemporaryFolder;
import timely.Configuration;
import timely.Server;
import timely.api.request.auth.BasicAuthLoginRequest;
import timely.api.request.timeseries.QueryRequest;
@@ -50,7 +33,6 @@ import timely.api.response.timeseries.QueryResponse;
import timely.auth.AuthCache;
import timely.netty.Constants;
import timely.test.IntegrationTest;
import timely.test.TestConfiguration;
import timely.util.JsonUtil;
/**
@@ -64,36 +46,6 @@ public class OneWaySSLBasicAuthAccessIT extends OneWaySSLBase {
private static final Long TEST_TIME = System.currentTimeMillis();
@ClassRule
public static final TemporaryFolder temp = new TemporaryFolder();
private static MiniAccumuloCluster mac = null;
private static File conf = null;
private static File clientTrustStoreFile = null;
protected SSLSocketFactory getSSLSocketFactory() throws Exception {
SslContextBuilder builder = SslContextBuilder.forClient();
builder.applicationProtocolConfig(ApplicationProtocolConfig.DISABLED);
builder.sslProvider(SslProvider.JDK);
builder.trustManager(clientTrustStoreFile); // Trust the server cert
SslContext ctx = builder.build();
Assert.assertEquals(JdkSslClientContext.class, ctx.getClass());
JdkSslContext jdk = (JdkSslContext) ctx;
SSLContext jdkSslContext = jdk.context();
return jdkSslContext.getSocketFactory();
}
protected static void setupSSL(TestConfiguration config) throws Exception {
SelfSignedCertificate serverCert = new SelfSignedCertificate();
config.put(Configuration.SSL_CERTIFICATE_FILE, serverCert.certificate().getAbsolutePath());
clientTrustStoreFile = serverCert.certificate().getAbsoluteFile();
config.put(Configuration.SSL_PRIVATE_KEY_FILE, serverCert.privateKey().getAbsolutePath());
config.put(Configuration.SSL_USE_OPENSSL, "false");
config.put(Configuration.SSL_USE_GENERATED_KEYPAIR, "false");
config.put(Configuration.ALLOW_ANONYMOUS_ACCESS, "false");
}
protected HttpsURLConnection getUrlConnection(URL url) throws Exception {
// Username and password are set in src/test/resources/security.xml
return getUrlConnection("test", "test1", url);
@@ -103,13 +55,7 @@ public class OneWaySSLBasicAuthAccessIT extends OneWaySSLBase {
HttpsURLConnection.setDefaultSSLSocketFactory(getSSLSocketFactory());
URL loginURL = new URL(url.getProtocol() + "://" + url.getHost() + ":" + url.getPort() + "/login");
HttpsURLConnection con = (HttpsURLConnection) loginURL.openConnection();
con.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
con.setHostnameVerifier((host, session) -> true);
con.setRequestMethod("POST");
con.setDoOutput(true);
con.setRequestProperty("Content-Type", "application/json");
@@ -131,47 +77,25 @@ public class OneWaySSLBasicAuthAccessIT extends OneWaySSLBase {
Assert.assertEquals(Constants.COOKIE_NAME, sessionCookie.name());
con = (HttpsURLConnection) url.openConnection();
con.setRequestProperty(Names.COOKIE, sessionCookie.name() + "=" + sessionCookie.value());
con.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
con.setHostnameVerifier((host, session) -> true);
return con;
}
@BeforeClass
public static void beforeClass() throws Exception {
temp.create();
final MiniAccumuloConfig macConfig = new MiniAccumuloConfig(temp.newFolder("mac"), "secret");
mac = new MiniAccumuloCluster(macConfig);
mac.start();
conf = temp.newFile("config.properties");
TestConfiguration config = TestConfiguration.createMinimalConfigurationForTest();
config.put(Configuration.INSTANCE_NAME, mac.getInstanceName());
config.put(Configuration.ZOOKEEPERS, mac.getZooKeepers());
setupSSL(config);
config.toConfiguration(conf);
OneWaySSLBase.beforeClass();
}
@AfterClass
public static void afterClass() throws Exception {
mac.stop();
OneWaySSLBase.afterClass();
}
@Before
public void setup() throws Exception {
super.setup();
Connector con = mac.getConnector("root", "secret");
con.securityOperations().changeUserAuthorizations("root", new Authorizations("A", "B", "C", "D", "E", "F"));
con.tableOperations().list().forEach(t -> {
if (t.startsWith("timely")) {
try {
con.tableOperations().delete(t);
} catch (Exception e) {
}
}
});
}
@After
@@ -33,8 +33,8 @@ import org.junit.ClassRule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import timely.Configuration;
import timely.Server;
import timely.Configuration;
import timely.auth.AuthCache;
import timely.netty.Constants;
import timely.test.TestConfiguration;
@@ -46,7 +46,7 @@ public class TwoWaySSLFailureIT extends QueryBase {
public static final TemporaryFolder temp = new TemporaryFolder();
private static MiniAccumuloCluster mac = null;
private static File conf = null;
private static Configuration conf = null;
private static SelfSignedCertificate serverCert = null;
private static File clientTrustStoreFile = null;
@@ -75,14 +75,14 @@ public class TwoWaySSLFailureIT extends QueryBase {
return jdkSslContext.getSocketFactory();
}
protected static void setupSSL(TestConfiguration config) throws Exception {
config.put(Configuration.SSL_CERTIFICATE_FILE, serverCert.certificate().getAbsolutePath());
config.put(Configuration.SSL_PRIVATE_KEY_FILE, serverCert.privateKey().getAbsolutePath());
protected static void setupSSL(Configuration config) throws Exception {
config.getSecurity().getSsl().setCertificateFile(serverCert.certificate().getAbsolutePath());
config.getSecurity().getSsl().setKeyFile(serverCert.privateKey().getAbsolutePath());
// Needed for 2way SSL
config.put(Configuration.SSL_TRUST_STORE_FILE, serverCert.certificate().getAbsolutePath());
config.put(Configuration.SSL_USE_OPENSSL, "false");
config.put(Configuration.SSL_USE_GENERATED_KEYPAIR, "false");
config.put(Configuration.ALLOW_ANONYMOUS_ACCESS, "false");
config.getSecurity().getSsl().setTrustStoreFile(serverCert.certificate().getAbsolutePath());
config.getSecurity().getSsl().setUseOpenssl(false);
config.getSecurity().getSsl().setUseGeneratedKeypair(false);
config.getSecurity().setAllowAnonymousAccess(false);
}
protected HttpsURLConnection getUrlConnection(URL url) throws Exception {
@@ -132,12 +132,10 @@ public class TwoWaySSLFailureIT extends QueryBase {
final MiniAccumuloConfig macConfig = new MiniAccumuloConfig(temp.newFolder("mac"), "secret");
mac = new MiniAccumuloCluster(macConfig);
mac.start();
conf = temp.newFile("config.properties");
TestConfiguration config = TestConfiguration.createMinimalConfigurationForTest();
config.put(Configuration.INSTANCE_NAME, mac.getInstanceName());
config.put(Configuration.ZOOKEEPERS, mac.getZooKeepers());
setupSSL(config);
config.toConfiguration(conf);
conf = TestConfiguration.createMinimalConfigurationForTest();
conf.getAccumulo().setInstanceName(mac.getInstanceName());
conf.getAccumulo().setZookeepers(mac.getZooKeepers());
setupSSL(conf);
}
@AfterClass
@@ -18,10 +18,8 @@ import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import org.apache.accumulo.core.client.Connector;
@@ -38,8 +36,8 @@ import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.rules.TemporaryFolder;
import timely.Configuration;
import timely.Server;
import timely.Configuration;
import timely.api.request.timeseries.QueryRequest;
import timely.api.response.timeseries.QueryResponse;
import timely.auth.AuthCache;
@@ -64,7 +62,7 @@ public class TwoWaySSLIT extends QueryBase {
public static final TemporaryFolder temp = new TemporaryFolder();
private static MiniAccumuloCluster mac = null;
private static File conf = null;
private static Configuration conf = null;
protected static SelfSignedCertificate serverCert = null;
protected static File clientTrustStoreFile = null;
@@ -92,14 +90,14 @@ public class TwoWaySSLIT extends QueryBase {
return jdkSslContext.getSocketFactory();
}
protected static void setupSSL(TestConfiguration config) throws Exception {
config.put(Configuration.SSL_CERTIFICATE_FILE, serverCert.certificate().getAbsolutePath());
config.put(Configuration.SSL_PRIVATE_KEY_FILE, serverCert.privateKey().getAbsolutePath());
protected static void setupSSL(Configuration config) throws Exception {
config.getSecurity().getSsl().setCertificateFile(serverCert.certificate().getAbsolutePath());
config.getSecurity().getSsl().setKeyFile(serverCert.privateKey().getAbsolutePath());
// Needed for 2way SSL
config.put(Configuration.SSL_TRUST_STORE_FILE, serverCert.certificate().getAbsolutePath());
config.put(Configuration.SSL_USE_OPENSSL, "false");
config.put(Configuration.SSL_USE_GENERATED_KEYPAIR, "false");
config.put(Configuration.ALLOW_ANONYMOUS_ACCESS, "false");
config.getSecurity().getSsl().setTrustStoreFile(serverCert.certificate().getAbsolutePath());
config.getSecurity().getSsl().setUseOpenssl(false);
config.getSecurity().getSsl().setUseGeneratedKeypair(false);
config.getSecurity().setAllowAnonymousAccess(false);
}
protected HttpsURLConnection getUrlConnection(URL url) throws Exception {
@@ -111,13 +109,7 @@ public class TwoWaySSLIT extends QueryBase {
HttpsURLConnection.setDefaultSSLSocketFactory(getSSLSocketFactory());
URL loginURL = new URL(url.getProtocol() + "://" + url.getHost() + ":" + url.getPort() + "/login");
HttpsURLConnection con = (HttpsURLConnection) loginURL.openConnection();
con.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
con.setHostnameVerifier((host, session) -> true);
con.setRequestMethod("GET");
con.setDoOutput(true);
con.setRequestProperty("Content-Type", "application/json");
@@ -133,28 +125,19 @@ public class TwoWaySSLIT extends QueryBase {
Assert.assertEquals(Constants.COOKIE_NAME, sessionCookie.name());
con = (HttpsURLConnection) url.openConnection();
con.setRequestProperty(Names.COOKIE, sessionCookie.name() + "=" + sessionCookie.value());
con.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
con.setHostnameVerifier((host, session) -> true);
return con;
}
@BeforeClass
public static void beforeClass() throws Exception {
temp.create();
final MiniAccumuloConfig macConfig = new MiniAccumuloConfig(temp.newFolder("mac"), "secret");
mac = new MiniAccumuloCluster(macConfig);
mac.start();
conf = temp.newFile("config.properties");
TestConfiguration config = TestConfiguration.createMinimalConfigurationForTest();
config.put(Configuration.INSTANCE_NAME, mac.getInstanceName());
config.put(Configuration.ZOOKEEPERS, mac.getZooKeepers());
setupSSL(config);
config.toConfiguration(conf);
conf = TestConfiguration.createMinimalConfigurationForTest();
conf.getAccumulo().setInstanceName(mac.getInstanceName());
conf.getAccumulo().setZookeepers(mac.getZooKeepers());
setupSSL(conf);
}
@AfterClass
@@ -38,8 +38,8 @@ import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.rules.TemporaryFolder;
import timely.Configuration;
import timely.Server;
import timely.Configuration;
import timely.api.request.timeseries.QueryRequest;
import timely.api.response.timeseries.QueryResponse;
import timely.auth.AuthCache;
@@ -65,7 +65,7 @@ public class TwoWaySSLOpenSSLIT extends QueryBase {
public static final TemporaryFolder temp = new TemporaryFolder();
private static MiniAccumuloCluster mac = null;
private static File conf = null;
private static Configuration conf = null;
protected static SelfSignedCertificate serverCert = null;
protected static File clientTrustStoreFile = null;
@@ -93,14 +93,14 @@ public class TwoWaySSLOpenSSLIT extends QueryBase {
return jdkSslContext.getSocketFactory();
}
protected static void setupSSL(TestConfiguration config) throws Exception {
config.put(Configuration.SSL_CERTIFICATE_FILE, serverCert.certificate().getAbsolutePath());
config.put(Configuration.SSL_PRIVATE_KEY_FILE, serverCert.privateKey().getAbsolutePath());
protected static void setupSSL(Configuration config) throws Exception {
config.getSecurity().getSsl().setCertificateFile(serverCert.certificate().getAbsolutePath());
config.getSecurity().getSsl().setKeyFile(serverCert.privateKey().getAbsolutePath());
// Needed for 2way SSL
config.put(Configuration.SSL_TRUST_STORE_FILE, serverCert.certificate().getAbsolutePath());
config.put(Configuration.SSL_USE_OPENSSL, "true");
config.put(Configuration.SSL_USE_GENERATED_KEYPAIR, "false");
config.put(Configuration.ALLOW_ANONYMOUS_ACCESS, "false");
config.getSecurity().getSsl().setTrustStoreFile(serverCert.certificate().getAbsolutePath());
config.getSecurity().getSsl().setUseOpenssl(false);
config.getSecurity().getSsl().setUseGeneratedKeypair(false);
config.getSecurity().setAllowAnonymousAccess(false);
}
protected HttpsURLConnection getUrlConnection(URL url) throws Exception {
@@ -150,12 +150,10 @@ public class TwoWaySSLOpenSSLIT extends QueryBase {
final MiniAccumuloConfig macConfig = new MiniAccumuloConfig(temp.newFolder("mac"), "secret");
mac = new MiniAccumuloCluster(macConfig);
mac.start();
conf = temp.newFile("config.properties");
TestConfiguration config = TestConfiguration.createMinimalConfigurationForTest();
config.put(Configuration.INSTANCE_NAME, mac.getInstanceName());
config.put(Configuration.ZOOKEEPERS, mac.getZooKeepers());
setupSSL(config);
config.toConfiguration(conf);
conf = TestConfiguration.createMinimalConfigurationForTest();
conf.getAccumulo().setInstanceName(mac.getInstanceName());
conf.getAccumulo().setZookeepers(mac.getZooKeepers());
setupSSL(conf);
}
@AfterClass
@@ -131,20 +131,20 @@ public class HttpApiIT extends OneWaySSLBase {
String suggest = "https://localhost:54322/api/search/lookup?";
// Test a known query
String result = query(suggest + "m=sys.cpu.idle%7Btag3%3D*%7D");
String result = query(suggest + "m=sys.cpu.idle%7Btag3%3D.*%7D");
assertTrue(result, result.indexOf("\"results\":[{\"tags\":{\"tag3\":\"value3\"}") >= 0);
// Test a fail
result = query(suggest + "m=sys.cpu.idle%7Btag3%3Dstupid%7D");
result = query(suggest + "m=sys.cpu.idle%7Btag3%3Dnomatch%7D");
assertTrue(result.indexOf("\"results\":[]") >= 0);
// Test multiple results
result = query(suggest + "m=sys.cpu.idle%7Btag3%3D*,tag4%3D*%7D");
result = query(suggest + "m=sys.cpu.idle%7Btag3%3D.*,tag4%3D.*%7D");
assertTrue(result, result.indexOf("\"results\":[{\"tags\":{\"tag3\":\"value3\"}") >= 0);
assertTrue(result, result.indexOf("{\"tags\":{\"tag4\":\"value4\"}") >= 0);
// Find a tag only in the metric that matches
result = query(suggest + "m=sys.cpu.idle%7Btag1%3D*%7D");
result = query(suggest + "m=sys.cpu.idle%7Btag1%3D.*%7D");
assertTrue(result, result.indexOf("\"results\":[]") >= 0);
} finally {
@@ -6,7 +6,6 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
@@ -39,9 +38,9 @@ import org.junit.rules.TemporaryFolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import timely.Configuration;
import timely.Server;
import timely.TestServer;
import timely.Configuration;
import timely.api.model.Metric;
import timely.api.model.Tag;
import timely.api.request.VersionRequest;
@@ -62,20 +61,18 @@ public class TimelyTcpIT {
public static final TemporaryFolder temp = new TemporaryFolder();
private static MiniAccumuloCluster mac = null;
private static File conf = null;
private static Configuration conf = null;
@BeforeClass
public static void beforeClass() throws Exception {
temp.create();
final MiniAccumuloConfig macConfig = new MiniAccumuloConfig(temp.newFolder("mac"), "secret");
mac = new MiniAccumuloCluster(macConfig);
mac.start();
conf = temp.newFile("config.properties");
TestConfiguration config = TestConfiguration.createMinimalConfigurationForTest();
config.put(Configuration.INSTANCE_NAME, mac.getInstanceName());
config.put(Configuration.ZOOKEEPERS, mac.getZooKeepers());
config.put(Configuration.SSL_USE_GENERATED_KEYPAIR, "true");
config.toConfiguration(conf);
conf = TestConfiguration.createMinimalConfigurationForTest();
conf.getAccumulo().setInstanceName(mac.getInstanceName());
conf.getAccumulo().setZookeepers(mac.getZooKeepers());
conf.getSecurity().getSsl().setUseOpenssl(false);
conf.getSecurity().getSsl().setUseGeneratedKeypair(true);
}
@AfterClass
@@ -0,0 +1,110 @@
package timely.test.integration.websocket;
import io.netty.handler.ssl.SslContextBuilder;
import org.apache.accumulo.core.client.Connector;
import org.apache.accumulo.minicluster.MiniAccumuloCluster;
import org.apache.accumulo.minicluster.MiniAccumuloConfig;
import org.junit.*;
import org.junit.experimental.categories.Category;
import org.junit.rules.TemporaryFolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import rx.Observable;
import timely.Configuration;
import timely.TestServer;
import timely.auth.AuthCache;
import timely.test.IntegrationTest;
import timely.test.TestConfiguration;
import ws.wamp.jawampa.Reply;
import ws.wamp.jawampa.WampClient;
import ws.wamp.jawampa.WampClientBuilder;
import ws.wamp.jawampa.connection.IWampConnectorProvider;
import ws.wamp.jawampa.transport.netty.NettyWampClientConnectorProvider;
import ws.wamp.jawampa.transport.netty.NettyWampConnectionConfig;
import java.util.concurrent.TimeUnit;
/**
* Integration tests for the operations available over WAMP
*/
@Category(IntegrationTest.class)
public class WampIT {
private static final Logger LOG = LoggerFactory.getLogger(WampIT.class);
private static final Long TEST_TIME = System.currentTimeMillis();
@ClassRule
public static final TemporaryFolder temp = new TemporaryFolder();
private static MiniAccumuloCluster mac = null;
private static Configuration conf = null;
private static TestServer server = null;
@BeforeClass
public static void beforeClass() throws Exception {
final MiniAccumuloConfig macConfig = new MiniAccumuloConfig(temp.newFolder("mac"), "secret");
mac = new MiniAccumuloCluster(macConfig);
mac.start();
conf = TestConfiguration.createMinimalConfigurationForTest();
conf.getAccumulo().setInstanceName(mac.getInstanceName());
conf.getAccumulo().setZookeepers(mac.getZooKeepers());
conf.getSecurity().getSsl().setUseOpenssl(false);
conf.getSecurity().getSsl().setUseGeneratedKeypair(true);
server = new TestServer(conf);
}
@AfterClass
public static void afterClass() throws Exception {
server.shutdown();
mac.stop();
}
@Before
public void setup() throws Exception {
Connector con = mac.getConnector("root", "secret");
con.tableOperations().list().forEach(t -> {
if (t.startsWith("timely")) {
try {
con.tableOperations().delete(t);
} catch (Exception e) {
}
}
});
}
@After
public void tearDown() throws Exception {
AuthCache.resetSessionMaxAge();
}
@Test
public void testVersion() throws Exception {
//final TestServer m = new TestServer(conf);
final WampClient client;
IWampConnectorProvider connectorProvider = new NettyWampClientConnectorProvider();
NettyWampConnectionConfig connectionConfiguration = new NettyWampConnectionConfig.Builder()
.withSslContext(SslContextBuilder.forClient().build())
.build();
WampClientBuilder builder = new WampClientBuilder();
builder.withConnectorProvider(connectorProvider)
.withConnectionConfiguration(connectionConfiguration)
.withUri("wss://localhost:54323/wamp")
.withRealm("default")
.withInfiniteReconnects()
.withReconnectInterval(5, TimeUnit.SECONDS);
// Create a client through the builder. This will not immediately start
// a connection attempt
client = builder.build();
client.open();
Observable<Reply> oReply = client.call("timely.version");
Reply reply = oReply.toBlocking().last();
System.out.println(reply.toString());
}
}
@@ -17,7 +17,9 @@ import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpHeaders.Names;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.cookie.ClientCookieEncoder;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
@@ -34,6 +36,7 @@ import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import org.apache.accumulo.core.util.UtilWaitThread;
@@ -63,6 +66,7 @@ import timely.api.response.timeseries.SearchLookupResponse;
import timely.api.response.timeseries.SearchLookupResponse.Result;
import timely.api.response.timeseries.SuggestResponse;
import timely.auth.AuthCache;
import timely.netty.Constants;
import timely.test.IntegrationTest;
import timely.test.integration.OneWaySSLBase;
import timely.util.JsonUtil;
@@ -176,15 +180,22 @@ public class WebSocketIT extends OneWaySSLBase {
private Channel ch = null;
private ClientHandler handler = null;
private Server s = null;
private String sessionId = null;
@Before
public void setup() throws Exception {
s = new Server(conf);
AuthCache.getCache().put("1234", new UsernamePasswordAuthenticationToken("test", "test1"));
this.sessionId = UUID.randomUUID().toString();
AuthCache.getCache().put(sessionId, new UsernamePasswordAuthenticationToken("test", "test1"));
group = new NioEventLoopGroup();
SslContext ssl = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();
String cookieVal = ClientCookieEncoder.STRICT.encode(Constants.COOKIE_NAME, sessionId);
HttpHeaders headers = new DefaultHttpHeaders();
headers.add(Names.COOKIE, cookieVal);
WebSocketClientHandshaker handshaker = WebSocketClientHandshakerFactory.newHandshaker(LOCATION,
WebSocketVersion.V13, (String) null, false, (HttpHeaders) new DefaultHttpHeaders());
WebSocketVersion.V13, (String) null, false, headers);
handler = new ClientHandler(handshaker);
Bootstrap boot = new Bootstrap();
boot.group(group).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {
@@ -201,15 +212,16 @@ public class WebSocketIT extends OneWaySSLBase {
// Wait until handshake is complete
while (!handshaker.isHandshakeComplete()) {
UtilWaitThread.sleep(500L);
LOG.debug("Waiting for Handshake to complete");
}
}
@Test
public void testClientDisappears() throws Exception {
try {
final String sessionId = "1235";
final String subscriptionId = "1235";
CreateSubscription c = new CreateSubscription();
c.setSessionId(sessionId);
c.setSubscriptionId(subscriptionId);
ch.writeAndFlush(new TextWebSocketFrame(JsonUtil.getObjectMapper().writeValueAsString(c)));
// Add some data
@@ -223,7 +235,7 @@ public class WebSocketIT extends OneWaySSLBase {
// Add subscription, confirm data
AddSubscription add = new AddSubscription();
add.setSessionId(sessionId);
add.setSubscriptionId(subscriptionId);
add.setMetric("sys.cpu.user");
add.setDelayTime(1000L);
ch.writeAndFlush(new TextWebSocketFrame(JsonUtil.getObjectMapper().writeValueAsString(add)));
@@ -254,7 +266,8 @@ public class WebSocketIT extends OneWaySSLBase {
second.setTags(secondTags);
for (String metrics : response) {
MetricResponse m = JsonUtil.getObjectMapper().readValue(metrics, MetricResponse.class);
Assert.assertTrue(m.equals(first.toMetricResponse()) || m.equals(second.toMetricResponse()));
Assert.assertTrue(m.equals(first.toMetricResponse(subscriptionId))
|| m.equals(second.toMetricResponse(subscriptionId)));
}
} finally {
ch.close().sync();
@@ -266,9 +279,9 @@ public class WebSocketIT extends OneWaySSLBase {
@Test
public void testSubscriptionWorkflow() throws Exception {
try {
final String sessionId = "1234";
final String subscriptionId = "1234";
CreateSubscription c = new CreateSubscription();
c.setSessionId(sessionId);
c.setSubscriptionId(subscriptionId);
ch.writeAndFlush(new TextWebSocketFrame(JsonUtil.getObjectMapper().writeValueAsString(c)));
// Add some data
@@ -282,7 +295,7 @@ public class WebSocketIT extends OneWaySSLBase {
// Add subscription, confirm data
AddSubscription add = new AddSubscription();
add.setSessionId(sessionId);
add.setSubscriptionId(subscriptionId);
add.setMetric("sys.cpu.user");
add.setDelayTime(1000L);
ch.writeAndFlush(new TextWebSocketFrame(JsonUtil.getObjectMapper().writeValueAsString(add)));
@@ -313,7 +326,8 @@ public class WebSocketIT extends OneWaySSLBase {
second.setTags(secondTags);
for (String metrics : response) {
MetricResponse m = JsonUtil.getObjectMapper().readValue(metrics, MetricResponse.class);
Assert.assertTrue(m.equals(first.toMetricResponse()) || m.equals(second.toMetricResponse()));
Assert.assertTrue(m.equals(first.toMetricResponse(subscriptionId))
|| m.equals(second.toMetricResponse(subscriptionId)));
}
// Add some more data
@@ -352,12 +366,13 @@ public class WebSocketIT extends OneWaySSLBase {
second.setTags(secondTags);
for (String metrics : response) {
MetricResponse m = JsonUtil.getObjectMapper().readValue(metrics, MetricResponse.class);
Assert.assertTrue(m.equals(first.toMetricResponse()) || m.equals(second.toMetricResponse()));
Assert.assertTrue(m.equals(first.toMetricResponse(subscriptionId))
|| m.equals(second.toMetricResponse(subscriptionId)));
}
// Add subscription
AddSubscription add2 = new AddSubscription();
add2.setSessionId(sessionId);
add2.setSubscriptionId(subscriptionId);
add2.setMetric("sys.cpu.idle");
add2.setDelayTime(1000L);
ch.writeAndFlush(new TextWebSocketFrame(JsonUtil.getObjectMapper().writeValueAsString(add2)));
@@ -410,23 +425,25 @@ public class WebSocketIT extends OneWaySSLBase {
fourth.setTags(fourthTags);
for (String metrics : response) {
MetricResponse m = JsonUtil.getObjectMapper().readValue(metrics, MetricResponse.class);
Assert.assertTrue(m.equals(first.toMetricResponse()) || m.equals(second.toMetricResponse())
|| m.equals(third.toMetricResponse()) || m.equals(fourth.toMetricResponse()));
Assert.assertTrue(m.equals(first.toMetricResponse(subscriptionId))
|| m.equals(second.toMetricResponse(subscriptionId))
|| m.equals(third.toMetricResponse(subscriptionId))
|| m.equals(fourth.toMetricResponse(subscriptionId)));
}
// Remove subscriptions to metric
RemoveSubscription remove1 = new RemoveSubscription();
remove1.setSessionId(sessionId);
remove1.setSubscriptionId(subscriptionId);
remove1.setMetric("sys.cpu.user");
ch.writeAndFlush(new TextWebSocketFrame(JsonUtil.getObjectMapper().writeValueAsString(remove1)));
RemoveSubscription remove2 = new RemoveSubscription();
remove2.setSessionId(sessionId);
remove2.setSubscriptionId(subscriptionId);
remove2.setMetric("sys.cpu.idle");
ch.writeAndFlush(new TextWebSocketFrame(JsonUtil.getObjectMapper().writeValueAsString(remove2)));
// Close subscription
CloseSubscription close = new CloseSubscription();
close.setSessionId(sessionId);
close.setSubscriptionId(subscriptionId);
ch.writeAndFlush(new TextWebSocketFrame(JsonUtil.getObjectMapper().writeValueAsString(close)));
} finally {
ch.close().sync();
@@ -439,7 +456,6 @@ public class WebSocketIT extends OneWaySSLBase {
public void testWSAggregators() throws Exception {
try {
AggregatorsRequest request = new AggregatorsRequest();
request.setSessionId("1234");
ch.writeAndFlush(new TextWebSocketFrame(JsonUtil.getObjectMapper().writeValueAsString(request)));
// Latency in TestConfiguration is 2s, wait for it
sleepUninterruptibly(4, TimeUnit.SECONDS);
@@ -464,7 +480,6 @@ public class WebSocketIT extends OneWaySSLBase {
public void testWSMetrics() throws Exception {
try {
MetricsRequest request = new MetricsRequest();
request.setSessionId("1234");
ch.writeAndFlush(new TextWebSocketFrame(JsonUtil.getObjectMapper().writeValueAsString(request)));
// Confirm receipt of all data sent to this point
@@ -496,7 +511,6 @@ public class WebSocketIT extends OneWaySSLBase {
String request =
"{"+
" \"operation\" : \"query\","+
" \"sessionId\" : \"1234\","+
" \"start\": "+TEST_TIME+","+
" \"end\": "+(TEST_TIME+6000)+","+
" \"queries\": ["+
@@ -557,10 +571,9 @@ public class WebSocketIT extends OneWaySSLBase {
String request =
"{"+
" \"operation\" : \"lookup\","+
" \"sessionId\" : \"1234\","+
" \"metric\" : \"sys.cpu.idle\","+
" \"tags\" : ["+
"\"tag3=*\""+
"\"tag3=.*\""+
"]"+
"}";
// @formatter:on
@@ -600,7 +613,6 @@ public class WebSocketIT extends OneWaySSLBase {
String request =
"{\n" +
" \"operation\" : \"suggest\",\n" +
" \"sessionId\" : \"1234\",\n" +
" \"type\": \"metrics\",\n" +
" \"q\": \"sys.cpu.user\",\n" +
" \"max\": 30\n" +