Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Access Parameter store config locally via Dotenv #4745

Merged
merged 25 commits into from
May 24, 2022
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
33254f3
Add check-dotenv to check-env of make file
jamesgorrie Apr 26, 2022
970a6b1
add Dotenv webpack to webpack.config.server
jamesgorrie May 4, 2022
dae8e25
move check-env to gen-env as dev step
jamesgorrie May 4, 2022
9bbbf58
move aws-parameters to scripts/dev
jamesgorrie May 4, 2022
25533c8
move aws-parameters to scripts/dev
jamesgorrie May 4, 2022
556b3f3
add dotenv to the install lifecycle
jamesgorrie May 4, 2022
0681749
remove unused config setup
jamesgorrie May 4, 2022
03282f0
lint
jamesgorrie May 4, 2022
1ea468a
Check NODE_ENV=production to exit if we can't generate .env file
jamesgorrie May 4, 2022
fb0b187
add namespace to log
jamesgorrie May 4, 2022
05763a9
add approachable value
jamesgorrie May 4, 2022
6704887
Remove stray §
jamesgorrie May 4, 2022
cf852c8
throw error on not having AWS access
jamesgorrie May 4, 2022
9bc60be
Merge branch 'server-side-dotenv' of github.com:guardian/dotcom-rende…
jamesgorrie May 4, 2022
3e76b3b
move gen-dotenv to build and dev targets
jamesgorrie May 4, 2022
c2c959b
Merge branch 'main' of github.com:guardian/dotcom-rendering into serv…
jamesgorrie May 18, 2022
ddf749d
Add process of adding env vars
jamesgorrie May 19, 2022
6ba8589
re suppress echoing in make
jamesgorrie May 19, 2022
3c74346
Merge branch 'server-side-dotenv' of github.com:guardian/dotcom-rende…
jamesgorrie May 19, 2022
bdfc282
refactor how we get aws SSM parameters for generating dotenv file
jamesgorrie May 19, 2022
9086f77
Merge branch 'main' of github.com:guardian/dotcom-rendering into serv…
jamesgorrie May 20, 2022
fd2dfc1
Add documentation on new dotenv functionality in DCR
OllysCoding May 20, 2022
b13b39a
Merge branch 'main' of github.com:guardian/dotcom-rendering into serv…
OllysCoding May 20, 2022
7bd2da3
Better grammar on dotenv architecture doc
jamesgorrie May 23, 2022
d5582b4
Merge branch 'main' of github.com:guardian/dotcom-rendering into serv…
jamesgorrie May 24, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions dotcom-rendering/docs/values/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Values

## Approachable

The project should have as few barriers as possible for anyone to work on it, including designers, UX, people outside of the Guardian and more.

### Lines in the sand

#### `.env` shouldn't be required
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 An important point not to be forgotten

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think it's worth enforcing in some or other way? That might be how we access to config is the only way I can think of.


While we use a `.env` file for configuration and secrets for `dotcom-rendering` to function correctly in `PROD`, not having it shouldn't block someone from running the project altogether.
jamesgorrie marked this conversation as resolved.
Show resolved Hide resolved

If you're considering adding something to the `.env` file - please consult with the dotcom team first. Wherever possible seeking solutions like passing the data from [Frontend](https://github.com/guardian/frontend) is preferred.

