datasource_specs.ts 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. import '../datasource';
  2. import { describe, beforeEach, it, expect, angularMocks } from 'test/lib/common';
  3. import helpers from 'test/specs/helpers';
  4. import CloudWatchDatasource from '../datasource';
  5. import 'app/features/dashboard/time_srv';
  6. describe('CloudWatchDatasource', function() {
  7. var ctx = new helpers.ServiceTestContext();
  8. var instanceSettings = {
  9. jsonData: { defaultRegion: 'us-east-1', access: 'proxy' },
  10. };
  11. beforeEach(angularMocks.module('grafana.core'));
  12. beforeEach(angularMocks.module('grafana.services'));
  13. beforeEach(angularMocks.module('grafana.controllers'));
  14. beforeEach(ctx.providePhase(['templateSrv', 'backendSrv']));
  15. beforeEach(ctx.createService('timeSrv'));
  16. beforeEach(
  17. angularMocks.inject(function($q, $rootScope, $httpBackend, $injector) {
  18. ctx.$q = $q;
  19. ctx.$httpBackend = $httpBackend;
  20. ctx.$rootScope = $rootScope;
  21. ctx.ds = $injector.instantiate(CloudWatchDatasource, {
  22. instanceSettings: instanceSettings,
  23. });
  24. $httpBackend.when('GET', /\.html$/).respond('');
  25. })
  26. );
  27. describe('When performing CloudWatch query', function() {
  28. var requestParams;
  29. var query = {
  30. range: { from: 'now-1h', to: 'now' },
  31. rangeRaw: { from: 1483228800, to: 1483232400 },
  32. targets: [
  33. {
  34. region: 'us-east-1',
  35. namespace: 'AWS/EC2',
  36. metricName: 'CPUUtilization',
  37. dimensions: {
  38. InstanceId: 'i-12345678',
  39. },
  40. statistics: ['Average'],
  41. period: '300',
  42. },
  43. ],
  44. };
  45. var response = {
  46. timings: [null],
  47. results: {
  48. A: {
  49. error: '',
  50. refId: 'A',
  51. series: [
  52. {
  53. name: 'CPUUtilization_Average',
  54. points: [[1, 1483228800000], [2, 1483229100000], [5, 1483229700000]],
  55. tags: {
  56. InstanceId: 'i-12345678',
  57. },
  58. },
  59. ],
  60. },
  61. },
  62. };
  63. beforeEach(function() {
  64. ctx.backendSrv.datasourceRequest = function(params) {
  65. requestParams = params.data;
  66. return ctx.$q.when({ data: response });
  67. };
  68. });
  69. it('should generate the correct query', function(done) {
  70. ctx.ds.query(query).then(function() {
  71. var params = requestParams.queries[0];
  72. expect(params.namespace).to.be(query.targets[0].namespace);
  73. expect(params.metricName).to.be(query.targets[0].metricName);
  74. expect(params.dimensions['InstanceId']).to.be('i-12345678');
  75. expect(params.statistics).to.eql(query.targets[0].statistics);
  76. expect(params.period).to.be(query.targets[0].period);
  77. done();
  78. });
  79. ctx.$rootScope.$apply();
  80. });
  81. it('should generate the correct query with interval variable', function(done) {
  82. ctx.templateSrv.data = {
  83. period: '10m',
  84. };
  85. var query = {
  86. range: { from: 'now-1h', to: 'now' },
  87. rangeRaw: { from: 1483228800, to: 1483232400 },
  88. targets: [
  89. {
  90. region: 'us-east-1',
  91. namespace: 'AWS/EC2',
  92. metricName: 'CPUUtilization',
  93. dimensions: {
  94. InstanceId: 'i-12345678',
  95. },
  96. statistics: ['Average'],
  97. period: '[[period]]',
  98. },
  99. ],
  100. };
  101. ctx.ds.query(query).then(function() {
  102. var params = requestParams.queries[0];
  103. expect(params.period).to.be('600');
  104. done();
  105. });
  106. ctx.$rootScope.$apply();
  107. });
  108. it('should return series list', function(done) {
  109. ctx.ds.query(query).then(function(result) {
  110. expect(result.data[0].target).to.be(response.results.A.series[0].name);
  111. expect(result.data[0].datapoints[0][0]).to.be(response.results.A.series[0].points[0][0]);
  112. done();
  113. });
  114. ctx.$rootScope.$apply();
  115. });
  116. it('should generate the correct targets by expanding template variables', function() {
  117. var templateSrv = {
  118. variables: [
  119. {
  120. name: 'instance_id',
  121. options: [
  122. { text: 'i-23456789', value: 'i-23456789', selected: false },
  123. { text: 'i-34567890', value: 'i-34567890', selected: true },
  124. ],
  125. current: {
  126. text: 'i-34567890',
  127. value: 'i-34567890',
  128. },
  129. },
  130. ],
  131. replace: function(target, scopedVars) {
  132. if (target === '$instance_id' && scopedVars['instance_id']['text'] === 'i-34567890') {
  133. return 'i-34567890';
  134. } else {
  135. return '';
  136. }
  137. },
  138. getVariableName: function(e) {
  139. return 'instance_id';
  140. },
  141. variableExists: function(e) {
  142. return true;
  143. },
  144. containsVariable: function(str, variableName) {
  145. return str.indexOf('$' + variableName) !== -1;
  146. },
  147. };
  148. var targets = [
  149. {
  150. region: 'us-east-1',
  151. namespace: 'AWS/EC2',
  152. metricName: 'CPUUtilization',
  153. dimensions: {
  154. InstanceId: '$instance_id',
  155. },
  156. statistics: ['Average'],
  157. period: 300,
  158. },
  159. ];
  160. var result = ctx.ds.expandTemplateVariable(targets, {}, templateSrv);
  161. expect(result[0].dimensions.InstanceId).to.be('i-34567890');
  162. });
  163. it('should generate the correct targets by expanding template variables from url', function() {
  164. var templateSrv = {
  165. variables: [
  166. {
  167. name: 'instance_id',
  168. options: [
  169. { text: 'i-23456789', value: 'i-23456789', selected: false },
  170. { text: 'i-34567890', value: 'i-34567890', selected: false },
  171. ],
  172. current: 'i-45678901',
  173. },
  174. ],
  175. replace: function(target, scopedVars) {
  176. if (target === '$instance_id') {
  177. return 'i-45678901';
  178. } else {
  179. return '';
  180. }
  181. },
  182. getVariableName: function(e) {
  183. return 'instance_id';
  184. },
  185. variableExists: function(e) {
  186. return true;
  187. },
  188. containsVariable: function(str, variableName) {
  189. return str.indexOf('$' + variableName) !== -1;
  190. },
  191. };
  192. var targets = [
  193. {
  194. region: 'us-east-1',
  195. namespace: 'AWS/EC2',
  196. metricName: 'CPUUtilization',
  197. dimensions: {
  198. InstanceId: '$instance_id',
  199. },
  200. statistics: ['Average'],
  201. period: 300,
  202. },
  203. ];
  204. var result = ctx.ds.expandTemplateVariable(targets, {}, templateSrv);
  205. expect(result[0].dimensions.InstanceId).to.be('i-45678901');
  206. });
  207. });
  208. describe('When query region is "default"', function() {
  209. it('should return the datasource region if empty or "default"', function() {
  210. var defaultRegion = instanceSettings.jsonData.defaultRegion;
  211. expect(ctx.ds.getActualRegion()).to.be(defaultRegion);
  212. expect(ctx.ds.getActualRegion('')).to.be(defaultRegion);
  213. expect(ctx.ds.getActualRegion('default')).to.be(defaultRegion);
  214. });
  215. it('should return the specified region if specified', function() {
  216. expect(ctx.ds.getActualRegion('some-fake-region-1')).to.be('some-fake-region-1');
  217. });
  218. var requestParams;
  219. beforeEach(function() {
  220. ctx.ds.performTimeSeriesQuery = function(request) {
  221. requestParams = request;
  222. return ctx.$q.when({ data: {} });
  223. };
  224. });
  225. it('should query for the datasource region if empty or "default"', function(done) {
  226. var query = {
  227. range: { from: 'now-1h', to: 'now' },
  228. rangeRaw: { from: 1483228800, to: 1483232400 },
  229. targets: [
  230. {
  231. region: 'default',
  232. namespace: 'AWS/EC2',
  233. metricName: 'CPUUtilization',
  234. dimensions: {
  235. InstanceId: 'i-12345678',
  236. },
  237. statistics: ['Average'],
  238. period: 300,
  239. },
  240. ],
  241. };
  242. ctx.ds.query(query).then(function(result) {
  243. expect(requestParams.queries[0].region).to.be(instanceSettings.jsonData.defaultRegion);
  244. done();
  245. });
  246. ctx.$rootScope.$apply();
  247. });
  248. });
  249. describe('When performing CloudWatch query for extended statistics', function() {
  250. var query = {
  251. range: { from: 'now-1h', to: 'now' },
  252. rangeRaw: { from: 1483228800, to: 1483232400 },
  253. targets: [
  254. {
  255. region: 'us-east-1',
  256. namespace: 'AWS/ApplicationELB',
  257. metricName: 'TargetResponseTime',
  258. dimensions: {
  259. LoadBalancer: 'lb',
  260. TargetGroup: 'tg',
  261. },
  262. statistics: ['p90.00'],
  263. period: 300,
  264. },
  265. ],
  266. };
  267. var response = {
  268. timings: [null],
  269. results: {
  270. A: {
  271. error: '',
  272. refId: 'A',
  273. series: [
  274. {
  275. name: 'TargetResponseTime_p90.00',
  276. points: [[1, 1483228800000], [2, 1483229100000], [5, 1483229700000]],
  277. tags: {
  278. LoadBalancer: 'lb',
  279. TargetGroup: 'tg',
  280. },
  281. },
  282. ],
  283. },
  284. },
  285. };
  286. beforeEach(function() {
  287. ctx.backendSrv.datasourceRequest = function(params) {
  288. return ctx.$q.when({ data: response });
  289. };
  290. });
  291. it('should return series list', function(done) {
  292. ctx.ds.query(query).then(function(result) {
  293. expect(result.data[0].target).to.be(response.results.A.series[0].name);
  294. expect(result.data[0].datapoints[0][0]).to.be(response.results.A.series[0].points[0][0]);
  295. done();
  296. });
  297. ctx.$rootScope.$apply();
  298. });
  299. });
  300. function describeMetricFindQuery(query, func) {
  301. describe('metricFindQuery ' + query, () => {
  302. let scenario: any = {};
  303. scenario.setup = setupCallback => {
  304. beforeEach(() => {
  305. setupCallback();
  306. ctx.backendSrv.datasourceRequest = args => {
  307. scenario.request = args.data;
  308. return ctx.$q.when({ data: scenario.requestResponse });
  309. };
  310. ctx.ds.metricFindQuery(query).then(args => {
  311. scenario.result = args;
  312. });
  313. ctx.$rootScope.$apply();
  314. });
  315. };
  316. func(scenario);
  317. });
  318. }
  319. describeMetricFindQuery('regions()', scenario => {
  320. scenario.setup(() => {
  321. scenario.requestResponse = {
  322. results: {
  323. metricFindQuery: {
  324. tables: [{ rows: [['us-east-1', 'us-east-1']] }],
  325. },
  326. },
  327. };
  328. });
  329. it('should call __GetRegions and return result', () => {
  330. expect(scenario.result[0].text).to.contain('us-east-1');
  331. expect(scenario.request.queries[0].type).to.be('metricFindQuery');
  332. expect(scenario.request.queries[0].subtype).to.be('regions');
  333. });
  334. });
  335. describeMetricFindQuery('namespaces()', scenario => {
  336. scenario.setup(() => {
  337. scenario.requestResponse = {
  338. results: {
  339. metricFindQuery: {
  340. tables: [{ rows: [['AWS/EC2', 'AWS/EC2']] }],
  341. },
  342. },
  343. };
  344. });
  345. it('should call __GetNamespaces and return result', () => {
  346. expect(scenario.result[0].text).to.contain('AWS/EC2');
  347. expect(scenario.request.queries[0].type).to.be('metricFindQuery');
  348. expect(scenario.request.queries[0].subtype).to.be('namespaces');
  349. });
  350. });
  351. describeMetricFindQuery('metrics(AWS/EC2)', scenario => {
  352. scenario.setup(() => {
  353. scenario.requestResponse = {
  354. results: {
  355. metricFindQuery: {
  356. tables: [{ rows: [['CPUUtilization', 'CPUUtilization']] }],
  357. },
  358. },
  359. };
  360. });
  361. it('should call __GetMetrics and return result', () => {
  362. expect(scenario.result[0].text).to.be('CPUUtilization');
  363. expect(scenario.request.queries[0].type).to.be('metricFindQuery');
  364. expect(scenario.request.queries[0].subtype).to.be('metrics');
  365. });
  366. });
  367. describeMetricFindQuery('dimension_keys(AWS/EC2)', scenario => {
  368. scenario.setup(() => {
  369. scenario.requestResponse = {
  370. results: {
  371. metricFindQuery: {
  372. tables: [{ rows: [['InstanceId', 'InstanceId']] }],
  373. },
  374. },
  375. };
  376. });
  377. it('should call __GetDimensions and return result', () => {
  378. expect(scenario.result[0].text).to.be('InstanceId');
  379. expect(scenario.request.queries[0].type).to.be('metricFindQuery');
  380. expect(scenario.request.queries[0].subtype).to.be('dimension_keys');
  381. });
  382. });
  383. describeMetricFindQuery('dimension_values(us-east-1,AWS/EC2,CPUUtilization,InstanceId)', scenario => {
  384. scenario.setup(() => {
  385. scenario.requestResponse = {
  386. results: {
  387. metricFindQuery: {
  388. tables: [{ rows: [['i-12345678', 'i-12345678']] }],
  389. },
  390. },
  391. };
  392. });
  393. it('should call __ListMetrics and return result', () => {
  394. expect(scenario.result[0].text).to.contain('i-12345678');
  395. expect(scenario.request.queries[0].type).to.be('metricFindQuery');
  396. expect(scenario.request.queries[0].subtype).to.be('dimension_values');
  397. });
  398. });
  399. describeMetricFindQuery('dimension_values(default,AWS/EC2,CPUUtilization,InstanceId)', scenario => {
  400. scenario.setup(() => {
  401. scenario.requestResponse = {
  402. results: {
  403. metricFindQuery: {
  404. tables: [{ rows: [['i-12345678', 'i-12345678']] }],
  405. },
  406. },
  407. };
  408. });
  409. it('should call __ListMetrics and return result', () => {
  410. expect(scenario.result[0].text).to.contain('i-12345678');
  411. expect(scenario.request.queries[0].type).to.be('metricFindQuery');
  412. expect(scenario.request.queries[0].subtype).to.be('dimension_values');
  413. });
  414. });
  415. it('should caclculate the correct period', function() {
  416. var hourSec = 60 * 60;
  417. var daySec = hourSec * 24;
  418. var start = 1483196400 * 1000;
  419. var testData: any[] = [
  420. [
  421. { period: 60, namespace: 'AWS/EC2' },
  422. { range: { from: new Date(start), to: new Date(start + 3600 * 1000) } },
  423. hourSec * 3,
  424. 60,
  425. ],
  426. [
  427. { period: null, namespace: 'AWS/EC2' },
  428. { range: { from: new Date(start), to: new Date(start + 3600 * 1000) } },
  429. hourSec * 3,
  430. 300,
  431. ],
  432. [
  433. { period: 60, namespace: 'AWS/ELB' },
  434. { range: { from: new Date(start), to: new Date(start + 3600 * 1000) } },
  435. hourSec * 3,
  436. 60,
  437. ],
  438. [
  439. { period: null, namespace: 'AWS/ELB' },
  440. { range: { from: new Date(start), to: new Date(start + 3600 * 1000) } },
  441. hourSec * 3,
  442. 60,
  443. ],
  444. [
  445. { period: 1, namespace: 'CustomMetricsNamespace' },
  446. {
  447. range: {
  448. from: new Date(start),
  449. to: new Date(start + (1440 - 1) * 1000),
  450. },
  451. },
  452. hourSec * 3 - 1,
  453. 1,
  454. ],
  455. [
  456. { period: 1, namespace: 'CustomMetricsNamespace' },
  457. { range: { from: new Date(start), to: new Date(start + 3600 * 1000) } },
  458. hourSec * 3 - 1,
  459. 60,
  460. ],
  461. [
  462. { period: 60, namespace: 'CustomMetricsNamespace' },
  463. { range: { from: new Date(start), to: new Date(start + 3600 * 1000) } },
  464. hourSec * 3,
  465. 60,
  466. ],
  467. [
  468. { period: null, namespace: 'CustomMetricsNamespace' },
  469. { range: { from: new Date(start), to: new Date(start + 3600 * 1000) } },
  470. hourSec * 3 - 1,
  471. 60,
  472. ],
  473. [
  474. { period: null, namespace: 'CustomMetricsNamespace' },
  475. { range: { from: new Date(start), to: new Date(start + 3600 * 1000) } },
  476. hourSec * 3,
  477. 60,
  478. ],
  479. [
  480. { period: null, namespace: 'CustomMetricsNamespace' },
  481. { range: { from: new Date(start), to: new Date(start + 3600 * 1000) } },
  482. daySec * 15,
  483. 60,
  484. ],
  485. [
  486. { period: null, namespace: 'CustomMetricsNamespace' },
  487. { range: { from: new Date(start), to: new Date(start + 3600 * 1000) } },
  488. daySec * 63,
  489. 300,
  490. ],
  491. [
  492. { period: null, namespace: 'CustomMetricsNamespace' },
  493. { range: { from: new Date(start), to: new Date(start + 3600 * 1000) } },
  494. daySec * 455,
  495. 3600,
  496. ],
  497. ];
  498. for (let t of testData) {
  499. let target = t[0];
  500. let options = t[1];
  501. let now = new Date(options.range.from.valueOf() + t[2] * 1000);
  502. let expected = t[3];
  503. let actual = ctx.ds.getPeriod(target, options, now);
  504. expect(actual).to.be(expected);
  505. }
  506. });
  507. });