newrelic 5.13.0 → 6.2.0

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.
package/.travis.yml CHANGED
@@ -1,28 +1,25 @@
1
- language: node_js
2
- node_js:
3
- # - "6"
4
- - "8"
5
- - "10"
1
+ language: generic
6
2
  env:
7
3
  global:
8
4
  - MONGODB=2.6.11
9
5
  matrix:
10
- - SUITE=smoke
11
- - SUITE=unit
12
- - SUITE=integration
13
- - SUITE=lint
14
- - SUITE=security
15
- - SUITE=versioned
16
- matrix:
17
- exclude:
18
- - node_js: "6"
19
- env: SUITE=security
20
- - node_js: "6"
21
- env: SUITE=lint
22
- - node_js: "8"
23
- env: SUITE=security
24
- - node_js: "8"
25
- env: SUITE=lint
6
+ # NODE 8
7
+ - NR_NODE_VERSION=8 SUITE=smoke
8
+ - NR_NODE_VERSION=8 SUITE=unit
9
+ - NR_NODE_VERSION=8 SUITE=integration
10
+ - NR_NODE_VERSION=8 SUITE=versioned
11
+ # NODE 10
12
+ - NR_NODE_VERSION=10 SUITE=smoke
13
+ - NR_NODE_VERSION=10 SUITE=unit
14
+ - NR_NODE_VERSION=10 SUITE=integration
15
+ - NR_NODE_VERSION=10 SUITE=versioned
16
+ # NODE 12 --
17
+ - NR_NODE_VERSION=12 SUITE=smoke
18
+ - NR_NODE_VERSION=12 SUITE=unit
19
+ - NR_NODE_VERSION=12 SUITE=integration
20
+ - NR_NODE_VERSION=12 SUITE=lint
21
+ - NR_NODE_VERSION=12 SUITE=security
22
+ - NR_NODE_VERSION=12 SUITE=versioned
26
23
  services:
27
24
  - memcached
28
25
  - mysql
@@ -34,9 +31,11 @@ addons:
34
31
  packages:
35
32
  - rabbitmq-server
36
33
  before_install:
34
+ - source ./bin/travis-node.sh
37
35
  - ./bin/travis-setup.sh
38
36
  install: if [ "$SUITE" != "smoke" ]; then npm ci; fi
39
37
  cache:
40
38
  directories:
41
39
  - "$HOME/.npm"
42
40
  script: npm run $SUITE
