Commit 6769aab0 authored by Fatih Acet's avatar Fatih Acet

Merge branch 'jivanvl-support-additional-panel-types' into 'master'

Support additional panel types

Closes #60008

See merge request gitlab-org/gitlab-ce!30001
parents ef348618 202a150f
...@@ -8,6 +8,7 @@ import { getSvgIconPathContent } from '~/lib/utils/icon_utils'; ...@@ -8,6 +8,7 @@ import { getSvgIconPathContent } from '~/lib/utils/icon_utils';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import { chartHeight, graphTypes, lineTypes } from '../../constants'; import { chartHeight, graphTypes, lineTypes } from '../../constants';
import { makeDataSeries } from '~/helpers/monitor_helper'; import { makeDataSeries } from '~/helpers/monitor_helper';
import { graphDataValidatorForValues } from '../../utils';
let debouncedResize; let debouncedResize;
...@@ -23,19 +24,7 @@ export default { ...@@ -23,19 +24,7 @@ export default {
graphData: { graphData: {
type: Object, type: Object,
required: true, required: true,
validator(data) { validator: graphDataValidatorForValues.bind(null, false),
return (
Array.isArray(data.queries) &&
data.queries.filter(query => {
if (Array.isArray(query.result)) {
return (
query.result.filter(res => Array.isArray(res.values)).length === query.result.length
);
}
return false;
}).length === data.queries.length
);
},
}, },
containerWidth: { containerWidth: {
type: Number, type: Number,
......
...@@ -4,6 +4,7 @@ import { debounceByAnimationFrame } from '~/lib/utils/common_utils'; ...@@ -4,6 +4,7 @@ import { debounceByAnimationFrame } from '~/lib/utils/common_utils';
import { getSvgIconPathContent } from '~/lib/utils/icon_utils'; import { getSvgIconPathContent } from '~/lib/utils/icon_utils';
import { chartHeight } from '../../constants'; import { chartHeight } from '../../constants';
import { makeDataSeries } from '~/helpers/monitor_helper'; import { makeDataSeries } from '~/helpers/monitor_helper';
import { graphDataValidatorForValues } from '../../utils';
export default { export default {
components: { components: {
...@@ -14,23 +15,11 @@ export default { ...@@ -14,23 +15,11 @@ export default {
graphData: { graphData: {
type: Object, type: Object,
required: true, required: true,
validator(data) { validator: graphDataValidatorForValues.bind(null, false),
return ( },
Array.isArray(data.queries) && containerWidth: {
data.queries.filter(query => { type: Number,
if (Array.isArray(query.result)) { required: true,
return (
query.result.filter(res => Array.isArray(res.values)).length === query.result.length
);
}
return false;
}).length === data.queries.length
);
},
containerWidth: {
type: Number,
required: true,
},
}, },
}, },
data() { data() {
......
<script> <script>
import { GlSingleStat } from '@gitlab/ui/dist/charts'; import { GlSingleStat } from '@gitlab/ui/dist/charts';
import { roundOffFloat } from '~/lib/utils/common_utils';
import { graphDataValidatorForValues } from '../../utils';
export default { export default {
components: { components: {
...@@ -7,22 +9,21 @@ export default { ...@@ -7,22 +9,21 @@ export default {
}, },
inheritAttrs: false, inheritAttrs: false,
props: { props: {
title: { graphData: {
type: String, type: Object,
required: true,
},
value: {
type: Number,
required: true,
},
unit: {
type: String,
required: true, required: true,
validator: graphDataValidatorForValues.bind(null, true),
}, },
}, },
computed: { computed: {
valueWithUnit() { queryInfo() {
return `${this.value}${this.unit}`; return this.graphData.queries[0];
},
engineeringNotation() {
return `${roundOffFloat(this.queryInfo.result[0].value[1], 1)}${this.queryInfo.unit}`;
},
graphTitle() {
return this.queryInfo.label;
}, },
}, },
}; };
...@@ -30,8 +31,8 @@ export default { ...@@ -30,8 +31,8 @@ export default {
<template> <template>
<div class="prometheus-graph col-12 col-lg-6"> <div class="prometheus-graph col-12 col-lg-6">
<div class="prometheus-graph-header"> <div class="prometheus-graph-header">
<h5 ref="graphTitle" class="prometheus-graph-title">{{ title }}</h5> <h5 ref="graphTitle" class="prometheus-graph-title">{{ graphTitle }}</h5>
</div> </div>
<gl-single-stat :value="valueWithUnit" :title="title" variant="success" /> <gl-single-stat :value="engineeringNotation" :title="graphTitle" variant="success" />
</div> </div>
</template> </template>
...@@ -7,6 +7,8 @@ import Icon from '~/vue_shared/components/icon.vue'; ...@@ -7,6 +7,8 @@ import Icon from '~/vue_shared/components/icon.vue';
import { getParameterValues } from '~/lib/utils/url_utility'; import { getParameterValues } from '~/lib/utils/url_utility';
import invalidUrl from '~/lib/utils/invalid_url'; import invalidUrl from '~/lib/utils/invalid_url';
import MonitorAreaChart from './charts/area.vue'; import MonitorAreaChart from './charts/area.vue';
import MonitorSingleStatChart from './charts/single_stat.vue';
import PanelType from './panel_type.vue';
import GraphGroup from './graph_group.vue'; import GraphGroup from './graph_group.vue';
import EmptyState from './empty_state.vue'; import EmptyState from './empty_state.vue';
import { timeWindows, timeWindowsKeyNames } from '../constants'; import { timeWindows, timeWindowsKeyNames } from '../constants';
...@@ -18,6 +20,8 @@ let sidebarMutationObserver; ...@@ -18,6 +20,8 @@ let sidebarMutationObserver;
export default { export default {
components: { components: {
MonitorAreaChart, MonitorAreaChart,
MonitorSingleStatChart,
PanelType,
GraphGroup, GraphGroup,
EmptyState, EmptyState,
Icon, Icon,
...@@ -152,6 +156,7 @@ export default { ...@@ -152,6 +156,7 @@ export default {
'useDashboardEndpoint', 'useDashboardEndpoint',
'allDashboards', 'allDashboards',
'multipleDashboardsEnabled', 'multipleDashboardsEnabled',
'additionalPanelTypesEnabled',
]), ]),
groupsWithData() { groupsWithData() {
return this.groups.filter(group => this.chartsWithData(group.metrics).length > 0); return this.groups.filter(group => this.chartsWithData(group.metrics).length > 0);
...@@ -173,6 +178,7 @@ export default { ...@@ -173,6 +178,7 @@ export default {
deploymentsEndpoint: this.deploymentsEndpoint, deploymentsEndpoint: this.deploymentsEndpoint,
dashboardEndpoint: this.dashboardEndpoint, dashboardEndpoint: this.dashboardEndpoint,
currentDashboard: this.currentDashboard, currentDashboard: this.currentDashboard,
projectPath: this.projectPath,
}); });
this.timeWindows = timeWindows; this.timeWindows = timeWindows;
...@@ -220,6 +226,8 @@ export default { ...@@ -220,6 +226,8 @@ export default {
chart.metrics.some(metric => this.metricsWithData.includes(metric.metric_id)), chart.metrics.some(metric => this.metricsWithData.includes(metric.metric_id)),
); );
}, },
// TODO: BEGIN, Duplicated code with panel_type until feature flag is removed
// Issue number: https://gitlab.com/gitlab-org/gitlab-ce/issues/63845
getGraphAlerts(queries) { getGraphAlerts(queries) {
if (!this.allAlerts) return {}; if (!this.allAlerts) return {};
const metricIdsForChart = queries.map(q => q.metricId); const metricIdsForChart = queries.map(q => q.metricId);
...@@ -228,6 +236,7 @@ export default { ...@@ -228,6 +236,7 @@ export default {
getGraphAlertValues(queries) { getGraphAlertValues(queries) {
return Object.values(this.getGraphAlerts(queries)); return Object.values(this.getGraphAlerts(queries));
}, },
// TODO: END
hideAddMetricModal() { hideAddMetricModal() {
this.$refs.addMetricModal.hide(); this.$refs.addMetricModal.hide();
}, },
...@@ -366,24 +375,34 @@ export default { ...@@ -366,24 +375,34 @@ export default {
:name="groupData.group" :name="groupData.group"
:show-panels="showPanels" :show-panels="showPanels"
> >
<monitor-area-chart <template v-if="additionalPanelTypesEnabled">
v-for="(graphData, graphIndex) in chartsWithData(groupData.metrics)" <panel-type
:key="graphIndex" v-for="(graphData, graphIndex) in chartsWithData(groupData.metrics)"
:project-path="projectPath" :key="`panel-type-${graphIndex}`"
:graph-data="graphData" :graph-data="graphData"
:deployment-data="deploymentData" :dashboard-width="elWidth"
:thresholds="getGraphAlertValues(graphData.queries)"
:container-width="elWidth"
group-id="monitor-area-chart"
>
<alert-widget
v-if="alertWidgetAvailable && graphData"
:alerts-endpoint="alertsEndpoint"
:relevant-queries="graphData.queries"
:alerts-to-manage="getGraphAlerts(graphData.queries)"
@setAlerts="setAlerts"
/> />
</monitor-area-chart> </template>
<template v-else>
<monitor-area-chart
v-for="(graphData, graphIndex) in chartsWithData(groupData.metrics)"
:key="graphIndex"
:graph-data="graphData"
:deployment-data="deploymentData"
:thresholds="getGraphAlertValues(graphData.queries)"
:container-width="elWidth"
:project-path="projectPath"
group-id="monitor-area-chart"
>
<alert-widget
v-if="alertWidgetAvailable && graphData"
:alerts-endpoint="alertsEndpoint"
:relevant-queries="graphData.queries"
:alerts-to-manage="getGraphAlerts(graphData.queries)"
@setAlerts="setAlerts"
/>
</monitor-area-chart>
</template>
</graph-group> </graph-group>
</div> </div>
<empty-state <empty-state
......
<script>
import { mapState } from 'vuex';
import _ from 'underscore';
import MonitorAreaChart from './charts/area.vue';
import MonitorSingleStatChart from './charts/single_stat.vue';
export default {
components: {
MonitorAreaChart,
MonitorSingleStatChart,
},
props: {
graphData: {
type: Object,
required: true,
},
dashboardWidth: {
type: Number,
required: true,
},
},
computed: {
...mapState('monitoringDashboard', ['deploymentData', 'projectPath']),
alertWidgetAvailable() {
return IS_EE && this.prometheusAlertsAvailable && this.alertsEndpoint && this.graphData;
},
},
methods: {
getGraphAlerts(queries) {
if (!this.allAlerts) return {};
const metricIdsForChart = queries.map(q => q.metricId);
return _.pick(this.allAlerts, alert => metricIdsForChart.includes(alert.metricId));
},
getGraphAlertValues(queries) {
return Object.values(this.getGraphAlerts(queries));
},
isPanelType(type) {
return this.graphData.type && this.graphData.type === type;
},
},
};
</script>
<template>
<monitor-single-stat-chart v-if="isPanelType('single-stat')" :graph-data="graphData" />
<monitor-area-chart
v-else
:graph-data="graphData"
:deployment-data="deploymentData"
:project-path="projectPath"
:thresholds="getGraphAlertValues(graphData.queries)"
:container-width="dashboardWidth"
group-id="monitor-area-chart"
>
<alert-widget
v-if="alertWidgetAvailable"
:alerts-endpoint="alertsEndpoint"
:relevant-queries="graphData.queries"
:alerts-to-manage="getGraphAlerts(graphData.queries)"
@setAlerts="setAlerts"
/>
</monitor-area-chart>
</template>
...@@ -12,6 +12,7 @@ export default (props = {}) => { ...@@ -12,6 +12,7 @@ export default (props = {}) => {
store.dispatch('monitoringDashboard/setFeatureFlags', { store.dispatch('monitoringDashboard/setFeatureFlags', {
prometheusEndpointEnabled: gon.features.environmentMetricsUsePrometheusEndpoint, prometheusEndpointEnabled: gon.features.environmentMetricsUsePrometheusEndpoint,
multipleDashboardsEnabled: gon.features.environmentMetricsShowMultipleDashboards, multipleDashboardsEnabled: gon.features.environmentMetricsShowMultipleDashboards,
additionalPanelTypesEnabled: gon.features.environmentMetricsAdditionalPanelTypes,
}); });
} }
......
...@@ -37,10 +37,11 @@ export const setEndpoints = ({ commit }, endpoints) => { ...@@ -37,10 +37,11 @@ export const setEndpoints = ({ commit }, endpoints) => {
export const setFeatureFlags = ( export const setFeatureFlags = (
{ commit }, { commit },
{ prometheusEndpointEnabled, multipleDashboardsEnabled }, { prometheusEndpointEnabled, multipleDashboardsEnabled, additionalPanelTypesEnabled },
) => { ) => {
commit(types.SET_DASHBOARD_ENABLED, prometheusEndpointEnabled); commit(types.SET_DASHBOARD_ENABLED, prometheusEndpointEnabled);
commit(types.SET_MULTIPLE_DASHBOARDS_ENABLED, multipleDashboardsEnabled); commit(types.SET_MULTIPLE_DASHBOARDS_ENABLED, multipleDashboardsEnabled);
commit(types.SET_ADDITIONAL_PANEL_TYPES_ENABLED, additionalPanelTypesEnabled);
}; };
export const requestMetricsDashboard = ({ commit }) => { export const requestMetricsDashboard = ({ commit }) => {
......
...@@ -11,6 +11,7 @@ export const SET_QUERY_RESULT = 'SET_QUERY_RESULT'; ...@@ -11,6 +11,7 @@ export const SET_QUERY_RESULT = 'SET_QUERY_RESULT';
export const SET_TIME_WINDOW = 'SET_TIME_WINDOW'; export const SET_TIME_WINDOW = 'SET_TIME_WINDOW';
export const SET_DASHBOARD_ENABLED = 'SET_DASHBOARD_ENABLED'; export const SET_DASHBOARD_ENABLED = 'SET_DASHBOARD_ENABLED';
export const SET_MULTIPLE_DASHBOARDS_ENABLED = 'SET_MULTIPLE_DASHBOARDS_ENABLED'; export const SET_MULTIPLE_DASHBOARDS_ENABLED = 'SET_MULTIPLE_DASHBOARDS_ENABLED';
export const SET_ADDITIONAL_PANEL_TYPES_ENABLED = 'SET_ADDITIONAL_PANEL_TYPES_ENABLED';
export const SET_ALL_DASHBOARDS = 'SET_ALL_DASHBOARDS'; export const SET_ALL_DASHBOARDS = 'SET_ALL_DASHBOARDS';
export const SET_ENDPOINTS = 'SET_ENDPOINTS'; export const SET_ENDPOINTS = 'SET_ENDPOINTS';
export const SET_GETTING_STARTED_EMPTY_STATE = 'SET_GETTING_STARTED_EMPTY_STATE'; export const SET_GETTING_STARTED_EMPTY_STATE = 'SET_GETTING_STARTED_EMPTY_STATE';
......
...@@ -75,6 +75,7 @@ export default { ...@@ -75,6 +75,7 @@ export default {
state.deploymentsEndpoint = endpoints.deploymentsEndpoint; state.deploymentsEndpoint = endpoints.deploymentsEndpoint;
state.dashboardEndpoint = endpoints.dashboardEndpoint; state.dashboardEndpoint = endpoints.dashboardEndpoint;
state.currentDashboard = endpoints.currentDashboard; state.currentDashboard = endpoints.currentDashboard;
state.projectPath = endpoints.projectPath;
}, },
[types.SET_DASHBOARD_ENABLED](state, enabled) { [types.SET_DASHBOARD_ENABLED](state, enabled) {
state.useDashboardEndpoint = enabled; state.useDashboardEndpoint = enabled;
...@@ -92,4 +93,7 @@ export default { ...@@ -92,4 +93,7 @@ export default {
[types.SET_ALL_DASHBOARDS](state, dashboards) { [types.SET_ALL_DASHBOARDS](state, dashboards) {
state.allDashboards = dashboards; state.allDashboards = dashboards;
}, },
[types.SET_ADDITIONAL_PANEL_TYPES_ENABLED](state, enabled) {
state.additionalPanelTypesEnabled = enabled;
},
}; };
...@@ -9,6 +9,7 @@ export default () => ({ ...@@ -9,6 +9,7 @@ export default () => ({
dashboardEndpoint: invalidUrl, dashboardEndpoint: invalidUrl,
useDashboardEndpoint: false, useDashboardEndpoint: false,
multipleDashboardsEnabled: false, multipleDashboardsEnabled: false,
additionalPanelTypesEnabled: false,
emptyState: 'gettingStarted', emptyState: 'gettingStarted',
showEmptyState: true, showEmptyState: true,
groups: [], groups: [],
...@@ -17,4 +18,5 @@ export default () => ({ ...@@ -17,4 +18,5 @@ export default () => ({
metricsWithData: [], metricsWithData: [],
allDashboards: [], allDashboards: [],
currentDashboard: null, currentDashboard: null,
projectPath: null,
}); });
...@@ -69,13 +69,26 @@ export const sortMetrics = metrics => ...@@ -69,13 +69,26 @@ export const sortMetrics = metrics =>
.sortBy('weight') .sortBy('weight')
.value(); .value();
export const normalizeQueryResult = timeSeries => ({ export const normalizeQueryResult = timeSeries => {
...timeSeries, let normalizedResult = {};
values: timeSeries.values.map(([timestamp, value]) => [
new Date(timestamp * 1000).toISOString(), if (timeSeries.values) {
Number(value), normalizedResult = {
]), ...timeSeries,
}); values: timeSeries.values.map(([timestamp, value]) => [
new Date(timestamp * 1000).toISOString(),
Number(value),
]),
};
} else if (timeSeries.value) {
normalizedResult = {
...timeSeries,
value: [new Date(timeSeries.value[0] * 1000).toISOString(), Number(timeSeries.value[1])],
};
}
return normalizedResult;
};
export const normalizeMetrics = metrics => { export const normalizeMetrics = metrics => {
const groupedMetrics = groupQueriesByChartInfo(metrics); const groupedMetrics = groupQueriesByChartInfo(metrics);
......
...@@ -30,4 +30,28 @@ export const getTimeDiff = selectedTimeWindow => { ...@@ -30,4 +30,28 @@ export const getTimeDiff = selectedTimeWindow => {
return { start, end }; return { start, end };
}; };
/**
* This method is used to validate if the graph data format for a chart component
* that needs a time series as a response from a prometheus query (query_range) is
* of a valid format or not.
* @param {Object} graphData the graph data response from a prometheus request
* @returns {boolean} whether the graphData format is correct
*/
export const graphDataValidatorForValues = (isValues, graphData) => {
const responseValueKeyName = isValues ? 'value' : 'values';
return (
Array.isArray(graphData.queries) &&
graphData.queries.filter(query => {
if (Array.isArray(query.result)) {
return (
query.result.filter(res => Array.isArray(res[responseValueKeyName])).length ===
query.result.length
);
}
return false;
}).length === graphData.queries.length
);
};
export default {}; export default {};
...@@ -13,6 +13,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController ...@@ -13,6 +13,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController
before_action only: [:metrics, :additional_metrics, :metrics_dashboard] do before_action only: [:metrics, :additional_metrics, :metrics_dashboard] do
push_frontend_feature_flag(:environment_metrics_use_prometheus_endpoint) push_frontend_feature_flag(:environment_metrics_use_prometheus_endpoint)
push_frontend_feature_flag(:environment_metrics_show_multiple_dashboards) push_frontend_feature_flag(:environment_metrics_show_multiple_dashboards)
push_frontend_feature_flag(:environment_metrics_additional_panel_types)
push_frontend_feature_flag(:prometheus_computed_alerts) push_frontend_feature_flag(:prometheus_computed_alerts)
end end
......
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import SingleStatChart from '~/monitoring/components/charts/single_stat.vue'; import SingleStatChart from '~/monitoring/components/charts/single_stat.vue';
import { graphDataPrometheusQuery } from '../mock_data';
describe('Single Stat Chart component', () => { describe('Single Stat Chart component', () => {
let singleStatChart; let singleStatChart;
...@@ -7,9 +8,7 @@ describe('Single Stat Chart component', () => { ...@@ -7,9 +8,7 @@ describe('Single Stat Chart component', () => {
beforeEach(() => { beforeEach(() => {
singleStatChart = shallowMount(SingleStatChart, { singleStatChart = shallowMount(SingleStatChart, {
propsData: { propsData: {
title: 'Time to render', graphData: graphDataPrometheusQuery,
value: 1,
unit: 'sec',
}, },
}); });
}); });
...@@ -19,9 +18,9 @@ describe('Single Stat Chart component', () => { ...@@ -19,9 +18,9 @@ describe('Single Stat Chart component', () => {
}); });
describe('computed', () => { describe('computed', () => {
describe('valueWithUnit', () => { describe('engineeringNotation', () => {
it('should interpolate the value and unit props', () => { it('should interpolate the value and unit props', () => {
expect(singleStatChart.vm.valueWithUnit).toBe('1sec'); expect(singleStatChart.vm.engineeringNotation).toBe('91MB');
}); });
}); });
}); });
......
...@@ -935,3 +935,75 @@ export const dashboardGitResponse = [ ...@@ -935,3 +935,75 @@ export const dashboardGitResponse = [
default: false, default: false,
}, },
]; ];
export const graphDataPrometheusQuery = {
title: 'Super Chart A2',
type: 'single-stat',
weight: 2,
metrics: [
{
id: 'metric_a1',
metric_id: 2,
query: 'max(go_memstats_alloc_bytes{job="prometheus"}) by (job) /1024/1024',
unit: 'MB',
label: 'Total Consumption',
prometheus_endpoint_path:
'/root/kubernetes-gke-project/environments/35/prometheus/api/v1/query?query=max%28go_memstats_alloc_bytes%7Bjob%3D%22prometheus%22%7D%29+by+%28job%29+%2F1024%2F1024',
},
],
queries: [
{
metricId: null,
id: 'metric_a1',
metric_id: 2,
query: 'max(go_memstats_alloc_bytes{job="prometheus"}) by (job) /1024/1024',
unit: 'MB',
label: 'Total Consumption',
prometheus_endpoint_path:
'/root/kubernetes-gke-project/environments/35/prometheus/api/v1/query?query=max%28go_memstats_alloc_bytes%7Bjob%3D%22prometheus%22%7D%29+by+%28job%29+%2F1024%2F1024',
result: [
{
metric: { job: 'prometheus' },
value: ['2019-06-26T21:03:20.881Z', 91],
},
],
},
],
};
export const graphDataPrometheusQueryRange = {
title: 'Super Chart A1',
type: 'area',
weight: 2,
metrics: [
{
id: 'metric_a1',
metric_id: 2,
query_range:
'avg(sum(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}) by (job)) without (job) /1024/1024/1024',
unit: 'MB',
label: 'Total Consumption',
prometheus_endpoint_path:
'/root/kubernetes-gke-project/environments/35/prometheus/api/v1/query?query=max%28go_memstats_alloc_bytes%7Bjob%3D%22prometheus%22%7D%29+by+%28job%29+%2F1024%2F1024',
},
],
queries: [
{
metricId: null,
id: 'metric_a1',
metric_id: 2,
query_range:
'avg(sum(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}) by (job)) without (job) /1024/1024/1024',
unit: 'MB',
label: 'Total Consumption',
prometheus_endpoint_path:
'/root/kubernetes-gke-project/environments/35/prometheus/api/v1/query?query=max%28go_memstats_alloc_bytes%7Bjob%3D%22prometheus%22%7D%29+by+%28job%29+%2F1024%2F1024',
result: [
{
metric: {},
values: [[1495700554.925, '8.0390625'], [1495700614.925, '8.0390625']],
},
],
},
],
};
...@@ -115,12 +115,14 @@ describe('Monitoring mutations', () => { ...@@ -115,12 +115,14 @@ describe('Monitoring mutations', () => {
environmentsEndpoint: 'environments.json', environmentsEndpoint: 'environments.json',
deploymentsEndpoint: 'deployments.json', deploymentsEndpoint: 'deployments.json',
dashboardEndpoint: 'dashboard.json', dashboardEndpoint: 'dashboard.json',
projectPath: '/gitlab-org/gitlab-ce',
}); });
expect(stateCopy.metricsEndpoint).toEqual('additional_metrics.json'); expect(stateCopy.metricsEndpoint).toEqual('additional_metrics.json');
expect(stateCopy.environmentsEndpoint).toEqual('environments.json'); expect(stateCopy.environmentsEndpoint).toEqual('environments.json');
expect(stateCopy.deploymentsEndpoint).toEqual('deployments.json'); expect(stateCopy.deploymentsEndpoint).toEqual('deployments.json');
expect(stateCopy.dashboardEndpoint).toEqual('dashboard.json'); expect(stateCopy.dashboardEndpoint).toEqual('dashboard.json');
expect(stateCopy.projectPath).toEqual('/gitlab-org/gitlab-ce');
}); });
}); });
......
import { getTimeDiff } from '~/monitoring/utils'; import { getTimeDiff, graphDataValidatorForValues } from '~/monitoring/utils';
import { timeWindows } from '~/monitoring/constants'; import { timeWindows } from '~/monitoring/constants';
import { graphDataPrometheusQuery, graphDataPrometheusQueryRange } from './mock_data';
describe('getTimeDiff', () => { describe('getTimeDiff', () => {
it('defaults to an 8 hour (28800s) difference', () => { it('defaults to an 8 hour (28800s) difference', () => {
...@@ -27,3 +28,27 @@ describe('getTimeDiff', () => { ...@@ -27,3 +28,27 @@ describe('getTimeDiff', () => {
}); });
}); });
}); });
describe('graphDataValidatorForValues', () => {
/*
* When dealing with a metric using the query format, e.g.
* query: 'max(go_memstats_alloc_bytes{job="prometheus"}) by (job) /1024/1024'
* the validator will look for the `value` key instead of `values`
*/
it('validates data with the query format', () => {
const validGraphData = graphDataValidatorForValues(true, graphDataPrometheusQuery);
expect(validGraphData).toBe(true);
});
/*
* When dealing with a metric using the query?range format, e.g.
* query_range: 'avg(sum(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}) by (job)) without (job) /1024/1024/1024',
* the validator will look for the `values` key instead of `value`
*/
it('validates data with the query_range format', () => {
const validGraphData = graphDataValidatorForValues(false, graphDataPrometheusQueryRange);
expect(validGraphData).toBe(true);
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment