Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/api.js6
-rw-r--r--app/assets/javascripts/ci_variable_list/store/actions.js155
-rw-r--r--app/assets/javascripts/ci_variable_list/store/index.js17
-rw-r--r--app/assets/javascripts/ci_variable_list/store/mutation_types.js22
-rw-r--r--app/assets/javascripts/ci_variable_list/store/mutations.js86
-rw-r--r--app/assets/javascripts/ci_variable_list/store/state.js24
-rw-r--r--app/assets/javascripts/ci_variable_list/store/utils.js44
-rw-r--r--app/assets/stylesheets/framework/typography.scss2
-rw-r--r--app/workers/admin_email_worker.rb2
-rw-r--r--app/workers/all_queues.yml6
-rw-r--r--app/workers/gitlab_usage_ping_worker.rb2
-rw-r--r--app/workers/prune_old_events_worker.rb2
-rw-r--r--changelogs/unreleased/27142-divider-in-readme-is-overlaying-with-image.yml5
-rw-r--r--doc/administration/packages/container_registry.md6
-rw-r--r--doc/administration/server_hooks.md5
-rw-r--r--doc/administration/troubleshooting/kubernetes_cheat_sheet.md2
-rw-r--r--doc/api/protected_environments.md16
-rw-r--r--doc/ci/README.md1
-rw-r--r--doc/ci/docker/using_docker_images.md2
-rw-r--r--doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md8
-rw-r--r--doc/ci/pipelines/pipeline_architectures.md269
-rw-r--r--doc/ci/quick_start/README.md5
-rw-r--r--doc/ci/services/postgres.md6
-rw-r--r--doc/development/code_comments.md2
-rw-r--r--doc/development/creating_enums.md2
-rw-r--r--doc/development/documentation/styleguide.md55
-rw-r--r--doc/development/gitaly.md8
-rw-r--r--doc/development/go_guide/index.md2
-rw-r--r--doc/topics/autodevops/index.md14
-rw-r--r--doc/topics/web_application_firewall/quick_start_guide.md60
-rw-r--r--doc/user/project/integrations/img/prometheus_dashboard_environments_v12_8.pngbin0 -> 7422 bytes
-rw-r--r--doc/user/project/integrations/img/prometheus_monitoring_dashboard_v12_8.pngbin0 -> 29683 bytes
-rw-r--r--doc/user/project/integrations/prometheus.md34
-rw-r--r--lib/gitlab/ci/templates/Jobs/Browser-Performance-Testing.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Jobs/Build.gitlab-ci.yml1
-rw-r--r--lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml2
-rw-r--r--lib/quality/kubernetes_client.rb3
-rw-r--r--locale/gitlab.pot18
-rw-r--r--spec/frontend/ci_variable_list/services/mock_data.js90
-rw-r--r--spec/frontend/ci_variable_list/store/actions_spec.js279
-rw-r--r--spec/frontend/ci_variable_list/store/mutations_spec.js64
-rw-r--r--spec/frontend/ci_variable_list/store/utils_spec.js47
-rw-r--r--spec/lib/quality/kubernetes_client_spec.rb6
43 files changed, 1281 insertions, 101 deletions
diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js
index 4dc4ce543e9..8a6395b42b5 100644
--- a/app/assets/javascripts/api.js
+++ b/app/assets/javascripts/api.js
@@ -47,6 +47,7 @@ const Api = {
adminStatisticsPath: '/api/:version/application/statistics',
pipelineSinglePath: '/api/:version/projects/:id/pipelines/:pipeline_id',
lsifPath: '/api/:version/projects/:id/commits/:commit_id/lsif/info',
+ environmentsPath: '/api/:version/projects/:id/environments',
group(groupId, callback) {
const url = Api.buildUrl(Api.groupPath).replace(':id', groupId);
@@ -483,6 +484,11 @@ const Api = {
return axios.get(url, { params: { path } });
},
+ environments(id) {
+ const url = Api.buildUrl(this.environmentsPath).replace(':id', encodeURIComponent(id));
+ return axios.get(url);
+ },
+
buildUrl(url) {
return joinPaths(gon.relative_url_root || '', url.replace(':version', gon.api_version));
},
diff --git a/app/assets/javascripts/ci_variable_list/store/actions.js b/app/assets/javascripts/ci_variable_list/store/actions.js
new file mode 100644
index 00000000000..f3a629b84ee
--- /dev/null
+++ b/app/assets/javascripts/ci_variable_list/store/actions.js
@@ -0,0 +1,155 @@
+import * as types from './mutation_types';
+import axios from '~/lib/utils/axios_utils';
+import Api from '~/api';
+import createFlash from '~/flash';
+import { __ } from '~/locale';
+import { prepareDataForApi, prepareDataForDisplay, prepareEnvironments } from './utils';
+
+export const toggleValues = ({ commit }, valueState) => {
+ commit(types.TOGGLE_VALUES, valueState);
+};
+
+export const clearModal = ({ commit }) => {
+ commit(types.CLEAR_MODAL);
+};
+
+export const resetEditing = ({ commit, dispatch }) => {
+ // fetch variables again if modal is being edited and then hidden
+ // without saving changes, to cover use case of reactivity in the table
+ dispatch('fetchVariables');
+ commit(types.RESET_EDITING);
+};
+
+export const requestAddVariable = ({ commit }) => {
+ commit(types.REQUEST_ADD_VARIABLE);
+};
+
+export const receiveAddVariableSuccess = ({ commit }) => {
+ commit(types.RECEIVE_ADD_VARIABLE_SUCCESS);
+};
+
+export const receiveAddVariableError = ({ commit }, error) => {
+ commit(types.RECEIVE_ADD_VARIABLE_ERROR, error);
+};
+
+export const addVariable = ({ state, dispatch }) => {
+ dispatch('requestAddVariable');
+
+ return axios
+ .patch(state.endpoint, {
+ variables_attributes: [prepareDataForApi(state.variable)],
+ })
+ .then(() => {
+ dispatch('receiveAddVariableSuccess');
+ dispatch('fetchVariables');
+ })
+ .catch(error => {
+ createFlash(error.response.data[0]);
+ dispatch('receiveAddVariableError', error);
+ });
+};
+
+export const requestUpdateVariable = ({ commit }) => {
+ commit(types.REQUEST_UPDATE_VARIABLE);
+};
+
+export const receiveUpdateVariableSuccess = ({ commit }) => {
+ commit(types.RECEIVE_UPDATE_VARIABLE_SUCCESS);
+};
+
+export const receiveUpdateVariableError = ({ commit }, error) => {
+ commit(types.RECEIVE_UPDATE_VARIABLE_ERROR, error);
+};
+
+export const updateVariable = ({ state, dispatch }, variable) => {
+ dispatch('requestUpdateVariable');
+
+ const updatedVariable = prepareDataForApi(variable);
+ updatedVariable.secrect_value = updateVariable.value;
+
+ return axios
+ .patch(state.endpoint, { variables_attributes: [updatedVariable] })
+ .then(() => {
+ dispatch('receiveUpdateVariableSuccess');
+ dispatch('fetchVariables');
+ })
+ .catch(error => {
+ createFlash(error.response.data[0]);
+ dispatch('receiveUpdateVariableError', error);
+ });
+};
+
+export const editVariable = ({ commit }, variable) => {
+ const variableToEdit = variable;
+ variableToEdit.secret_value = variableToEdit.value;
+ commit(types.VARIABLE_BEING_EDITED, variableToEdit);
+};
+
+export const requestVariables = ({ commit }) => {
+ commit(types.REQUEST_VARIABLES);
+};
+export const receiveVariablesSuccess = ({ commit }, variables) => {
+ commit(types.RECEIVE_VARIABLES_SUCCESS, variables);
+};
+
+export const fetchVariables = ({ dispatch, state }) => {
+ dispatch('requestVariables');
+
+ return axios
+ .get(state.endpoint)
+ .then(({ data }) => {
+ dispatch('receiveVariablesSuccess', prepareDataForDisplay(data.variables));
+ })
+ .catch(() => {
+ createFlash(__('There was an error fetching the variables.'));
+ });
+};
+
+export const requestDeleteVariable = ({ commit }) => {
+ commit(types.REQUEST_DELETE_VARIABLE);
+};
+
+export const receiveDeleteVariableSuccess = ({ commit }) => {
+ commit(types.RECEIVE_DELETE_VARIABLE_SUCCESS);
+};
+
+export const receiveDeleteVariableError = ({ commit }, error) => {
+ commit(types.RECEIVE_DELETE_VARIABLE_ERROR, error);
+};
+
+export const deleteVariable = ({ dispatch, state }, variable) => {
+ dispatch('requestDeleteVariable');
+
+ const destroy = true;
+
+ return axios
+ .patch(state.endpoint, { variables_attributes: [prepareDataForApi(variable, destroy)] })
+ .then(() => {
+ dispatch('receiveDeleteVariableSuccess');
+ dispatch('fetchVariables');
+ })
+ .catch(error => {
+ createFlash(error.response.data[0]);
+ dispatch('receiveDeleteVariableError', error);
+ });
+};
+
+export const requestEnvironments = ({ commit }) => {
+ commit(types.REQUEST_ENVIRONMENTS);
+};
+
+export const receiveEnvironmentsSuccess = ({ commit }, environments) => {
+ commit(types.RECEIVE_ENVIRONMENTS_SUCCESS, environments);
+};
+
+export const fetchEnvironments = ({ dispatch, state }) => {
+ dispatch('requestEnvironments');
+
+ return Api.environments(state.projectId)
+ .then(res => {
+ dispatch('receiveEnvironmentsSuccess', prepareEnvironments(res.data));
+ })
+ .catch(() => {
+ createFlash(__('There was an error fetching the environments information.'));
+ });
+};
diff --git a/app/assets/javascripts/ci_variable_list/store/index.js b/app/assets/javascripts/ci_variable_list/store/index.js
new file mode 100644
index 00000000000..db4ba95b3c2
--- /dev/null
+++ b/app/assets/javascripts/ci_variable_list/store/index.js
@@ -0,0 +1,17 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import * as actions from './actions';
+import mutations from './mutations';
+import state from './state';
+
+Vue.use(Vuex);
+
+export default (initialState = {}) =>
+ new Vuex.Store({
+ actions,
+ mutations,
+ state: {
+ ...state(),
+ ...initialState,
+ },
+ });
diff --git a/app/assets/javascripts/ci_variable_list/store/mutation_types.js b/app/assets/javascripts/ci_variable_list/store/mutation_types.js
new file mode 100644
index 00000000000..240066d0f22
--- /dev/null
+++ b/app/assets/javascripts/ci_variable_list/store/mutation_types.js
@@ -0,0 +1,22 @@
+export const TOGGLE_VALUES = 'TOGGLE_VALUES';
+export const VARIABLE_BEING_EDITED = 'VARIABLE_BEING_EDITED';
+export const RESET_EDITING = 'RESET_EDITING';
+export const CLEAR_MODAL = 'CLEAR_MODAL';
+
+export const REQUEST_VARIABLES = 'REQUEST_VARIABLES';
+export const RECEIVE_VARIABLES_SUCCESS = 'RECEIVE_VARIABLES_SUCCESS';
+
+export const REQUEST_DELETE_VARIABLE = 'REQUEST_DELETE_VARIABLE';
+export const RECEIVE_DELETE_VARIABLE_SUCCESS = 'RECEIVE_DELETE_VARIABLE_SUCCESS';
+export const RECEIVE_DELETE_VARIABLE_ERROR = 'RECEIVE_DELETE_VARIABLE_ERROR';
+
+export const REQUEST_ADD_VARIABLE = 'REQUEST_ADD_VARIABLE';
+export const RECEIVE_ADD_VARIABLE_SUCCESS = 'RECEIVE_ADD_VARIABLE_SUCCESS';
+export const RECEIVE_ADD_VARIABLE_ERROR = 'RECEIVE_ADD_VARIABLE_ERROR';
+
+export const REQUEST_UPDATE_VARIABLE = 'REQUEST_UPDATE_VARIABLE';
+export const RECEIVE_UPDATE_VARIABLE_SUCCESS = 'RECEIVE_UPDATE_VARIABLE_SUCCESS';
+export const RECEIVE_UPDATE_VARIABLE_ERROR = 'RECEIVE_UPDATE_VARIABLE_ERROR';
+
+export const REQUEST_ENVIRONMENTS = 'REQUEST_ENVIRONMENTS';
+export const RECEIVE_ENVIRONMENTS_SUCCESS = 'RECEIVE_ENVIRONMENTS_SUCCESS';
diff --git a/app/assets/javascripts/ci_variable_list/store/mutations.js b/app/assets/javascripts/ci_variable_list/store/mutations.js
new file mode 100644
index 00000000000..74e2bcfa2db
--- /dev/null
+++ b/app/assets/javascripts/ci_variable_list/store/mutations.js
@@ -0,0 +1,86 @@
+import * as types from './mutation_types';
+import { __ } from '~/locale';
+
+export default {
+ [types.REQUEST_VARIABLES](state) {
+ state.isLoading = true;
+ },
+
+ [types.RECEIVE_VARIABLES_SUCCESS](state, variables) {
+ state.isLoading = false;
+ state.variables = variables;
+ },
+
+ [types.REQUEST_DELETE_VARIABLE](state) {
+ state.isDeleting = true;
+ },
+
+ [types.RECEIVE_DELETE_VARIABLE_SUCCESS](state) {
+ state.isDeleting = false;
+ },
+
+ [types.RECEIVE_DELETE_VARIABLE_ERROR](state, error) {
+ state.isDeleting = false;
+ state.error = error;
+ },
+
+ [types.REQUEST_ADD_VARIABLE](state) {
+ state.isLoading = true;
+ },
+
+ [types.RECEIVE_ADD_VARIABLE_SUCCESS](state) {
+ state.isLoading = false;
+ },
+
+ [types.RECEIVE_ADD_VARIABLE_ERROR](state, error) {
+ state.isLoading = false;
+ state.error = error;
+ },
+
+ [types.REQUEST_UPDATE_VARIABLE](state) {
+ state.isLoading = true;
+ },
+
+ [types.RECEIVE_UPDATE_VARIABLE_SUCCESS](state) {
+ state.isLoading = false;
+ },
+
+ [types.RECEIVE_UPDATE_VARIABLE_ERROR](state, error) {
+ state.isLoading = false;
+ state.error = error;
+ },
+
+ [types.TOGGLE_VALUES](state, valueState) {
+ state.valuesHidden = valueState;
+ },
+
+ [types.REQUEST_ENVIRONMENTS](state) {
+ state.isLoading = true;
+ },
+
+ [types.RECEIVE_ENVIRONMENTS_SUCCESS](state, environments) {
+ state.isLoading = false;
+ state.environments = environments;
+ state.environments.unshift(__('All environments'));
+ },
+
+ [types.VARIABLE_BEING_EDITED](state, variable) {
+ state.variableBeingEdited = variable;
+ },
+
+ [types.CLEAR_MODAL](state) {
+ state.variable = {
+ variable_type: __('Variable'),
+ key: '',
+ secret_value: '',
+ protected: false,
+ masked: false,
+ environment_scope: __('All environments'),
+ };
+ },
+
+ [types.RESET_EDITING](state) {
+ state.variableBeingEdited = null;
+ state.showInputValue = false;
+ },
+};
diff --git a/app/assets/javascripts/ci_variable_list/store/state.js b/app/assets/javascripts/ci_variable_list/store/state.js
new file mode 100644
index 00000000000..c5e0bbfdbf4
--- /dev/null
+++ b/app/assets/javascripts/ci_variable_list/store/state.js
@@ -0,0 +1,24 @@
+import { __ } from '~/locale';
+
+export default () => ({
+ endpoint: null,
+ projectId: null,
+ isGroup: null,
+ maskableRegex: null,
+ isLoading: false,
+ isDeleting: false,
+ variable: {
+ variable_type: __('Variable'),
+ key: '',
+ secret_value: '',
+ protected: false,
+ masked: false,
+ environment_scope: __('All environments'),
+ },
+ variables: null,
+ valuesHidden: true,
+ error: null,
+ environments: [],
+ typeOptions: [__('Variable'), __('File')],
+ variableBeingEdited: null,
+});
diff --git a/app/assets/javascripts/ci_variable_list/store/utils.js b/app/assets/javascripts/ci_variable_list/store/utils.js
new file mode 100644
index 00000000000..44807e03dad
--- /dev/null
+++ b/app/assets/javascripts/ci_variable_list/store/utils.js
@@ -0,0 +1,44 @@
+import { __ } from '~/locale';
+
+const variableType = 'env_var';
+const fileType = 'file';
+
+const variableTypeHandler = type => (type === 'Variable' ? variableType : fileType);
+
+export const prepareDataForDisplay = variables => {
+ const variablesToDisplay = [];
+ variables.forEach(variable => {
+ const variableCopy = variable;
+ if (variableCopy.variable_type === variableType) {
+ variableCopy.variable_type = __('Variable');
+ } else {
+ variableCopy.variable_type = __('File');
+ }
+
+ if (variableCopy.environment_scope === '*') {
+ variableCopy.environment_scope = __('All environments');
+ }
+ variablesToDisplay.push(variableCopy);
+ });
+ return variablesToDisplay;
+};
+
+export const prepareDataForApi = (variable, destroy = false) => {
+ const variableCopy = variable;
+ variableCopy.protected.toString();
+ variableCopy.masked.toString();
+ variableCopy.variable_type = variableTypeHandler(variableCopy.variable_type);
+
+ if (variableCopy.environment_scope === __('All environments')) {
+ variableCopy.environment_scope = __('*');
+ }
+
+ if (destroy) {
+ // eslint-disable-next-line
+ variableCopy._destroy = destroy;
+ }
+
+ return variableCopy;
+};
+
+export const prepareEnvironments = environments => environments.map(e => e.name);
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index 6cd69fe75ce..a1bfa03a5ac 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -102,7 +102,6 @@
padding-bottom: 0.3em;
border-bottom: 1px solid $white-dark;
color: $gl-text-color;
- overflow: hidden;
&:first-child {
margin-top: 0;
@@ -116,7 +115,6 @@
padding-bottom: 0.3em;
border-bottom: 1px solid $white-dark;
color: $gl-text-color;
- overflow: hidden;
}
h3 {
diff --git a/app/workers/admin_email_worker.rb b/app/workers/admin_email_worker.rb
index a7cc4fb0d11..9388b1014ac 100644
--- a/app/workers/admin_email_worker.rb
+++ b/app/workers/admin_email_worker.rb
@@ -7,7 +7,7 @@ class AdminEmailWorker
include CronjobQueue
# rubocop:enable Scalability/CronWorkerContext
- feature_category_not_owned!
+ feature_category :source_code_management
def perform
send_repository_check_mail if Gitlab::CurrentSettings.repository_checks_enabled
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index f6daab73689..459872965f8 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -58,7 +58,7 @@
:resource_boundary: :unknown
:weight: 1
- :name: cronjob:admin_email
- :feature_category: :not_owned
+ :feature_category: :source_code_management
:has_external_dependencies:
:latency_sensitive:
:resource_boundary: :unknown
@@ -88,7 +88,7 @@
:resource_boundary: :unknown
:weight: 1
- :name: cronjob:gitlab_usage_ping
- :feature_category: :not_owned
+ :feature_category: :collection
:has_external_dependencies:
:latency_sensitive:
:resource_boundary: :unknown
@@ -142,7 +142,7 @@
:resource_boundary: :cpu
:weight: 1
- :name: cronjob:prune_old_events
- :feature_category: :not_owned
+ :feature_category: :users
:has_external_dependencies:
:latency_sensitive:
:resource_boundary: :unknown
diff --git a/app/workers/gitlab_usage_ping_worker.rb b/app/workers/gitlab_usage_ping_worker.rb
index bf0dc0fdd59..002542e26f4 100644
--- a/app/workers/gitlab_usage_ping_worker.rb
+++ b/app/workers/gitlab_usage_ping_worker.rb
@@ -9,7 +9,7 @@ class GitlabUsagePingWorker
include CronjobQueue
# rubocop:enable Scalability/CronWorkerContext
- feature_category_not_owned!
+ feature_category :collection
# Retry for up to approximately three hours then give up.
sidekiq_options retry: 10, dead: false
diff --git a/app/workers/prune_old_events_worker.rb b/app/workers/prune_old_events_worker.rb
index 835c51ec846..541a17c0fcf 100644
--- a/app/workers/prune_old_events_worker.rb
+++ b/app/workers/prune_old_events_worker.rb
@@ -7,7 +7,7 @@ class PruneOldEventsWorker
include CronjobQueue
# rubocop:enable Scalability/CronWorkerContext
- feature_category_not_owned!
+ feature_category :users
DELETE_LIMIT = 10_000
diff --git a/changelogs/unreleased/27142-divider-in-readme-is-overlaying-with-image.yml b/changelogs/unreleased/27142-divider-in-readme-is-overlaying-with-image.yml
deleted file mode 100644
index dd246a35640..00000000000
--- a/changelogs/unreleased/27142-divider-in-readme-is-overlaying-with-image.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Do not draw heading borders over floated images in markdown
-merge_request:
-author: Gwen_
-type: fixed
diff --git a/doc/administration/packages/container_registry.md b/doc/administration/packages/container_registry.md
index 87be9d500fb..26c83ab6034 100644
--- a/doc/administration/packages/container_registry.md
+++ b/doc/administration/packages/container_registry.md
@@ -619,7 +619,7 @@ provided by `gitlab-ctl`.
Consider the following example, where you first build the image:
-```bash
+```shell
# This builds a image with content of sha256:111111
docker build -t my.registry.com/my.group/my.project:latest .
docker push my.registry.com/my.group/my.project:latest
@@ -627,7 +627,7 @@ docker push my.registry.com/my.group/my.project:latest
Now, you do overwrite `:latest` with a new version:
-```bash
+```shell
# This builds a image with content of sha256:222222
docker build -t my.registry.com/my.group/my.project:latest .
docker push my.registry.com/my.group/my.project:latest
@@ -774,7 +774,7 @@ once a week.
Create a file under `/etc/cron.d/registry-garbage-collect`:
-```bash
+```shell
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
diff --git a/doc/administration/server_hooks.md b/doc/administration/server_hooks.md
index 4c72634d4ff..37fffcdce22 100644
--- a/doc/administration/server_hooks.md
+++ b/doc/administration/server_hooks.md
@@ -88,11 +88,10 @@ pattern (`*~`).
The hooks are searched and executed in this order:
-1. `gitlab-shell/hooks` directory as known to Gitaly.
-1. `<project>.git/hooks/<hook_name>` - executed by `git` itself, this is symlinked to `gitlab-shell/hooks/<hook_name>`.
+1. Built-in GitLab server hooks (not user-customizable).
1. `<project>.git/custom_hooks/<hook_name>` - per-project hook (this was kept as the already existing behavior).
1. `<project>.git/custom_hooks/<hook_name>.d/*` - per-project hooks.
-1. `<project>.git/hooks/<hook_name>.d/*` OR `<custom_hooks_dir>/<hook_name.d>/*` - global hooks: all executable files (except editor backup files).
+1. `<custom_hooks_dir>/<hook_name>.d/*` - global hooks: all executable files (except editor backup files).
The hooks of the same type are executed in order and execution stops on the
first script exiting with a non-zero value.
diff --git a/doc/administration/troubleshooting/kubernetes_cheat_sheet.md b/doc/administration/troubleshooting/kubernetes_cheat_sheet.md
index 48d415c6bdf..4ffce11aed0 100644
--- a/doc/administration/troubleshooting/kubernetes_cheat_sheet.md
+++ b/doc/administration/troubleshooting/kubernetes_cheat_sheet.md
@@ -74,7 +74,7 @@ and they will assist you with any issues you are having.
- How to get cronjobs configured on a cluster
- ```bash
+ ```shell
kubectl get cronjobs
```
diff --git a/doc/api/protected_environments.md b/doc/api/protected_environments.md
index 7d4e62a8ff5..852a5ae6e71 100644
--- a/doc/api/protected_environments.md
+++ b/doc/api/protected_environments.md
@@ -17,7 +17,7 @@ Currently, these levels are recognized:
Gets a list of protected environments from a project:
-```bash
+```shell
GET /projects/:id/protected_environments
```
@@ -25,7 +25,7 @@ GET /projects/:id/protected_environments
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
-```bash
+```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" 'https://gitlab.example.com/api/v4/projects/5/protected_environments/'
```
@@ -51,7 +51,7 @@ Example response:
Gets a single protected environment:
-```bash
+```shell
GET /projects/:id/protected_environments/:name
```
@@ -60,7 +60,7 @@ GET /projects/:id/protected_environments/:name
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `name` | string | yes | The name of the protected environment |
-```bash
+```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" 'https://gitlab.example.com/api/v4/projects/5/protected_environments/production'
```
@@ -84,11 +84,11 @@ Example response:
Protects a single environment:
-```bash
+```shell
POST /projects/:id/protected_environments
```
-```bash
+```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" 'https://gitlab.example.com/api/v4/projects/5/protected_environments?name=staging&deploy_access_levels%5B%5D%5Buser_id%5D=1'
```
@@ -122,11 +122,11 @@ Example response:
Unprotects the given protected environment:
-```bash
+```shell
DELETE /projects/:id/protected_environments/:name
```
-```bash
+```shell
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" 'https://gitlab.example.com/api/v4/projects/5/protected_environments/staging'
```
diff --git a/doc/ci/README.md b/doc/ci/README.md
index f25a0ade42a..5206807cf7a 100644
--- a/doc/ci/README.md
+++ b/doc/ci/README.md
@@ -50,6 +50,7 @@ To get started with GitLab CI/CD, we recommend you read through
the following documents:
- [How GitLab CI/CD works](introduction/index.md#how-gitlab-cicd-works).
+- [Fundamental pipeline architectures](pipelines/pipeline_architectures.md).
- [GitLab CI/CD basic workflow](introduction/index.md#basic-cicd-workflow).
- [Step-by-step guide for writing `.gitlab-ci.yml` for the first time](../user/project/pages/getting_started_part_four.md).
diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md
index 621b679de73..08419685388 100644
--- a/doc/ci/docker/using_docker_images.md
+++ b/doc/ci/docker/using_docker_images.md
@@ -397,7 +397,7 @@ Before the new extended Docker configuration options, you would need to create
your own image based on the `super/sql:latest` image, add the default command,
and then use it in job's configuration, like:
-```Dockerfile
+```dockerfile
# my-super-sql:latest image's Dockerfile
FROM super/sql:latest
diff --git a/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md b/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md
index f59401c6f87..848808f65ea 100644
--- a/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md
+++ b/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md
@@ -63,7 +63,7 @@ Next, we'll create a small subset of tests that exemplify most of the states I e
this `Weapon` class to go through. To get started, create a folder called `lib/tests`
and add the following code to a new file `weaponTests.ts`:
-```ts
+```typescript
import { expect } from 'chai';
import { Weapon, BulletFactory } from '../lib/weapon';
@@ -114,7 +114,7 @@ describe('Weapon', () => {
To build and run these tests using gulp, let's also add the following gulp functions
to the existing `gulpfile.js` file:
-```ts
+```typescript
gulp.task('build-test', function () {
return gulp.src('src/tests/**/*.ts', { read: false })
.pipe(tap(function (file) {
@@ -140,7 +140,7 @@ to trigger the weapon. In the `src/lib` folder create a `weapon.ts` file. We'll
to it: `Weapon` and `BulletFactory` which will encapsulate Phaser's **sprite** and
**group** objects, and the logic specific to our game.
-```ts
+```typescript
export class Weapon {
private isTriggered: boolean = false;
private currentTimer: number = 0;
@@ -210,7 +210,7 @@ export class BulletFactory {
Lastly, we'll redo our entry point, `game.ts`, to tie together both `Player` and `Weapon` objects
as well as add them to the update loop. Here is what the updated `game.ts` file looks like:
-```ts
+```typescript
import { Player } from "./player";
import { Weapon, BulletFactory } from "./weapon";
diff --git a/doc/ci/pipelines/pipeline_architectures.md b/doc/ci/pipelines/pipeline_architectures.md
new file mode 100644
index 00000000000..0e6745a59eb
--- /dev/null
+++ b/doc/ci/pipelines/pipeline_architectures.md
@@ -0,0 +1,269 @@
+---
+type: reference
+---
+
+# Pipeline Architecture
+
+Pipelines are the fundamental building blocks for CI/CD in GitLab. This page documents
+some of the important concepts related to them.
+
+There are three main ways to structure your pipelines, each with their
+own advantages. These methods can be mixed and matched if needed:
+
+- [Basic](#basic-pipelines): Good for straightforward projects where all the configuration is in one easy to find place.
+- [Directed Acylic Graph](#directed-acyclic-graph-pipelines): Good for large, complex projects that need efficient execution.
+- [Child/Parent Pipelines](#child--parent-pipelines): Good for monorepos and projects with lots of independently defined components.
+
+For more details about
+any of the keywords used below, check out our [CI YAML reference](../yaml/) for details.
+
+## Basic Pipelines
+
+This is the simplest pipeline in GitLab. It will run everything in the build stage concurrently,
+and once all of those finish, it will run everything in the test stage the same way, and so on.
+It's not the most efficient, and if you have lots of steps it can grow quite complex, but it's
+easier to maintain:
+
+```mermaid
+graph LR
+ subgraph deploy stage
+ deploy --> deploy_a
+ deploy --> deploy_b
+ end
+ subgraph test stage
+ test --> test_a
+ test --> test_b
+ end
+ subgraph build stage
+ build --> build_a
+ build --> build_b
+ end
+ build_a -.-> test
+ build_b -.-> test
+ test_a -.-> deploy
+ test_b -.-> deploy
+```
+
+Example basic `/.gitlab-ci.yml` pipeline configuration matching the diagram:
+
+```yaml
+stages:
+ - build
+ - test
+ - deploy
+
+image: alpine
+
+build_a:
+ stage: build
+ script:
+ - echo "This job builds something."
+
+build_b:
+ stage: build
+ script:
+ - echo "This job builds something else."
+
+test_a:
+ stage: test
+ script:
+ - echo "This job tests something. It will only run when all jobs in the"
+ - echo "build stage are complete."
+
+test_b:
+ stage: test
+ script:
+ - echo "This job tests something else. It will only run when all jobs in the"
+ - echo "build stage are complete too. It will start at about the same time as test_a."
+
+deploy_a:
+ stage: deploy
+ script:
+ - echo "This job deploys something. It will only run when all jobs in the"
+ - echo "test stage complete."
+
+deploy_b:
+ stage: deploy
+ script:
+ - echo "This job deploys something else. It will only run when all jobs in the"
+ - echo "test stage complete. It will start at about the same time as deploy_a."
+```
+
+## Directed Acyclic Graph Pipelines
+
+If efficiency is important to you and you want everything to run as quickly as possible,
+you can use [Directed Acylic Graphs (DAG)](../directed_acyclic_graph/index.md). Use the
+[`needs` keyword](../yaml/README.md#needs) to define dependency relationships between
+your jobs. When GitLab knows the relationships between your jobs, it can run everything
+as fast as possible, and even skips into subsequent stages when possible.
+
+In the example below, if `build_a` and `test_a` are much faster than `build_b` and
+`test_b`, GitLab will start `deploy_a` even if `build_b` is still running.
+
+```mermaid
+graph LR
+ subgraph Pipeline using DAG
+ build_a --> test_a --> deploy_a
+ build_b --> test_b --> deploy_b
+ end
+```
+
+Example DAG `/.gitlab-ci.yml` configuration matching the diagram:
+
+```yaml
+stages:
+ - build
+ - test
+ - deploy
+
+image: alpine
+
+build_a:
+ stage: build
+ script:
+ - echo "This job builds something quickly."
+
+build_b:
+ stage: build
+ script:
+ - echo "This job builds something else slowly."
+
+test_a:
+ stage: test
+ needs: build_a
+ script:
+ - echo "This test job will start as soon as build_a finishes."
+ - echo "It will not wait for build_b, or other jobs in the build stage, to finish."
+
+test_b:
+ stage: test
+ needs: build_b
+ script:
+ - echo "This test job will start as soon as build_b finishes."
+ - echo "It will not wait for other jobs in the build stage to finish."
+
+deploy_a:
+ stage: deploy
+ needs: test_a
+ script:
+ - echo "Since build_a and test_a run quickly, this deploy job can run much earlier."
+ - echo "It does not need to wait for build_b or test_b."
+
+deploy_b:
+ stage: deploy
+ needs: test_b
+ script:
+ - echo "Since build_b and test_b run slowly, this deploy job will run much later."
+```
+
+## Child / Parent Pipelines
+
+In the examples above, it's clear we've got two types of things that could be built independently.
+This is an ideal case for using [Child / Parent Pipelines](../parent_child_pipelines.md)) via
+the [`trigger` keyword](../yaml/README.md#trigger). It will separate out the configuration
+into multiple files, keeping things very simple. You can also combine this with:
+
+- The [`rules` keyword](../yaml/README.md#rules): For example, have the child pipelines triggered only
+ when there are changes to that area.
+- The [`include` keyword](../yaml/README.md#include): Bring in common behaviors, ensuring
+ you are not repeating yourself.
+- [DAG pipelines](#directed-acyclic-graph-pipelines) inside of child pipelines, achieving the benefits of both.
+
+```mermaid
+graph LR
+ subgraph Parent pipeline
+ trigger_a -.-> build_a
+ trigger_b -.-> build_b
+ subgraph child pipeline B
+ build_b --> test_b --> deploy_b
+ end
+
+ subgraph child pipeline A
+ build_a --> test_a --> deploy_a
+ end
+ end
+```
+
+Example `/.gitlab-ci.yml` configuration for the parent pipeline matching the diagram:
+
+```yaml
+stages:
+ - triggers
+
+trigger_a:
+ stage: triggers
+ trigger:
+ include: a/.gitlab-ci.yml
+ rules:
+ - changes:
+ - a/*
+
+trigger_b:
+ stage: triggers
+ trigger:
+ include: b/.gitlab-ci.yml
+ rules:
+ - changes:
+ - b/*
+```
+
+Example child `a` pipeline configuration, located in `/a/.gitlab-ci.yml`, making
+use of the DAG `needs:` keyword:
+
+```yaml
+stages:
+ - build
+ - test
+ - deploy
+
+image: alpine
+
+build_a:
+ stage: build
+ script:
+ - echo "This job builds something."
+
+test_a:
+ stage: test
+ needs: build_a
+ script:
+ - echo "This job tests something."
+
+deploy_a:
+ stage: deploy
+ needs: test_a
+ script:
+ - echo "This job deploys something."
+```
+
+Example child `b` pipeline configuration, located in `/b/.gitlab-ci.yml`, making
+use of the DAG `needs:` keyword:
+
+```yaml
+stages:
+ - build
+ - test
+ - deploy
+
+image: alpine
+
+build_b:
+ stage: build
+ script:
+ - echo "This job builds something else."
+
+test_b:
+ stage: test
+ needs: build_b
+ script:
+ - echo "This job tests something else."
+
+deploy_b:
+ stage: deploy
+ needs: test_b
+ script:
+ - echo "This job deploys something else."
+```
+
+It's also possible to set jobs to run before or after triggering child pipelines,
+for example if you have common setup steps or a unified deployment at the end.
diff --git a/doc/ci/quick_start/README.md b/doc/ci/quick_start/README.md
index 6de3eaf8831..83b3ec44314 100644
--- a/doc/ci/quick_start/README.md
+++ b/doc/ci/quick_start/README.md
@@ -17,6 +17,11 @@ NOTE: **Note:**
Coming over to GitLab from Jenkins? Check out our [reference](../jenkins/index.md)
for converting your pre-existing pipelines over to our format.
+NOTE: **Note:**
+There are a few different [basic pipeline architectures](../pipelines/pipeline_architectures.md)
+that you can consider for use in your project. You may want to familiarize
+yourself with these prior to getting started.
+
GitLab offers a [continuous integration](https://about.gitlab.com/stages-devops-lifecycle/continuous-integration/) service. For each commit or push to trigger your CI
[pipeline](../pipelines.md), you must:
diff --git a/doc/ci/services/postgres.md b/doc/ci/services/postgres.md
index a137f10949f..235f237ba94 100644
--- a/doc/ci/services/postgres.md
+++ b/doc/ci/services/postgres.md
@@ -17,12 +17,12 @@ First, in your `.gitlab-ci.yml` add:
```yaml
services:
- - postgres:latest
+ - postgres:12.2-alpine
variables:
POSTGRES_DB: nice_marmot
POSTGRES_USER: runner
- POSTGRES_PASSWORD: ""
+ POSTGRES_PASSWORD: "runner-password"
```
NOTE: **Note:**
@@ -37,7 +37,7 @@ And then configure your application to use the database, for example:
```yaml
Host: postgres
User: runner
-Password:
+Password: runner-password
Database: nice_marmot
```
diff --git a/doc/development/code_comments.md b/doc/development/code_comments.md
index c1d58c1bd4b..a71e2b3c792 100644
--- a/doc/development/code_comments.md
+++ b/doc/development/code_comments.md
@@ -7,7 +7,7 @@ check if a comment is still relevant and what needs to be done to address it.
Examples:
-```rb
+```ruby
# Deprecated scope until code_owner column has been migrated to rule_type.
# To be removed with https://gitlab.com/gitlab-org/gitlab/issues/11834.
scope :code_owner, -> { where(code_owner: true).or(where(rule_type: :code_owner)) }
diff --git a/doc/development/creating_enums.md b/doc/development/creating_enums.md
index 64385a2ea79..79ed465b121 100644
--- a/doc/development/creating_enums.md
+++ b/doc/development/creating_enums.md
@@ -8,7 +8,7 @@ To use this type, add `limit: 2` to the migration that creates the column.
Example:
-```rb
+```ruby
def change
add_column :ci_job_artifacts, :file_format, :integer, limit: 2
end
diff --git a/doc/development/documentation/styleguide.md b/doc/development/documentation/styleguide.md
index e2f84e1200e..de7a437a1d6 100644
--- a/doc/development/documentation/styleguide.md
+++ b/doc/development/documentation/styleguide.md
@@ -783,33 +783,64 @@ nicely on different mobile devices.
- When providing a shell command and its output, prefix the shell command with `$` and
leave a blank line between the command and the output.
- When providing a command without output, don't prefix the shell command with `$`.
+- If you need to include triple backticks inside a code block, use four backticks
+ for the codeblock fences instead of three.
- For regular code blocks, always use a highlighting class corresponding to the
language for better readability. Examples:
- ~~~md
+ ````markdown
```ruby
Ruby code
```
- ```js
+ ```javascript
JavaScript code
```
- ```md
+ ```markdown
[Markdown code example](example.md)
```
- ```text
+ ```plaintext
Code or text for which no specific highlighting class is available.
```
- ~~~
-
-- To display raw Markdown instead of rendered Markdown, you can use triple backticks
- with `md`, like the `Markdown code` example above, unless you want to include triple
- backticks in the code block as well. In that case, use triple tildes (`~~~`) instead.
-- [Syntax highlighting for code blocks](https://github.com/rouge-ruby/rouge/wiki/List-of-supported-languages-and-lexers)
- is available for many languages. Use `shell` instead of `bash` or `sh` for shell output.
-- For a complete reference on code blocks, check the [Kramdown guide](https://about.gitlab.com/handbook/product/technical-writing/markdown-guide/#code-blocks).
+ ````
+
+Syntax highlighting is required for code blocks added to the GitLab documentation.
+Refer to the table below for the most common language classes, or check the
+[complete list](https://github.com/rouge-ruby/rouge/wiki/List-of-supported-languages-and-lexers)
+of language classes available.
+
+| Preferred language tags | Language aliases and notes |
+|-------------------------|------------------------------------------------------------------------------|
+| `asciidoc` | |
+| `dockerfile` | Alias: `docker`. |
+| `elixir` | |
+| `erb` | |
+| `golang` | Alias: `go`. |
+| `graphql` | |
+| `haml` | |
+| `html` | |
+| `ini` | For some simple config files that are not in TOML format. |
+| `javascript` | Alias `js`. |
+| `json` | |
+| `markdown` | Alias: `md`. |
+| `mermaid` | |
+| `nginx` | |
+| `perl` | |
+| `php` | |
+| `plaintext` | Examples with no defined language, such as output from shell commands or API calls. If a codeblock has no language, it defaults to `plaintext`. Alias: `text`. |
+| `prometheus` | Prometheus configuration examples. |
+| `python` | |
+| `ruby` | Alias: `rb`. |
+| `shell` | Aliases: `bash` or `sh`. |
+| `sql` | |
+| `toml` | Runner configuration examples, and other toml formatted configuration files. |
+| `typescript` | Alias: `ts`. |
+| `xml` | |
+| `yaml` | Alias: `yml`. |
+
+For a complete reference on code blocks, check the [Kramdown guide](https://about.gitlab.com/handbook/product/technical-writing/markdown-guide/#code-blocks).
## GitLab SVG icons
diff --git a/doc/development/gitaly.md b/doc/development/gitaly.md
index 32017a284d5..b275a265cc6 100644
--- a/doc/development/gitaly.md
+++ b/doc/development/gitaly.md
@@ -240,13 +240,13 @@ Here are the steps to gate a new feature in Gitaly behind a feature flag.
1. Create a package scoped flag name:
- ```go
+ ```golang
var findAllTagsFeatureFlag = "go-find-all-tags"
```
1. Create a switch in the code using the `featureflag` package:
- ```go
+ ```golang
if featureflag.IsEnabled(ctx, findAllTagsFeatureFlag) {
// go implementation
} else {
@@ -256,7 +256,7 @@ Here are the steps to gate a new feature in Gitaly behind a feature flag.
1. Create Prometheus metrics:
- ```go
+ ```golang
var findAllTagsRequests = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "gitaly_find_all_tags_requests_total",
@@ -280,7 +280,7 @@ Here are the steps to gate a new feature in Gitaly behind a feature flag.
1. Set headers in tests:
- ```go
+ ```golang
import (
"google.golang.org/grpc/metadata"
diff --git a/doc/development/go_guide/index.md b/doc/development/go_guide/index.md
index 73a1dd8ad8a..ae215026f56 100644
--- a/doc/development/go_guide/index.md
+++ b/doc/development/go_guide/index.md
@@ -195,7 +195,7 @@ When comparing expected and actual values in tests, use
and others to improve readability when comparing structs, errors,
large portions of text, or JSON documents:
-```go
+```golang
type TestData struct {
// ...
}
diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md
index dcd822705f9..aa210f3550f 100644
--- a/doc/topics/autodevops/index.md
+++ b/doc/topics/autodevops/index.md
@@ -918,12 +918,12 @@ instead of the default `ruby:latest`:
1. Set `AUTO_DEVOPS_BUILD_IMAGE_EXTRA_ARGS` to `--build-arg=RUBY_VERSION=alpine`.
1. Add the following to a custom `Dockerfile`:
- ```docker
- ARG RUBY_VERSION=latest
- FROM ruby:$RUBY_VERSION
+ ```dockerfile
+ ARG RUBY_VERSION=latest
+ FROM ruby:$RUBY_VERSION
- # ... put your stuff here
- ```
+ # ... put your stuff here
+ ```
NOTE: **Note:**
Passing in complex values (newlines and spaces, for example) will likely
@@ -955,14 +955,14 @@ In projects:
1. Activate the experimental `Dockerfile` syntax by adding the following
to the top of the file:
- ```docker
+ ```dockerfile
# syntax = docker/dockerfile:experimental
```
1. To make secrets available in any `RUN $COMMAND` in the `Dockerfile`, mount
the secret file and source it prior to running `$COMMAND`:
- ```docker
+ ```dockerfile
RUN --mount=type=secret,id=auto-devops-build-secrets . /run/secrets/auto-devops-build-secrets && $COMMAND
```
diff --git a/doc/topics/web_application_firewall/quick_start_guide.md b/doc/topics/web_application_firewall/quick_start_guide.md
index e3cf0bcd498..96c2da4e963 100644
--- a/doc/topics/web_application_firewall/quick_start_guide.md
+++ b/doc/topics/web_application_firewall/quick_start_guide.md
@@ -80,11 +80,11 @@ under which this application will be deployed.
![Google auth](../autodevops/img/guide_google_auth_v12_3.png)
1. The last step is to provide the cluster details.
- 1. Give it a name, leave the environment scope as is, and choose the GCP project under which the cluster
- will be created (per the instructions to [configure your Google account](#configuring-your-google-account), a project should have already been created for you).
- 1. Choose the [region/zone](https://cloud.google.com/compute/docs/regions-zones/) under which the cluster will be created.
- 1. Enter the number of nodes you want it to have.
- 1. Choose the [machine type](https://cloud.google.com/compute/docs/machine-types).
+ 1. Give it a name, leave the environment scope as is, and choose the GCP project under which the cluster
+ will be created (per the instructions to [configure your Google account](#configuring-your-google-account), a project should have already been created for you).
+ 1. Choose the [region/zone](https://cloud.google.com/compute/docs/regions-zones/) under which the cluster will be created.
+ 1. Enter the number of nodes you want it to have.
+ 1. Choose the [machine type](https://cloud.google.com/compute/docs/machine-types).
![GitLab GKE cluster details](../autodevops/img/guide_gitlab_gke_details_v12_3.png)
@@ -180,40 +180,40 @@ your cluster either using [Cloud Shell](https://cloud.google.com/shell/) or the
1. After connecting to your cluster, check if the Ingress-NGINX controller is running and ModSecurity is enabled.
- This is done by running the following commands:
+ This is done by running the following commands:
- ```bash
- $ kubectl get pods -n gitlab-managed-apps | grep 'ingress-controller'
- ingress-nginx-ingress-controller-55f9cf6584-dxljn 2/2 Running
+ ```shell
+ $ kubectl get pods -n gitlab-managed-apps | grep 'ingress-controller'
+ ingress-nginx-ingress-controller-55f9cf6584-dxljn 2/2 Running
- $ kubectl -n gitlab-managed-apps exec -it $(kubectl get pods -n gitlab-managed-apps | grep 'ingress-controller' | awk '{print $1}') -- cat /etc/nginx/nginx.conf | grep 'modsecurity on;'
- modsecurity on;
- ```
+ $ kubectl -n gitlab-managed-apps exec -it $(kubectl get pods -n gitlab-managed-apps | grep 'ingress-controller' | awk '{print $1}') -- cat /etc/nginx/nginx.conf | grep 'modsecurity on;'
+ modsecurity on;
+ ```
1. Verify the Rails application has been installed properly.
- ```bash
- $ kubectl get ns
- auto-devv-2-16730183-production Active
+ ```shell
+ $ kubectl get ns
+ auto-devv-2-16730183-production Active
- $ kubectl get pods -n auto-devv-2-16730183-production
- NAME READY STATUS RESTARTS
- production-5778cfcfcd-nqjcm 1/1 Running 0
- production-postgres-6449f8cc98-r7xgg 1/1 Running 0
- ```
+ $ kubectl get pods -n auto-devv-2-16730183-production
+ NAME READY STATUS RESTARTS
+ production-5778cfcfcd-nqjcm 1/1 Running 0
+ production-postgres-6449f8cc98-r7xgg 1/1 Running 0
+ ```
1. To make sure the Rails application is responding, send a request to it by running:
- ```bash
- $ kubectl get ing -n auto-devv-2-16730183-production
- NAME HOSTS PORTS
- production-auto-deploy fjdiaz-auto-devv-2.34.68.60.207.nip.io,le-16730183.34.68.60.207.nip.io 80, 443
+ ```shell
+ $ kubectl get ing -n auto-devv-2-16730183-production
+ NAME HOSTS PORTS
+ production-auto-deploy fjdiaz-auto-devv-2.34.68.60.207.nip.io,le-16730183.34.68.60.207.nip.io 80, 443
- $ curl --location --insecure fjdiaz-auto-devv-2.34.68.60.207.nip.io | grep 'Rails!' --after 2 --before 2
- <body>
- <p>You're on Rails!</p>
- </body>
- ```
+ $ curl --location --insecure fjdiaz-auto-devv-2.34.68.60.207.nip.io | grep 'Rails!' --after 2 --before 2
+ <body>
+ <p>You're on Rails!</p>
+ </body>
+ ```
Now that we have confirmed our system is properly setup, we can go ahead and test
the WAF with OWASP CRS!
@@ -223,7 +223,7 @@ the WAF with OWASP CRS!
Now let's send a potentially malicious request, as if we were a scanner,
checking for vulnerabilities within our application and examine the modsecurity logs:
-```bash
+```shell
$ curl --location --insecure fjdiaz-auto-devv-2.34.68.60.207.nip.io --header "User-Agent: absinthe" | grep 'Rails!' --after 2 --before 2
<body>
<p>You're on Rails!</p>
diff --git a/doc/user/project/integrations/img/prometheus_dashboard_environments_v12_8.png b/doc/user/project/integrations/img/prometheus_dashboard_environments_v12_8.png
new file mode 100644
index 00000000000..467deb86881
--- /dev/null
+++ b/doc/user/project/integrations/img/prometheus_dashboard_environments_v12_8.png
Binary files differ
diff --git a/doc/user/project/integrations/img/prometheus_monitoring_dashboard_v12_8.png b/doc/user/project/integrations/img/prometheus_monitoring_dashboard_v12_8.png
new file mode 100644
index 00000000000..8899852ed04
--- /dev/null
+++ b/doc/user/project/integrations/img/prometheus_monitoring_dashboard_v12_8.png
Binary files differ
diff --git a/doc/user/project/integrations/prometheus.md b/doc/user/project/integrations/prometheus.md
index e703f15b4d3..2f6c77c8217 100644
--- a/doc/user/project/integrations/prometheus.md
+++ b/doc/user/project/integrations/prometheus.md
@@ -55,6 +55,17 @@ will help you to quickly create a deployment:
1. Navigate to your project's **CI/CD > Pipelines** page, and run a pipeline on any branch.
1. When the pipeline has run successfully, graphs will be available on the **Operations > Metrics** page.
+![Monitoring Dashboard](img/prometheus_monitoring_dashboard_v12_8.png)
+
+#### Using the Metrics Dashboard
+
+##### Select an environment
+
+The **Environment** dropdown box above the dashboard displays the list of all [environments](#monitoring-cicd-environments).
+It enables you to search as you type through all environments and select the one you're looking for.
+
+![Monitoring Dashboard Environments](img/prometheus_dashboard_environments_v12_8.png)
+
#### About managed Prometheus deployments
Prometheus is deployed into the `gitlab-managed-apps` namespace, using the [official Helm chart](https://github.com/helm/charts/tree/master/stable/prometheus). Prometheus is only accessible within the cluster, with GitLab communicating through the [Kubernetes API](https://kubernetes.io/docs/concepts/overview/kubernetes-api/).
@@ -428,6 +439,29 @@ Note the following properties:
![single stat panel type](img/prometheus_dashboard_single_stat_panel_type.png)
+###### Percentile based results
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/201946) in GitLab 12.8.
+
+Query results sometimes need to be represented as a percentage value out of 100. You can use the `max_value` property at the root of the panel definition:
+
+```yaml
+dashboard: 'Dashboard Title'
+panel_groups:
+ - group: 'Group Title'
+ panels:
+ - title: "Single Stat"
+ type: "single-stat"
+ max_value: 100
+ metrics:
+ - id: 10
+ query: 'max(go_memstats_alloc_bytes{job="prometheus"})'
+ unit: '%'
+ label: "Total"
+```
+
+For example, if you have a query value of `53.6`, adding `%` as the unit results in a single stat value of `53.6%`, but if the maximum expected value of the query is `120`, the value would be `44.6%`. Adding the `max_value` causes the correct percentage value to display.
+
##### Heatmaps
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/30581) in GitLab 12.5.
diff --git a/lib/gitlab/ci/templates/Jobs/Browser-Performance-Testing.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Browser-Performance-Testing.gitlab-ci.yml
index c3ca44eea9e..20063cf6a69 100644
--- a/lib/gitlab/ci/templates/Jobs/Browser-Performance-Testing.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Jobs/Browser-Performance-Testing.gitlab-ci.yml
@@ -1,12 +1,10 @@
performance:
stage: performance
- # pin to a version matching the dind service, just to be safe
image: docker:19.03.5
allow_failure: true
variables:
DOCKER_TLS_CERTDIR: ""
services:
- # pin to a known working version until https://gitlab.com/gitlab-org/gitlab-runner/issues/6697 is fixed
- docker:19.03.5-dind
script:
- |
diff --git a/lib/gitlab/ci/templates/Jobs/Build.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Build.gitlab-ci.yml
index 488945ffa3e..bb0de9df8bf 100644
--- a/lib/gitlab/ci/templates/Jobs/Build.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Jobs/Build.gitlab-ci.yml
@@ -4,7 +4,6 @@ build:
variables:
DOCKER_TLS_CERTDIR: ""
services:
- # pin to a known working version until https://gitlab.com/gitlab-org/gitlab-runner/issues/6697 is fixed
- docker:19.03.5-dind
script:
- |
diff --git a/lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml
index dd5144e28a7..5b8d41bbd10 100644
--- a/lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml
@@ -1,10 +1,8 @@
code_quality:
stage: test
- # pin to a version matching the dind service, just to be safe
image: docker:19.03.5
allow_failure: true
services:
- # pin to a known working version until https://gitlab.com/gitlab-org/gitlab-runner/issues/6697 is fixed
- docker:19.03.5-dind
variables:
DOCKER_DRIVER: overlay2
diff --git a/lib/quality/kubernetes_client.rb b/lib/quality/kubernetes_client.rb
index 453b9d21adb..f83652e117f 100644
--- a/lib/quality/kubernetes_client.rb
+++ b/lib/quality/kubernetes_client.rb
@@ -48,7 +48,8 @@ module Quality
resource_names = raw_resource_names
command = [
'delete',
- %(--namespace "#{namespace}")
+ %(--namespace "#{namespace}"),
+ '--ignore-not-found'
]
Array(release_name).each do |release|
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 70945366c6c..b492921680b 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -534,6 +534,9 @@ msgstr ""
msgid "(removed)"
msgstr ""
+msgid "*"
+msgstr ""
+
msgid "+ %{amount} more"
msgstr ""
@@ -1544,6 +1547,9 @@ msgstr ""
msgid "All email addresses will be used to identify your commits."
msgstr ""
+msgid "All environments"
+msgstr ""
+
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr ""
@@ -8385,6 +8391,9 @@ msgstr ""
msgid "Fetching licenses failed. You are not permitted to perform this action."
msgstr ""
+msgid "File"
+msgstr ""
+
msgid "File Hooks"
msgstr ""
@@ -19425,6 +19434,12 @@ msgstr ""
msgid "There was an error fetching the Designs"
msgstr ""
+msgid "There was an error fetching the environments information."
+msgstr ""
+
+msgid "There was an error fetching the variables."
+msgstr ""
+
msgid "There was an error fetching value stream analytics stages."
msgstr ""
@@ -21367,6 +21382,9 @@ msgstr ""
msgid "Value Stream Analytics gives an overview of how much time it takes to go from idea to production in your project."
msgstr ""
+msgid "Variable"
+msgstr ""
+
msgid "Variables"
msgstr ""
diff --git a/spec/frontend/ci_variable_list/services/mock_data.js b/spec/frontend/ci_variable_list/services/mock_data.js
new file mode 100644
index 00000000000..89473b57af9
--- /dev/null
+++ b/spec/frontend/ci_variable_list/services/mock_data.js
@@ -0,0 +1,90 @@
+export default {
+ mockVariables: [
+ {
+ environment_scope: 'All environments',
+ id: 113,
+ key: 'test_var',
+ masked: false,
+ protected: false,
+ value: 'test_val',
+ variable_type: 'Variable',
+ },
+ {
+ environment_scope: 'All environments',
+ id: 114,
+ key: 'test_var_2',
+ masked: false,
+ protected: false,
+ value: 'test_val_2',
+ variable_type: 'Variable',
+ },
+ {
+ environment_scope: 'All environments',
+ id: 115,
+ key: 'test_var_3',
+ masked: false,
+ protected: false,
+ value: 'test_val_3',
+ variable_type: 'Variable',
+ },
+ ],
+
+ mockVariablesApi: [
+ {
+ environment_scope: '*',
+ id: 113,
+ key: 'test_var',
+ masked: false,
+ protected: false,
+ value: 'test_val',
+ variable_type: 'env_var',
+ },
+ {
+ environment_scope: '*',
+ id: 114,
+ key: 'test_var_2',
+ masked: false,
+ protected: false,
+ value: 'test_val_2',
+ variable_type: 'file',
+ },
+ ],
+
+ mockVariablesDisplay: [
+ {
+ environment_scope: 'All environments',
+ id: 113,
+ key: 'test_var',
+ masked: false,
+ protected: false,
+ value: 'test_val',
+ variable_type: 'Variable',
+ },
+ {
+ environment_scope: 'All environments',
+ id: 114,
+ key: 'test_var_2',
+ masked: false,
+ protected: false,
+ value: 'test_val_2',
+ variable_type: 'File',
+ },
+ ],
+
+ mockEnvironments: [
+ {
+ id: 28,
+ name: 'staging',
+ slug: 'staging',
+ external_url: 'https://staging.example.com',
+ state: 'available',
+ },
+ {
+ id: 29,
+ name: 'production',
+ slug: 'production',
+ external_url: 'https://production.example.com',
+ state: 'available',
+ },
+ ],
+};
diff --git a/spec/frontend/ci_variable_list/store/actions_spec.js b/spec/frontend/ci_variable_list/store/actions_spec.js
new file mode 100644
index 00000000000..84455612f0c
--- /dev/null
+++ b/spec/frontend/ci_variable_list/store/actions_spec.js
@@ -0,0 +1,279 @@
+import Api from '~/api';
+import MockAdapter from 'axios-mock-adapter';
+import testAction from 'helpers/vuex_action_helper';
+import axios from '~/lib/utils/axios_utils';
+import createFlash from '~/flash';
+import getInitialState from '~/ci_variable_list/store/state';
+import * as actions from '~/ci_variable_list/store/actions';
+import * as types from '~/ci_variable_list/store/mutation_types';
+import mockData from '../services/mock_data';
+import { prepareDataForDisplay, prepareEnvironments } from '~/ci_variable_list/store/utils';
+
+jest.mock('~/api.js');
+jest.mock('~/flash.js');
+
+describe('CI variable list store actions', () => {
+ let mock;
+ let state;
+ const mockVariable = {
+ environment_scope: '*',
+ id: 63,
+ key: 'test_var',
+ masked: false,
+ protected: false,
+ value: 'test_val',
+ variable_type: 'env_var',
+ _destory: true,
+ };
+ const payloadError = new Error('Request failed with status code 500');
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ state = getInitialState();
+ state.endpoint = '/variables';
+ });
+
+ afterEach(() => {
+ mock.restore();
+ });
+
+ describe('toggleValues', () => {
+ const valuesHidden = false;
+ it('commits TOGGLE_VALUES mutation', () => {
+ testAction(actions.toggleValues, valuesHidden, {}, [
+ {
+ type: types.TOGGLE_VALUES,
+ payload: valuesHidden,
+ },
+ ]);
+ });
+ });
+
+ describe('clearModal', () => {
+ it('commits CLEAR_MODAL mutation', () => {
+ testAction(actions.clearModal, {}, {}, [
+ {
+ type: types.CLEAR_MODAL,
+ },
+ ]);
+ });
+ });
+
+ describe('resetEditing', () => {
+ it('commits RESET_EDITING mutation', () => {
+ testAction(
+ actions.resetEditing,
+ {},
+ {},
+ [
+ {
+ type: types.RESET_EDITING,
+ },
+ ],
+ [{ type: 'fetchVariables' }],
+ );
+ });
+ });
+
+ describe('deleteVariable', () => {
+ it('dispatch correct actions on successful deleted variable', done => {
+ mock.onPatch(state.endpoint).reply(200);
+
+ testAction(
+ actions.deleteVariable,
+ mockVariable,
+ state,
+ [],
+ [
+ { type: 'requestDeleteVariable' },
+ { type: 'receiveDeleteVariableSuccess' },
+ { type: 'fetchVariables' },
+ ],
+ () => {
+ done();
+ },
+ );
+ });
+
+ it('should show flash error and set error in state on delete failure', done => {
+ mock.onPatch(state.endpoint).reply(500, '');
+
+ testAction(
+ actions.deleteVariable,
+ mockVariable,
+ state,
+ [],
+ [
+ { type: 'requestDeleteVariable' },
+ {
+ type: 'receiveDeleteVariableError',
+ payload: payloadError,
+ },
+ ],
+ () => {
+ expect(createFlash).toHaveBeenCalled();
+ done();
+ },
+ );
+ });
+ });
+
+ describe('updateVariable', () => {
+ it('dispatch correct actions on successful updated variable', done => {
+ mock.onPatch(state.endpoint).reply(200);
+
+ testAction(
+ actions.updateVariable,
+ mockVariable,
+ state,
+ [],
+ [
+ { type: 'requestUpdateVariable' },
+ { type: 'receiveUpdateVariableSuccess' },
+ { type: 'fetchVariables' },
+ ],
+ () => {
+ done();
+ },
+ );
+ });
+
+ it('should show flash error and set error in state on update failure', done => {
+ mock.onPatch(state.endpoint).reply(500, '');
+
+ testAction(
+ actions.updateVariable,
+ mockVariable,
+ state,
+ [],
+ [
+ { type: 'requestUpdateVariable' },
+ {
+ type: 'receiveUpdateVariableError',
+ payload: payloadError,
+ },
+ ],
+ () => {
+ expect(createFlash).toHaveBeenCalled();
+ done();
+ },
+ );
+ });
+ });
+
+ describe('addVariable', () => {
+ it('dispatch correct actions on successful added variable', done => {
+ mock.onPatch(state.endpoint).reply(200);
+
+ testAction(
+ actions.addVariable,
+ {},
+ state,
+ [],
+ [
+ { type: 'requestAddVariable' },
+ { type: 'receiveAddVariableSuccess' },
+ { type: 'fetchVariables' },
+ ],
+ () => {
+ done();
+ },
+ );
+ });
+
+ it('should show flash error and set error in state on add failure', done => {
+ mock.onPatch(state.endpoint).reply(500, '');
+
+ testAction(
+ actions.addVariable,
+ {},
+ state,
+ [],
+ [
+ { type: 'requestAddVariable' },
+ {
+ type: 'receiveAddVariableError',
+ payload: payloadError,
+ },
+ ],
+ () => {
+ expect(createFlash).toHaveBeenCalled();
+ done();
+ },
+ );
+ });
+ });
+
+ describe('fetchVariables', () => {
+ it('dispatch correct actions on fetchVariables', done => {
+ mock.onGet(state.endpoint).reply(200, { variables: mockData.mockVariables });
+
+ testAction(
+ actions.fetchVariables,
+ {},
+ state,
+ [],
+ [
+ { type: 'requestVariables' },
+ {
+ type: 'receiveVariablesSuccess',
+ payload: prepareDataForDisplay(mockData.mockVariables),
+ },
+ ],
+ () => {
+ done();
+ },
+ );
+ });
+
+ it('should show flash error and set error in state on fetch variables failure', done => {
+ mock.onGet(state.endpoint).reply(500);
+
+ testAction(actions.fetchVariables, {}, state, [], [{ type: 'requestVariables' }], () => {
+ expect(createFlash).toHaveBeenCalledWith('There was an error fetching the variables.');
+ done();
+ });
+ });
+ });
+
+ describe('fetchEnvironments', () => {
+ it('dispatch correct actions on fetchEnvironments', done => {
+ Api.environments = jest.fn().mockResolvedValue({ data: mockData.mockEnvironments });
+
+ testAction(
+ actions.fetchEnvironments,
+ {},
+ state,
+ [],
+ [
+ { type: 'requestEnvironments' },
+ {
+ type: 'receiveEnvironmentsSuccess',
+ payload: prepareEnvironments(mockData.mockEnvironments),
+ },
+ ],
+ () => {
+ done();
+ },
+ );
+ });
+
+ it('should show flash error and set error in state on fetch environments failure', done => {
+ Api.environments = jest.fn().mockRejectedValue();
+
+ testAction(
+ actions.fetchEnvironments,
+ {},
+ state,
+ [],
+ [{ type: 'requestEnvironments' }],
+ () => {
+ expect(createFlash).toHaveBeenCalledWith(
+ 'There was an error fetching the environments information.',
+ );
+ done();
+ },
+ );
+ });
+ });
+});
diff --git a/spec/frontend/ci_variable_list/store/mutations_spec.js b/spec/frontend/ci_variable_list/store/mutations_spec.js
new file mode 100644
index 00000000000..1bb34e88cf5
--- /dev/null
+++ b/spec/frontend/ci_variable_list/store/mutations_spec.js
@@ -0,0 +1,64 @@
+import state from '~/ci_variable_list/store/state';
+import mutations from '~/ci_variable_list/store/mutations';
+import * as types from '~/ci_variable_list/store/mutation_types';
+
+describe('CI variable list mutations', () => {
+ let stateCopy;
+
+ beforeEach(() => {
+ stateCopy = state();
+ });
+
+ describe('TOGGLE_VALUES', () => {
+ it('should toggle state', () => {
+ const valuesHidden = false;
+
+ mutations[types.TOGGLE_VALUES](stateCopy, valuesHidden);
+
+ expect(stateCopy.valuesHidden).toEqual(valuesHidden);
+ });
+ });
+
+ describe('VARIABLE_BEING_EDITED', () => {
+ it('should set variable that is being edited', () => {
+ const variableBeingEdited = {
+ environment_scope: '*',
+ id: 63,
+ key: 'test_var',
+ masked: false,
+ protected: false,
+ value: 'test_val',
+ variable_type: 'env_var',
+ };
+
+ mutations[types.VARIABLE_BEING_EDITED](stateCopy, variableBeingEdited);
+
+ expect(stateCopy.variableBeingEdited).toEqual(variableBeingEdited);
+ });
+ });
+
+ describe('RESET_EDITING', () => {
+ it('should reset variableBeingEdited to null', () => {
+ mutations[types.RESET_EDITING](stateCopy);
+
+ expect(stateCopy.variableBeingEdited).toEqual(null);
+ });
+ });
+
+ describe('CLEAR_MODAL', () => {
+ it('should clear modal state ', () => {
+ const modalState = {
+ variable_type: 'Variable',
+ key: '',
+ secret_value: '',
+ protected: false,
+ masked: false,
+ environment_scope: 'All environments',
+ };
+
+ mutations[types.CLEAR_MODAL](stateCopy);
+
+ expect(stateCopy.variable).toEqual(modalState);
+ });
+ });
+});
diff --git a/spec/frontend/ci_variable_list/store/utils_spec.js b/spec/frontend/ci_variable_list/store/utils_spec.js
new file mode 100644
index 00000000000..9d5dd6b4f29
--- /dev/null
+++ b/spec/frontend/ci_variable_list/store/utils_spec.js
@@ -0,0 +1,47 @@
+import {
+ prepareDataForDisplay,
+ prepareEnvironments,
+ prepareDataForApi,
+} from '~/ci_variable_list/store/utils';
+import mockData from '../services/mock_data';
+
+describe('CI variables store utils', () => {
+ it('prepares ci variables for display', () => {
+ expect(prepareDataForDisplay(mockData.mockVariablesApi)).toStrictEqual(
+ mockData.mockVariablesDisplay,
+ );
+ });
+
+ it('prepares single ci variable for api', () => {
+ expect(prepareDataForApi(mockData.mockVariablesDisplay[0])).toStrictEqual({
+ environment_scope: '*',
+ id: 113,
+ key: 'test_var',
+ masked: false,
+ protected: false,
+ value: 'test_val',
+ variable_type: 'env_var',
+ });
+
+ expect(prepareDataForApi(mockData.mockVariablesDisplay[1])).toStrictEqual({
+ environment_scope: '*',
+ id: 114,
+ key: 'test_var_2',
+ masked: false,
+ protected: false,
+ value: 'test_val_2',
+ variable_type: 'file',
+ });
+ });
+
+ it('prepares single ci variable for delete', () => {
+ expect(prepareDataForApi(mockData.mockVariablesDisplay[0], true)).toHaveProperty(
+ '_destroy',
+ true,
+ );
+ });
+
+ it('prepares environments for display', () => {
+ expect(prepareEnvironments(mockData.mockEnvironments)).toStrictEqual(['staging', 'production']);
+ });
+});
diff --git a/spec/lib/quality/kubernetes_client_spec.rb b/spec/lib/quality/kubernetes_client_spec.rb
index 3a362dfccbf..1cfee5200f3 100644
--- a/spec/lib/quality/kubernetes_client_spec.rb
+++ b/spec/lib/quality/kubernetes_client_spec.rb
@@ -38,7 +38,7 @@ RSpec.describe Quality::KubernetesClient do
.and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
expect(Gitlab::Popen).to receive(:popen_with_detail)
- .with([%(kubectl delete --namespace "#{namespace}" #{pod_for_release})])
+ .with([%(kubectl delete --namespace "#{namespace}" --ignore-not-found #{pod_for_release})])
.and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
# We're not verifying the output here, just silencing it
@@ -64,7 +64,7 @@ RSpec.describe Quality::KubernetesClient do
.and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
expect(Gitlab::Popen).to receive(:popen_with_detail)
- .with([%(kubectl delete --namespace "#{namespace}" #{pod_for_release})])
+ .with([%(kubectl delete --namespace "#{namespace}" --ignore-not-found #{pod_for_release})])
.and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
# We're not verifying the output here, just silencing it
@@ -89,7 +89,7 @@ RSpec.describe Quality::KubernetesClient do
.and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
expect(Gitlab::Popen).to receive(:popen_with_detail)
- .with([%(kubectl delete --namespace "#{namespace}" #{pod_for_release})])
+ .with([%(kubectl delete --namespace "#{namespace}" --ignore-not-found #{pod_for_release})])
.and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
# We're not verifying the output here, just silencing it