41
+ after_script: find /home/travis/.npm/_logs -type f -exec cat '{}' +
package/NEWS.md CHANGED
@@ -1,3 +1,169 @@
1
+ ### 6.2.0 (2019-11-25):
2
+
3
+ * Upgraded `tap` to resolve `handlebars` audit warnings.
4
+
5
+ * Added `getLinkingMetadata()` method to the API.
6
+
7
+ This new method can be used to retrieve the identifying information for the
8
+ agent and current active span and trace. Please consult [the documentation](https://docs.newrelic.com/docs/agents/nodejs-agent/api-guides/nodejs-agent-api#getLinkingMetadata)
9
+ for more information.
10
+
11
+ * Added `getTraceMetadata()` to the agent API.
12
+
13
+ This new method can be used to retrieve the current active Distributed Tracing
14
+ span and trace ids. Please consult [the documentation](https://docs.newrelic.com/docs/agents/nodejs-agent/api-guides/nodejs-agent-api#getTraceMetadata)
15
+ for more information.
16
+
17
+ * Added an `isSampled()` method to `Transaction` and `TransactionHandle`.
18
+
19
+ This new method can be used to retrieve the sampling decision made for a given
20
+ transaction. Please consult [the documentation](https://docs.newrelic.com/docs/agents/nodejs-agent/api-guides/nodejs-agent-api#transaction-handle-isSampled)
21
+ for more information.
22
+
23
+ ### 6.1.0 (2019-11-05):
24
+
25
+ * `@newrelic/native-metrics` module is defaulted to disabled in serverless mode.
26
+
27
+ This can reduce lambda cold-start times by up to 170ms. The `native-metrics` module
28
+ can rarely load in serverless environments due to differences from build environment to
29
+ deployed environment and offers little value in a serverless environment.
30
+
31
+ * Added env var `NEW_RELIC_NATIVE_METRICS_ENABLED` to enable/disable the
32
+ native-metrics module
33
+
34
+ * Added a test for querying poolCluster.of()
35
+
36
+ * Removed unused `mysql` bootstrap test code.
37
+
38
+ * Increased timeout for `index-bad-version` test to reduce flickers on Node 12.
39
+
40
+ * Changed file modification to leverage `writeFile` for `watchFile` test. This
41
+ triggers the watcher in a reasonable amount of time much more consistently.
42
+
43
+ * Added `@newrelic/aws-sdk` module to agent for auto-include on install.
44
+
45
+ * Added splitting of application name using semicolons in the env var.
46
+
47
+ * Removed testing of Bluebird 3.7 on Node v10 until they fix [the segfault
48
+ issue](https://github.com/petkaantonov/bluebird/issues/1618).
49
+
50
+ * Instrumented `connection.execute` for `mysql2`.
51
+
52
+ * Added HTTP method to segment attributes for external requests.
53
+
54
+ * Updated the `bin/ssl.sh` such that it uses verbose output, will exit on first
55
+ error code, and will refuse to proceed with LibreSSL (which can't generate certs).
56
+
57
+ * Added a `clear` sub-command to `bin/ssl.sh` that will allow developers to quickly
58
+ remove generated ssl/cert files and regenerate (useful is switch between platforms
59
+ via containers/docker and certs needs to be regenerated)
60
+
61
+ ### 6.0.0 (2019-10-29):
62
+
63
+ * Added official parity support for Node 12.
64
+ * Exception: Errors resulting in unhandled rejections will no longer be scoped to the
65
+ transaction that was active when the rejected promise was created.
66
+
67
+ As of node 12, the promise responsible for triggering the init async hook will
68
+ no longer be passed through on the promise wrap instance. This breaks the linkage
69
+ used to relate a given promise rejection to the transaction it was scheduled in.
70
+
71
+ * **BREAKING** Removed support for Node 6, 7, and 9.
72
+
73
+ The minimum supported version is now Node v8. For further information on our
74
+ support policy, see:
75
+ https://docs.newrelic.com/docs/agents/nodejs-agent/getting-started/compatibility-requirements-nodejs-agent.
76
+
77
+ * Bumped version of `@newrelic/superagent` instrumentation to `v2.0.0`.
78
+ * Bumped version of `@newrelic/native-metrics` to `v5.0.0`.
79
+
80
+ * **BREAKING** Bumped version of `@newrelic/koa` instrumentation to `v3.0.0`
81
+ `@newrelic/koa` update includes changes to transaction naming in addition to
82
+ dropping Node versions 6, 7, and 9. See `@newrelic/koa`release notes for what was
83
+ included in `v2.0.0` and `v3.0.0` updates. https://github.com/newrelic/node-newrelic-koa/blob/master/NEWS.md.
84
+
85
+ * **BREAKING** `max_samples_stored` behavior has changed to replace
86
+ `max_samples_per_minute`. `max_samples_per_minute` is no longer a configuration
87
+ parameter.
88
+
89
+ The new behavior for `max_samples_stored` is as follows: "The agent will collect
90
+ all events up to this number per minute. If there are more than that, a statistical
91
+ sampling will be collected." This usage of the configuration is consistent with
92
+ other agents.
93
+
94
+ If your application has previously used `max_samples_per_minute` as an upper bound,
95
+ you may need to lower the threshold to a valid maximum to avoid data being dropped
96
+ on the server. No larger than 10k is recommended.
97
+
98
+ * Updated utilization callback test to point to a host that can't represent a valid
99
+ provider. Previously, location where CI provider runs tests could cause test to
100
+ fail.
101
+
102
+ * Added support for `Promise.allSettled()` method in Bluebird 3.7.
103
+
104
+ * Bumped `mongodb` dev dependency past security warning.
105
+
106
+ * Fixed `mongodb` versioned tests so they are self-contained by using version under
107
+ test for setup/teardown instead of agent dev-dependency version.
108
+
109
+ * Forced filename resolution if not already cached on module load. This should not
110
+ occur in normal/non-test scenarios but provides a fall-back to maintain
111
+ functionality.
112
+
113
+ * Refactored `restify` versioned tests to be less dependent on the order of asynchronous
114
+ operations.
115
+
116
+ * Updated README to reference Pug rather than Jade.
117
+
118
+ ### 5.13.1 (2019-10-10):
119
+
120
+ * Added back generation of entity stats logging and uninstrumented support metric
121
+ generation on metric harvests.
122
+
123
+ * Removed legacy harvest code from main agent.
124
+
125
+ * Updated `https-proxy-agent` to v3 for security fix.
126
+
127
+ Shoutout to @asturur for the contribution.
128
+
129
+ * Added diagnostic code injector.
130
+
131
+ The agent may now be configured to make transaction state checks via code
132
+ injection. This may be turned on by setting `code_injector.diagnostics.enabled`
133
+ to `true`. While this option is enabled, code around async boundaries will be added
134
+ to track transactions, and log a message when they are not properly reinstated.
135
+
136
+ * Fixed bug where `API.shutdown()` would not properly harvest when configured to.
137
+
138
+ * `primary_application_id` now defaults to 'Unknown' in serverless mode to allow
139
+ Distributed Tracing to function correctly when `NEW_RELIC_PRIMARY_APPLICATION_ID`
140
+ is not defined.
141
+
142
+ * Upgraded `tap` to latest version
143
+
144
+ * Upgraded `mocha` to latest version.
145
+
146
+ * Adds `--exit` flag to mocha test runs to prevent infinite runs on CI.
147
+
148
+ * Fixed bug where multiple agent restarts would cause the number of 'stopped'
149
+ listeners to exceed limit.
150
+
151
+ * Fixed inconsistent async return from collector API.
152
+
153
+ This could result in an infinite loop due to attempting to merge before clearing.
154
+ *This bug should not have impacted normal agent runs but was uncovered for certain
155
+ test cases.*
156
+
157
+ * Fixed tests that leave work scheduled on the event loop.
158
+
159
+ * Fixed issue that could result in vendor utilization detection failure.
160
+ As a part of this fix, the request that hits the timeout will immediately abort
161
+ instead of hanging around for the default timeout.
162
+
163
+ ### 5.13.0 (2019-10-01):
164
+
165
+ * Same as 5.12.0
166
+
1
167
  ### 5.12.0 (2019-10-01):
2
168
 
3
169
  * Now supports Restify 7 and 8.
package/README.md CHANGED
@@ -73,11 +73,11 @@ Here's the list of the most important variables and their values:
73
73
  * `NEW_RELIC_LICENSE_KEY`: Your New Relic license key. This is a required
74
74
  setting with no default value.
75
75
  * `NEW_RELIC_APP_NAME`: The name of this application, for reporting to New
76
- Relic's servers. This value can be also be a comma-delimited list of names.
77
- This is a required setting with no default value. (NOTE: as a convenience to
78
- Azure users, the module will use `APP_POOL_ID` as the application name if
79
- it's set, so you can use the name you chose for your Azure Web Server without
80
- setting it twice.)
76
+ Relic's servers. This value can be also be a comma-delimited, or
77
+ semicolon-delimited, list of names. This is a required setting with no
78
+ default value. (NOTE: as a convenience to Azure users, the module will use
79
+ `APP_POOL_ID` as the application name if it's set, so you can use the name
80
+ you chose for your Azure Web Server without setting it twice.)
81
81
  * `NEW_RELIC_NO_CONFIG_FILE`: Inhibit loading of the configuration file
82
82
  altogether. Use with care. This presumes that all important configuration
83
83
  will be available via environment variables, and some log messages assume
@@ -109,15 +109,15 @@ enable browser timings.
109
109
 
110
110
  - Insert the result of `newrelic.getBrowserTimingHeader()` into your html page.
111
111
  - The browser timing headers should be placed in the beginning of your `<head>` tag.
112
- - As an exception to the above, for maximum IE compatability, the results of
112
+ - As an exception to the above, for maximum IE compatibility, the results of
113
113
  `getBrowserTimingHeader()` should be placed *after* any
114
114
  `X-UA-COMPATIBLE HTTP-EQUIV` meta tags.
115
115
  - Do *not* cache the header, call it once for every request.
116
116
 
117
117
  ### Example
118
118
 
119
- Below is an example using `express` and `jade`; Express is a popular web
120
- application framework, and `jade` is a popular template module. Although the
119
+ Below is an example using `express` and `pug`; Express is a popular web
120
+ application framework, and `pug` is a popular template module. Although the
121
121
  specifics are different for other frameworks, the general approach described
122
122
  below should work in most cases.
123
123
 
@@ -140,9 +140,9 @@ app.get('/user/:id', function (req, res) {
140
140
  app.listen(process.env.PORT);
141
141
  ```
142
142
 
143
- *layout.jade:*
143
+ *layout.pug:*
144
144
 
145
- ```jade
145
+ ```pug
146
146
  doctype html
147
147
  html
148
148
  head
package/api.js CHANGED
@@ -128,6 +128,57 @@ API.prototype.getTransaction = function getTransaction() {
128
128
  return new TransactionHandle(transaction)
129
129
  }
130
130
 
131
+ /**
132
+ * This method returns an object with the following keys/data:
133
+ * - `trace.id`: The current trace ID
134
+ * - `span.id`: The current span ID
135
+ * - `entity.name`: The application name specified in the connect request as
136
+ * app_name. If multiple application names are specified this will only be
137
+ * the first name
138
+ * - `entity.type`: The string "SERVICE"
139
+ * - `entity.guid`: The entity ID returned in the connect reply as entity_guid
140
+ * - `hostname`: The hostname as specified in the connect request as
141
+ * utilization.full_hostname. If utilization.full_hostname is null or empty,
142
+ * this will be the hostname specified in the connect request as host.
143
+ *
144
+ * @returns {LinkingMetadata} The linking object with the data above
145
+ */
146
+ API.prototype.getLinkingMetadata = function getLinkingMetadata(omitSupportability) {
147
+ if (omitSupportability !== true) {
148
+ const metric = this.agent.metrics.getOrCreateMetric(
149
+ NAMES.SUPPORTABILITY.API + '/getLinkingMetadata'
150
+ )
151
+ metric.incrementCallCount()
152
+ }
153
+
154
+ const agent = this.agent
155
+
156
+ const segment = agent.tracer.getSegment()
157
+ const config = agent.config
158
+
159
+ const linkingMetadata = {
160
+ 'entity.name': config.applications()[0],
161
+ 'entity.type': 'SERVICE',
162
+ 'hostname': config.getHostnameSafe()
163
+ }
164
+
165
+ if (config.distributed_tracing.enabled && segment) {
166
+ linkingMetadata['trace.id'] = segment.transaction.getTraceId()
167
+ const spanId = segment.getSpanId()
168
+ if (spanId) {
169
+ linkingMetadata['span.id'] = spanId
170
+ }
171
+ } else {
172
+ logger.debug('getLinkingMetadata with no active transaction')
173
+ }
174
+
175
+ if (config.entity_guid) {
176
+ linkingMetadata['entity.guid'] = config.entity_guid
177
+ }
178
+
179
+ return linkingMetadata
180
+ }
181
+
131
182
  /**
132
183
  * Specify the `Dispatcher` and `Dispatcher Version` environment values.
133
184
  * A dispatcher is typically the service responsible for brokering
@@ -1327,6 +1378,36 @@ function instrumentMessages(moduleName, onRequire, onError) {
1327
1378
  shimmer.registerInstrumentation(opts)
1328
1379
  }
1329
1380
 
1381
+ /**
1382
+ * Returns the current trace and span id.
1383
+ *
1384
+ * @returns {*} The object containing the current trace and span ids
1385
+ */
1386
+ API.prototype.getTraceMetadata = function getTraceMetadata() {
1387
+ var metric = this.agent.metrics.getOrCreateMetric(
1388
+ NAMES.SUPPORTABILITY.API + '/getTraceMetadata'
1389
+ )
1390
+ metric.incrementCallCount()
1391
+
1392
+ const metadata = {}
1393
+
1394
+ const segment = this.agent.tracer.getSegment()
1395
+ if (!segment) {
1396
+ logger.debug("No transaction found when calling API#getTraceMetadata")
1397
+ } else if (!this.agent.config.distributed_tracing.enabled) {
1398
+ logger.debug("Distributed tracing disabled when calling API#getTraceMetadata")
1399
+ } else {
1400
+ metadata.traceId = segment.transaction.getTraceId()
1401
+
1402
+ const spanId = segment.getSpanId()
1403
+ if (spanId) {
1404
+ metadata.spanId = spanId
1405
+ }
1406
+ }
1407
+
1408
+ return metadata
1409
+ }
1410
+
1330
1411
  /**
1331
1412
  * Shuts down the agent.
1332
1413
  *
@@ -1405,8 +1486,9 @@ function _doShutdown(api, options, callback) {
1405
1486
  }
1406
1487
 
1407
1488
  agent.on('started', function shutdownHarvest() {
1408
- agent.harvest(afterHarvest)
1489
+ agent.forceHarvestAll(afterHarvest)
1409
1490
  })
1491
+
1410
1492
  agent.on('errored', function logShutdownError(error) {
1411
1493
  agent.stop(callback)
1412
1494
  if (error) {
@@ -1417,7 +1499,7 @@ function _doShutdown(api, options, callback) {
1417
1499
  }
1418
1500
  })
1419
1501
  } else if (options.collectPendingData) {
1420
- agent.harvest(afterHarvest)
1502
+ agent.forceHarvestAll(afterHarvest)
1421
1503
  } else {
1422
1504
  agent.stop(callback)
1423
1505
  }
@@ -1,5 +1,7 @@
1
1
  #! /bin/bash
2
2
 
3
+ set -x
4
+
3
5
  VERSIONED_MODE="${VERSIONED_MODE:---major}"
4
6
  if [[ $TRAVIS_BRANCH == `git describe --tags --always HEAD` ]]; then
5
7
  VERSIONED_MODE=--minor
@@ -20,4 +22,4 @@ fi
20
22
  export AGENT_PATH=`pwd`
21
23
 
22
24
  # This is meant to be temporary. Remove once new major version with fixes rolled into agent.
23
- time ./node_modules/.bin/versioned-tests $VERSIONED_MODE -i 2 --skip koa ${directories[@]}
25
+ time ./node_modules/.bin/versioned-tests $VERSIONED_MODE -i 2 ${directories[@]}
package/bin/ssl.sh CHANGED
@@ -1,5 +1,21 @@
1
1
  #! /bin/sh
2
2
 
3
+ # https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html
4
+ set -e # exit if any command fails
5
+ set -x # be chatty and show the lines we're running
6
+
7
+ # LibreSSL fails on the openssl ca step for reasons
8
+ # that are mysterious and not understood, so let
9
+ # bail early if we detect that's the case
10
+ ENGINE_OPENSSL=`openssl version | awk '{print $(1)}'`
11
+ if [ "$ENGINE_OPENSSL" == "LibreSSL" ]
12
+ then
13
+ echo "LibreSSL is not supported, please install a stock openssl and \n"
14
+ echo "make sure that openssl binary is in your PATH"
15
+ exit 1
16
+ fi
17
+
18
+ # CACONFIG is the only non-generated file
3
19
  CACONFIG="test/lib/test-ca.conf"
4
20
  SSLKEY="test/lib/test-key.key"
5
21
  CACERT="test/lib/ca-certificate.crt"
@@ -7,16 +23,36 @@ CAINDEX="test/lib/ca-index"
7
23
  CASERIAL="test/lib/ca-serial"
8
24
  CERTIFICATE="test/lib/self-signed-test-certificate.crt"
9
25
 
26
+ # USAGE: ./bin/ssl.sh clear
27
+ # a sub command to remove all the generated files and start over
28
+ if [ "$1" == "clear" ]
29
+ then
30
+ rm $SSLKEY
31
+ rm $CACERT
32
+ rm $CAINDEX
33
+ rm $CASERIAL
34
+ rm $CERTIFICATE
35
+ exit 0
36
+ fi
37
+
38
+ # if there's already a certificate, then exit, but
39
+ # exit with a success code so build continue
10
40
  if [ -a $CERTIFICATE ]; then
11
41
  exit 0;
12
42
  fi
13
43
 
44
+ # generates an RSA key
14
45
  openssl genrsa -out $SSLKEY 1024
15
46
 
47
+ # ca-index is the "certificate authority" database
48
+ # and ca-serial is a file that openssl will read
49
+ # "the next serial number for the ca-index entry"
50
+ # from.
16
51
  touch $CAINDEX
17
-
18
52
  echo 000a > $CASERIAL
19
53
 
54
+ # this generates a certificate for the
55
+ # certificate authority
20
56
  openssl req \
21
57
  -new \
22
58
  -subj "/O=testsuite/OU=New Relic CA/CN=Node.js test CA" \
@@ -25,12 +61,17 @@ openssl req \
25
61
  -x509 \
26
62
  -out $CACERT
27
63
 
64
+ # this generates a "certificate signing request" file
28
65
  openssl req \
29
66
  -new \
30
67
  -subj "/O=testsuite/OU=Node.js agent team/CN=ssl.lvh.me" \
31
68
  -key $SSLKEY \
32
69
  -out server.csr
33
70
 
71
+ # using the files generated above, this tells the
72
+ # certificate authority about the request for a certificate,
73
+ # which generates the self-signed-test-certificate.crt file.
74
+ # This is the file used by the web server
34
75
  openssl ca \
35
76
  -batch \
36
77
  -cert $CACERT \
@@ -39,4 +80,5 @@ openssl ca \
39
80
  -in server.csr \
40
81
  -out $CERTIFICATE
41
82
 
83
+ # remove the signing request now that we're done with it
42
84
  rm -f server.csr
@@ -0,0 +1,51 @@
1
+ #!/bin/bash
2
+
3
+ # https://stackoverflow.com/questions/16989598/bash-comparing-version-numbers/24067243
4
+ # tests version strings using `sort`'s -V option
5
+ function version_gt() { test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$1"; }
6
+
7
+ sudo apt-get update
8
+
9
+ # so dumb that we need python-software-properties
10
+ # to get add-apt-repository
11
+ sudo apt-get -y install build-essential libssl-dev curl python-software-properties time sudo
12
+
13
+ # update gcc to something that will install pg-native module
14
+ sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
15
+ sudo apt-get -y update
16
+ sudo apt-get -y install gcc-4.9 g++-4.9
17
+ sudo rm /usr/bin/g++
18
+ sudo rm /usr/bin/gcc
19
+ sudo ln -s /usr/bin/g++-4.9 /usr/bin/g++
20
+ sudo ln -s /usr/bin/gcc-4.9 /usr/bin/gcc
21
+
22
+ # Where is ppa:ubuntu-toolchain-r/test?
23
+ # sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
24
+ # sudo apt-get -y update
25
+ # sudo apt-get -y install libstdc++6-4.7-dev
26
+
27
+ # only do for NODE 12 AND for old glibc
28
+
29
+ GLIBC_VERSION_CHECK=`ldd --version | head -n 1 | awk '{print $NF}'`
30
+
31
+ if [ -z $NR_NODE_VERSION ]; then
32
+ echo "please define NR_NODE_VERSION in local env"
33
+ exit 1
34
+ fi
35
+
36
+ if [ $NR_NODE_VERSION -gt 11 ] && version_gt 2.17 $GLIBC_VERSION_CHECK;then
37
+ echo "Installing updated glibc\n"
38
+ # can we get this from an actual repository?
39
+ curl -LO 'http://launchpadlibrarian.net/130794928/libc6_2.17-0ubuntu4_amd64.deb'
40
+ sudo dpkg -i libc6_2.17-0ubuntu4_amd64.deb
41
+ else
42
+ echo "Skipping glibc update\n"
43
+ fi
44
+
45
+ # install nvm
46
+ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.0/install.sh | bash
47
+ export NVM_DIR="$HOME/.nvm"
48
+ [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
49
+ [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
50
+
51
+ nvm install $NR_NODE_VERSION
@@ -48,7 +48,3 @@ if [ "$SUITE" = "versioned" ]; then
48
48
  else
49
49
  echo " --- no $SUITE installation requirements --- "
50
50
  fi
51
-
52
-
53
- # Always install time.
54
- sudo apt-get install -qq time