e.g. [Fallback for images salt](#) (In development so not linked to)
18 changes: 12 additions & 6 deletions dotcom-rendering/makefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,15 @@ deploy:

# prod #########################################

build: clean-dist install
build: export NODE_ENV=production
build: clean-dist install gen-dotenv
$(call log, "building production bundles")
@NODE_ENV=production webpack --config ./scripts/webpack/webpack.config.js --progress
@webpack --config ./scripts/webpack/webpack.config.js --progress

build-ci: clean-dist install
build-ci: export NODE_ENV=production
build-ci: clean-dist install gen-dotenv
$(call log, "building production bundles")
@NODE_ENV=production SKIP_LEGACY=true webpack --config ./scripts/webpack/webpack.config.js
@SKIP_LEGACY=true webpack --config ./scripts/webpack/webpack.config.js

start-ci: install
$(call log, "starting PROD server...")
Expand Down Expand Up @@ -66,11 +68,11 @@ run-ci: stop build-ci start-ci

# dev #########################################

dev: clear clean-dist install
dev: clear clean-dist install gen-dotenv
$(call log, "starting DEV server")
@NODE_ENV=development SKIP_LEGACY=true webpack serve --config ./scripts/webpack/webpack.config.js

dev-legacy: clear clean-dist install
dev-legacy: clear clean-dist install gen-dotenv
$(call log, "starting DEV server")
@NODE_ENV=development webpack serve --config ./scripts/webpack/webpack.config.js

Expand Down Expand Up @@ -177,6 +179,10 @@ gen-fixtures:
$(call log, "Generating new article fixture data")
@node scripts/test-data/gen-fixtures.js

gen-dotenv:
$(call log, "Generating .env file")
@node scripts/dotenv/gen-dotenv.js

perf-test:
@node scripts/perf/perf-test.js

Expand Down
1 change: 1 addition & 0 deletions dotcom-rendering/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@
"cypress-wait-until": "^1.7.1",
"cypress-webpack-preprocessor-v5": "^5.0.0-alpha.1",
"doctoc": "^2.1.0",
"dotenv-webpack": "^7.1.0",
"dompurify": "^2.3.6",
"dynamic-import-polyfill": "^0.1.1",
"eslint": "^7.24.0",
Expand Down
87 changes: 87 additions & 0 deletions dotcom-rendering/scripts/dotenv/gen-dotenv.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
const { CredentialsProviderError } = require('@aws-sdk/property-provider');
const path = require('path');
const fs = require('fs').promises;
const { prompt, log, warn } = require('../env/log');
const secrets = require('../secrets');
const { getAwsSsmParameters } = require('./get-aws-ssm-parameters');

const ENV_PATH = path.resolve(__dirname, '../../.env');

const checkEnv = async () => {
try {
const env = (await fs.readFile(ENV_PATH)).toString();

let valid = true;
for (const secret of secrets) {
const regex = new RegExp(`^${secret.key.replace('.', '\\.')}=.*`);
if (!env.match(regex)) valid = false;
}

return valid;
} catch (_err) {
return false;
}
};

const genEnv = async () => {
const env = process.env.NODE_ENV === 'production' ? 'prod' : 'dev';
const parameters = await getAwsSsmParameters(env);

let envString = '';
for (const secret of secrets) {
envString += `${secret.key}=${parameters[secret.key]}\n`;
}

await fs.writeFile(ENV_PATH, envString);
};

// eslint-disable-next-line @typescript-eslint/no-floating-promises
(async () => {
try {
const validEnv = await checkEnv();
if (!validEnv) {
log(
'[scripts/dotenv] .env file is missing, attemting to generate it from AWS parameters...',
);

await genEnv();

log('[scripts/dotenv] .env file written successfully');
} else {
log('[scripts/dotenv] valid .env file exists, moving on...');
}
} catch (err) {
if (err instanceof CredentialsProviderError) {
const PROD = process.env.NODE_ENV === 'production';
if (PROD) {
warn(
'[scripts/dotenv] could not generate .env file from AWS Parameter Store. Exiting',
);
throw err;
}

prompt(
'[scripts/dotenv] Could not load AWS credentials to generate .env file',
"[scripts/dotenv] This won't stop dotcom-rendering from working, it will just vary from PROD by:",
);

for (const secret of secrets) {
prompt(
`[scripts/dotenv] * ${secret.key}: ${secret.missingMessage}`,
);
}

prompt(
'',
'[scripts/dotenv] To get things working PROD like either:',
'[scripts/dotenv] * Get your credentials from Janus',
'[scripts/dotenv] * Ask a local engineer for a copy of the .env file',
'[scripts/dotenv] Then try again.',
);

process.exit(0);
} else {
throw err;
}
}
})();
56 changes: 56 additions & 0 deletions dotcom-rendering/scripts/dotenv/get-aws-ssm-parameters.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
const {
GetParametersByPathCommand,
SSMClient,
} = require('@aws-sdk/client-ssm');

process.env.AWS_PROFILE = 'frontend';
const ssm = new SSMClient({ region: 'eu-west-1' });
const env = process.env.NODE_ENV === 'production' ? 'prod' : 'dev';

async function* scrollParameters(params) {
let command = new GetParametersByPathCommand(params);
let response = await ssm.send(command);

while (true) {
const parameters = response.Parameters;

// SSM returns undefined if there are no parameters
if (parameters === undefined) {
break;
}

for (const parameter of parameters) {
yield parameter;
}

if (!response.NextToken) {
break;
}

command = new GetParametersByPathCommand({
Path: `/dotcom/${env}/`,
Recursive: true,
WithDecryption: true,
NextToken: response.NextToken,
});
response = ssm.send(command);
}
}

async function getAwsSsmParameters() {
const parameters = {};
const params = {
Path: `/dotcom/${env}/`,
Recursive: true,
WithDecryption: true,
};

for await (const parameter of scrollParameters(params)) {
const key = parameter.Name.replace(`/dotcom/${env}/`, '');
parameters[key] = parameter.Value;
}

return parameters;
}

module.exports = { getAwsSsmParameters };
8 changes: 8 additions & 0 deletions dotcom-rendering/scripts/secrets.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const secrets = [
{
key: 'IMAGE_SALT',
missingMessage: 'Images will fallback to a placeholder image',
},
];

module.exports = secrets;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
declare module 'dotenv-webpack';
mxdvl marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 2 additions & 0 deletions dotcom-rendering/scripts/webpack/webpack.config.server.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// @ts-check
const Dotenv = require('dotenv-webpack');
const GuStatsReportPlugin = require('./plugins/gu-stats-report-plugin');

const DEV = process.env.NODE_ENV === 'development';
Expand Down Expand Up @@ -60,6 +61,7 @@ module.exports = ({ sessionId }) => ({
team: 'dotcom',
sessionId,
}),
new Dotenv(),
]
: undefined,
module: {
Expand Down
86 changes: 0 additions & 86 deletions dotcom-rendering/src/server/lib/aws/aws-parameters.ts

This file was deleted.

16 changes: 0 additions & 16 deletions dotcom-rendering/src/server/prod-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,6 @@ import {
render as renderAMPArticle,
renderPerfTest as renderAMPArticlePerfTest,
} from '../amp/server';
import { log, warn } from '../../scripts/env/log';
import {
getGuardianConfiguration,
GuardianConfiguration,
} from './lib/aws/aws-parameters';
import { recordBaselineCloudWatchMetrics } from './lib/aws/metrics-baseline';
import { logger } from './lib/logging';
import { getContentFromURLMiddleware } from './lib/get-content-from-url';
Expand All @@ -39,17 +34,6 @@ const logRenderTime = responseTime(
export const prodServer = () => {
logger.info('dotcom-rendering is GO.');

if (process.env.DISABLE_LOGGING_AND_METRICS !== 'true') {
getGuardianConfiguration('prod')
.then((config: GuardianConfiguration) => {
log(`loaded ${config.size()} configuration parameters`);
})
.catch((err: any) => {
warn('Failed to get configuration. Bad AWS credentials?');
warn(err);
});
}

const app = express();

app.use(express.json({ limit: '50mb' }));
Expand Down
Loading