plywood-proxy
Version:
A Plywood proxy
261 lines (215 loc) • 8.18 kB
text/typescript
;
import * as path from 'path';
import * as Q from 'q';
import * as nopt from 'nopt';
import { DataSource, DataSourceJS } from '../common/models/index';
import { DataSourceManager, dataSourceManagerFactory, loadFileSync, dataSourceToYAML, properDruidRequesterFactory, dataSourceFillerFactory } from './utils/index';
export interface PivotConfig {
port?: number;
verbose?: boolean;
druidHost?: string;
timeout?: number;
useSegmentMetadata?: string;
sourceListScan?: string;
sourceListRefreshInterval?: number;
dataSources?: DataSourceJS[];
}
var packageObj = loadFileSync(path.join(__dirname, '../../package.json'), 'json') || {};
export const VERSION = packageObj.version;
function errorExit(e: Error): void {
console.error(e.message);
console.error((e as any).stack);
process.exit(1);
}
function printUsage() {
console.log(`
Usage: plywood-proxy [options]
Possible usage:
plywood-proxy --example wiki
plywood-proxy --druid your.broker.host:8082
--help Print this help message
--version Display the version number
-v, --verbose Display the DB queries that are being made
-p, --port The port pivot will run on
--example Start pivot with some example data (overrides all other options)
-c, --config The configuration YAML files to use
--print-config Prints out the auto generated config
--with-comments Adds comments when printing the auto generated config
--data-sources-only Only print the data sources in the auto generated config
-f, --file Start pivot on top of this file based data source (must be JSON, CSV, or TSV)
-d, --druid The Druid broker node to connect to
--use-segment-metadata Use the segmentMetadata query for introspection instead of GET /druid/v2/datasources/...
`
);
}
function parseArgs() {
return nopt(
{
"help": Boolean,
"version": Boolean,
"verbose": Boolean,
"port": Number,
"example": String,
"config": String,
"print-config": Boolean,
"with-comments": Boolean,
"data-sources-only": Boolean,
"file": String,
"druid": String,
"use-segment-metadata": Boolean
},
{
"v": ["--verbose"],
"p": ["--port"],
"c": ["--config"],
"f": ["--file"],
"d": ["--druid"]
},
process.argv
);
}
var parsedArgs = parseArgs();
//console.log(parsedArgs);
if (parsedArgs['help']) {
printUsage();
process.exit();
}
if (parsedArgs['version']) {
console.log(packageObj.version);
process.exit();
}
const DEFAULT_CONFIG: PivotConfig = {
port: 9090,
sourceListScan: 'auto',
sourceListRefreshInterval: 10000,
dataSources: []
};
var exampleConfig: PivotConfig = null;
if (parsedArgs['example']) {
delete parsedArgs['druid'];
var example = parsedArgs['example'];
if (example === 'wiki') {
exampleConfig = loadFileSync(path.join(__dirname, `../../config-example-${example}.yaml`), 'yaml');
} else {
console.log(`Unknown example '${example}'. Possible examples are: wiki`);
process.exit();
}
}
if (!parsedArgs['example'] && !parsedArgs['config'] && !parsedArgs['druid'] && !parsedArgs['file']) {
printUsage();
process.exit();
}
var configFilePath = parsedArgs['config'];
var config: PivotConfig;
if (configFilePath) {
config = loadFileSync(configFilePath, 'yaml');
if (!config) {
config = DEFAULT_CONFIG;
console.log(`Could not load config from '${configFilePath}', using default`);
}
} else {
config = DEFAULT_CONFIG;
}
// If there is an example config take its dataSources
if (exampleConfig && Array.isArray(exampleConfig.dataSources)) {
config.dataSources = exampleConfig.dataSources;
}
// If a file is specified add it as a dataSource
var file = parsedArgs['file'];
if (file) {
config.dataSources.push({
name: path.basename(file, path.extname(file)),
engine: 'native',
source: file
});
}
export const PRINT_CONFIG = Boolean(parsedArgs['print-config']);
export const START_SERVER = !PRINT_CONFIG;
export const VERBOSE = Boolean(parsedArgs['verbose'] || config.verbose);
export const PORT = parseInt(parsedArgs['port'] || config.port, 10);
export const DRUID_HOST = parsedArgs['druid'] || config.druidHost;
export const TIMEOUT = parseInt(<any>config.timeout, 10) || 30000;
export const USE_SEGMENT_METADATA = Boolean(parsedArgs["use-segment-metadata"] || config.useSegmentMetadata);
export const SOURCE_LIST_SCAN = START_SERVER ? config.sourceListScan : 'disable';
export const SOURCE_LIST_REFRESH_INTERVAL = START_SERVER ? (parseInt(<any>config.sourceListRefreshInterval, 10) || 10000) : 0;
if (SOURCE_LIST_REFRESH_INTERVAL && SOURCE_LIST_REFRESH_INTERVAL < 1000) {
errorExit(new Error('can not refresh more often than once per second'));
}
export const DATA_SOURCES: DataSource[] = (config.dataSources || []).map((dataSourceJS: DataSourceJS, i: number) => {
if (typeof dataSourceJS !== 'object') errorExit(new Error(`DataSource ${i} is not valid`));
var dataSourceName = dataSourceJS.name;
if (typeof dataSourceName !== 'string') errorExit(new Error(`DataSource ${i} must have a name`));
// Convert maxTime into refreshRule if a maxTime exists
if (dataSourceJS.maxTime && (typeof dataSourceJS.maxTime === 'string' || (<any>dataSourceJS.maxTime).toISOString)) {
dataSourceJS.refreshRule = { rule: 'fixed', time: <any>dataSourceJS.maxTime };
console.warn('maxTime found in config, this is deprecated please convert it to a refreshRule like so:', dataSourceJS.refreshRule);
delete dataSourceJS.maxTime;
}
try {
return DataSource.fromJS(dataSourceJS);
} catch (e) {
errorExit(e);
}
});
var druidRequester = properDruidRequesterFactory({
druidHost: DRUID_HOST,
timeout: TIMEOUT,
verbose: VERBOSE,
concurrentLimit: 5
});
var fileDirectory = path.join(__dirname, '../..');
export const DATA_SOURCE_MANAGER: DataSourceManager = dataSourceManagerFactory({
dataSources: DATA_SOURCES,
druidRequester,
dataSourceFiller: dataSourceFillerFactory(druidRequester, fileDirectory, TIMEOUT, USE_SEGMENT_METADATA),
sourceListScan: SOURCE_LIST_SCAN,
sourceListRefreshInterval: SOURCE_LIST_REFRESH_INTERVAL,
log: PRINT_CONFIG ? null : (line: string) => console.log(line)
});
if (PRINT_CONFIG) {
var withComments = Boolean(parsedArgs['with-comments']);
var dataSourcesOnly = Boolean(parsedArgs['data-sources-only']);
DATA_SOURCE_MANAGER.getQueryableDataSources().then((dataSources) => {
var lines = [
`# generated by Pivot version ${VERSION}`,
''
];
if (!dataSourcesOnly) {
if (VERBOSE) {
if (withComments) {
lines.push("# Run Pivot in verbose mode so it prints out the queries that it issues");
}
lines.push(`verbose: true`, '');
}
if (withComments) {
lines.push("# The port on which the Pivot server will listen on");
}
lines.push(`port: ${PORT}`, '');
if (DRUID_HOST) {
if (withComments) {
lines.push("# A Druid broker node that can serve data (only used if you have Druid based data source)");
}
lines.push(`druidHost: ${DRUID_HOST}`, '');
if (withComments) {
lines.push("# A timeout for the Druid queries in ms (default: 30000)");
lines.push("#timeout: 30000", '');
}
}
if (USE_SEGMENT_METADATA) {
if (withComments) {
lines.push("# Use the segmentMetadata query for introspection (only works well with Druid >= 0.8.2)");
}
lines.push(`useSegmentMetadata: true`, '');
}
lines.push("# Should new datasources automatically be added");
lines.push(`sourceListScan: disable`, '');
}
if (dataSources.length) {
lines.push('dataSources:');
lines = lines.concat.apply(lines, dataSources.map(d => dataSourceToYAML(d, withComments)));
} else {
lines.push('dataSources: [] # Could not find any data sources please verify network connectivity');
}
console.log(lines.join('\n'));
}).done();
}