summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/monitoring
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2019-12-13 12:07:41 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2019-12-13 12:07:41 +0000
commit8cc5f2790908ba9bb8eecba2b78a3c5a88c77b90 (patch)
tree2d6211503a5111d43a9edce0c56b94fd1b347e1b /app/assets/javascripts/monitoring
parent17b91a3c6ab73fff087e91665e9afb8046cbf045 (diff)
downloadgitlab-ce-8cc5f2790908ba9bb8eecba2b78a3c5a88c77b90.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/monitoring')
-rw-r--r--app/assets/javascripts/monitoring/constants.js37
-rw-r--r--app/assets/javascripts/monitoring/stores/actions.js21
-rw-r--r--app/assets/javascripts/monitoring/stores/mutation_types.js8
-rw-r--r--app/assets/javascripts/monitoring/stores/mutations.js120
-rw-r--r--app/assets/javascripts/monitoring/stores/state.js2
5 files changed, 167 insertions, 21 deletions
diff --git a/app/assets/javascripts/monitoring/constants.js b/app/assets/javascripts/monitoring/constants.js
index 1a1fcdd0e66..e613351e524 100644
--- a/app/assets/javascripts/monitoring/constants.js
+++ b/app/assets/javascripts/monitoring/constants.js
@@ -1,5 +1,42 @@
import { __ } from '~/locale';
+export const PROMETHEUS_TIMEOUT = 120000; // TWO_MINUTES
+
+/**
+ * Errors in Prometheus Queries (PromQL) for metrics
+ */
+export const metricsErrors = {
+ /**
+ * Connection timed out to prometheus server
+ * the timeout is set to PROMETHEUS_TIMEOUT
+ *
+ */
+ TIMEOUT: 'TIMEOUT',
+
+ /**
+ * The prometheus server replies with an empty data set
+ */
+ NO_DATA: 'NO_DATA',
+
+ /**
+ * The prometheus server cannot be reached
+ */
+ CONNECTION_FAILED: 'CONNECTION_FAILED',
+
+ /**
+ * The prometheus server was reach but it cannot process
+ * the query. This can happen for several reasons:
+ * - PromQL syntax is incorrect
+ * - An operator is not supported
+ */
+ BAD_DATA: 'BAD_DATA',
+
+ /**
+ * No specific reason found for error
+ */
+ UNKNOWN_ERROR: 'UNKNOWN_ERROR',
+};
+
export const sidebarAnimationDuration = 300; // milliseconds.
export const chartHeight = 300;
diff --git a/app/assets/javascripts/monitoring/stores/actions.js b/app/assets/javascripts/monitoring/stores/actions.js
index 268d9d636b1..a655191b2b4 100644
--- a/app/assets/javascripts/monitoring/stores/actions.js
+++ b/app/assets/javascripts/monitoring/stores/actions.js
@@ -6,7 +6,7 @@ import statusCodes from '../../lib/utils/http_status';
import { backOff } from '../../lib/utils/common_utils';
import { s__, sprintf } from '../../locale';
-const TWO_MINUTES = 120000;
+import { PROMETHEUS_TIMEOUT } from '../constants';
function backOffRequest(makeRequestCallback) {
return backOff((next, stop) => {
@@ -19,7 +19,7 @@ function backOffRequest(makeRequestCallback) {
}
})
.catch(stop);
- }, TWO_MINUTES);
+ }, PROMETHEUS_TIMEOUT);
}
export const setGettingStartedEmptyState = ({ commit }) => {
@@ -125,9 +125,17 @@ export const fetchPrometheusMetric = ({ commit }, { metric, params }) => {
step,
};
- return fetchPrometheusResult(metric.prometheus_endpoint_path, queryParams).then(result => {
- commit(types.SET_QUERY_RESULT, { metricId: metric.metric_id, result });
- });
+ commit(types.REQUEST_METRIC_RESULT, { metricId: metric.metric_id });
+
+ return fetchPrometheusResult(metric.prometheus_endpoint_path, queryParams)
+ .then(result => {
+ commit(types.RECEIVE_METRIC_RESULT_SUCCESS, { metricId: metric.metric_id, result });
+ })
+ .catch(error => {
+ commit(types.RECEIVE_METRIC_RESULT_ERROR, { metricId: metric.metric_id, error });
+ // Continue to throw error so the dashboard can notify using createFlash
+ throw error;
+ });
};
export const fetchPrometheusMetrics = ({ state, commit, dispatch, getters }, params) => {
@@ -159,7 +167,8 @@ export const fetchDeploymentsData = ({ state, dispatch }) => {
if (!state.deploymentsEndpoint) {
return Promise.resolve([]);
}
- return backOffRequest(() => axios.get(state.deploymentsEndpoint))
+ return axios
+ .get(state.deploymentsEndpoint)
.then(resp => resp.data)
.then(response => {
if (!response || !response.deployments) {
diff --git a/app/assets/javascripts/monitoring/stores/mutation_types.js b/app/assets/javascripts/monitoring/stores/mutation_types.js
index fa15a2ba800..e4e467f3d68 100644
--- a/app/assets/javascripts/monitoring/stores/mutation_types.js
+++ b/app/assets/javascripts/monitoring/stores/mutation_types.js
@@ -1,13 +1,19 @@
export const REQUEST_METRICS_DATA = 'REQUEST_METRICS_DATA';
export const RECEIVE_METRICS_DATA_SUCCESS = 'RECEIVE_METRICS_DATA_SUCCESS';
export const RECEIVE_METRICS_DATA_FAILURE = 'RECEIVE_METRICS_DATA_FAILURE';
+
export const REQUEST_DEPLOYMENTS_DATA = 'REQUEST_DEPLOYMENTS_DATA';
export const RECEIVE_DEPLOYMENTS_DATA_SUCCESS = 'RECEIVE_DEPLOYMENTS_DATA_SUCCESS';
export const RECEIVE_DEPLOYMENTS_DATA_FAILURE = 'RECEIVE_DEPLOYMENTS_DATA_FAILURE';
+
export const REQUEST_ENVIRONMENTS_DATA = 'REQUEST_ENVIRONMENTS_DATA';
export const RECEIVE_ENVIRONMENTS_DATA_SUCCESS = 'RECEIVE_ENVIRONMENTS_DATA_SUCCESS';
export const RECEIVE_ENVIRONMENTS_DATA_FAILURE = 'RECEIVE_ENVIRONMENTS_DATA_FAILURE';
-export const SET_QUERY_RESULT = 'SET_QUERY_RESULT';
+
+export const REQUEST_METRIC_RESULT = 'REQUEST_METRIC_RESULT';
+export const RECEIVE_METRIC_RESULT_SUCCESS = 'RECEIVE_METRIC_RESULT_SUCCESS';
+export const RECEIVE_METRIC_RESULT_ERROR = 'RECEIVE_METRIC_RESULT_ERROR';
+
export const SET_TIME_WINDOW = 'SET_TIME_WINDOW';
export const SET_ALL_DASHBOARDS = 'SET_ALL_DASHBOARDS';
export const SET_ENDPOINTS = 'SET_ENDPOINTS';
diff --git a/app/assets/javascripts/monitoring/stores/mutations.js b/app/assets/javascripts/monitoring/stores/mutations.js
index db5ec4e9e2b..f04c12c2ac8 100644
--- a/app/assets/javascripts/monitoring/stores/mutations.js
+++ b/app/assets/javascripts/monitoring/stores/mutations.js
@@ -2,6 +2,9 @@ import Vue from 'vue';
import { slugify } from '~/lib/utils/text_utility';
import * as types from './mutation_types';
import { normalizeMetric, normalizeQueryResult } from './utils';
+import { BACKOFF_TIMEOUT } from '../../lib/utils/common_utils';
+import { metricsErrors } from '../constants';
+import httpStatusCodes from '~/lib/utils/http_status';
const normalizePanelMetrics = (metrics, defaultLabel) =>
metrics.map(metric => ({
@@ -9,7 +12,74 @@ const normalizePanelMetrics = (metrics, defaultLabel) =>
label: metric.label || defaultLabel,
}));
+/**
+ * Locate and return a metric in the dashboard by its id
+ * as generated by `uniqMetricsId()`.
+ * @param {String} metricId Unique id in the dashboard
+ * @param {Object} dashboard Full dashboard object
+ */
+const findMetricInDashboard = (metricId, dashboard) => {
+ let res = null;
+ dashboard.panel_groups.forEach(group => {
+ group.panels.forEach(panel => {
+ panel.metrics.forEach(metric => {
+ if (metric.metric_id === metricId) {
+ res = metric;
+ }
+ });
+ });
+ });
+ return res;
+};
+
+/**
+ * Set a new state for a metric.
+ *
+ * Initally metric data is not populated, so `Vue.set` is
+ * used to add new properties to the metric.
+ *
+ * @param {Object} metric - Metric object as defined in the dashboard
+ * @param {Object} state - New state
+ * @param {Array|null} state.result - Array of results
+ * @param {String} state.error - Error code from metricsErrors
+ * @param {Boolean} state.loading - True if the metric is loading
+ */
+const setMetricState = (metric, { result = null, error = null, loading = false }) => {
+ Vue.set(metric, 'result', result);
+ Vue.set(metric, 'error', error);
+ Vue.set(metric, 'loading', loading);
+};
+
+/**
+ * Maps a backened error state to a `metricsErrors` constant
+ * @param {Object} error - Error from backend response
+ */
+const getMetricError = error => {
+ if (!error) {
+ return metricsErrors.UNKNOWN_ERROR;
+ }
+
+ // Special error responses
+ if (error.message === BACKOFF_TIMEOUT) {
+ return metricsErrors.TIMEOUT;
+ }
+
+ // Axios error responses
+ const { response } = error;
+ if (response && response.status === httpStatusCodes.SERVICE_UNAVAILABLE) {
+ return metricsErrors.CONNECTION_FAILED;
+ } else if (response && response.status === httpStatusCodes.BAD_REQUEST) {
+ // Note: "error.response.data.error" may contain Prometheus error information
+ return metricsErrors.BAD_DATA;
+ }
+
+ return metricsErrors.UNKNOWN_ERROR;
+};
+
export default {
+ /**
+ * Dashboard panels structure and global state
+ */
[types.REQUEST_METRICS_DATA](state) {
state.emptyState = 'loading';
state.showEmptyState = true;
@@ -40,6 +110,10 @@ export default {
state.emptyState = error ? 'unableToConnect' : 'noData';
state.showEmptyState = true;
},
+
+ /**
+ * Deployments and environments
+ */
[types.RECEIVE_DEPLOYMENTS_DATA_SUCCESS](state, deployments) {
state.deploymentData = deployments;
},
@@ -53,28 +127,46 @@ export default {
state.environments = [];
},
- [types.SET_QUERY_RESULT](state, { metricId, result }) {
- if (!metricId || !result || result.length === 0) {
+ /**
+ * Individual panel/metric results
+ */
+ [types.REQUEST_METRIC_RESULT](state, { metricId }) {
+ const metric = findMetricInDashboard(metricId, state.dashboard);
+
+ setMetricState(metric, {
+ loading: true,
+ });
+ },
+ [types.RECEIVE_METRIC_RESULT_SUCCESS](state, { metricId, result }) {
+ if (!metricId) {
return;
}
state.showEmptyState = false;
- /**
- * Search the dashboard state for a matching id
- */
- state.dashboard.panel_groups.forEach(group => {
- group.panels.forEach(panel => {
- panel.metrics.forEach(metric => {
- if (metric.metric_id === metricId) {
- // ensure dates/numbers are correctly formatted for charts
- const normalizedResults = result.map(normalizeQueryResult);
- Vue.set(metric, 'result', Object.freeze(normalizedResults));
- }
- });
+ const metric = findMetricInDashboard(metricId, state.dashboard);
+ if (!result || result.length === 0) {
+ // If no data is return we still consider it an error and set it to undefined
+ setMetricState(metric, {
+ error: metricsErrors.NO_DATA,
+ });
+ } else {
+ const normalizedResults = result.map(normalizeQueryResult);
+ setMetricState(metric, {
+ result: Object.freeze(normalizedResults),
});
+ }
+ },
+ [types.RECEIVE_METRIC_RESULT_ERROR](state, { metricId, error }) {
+ if (!metricId) {
+ return;
+ }
+ const metric = findMetricInDashboard(metricId, state.dashboard);
+ setMetricState(metric, {
+ error: getMetricError(error),
});
},
+
[types.SET_ENDPOINTS](state, endpoints) {
state.metricsEndpoint = endpoints.metricsEndpoint;
state.environmentsEndpoint = endpoints.environmentsEndpoint;
diff --git a/app/assets/javascripts/monitoring/stores/state.js b/app/assets/javascripts/monitoring/stores/state.js
index 88f333aeb80..ee8a85ea222 100644
--- a/app/assets/javascripts/monitoring/stores/state.js
+++ b/app/assets/javascripts/monitoring/stores/state.js
@@ -8,9 +8,11 @@ export default () => ({
emptyState: 'gettingStarted',
showEmptyState: true,
showErrorBanner: true,
+
dashboard: {
panel_groups: [],
},
+
deploymentData: [],
environments: [],
allDashboards: [],