@architect/inventory 2.1.1 → 2.1.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.
- package/changelog.md +13 -0
- package/package.json +1 -1
- package/src/config/pragmas/validate/_scheduled.js +1 -6
- package/src/lib/is.js +2 -1
- package/src/validate/config.js +64 -0
- package/src/validate/index.js +3 -4
- package/src/validate/layers.js +65 -33
- package/src/validate/arn.js +0 -24
- package/src/validate/runtimes.js +0 -28
package/changelog.md
CHANGED
package/package.json
CHANGED
|
@@ -53,7 +53,6 @@ function validateCron (schedule, errors) {
|
|
|
53
53
|
if (!year.toString().match(minHrYr)) expErr('year', year)
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
|
|
57
56
|
let singular = [ 'minute', 'hour', 'day' ]
|
|
58
57
|
let plural = [ 'minutes', 'hours', 'days' ]
|
|
59
58
|
function validateRate (schedule, errors) {
|
|
@@ -69,11 +68,7 @@ function validateRate (schedule, errors) {
|
|
|
69
68
|
|
|
70
69
|
// Value must be a >0 number
|
|
71
70
|
if (!is.number(value) || !(value > 0)) {
|
|
72
|
-
expErr('rate value must be a number greater than 0', value)
|
|
73
|
-
}
|
|
74
|
-
// Value must be a whole number
|
|
75
|
-
else if (!value.toString().match(/^\d+$/)) {
|
|
76
|
-
expErr('rate value must be a whole number', value)
|
|
71
|
+
expErr('rate value must be a whole number greater than 0', value)
|
|
77
72
|
}
|
|
78
73
|
// Interval must be a string
|
|
79
74
|
if (!is.string(interval)) {
|
package/src/lib/is.js
CHANGED
|
@@ -4,9 +4,10 @@ module.exports = {
|
|
|
4
4
|
// Types
|
|
5
5
|
array: item => Array.isArray(item),
|
|
6
6
|
bool: item => typeof item === 'boolean',
|
|
7
|
-
number: item =>
|
|
7
|
+
number: item => Number.isInteger(item),
|
|
8
8
|
object: item => typeof item === 'object' && !Array.isArray(item),
|
|
9
9
|
string: item => typeof item === 'string',
|
|
10
|
+
defined: item => typeof item !== 'undefined' && item !== null,
|
|
10
11
|
// Filesystem
|
|
11
12
|
exists: path => existsSync(path),
|
|
12
13
|
folder: path => existsSync(path) && lstatSync(path).isDirectory(),
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
let is = require('../lib/is')
|
|
2
|
+
let { lambdas } = require('../lib/pragmas')
|
|
3
|
+
let { aliases, runtimeList } = require('lambda-runtimes')
|
|
4
|
+
let allRuntimes = runtimeList.concat([ 'deno' ])
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Configuration validator
|
|
8
|
+
*/
|
|
9
|
+
module.exports = function configValidator (params, inventory, errors) {
|
|
10
|
+
let { runtime: globalRuntime, memory: globalMemory, timeout: globalTimeout } = inventory.aws
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Global config
|
|
14
|
+
*/
|
|
15
|
+
// Memory
|
|
16
|
+
if (is.defined(globalMemory) && invalidMemory(globalMemory)) {
|
|
17
|
+
errors.push(invalidMemoryMsg(`${globalMemory} MB (@aws)`))
|
|
18
|
+
}
|
|
19
|
+
// Runtime
|
|
20
|
+
if (globalRuntime &&
|
|
21
|
+
!allRuntimes.includes(globalRuntime) &&
|
|
22
|
+
!aliases[globalRuntime]) {
|
|
23
|
+
errors.push(`Invalid project-level runtime: ${globalRuntime}`)
|
|
24
|
+
}
|
|
25
|
+
// Timeout
|
|
26
|
+
if (is.defined(globalTimeout) && invalidTimeout(globalTimeout)) {
|
|
27
|
+
errors.push(invalidTimeoutMsg(`${globalTimeout} seconds (@aws)`))
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Lambda config
|
|
32
|
+
*/
|
|
33
|
+
lambdas.forEach(p => {
|
|
34
|
+
let pragma = inventory[p]
|
|
35
|
+
if (pragma) pragma.forEach(({ name, config }) => {
|
|
36
|
+
let { memory, runtime, timeout } = config
|
|
37
|
+
|
|
38
|
+
// Memory
|
|
39
|
+
if (invalidMemory(memory) && memory !== globalMemory) {
|
|
40
|
+
errors.push(invalidMemoryMsg(`${memory} MB (@${p} ${name})`))
|
|
41
|
+
}
|
|
42
|
+
// Runtime
|
|
43
|
+
if (!allRuntimes.includes(runtime) && runtime !== globalRuntime) {
|
|
44
|
+
errors.push(`Invalid runtime: ${runtime} (@${p} ${name})`)
|
|
45
|
+
}
|
|
46
|
+
// Timeout
|
|
47
|
+
if (invalidTimeout(timeout) && timeout !== globalTimeout) {
|
|
48
|
+
errors.push(invalidTimeoutMsg(`${timeout} seconds (@${p} ${name})`))
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Memory
|
|
55
|
+
let minMemory = 128
|
|
56
|
+
let maxMemory = 10240
|
|
57
|
+
let invalidMemory = memory => !is.number(memory) || (memory < minMemory) || (memory > maxMemory)
|
|
58
|
+
let invalidMemoryMsg = info => `Invalid Lambda memory setting: ${info}, memory must be between ${minMemory} - ${maxMemory} MB`
|
|
59
|
+
|
|
60
|
+
// Timeout
|
|
61
|
+
let minTimeout = 1
|
|
62
|
+
let maxTimeout = 1 * 60 * 15 // 15 mins
|
|
63
|
+
let invalidTimeout = timeout => !is.number(timeout) || (timeout < minTimeout) || (timeout > maxTimeout)
|
|
64
|
+
let invalidTimeoutMsg = info => `Invalid Lambda timeout setting: ${info}, timeout must be between ${minTimeout} - ${maxTimeout} seconds`
|
package/src/validate/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
let
|
|
1
|
+
let config = require('./config')
|
|
2
2
|
let layers = require('./layers')
|
|
3
3
|
let tablesChildren = require('./tables-children')
|
|
4
4
|
let errorFmt = require('../lib/error-fmt')
|
|
@@ -12,9 +12,8 @@ module.exports = function finalValidation (params, inventory) {
|
|
|
12
12
|
/**
|
|
13
13
|
* Deal with vendor configuration errors
|
|
14
14
|
*/
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
runtimes(params, inventory, errors)
|
|
15
|
+
// Analyze function configuration
|
|
16
|
+
config(params, inventory, errors)
|
|
18
17
|
|
|
19
18
|
// Ensure layer configuration will work, AWS blows up with awful errors on this
|
|
20
19
|
layers(params, inventory, errors)
|
package/src/validate/layers.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
let { sep } = require('path')
|
|
2
2
|
let { lambdas } = require('../lib/pragmas')
|
|
3
|
-
let
|
|
3
|
+
let is = require('../lib/is')
|
|
4
|
+
let plural = arr => arr.length > 1 ? 's' : ''
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Layer validator
|
|
7
8
|
*/
|
|
8
9
|
module.exports = function layerValidator (params, inventory, errors) {
|
|
9
|
-
let {
|
|
10
|
+
let { _project } = inventory
|
|
11
|
+
let { region, layers: globalLayers } = inventory.aws
|
|
10
12
|
let { cwd, validateLayers = true } = params
|
|
11
13
|
|
|
12
14
|
// Shouldn't be possible because we backfill region, but jic
|
|
@@ -15,39 +17,69 @@ module.exports = function layerValidator (params, inventory, errors) {
|
|
|
15
17
|
// Allow for manual opt-out of layer validation
|
|
16
18
|
if (!validateLayers) return
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
})
|
|
34
|
-
}
|
|
20
|
+
/**
|
|
21
|
+
* Global config
|
|
22
|
+
*/
|
|
23
|
+
let location = _project?.manifest?.replace(cwd, '')
|
|
24
|
+
validateLayer({ layers: globalLayers, region, location, errors })
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Lambda config
|
|
28
|
+
*/
|
|
29
|
+
lambdas.forEach(p => {
|
|
30
|
+
let pragma = inventory[p]
|
|
31
|
+
if (pragma) pragma.forEach(({ config, configFile }) => {
|
|
32
|
+
let location = configFile?.replace(cwd, '')
|
|
33
|
+
validateLayer({ layers: config.layers, region, location, errors })
|
|
34
|
+
})
|
|
35
35
|
})
|
|
36
|
+
}
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
// CloudFormation fails without a helpful error if any layers aren't in the same region as the app because CloudFormation
|
|
47
|
-
for (let arn of layers) {
|
|
48
|
-
let arnError = validateARN({ arn, region, loc })
|
|
49
|
-
if (arnError) errors.push(arnError)
|
|
50
|
-
}
|
|
38
|
+
function validateLayer ({ layers, region, location, errors }) {
|
|
39
|
+
let loc = location && location.startsWith(sep) ? location.substr(1) : location
|
|
40
|
+
let config = loc ? ` (${loc})` : ''
|
|
41
|
+
if (!layers || !layers.length) return
|
|
42
|
+
else {
|
|
43
|
+
if (layers.length > 5) {
|
|
44
|
+
let list = `${layers.map(l => ` - ${l}`).join('\n')}`
|
|
45
|
+
errors.push(`Lambdas can only be configured with up to 5 layers, got ${layers.length} layers${config}:\n${list}`)
|
|
51
46
|
}
|
|
47
|
+
// CloudFormation fails without a helpful error if any layers aren't in the same region as the app because CloudFormation
|
|
48
|
+
let arnErrors = validateARN({ layers, region, config })
|
|
49
|
+
if (arnErrors) errors.push(arnErrors)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Validates Lambda layer / policy ARNs, prob can't be used for other kinds of ARN
|
|
54
|
+
function validateARN ({ layers, region, config }) {
|
|
55
|
+
let invalidArns = []
|
|
56
|
+
let badRegions = []
|
|
57
|
+
layers.forEach(arn => {
|
|
58
|
+
let parts = is.string(arn) && arn.split(':')
|
|
59
|
+
// Invalid
|
|
60
|
+
if (!is.string(arn) ||
|
|
61
|
+
!arn.startsWith('arn:') ||
|
|
62
|
+
parts.length !== 8) {
|
|
63
|
+
return invalidArns.push(` - ${arn}`)
|
|
64
|
+
}
|
|
65
|
+
// Bad region
|
|
66
|
+
let layerRegion = parts[3]
|
|
67
|
+
if (region !== layerRegion) {
|
|
68
|
+
badRegions.push(
|
|
69
|
+
` - Layer ARN: ${arn}\n` +
|
|
70
|
+
` - Layer region: ${layerRegion}`
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
let err = ''
|
|
75
|
+
if (invalidArns.length) {
|
|
76
|
+
err += `Invalid ARN${plural(invalidArns)}${config}:\n` +
|
|
77
|
+
invalidArns.join('\n')
|
|
78
|
+
}
|
|
79
|
+
if (badRegions.length) {
|
|
80
|
+
err += `Layer${plural(badRegions)} ` +
|
|
81
|
+
`not in app's region of ${region}${config}:\n` +
|
|
82
|
+
badRegions.join('\n')
|
|
52
83
|
}
|
|
84
|
+
if (err) return err
|
|
53
85
|
}
|
package/src/validate/arn.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
let is = require('../lib/is')
|
|
2
|
-
|
|
3
|
-
// Validates Lambda layer / policy ARNs, prob can't be used for other kinds of ARN
|
|
4
|
-
module.exports = function validateARN ({ arn, region, loc }) {
|
|
5
|
-
if (!is.string(arn) ||
|
|
6
|
-
!arn.startsWith('arn:') ||
|
|
7
|
-
arn.split(':').length !== 8) {
|
|
8
|
-
/* istanbul ignore next */
|
|
9
|
-
let lambda = loc ? `in ${loc}` : ''
|
|
10
|
-
return `Invalid ARN${lambda}: ${arn}`
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
let parts = arn.split(':')
|
|
14
|
-
let layerRegion = parts[3]
|
|
15
|
-
if (region !== layerRegion) {
|
|
16
|
-
/* istanbul ignore next */
|
|
17
|
-
let lambda = loc ? ` - Lambda: ${loc}\n` : ''
|
|
18
|
-
let err = `Lambda layers must be in the same region as app\n` + lambda +
|
|
19
|
-
` - App region: ${region}\n` +
|
|
20
|
-
` - Layer ARN: ${arn}\n` +
|
|
21
|
-
` - Layer region: ${layerRegion}`
|
|
22
|
-
return err
|
|
23
|
-
}
|
|
24
|
-
}
|
package/src/validate/runtimes.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
let { lambdas } = require('../lib/pragmas')
|
|
2
|
-
let { aliases, runtimeList } = require('lambda-runtimes')
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Runtime validator
|
|
6
|
-
*/
|
|
7
|
-
module.exports = function runtimeValidator (params, inventory, errors) {
|
|
8
|
-
|
|
9
|
-
let allRuntimes = runtimeList.concat([ 'deno' ])
|
|
10
|
-
let globalRuntime = inventory.aws?.runtime
|
|
11
|
-
if (globalRuntime &&
|
|
12
|
-
!allRuntimes.includes(globalRuntime) &&
|
|
13
|
-
!aliases[globalRuntime]) {
|
|
14
|
-
errors.push(`Invalid project-level runtime: ${globalRuntime}`)
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// Walk the tree of layer configs, starting with @aws
|
|
18
|
-
lambdas.forEach(p => {
|
|
19
|
-
let pragma = inventory[p]
|
|
20
|
-
if (pragma) pragma.forEach(entry => {
|
|
21
|
-
let runtime = entry.config.runtime
|
|
22
|
-
if (runtime === globalRuntime) return
|
|
23
|
-
if (!allRuntimes.includes(runtime)) {
|
|
24
|
-
errors.push(`Invalid runtime: ${runtime} (@${p} ${entry.name})`)
|
|
25
|
-
}
|
|
26
|
-
})
|
|
27
|
-
})
|
|
28
|
-
}
|