@alertlogic/al-collector-js 3.0.1 → 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,56 @@
1
+ #!/bin/bash
2
+
3
+ # Create ps_outputs repository
4
+ DIR=".ps_outputs/"
5
+ if [[ -d "$DIR" ]]; then
6
+ rm -rf $DIR
7
+ fi
8
+ mkdir $DIR
9
+
10
+ showUssage()
11
+ {
12
+ echo ""
13
+ echo "Usage: $0 -c [ COVERAGE_FILES ] -b [ UNITESTS_FILES ]"
14
+ echo -e "\t-c List of coverage reports to collect, i.e. '*.coverage.xml,coverage.xml,*.covertool.xml'"
15
+ echo -e "\t-u List of unit test reports to collect, i.e. 'junit.xml,junit_report.xml'"
16
+ }
17
+
18
+ while getopts "c:u:" opt
19
+ do
20
+ case "$opt" in
21
+ c ) coverageFiles="$OPTARG" ;;
22
+ u ) unitTestFiles="$OPTARG" ;;
23
+ ? ) showUssage ;;
24
+ esac
25
+ done
26
+
27
+ if [ -z "$coverageFiles" ] && [ -z "$unitTestFiles" ]
28
+ then
29
+ echo "Please provide one parameter at least";
30
+ showUssage
31
+ exit 1
32
+ fi
33
+
34
+ # Split coverageFiles and collect
35
+ if [ "$coverageFiles" != "" ]; then
36
+ IFS=',' read -r -a coverage_reports <<< "$coverageFiles"
37
+ for element in "${coverage_reports[@]}"
38
+ do
39
+ find . -type f \( -name "$element" \) -exec cp {} .ps_outputs/covertool.xml \;
40
+ done
41
+ fi
42
+
43
+ # Split unitTestFiles and collect
44
+ if [ "$unitTestFiles" != "" ]; then
45
+ IFS=',' read -r -a unit_tests <<< "$unitTestFiles"
46
+ for element in "${unit_tests[@]}"
47
+ do
48
+ find . -type f \( -name "$element" \) -exec cp {} .ps_outputs/ \;
49
+ done
50
+ fi
51
+
52
+ # Fail if it does not find any coverage report
53
+ if [[ ! -e .ps_outputs/covertool.xml ]]; then
54
+ echo 'No coverage results, changed build result to FAILURE'
55
+ exit 1
56
+ fi
@@ -0,0 +1,8 @@
1
+ #!/bin/bash
2
+ mkdir .ps_outputs
3
+ find . -type f \( -name '*.coverage.xml' -o -name '*.covertool.xml' \) -exec cp {} .ps_outputs/covertool.xml \;
4
+ if [[ ! -e .ps_outputs/covertool.xml ]]; then
5
+ echo 'No coverage results, changed build result to FAILURE'
6
+ exit 1
7
+ fi
8
+ find . -type f \( -name 'logs/**/junit_report.xml' \) -exec cp {} .ps_outputs/ \;
@@ -0,0 +1,37 @@
1
+ #!/bin/bash
2
+ #
3
+ # Post test run metrics to Datadog
4
+ #
5
+
6
+ : ${DATADOG_API_KEY:alps-env-secret}
7
+
8
+ if [[ $# -ne 2 ]]
9
+ then
10
+ echo "Usage: $0 <xml surefire report> <html coverage report>"
11
+ exit 1
12
+ fi
13
+
14
+ surefire_report=$1
15
+ coverage_report=$2
16
+
17
+ SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]:-$0}"; )" &> /dev/null && pwd 2> /dev/null; )";
18
+
19
+ echo -n "==> posting datadog metrics from surefire report "
20
+ curl -X POST \
21
+ "https://api.datadoghq.com/api/v1/series" \
22
+ -H "Content-Type: text/json" \
23
+ -H "DD-API-KEY: ${DATADOG_API_KEY}" \
24
+ -d @- << EOF
25
+ $(${SCRIPT_DIR}/testreport_tool surefire-to-datadog --report $surefire_report | jq .)
26
+ EOF
27
+ echo
28
+
29
+ echo -n "==> posting datadog metrics from coverage report "
30
+ curl -X POST \
31
+ "https://api.datadoghq.com/api/v1/series" \
32
+ -H "Content-Type: text/json" \
33
+ -H "DD-API-KEY: ${DATADOG_API_KEY}" \
34
+ -d @- << EOF
35
+ $(${SCRIPT_DIR}/testreport_tool coverage-to-datadog --report $coverage_report | jq .)
36
+ EOF
37
+ echo
@@ -0,0 +1,297 @@
1
+ #!/usr/bin/env python3
2
+ '''Utility functions for test reports
3
+
4
+ Provides the following functions:
5
+
6
+ - Combine eunit and ct surefire test result into a single report that
7
+ an be uploaded to Datadog CI
8
+
9
+ - Extract and transform
10
+ - aggregate html test coverage report generated by rebar3 coverage
11
+ to datadog POST metric API input
12
+ - surefire xml test report to datadog POST metric API input
13
+ '''
14
+ import argparse
15
+ import json
16
+ import os
17
+ import subprocess
18
+ import sys
19
+ import traceback
20
+ import xml.etree.ElementTree as ET
21
+
22
+ from datetime import datetime
23
+ from html.parser import HTMLParser
24
+ from xml.etree.ElementTree import ElementTree
25
+
26
+ APP = os.path.basename(os.getcwd())
27
+
28
+ # ------------------------------------------------------------------------------
29
+ # Helpers
30
+ # ------------------------------------------------------------------------------
31
+ class State:
32
+ '''HTML coverage parser states'''
33
+ aggregate = 1
34
+ collect = 2
35
+ reset = 0
36
+
37
+ class Result:
38
+ '''Coverage result'''
39
+ __slot__ = ['total', 'modules']
40
+
41
+ class Module():
42
+ __slot__ = ['name', 'coverage']
43
+ def __init__(self, module, coverage):
44
+ self.name = module
45
+ self.coverage = coverage
46
+
47
+ def __init__(self):
48
+ self.modules = []
49
+ self.total = 0
50
+
51
+ class CoverageReportParser(HTMLParser):
52
+ '''Custom HTML parser for rebar3 coverage reports
53
+
54
+ Extracts the aggregate coverage data from the report.
55
+ '''
56
+ _results = []
57
+ _state = State.reset
58
+
59
+ def handle_starttag(self, tag, attrs):
60
+ if self._state == State.aggregate and tag == 'td':
61
+ self._state = State.collect
62
+
63
+ def handle_endtag(self, tag):
64
+ if tag == 'table':
65
+ self._state = State.reset
66
+
67
+ def handle_data(self, data):
68
+ if data == 'aggregate summary':
69
+ self._state = State.aggregate
70
+ if self._state == State.collect:
71
+ data = data.strip()
72
+ if data:
73
+ self._results.append(data)
74
+
75
+ @property
76
+ def result(self):
77
+ modules = self._results[::2]
78
+ coverages = self._results[1::2]
79
+ result = Result()
80
+ for name, coverage in zip(modules, coverages):
81
+ coverage = int(coverage.replace('%', ''))
82
+ if name == 'Total':
83
+ result.total = coverage
84
+ else:
85
+ result.modules.append(Result.Module(name, coverage))
86
+ return result
87
+
88
+ def git_describe():
89
+ try:
90
+ result = subprocess.check_output(['git', 'describe'])
91
+ result = result.decode('utf8')
92
+ result = result.strip()
93
+ except Exception:
94
+ result = None
95
+ return result
96
+
97
+ def remove_attributes(xml):
98
+ '''Remove xml attributes that DataDog CI does not understand
99
+
100
+ DataDog CI silently fails to upload common test results with
101
+ timestamp attribute
102
+ '''
103
+ for testsuite in xml.findall('testsuite'):
104
+ if 'log' in testsuite.attrib:
105
+ del testsuite.attrib['log']
106
+ if 'timestamp' in testsuite.attrib:
107
+ del testsuite.attrib['timestamp']
108
+ for testcase in list(testsuite):
109
+ if 'timestamp' in testcase.attrib:
110
+ del testcase.attrib['timestamp']
111
+ if 'log' in testcase.attrib:
112
+ del testcase.attrib['log']
113
+ return xml
114
+
115
+ def parse_coverage_report(report):
116
+ parser = CoverageReportParser()
117
+ with open(report, 'r') as f:
118
+ contents = f.readlines()
119
+ contents = ''.join(contents)
120
+ parser.feed(contents)
121
+ return parser.result
122
+
123
+ # ------------------------------------------------------------------------------
124
+ # Commands
125
+ # ------------------------------------------------------------------------------
126
+ def cmd_coverage_to_datadog(args):
127
+ now = int(datetime.now().timestamp())
128
+ git_repo = args.git_repo
129
+ git_tag = args.git_tag
130
+ metric_prefix = args.metric_prefix + '.coverage'
131
+
132
+ tags = [f'git_repo:{git_repo}', f'git_tag:{git_tag}']
133
+ result = parse_coverage_report(args.report)
134
+ series = []
135
+ for module in result.modules:
136
+ points = [[now, module.coverage]]
137
+ series.append({'metric': f'{metric_prefix}.module',
138
+ 'points': points,
139
+ 'tags': tags + [f'module:{module.name}']})
140
+ series.append({'metric': f'{metric_prefix}.total',
141
+ 'points': [[now, result.total]],
142
+ 'tags': tags})
143
+ print(json.dumps({'series': series}))
144
+
145
+ def cmd_surefire_to_datadog(args):
146
+ now = int(datetime.now().timestamp())
147
+ git_repo = args.git_repo
148
+ git_tag = args.git_tag
149
+ metric_prefix = args.metric_prefix
150
+ report = args.report
151
+ xml = ET.parse(report)
152
+ result = []
153
+ for testsuite in xml.findall('testsuite'):
154
+ name = testsuite.attrib['name']
155
+ errors = int(testsuite.attrib['errors'])
156
+ skipped = int(testsuite.attrib['skipped'])
157
+ tests = int(testsuite.attrib['tests'])
158
+ time = float(testsuite.attrib['time'])
159
+ suite = {
160
+ 'name': name,
161
+ 'tests': tests,
162
+ 'errors': errors,
163
+ 'time_secs': time,
164
+ 'skipped': skipped}
165
+ result.append(suite)
166
+ series = []
167
+ for suite in result:
168
+ time_secs = suite['time_secs']
169
+ tags = [f'git_repo:{git_repo}',
170
+ f'git_tag:{git_tag}',
171
+ f'suite:{suite["name"]}']
172
+ def to_series(type):
173
+ value = suite[type]
174
+ return {'metric': f'{metric_prefix}.count.{type}',
175
+ 'points': [[now, value]],
176
+ 'tags': tags}
177
+ series.append({
178
+ 'metric': f'{metric_prefix}.time',
179
+ 'points': [[now, time_secs]],
180
+ 'tags': tags})
181
+ if args.send_zeros or suite['tests']:
182
+ series.append(to_series('tests')),
183
+ if args.send_zeros or suite['errors']:
184
+ series.append(to_series('errors'))
185
+ if args.send_zeros or suite['skipped']:
186
+ series.append(to_series('skipped'))
187
+ print(json.dumps({'series': series}))
188
+
189
+ def cmd_surefire_combine(args):
190
+ # Parse arguments
191
+ service = args.service
192
+ ct_report = args.ct
193
+ eunit_report = args.eunit
194
+ combined_report = args.result
195
+ print(f'==> combining CT report {ct_report} and eunit report {eunit_report} to {combined_report}')
196
+
197
+ # Load ct and unit surefire repots
198
+ ct_xml = ET.parse(ct_report)
199
+ eunit_xml = ET.parse(eunit_report)
200
+
201
+ # Create result xml
202
+ result_xml = ElementTree(ET.Element('testsuites'))
203
+ result_root = result_xml.getroot()
204
+
205
+ # Add CT surefire results
206
+ ct_xml = remove_attributes(ct_xml)
207
+ for testsuite in ct_xml.findall('testsuite'):
208
+ result_root.append(testsuite)
209
+
210
+ # Add eunit surefire results
211
+ testsuite = eunit_xml.getroot()
212
+ testsuite.attrib['name'] = service
213
+ result_root.append(testsuite)
214
+
215
+ # Write result
216
+ result_xml.write(combined_report)
217
+ print(f'==> wrote combined report {combined_report}')
218
+
219
+ # ------------------------------------------------------------------------------
220
+ # Command line parsing
221
+ # ------------------------------------------------------------------------------
222
+ def cmd_coverage_to_datadog_subparser(subparsers):
223
+ s = subparsers.add_parser(
224
+ 'coverage-to-datadog',
225
+ help='transform coverage html report to datdog series')
226
+ s.add_argument('--report',
227
+ default='_build/test/cover/index.html',
228
+ help='Path of the combined report')
229
+ s.set_defaults(func=cmd_coverage_to_datadog)
230
+
231
+ def cmd_surefire_to_datadog_subparser(subparsers):
232
+ s = subparsers.add_parser(
233
+ 'surefire-to-datadog',
234
+ help='transform surefire xml report to datadog series')
235
+ s.add_argument('--send-zeros',
236
+ action='store_true',
237
+ help='Genreate metriceven if the value is 0')
238
+ s.add_argument('--report',
239
+ default=f'_build/test/report/{APP}_surefire_report.xml',
240
+ help='Path of the combined report')
241
+ s.set_defaults(func=cmd_surefire_to_datadog)
242
+
243
+ def cmd_surefire_combine_subparser(subparsers):
244
+ s = subparsers.add_parser(
245
+ 'surefire-combine',
246
+ help='Combine eunit and ct surefire reports')
247
+ s.add_argument('--service',
248
+ help='Name of the service')
249
+ s.add_argument('--ct',
250
+ help='path to the CT surefire report')
251
+ s.add_argument('--eunit',
252
+ help='path to the Eunit surefire report')
253
+ s.add_argument('--result',
254
+ help='Path for the combined report')
255
+ s.set_defaults(func=cmd_surefire_combine)
256
+
257
+ def init_argparse():
258
+ parser = argparse.ArgumentParser(
259
+ formatter_class=argparse.RawDescriptionHelpFormatter,
260
+ description='Surefire report helper')
261
+ parser.add_argument('--metric-prefix',
262
+ default=os.environ.get('TRT_METRIC_PREFIX', 'ae.dev.test'),
263
+ help='prefix value to use for metrics')
264
+ parser.add_argument(
265
+ '--git-repo',
266
+ default=APP,
267
+ help='Name of the git repository for the service')
268
+ parser.add_argument(
269
+ '--git-tag',
270
+ default=git_describe(),
271
+ help='Associated git tag for the reports')
272
+ subparsers = parser.add_subparsers(
273
+ title='commands',
274
+ metavar='cmd [opts]',
275
+ help='commands, use --help to see help')
276
+ cmd_surefire_combine_subparser(subparsers)
277
+ cmd_surefire_to_datadog_subparser(subparsers)
278
+ cmd_coverage_to_datadog_subparser(subparsers)
279
+ return parser
280
+
281
+ # ------------------------------------------------------------------------------
282
+ # Main
283
+ # ------------------------------------------------------------------------------
284
+ def main():
285
+ parser = init_argparse()
286
+ try:
287
+ args = parser.parse_args()
288
+ if 'func' in args:
289
+ args.func(args)
290
+ else:
291
+ parser.print_usage()
292
+ except Exception as e:
293
+ print(f'ERROR: {e}')
294
+ print(f'{traceback.format_exc()}')
295
+
296
+ if __name__ == '__main__':
297
+ main()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alertlogic/al-collector-js",
3
- "version": "3.0.1",
3
+ "version": "3.0.2",
4
4
  "license": "MIT",
5
5
  "description": "Alert Logic Collector Common Library",
6
6
  "repository": {
@@ -9,7 +9,7 @@
9
9
  },
10
10
  "scripts": {
11
11
  "lint": "jshint --show-non-errors --exclude \"./node_modules/*,./proto/*\" **/*.js *.js",
12
- "test": "JUNIT_REPORT_PATH=./test/report.xml nyc --reporter=cobertura mocha --colors --reporter mocha-jenkins-reporter",
12
+ "test": "JUNIT_REPORT_PATH=./test/report.xml nyc --reporter=cobertura --reporter=text mocha --colors --reporter mocha-jenkins-reporter",
13
13
  "rel": "npm publish --access=public"
14
14
  },
15
15
  "main": "index.js",
package/ps_spec.yml CHANGED
@@ -8,18 +8,21 @@ stages:
8
8
  - pull_request
9
9
  - pull_request:
10
10
  trigger_phrase: test it
11
- image: node:12
11
+ image: node:14
12
12
  compute_size: small
13
13
  commands:
14
14
  - make test
15
-
16
15
  -
17
16
  name: Master Push - Publish
18
17
  when:
19
18
  - push: ['npm']
20
- image: node:12
19
+ image: node:14
21
20
  compute_size: small
22
21
  commands:
22
+ - make test
23
+ - git clone git@algithub.pd.alertlogic.net:alertlogic/al-ps-tools.git
24
+ - cp coverage/cobertura-coverage.xml al-collector-js.coverage.xml
25
+ - bash ./al-ps-tools/helpers/collect.sh -c 'al-collector-js.coverage.xml'
23
26
  - |
24
27
  set -ex
25
28
 
@@ -46,4 +49,5 @@ stages:
46
49
  echo "NOT PUBLISHING $PKGNAME $PKGVERSION"
47
50
  fi
48
51
  - echo done
49
-
52
+ outputs:
53
+ file: ./.ps_outputs/*