diff --git a/.npmignore b/.npmignore index 01b7b93..05431c7 100644 --- a/.npmignore +++ b/.npmignore @@ -2,7 +2,8 @@ src spec .travis.yml jest.config.js -tsconfig.lib.json -tsconfig.bin.json -tsconfig.jest.json - +lib.tsconfig.json +bin.tsconfig.json +webpack.config.js +yarn.lock +.vscode diff --git a/README.md b/README.md index 0c70acd..060cdce 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,43 @@ # tsdb-kit -[![Build Status](https://travis-ci.org/CorpGlory/tsdb-kit.svg?branch=master)](https://travis-ci.org/CorpGlory/tsdb-kit) +TSDB-kit is a node.js library and CLI-tool for querying timeseries-datasources. -Node.js library and utilities for running Grafana datasources on backend. -You can send your datasource metrics from Grafana to compile it on Node.js and query your datasource via Grafana API in background. +## Features -User gets a unified interface to all datasources. Library gives single output format: fields order, time units, etc +- can query datasources directly or using Grafana as proxy +- can be used as a lib from your node.js-code or as a CLI-tool +- user gets a unified interface to all datasources. Library gives single output format: fields order, time units, etc. ## Supported datasources +### Direct + +* Prometheus + +### Grafana + * Influxdb * Graphite * Prometheus * PostgreSQL / TimescaleDB / MySQL * ElasticSearch -Please write us at ping@corpglory.com if you want your datasource to be supported: +Please write us at ping@corpglory.com if you want your datasource to be supported + +## Usage + +### Lib (TODO) + +### CLI + +For now, CLI supports only direct Prometheus queries + +For example: + +`npx @corpglory/tsdb-kit -U http://localhost:9090 -q '100-(avg by (instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)' -u my_user -p my_password` + + +## Development (TODO) ## Projects based on library * [grafana-data-exporter](https://github.com/CorpGlory/grafana-data-exporter) diff --git a/package.json b/package.json index 069505a..ba85bb0 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,13 @@ { "name": "@corpglory/tsdb-kit", - "version": "1.1.1", + "version": "2.0.0", "description": "", "scripts": { "build": "yarn build:lib && yarn build:bin", "build:lib": "tsc --p lib.tsconfig.json", "build:bin": "webpack --config webpack.config.js", - "dev": "tsc -w", + "dev:lib": "tsc --p lib.tsconfig.json -w", + "dev:bin": "webpack --watch --config webpack.config.js", "test": "jest" }, "repository": { @@ -32,6 +33,7 @@ "devDependencies": { "@types/jest": "^26.0.15", "@types/lodash": "^4.14.165", + "argparse": "^2.0.1", "jest": "^26.6.3", "ts-jest": "^26.4.4", "ts-loader": "^9.3.1", diff --git a/spec/prometheus.jest.ts b/spec/prometheus.jest.ts index 456c24c..3df0134 100644 --- a/spec/prometheus.jest.ts +++ b/spec/prometheus.jest.ts @@ -7,7 +7,11 @@ import 'jest'; describe('Test Prometheus time range processing', function() { let datasource = { type: DatasourceType.PROMETHEUS, - url: 'api/datasources/proxy/4/api/v1/query_range?query=node_disk_io_time_ms&start=1543411320&end=1543432950&step=30' + url: 'api/datasources/proxy/4/api/v1/query_range?query=node_disk_io_time_ms&start=1543411320&end=1543432950&step=30', + auth: { + username: 'my_user', + password: 'my_password', + } } let targets = []; let prometheus = new PrometheusConnector(datasource, targets); @@ -19,4 +23,11 @@ describe('Test Prometheus time range processing', function() { expect(query.url.indexOf(`start=${Math.floor(from / 1000)}`) !== -1).toBeTruthy(); expect(query.url.indexOf(`end=${Math.floor(to / 1000)}`) !== -1).toBeTruthy(); }); + + it('check that username/password present in query', function() { + let query = prometheus.getQuery(0, 0, 1000, 0); + + expect(query.auth?.username).toBe('my_user'); + expect(query.auth?.password).toBe('my_password'); + }) }); diff --git a/src/connectors/index.ts b/src/connectors/index.ts index b15fadc..0f89291 100644 --- a/src/connectors/index.ts +++ b/src/connectors/index.ts @@ -12,6 +12,7 @@ export enum DatasourceType { MYSQL = 'mysql', } +// TODO: Datasource: type -> class export declare type Datasource = { url: string; type: DatasourceType; @@ -22,6 +23,7 @@ export declare type Datasource = { }; data?: any; datasourceId?: string; + auth?: any; }; export type DatasourceQuery = { @@ -29,6 +31,10 @@ export type DatasourceQuery = { method: string; schema: any; headers?: any; + auth?: { + username: string; + password: string; + }; } export type DataTable = { diff --git a/src/connectors/prometheus.ts b/src/connectors/prometheus.ts index 37ad0f1..b48f753 100644 --- a/src/connectors/prometheus.ts +++ b/src/connectors/prometheus.ts @@ -21,7 +21,8 @@ export class PrometheusConnector extends DatasourceConnector { method: 'GET', schema: { params: this.datasource.params - } + }, + auth: this.datasource.auth, } } diff --git a/src/services/query_service/direct.ts b/src/services/query_service/direct.ts index acf3102..5cd89f7 100644 --- a/src/services/query_service/direct.ts +++ b/src/services/query_service/direct.ts @@ -14,8 +14,7 @@ export class DirectQueryService extends QueryService { async query(query: DatasourceQuery): Promise> { // TODO: support auth let axiosQuery = { - url: query.url, - method: query.method, + ...query, }; _.defaults(axiosQuery, query.schema); diff --git a/src/tsdb-kit/index.ts b/src/tsdb-kit/index.ts index 66e54ef..47854a7 100644 --- a/src/tsdb-kit/index.ts +++ b/src/tsdb-kit/index.ts @@ -2,20 +2,41 @@ import { queryByConfig, QueryConfig } from '..'; import { DatasourceType, QueryType } from '../connectors'; +const { version } = require('../../package.json') +import { ArgumentParser } from 'argparse'; import * as _ from 'lodash'; +const parser = new ArgumentParser(); -// TODO: these `const`s should be CLI arguments -const PROMETHEUS_URL = 'http://localhost:9090'; -const QUERY = '100-(avg by (instance) (irate(node_cpu_seconds_total{job="nvicta-ai-node-exporter",mode="idle"}[5m])) * 100)'; -const FROM = 1660670020000; // ms -const TO = 1660670026000; // ms +parser.add_argument('-v', '--version', { action: 'version', version }); +parser.add_argument('-U', '--url', { help: 'Datasource URL', required: true }); +parser.add_argument('-q', '--query', { help: 'Query Template', required: true }); +parser.add_argument('-f', '--from', { help: 'From timestamp (ms), e.g. 1660670020000. If not specified, `now-5m` is used' }); +parser.add_argument('-t', '--to', { help: 'To timestamp (ms), e.g. 1660670026000. If not specified, `now` is used' }); +parser.add_argument('-u', '--username', { help: 'Basic Auth Username' }); +parser.add_argument('-p', '--password', { help: 'Basic Auth Password' }); +const args = parser.parse_args(); + +const timeNowInMs = new Date().getTime(); + +const PROMETHEUS_URL = args.url; +const QUERY = args.query; +const FROM = args.from || timeNowInMs - 5 * 60 * 1000; +const TO = args.to || timeNowInMs; +const USERNAME = args.username; +const PASSWORD = args.password; + +let auth; +if(USERNAME && PASSWORD) { + auth = { username: USERNAME, password: PASSWORD }; +} const datasource = { type: DatasourceType.PROMETHEUS, // TODO: remove PROMETHEUS_URL from here - url: `${PROMETHEUS_URL}/api/v1/query_range?query=${QUERY}&start=1543411320&end=1543432950&step=30` -} + url: `${PROMETHEUS_URL}/api/v1/query_range?query=${QUERY}&start=1543411320&end=1543432950&step=30`, + auth, +}; const targets = []; const queryConfig = new QueryConfig(QueryType.DIRECT, datasource, targets); queryByConfig(queryConfig, PROMETHEUS_URL, FROM, TO) diff --git a/webpack.config.js b/webpack.config.js index 96ee9fd..cee3958 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,3 +1,4 @@ +const webpack = require('webpack'); const path = require('path'); @@ -12,13 +13,16 @@ module.exports = { path: path.resolve(__dirname, './bin'), filename: 'tsdb-kit.js' }, + plugins: [ + new webpack.BannerPlugin({ banner: "#!/usr/bin/env node", raw: true }), + ], resolve: { extensions: ['.ts', '.js'], }, module: { rules: [ { - test: /.ts?$/, + test: /.ts$/, loader: 'ts-loader', options: { configFile: 'bin.tsconfig.json' diff --git a/yarn.lock b/yarn.lock index 67afc9d..e539f99 100644 --- a/yarn.lock +++ b/yarn.lock @@ -905,6 +905,11 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"