mirror of
https://github.com/apache/superset.git
synced 2026-04-19 08:04:53 +00:00
feat: Support env vars configuration for WebSocket server (#14398)
This commit is contained in:
@@ -64,6 +64,8 @@ npm install
|
||||
|
||||
Copy `config.example.json` to `config.json` and adjust the values for your environment.
|
||||
|
||||
Configuration via environment variables is also supported which can be helpful in certain contexts, e.g., deployment. `src/config.ts` can be consulted to see the full list of supported values.
|
||||
|
||||
## Superset Configuration
|
||||
|
||||
Configure the Superset Flask app to enable global async queries (in `superset_config.py`):
|
||||
|
||||
69
superset-websocket/spec/config.spec.ts
Normal file
69
superset-websocket/spec/config.spec.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { buildConfig } from '../src/config';
|
||||
|
||||
describe('buildConfig', () => {
|
||||
test('builds configuration and applies env overrides', () => {
|
||||
let config = buildConfig();
|
||||
|
||||
expect(config.jwtSecret).toEqual(
|
||||
'test123-test123-test123-test123-test123-test123-test123',
|
||||
);
|
||||
expect(config.redis.host).toEqual('127.0.0.1');
|
||||
expect(config.redis.port).toEqual(6379);
|
||||
expect(config.redis.password).toEqual('');
|
||||
expect(config.redis.db).toEqual(10);
|
||||
expect(config.redis.ssl).toEqual(false);
|
||||
expect(config.statsd.host).toEqual('127.0.0.1');
|
||||
expect(config.statsd.port).toEqual(8125);
|
||||
expect(config.statsd.globalTags).toEqual([]);
|
||||
|
||||
process.env.JWT_SECRET = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
|
||||
process.env.REDIS_HOST = '10.10.10.10';
|
||||
process.env.REDIS_PORT = '6380';
|
||||
process.env.REDIS_PASSWORD = 'admin';
|
||||
process.env.REDIS_DB = '4';
|
||||
process.env.REDIS_SSL = 'true';
|
||||
process.env.STATSD_HOST = '15.15.15.15';
|
||||
process.env.STATSD_PORT = '8000';
|
||||
process.env.STATSD_GLOBAL_TAGS = 'tag-1,tag-2';
|
||||
|
||||
config = buildConfig();
|
||||
|
||||
expect(config.jwtSecret).toEqual('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa');
|
||||
expect(config.redis.host).toEqual('10.10.10.10');
|
||||
expect(config.redis.port).toEqual(6380);
|
||||
expect(config.redis.password).toEqual('admin');
|
||||
expect(config.redis.db).toEqual(4);
|
||||
expect(config.redis.ssl).toEqual(true);
|
||||
expect(config.statsd.host).toEqual('15.15.15.15');
|
||||
expect(config.statsd.port).toEqual(8000);
|
||||
expect(config.statsd.globalTags).toEqual(['tag-1', 'tag-2']);
|
||||
|
||||
delete process.env.JWT_SECRET;
|
||||
delete process.env.REDIS_HOST;
|
||||
delete process.env.REDIS_PORT;
|
||||
delete process.env.REDIS_PASSWORD;
|
||||
delete process.env.REDIS_DB;
|
||||
delete process.env.REDIS_SSL;
|
||||
delete process.env.STATSD_HOST;
|
||||
delete process.env.STATSD_PORT;
|
||||
delete process.env.STATSD_GLOBAL_TAGS;
|
||||
});
|
||||
});
|
||||
133
superset-websocket/src/config.ts
Normal file
133
superset-websocket/src/config.ts
Normal file
@@ -0,0 +1,133 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
type ConfigType = {
|
||||
port: number;
|
||||
logLevel: string;
|
||||
logToFile: boolean;
|
||||
logFilename: string;
|
||||
statsd: {
|
||||
host: string;
|
||||
port: number;
|
||||
globalTags: Array<string>;
|
||||
};
|
||||
redis: {
|
||||
port: number;
|
||||
host: string;
|
||||
password: string;
|
||||
db: number;
|
||||
ssl: boolean;
|
||||
};
|
||||
redisStreamPrefix: string;
|
||||
redisStreamReadCount: number;
|
||||
redisStreamReadBlockMs: number;
|
||||
jwtSecret: string;
|
||||
jwtCookieName: string;
|
||||
socketResponseTimeoutMs: number;
|
||||
pingSocketsIntervalMs: number;
|
||||
gcChannelsIntervalMs: number;
|
||||
};
|
||||
|
||||
function defaultConfig(): ConfigType {
|
||||
return {
|
||||
port: 8080,
|
||||
logLevel: 'info',
|
||||
logToFile: false,
|
||||
logFilename: 'app.log',
|
||||
redisStreamPrefix: 'async-events-',
|
||||
redisStreamReadCount: 100,
|
||||
redisStreamReadBlockMs: 5000,
|
||||
jwtSecret: '',
|
||||
jwtCookieName: 'async-token',
|
||||
socketResponseTimeoutMs: 60 * 1000,
|
||||
pingSocketsIntervalMs: 20 * 1000,
|
||||
gcChannelsIntervalMs: 120 * 1000,
|
||||
statsd: {
|
||||
host: '127.0.0.1',
|
||||
port: 8125,
|
||||
globalTags: [],
|
||||
},
|
||||
redis: {
|
||||
host: '127.0.0.1',
|
||||
port: 6379,
|
||||
password: '',
|
||||
db: 0,
|
||||
ssl: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function configFromFile(): Partial<ConfigType> {
|
||||
const isTest = process.env.NODE_ENV === 'test';
|
||||
const configFile = isTest ? '../config.test.json' : '../config.json';
|
||||
try {
|
||||
return require(configFile);
|
||||
} catch (err) {
|
||||
console.warn('config.json file not found');
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const isPresent = (s: string) => /\S+/.test(s);
|
||||
const toNumber = Number;
|
||||
const toBoolean = (s: string) => s.toLowerCase() === 'true';
|
||||
const toStringArray = (s: string) => s.split(',');
|
||||
|
||||
function applyEnvOverrides(config: ConfigType): ConfigType {
|
||||
const envVarConfigSetter: { [envVar: string]: (val: string) => void } = {
|
||||
PORT: val => (config.port = toNumber(val)),
|
||||
LOG_LEVEL: val => (config.logLevel = val),
|
||||
LOG_TO_FILE: val => (config.logToFile = toBoolean(val)),
|
||||
LOG_FILENAME: val => (config.logFilename = val),
|
||||
REDIS_STREAM_PREFIX: val => (config.redisStreamPrefix = val),
|
||||
REDIS_STREAM_READ_COUNT: val =>
|
||||
(config.redisStreamReadCount = toNumber(val)),
|
||||
REDIS_STREAM_READ_BLOCK_MS: val =>
|
||||
(config.redisStreamReadBlockMs = toNumber(val)),
|
||||
JWT_SECRET: val => (config.jwtSecret = val),
|
||||
JWT_COOKIE_NAME: val => (config.jwtCookieName = val),
|
||||
SOCKET_RESPONSE_TIMEOUT_MS: val =>
|
||||
(config.socketResponseTimeoutMs = toNumber(val)),
|
||||
PING_SOCKETS_INTERVAL_MS: val =>
|
||||
(config.pingSocketsIntervalMs = toNumber(val)),
|
||||
GC_CHANNELS_INTERVAL_MS: val =>
|
||||
(config.gcChannelsIntervalMs = toNumber(val)),
|
||||
REDIS_HOST: val => (config.redis.host = val),
|
||||
REDIS_PORT: val => (config.redis.port = toNumber(val)),
|
||||
REDIS_PASSWORD: val => (config.redis.password = val),
|
||||
REDIS_DB: val => (config.redis.db = toNumber(val)),
|
||||
REDIS_SSL: val => (config.redis.ssl = toBoolean(val)),
|
||||
STATSD_HOST: val => (config.statsd.host = val),
|
||||
STATSD_PORT: val => (config.statsd.port = toNumber(val)),
|
||||
STATSD_GLOBAL_TAGS: val => (config.statsd.globalTags = toStringArray(val)),
|
||||
};
|
||||
|
||||
for (const [envVar, set] of Object.entries(envVarConfigSetter)) {
|
||||
const envValue = process.env[envVar];
|
||||
if (envValue && isPresent(envValue)) {
|
||||
set(envValue);
|
||||
}
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
export function buildConfig(): ConfigType {
|
||||
const config = Object.assign(defaultConfig(), configFromFile());
|
||||
return applyEnvOverrides(config);
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import Redis from 'ioredis';
|
||||
import StatsD from 'hot-shots';
|
||||
|
||||
import { createLogger } from './logger';
|
||||
import { buildConfig } from './config';
|
||||
|
||||
export type StreamResult = [
|
||||
recordId: string,
|
||||
@@ -79,45 +80,9 @@ interface ChannelValue {
|
||||
|
||||
const environment = process.env.NODE_ENV;
|
||||
|
||||
// default options
|
||||
export const opts = {
|
||||
port: 8080,
|
||||
logLevel: 'info',
|
||||
logToFile: false,
|
||||
logFilename: 'app.log',
|
||||
statsd: {
|
||||
host: '127.0.0.1',
|
||||
port: 8125,
|
||||
globalTags: [],
|
||||
},
|
||||
redis: {
|
||||
port: 6379,
|
||||
host: '127.0.0.1',
|
||||
password: '',
|
||||
db: 0,
|
||||
ssl: false,
|
||||
},
|
||||
redisStreamPrefix: 'async-events-',
|
||||
redisStreamReadCount: 100,
|
||||
redisStreamReadBlockMs: 5000,
|
||||
jwtSecret: '',
|
||||
jwtCookieName: 'async-token',
|
||||
socketResponseTimeoutMs: 60 * 1000,
|
||||
pingSocketsIntervalMs: 20 * 1000,
|
||||
gcChannelsIntervalMs: 120 * 1000,
|
||||
};
|
||||
|
||||
const startServer = process.argv[2] === 'start';
|
||||
const configFile =
|
||||
environment === 'test' ? '../config.test.json' : '../config.json';
|
||||
let config = {};
|
||||
try {
|
||||
config = require(configFile);
|
||||
} catch (err) {
|
||||
console.error('config.json not found, using defaults');
|
||||
}
|
||||
// apply config overrides
|
||||
Object.assign(opts, config);
|
||||
|
||||
export const opts = buildConfig();
|
||||
|
||||
// init logger
|
||||
const logger = createLogger({
|
||||
|
||||
Reference in New Issue
Block a user