mocks.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. import {
  2. DataSourceApi,
  3. DataSourceInstanceSettings,
  4. DataSourceJsonData,
  5. DataSourcePluginMeta,
  6. DataSourceRef,
  7. ScopedVars,
  8. } from '@grafana/data';
  9. import { config, DataSourceSrv, GetDataSourceListFilters } from '@grafana/runtime';
  10. import { contextSrv } from 'app/core/services/context_srv';
  11. import { DatasourceSrv } from 'app/features/plugins/datasource_srv';
  12. import {
  13. AlertmanagerAlert,
  14. AlertManagerCortexConfig,
  15. AlertmanagerGroup,
  16. AlertmanagerStatus,
  17. AlertState,
  18. GrafanaManagedReceiverConfig,
  19. Silence,
  20. SilenceState,
  21. } from 'app/plugins/datasource/alertmanager/types';
  22. import { AccessControlAction, FolderDTO } from 'app/types';
  23. import { Alert, AlertingRule, CombinedRule, RecordingRule, RuleGroup, RuleNamespace } from 'app/types/unified-alerting';
  24. import {
  25. GrafanaAlertStateDecision,
  26. GrafanaRuleDefinition,
  27. PromAlertingRuleState,
  28. PromRuleType,
  29. RulerAlertingRuleDTO,
  30. RulerGrafanaRuleDTO,
  31. RulerRuleGroupDTO,
  32. RulerRulesConfigDTO,
  33. } from 'app/types/unified-alerting-dto';
  34. let nextDataSourceId = 1;
  35. export function mockDataSource<T extends DataSourceJsonData = DataSourceJsonData>(
  36. partial: Partial<DataSourceInstanceSettings<T>> = {},
  37. meta: Partial<DataSourcePluginMeta> = {}
  38. ): DataSourceInstanceSettings<T> {
  39. const id = partial.id ?? nextDataSourceId++;
  40. return {
  41. id,
  42. uid: `mock-ds-${nextDataSourceId}`,
  43. type: 'prometheus',
  44. name: `Prometheus-${id}`,
  45. access: 'proxy',
  46. jsonData: {} as T,
  47. meta: {
  48. info: {
  49. logos: {
  50. small: 'https://prometheus.io/assets/prometheus_logo_grey.svg',
  51. large: 'https://prometheus.io/assets/prometheus_logo_grey.svg',
  52. },
  53. },
  54. ...meta,
  55. } as any as DataSourcePluginMeta,
  56. ...partial,
  57. };
  58. }
  59. export const mockPromAlert = (partial: Partial<Alert> = {}): Alert => ({
  60. activeAt: '2021-03-18T13:47:05.04938691Z',
  61. annotations: {
  62. message: 'alert with severity "warning"',
  63. },
  64. labels: {
  65. alertname: 'myalert',
  66. severity: 'warning',
  67. },
  68. state: PromAlertingRuleState.Firing,
  69. value: '1e+00',
  70. ...partial,
  71. });
  72. export const mockRulerGrafanaRule = (
  73. partial: Partial<RulerGrafanaRuleDTO> = {},
  74. partialDef: Partial<GrafanaRuleDefinition> = {}
  75. ): RulerGrafanaRuleDTO => {
  76. return {
  77. for: '1m',
  78. grafana_alert: {
  79. uid: '123',
  80. title: 'myalert',
  81. namespace_uid: '123',
  82. namespace_id: 1,
  83. condition: 'A',
  84. no_data_state: GrafanaAlertStateDecision.Alerting,
  85. exec_err_state: GrafanaAlertStateDecision.Alerting,
  86. data: [
  87. {
  88. datasourceUid: '123',
  89. refId: 'A',
  90. queryType: 'huh',
  91. model: {} as any,
  92. },
  93. ],
  94. ...partialDef,
  95. },
  96. annotations: {
  97. message: 'alert with severity "{{.warning}}}"',
  98. },
  99. labels: {
  100. severity: 'warning',
  101. },
  102. ...partial,
  103. };
  104. };
  105. export const mockRulerAlertingRule = (partial: Partial<RulerAlertingRuleDTO> = {}): RulerAlertingRuleDTO => ({
  106. alert: 'alert1',
  107. expr: 'up = 1',
  108. labels: {
  109. severity: 'warning',
  110. },
  111. annotations: {
  112. summary: 'test alert',
  113. },
  114. });
  115. export const mockRulerRuleGroup = (partial: Partial<RulerRuleGroupDTO> = {}): RulerRuleGroupDTO => ({
  116. name: 'group1',
  117. rules: [mockRulerAlertingRule()],
  118. ...partial,
  119. });
  120. export const mockPromAlertingRule = (partial: Partial<AlertingRule> = {}): AlertingRule => {
  121. return {
  122. type: PromRuleType.Alerting,
  123. alerts: [mockPromAlert()],
  124. name: 'myalert',
  125. query: 'foo > 1',
  126. lastEvaluation: '2021-03-23T08:19:05.049595312Z',
  127. evaluationTime: 0.000395601,
  128. annotations: {
  129. message: 'alert with severity "{{.warning}}}"',
  130. },
  131. labels: {
  132. severity: 'warning',
  133. },
  134. state: PromAlertingRuleState.Firing,
  135. health: 'OK',
  136. ...partial,
  137. };
  138. };
  139. export const mockPromRecordingRule = (partial: Partial<RecordingRule> = {}): RecordingRule => {
  140. return {
  141. type: PromRuleType.Recording,
  142. query: 'bar < 3',
  143. labels: {
  144. cluster: 'eu-central',
  145. },
  146. health: 'OK',
  147. name: 'myrecordingrule',
  148. lastEvaluation: '2021-03-23T08:19:05.049595312Z',
  149. evaluationTime: 0.000395601,
  150. ...partial,
  151. };
  152. };
  153. export const mockPromRuleGroup = (partial: Partial<RuleGroup> = {}): RuleGroup => {
  154. return {
  155. name: 'mygroup',
  156. interval: 60,
  157. rules: [mockPromAlertingRule()],
  158. ...partial,
  159. };
  160. };
  161. export const mockPromRuleNamespace = (partial: Partial<RuleNamespace> = {}): RuleNamespace => {
  162. return {
  163. dataSourceName: 'Prometheus-1',
  164. name: 'default',
  165. groups: [mockPromRuleGroup()],
  166. ...partial,
  167. };
  168. };
  169. export const mockAlertmanagerAlert = (partial: Partial<AlertmanagerAlert> = {}): AlertmanagerAlert => {
  170. return {
  171. annotations: {
  172. summary: 'US-Central region is on fire',
  173. },
  174. endsAt: '2021-06-22T21:49:28.562Z',
  175. fingerprint: '88e013643c3df34ac3',
  176. receivers: [{ name: 'pagerduty' }],
  177. startsAt: '2021-06-21T17:25:28.562Z',
  178. status: { inhibitedBy: [], silencedBy: [], state: AlertState.Active },
  179. updatedAt: '2021-06-22T21:45:28.564Z',
  180. generatorURL: 'https://play.grafana.com/explore',
  181. labels: { severity: 'warning', region: 'US-Central' },
  182. ...partial,
  183. };
  184. };
  185. export const mockAlertGroup = (partial: Partial<AlertmanagerGroup> = {}): AlertmanagerGroup => {
  186. return {
  187. labels: {
  188. severity: 'warning',
  189. region: 'US-Central',
  190. },
  191. receiver: {
  192. name: 'pagerduty',
  193. },
  194. alerts: [
  195. mockAlertmanagerAlert(),
  196. mockAlertmanagerAlert({
  197. status: { state: AlertState.Suppressed, silencedBy: ['123456abcdef'], inhibitedBy: [] },
  198. labels: { severity: 'warning', region: 'US-Central', foo: 'bar', ...partial.labels },
  199. }),
  200. ],
  201. ...partial,
  202. };
  203. };
  204. export const mockSilence = (partial: Partial<Silence> = {}): Silence => {
  205. return {
  206. id: '1a2b3c4d5e6f',
  207. matchers: [{ name: 'foo', value: 'bar', isEqual: true, isRegex: false }],
  208. startsAt: new Date().toISOString(),
  209. endsAt: new Date(Date.now() + 60 * 60 * 1000).toISOString(),
  210. updatedAt: new Date().toISOString(),
  211. createdBy: config.bootData.user.name || 'admin',
  212. comment: 'Silence noisy alerts',
  213. status: {
  214. state: SilenceState.Active,
  215. },
  216. ...partial,
  217. };
  218. };
  219. export class MockDataSourceSrv implements DataSourceSrv {
  220. datasources: Record<string, DataSourceApi> = {};
  221. // @ts-ignore
  222. private settingsMapByName: Record<string, DataSourceInstanceSettings> = {};
  223. private settingsMapByUid: Record<string, DataSourceInstanceSettings> = {};
  224. private settingsMapById: Record<string, DataSourceInstanceSettings> = {};
  225. // @ts-ignore
  226. private templateSrv = {
  227. getVariables: () => [],
  228. replace: (name: any) => name,
  229. };
  230. defaultName = '';
  231. constructor(datasources: Record<string, DataSourceInstanceSettings>) {
  232. this.datasources = {};
  233. this.settingsMapByName = Object.values(datasources).reduce<Record<string, DataSourceInstanceSettings>>(
  234. (acc, ds) => {
  235. acc[ds.name] = ds;
  236. return acc;
  237. },
  238. {}
  239. );
  240. for (const dsSettings of Object.values(this.settingsMapByName)) {
  241. this.settingsMapByUid[dsSettings.uid] = dsSettings;
  242. this.settingsMapById[dsSettings.id] = dsSettings;
  243. if (dsSettings.isDefault) {
  244. this.defaultName = dsSettings.name;
  245. }
  246. }
  247. }
  248. get(name?: string | null | DataSourceRef, scopedVars?: ScopedVars): Promise<DataSourceApi> {
  249. return DatasourceSrv.prototype.get.call(this, name, scopedVars);
  250. //return Promise.reject(new Error('not implemented'));
  251. }
  252. /**
  253. * Get a list of data sources
  254. */
  255. getList(filters?: GetDataSourceListFilters): DataSourceInstanceSettings[] {
  256. return DatasourceSrv.prototype.getList.call(this, filters);
  257. }
  258. /**
  259. * Get settings and plugin metadata by name or uid
  260. */
  261. getInstanceSettings(nameOrUid: string | null | undefined): DataSourceInstanceSettings | undefined {
  262. return (
  263. DatasourceSrv.prototype.getInstanceSettings.call(this, nameOrUid) ||
  264. ({ meta: { info: { logos: {} } } } as unknown as DataSourceInstanceSettings)
  265. );
  266. }
  267. async loadDatasource(name: string): Promise<DataSourceApi<any, any>> {
  268. return DatasourceSrv.prototype.loadDatasource.call(this, name);
  269. }
  270. reload() {}
  271. }
  272. export const mockGrafanaReceiver = (
  273. type: string,
  274. overrides: Partial<GrafanaManagedReceiverConfig> = {}
  275. ): GrafanaManagedReceiverConfig => ({
  276. type: type,
  277. name: type,
  278. disableResolveMessage: false,
  279. settings: {},
  280. ...overrides,
  281. });
  282. export const someGrafanaAlertManagerConfig: AlertManagerCortexConfig = {
  283. template_files: {
  284. 'first template': 'first template content',
  285. 'second template': 'second template content',
  286. 'third template': 'third template',
  287. },
  288. alertmanager_config: {
  289. route: {
  290. receiver: 'default',
  291. },
  292. receivers: [
  293. {
  294. name: 'default',
  295. grafana_managed_receiver_configs: [mockGrafanaReceiver('email')],
  296. },
  297. {
  298. name: 'critical',
  299. grafana_managed_receiver_configs: [mockGrafanaReceiver('slack'), mockGrafanaReceiver('pagerduty')],
  300. },
  301. ],
  302. },
  303. };
  304. export const someCloudAlertManagerStatus: AlertmanagerStatus = {
  305. cluster: {
  306. peers: [],
  307. status: 'ok',
  308. },
  309. uptime: '10 hours',
  310. versionInfo: {
  311. branch: '',
  312. version: '',
  313. goVersion: '',
  314. buildDate: '',
  315. buildUser: '',
  316. revision: '',
  317. },
  318. config: {
  319. route: {
  320. receiver: 'default-email',
  321. },
  322. receivers: [
  323. {
  324. name: 'default-email',
  325. email_configs: [
  326. {
  327. to: 'example@example.com',
  328. },
  329. ],
  330. },
  331. ],
  332. },
  333. };
  334. export const someCloudAlertManagerConfig: AlertManagerCortexConfig = {
  335. template_files: {
  336. 'foo template': 'foo content',
  337. },
  338. alertmanager_config: {
  339. route: {
  340. receiver: 'cloud-receiver',
  341. routes: [
  342. {
  343. receiver: 'foo-receiver',
  344. },
  345. {
  346. receiver: 'bar-receiver',
  347. },
  348. ],
  349. },
  350. receivers: [
  351. {
  352. name: 'cloud-receiver',
  353. email_configs: [
  354. {
  355. to: 'domas.lapinskas@grafana.com',
  356. },
  357. ],
  358. slack_configs: [
  359. {
  360. api_url: 'http://slack1',
  361. channel: '#mychannel',
  362. actions: [
  363. {
  364. text: 'action1text',
  365. type: 'action1type',
  366. url: 'http://action1',
  367. },
  368. ],
  369. fields: [
  370. {
  371. title: 'field1',
  372. value: 'text1',
  373. },
  374. {
  375. title: 'field2',
  376. value: 'text2',
  377. },
  378. ],
  379. },
  380. ],
  381. },
  382. ],
  383. },
  384. };
  385. export const somePromRules = (dataSourceName = 'Prometheus'): RuleNamespace[] => [
  386. {
  387. dataSourceName,
  388. name: 'namespace1',
  389. groups: [
  390. mockPromRuleGroup({ name: 'group1', rules: [mockPromAlertingRule({ name: 'alert1' })] }),
  391. mockPromRuleGroup({ name: 'group2', rules: [mockPromAlertingRule({ name: 'alert2' })] }),
  392. ],
  393. },
  394. {
  395. dataSourceName,
  396. name: 'namespace2',
  397. groups: [mockPromRuleGroup({ name: 'group3', rules: [mockPromAlertingRule({ name: 'alert3' })] })],
  398. },
  399. ];
  400. export const someRulerRules: RulerRulesConfigDTO = {
  401. namespace1: [
  402. mockRulerRuleGroup({ name: 'group1', rules: [mockRulerAlertingRule({ alert: 'alert1' })] }),
  403. mockRulerRuleGroup({ name: 'group2', rules: [mockRulerAlertingRule({ alert: 'alert2' })] }),
  404. ],
  405. namespace2: [mockRulerRuleGroup({ name: 'group3', rules: [mockRulerAlertingRule({ alert: 'alert3' })] })],
  406. };
  407. export const mockCombinedRule = (partial?: Partial<CombinedRule>): CombinedRule => ({
  408. name: 'mockRule',
  409. query: 'expr',
  410. group: {
  411. name: 'mockCombinedRuleGroup',
  412. rules: [],
  413. },
  414. namespace: {
  415. name: 'mockCombinedNamespace',
  416. groups: [{ name: 'mockCombinedRuleGroup', rules: [] }],
  417. rulesSource: 'grafana',
  418. },
  419. labels: {},
  420. annotations: {},
  421. promRule: mockPromAlertingRule(),
  422. rulerRule: mockRulerAlertingRule(),
  423. ...partial,
  424. });
  425. export const mockFolder = (partial?: Partial<FolderDTO>): FolderDTO => {
  426. return {
  427. id: 1,
  428. uid: 'gdev-1',
  429. title: 'Gdev',
  430. version: 1,
  431. url: '',
  432. canAdmin: true,
  433. canDelete: true,
  434. canEdit: true,
  435. canSave: true,
  436. ...partial,
  437. };
  438. };
  439. export const enableRBAC = () => {
  440. jest.spyOn(contextSrv, 'accessControlEnabled').mockReturnValue(true);
  441. };
  442. export const disableRBAC = () => {
  443. jest.spyOn(contextSrv, 'accessControlEnabled').mockReturnValue(false);
  444. };
  445. export const grantUserPermissions = (permissions: AccessControlAction[]) => {
  446. jest
  447. .spyOn(contextSrv, 'hasPermission')
  448. .mockImplementation((action) => permissions.includes(action as AccessControlAction));
  449. };