Skip to content

Commit 32e3041

Browse files
author
Silla Tan
authored
Ops/prevent needless ssm updates (#17)
* add dataloader * add get batch-parameters * refactor get-all-parameters to use data loader * prevent update unless value is changed * fix tests * update eslint and enforce node 8 * add node version * bump package * Update .travis.yml * only log on complete when config is updated * cache dataloader * fix batch params to that it includes invalid parameters * fix tests * update tests * remove topairs
1 parent f0adb38 commit 32e3041

16 files changed

+841
-446
lines changed

.eslintrc

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,6 @@
2424
"no-unused-vars": [2, { "argsIgnorePattern": "^_" }],
2525
"no-console": 0,
2626
"no-process-exit": 0,
27-
"node/no-unsupported-features": [
28-
2,
29-
{
30-
"version": 6
31-
}
32-
],
3327
"promise/param-names": 2,
3428
"promise/catch-or-return": 2,
3529
"promise/no-native": 0,

.node-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
8.10.0

.travis.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
language: node_js
22

33
node_js:
4-
- '6.10'
5-
- '8'
4+
- '8.10'
65

76
install:
87
- yarn install
@@ -23,4 +22,4 @@ deploy:
2322
tags: true
2423
repo: ACloudGuru/oprah
2524
branch: master
26-
node: '6.10'
25+
node: '8.10'

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
55
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
66

7+
8+
## [v3.0.0](2019-11-01)
9+
10+
### Added
11+
- Prevent SSM from writing values unless it has changed
12+
13+
### Removed
14+
- Dropped support for `node 6`
15+
716
## [v2.7.0](2019-04-23)
817

918
### Added

lib/services/parameter-store/stores/ddb/make-update-parameters.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
const entries = require('lodash.topairs');
21
const get = require('lodash.get');
32
const { generateNextVersionNumber } = require('./generate-next-version-number');
43
const Bluebird = require('bluebird');
@@ -14,7 +13,7 @@ const makeUpdateParameters =
1413
}) => {
1514

1615
return Bluebird.all(
17-
entries(parameters)
16+
Object.entries(parameters)
1817
.map(([key, value]) => {
1918
return getLatestVersion({ parameterName: key })
2019
.then(Item => {
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
'use strict';
2+
3+
const Bluebird = require('bluebird');
4+
const { getSsmClient } = require('./get-ssm-client');
5+
const get = require('lodash.get');
6+
const chunk = require('lodash.chunk');
7+
const flatten = require('lodash.flatten');
8+
9+
const getParametersFromSSM = ({ parameterNames }) => {
10+
const ssm = getSsmClient();
11+
12+
return ssm.getParameters({
13+
Names: parameterNames,
14+
WithDecryption: true
15+
})
16+
.promise()
17+
.then(res => {
18+
const validParameters = get(res, 'Parameters') || [];
19+
const invalidParameters = get(res, 'InvalidParameters') || [];
20+
const normalizedInvalidParameters = invalidParameters.map(name => ({
21+
Name: name
22+
}));
23+
const allParameters = [].concat(validParameters, normalizedInvalidParameters);
24+
25+
return parameterNames.reduce((acc, name) => {
26+
const matchingParameter = allParameters.find(parameter => get(parameter, 'Name') === name);
27+
const value = get(matchingParameter, 'Value') || '';
28+
return acc.concat(value);
29+
}, [])
30+
});
31+
}
32+
33+
const getBatchParameters = ({ parameterNames }) => {
34+
const chunks = chunk(parameterNames, 10);
35+
const chunksOfRequests = chunks.map(chunkOfTen => () => getParametersFromSSM({ parameterNames: chunkOfTen }));
36+
37+
return Bluebird.mapSeries(chunksOfRequests, request => request())
38+
.then(flatten)
39+
}
40+
41+
module.exports = {
42+
getBatchParameters
43+
};
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
const Bluebird = require('bluebird');
2+
3+
const mockGetParameters = jest.fn()
4+
.mockImplementationOnce(() => ({
5+
promise: () => Bluebird.resolve({
6+
Parameters: [
7+
{
8+
Name: 'TEST/ONE',
9+
Value: '1',
10+
},
11+
{
12+
Name: 'TEST/TWO',
13+
Value: '2',
14+
},
15+
{
16+
Name: 'TEST/THREE',
17+
Value: '3',
18+
},
19+
{
20+
Name: 'TEST/FOUR',
21+
Value: '4',
22+
},
23+
{
24+
Name: 'TEST/SIX',
25+
Value: '6',
26+
},
27+
{
28+
Name: 'TEST/SEVEN',
29+
Value: '7',
30+
},
31+
{
32+
Name: 'TEST/NINE',
33+
Value: '9',
34+
},
35+
{
36+
Name: 'TEST/TEN',
37+
Value: '10',
38+
}
39+
],
40+
InvalidParameters: [
41+
{
42+
Name: 'TEST/FIVE',
43+
Value: '5',
44+
},
45+
{
46+
Name: 'TEST/EIGHT',
47+
Value: '8',
48+
},
49+
]
50+
})
51+
}))
52+
.mockImplementationOnce(() => ({
53+
promise: () => Bluebird.resolve({
54+
Parameters: [
55+
{
56+
Name: 'TEST/ELEVEN',
57+
Value: '11',
58+
},
59+
]
60+
})
61+
}));
62+
63+
const AWS = require('aws-sdk');
64+
AWS.SSM.mockImplementation(function () {
65+
return {
66+
getParameters: mockGetParameters
67+
};
68+
});
69+
70+
const { getBatchParameters } = require('./get-batch-parameters');
71+
72+
describe('getBatchParameters', () => {
73+
let resultPromise;
74+
75+
beforeAll(() => {
76+
resultPromise = getBatchParameters({
77+
parameterNames: [
78+
'TEST/ONE',
79+
'TEST/TWO',
80+
'TEST/THREE',
81+
'TEST/FOUR',
82+
'TEST/FIVE',
83+
'TEST/SIX',
84+
'TEST/SEVEN',
85+
'TEST/EIGHT',
86+
'TEST/NINE',
87+
'TEST/TEN',
88+
'TEST/ELEVEN'
89+
]
90+
});
91+
return resultPromise;
92+
});
93+
94+
it('should get all the parameters in ssm in chunks of ten due to api limits', () => {
95+
expect(mockGetParameters.mock.calls[0][0]).toEqual({
96+
Names: [
97+
'TEST/ONE',
98+
'TEST/TWO',
99+
'TEST/THREE',
100+
'TEST/FOUR',
101+
'TEST/FIVE',
102+
'TEST/SIX',
103+
'TEST/SEVEN',
104+
'TEST/EIGHT',
105+
'TEST/NINE',
106+
'TEST/TEN'
107+
],
108+
WithDecryption: true
109+
});
110+
111+
expect(mockGetParameters.mock.calls[1][0]).toEqual({
112+
Names: [
113+
'TEST/ELEVEN'
114+
],
115+
WithDecryption: true
116+
});
117+
});
118+
119+
it('should merge the results of all the request to ssm and strip the path and handle non-existant parameters', () => {
120+
return resultPromise
121+
.then(res => {
122+
expect(res).toEqual([
123+
'1',
124+
'2',
125+
'3',
126+
'4',
127+
'',
128+
'6',
129+
'7',
130+
'',
131+
'9',
132+
'10',
133+
'11'
134+
]);
135+
});
136+
})
137+
});

lib/services/parameter-store/stores/ssm/make-get-all-parameters.js

Lines changed: 19 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,27 @@
11
'use strict';
22

3-
const { getSsmClient } = require('./get-ssm-client');
43
const get = require('lodash.get');
5-
const chunk = require('lodash.chunk');
6-
const flatten = require('lodash.flatten');
7-
8-
function getAllParametersInSSM({ parameterNames }) {
9-
const ssm = getSsmClient();
10-
11-
return Promise.all(
12-
chunk(parameterNames, 10)
13-
.map(chunkOfTen => {
14-
return ssm.getParameters({
15-
Names: chunkOfTen,
16-
WithDecryption: true
17-
})
18-
.promise()
19-
.then(res => get(res, 'Parameters'))
20-
})
21-
)
22-
.then(flatten);
23-
}
24-
25-
function accumulateConfigs(allConfigs) {
26-
return allConfigs.reduce((acc, parameterObject) => {
27-
const parameterPath = get(parameterObject, 'Name') || '';
28-
const key = parameterPath.split('/').pop();
29-
30-
if (!key) {
31-
return acc;
32-
}
33-
34-
return Object.assign({}, acc, { [key]: get(parameterObject, 'Value') });
35-
}, {});
36-
}
374

385
const makeGetAllParameters =
39-
() =>
40-
({ parameterNames}) => {
41-
return getAllParametersInSSM({ parameterNames })
42-
.then(accumulateConfigs);
6+
({ loader }) =>
7+
({ parameterNames }) => {
8+
return loader.loadMany(parameterNames)
9+
.then(parameters => parameters.reduce(
10+
(acc, parameter, index) => {
11+
const parameterPath = get(parameterNames, index) || '';
12+
const key = parameterPath.split('/').pop();
13+
14+
if (!key) {
15+
return acc;
16+
}
17+
18+
return {
19+
...acc,
20+
[key]: parameter
21+
}
22+
},
23+
{}
24+
))
4325
}
4426

4527
module.exports = {

0 commit comments

Comments
 (0)