Skip to content

Commit ad5773c

Browse files
authored
fix: config loading required vars (#1955)
1 parent 0fa240d commit ad5773c

12 files changed

Lines changed: 120 additions & 130 deletions

File tree

.env.tpl

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,19 @@ DATABASE_URL=http://localhost:3000
2727
DATABASE_TOKEN=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJzdXBhYmFzZSIsImlhdCI6MTYwMzk2ODgzNCwiZXhwIjoyNTUwNjUzNjM0LCJyb2xlIjoic2VydmljZV9yb2xlIn0.necIJaiP7X2T2QjGeV-FhpkizcNTX8HjDDBAxpgQTEI
2828

2929
# Postgres Database
30-
DATABASE_CONNECTION=postgresql://postgres:postgres@localhost:5432/postgres
30+
DATABASE_CONNECTION=postgresql://postgres:postgres@localhost:5432/postgres
31+
32+
# Cluster
33+
CLUSTER_BASIC_AUTH_TOKEN = dGVzdDp0ZXN0
34+
CLUSTER_SERVICE =
35+
CLUSTER_API_URL = http://localhost:9094
36+
37+
# Maintenance Mode
38+
MAINTENANCE_MODE = rw
39+
40+
# S3
41+
S3_ENDPOINT = http://localhost:9095
42+
S3_REGION = test
43+
S3_ACCESS_KEY_ID = test
44+
S3_SECRET_ACCESS_KEY = test
45+
S3_BUCKET_NAME = test

packages/api/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ wrangler secret put S3_ACCESS_KEY_ID --env production # Get from Amazon S3 (not
7676
wrangler secret put S3_SECRET_ACCESS_KEY --env production # Get from Amazon S3 (not required for dev)
7777
wrangler secret put S3_BUCKET_NAME --env production # e.g nft.storage-staging-us-east-2 (not required for dev)
7878
wrangler secret put PRIVATE_KEY --env production # Get from 1password
79+
wrangler secret put MAINTENANCE_MODE --env production # default value is "rw"
80+
7981
wrangler publish --env production
8082
```
8183

packages/api/pw-test.config.cjs

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,23 @@ const nodeBuiltinsPlugin = {
2424
},
2525
}
2626

27+
const config = {
28+
inject: [
29+
path.join(__dirname, './scripts/node-globals.js'),
30+
path.join(__dirname, './test/scripts/globals.js'),
31+
],
32+
define: {
33+
NFT_STORAGE_VERSION: JSON.stringify('0.1.0'),
34+
NFT_STORAGE_COMMITHASH: JSON.stringify('322332'),
35+
NFT_STORAGE_BRANCH: JSON.stringify('main'),
36+
},
37+
plugins: [nodeBuiltinsPlugin],
38+
}
39+
2740
/** @type {import('playwright-test').RunnerOptions} */
2841
module.exports = {
29-
buildConfig: {
30-
inject: [path.join(__dirname, './scripts/node-globals.js')],
31-
plugins: [nodeBuiltinsPlugin],
32-
},
33-
buildSWConfig: {
34-
inject: [path.join(__dirname, './scripts/node-globals.js')],
35-
plugins: [nodeBuiltinsPlugin],
36-
},
42+
buildConfig: config,
43+
buildSWConfig: config,
3744
beforeTests: async () => {
3845
const mock = await startMockServer('AWS S3', 9095, 'test/mocks/aws-s3')
3946

packages/api/src/bindings.d.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@ export interface ServiceConfiguration {
1717
ENV: RuntimeEnvironmentName
1818

1919
/** Semantic version for current build */
20-
NFT_STORAGE_VERSION: string
20+
VERSION: string
2121

2222
/** Git branch name of current build */
23-
NFT_STORAGE_BRANCH: string
23+
BRANCH: string
2424

2525
/** Git commit hash of current build */
26-
NFT_STORAGE_COMMITHASH: string
26+
COMMITHASH: string
2727

2828
/** Current maintenance mode */
2929
MAINTENANCE_MODE: Mode

packages/api/src/config.js

Lines changed: 45 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -8,37 +8,6 @@ import {
88
* @typedef {import('./bindings').RuntimeEnvironmentName} RuntimeEnvironmentName
99
*/
1010

11-
/**
12-
* Default configuration values to be used in test and dev if no explicit definition is found.
13-
*
14-
* @type Record<string, string>
15-
*/
16-
export const DEFAULT_CONFIG_VALUES = {
17-
SALT: 'secret',
18-
DEBUG: 'true',
19-
DATABASE_URL: 'http://localhost:3000',
20-
DATABASE_TOKEN:
21-
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJzdXBhYmFzZSIsImlhdCI6MTYwMzk2ODgzNCwiZXhwIjoyNTUwNjUzNjM0LCJyb2xlIjoic2VydmljZV9yb2xlIn0.necIJaiP7X2T2QjGeV-FhpkizcNTX8HjDDBAxpgQTEI',
22-
CLUSTER_API_URL: 'http://localhost:9094',
23-
CLUSTER_BASIC_AUTH_TOKEN: 'dGVzdDp0ZXN0', // test:test
24-
MAGIC_SECRET_KEY: 'test',
25-
ENV: 'test',
26-
SENTRY_DSN: 'https://test@test.ingest.sentry.io/0000000',
27-
NFT_STORAGE_BRANCH: 'test',
28-
NFT_STORAGE_VERSION: 'test',
29-
NFT_STORAGE_COMMITHASH: 'test',
30-
MAINTENANCE_MODE: 'rw',
31-
METAPLEX_AUTH_TOKEN: 'metaplex-test-token',
32-
MAILCHIMP_API_KEY: '',
33-
LOGTAIL_TOKEN: '',
34-
S3_ENDPOINT: 'http://localhost:9095',
35-
S3_REGION: 'test',
36-
S3_ACCESS_KEY_ID: 'test',
37-
S3_SECRET_ACCESS_KEY: 'test',
38-
S3_BUCKET_NAME: 'test',
39-
PRIVATE_KEY: 'xmbtWjE9eYuAxae9G65lQSkw36HV6H+0LSFq2aKqVwY=',
40-
}
41-
4211
/**
4312
* If the CLUSTER_SERVICE variable is set, the service URL will be resolved from here.
4413
*
@@ -49,12 +18,6 @@ const CLUSTER_SERVICE_URLS = {
4918
IpfsCluster3: 'https://nft3.storage.ipfscluster.io/api/',
5019
}
5120

52-
/**
53-
* @param {RuntimeEnvironmentName} env
54-
* @returns {boolean} true if the named runtime environment should fallback to values from DEFAULT_CONFIG_VALUES if no config var is present.
55-
*/
56-
const allowDefaultConfigValues = (env) => env === 'test' || env === 'dev'
57-
5821
/** @type ServiceConfiguration|undefined */
5922
let _globalConfig
6023

@@ -90,14 +53,6 @@ export const overrideServiceConfigForTesting = (config) => {
9053
*
9154
* Exported for testing. See {@link getServiceConfig} for main public accessor.
9255
*
93-
* Config values are resolved by looking for global variables with the names matching the keys of {@link DEFAULT_CONFIG_VALUES}.
94-
*
95-
* If no global value is found for a variable, an error will be thrown if the runtimeEnvironment (ENV variable)
96-
* is set to a "production like" environment.
97-
*
98-
* If {@link allowDefaultConfigValues} returns true for the current environment, the value from {@link DEFAULT_CONFIG_VALUES} will be
99-
* used if a variable is missing.
100-
*
10156
* @returns {ServiceConfiguration}
10257
*/
10358
export function loadServiceConfig() {
@@ -114,13 +69,14 @@ export function loadServiceConfig() {
11469
* @returns {ServiceConfiguration}
11570
*/
11671
export function serviceConfigFromVariables(vars) {
117-
let clusterUrl = vars.CLUSTER_API_URL
118-
if (vars.CLUSTER_SERVICE) {
119-
const serviceUrl = CLUSTER_SERVICE_URLS[vars.CLUSTER_SERVICE]
120-
if (!serviceUrl) {
72+
let clusterUrl
73+
if (!vars.CLUSTER_SERVICE) {
74+
clusterUrl = vars.CLUSTER_API_URL
75+
} else {
76+
clusterUrl = CLUSTER_SERVICE_URLS[vars.CLUSTER_SERVICE]
77+
if (!clusterUrl) {
12178
throw new Error(`unknown cluster service: ${vars.CLUSTER_SERVICE}`)
12279
}
123-
clusterUrl = serviceUrl
12480
}
12581

12682
return {
@@ -135,9 +91,6 @@ export function serviceConfigFromVariables(vars) {
13591
CLUSTER_BASIC_AUTH_TOKEN: vars.CLUSTER_BASIC_AUTH_TOKEN,
13692
MAGIC_SECRET_KEY: vars.MAGIC_SECRET_KEY,
13793
SENTRY_DSN: vars.SENTRY_DSN,
138-
NFT_STORAGE_BRANCH: vars.NFT_STORAGE_BRANCH,
139-
NFT_STORAGE_VERSION: vars.NFT_STORAGE_VERSION,
140-
NFT_STORAGE_COMMITHASH: vars.NFT_STORAGE_COMMITHASH,
14194
METAPLEX_AUTH_TOKEN: vars.METAPLEX_AUTH_TOKEN,
14295
MAILCHIMP_API_KEY: vars.MAILCHIMP_API_KEY,
14396
LOGTAIL_TOKEN: vars.LOGTAIL_TOKEN,
@@ -147,6 +100,13 @@ export function serviceConfigFromVariables(vars) {
147100
S3_SECRET_ACCESS_KEY: vars.S3_SECRET_ACCESS_KEY,
148101
S3_BUCKET_NAME: vars.S3_BUCKET_NAME,
149102
PRIVATE_KEY: vars.PRIVATE_KEY,
103+
// These are injected in esbuild
104+
// @ts-ignore
105+
BRANCH: NFT_STORAGE_BRANCH,
106+
// @ts-ignore
107+
VERSION: NFT_STORAGE_VERSION,
108+
// @ts-ignore
109+
COMMITHASH: NFT_STORAGE_COMMITHASH,
150110
}
151111
}
152112

@@ -166,29 +126,45 @@ export function loadConfigVariables() {
166126
/** @type Record<string, unknown> */
167127
const globals = globalThis
168128

169-
const notFound = []
170-
for (const name of Object.keys(DEFAULT_CONFIG_VALUES)) {
129+
const required = [
130+
'ENV',
131+
'DEBUG',
132+
'SALT',
133+
'DATABASE_URL',
134+
'DATABASE_TOKEN',
135+
'MAGIC_SECRET_KEY',
136+
'MAILCHIMP_API_KEY',
137+
'METAPLEX_AUTH_TOKEN',
138+
'LOGTAIL_TOKEN',
139+
'PRIVATE_KEY',
140+
'SENTRY_DSN',
141+
'CLUSTER_BASIC_AUTH_TOKEN',
142+
'MAINTENANCE_MODE',
143+
'S3_REGION',
144+
'S3_ACCESS_KEY_ID',
145+
'S3_SECRET_ACCESS_KEY',
146+
'S3_BUCKET_NAME',
147+
]
148+
149+
for (const name of required) {
171150
const val = globals[name]
172151
if (typeof val === 'string') {
173152
vars[name] = val
174153
} else {
175-
notFound.push(name)
176-
}
177-
}
178-
179-
if (notFound.length !== 0) {
180-
const env = parseRuntimeEnv(vars.ENV)
181-
if (!allowDefaultConfigValues(env)) {
182154
throw new Error(
183-
'Missing required config variables: ' + notFound.join(', ')
155+
`Missing required config variables: ${name}. Check your .env, testing globals or cloudflare vars.`
184156
)
185157
}
186-
console.warn(
187-
'Using default values for config variables: ',
188-
notFound.join(', ')
189-
)
190-
for (const name of notFound) {
191-
vars[name] = DEFAULT_CONFIG_VALUES[name]
158+
}
159+
160+
const optional = ['CLUSTER_SERVICE', 'CLUSTER_API_URL', 'S3_ENDPOINT']
161+
162+
for (const name of optional) {
163+
const val = globals[name]
164+
if (typeof val === 'string') {
165+
vars[name] = val
166+
} else {
167+
console.warn(`Missing optional config variables: ${name}`)
192168
}
193169
}
194170

packages/api/src/index.js

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -58,16 +58,11 @@ r.add(
5858
'get',
5959
'/version',
6060
(event) => {
61-
const {
62-
NFT_STORAGE_VERSION,
63-
NFT_STORAGE_COMMITHASH,
64-
NFT_STORAGE_BRANCH,
65-
MAINTENANCE_MODE,
66-
} = getServiceConfig()
61+
const { VERSION, COMMITHASH, BRANCH, MAINTENANCE_MODE } = getServiceConfig()
6762
return new JSONResponse({
68-
version: NFT_STORAGE_VERSION,
69-
commit: NFT_STORAGE_COMMITHASH,
70-
branch: NFT_STORAGE_BRANCH,
63+
version: VERSION,
64+
commit: COMMITHASH,
65+
branch: BRANCH,
7166
mode: MAINTENANCE_MODE,
7267
})
7368
},

packages/api/src/utils/context.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ const sentryOptions = {
3232
rewriteFrames: {
3333
root: '/',
3434
},
35-
release: config.NFT_STORAGE_VERSION,
35+
release: config.VERSION,
3636
pkg,
3737
}
3838

packages/api/src/utils/logs.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { nanoid } from 'nanoid/non-secure'
22
import { getServiceConfig } from '../config'
33

4-
const { NFT_STORAGE_VERSION, NFT_STORAGE_COMMITHASH, NFT_STORAGE_BRANCH } =
5-
getServiceConfig()
4+
const { VERSION, COMMITHASH, BRANCH } = getServiceConfig()
65
const logtailApiURL = 'https://in.logtail.com/'
76

87
const buildMetadataFromHeaders = (/** @type {Headers} */ headers) => {
@@ -56,9 +55,9 @@ export class Logging {
5655
cf: rCf,
5756
},
5857
cloudflare_worker: {
59-
version: NFT_STORAGE_VERSION,
60-
commit: NFT_STORAGE_COMMITHASH,
61-
branch: NFT_STORAGE_BRANCH,
58+
version: VERSION,
59+
commit: COMMITHASH,
60+
branch: BRANCH,
6261
worker_id: nanoid(10),
6362
worker_started: this.startTs,
6463
},

packages/api/test/config.spec.js

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,13 @@
1-
import * as assert from 'assert'
1+
import assert from 'assert'
22
import {
33
serviceConfigFromVariables,
4-
DEFAULT_CONFIG_VALUES,
54
loadConfigVariables,
65
loadServiceConfig,
76
} from '../src/config.js'
87

98
/** @type Record<string, unknown> */
109
const globals = globalThis
1110

12-
/**
13-
* Helper to remove all the config variables we care about from the global context.
14-
*/
15-
const scrubGlobals = () => {
16-
for (const name of Object.keys(DEFAULT_CONFIG_VALUES)) {
17-
delete globals[name]
18-
}
19-
}
20-
2111
/**
2212
* @param {Record<string, string>} vars
2313
*/
@@ -28,17 +18,6 @@ const defineGlobals = (vars) => {
2818
}
2919

3020
describe('loadServiceConfig', () => {
31-
beforeEach(scrubGlobals)
32-
afterEach(scrubGlobals)
33-
34-
it('fails if config vars are missing when ENV == "staging" or "production"', () => {
35-
const strictEnvs = ['staging', 'production']
36-
for (const env of strictEnvs) {
37-
defineGlobals({ ENV: env })
38-
assert.throws(() => loadServiceConfig())
39-
}
40-
})
41-
4221
it('uses default config values for missing vars when ENV == "test" or "dev"', () => {
4322
const lenientEnvs = ['test', 'dev']
4423
for (const env of lenientEnvs) {
@@ -51,9 +30,6 @@ describe('loadServiceConfig', () => {
5130
})
5231

5332
describe('loadConfigVariables', () => {
54-
beforeEach(scrubGlobals)
55-
afterEach(scrubGlobals)
56-
5733
it('looks up values on the globalThis object', () => {
5834
globals['SALT'] = 'extra-salty'
5935
const vars = loadConfigVariables()
@@ -125,9 +101,6 @@ describe('serviceConfigFromVariables', () => {
125101

126102
describe('uses unaltered values for string config variables', () => {
127103
const stringValuedVars = [
128-
'NFT_STORAGE_VERSION',
129-
'NFT_STORAGE_BRANCH',
130-
'NFT_STORAGE_COMMITHASH',
131104
'SALT',
132105
'METAPLEX_AUTH_TOKEN',
133106
'PRIVATE_KEY',
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
globalThis.ENV = 'test'
2+
globalThis.DEBUG = 'true'
3+
globalThis.SALT = 'secret'
4+
globalThis.DATABASE_URL = 'http://localhost:3000'
5+
globalThis.DATABASE_TOKEN =
6+
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJzdXBhYmFzZSIsImlhdCI6MTYwMzk2ODgzNCwiZXhwIjoyNTUwNjUzNjM0LCJyb2xlIjoic2VydmljZV9yb2xlIn0.necIJaiP7X2T2QjGeV-FhpkizcNTX8HjDDBAxpgQTEI'
7+
8+
globalThis.MAGIC_SECRET_KEY = 'test'
9+
globalThis.MAILCHIMP_API_KEY = ''
10+
globalThis.METAPLEX_AUTH_TOKEN = 'metaplex-test-token'
11+
globalThis.LOGTAIL_TOKEN = ''
12+
globalThis.PRIVATE_KEY = 'xmbtWjE9eYuAxae9G65lQSkw36HV6H+0LSFq2aKqVwY='
13+
globalThis.SENTRY_DSN = 'https://test@test.ingest.sentry.io/0000000'
14+
15+
globalThis.CLUSTER_API_URL = 'http://localhost:9094'
16+
// will be used with we can active auth in cluster base64 of test:test
17+
globalThis.CLUSTER_BASIC_AUTH_TOKEN = 'dGVzdDp0ZXN0'
18+
globalThis.CLUSTER_SERVICE = ''
19+
20+
globalThis.MAINTENANCE_MODE = 'rw'
21+
22+
globalThis.S3_ENDPOINT = 'http://localhost:9095'
23+
globalThis.S3_REGION = 'test'
24+
globalThis.S3_ACCESS_KEY_ID = 'test'
25+
globalThis.S3_SECRET_ACCESS_KEY = 'test'
26+
globalThis.S3_BUCKET_NAME = 'test'

0 commit comments

Comments
 (0)